首页
社区
课程
招聘
[原创]#30天写作挑战#反汇编代码还原之优化方式
发表于: 2020-9-1 22:44 15773

[原创]#30天写作挑战#反汇编代码还原之优化方式

2020-9-1 22:44
15773

目录

反汇编代码中的优化方式

一丶优化方式

1.1 前言

在我们学习反汇编的时候.很多人都以为反汇编很难. 其实不然. 学什么都是有技巧的.

 

而想要锻炼我们的反汇编能力. 第一就是要进行多练. 第二就是熟悉底层原理. 第三就是熟悉套路.

 

往后几篇 会把C与C++的反汇编形式体现出来. 如果写的不对的话请批评指正.

1.2优化方式分类

​ 汇编中的加法 减法 乘法 除法 取模 等等 都是有优化方式.以及有套路的.

 

​ 优化方式分为以下几种

  • 常量折叠
  • 常量传播
  • 变量去除
  • 归并优化
  • Cpu流水线优化
  • 数学变换
  • 不可达分支优化
  • 代码外提优化
    遇到新的优化再说

那么着重介绍一下上面优化方式所代表的意思.

 

优化的前提是在 Release下切开启O2选项优化速度的前提 Debug版本也会优化但是更多的是方便程序员调试.所以在不影响调试的前提下才会进行优化.

1.3 常量折叠

有以下例子:

1
2
3
4
5
int n = 0;
int m = 1;
printf("%d",7 + 8);
printf("%d",n + 6);
printf("%d",n + m);

所谓常量折叠就是 在编译前 所遇到的常量.是可以进行计算的.那么就会优化为一个常量值

 

例如上面的 7 + 8 不会产生add指令 而是在程序编译后直接成为 15(0xF)

 

VC6.0VS2019分别编译 为什么要两个编译器. 目的就是让大家知道.这个套路不管是几十年前的6.0还是现如今的Vs2019 都是一样的. 可能会有些稍许不同.但是绝不影响你的逆向.以及反汇编.而恰巧这才是真正的核心所在.包括gcc编译也是一样.

 

Vc6.0 保留核心

1
2
3
4
5
6
7
8
9
10
.text:00401000                 push    0Fh
.text:00401002                 push    offset aD       ; "%d"
.text:00401007                 call    _printf
.text:0040100C                 push    6
.text:0040100E                 push    offset aD       ; "%d"
.text:00401013                 call    _printf
.text:00401018                 push    1
.text:0040101A                 push    offset aD       ; "%d"
.text:0040101F                 call    _printf
.text:00401024                 add     esp, 18h

可以看到 7+8在反汇编的形势下直接变为了 0xF. 也就是10进制的15

 

Vs2019

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:00401040 sub_401040      proc near               ; CODE XREF: start-8D↓p
.text:00401040                 push    0Fh
.text:00401042                 push    offset unk_417A8C
.text:00401047                 call    sub_401010
.text:0040104C                 push    6
.text:0040104E                 push    offset unk_417A8C
.text:00401053                 call    sub_401010
.text:00401058                 push    1
.text:0040105A                 push    offset unk_417A8C
.text:0040105F                 call    sub_401010
.text:00401064                 add     esp, 18h
.text:00401067                 xor     eax, eax
.text:00401069                 retn
.text:00401069 sub_401040      endp

可以看到唯一不同的就是高版本 IDA没有识别出sig库.所以调用的都成了 sub_401010 而低版本函数已经认出来了.

 

还有就是文件体积变大了. vc6.0 编译出来28kb 2019编译出来98kb. 你品.

 

上面例子足以说明什么是常量折叠 含义就是常量会在编译器给你计算出来

1.4常量传播

常量传播也叫做常量扩散 指的就是 变量在写入或者读取的时候没有传递内存地址(&)也没有传指针或者引用来修改值的时候就会发生常量传播

 

大白话讲就是 你没有修改我变量的代码.那么这个变量我就可以认为是常量了.

 

以上面高级代码为例子

1
2
int n = 0;
printf("%d",n + 6);

那么进行常量传播之后. n因为没有对其修改.也没有对其进行传地址的操作. 所以编译器就会把它变为常量了.

 

那么上面的代码就会产生如下代码

1
2
int n = 0;
printf("%d",0 + 6);

而看到这里想必大家应该明白了. 0 + 6 又符合常量折叠. 所以代码继续变化

1
2
int n = 0;
printf("%d",6);

以上面汇编为例子

1
2
3
.text:0040100C                 push    6
.text:0040100E                 push    offset aD       ; "%d"
.text:00401013                 call    _printf

想必大家知道这里为啥是 push 6 了. 这里进行了两次优化 一次是常量传播,一次是常量折叠

1.5变量去除

变量去除指的就是你程序中定义了变量但是没有对其进行修改. 然后进行常量传播,常量折叠一步一步给优化掉

 

还是以高级代码为例子

1
2
3
int n = 0;
int m = 1;
printf("%d",n + m);

程序首先发现 n m 两个变量相加. 但是看了一下上面. 发现没有对其进行修改.所以代码就会变为如下

1
printf("%d",0 + 1);

