avatar

5.1日笔记 (week 2)

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,我去搞一下数学证明:

image-20200501154537929

搞几个例子,反推一下吧,这样便于多方面的理解

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,满足向零取,数学证明:

image-20200501165014099

所以我们可以反推出C代码

1
printf("%d",argc / 9);

总结:

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/oimul是表明的有符号计算,操作数是优前的被除数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

我们再去用公式推导一下:

image-20200501180545772

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 所以要去做题!!嘎嘎嘎!晚安!😋

Author: L0x1c
Link: https://l0x1c.github.io/2020/05/01/2020-5-1/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