首页
社区
课程
招聘
[旧帖] 要命的除法,看不懂啊 0.00雪花
发表于: 2013-6-13 06:08 8823

[旧帖] 要命的除法,看不懂啊 0.00雪花

2013-6-13 06:08
8823
谁能帮个忙,指点下,下面是个啥除法啊?

0040170C  |.  B8 FD4A815A   MOV EAX,5A814AFD
00401711  |.  F7E9          IMUL ECX
00401713  |.  2BD1          SUB EDX,ECX
00401715  |.  C1FA 06       SAR EDX,6
00401718  |.  8BC2          MOV EAX,EDX
0040171A  |.  C1E8 1F       SHR EAX,1F
0040171D  |.  03C2          ADD EAX,EDX
0040171F  |.  6BC0 63       IMUL EAX,EAX,63
00401722  |.  03C8          ADD ECX,EAX
00401724  |.  B8 67666666   MOV EAX,66666667                        
00401729  |.  F7E9          IMUL ECX
0040172B  |.  C1FA 02       SAR EDX,2
0040172E  |.  8BC2          MOV EAX,EDX
00401730  |.  C1E8 1F       SHR EAX,1F
00401733  |.  03C2          ADD EAX,EDX

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (13)
雪    币: 3305
活跃值: (2027)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
看得懂的都成大神了。
2013-6-13 07:22
0
雪    币: 371
活跃值: (72)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
单条指令能看懂,结合起来就傻了...
2013-6-13 08:15
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
确实如楼上所说,合在一起就不懂了
2013-6-13 08:49
0
雪    币: 196
活跃值: (135)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
5
这是编译对除法优化的结果,以前坛子里有位牛人写过一篇总结帖,可以搜下看看,大概如下:
(ecx + ecx / 181 * 0x63) / 10;
2013-6-13 08:52
0
雪    币: 40
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
除法
     两个操作数都是变量的情况无法优化, 只能用div/idiv
mov eax, dword ptr [ebp-4] ;将被除数放入eax
cdq                      ;将eax中的符号位扩展到edx, 即将edx:eax扩展成一个值与eax相同的64位有符号整数
idiv dword ptr [ebp-8]     ;用除数去除edx:eax, 商保存在eax中, 余数在edx中
除数是2的幂
     这种情况用sar优化. sar是算术右移, 即右移后左边空出来的位用符号位填充. sar相当于向下取整, 这在被除数非负时是符合要求的, 但是在被除数是负数时, 按照C的除法规则, 应该是向上取整的, 所以需要对被除数做一些调整. x<0 时, x/2^n 中的x应该被调整为 x+(2^n-1), 即把商+1, 这样再对商向下取整就符合要求了
;除2时
mov eax, dword ptr [ebp-4] ;将被除数放入eax
cdq                      ;将eax中的符号位扩展到edx, 即将edx:eax扩展成一个值与eax相同的64位有符号整数
sub eax, edx
;非负时, edx为0, 不改变eax; 为负数时, edx为-1, 相当于eax+1, 符合x+(2^1-1)
sar eax, 1     ;算术右移, 即左边空出来的位用符号位填充
;除数大于2时, 这里是8
mov eax, dword ptr [ebp-4] ;将被除数放入eax
cdq                      ;将eax中的符号位扩展到edx, 即将edx:eax扩展成一个值与eax相同的64位有符号整数
and edx, 7     ;除数非负时, edx为0, and后edx仍为0; 除数为负数时, edx为0xFFFFFFFF, and后为7
add eax, edx     ;加上edx, 如果非负, edx为0, 对eax无影响; 如果为负数, 相当于eax+7, 符合x+(2^8-1)
sar eax, 3
除数不是2的幂
     情况1
