C语言函数反汇编

时间:2021-09-27 00:58:09

C语言函数反汇编

​ 从反汇编的角度,看C是怎么创建函数的。在创建函数的过程中系统做了哪些准备,数据被存放在那里,又是如何完成函数操作的,参数与局部变量在底层硬件中又是怎么流动的?

函数的创建与堆栈操作密切相关

  • 空函数
  • 主函数
  • 带参函数
  • 无参、有局部变量的函数
  • 带参且有局部变量的函数

​ 分别从这些函数入手,查看反汇编代码,发现异同,从而加深C语言函数定义的正向理解,以及函数的反汇编代码。

空函数与主函数:

//空函数与主函数
#include "stdio.h"

void Plus(){
	
}

int main()
{
    Plus();
}

对应的汇编代码:

//主函数部分
int main ()
{
//保存栈底
005C13E0  push        ebp  	
//提升堆栈
005C13E1  mov         ebp,esp  		
005C13E3  sub         esp,0C0h 
//保存现场
005C13E9  push        ebx  
005C13EA  push        esi  
005C13EB  push        edi  
005C13EC  lea         edi,[ebp-0C0h] 
//填充堆栈
005C13F2  mov         ecx,30h  
005C13F7  mov         eax,0CCCCCCCCh  
005C13FC  rep stos    dword ptr es:[edi]  
//调用函数Plus
	Plus();
005C13FE  call        @ILT+265(_Plus) (5C110Eh)  
}		//到这里主函数大括号包起来的部分就已经结束了
//清空eax
005C1403  xor         eax,eax  
//恢复现场
005C1405  pop         edi  
005C1406  pop         esi  
005C1407  pop         ebx  
//恢复堆栈
005C1408  add         esp,0C0h  
005C140E  cmp         ebp,esp  		//用于比较栈底(ebp)和栈底(esp)两个存储的地址是否相同,并生成一个结果
005C1410  call        @ILT+305(__RTC_CheckEsp) (5C1136h)  		//看上面的结果是否是相同的,如果不是相同的,则针对这一问题进行下一步操作
005C1415  mov         esp,ebp  
005C1417  pop         ebp  
005C1418  ret  

005C1410  call        @ILT+305(__RTC_CheckEsp) (5C1136h)
#下面是上面语句的跳转
005C1490  jne         esperror (5C1493h)  //如果是一样的,那就不跳转到处理程序部分了
005C1492  ret  
#如果栈顶与栈底不一样的处理代码部分
esperror:
005C1493  push        ebp  
005C1494  mov         ebp,esp  
005C1496  sub         esp,0  
005C1499  push        eax  
005C149A  push        edx  
005C149B  push        ebx  
005C149C  push        esi  
005C149D  push        edi  
005C149E  mov         eax,dword ptr [ebp+4]  
005C14A1  push        0  
005C14A3  push        eax  
005C14A4  call        _RTC_Failure (5C11AEh)  
005C14A9  add         esp,8  
005C14AC  pop         edi  
005C14AD  pop         esi  
005C14AE  pop         ebx  
005C14AF  pop         edx  
005C14B0  pop         eax  
005C14B1  mov         esp,ebp  
005C14B3  pop         ebp  
005C14B4  ret

​ 在函数体中实际操作部分只有调用Plus函数这么一句 Plus():call @ILT+265(_Plus) (5C110Eh),其余部分都是在为创建这个函数做准备工作:保存栈底,提升堆栈,保存现场,填充堆栈,完成函数内容,回复现场,恢复堆栈。

为函数需要创建空间 C语言函数反汇编 收尾工作 C语言函数反汇编 上面是主函数的完整过程。

下面是主函数中调用的空函数:

void Plus(){
005C13B0  push        ebp  
005C13B1  mov         ebp,esp  
005C13B3  sub         esp,0C0h  
005C13B9  push        ebx  
005C13BA  push        esi  
005C13BB  push        edi  
005C13BC  lea         edi,[ebp-0C0h]  
005C13C2  mov         ecx,30h  
005C13C7  mov         eax,0CCCCCCCCh  
005C13CC  rep stos    dword ptr es:[edi]  
	
}
005C13CE  pop         edi  
005C13CF  pop         esi  
005C13D0  pop         ebx  
005C13D1  mov         esp,ebp  
005C13D3  pop         ebp  
005C13D4  ret 

