除法逆向

除法

除数为变量的时候是没有优化,只能使用除法指令,我说的变量是和argc这类在编译期间不能计算的变量,如果不是的话则会进行常量传播优化,也就当成常量去优化除法了

向下取整:取一个数的小于这个数的最大整数,如3.5 向下则是3
向上取整:取一个数的大于这个数的最小整数,如3.5 向上则是4
向0取整:坐标轴靠近0的整数,3.5是3,-3.5是-3

1两个无符号整数相除,结果还是无符号的
2两个有符号整数相除,那么结果是有符号的
3有符号树和无符号数混除,那么结果是无符号的

Release版

无符号
1除数为变量
int main(unsigned int argc)
{
    unsigned int a = 10;
    printf("%d", a / argc);
}

在这里插入图片描述

2 除数为常量

2.1除数为2的幂

int main(int argc)
{
	//如果被除数是常量的话就会变成 常量/常量 则会是常量折叠优化,编译器默认是按执行效率优化的
    printf("%d", (unsigned int)argc / 4);
}

在这里插入图片描述
2.2除数为非2的幂
2.2.1MagicNumber无进位

公式为C(除数)=2^(32+n) / Magic 32是看是什么程序,32位程序就是32,16位就是16
**这个公式计算C是向上取整**
int main(int argc)
{
    printf("%d", (unsigned int)argc / 3);
}

在这里插入图片描述
2.2.2 MagicNumber有进位

