• 大神洗礼第四讲——函数相关及编程技巧


    更多干货请移步我的公众号「aCloudDeveloper」,专注技术干货分享,期待与你相遇。

    Author:bakari       Date:2012.11.2

    1、参数传递问题:

    < 1 >、堆栈传参

    < 2 >、寄存器传参(利用通用寄存器进行函数参数传递的方法)

    < 3 >、全局变量或静态变量传参

    2、 Call Convention(函数调用约定)

    < 1 >、_cdecl

    a、 参数从右向左压入堆栈

    b、 函数被调用者修改堆栈

    c、 在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见.

    d、 C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。

    < 2 >、_stdcall

    a、 压栈方式与_cdecl一样,与之不一样的是堆栈的平衡不是由函数调用者完成,而是自身完成,在退出时自己清空堆栈。

    b、 此种方式在函数返回是以 ret 8 指令来平衡堆栈,此处:ret 8 = add esp , 8。

    < 3 >、上两种方式最为常用,此外还有fastcall ,thiscall, naked call,_pascal等 _pascal 入栈方式是从左到右。

    下面通过一些例子来深入理解。

    3、 跟踪汇编代码看函数参数的调用机制 我们看这样一个简单的函数,

    编译器翻译的汇编指令如下:

    < 1 >、void Test1(){} 

     1 void Test1()
     2  {
     3  013516E0  push        ebp  
     4  013516E1  mov         ebp,esp 
     5  013516E3  sub         esp,0C0h 
     6  013516E9  push        ebx  
     7  013516EA  push        esi  
     8  013516EB  push        edi  
     9  013516EC  lea         edi,[ebp-0C0h] 
    10  013516F2  mov         ecx,30h ;每次移四个字节,30h * 4 = 0C0h
    11  013516F7  mov         eax,0CCCCCCCCh 
    12  013516FC  rep stos    dword ptr es:[edi] ;将开辟的内存赋值为cc
    13  }
    14  013516FE  pop         edi  
    15  013516FF  pop         esi  
    16  01351700  pop         ebx  
    17  01351701  mov         esp,ebp 
    18  01351703  pop         ebp  
    19  01351704  ret    ;--->编译器默认为_cdecl   
    20  

     据此画出内存布局图为:

    < 2 >、有参数的情况

     1 int _cdecl Test (int i, int j)
     2  {
     3      return j + i ++;
     4  }
     5  
     6  int _cdecl Test (int i, int j)
     7  {
     8  01311690  push        ebp  
     9  01311691  mov         ebp,esp 
    10  01311693  sub         esp,0C4h 
    11  01311699  push        ebx  
    12  0131169A  push        esi  
    13  0131169B  push        edi  
    14  0131169C  lea         edi,[ebp-0C4h] 
    15  013116A2  mov         ecx,31h ;31h * 4 = 0C4h
    16  013116A7  mov         eax,0CCCCCCCCh 
    17  013116AC  rep stos    dword ptr es:[edi] ;和上面无参的类型一样
    18      return j + i ++;
    19  013116AE  mov         eax,dword ptr [j] 
    20  013116B1  add         eax,dword ptr [i] 
    21  013116B4  mov         dword ptr [ebp-0C4h],eax 
    22  013116BA  mov         ecx,dword ptr [i] 
    23  013116BD  add         ecx,1 
    24  013116C0  mov         dword ptr [i],ecx 
    25  013116C3  mov         eax,dword ptr [ebp-0C4h] 
    26  }
    27  013116C9  pop         edi  
    28  013116CA  pop         esi  
    29  013116CB  pop         ebx  
    30  013116CC  mov         esp,ebp 
    31  013116CE  pop         ebp  
    32  013116CF  ret    
    33  
    34  00D21D88  push        2    ;在调用函数之前先将参数从右往左压入堆栈
    35  00D21D8A  mov         eax,dword ptr [i] 
    36  00D21D8D  push        eax  
    37  00D21D8E  call        Test (0D211F4h) ;函数调用
    38  00D21D93  add         esp,8           ;平衡堆栈
    39  00D21D96  mov         dword ptr [i],eax 
    40  

    内存布布局如下:

    4、 编写裸函数(不让系统加汇编的代码,而是人为的加上去)

    比如:

     1 int  _declspec (naked) MyFunc()
     2  {
     3      _asm {
     4          push ebp
     5          mov ebp, esp
     6          sub esp, 0C0h
     7          push ebx
     8          push esi
     9          push edi
    10          lea edi, dword ptr [ebp - 0C0h]
    11          mov ecx, 30h
    12          mov eax, 0cccccccch
    13          rep stos dword ptr [edi]
    14      }
    15  
    16      _asm {
    17          mov eax, 8      ;返回值为8
    18      }
    19  
    20      _asm {
    21          pop edi
    22          pop esi
    23          pop ebx
    24          mov esp, ebp  ;平衡堆栈
    25          pop ebp
    26          ret            ;记得一定要返回
    27      }
    28  }
    29  

    在main函数调用的结果printf("%d\n", MyFunc());

    练习:

    < 1 >、无参数的情况(不在堆栈上展开)

     1 void _declspec (naked) BlankFunc(void)
     2  {
     3      _asm {
     4          push ebp
     5          mov ebp, esp
     6          pushad
     7          popad
     8          mov esp, ebp
     9          pop ebp
    10      }
    11  }
    12  

    < 2 >、有参数的情况(在堆栈上展开)

     1 int Nest (int a, int b)
     2  {
     3      int nValue_1 = a;
     4      int nValue_2 = b;
     5      return nValue_1 + nValue_2;
     6  }
     7  int _declspec (naked) myFunc(int a, int b)
     8  {
     9      _asm {
    10          push ebp
    11          mov ebp, esp
    12          sub esp, 8
    13          push edi
    14          push ebx
    15          push ecx
    16          lea edi, dword ptr [ebp - 8]
    17          mov ecx, 2
    18          mov eax, 0cccccccch
    19          rep stos dword ptr [edi]
    20      }
    21      _asm {
    22          mov eax, dword ptr[ebp + 8]  //有了上面的知识,这里就不难理解了。
    23          mov dword ptr [ebp - 4], eax
    24          mov ebx, dword ptr[ebp + 12] 
    25          mov dword ptr [ebp - 8], ebx
    26          add eax, ebx
    27      }
    28      _asm {
    29          pop edi
    30          pop ebx
    31          pop ecx
    32          mov esp, ebp
    33          pop ebp
    34          ret
    35      }
    36  }
    37  
    stay hungry stay foolish ----jobs 希望多多烧香!
  • 相关阅读:
    kafka的概念
    java的多线程:java安全问题产生的原因与JMM的关系
    java的多线程:线程安全问题
    web自动化之键盘操作
    获取cookie里面的sessionid
    Python连接mysql数据库
    Python 发送Email
    Python日志模块
    openxl模块从excel里面读取数据
    Python读取配置文件
  • 原文地址:https://www.cnblogs.com/bakari/p/2751980.html
Copyright © 2020-2023  润新知