​ 其实空函数与我们的主函数是一样的。函数调用前和函数调用后是一摸一样。

推测出相同之处:调用函数的基本步骤是一样的。

​ 这里的主函数和主函数中调用的函数都没有写东西,所以看一下写了东西的函数是什么样。

有局部变量的函数:

#include "stdio.h"

int Plus1(){
	int x=1;
	int y=2;
	return x+y;
}

int main (){
	Plus1();
}

对应的汇编代码:

int main (){
007F1450  push        ebp  
007F1451  mov         ebp,esp  
007F1453  sub         esp,0C0h  
007F1459  push        ebx  
007F145A  push        esi  
007F145B  push        edi  
007F145C  lea         edi,[ebp-0C0h]  
007F1462  mov         ecx,30h  
007F1467  mov         eax,0CCCCCCCCh  
007F146C  rep stos    dword ptr es:[edi]  
	Plus1();
007F146E  call        @ILT+250(_Plus1) (7F10FFh)  
}
007F1473  xor         eax,eax  
007F1475  pop         edi  
007F1476  pop         esi  
007F1477  pop         ebx  
007F1478  add         esp,0C0h  
007F147E  cmp         ebp,esp  
007F1480  call        @ILT+305(__RTC_CheckEsp) (7F1136h)  
007F1485  mov         esp,ebp  
007F1487  pop         ebp  
007F1488  ret  

看主函数其实还是没有什么不同。但是跳转到Plus1这个函数时就发生了些不同。

int Plus1(){
//为函数需要创建空间
008713B0  push        ebp  
008713B1  mov         ebp,esp  
008713B3  sub         esp,0F0h  
008713B9  push        ebx  
008713BA  push        esi  
008713BB  push        edi  
008713BC  lea         edi,[ebp-0F0h]  
008713C2  mov         ecx,3Ch  
008713C7  mov         eax,0CCCCCCCCh  
008713CC  rep stos    dword ptr es:[edi]  
	int x=1;
//把1存入x代表的内存地址中
008713CE  mov         dword ptr [x],1  
	int y=2;
//把2存入y代表的内存地址中
008713D5  mov         dword ptr [y],2  
	return x+y;
//先将x这个地址的值给到eax这个寄存器中
008713DC  mov         eax,dword ptr [x]  
//再把y这个地址的值加到eax寄存器里面
008713DF  add         eax,dword ptr [y]  	//就这样就完成了两个局部变量相加的过程,结果被存到了eax中
}
//收尾工作
008713E2  pop         edi  
008713E3  pop         esi  
008713E4  pop         ebx  
008713E5  mov         esp,ebp  
008713E7  pop         ebp  
008713E8  ret  

C语言函数反汇编

其实这里并没有讲清楚局部变量x和y存放的位置。

C语言函数反汇编 所以我们打开了x32dbg。这里就很清楚的写出来了,局部变量x=1是存放在堆栈的[ebp-8]这个位置,局部变量y=2是存放在堆栈的[ebp-14]这个位置的。也就是栈底的上面。

C语言函数反汇编 ​ 缓冲区溢出。

带参数的局部变量:

#include "stdio.h"

int Plus2(int x , int y){

	return x+y;
}

int main (){
	Plus2(1,2);
}

对应的汇编代码

int main (){
//为函数需要创建空间
004313F0  push        ebp  
004313F1  mov         ebp,esp  
004313F3  sub         esp,0C0h  
004313F9  push        ebx  
004313FA  push        esi  
004313FB  push        edi  
004313FC  lea         edi,[ebp-0C0h]  
00431402  mov         ecx,30h  
00431407  mov         eax,0CCCCCCCCh  
0043140C  rep stos    dword ptr es:[edi]  
	Plus2(1,2);
//这里直接将1和2压入堆栈中
0043140E  push        2  
00431410  push        1  
00431412  call        @ILT+255(_Plus2) (431104h)  
//堆栈平衡,外平衡
00431417  add         esp,8  
}
//收尾工作
0043141A  xor         eax,eax  
0043141C  pop         edi  
0043141D  pop         esi  
0043141E  pop         ebx  
0043141F  add         esp,0C0h  
00431425  cmp         ebp,esp  
00431427  call        @ILT+305(__RTC_CheckEsp) (431136h)  
0043142C  mov         esp,ebp  
0043142E  pop         ebp  
0043142F  ret  