公式为C(除数) = 2n / (M + 2^32) 
int main(int argc)
{
	//Windows平台还需要满足乘减移加移
    printf("%d", (unsigned int)argc / 7)

在这里插入图片描述

有符号
1除数为变量
int main(int argc)
{
    int a = 10;
    printf("%d", a / argc);
}

在这里插入图片描述

2 除数为常量

2.1 除数为正
2.1.1 除数为2的幂
有符号数除法不能直接移位:c语言中有符号除法规则是向0取整,而移位sar是属于向下取整,所以就会有问题,要调整为向上取整才符合向0取整,例如-7/2,如果用移位的话,得到的值是-4,也就是向下取整,而正确答案应该是向上取整,所以要调整一下
一般计算机整数相除,选择向下取整,负数相除,选择向上取整 ,所以下整转上整

代码定式:
cdq		
// eax >= 0, edx = 0;
// eax < 0, edx = -1;
and edx, 3		//2^n -1	//检测n的值是否相等
add eax, edx
sar eax, 2		//n	//检测n的值是否相等
eax / 2^n

公式是
除以2的时候会再次优化成一条指令
把指令
and edx, 3 //2^n -1
add eax, edx //eax+edx=eax+3
替换成
sub eax,edx //这就相当于eax–1==eax+1=eax+2的1次方-1

int main(int argc)
{
    printf("%d", argc / 4);
}

在这里插入图片描述

2.1.2 除数为非2的幂
2.1.2.1 MagicNumber为正

套用公式
C=2^(32+n) / Magic
int main(int argc)
{
	//注意除以3的时候没有看到sar edx,1这条指令
    printf("%d", argc / 5);
    /*          
    汇编代码如下
    .text:00401003                 mov     eax, 66666667h                                                                    
    .text:00401008                 imul    [ebp+argc]
    .text:0040100B                 sar     edx, 1          ; 因为edx是乘积结果的高位,所以移动位数都要加上32,因为是32程序就是32位n=1,
    .text:0040100D                 mov     eax, edx		   ;固定的三条指令是进行取值调整,达到向0取整的目的
    .text:0040100F                 shr     eax, 1Fh
    .text:00401012                 add     eax, edx
    .text:00401014                 push    eax
    .text:00401015                 push    offset format   ; "%d"
    .text:0040101A                 call    _printf
    */
}

2.1.2.2 MagicNumber为负

套用公式
C=2^(32+n) / Magic

int main(int argc)
{
    printf("%d", argc / 7);
    /*
    反汇编优化代码
    .text:00401003                 mov     eax, 92492493h   ;magicNumber为负
    .text:00401008                 imul    [ebp+argc]
    .text:0040100B                 add     edx, [ebp+argc]  ;这里多一步加法是为了调整为真正的乘积的值                                     
    .text:0040100E                 sar     edx, 2
    .text:00401011                 mov     eax, edx	;固定的三条指令是进行取值调整,达到向0取整的目的
    .text:00401013                 shr     eax, 1Fh
    .text:00401016                 add     eax, edx
    .text:00401018                 push    eax
    .text:00401019                 push    offset format   ; "%d"
    .text:0040101E                 call    _printf
    */
   
}

相对于magicNumber为正多一条add的原因

;相对于magicNumber正数情况 多一步加法操作
用16位举例
16位任意数的补码=ffff+1-任意的数,这就是为什么补码要取反加1
;A * 8086  运算结果 注意以下均为16进制
;用到的公式:
A + ~A = FFFF
A + ~A + 1 = 10000 (求补码)

;计算机计算的结果
利用公式m=2^(32+n)/C 因为此时C是整数,所以m实际值为+M,而在编译后m是负数,用imul计算的是m的补码*a,而不是真正的值ma,相差的值看是32位还是16位程序,
举例16位程序
(举例m=ffff是实际值,此时m为+m,a=2,此时你用imul计算的话,因为高位为1,所以用ffff的补码*a 转成十进制计算
果-a=-1*2=-2,而实际值=ffff*a转换成十进制=65535*2=131070,假设差值x,-a+X=ffff*a==>X=ffff*a+a=10000*a
ffffa=-a+10000a  用imul计算的乘积-a放在dx.ax中,又刚好差值X=10000a 的乘积相当于把a放在高位dx中,所以实值
只需要高位相加就是正确的结果,加一条指令add dx,a(a是乘法因子)就完成了
)
A * -(10000 - 8086) ;magicNumber>=8000,所以m为负数 m补=-(2^16-m无)
A * (8086 - 10000)
8086a - 10000a=edx.eax 此时真值多减了10000a
8086a=edx.eax + 10000a
;因为imul计算结果多减了一个10000a,所以要再加回去,这才是真正的值
;因为10000a 的乘积相当于把a放在高位edx,eax的高位
;所以结果多一步汇编
add  edx, 乘法因子(10000a) 

2.2 除数为负
2.2.1 除数为2的幂

int main(int argc)
{
	//和除数为正,除数为2的幂优化一样,多了一条neg指令而已
    printf("%d", argc / -4);
}

在这里插入图片描述

2.2.2 除数为非2的幂

2.2.2.1 MagicNumber为正

  2^(32+n) / neg(Magic)
int main(int argc)
{
    printf("%d", argc / -7);
    /*
    汇编代码
    .text:00401003                 mov     eax, 6DB6DB6Dh;magic为正
    .text:00401008                 imul    [ebp+argc]
    .text:0040100B                 sub     edx, [ebp+argc];这里多一步减法是为了调整为真正的乘积的值
    .text:0040100E                 sar     edx, 2
    .text:00401011                 mov     eax, edx	;固定的三条指令是进行取值调整,达到向0取整的目的
    .text:00401013                 shr     eax, 1Fh
    .text:00401016                 add     eax, edx
    .text:00401018                 push    eax
    .text:00401019                 push    offset format   ; "%d"
    .text:0040101E                 call    _printf
    */

}
; 证明
利用公式m=2^(32+n)/C 因为此时C是负数m=2^(32+n)/-C,把负数提出来,所以m实际值为-M,而在编译后m是正数,imul算出来的结果也是有偏差的,一样要减去差值才是实际的值
A * 8086
;用到的公式:
A + ~A = FFFF
A + ~A + 1 = 10000 (求补码)

;计算机计算的结果
A * (10000 - 8086)
A * 10000 - 8086a
-8086a + 10000a
edx.eax + 10000a
因为乘法的结果多加了10000a,所以得把10000a的部分减去
sub edx, 乘法因子 

2.2.2.2 MagicNumber为负

2^(32+n) / neg(Magic)
int main(int argc)
{
    printf("%d", argc / -5);
    /*
    text:00401003                  mov     eax, 99999999h;magicNumber为负
    .text:00401008                 imul    [ebp+argc]
    .text:0040100B                 sar     edx, 1
    .text:0040100D                 mov     eax, edx	;固定的三条指令是进行取值调整,达到向0取整的目的
    .text:0040100F                 shr     eax, 1Fh
    .text:00401012                 add     eax, edx
    .text:00401014                 push    eax
    .text:00401015                 push    offset format   ; "%d"
    .text:0040101A                 call    _printf
    */
   
}

总结:先判断是有符号还是无符号数,根据mul imul乘法指令去确定,然后根据MagicNumber进位和无进位的公式套用看看是否符合公式,如果符合则是除法,因为汇编指令可以自己构造

总结汇编代码定式

有符号除法

除数为变量

idiv reg              ;单操作数
idiv reg,reg/Mem      ;双操作数

除法为常量

除数为正

除数为2的幂
mov eax,mem
cdq
and edx,m     ;m = 2^n-1 
add eax,edx   ;负数相当于加m 正数相当于不变
sar eax,n     ;除数相当于 2^n
除数非2的幂
  • MagicNumber为正
mov eax,Magic
imul reg        ;reg保存了被除数
sar edx,n       ;有符号移位这里满足:C(被除数) = 2^(32+n) / Magic

mov reg,edx		;固定的三条指令是进行调整的作用,这样取值达到了向0取整
shr reg,1Fh     ;拿到符号位
add edx,reg     ;负数相当于减 1  正数相当于不变,这样取值才是正确的
  • MagicNumber为负
mov eax,Magic
imul reg        ;reg保存了被除数
add edx,reg		;这里多一步加法是为了调整为
sar edx,n       ;这里满足:C(被除数) = 2^(32+n) / Magic

mov reg,edx		;固定的三条指令是进行调整的作用,这样取值达到了向0取整
shr reg,1Fh     ;拿到符号位
add edx,reg     ;负数相当于减 1  正数相当于不变

除数为负

除数为2的幂
mov eax,mem
cdq
and edx,m     ;m = 2^n-1 
add eax,edx   ;负数相当于加m 正数相当于不变
sar eax,n     ;除数相当于 2^n
neg eax       ;这里代表除数是负数,因为计算器是补码进行计算的,如果是负数的话,显示的时候要转成原码显示
除数为非2的幂

MagicNumber为正

;有符号Magic不该调整的调整了 说明被除数是负数
;套用公式计算的时候实际Magic需要取反加1
mov eax,Magic
imul reg        ;reg保存了被除数这里满足:被除数 = 2^(32+n) / neg(Magic)
sub edx,reg
sar edx,n

mov reg,edx		;固定的三条指令是进行调整的作用,这样取值达到了向0取整
shr reg,1Fh     ;拿到符号位
add edx,reg     ;负数相当于减 1  正数相当于不变

MagicNumber为负

;有符号Magic该调整没调整 说明被除数是负数
;计算的时候实际Magic需要取反加1
mov eax,Magic
imul reg        ;reg保存了被除数、这里满足:被除数 = 2^(32+n) / neg(Magic)
sar edx,n   
mov reg,edx
shr reg,1Fh     ;拿到符号位
add edx,reg     ;负数相当于减 1  正数相当于不变

无符号除法定式总结

除数为变量

无符号除法
div reg

除数为常量

除数为2的幂
and edx,m     ;m = 2^n-1 
shr eax,n     ;除数相当于 2^n
除数为非2的幂

MagicNumber有进位

mov eax,Magic   ;这里满足:被除数 = (2^ (m + A)) / (2^32 + Magic)
mul reg         ;reg保存了被除数
sub reg,edx
shr reg,m
add reg,edx
shr reg,A       ;这句没有的话n值为1

MagicNumber无进位

mov eax,Magic   ;这里满足:被除数 = 2^ (32 + n) /  Magic
mul reg         ;reg保存了被除数
shr edx,n

除法结论

利用imul和mul判断有无符号
imu有符号

1判断magic为正负

  • magic为正,乘数和位移运算中间无调整,除数为正 ,如果有调整这是负数
  • magic为负,乘数和位移运算中间有加法调整(乘积高位加一个乘法因子),除数为正,如果没有调整则是负数
    记住负数neg(magicNumber)后套用公式C=2^32+n/magicNumber
mul无符号
  • 只要在windows平台下不是乘减移加移的定式就是无进位的,也可以看代码的套用公式试以下,无进位的指令短,直接就是am>>n位
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值