【博文推荐】如何获得C语言函数起始地址和返回地址

 本博文出自博客gmxydm 博主,有任何问题请进入博主页面互动讨论!
博文地址:http://5412097.blog./5402097/1641374
 

在反外挂系统中,经常会检测函数的返回地址,确认函数的返回地址在规定的范围之内,从而保证,游戏程序中的函数,不被外挂所调用。这种检查方式就涉及到一个基本的技术问题,如何获得函数的返回地址?

10年积累的成都做网站、网站建设经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站策划后付款的网站建设流程,更有茂名免费网站建设让你可以放心的选择与我们合作。

例如下面的***段代码:

 
 
  1. #include 
  2. int main() 
  3. { 
  4. getchar(); 
  5. return 0; 
  6. } 

非常简单的一段程序,那么我们如何获得该函数的起始地址和返回地址呢?起始地址获取非常容易,如下:

 
 
  1. #include 
  2. int main() 
  3. { 
  4. printf("%0x\n",main); 
  5. getchar(); 
  6. return 0; 
  7. } 

那么如何获得函数的返回地址呢?这个就相对来说比较困难。我们先看***段代码反汇编后的结果:

 
 
  1. #include 
  2. intmain() 
  3. { 
  4. 009919E0 push ebp 
  5. 009919E1 mov ebp,esp 
  6. 009919E3 sub esp,0C0h 
  7. 009919E9 push ebx 
  8. 009919EA push esi 
  9. 009919EB push edi 
  10. 009919EC lea edi,[ebp-0C0h] 
  11. 009919F2 mov ecx,30h 
  12. 009919F7 mov eax,0CCCCCCCCh 
  13. 009919FC rep stos dword ptr es:[edi] 
  14. getchar(); 
  15. 009919FE mov esi,esp 
  16. 00991A00 call dword ptr [__imp__getchar (9982B0h)] 
  17. 00991A06 cmp esi,esp 
  18. 00991A08 call @ILT+295(__RTC_CheckEsp) (99112Ch) 
  19. return 0; 
  20. 00991A0D xor eax,eax 
  21. } 
  22. 00991A0F pop edi 
  23. 00991A10 pop esi 
  24. 00991A11 pop ebx 
  25. 00991A12 add esp,0C0h 
  26. 00991A18 cmp ebp,esp 
  27. 00991A1A call @ILT+295(__RTC_CheckEsp) (99112Ch) 
  28. 00991A1F mov esp,ebp 
  29. 00991A21 pop ebp 
  30. 00991A22 ret

代码开始部分,先保存ebp的内容,然后将ESP的内容写入EBP:

 
 
  1. 009919E0 push ebp 
  2.  
  3. 009919E1 mov ebp,esp 

汇编指令call会做两件事情,其一,将call指令后面的一条指令的地址压入栈中,无条件跳转到call指令的调用地指处,开始执行子程序。

和call指令对应的ret指令,则开始执行call指令后面的一条指令。

那么,ret指令如何知道call指令后面一条指令的地址呢?因为call指令已经将这条指令压入到了栈中,所以ret指令可以找到call指令后的一条指令的地址。

既然ret指令可以找到call的返回地址,也就是call的下一条指令的地址,那么我们也可以找到!!!

main函数在执行前以及执行过程中,栈的分布如下:

通过以上几张图,我们可以清楚的看到,main函数的返回地址在[EBP+4]处。所以,获得main函数的返回地址的代码如下:

 
 
  1. #include 
  2. int main() 
  3. { 
  4. int re_addr; 
  5. __asm 
  6. { 
  7. mov eax,dword ptr [ebp+4] 
  8. mov re_addr,eax 
  9. } 
  10. printf("%0X\n",re_addr); 
  11. getchar(); 
  12. return 0; 
  13. } 

其中__tmainCRTStartup()函数调用了main函数,调用的汇编代码如下:

mainret = main(argc, argv, envp);

00B81926  mov        eax,dword ptr [envp (0B87140h)] 

00B8192B  push       eax 

00B8192C  mov        ecx,dword ptr [argv (0B87144h)] 

00B81932  push       ecx 

00B81933  mov        edx,dword ptr [argc (0B8713Ch)] 

00B81939  push       edx 

00B8193A call        @ILT+300(_main)(0B81131h) 

00B8193F  add        esp,0Ch 

00B81942  mov        dword ptr [mainret (0B87154h)],eax

可以看出,call main指令后的一条指令的地址为:00B8193F,而我们获得的main的返回地址如下:

B8193F

说明我们获得的结果正确。

对于其他函数的情况类似,下面笔者就把获得某个函数的返回值得功能,做成一个函数,提供给大家,如下:

 
 
  1. #include 
  2.  
  3. int Get_return_addr() 
  4. { 
  5. int re_addr; 
  6. __asm 
  7. { 
  8. mov eax,dword ptr [ebp] 
  9. mov ebx,dword ptr [eax+4] 
  10. mov re_addr,ebx 
  11. } 
  12. returnre_addr; 
  13.  
  14. } 
  15.  
  16. int main() 
  17. { 
  18. intre_addr=Get_return_addr(); 
  19. printf("%0X\n",re_addr); 
  20. getchar(); 
  21. return 0; 
  22. } 

Get_return_add函数中,为什么会多几条汇编指令呢?大家可以自行思考。


名称栏目:【博文推荐】如何获得C语言函数起始地址和返回地址
网站URL:http://www.dvlbo.com/article/ccodpep.html