而0+1符合常量折叠.所以最终代码就变为了.

1
printf("%d",1);

对应反汇编

1
2
3
.text:00401018                 push    1
.text:0040101A                 push    offset aD       ; "%d"
.text:0040101F                 call    _printf

1.6 归并优化

归并优化,如果可以一起优化那么我就一起优化.

 

我们知道 printf 属于C调用约定. 所以需要外平栈 而且他是可变参.通过你push参数个数的不同.外平栈的大小也会相应改变

 

比如:

1
2
3
4
.text:00401018                 push    1
.text:0040101A                 push    offset aD       ; "%d"
.text:0040101F                 call    _printf
                               add esp,8

但是我们在上面的汇编代码中.并没有看到 add esp,8 而是直接看到了 add esp,0x18

 

原因是什么. 在你调用printf的时候.而下面你又调用了相同的几个 printf ,printf 都是C调用约定.

 

所以我就一起给你平了

 

所以代码就有如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:00401040 sub_401040      proc near               ; CODE XREF: start-8D↓p
.text:00401040                 push    0Fh
.text:00401042                 push    offset unk_417A8C
.text:00401047                 call    sub_401010
.text:0040104C                 push    6
.text:0040104E                 push    offset unk_417A8C
.text:00401053                 call    sub_401010
.text:00401058                 push    1
.text:0040105A                 push    offset unk_417A8C
.text:0040105F                 call    sub_401010
.text:00401064                 add     esp, 18h
.text:00401067                 xor     eax, eax
.text:00401069                 retn
.text:00401069 sub_401040      endp

一个参数是4个字节. 所以累计总共push了6个参数. 4 * 6 = 24个字节.所以平栈也需要24个字节

 

24恰巧是 十六进制的 0x18

1.7Cpu流水线优化

Cpu流水线优化其实说白了就是打乱指令执行顺序,而不影响原有功能. 这个在我们反汇编的时候需要注意.正常的汇编代码都是平平整整顺顺序序 让人一眼看的很舒服,而且反汇编出高级代码也很快.

 

这里以简单的汇编为例子 因为我要写很多代码才会遇到流水线优化.这里我模拟一下

 

正常的汇编代码指令顺序

1
2
3
4
5
6
7
xor eax,eax
xor ebx,ebx
xor ecx,ecx
mov eax,1
add eax,2
mov ebx,eax
mov ecx,3

打乱的流水线

1
2
3
4
5
6
7
xor eax,eax
mov eax,1
xor ecx,ecx
add eax,2
mov ecx,3
xor ebx,ebx
mov ebx,eax

汇编代码就很简单.

 

我们着重看一下打乱的汇编.

 

在没打乱之前代码平平整整.打乱之后发现很多汇编进行了穿插

 

比如

1
2
mov eax,1
xor ecx,ecx

在Cpu执行 mov eax,1的时候.可以直接执行 xor ecx,ecx 这样的好处是下一行汇编不依赖于上一行汇编.

 

之前的指令是下一行指令依赖于上一行指令. 那么Cpu 如果在执行第二行的时候发现你依赖于上一行汇编.那么就会等待.

1
2
mov eax,1
add eax,2

第一行执行了 mov eax,1 那么第二行又使用了eax. 那么第二行执行的时候就要等待第一行.

 

而打断的好处就是 我执行第一行的时候也可以执行第二行而且不会影响你的结果. 也可以提升速度

 

流水线优化需要你细品.

 

当你品完之后再看下打乱的汇编

1
2
3
4
5
6
7
xor eax,eax
mov eax,1
xor ecx,ecx
add eax,2
mov ecx,3
xor ebx,ebx
mov ebx,eax

是不是发现很顺眼了. 那么当你还原的时候完全可以按照自己的意愿来恢复汇编进行还原

1
2
3
4
5
6
7
8
9
xor ecx,ecx
mov ecx,3
 
xor eax,eax
mov eax,1
add eax,2
 
xor ebx,ebx
mov ebx,eax

是不是就很简单了.

1.8 数学变换优化

数学变换优化: 如果操作的数是无意义的.那么就会进行优化.

1
2
3
4
5
6
i = 10;
b = 11;
i = b + 0;
i = b - 0;
i = b *3;
i = i/3;

那么以上高级代码直接进行优化.优化为

1
i = b;

1.9 不可达分支优化

不可达分支则是分支永远都不会走.那么也不会产生汇编代码.也没有存在的意义

1
2
3
4
5
6
7
8
9
a = 10;
if (a == 10)
{
    xxxx
}
else
{
    xxxxx
}

上面已经知道a就是个常量.值就是10.那么会走if块. 而 else永远不会走.那么就会将else优化掉. 当然实际情况中代码肯定很多.不会像我一样简单的写一个 a = 10 去判断.

2.0代码外提优化

所谓代码外提一般是在循环的时候进行优化.循环体内部没有产生修改此变量的代码.就会进行优化

1
2
3
4
5
6
7
int x = xxx;
while(x > y/3)
{
    xxx....
    x--;
 
}

循环体内部并没有操作y/3. 所以这个值都会放到外面执行
则会优化为

