这里写了一个简单的C语言if else,然后通过汇编代码把它逆向分析出来

在参数压栈后发现函数调用代码,被调用函数暂时成为fun1,在函数调用处单步步入,发现被调用函数的汇编代码:

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
00401020   push        ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
00401038 mov eax,dword ptr [ebp+8]
0040103B cmp eax,dword ptr [ebp+0Ch]
0040103E jle fun+2Bh (0040104b)
00401040 mov ecx,dword ptr [ebp+8]
00401043 mov dword ptr [_MAX (00427c50)],ecx
00401049 jmp fun+34h (00401054)
0040104B mov edx,dword ptr [ebp+0Ch]
0040104E mov dword ptr [_MAX (00427c50)],edx
00401054 xor eax,eax
00401056 pop edi
00401057 pop esi
00401058 pop ebx
00401059 mov esp,ebp
0040105B pop ebp
0040105C ret

前后几行都是对堆栈的操作,在学习堆栈图的时候就比较熟练了,就不用多说了,去掉前后几行堆栈操作,中间的

几行就是函数真正的功能代码了。只需要分析这几行就可以了:

1
2
3
4
5
6
7
8
00401038   mov         eax,dword ptr [ebp+8]
0040103B cmp eax,dword ptr [ebp+0Ch]
0040103E jle fun+2Bh (0040104b)
00401040 mov ecx,dword ptr [ebp+8]
00401043 mov dword ptr [_MAX (00427c50)],ecx
00401049 jmp fun+34h (00401054)
0040104B mov edx,dword ptr [ebp+0Ch]
0040104E mov dword ptr [_MAX (00427c50)],edx

首先第一行的mov指令就是把ebp+8放入eax寄存器,ebp+8就是最后一个压入堆栈的参数,也就是函数左边第一个参数,这个参数暂时称为p1吧(这个在我上一篇文章里说过了)

然后一个cmp指令把ebp+c和eax进行比较,ebp+c就是函数的第二个参数,这个参数暂时称为p2,那就可以知道这两行是在对函数的两个参数p1和p2进行比较

第三行一个jcc指令,jle是小于等于则跳转到0x40104b,上面进行判断,然后紧接着就是条件不成立就跳转,推测这应该是一个if判断语句,逆向代码里是jle跳转(小于等于),那么正向代码里的if条件应该就是大于。

jle所跳转到的地方就是正向代码里if花括号之内的代码了,如果if条件成立,就执行这部分的代码,它是两个MOV指令,首先把参数p2放到edx寄存器,然后把ecx里的值复制给一个地址为0x427c50的全局变量,这个全局变量暂时称为gv1。赋值给全局变量以后下面就开始清理堆栈了,说明这个函数已经运行完了。

上面是jle跳转的情况,再来看如果jle不跳转呢:

在jle下面也是两个mov指令,第一个mov指令把参数p1的值放到ecx寄存器,然后第二个mov指令把ecx的值赋值给全局变量gv1,紧接着是一个jmp指令,跳到0x401054的地方,可以看到0x401054是一个xor指令,开始清理堆栈,说明这个函数已经运行完了。

大概分析出这个函数的功能是这样: 接收两个参数p1和p2,然后判断这两个参数,如果p1小于等于p2是成立的,就把p2的值赋值给全局变量gv1,如果p1小于等于p2是不成立的就把p1的值复制给全局变量gv1。把这个逻辑用正向c语言代码实现出来就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int gv1 = 0; //声明全局变量gv1,并初始化
int fun1(int p1,int p2){//声明函数fun1,并接收两个参数p1和p2
if (p1 > p2){
gv1 = p1; //相当于汇编里的"如果p1小于等于p2,就把p1赋值给全局变量gv1"
}
else{
gv1 = p2; //相当于汇编里的"否则把p1赋值给全局变量gv1"
}
}

int main(){
fun1();
}

以上就是逆向还原出来的正向代码。为什么汇编里jcc条件是小于等于,在正向里就变成if大于了呢

因为在汇编里jcc跳去执行的那部分代码,就是正向代码里if条件成立时所执行的那部分代码,所以应该把jcc跳去执行那部分代码写在正向代码里if的花括号之内,所以应该反着写。

那么反着写小于等于就变成了大于。代码是一行一行的执行的,如果jcc不跳转,就会继续执行jcc下面的那行代码,就相当于在正向里if条件不成立,那么就执行if花括号之外的代码,所以jcc下面的代码应该是在正向里else花括号之内才对。

原本的正向代码是这样的: