5.1日更新
今天是劳动节!好的本可爱不放假,继续看书,最近调整身体,调整了两天,有点身体不太好😭学习!
今日任务:小黄书+看雪CTF
步入正题!
各类型输出转换——Release版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| //IDA中的参数标识,经过优化后,省去了局部变量,直接使用参数 arg_0 = dword ptr 4 //变量 / 变量 和Debug版相同,此处省略 //...... //变量 / 常量 (常量值为2的幂)和Debug版相同,此处省略 //..... //变量 / 常量 (常量值为非2的幂),这里的汇编代码和Debug版的汇编代码差别很大 //将数值 92492493h放到eax中 mov eax,92492493h //有符号乘法,用esi乘以eax,esi中保存被除数 imul esi //edx为扩展的高位 add edx,esi //右移2位 sar edx,2 //结果放回eax mov eax,edx //将eax右移31次 shr eax,1Fh //加以右移结果,放入edx中 add edx,eax push edx push offset aNvarTwo7D; "nVarTwo / 7 = %d" call _printf //其余代码和Debug版类似,略 //......
|
这个方式有点没看懂,出现了一个大数据0x92492493,我去搞一下数学证明:
搞几个例子,反推一下吧,这样便于多方面的理解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| .text:00401000 _main proc near ; CODE XREF: start+AF p .text:00401000 arg_0 = dword ptr 4 .text:00401000 mov ecx,[esp+arg_0] .text:00401004 mov eax,38E38E39h .text:00401000 imul ecx //ecx乘以参数 .text:0040100B sar edx,1 //有符号移位 .text:0040100D mov eax,edx .text:0040100F shr eax,1Fh //无符号移位 .text:00401012 add edx,eax .text:00401014 push edx .text:00401015 push offset Format ; "%d" .text:0040101A call _printf .text:0040101F add esp,8 .text:00401022 retn .text:00401022_main endp
|
看一下我们的.text:00401004位置,是mov eax,38E38E39h
,之后做了乘法和移位的操作,最后push edx
,乘法指令中,edx
存放乘积数据的高4字节,所以直接用edx
相当于,将我们的乘积直接右移32位,之后又进行了一次右移1位,那么这里就相当于一个 2^33 的除法,在地址.text:0040100D处,eax得到了ecx的值,然后对eax右移了1F位,对应10进制也就是右移了31位,然后有一个加法,其实这里移位的目的是得到有符号数的符号位,如果结果是正数,那么add edx,eax
就是加0,等于什么都没干,如果结果是负数,那么后面的代码直接使用edx作为计算结果,需要对除法的商调整+1,满足向零取,数学证明:
所以我们可以反推出C代码
总结:
1 2 3 4 5 6 7
| mov eax,MagicNumber imul ... sar edx,... mov reg,edx shr reg,1Fh add edx,reg //之后就直接用edx的值,eax的不用
|
遇到上面你的指令序列的时候,基本可以判定是除法优化后的代码,除法的原型为a/o
,imul
是表明的有符号计算,操作数是优前的被除数a,右移的总次数确定n的值,用公式o= 2^n/c
,将MagicNumber作为c代入公式求我们的除数o的近似值,四舍五入取整,就可以恢复除法原型!
👴再搞个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| .text:00401080 _main proc near ; CODE xREF: start+AF p .text:00401080 arg_0 = dword ptr 4 .text:00401080 mov ecx,[esp+arg_0] .text:00401084 mov eax,24924925h .text:00401089 mul ecx .text:0040108B sub ecx,edx .text:0040108D shr ecx,1 .text:0040108F add ecx,edx .text:00401091 shr ecx,2 .text:00401094 push ecx .text:00401095 push offset Format .text:0040109A call _printf .text:0040109F add esp,8 .text:004010A2 xor eax,eax .text:004010A4 retn .text:004010A4 _ma in endp
|
我们再去用公式推导一下:
1
| printf("nVarTwo / 7 = %d\r\n", argc /7);
|
计算得出MagicNumber后,如果其值超出4字节整数的表达范围,编译器会对其进行调整,如上个例子中的argc/7
,计算MagicNumber时,编译器选择 2^35/7
,其结果超出了4字节整数的表达范围,所以编译器调整MagicNumber的取值为2^35/7-2^32
公式:a/o = [(a-a*c/2^32)/2 + a*c/2^32]/2^(n-1)
总结:
1 2 3 4 5 6 7
| mov eax,MagicNumber //reg表示通用寄存器 mul reg sub reg,edx shr reg,1 add reg,edx shr reg,A //这句没有,那么n就是1,否则这里就是n-1的值
|
如果遇到上面的指令序列,基本可以判定出发优化后的代码,其除法原型为a/常量o,mul代表的无符号计算,用公式o = 2^(32+n)/(2^32+c)
将MagicNumber作为c值代入公式求解常数除数o,即可恢复除法原型
那么我们返回去深度分析最开始的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| //IDA中的参数标识,经过优化后,省去了局部变量,直接使用参数 arg_0 = dword ptr 4 //变量 / 变量 和Debug版相同,此处省略 //...... //变量 / 常量 (常量值为2的幂)和Debug版相同,此处省略 //..... //变量 / 常量 (常量值为非2的幂),这里的汇编代码和Debug版的汇编代码差别很大 //将数值 92492493h放到eax中 mov eax,92492493h //有符号乘法,用esi乘以eax,esi中保存被除数 imul esi //edx为扩展的高位 add edx,esi //右移2位 sar edx,2 //结果放回eax mov eax,edx //将eax右移31次 shr eax,1Fh //加以右移结果,放入edx中 add edx,eax push edx push offset aNvarTwo7D; "nVarTwo / 7 = %d" call _printf //其余代码和Debug版类似,略 //......
|
编译器在计算MagicNumber时是作为无符号处理的,而代入除法转换乘法的代码中又是作为有符号乘数处理的,因为有符号的最高位不是数值,而是符号位,所以对应的符号乘法指令是不会让最高位参与数值计算的,这会导致乘数的数学意义和MagicNumber不一致
暂时停止更新两天吧!明天要打de1ctf 所以要去做题!!嘎嘎嘎!晚安!😋