​ 我们可以看到这时主函数里面有了不同,它将参数直接压入堆栈中,然后再调用Plus2函数,最后还多了一个平衡堆栈的步骤。

int Plus2(int x , int y){
004313B0  push        ebp  
004313B1  mov         ebp,esp  
004313B3  sub         esp,0C0h  
004313B9  push        ebx  
004313BA  push        esi  
004313BB  push        edi  
004313BC  lea         edi,[ebp-0C0h]  
004313C2  mov         ecx,30h  
004313C7  mov         eax,0CCCCCCCCh  
004313CC  rep stos    dword ptr es:[edi]  

	return x+y;
//在eax中进行运算
004313CE  mov         eax,dword ptr [x]  
004313D1  add         eax,dword ptr [y]  
}
004313D4  pop         edi  
004313D5  pop         esi  
004313D6  pop         ebx  
004313D7  mov         esp,ebp  
004313D9  pop         ebp  
004313DA  ret  

C语言函数反汇编C语言函数反汇编 同样,只看编译器上是显示不出存储位置的。

C语言函数反汇编 同理,我们现在可以看到它是存放在堆栈的[ebp+8]和[ebp+c]这里面的。 C语言函数反汇编 堆栈平衡 C语言函数反汇编

带参且有局部变量的函数:

#include "stdio.h"

int Plus3(int x , int y){
	int a=1;
	int b=2;
	return x+y+a+b;
}

int main (){
	Plus3(1,2);
}

对应汇编代码:

​ 主函数部分:

int main (){
003E1400  push        ebp  
003E1401  mov         ebp,esp  
003E1403  sub         esp,0C0h  
003E1409  push        ebx  
003E140A  push        esi  
003E140B  push        edi  
003E140C  lea         edi,[ebp-0C0h]  
003E1412  mov         ecx,30h  
003E1417  mov         eax,0CCCCCCCCh  
003E141C  rep stos    dword ptr es:[edi]  
	Plus3(1,2);
003E141E  push        2  
003E1420  push        1  
003E1422  call        @ILT+255(_Plus3) (3E1104h)  
003E1427  add         esp,8  
}
003E142A  xor         eax,eax  
003E142C  pop         edi  
003E142D  pop         esi  
003E142E  pop         ebx  
003E142F  add         esp,0C0h  
003E1435  cmp         ebp,esp  
003E1437  call        @ILT+305(__RTC_CheckEsp) (3E1136h)  
003E143C  mov         esp,ebp  
003E143E  pop         ebp  
003E143F  ret  

​ 调用函数Plus3部分:

int Plus3(int x , int y){
003E13B0  push        ebp  
003E13B1  mov         ebp,esp  
003E13B3  sub         esp,0D8h  
003E13B9  push        ebx  
003E13BA  push        esi  
003E13BB  push        edi  
003E13BC  lea         edi,[ebp-0D8h]  
003E13C2  mov         ecx,36h  
003E13C7  mov         eax,0CCCCCCCCh  
003E13CC  rep stos    dword ptr es:[edi]  
	int a=1;
003E13CE  mov         dword ptr [a],1  
	int b=2;
003E13D5  mov         dword ptr [b],2  
	return x+y+a+b;
003E13DC  mov         eax,dword ptr [x]  
003E13DF  add         eax,dword ptr [y]  
003E13E2  add         eax,dword ptr [a]  
003E13E5  add         eax,dword ptr [b]  
}
003E13E8  pop         edi  
003E13E9  pop         esi  
003E13EA  pop         ebx  
003E13EB  mov         esp,ebp  
003E13ED  pop         ebp  
003E13EE  ret  

其实可以发现就是带参和带局部变量的结合,没有什么其他变化。