分析过程
这里随便搞了一个乱写的函数基本只用了if-else语句,现在的目的是根据该函数的反汇编结果还原为C函数
汇编指令:
void fun(int x, int y)
{
00AB1800 push ebp
00AB1801 mov ebp,esp
00AB1803 sub esp,0D8h
00AB1809 push ebx
00AB180A push esi
00AB180B push edi
00AB180C lea edi,[ebp-18h]
00AB180F mov ecx,6
00AB1814 mov eax,0CCCCCCCCh
00AB1819 rep stos dword ptr es:[edi]
00AB181B mov ecx,offset _C478F8FA_main@cpp (0ABC067h)
00AB1820 call @__CheckForDebuggerJustMyCode@4 (0AB1311h)
00AB1825 mov eax,dword ptr [0ABA000h]
00AB182A mov dword ptr [ebp-0x4],eax
00AB182D mov dword ptr [ebp-0x8],2
00AB1834 mov eax,dword ptr [ebp+0x8]
00AB1837 cmp eax,dword ptr [ebp+0xc]
00AB183A jl __$EncStackInitStart+3Bh (0AB1847h)
00AB183C mov eax,dword ptr [ebp-0x8]
00AB183F add eax,1
00AB1842 mov dword ptr [ebp-0x8],eax
00AB1845 jmp __$EncStackInitStart+58h (0AB1864h)
00AB1847 mov eax,dword ptr [ebp+0x8]
00AB184A cmp eax,dword ptr [ebp+0xc]
00AB184D jge __$EncStackInitStart+4Dh (0AB1859h)
00AB184F mov eax,dword ptr [ebp-0x4]
00AB1852 mov dword ptr [0ABA000h],eax
00AB1857 jmp __$EncStackInitStart+58h (0AB1864h)
00AB1859 mov eax,dword ptr [ebp-0x4]
00AB185C add eax,dword ptr [ebp-0x8]
00AB185F mov dword ptr [0ABA000h],eax
00AB1864 pop edi
00AB1865 pop esi
00AB1866 pop ebx
00AB1867 add esp,0D8h
00AB186D cmp ebp,esp
00AB186F call __RTC_CheckEsp (0AB1235h)
00AB1874 mov esp,ebp
00AB1876 pop ebp
00AB1877 ret
这里保留了指令的内存地址,目的是为了能够清楚地找到跳转指令所跳转地地方
1、找到核心部分
00AB1825 mov eax,dword ptr [0ABA000h]
00AB182A mov dword ptr [ebp-0x4],eax
00AB182D mov dword ptr [ebp-0x8],2
00AB1834 mov eax,dword ptr [ebp+0x8]
00AB1837 cmp eax,dword ptr [ebp+0xc]
00AB183A jl __$EncStackInitStart+3Bh (0AB1847h)
00AB183C mov eax,dword ptr [ebp-0x8]
00AB183F add eax,1
00AB1842 mov dword ptr [ebp-0x8],eax
00AB1845 jmp __$EncStackInitStart+58h (0AB1864h)
00AB1847 mov eax,dword ptr [ebp+0x8]
00AB184A cmp eax,dword ptr [ebp+0xc]
00AB184D jge __$EncStackInitStart+4Dh (0AB1859h)
00AB184F mov eax,dword ptr [ebp-0x4]
00AB1852 mov dword ptr [0ABA000h],eax
00AB1857 jmp __$EncStackInitStart+58h (0AB1864h)
00AB1859 mov eax,dword ptr [ebp-0x4]
00AB185C add eax,dword ptr [ebp-0x8]
00AB185F mov dword ptr [0ABA000h],eax
这部分的指令是执行函数功能的核心指令,其余的那些指令是用于为函数分配内存空间和平衡堆栈用的
2、分析参数
如果只给一个函数内部的指令,看不到参数压栈指令。此时,可以去分析ret,ebp-0x8,ebp-0xc
等值从而找到参数的个数。
00AB1834 mov eax,dword ptr [ebp+0x8]
00AB1837 cmp eax,dword ptr [ebp+0xc]
从这两步和ret
可以初步推断,这个函数参数有两个参数。由此,我令x=[ebp+0x8],y=[ebp+0xc]
,这里的调用约定按照_cdecl
即,参数从右至左依次入栈由调用者清理栈区。
3、分析全局变量
函数中多次出现mov eax,dword ptr [0ABA000h]
类似的指令,我可以猜测[0ABA000]
这里存的是全局变量。在该函数调用中只有这一个地址出现,那么我们判断这次调用中应该只使用了一个全局变量。由此,我令a=[0ABA000h]
。
4、分析局部变量
局部变量会存储在调用函数时为函数分配的缓冲区中,ebp-0x4,ebp-0x8
等地方会被用于存储局部变量。我们观察核心部分,找到局部变量。令b=[ebp-0x4],c=[ebp-0x8]
。
5、功能分析
先看跳转指令,cmp a,b
后接着一个jcc
指令,一般来说这里会有一个if-else
语句。跳转后,又发现了一个语句,如下
00AB1834 mov eax,dword ptr [ebp+0x8]
00AB1837 cmp eax,dword ptr [ebp+0xc]
00AB183A jl __$EncStackInitStart+3Bh (0AB1847h)
00AB1847 mov eax,dword ptr [ebp+0x8]
00AB184A cmp eax,dword ptr [ebp+0xc]
00AB184D jge __$EncStackInitStart+4Dh (0AB1859h)
这是一个if-else if
语句的典型特征。这里的jl
是小于跳转,jge
是大于等于跳转。在这一次的汇编指令中一共有四个jmp
分析时需要弄清楚判断条件和四个指令之间的跳转关系。
由分析可得到以下伪代码:
if(x>=y) then c=c+1 else if(x<y) then b = a else a=c+b
下面是分析时画的堆栈图:
6、返回值分析
函数的返回值一般是放在eax
中的,不在eax
中的我也没见过,之后可能能见着。所以我们需要去分析汇编指令看看是否有mov eax,
相关的给eax
赋值的指令。
00AB1859 mov eax,dword ptr [ebp-0x4]
00AB185C add eax,dword ptr [ebp-0x8]
00AB185F mov dword ptr [0ABA000h],eax
在函数结尾处我们只看见了这玩意,但是几个指令显然是if-else if-else
这里面的指令,并未存在明显的给eax
赋值的指令,所以推断该函数无返回值,为void类型
7、还原C函数
int a;//因为不知道这个全局变量原先的值是多少所以不赋值,默认为零
void fun(int x, int y)
{
int b = a;
int c = 2;
if(x >= y)
{
c = c + 1;
}
else if(x < y)
{
b = a;
}
else
{
a = c + b;
}
}
总结
-
if-else 语句反汇编后有很明显的特征,在分析时一定要搞清楚判断条件和
jxx
的跳转关系。 -
以上的实验基于x86体系,x64的其实大差不差,最近在学x86所以就这样了。