mov ecx, dword ptr [ebp-4]     ;ecx中是被除数
mov eax, 38E38E39h     
imul ecx     ;x*c ,乘法结果放在edx:eax中
sar edx, 1     ;edx是乘法结果的高32位, 直接操纵它等价于乘积已经右移了32位, 因此这是乘积右移33位
mov eax, edx
shr eax, 1Fh     ;右移31位, 目的是取符号位
add edx, eax     ;至此, edx中就是除法的计算结果了
     在这种情况下, 假设除数是o, 编译器会事先计算一个常量c=(2^n)/o, 然后对于被除数x, (x*c)>>n 就是除法的结果了. 对于n的取值, 编译器始终保证这是一个大于32的值, 这样在做乘法的时候可以提高效率.
     套用公式, 上面的代码是在计算 (x*38E38E39h)>>33, 并且在结果上又加上了符号位的值, 之所以要加符号位, 是因为我们依然用的是向下取整的sar, 这在结果是非负数的时候是正确的, 但是在结果是负数时比正确值少1(比如 -3.5 本来应该取 -3 的, 它取成 -4 了), 所以结果再加上一个符号位的值就正确了.
;总结, 当遇到以下形式的语句时, 基本可判断为除法运算
mov eax, MagicNumber     
imul 被除数
sar edx, a
mov eax, edx
shr eax, 1Fh
add edx, eax     ;之后, 直接使用edx的值
;n=a+32, (2^n)/MagicNumber 的结果就是除数的近似值
     情况2
mov ecx, dword ptr [ebp-4]     ;ecx中是被除数
mov eax, 24924925h
mul ecx     
sub ecx, edx
shr ecx, 1
add ecx, edx
shr ecx, 2     ;至此, ecx中是除法结果
     这种情况会在编译器选定的魔数超过4字节大小时出现, 编译器会将魔数调整为2^(32+n)/o-2^32 .
     化简整理上面的运算过程, 结果是 ecx * ((2^32+c) / 2^35), 如果把上面的魔数c的计算式代入的话, 还差一个n就能化简为 ecx/o 的形式了, 这个n就是ecx右移的位数, 这里是3位, 所以n=3. 代入魔数的计算式, o≈7. 验算一下, 2^35/7-2^32 正好等于魔数
;总结, 当遇到以下形式的语句时, 基本可判断为除法运算
mov reg, dword ptr [ebp-4]     ;reg中是被除数
mov eax, MagicNumber
mul reg
sub reg, edx
shr reg, 1
add reg, edx
shr reg, a     ;这句可能没有. 这里的a的值应该为n-1, 如果n为1, 这句没有. 至此, reg中是除法结果
;n=a+1, 除数的近似值为 (2^(32+n))/(2^32+MagicNumber)
     情况3
mov esi, dword ptr [ebp-4]     ;esi中是被除数
mov eax, 92492493h
imul esi
add edx, esi
sar edx, 2
mov eax, edx
shr eax, 1Fh     ;负数调整+1
add edx, eax     ;至此, edx中是除法结果
     这种情况会在魔数大于0x80000000时出现. 魔数本应是一个无符号数, 但是参与了有符号运算, 这时符号位不计为值, 导致参与运算的实际值为 MagicNumber-2^32
;总结, 当遇到以下形式的语句时, 基本可判断为除法运算
mov reg, dword ptr [ebp-4]     ;reg中是被除数
mov eax, MagicNumber     ;MagicNumber大于7fffffffh
mul reg
add edx, reg
sar edx, a
mov reg, edx
shr reg, 1Fh     
add edx, reg     ;至此, edx是除法结果
;n=a+32, (2^n)/MagicNumber 的结果就是除数的近似值
除数为负的2的幂
mov eax, dword ptr [ebp-4] ;将被除数放入eax
cdq                     
and edx, 7
add eax, edx
sar eax, 3     ;上面是除数为2的幂时的运算
neg eax     ;对结果取反
;这相当于把 n/(-o) 变成了 -(n/o)
除数为负的非2的幂
     情况1
mov ecx, dword ptr [ebp-4] ;将被除数放入ecx
mov eax, 99999999h
imul ecx
sar edx, 1
mov eax, edx
shr eax, 1Fh
add edx, eax     ;至此, edx中为除法结果
     首先注意这和除数大于0且不为2的幂时的区别: 虽然魔数大于0x7fffffff, 但是imul和sar直接并没有调整语句, 此时基本可以判断这是除数为负数的情况.
     此时的魔数实际上补码形式. 当除数o为正数时, 设魔数c=2^n/o; 当o为负数时, -c=2^32-2^n/|o| ==> |o|=2^n/(2^32-c)
;总结, 当遇到以下形式的语句时, 基本可判断为除法运算
mov reg, dword ptr [ebp-4]     ;reg中是被除数
mov eax, MagicNumber     ;MagicNumber大于7fffffffh且imul与sar之间没有调整语句
imul reg
sar edx, a
mov reg, edx
shr reg, 1Fh     
add edx, reg     ;至此, edx是除法结果
;n=a+32, -((2^n)/(2^32-MagicNumber)) 的结果就是除数的近似值
     情况2
mov ecx, dword ptr [ebp-4] ;将被除数放入ecx
mov eax, 6DB6DB6Dh
imul ecx
sub edx, ecx
sar edx, 2
mov eax, edx
shr eax, 1Fh
add edx, eax     ;至此, edx中为除法结果
     上面的运算过程化简之后可得 ecx* ((eax-2^32)/2^34) = edx, 因为edx中是除法结果, 所以 ecx* ((eax-2^32)/2^34) = ecx/o . 可得 |o| = 2^n / (2^32 - c) = 2^34 / 92492493h ≈ 7
;总结, 当遇到以下形式的语句时, 基本可判断为除法运算
mov reg, dword ptr [ebp-4]     ;reg中是被除数
mov eax, MagicNumber     ;MagicNumber小于7fffffffh且imul与sar之间有调整语句sub
imul reg
sub edx, reg
sar edx, a
mov reg, edx
shr reg, 1Fh     
add edx, reg     ;至此, edx是除法结果
;n=a+32, -((2^n)/(2^32-MagicNumber)) 的结果就是除数的近似值
2013-6-13 09:00
0
雪    币: 517
活跃值: (35)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
7
打开windows计算器,对照着看效果,看懂应该不成问题。主要是编译时对代码进行了优化,形成非常规机制而已。
2013-6-13 09:15
0
雪    币: 627
活跃值: (663)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
8
前半段:除-99
后半段:见参考书




Magic Number计算器

参考书:
Hacker’s Delight, Second Edition by Henry S. Warren, Jr.

链接:
Hacker's Delight Second Edition, ePub, PDF, CHM格式下载以及部分除法优化内容
浅析VC++6.0对整数除以常量的处理
上传的附件:
2013-6-13 09:33
0
雪    币: 45
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
也不是看得懂就大神了。。。你看我还是菜鸟。。
看看《C++反汇编揭秘》对应章节有很详细的讲解
还有A1Pass发的帖子也有讲除法优化的
2013-6-13 09:42
0
雪    币: 627
活跃值: (663)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
10
后半段答案:除10

上传的附件:
2013-6-13 09:56
0
雪    币: 69
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
C++反汇编有详细讲解,我觉得是(ECX+ECX/99×0X63)/10
2013-6-18 21:22
0
雪    币: 69
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
好吧…… 我错了 还有负号 是-99
2013-6-18 21:25
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
这个好像太难了吧。果断看不懂,帮不了你啊。
2013-6-19 14:19
0
雪    币: 661
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
《C++反汇编与逆向分析技术揭秘》 中第四章对各种运算有详解,特别是除法。LZ可以看看

看这种代码一条一条的看分析不出什么的。
2013-6-21 14:30
0
游客
登录 | 注册 方可回帖
返回
//