1
2
3
4
5
6
t = y / 3;
while(x > t)
{
  xxxxx....
  x--;
}

而t变量很可能也会经过上面的几种优化变为了寄存器变量

二丶去掉优化方式

​ 代码混淆与优化是对立的.所以学习下优化也方便我们更好的"人肉"优化混淆代码

 

去掉优化的方式说一下

 

上面所说.都是编译器已经识别到了你的程序没有传地址传指针等. 所以我们想办法就不让他优化.

 

简单的函数如果你什么也不做也会给你优化掉的.

1
2
3
4
5
6
7
int n = 10;
int m = 0;
scanf("%d",&n);
scanf("%d",&m);
int c = n + m;
scanf("%d",&c);
printf("%d%d%d",&n,&m,&c)

我们使用了很多scanf 以及printf 并对其变量取地址 那么编译器就不知道我们会不会修改变量的值了.

 

那么就不会给我们优化了.此时查看汇编代码就能看到真实的最小限度的优化代码了.

三丶总结

上面的几种优化方式,虽然很简单.但是我们也必须要掌握以及了解的. 因为后面的反汇编 代码还原 会有更多的优化. 而那些优化在配合 上面所说的优化就会让你感觉很难. 或者不知道汇编为什么那样做. 而你了解了这些 在单独看各自的优化就会明白.也会豁然开朗.

 

这些在我博客上已经写过.这里发出来也是为了让新会员重新了解一下反汇编. 高手能复习. 新人能学习.

 

CPP代码很简单.本次不提交了


[课程]Android-CTF解题方法汇总!

最后于 2020-9-21 11:58 被TkBinary编辑 ,原因: 流水线优化位置,操作数手写有误.修改了
收藏
免费 19
支持
分享
打赏 + 3.00雪花
打赏次数 3 雪花 + 3.00
 
赞赏  Passdtab   +1.00 2020/09/03 上天入地,无所不能。
赞赏  Editor   +1.00 2020/09/02 精品文章~
赞赏  Ack麦子   +1.00 2020/09/02 感谢分享~
最新回复 (23)
雪    币: 23080
活跃值: (3432)
能力值: (RANK:648 )
在线值:
发帖
回帖
粉丝
2
666
2020-9-2 09:43
0
雪    币: 21449
活跃值: (62278)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
3
感谢分享!
2020-9-2 09:50
0
雪    币: 41
活跃值: (823)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
豁然开朗
2020-9-2 10:11
0
雪    币: 1706
活跃值: (9630)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
5
金奔腾 豁然开朗
干就完了
2020-9-2 10:12
0
雪    币: 1706
活跃值: (9630)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
6
2020-9-2 10:12
0
雪    币: 498
活跃值: (251)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
学习了
2020-9-2 10:14
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
666
2020-9-2 10:58
0
雪    币: 136
活跃值: (432)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
写的不错。
2020-9-2 11:30
0
雪    币: 203
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
nice
2020-9-2 11:36
0
雪    币: 446
活跃值: (595)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
学习学习
2020-9-2 13:55
0
雪    币: 8370
活跃值: (4926)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
12
支持,加一个inline优化吧,这个也属于比较基础的
2020-9-2 14:04
0
雪    币: 1706
活跃值: (9630)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
13
alphc 支持,加一个inline优化吧,这个也属于比较基础的
好的我一会看看.这个应该属于C++的.等后面还会继续出C以及C++的各种优化我顺便看看. 谢谢提醒.
2020-9-2 14:08
0
雪    币: 512
活跃值: (3465)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
合适入门,支持下!
2020-9-2 14:27
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
支持哦!
2020-9-2 15:57
0
雪    币: 1
活跃值: (207)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
好文章好老师
2020-9-3 07:14
0
雪    币: 1768
活跃值: (239)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
膜拜大佬,顶一个
2020-9-3 14:08
0
雪    币: 5037
活跃值: (2151)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
已学习。
2020-9-4 09:19
0
雪    币: 5511
活跃值: (2072)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
跟着楼主30天学习
2020-9-4 09:36
0
雪    币: 446
活跃值: (595)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
踩一下
2020-9-4 09:48
0
雪    币: 1706
活跃值: (9630)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
21
wanttobeno 跟着楼主30天学习
别掉队呀 开车很快的
2020-9-4 09:52
0
雪    币: 5511
活跃值: (2072)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
xor eax,eax
xor ebx,ebx
xor ecx,ecx
mov eax,1
add eax,1   ### 这里按照下面的,不是应该add eax,2?
mov ebx,eax
mov ecx,3


2020-9-4 13:44
0
雪    币: 1706
活跃值: (9630)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
23
wanttobeno xor eax,eax xor ebx,ebx xor ecx,ecx mov eax,1 add e ...
对的,应该是add eax,2 我写错了. 流水线优化那块吧.我编辑一下.
2020-9-4 13:59
0
雪    币: 21449
活跃值: (62278)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
24

欢迎更多的小伙伴参与到 #30天写作挑战#中来!活动详情:https://bbs.pediy.com/thread-261705.htm

2020-9-7 16:54
0
游客
登录 | 注册 方可回帖
返回
//