__cdecl 和 __stdcall 压栈参数顺序是一致的,但平衡堆栈方式不一样。

__cdecl调用函数方式是调用者,即函数外部平衡堆栈,一般是在函数外部调用add esp, xxxx,函数内部只需要ret返回就行

__stdcall调用函数方式是被调用,即函数内部平衡堆栈,一般在函数内部结束的时候调用ret xxx或者add esp,xxxx ; ret

在vs2005里面C++工程默认都是__cdecl,所以需要外部平衡堆栈

看一个实例,本来__asm里面本来原意会去取函数参数,并打印出来

//__declspec(naked) void TestMy(int c, int d) { __noop("good morning"); printf("hi/n"); static int vEsp, argc1, argc2; __asm { push eax mov eax, [esp + 0x04] mov vEsp, eax mov eax, [esp + 0x08] mov argc1, eax mov eax,[esp + 0x0C] mov argc2, eax pop eax } printf("%x -- %d -- %d /n",vEsp, argc1, argc2); //__asm //{ // ret //} } int _tmain(int argc, _TCHAR* argv[]) { int argc1 = 1; int argc2 = 2; TestMy(argc1, argc2); system("pause"); return 0; }

但经过编译过后,汇编代码如下:

Release版本

00401000 > .  56              push esi                                                            ; {
00401001   .  8B35 A4204000   mov esi,dword ptr ds:[<&MSVCR80.printf>]  
00401007   .  68 F4204000     push huibianT.004020F4
0040100C   .  FFD6            call esi
0040100E   .  83C4 04         add esp,4
00401011   .  50              push eax
00401012   .  8B4424 04       mov eax,dword ptr ss:[esp+4]
00401016   .  A3 7C334000     mov dword ptr ds:[__native_startup_lockonement],eax
0040101B   .  8B4424 08       mov eax,dword ptr ss:[esp+8]
0040101F   .  A3 78334000     mov dword ptr ds:[__native_startup_stateeement],eax
00401024   .  8B4424 0C       mov eax,dword ptr ss:[esp+C]
00401028   .  A3 74334000     mov dword ptr ds:[_adjust_fdivlestatussonement],eax
0040102D   .  58              pop eax
0040102E   .  A1 74334000     mov eax,dword ptr ds:[_adjust_fdivlestatussonement]
00401033   .  8B0D 78334000   mov ecx,dword ptr ds:[__native_startup_stateeement]
00401039   .  8B15 7C334000   mov edx,dword ptr ds:[__native_startup_lockonement]
0040103F   .  50              push eax
00401040   .  51              push ecx
00401041   .  52              push edx
00401042   .  68 F8204000     push huibianT.004020F8
00401047   .  FFD6            call esi
00401049   .  68 10214000     push huibianT.00402110

0040104E   .  FF15 9C204000   call dword ptr ds:[<&MSVCR80.system>]
00401054   .  83C4 14         add esp,14
00401057   .  33C0            xor eax,eax

00401059 > .  5E              pop esi

0040105A   .  C3              retn

Release版本优化过后函数参数不知去向。

而Debug版本的TestMy函数内部的esp已经早就破坏了,[esp+0x08]已经不再表示第一个参数。

00411449    53                push ebx
0041144A    56                push esi
0041144B    57                push edi
0041144C    8DBD 40FFFFFF     lea edi,dword ptr ss:[ebp-C0]
00411452    B9 30000000       mov ecx,30
00411457    B8 CCCCCCCC       mov eax,CCCCCCCC
0041145C    F3:AB             rep stos dword ptr es:[edi]
0041145E    8BF4              mov esi,esp
00411460    68 40574100       push huibianT.00415740
00411465    FF15 C8824100     call dword ptr ds:[<&MSVCR80D.printf>]
0041146B    83C4 04           add esp,4
0041146E    3BF4              cmp esi,esp
00411470    E8 DAFCFFFF       call huibianT.0041114F
00411475    50                push eax
00411476    8B4424 04         mov eax,dword ptr ss:[esp+4]
0041147A    A3 EC744100       mov dword ptr ds:[vEsp2ggerListeningIPTORWe>::NativeDll::ProcessVer>
0041147F    8B4424 08         mov eax,dword ptr ss:[esp+8]
00411483    A3 E8744100       mov dword ptr ds:[argc1ggerListeningIPTORWe>::NativeDll::ProcessVer>
00411488    8B4424 0C         mov eax,dword ptr ss:[esp+C]                                       
0041148C    A3 E4744100       mov dword ptr ds:[argc2ggerListeningIPTORWe>::NativeDll::ProcessVer>
00411491    58                pop eax
00411492    8BF4              mov esi,esp
00411494    A1 E4744100       mov eax,dword ptr ds:[argc2ggerListeningIPTORWe>::NativeDll::Proces>
00411499    50                push eax
0041149A    8B0D E8744100     mov ecx,dword ptr ds:[argc1ggerListeningIPTORWe>::NativeDll::Proces>
04114A0    51                push ecx
004114A1    8B15 EC744100     mov edx,dword ptr ds:[vEsp2ggerListeningIPTORWe>::NativeDll::Proces>
004114A7    52                push edx
004114A8    68 A8574100       push huibianT.004157A8
004114AD    FF15 C8824100     call dword ptr ds:[<&MSVCR80D.printf>]
004114B3    83C4 10           add esp,10
004114B6    3BF4              cmp esi,esp
004114B8    E8 92FCFFFF       call huibianT.0041114F
004114BD    5F                pop edi
004114BE    5E                pop esi
004114BF    5B                pop ebx
004114C0    81C4 C0000000     add esp,0C0
004114C6    3BEC              cmp ebp,esp
004114C8    E8 82FCFFFF       call huibianT.0041114F
004114CD    8BE5              mov esp,ebp
004114CF    5D                pop ebp
004114D0    C3                retn

当使用__declspec(naked)调用约定的时候,如下面

__declspec(naked) void TestMy(int c, int d) { __noop("good morning"); printf("hi/n"); int vEsp, argc1, argc2; __asm { push eax mov eax, [esp + 0x04] mov vEsp, eax mov eax, [esp + 0x08] mov argc1, eax mov eax,[esp + 0x0C] mov argc2, eax pop eax } printf("%x -- %d -- %d /n",vEsp, argc1, argc2); __asm { ret } } int _tmain(int argc, _TCHAR* argv[]) { int argc1 = 1; int argc2 = 2; TestMy(argc1, argc2); system("pause"); return 0; }

需要注意的是使用这样约定调用的时候,函数内部不会帮你处理堆栈,需要你自己来处理,也就是说不会帮你ret,需要你自己来操作。但可以内部调用其他函数,而且会帮你的其他函数平衡好堆栈,即ebp和esp不会因为操作这些函数而改变,只有当你用__asm里面进行对其进行操作,才会改变。因为默认是__cdecl,所以我处理返回是ret。 另外我这里故意将内部临时变量从static int 变为 int,发现ebp和esp也并没有做改变。汇编代码如下:

Debug版本

00411440 >  8BF4              mov esi,esp                                        ; {
00411442    68 40574100       push huibianT.00415740
00411447    FF15 C8824100     call dword ptr ds:[<&MSVCR80D.printf>]
0041144D    83C4 04           add esp,4
00411450    3BF4              cmp esi,esp
00411452    E8 F8FCFFFF       call huibianT.0041114F
00411457    50                push eax                                                            ; push eax
00411458    8B4424 04         mov eax,dword ptr ss:[esp+4]                     ; mov eax, [esp + 0x04]
0041145C    8945 F8           mov dword ptr ss:[ebp-8],eax                       ; mov vEsp, eax
0041145F    8B4424 08         mov eax,dword ptr ss:[esp+8]                      ; mov eax, [esp + 0x08]
00411463    8945 EC           mov dword ptr ss:[ebp-14],eax                      ; mov argc1, eax
00411466    8B4424 0C         mov eax,dword ptr ss:[esp+C]                      ; mov eax,[esp + 0x0C]
0041146A    8945 E0           mov dword ptr ss:[ebp-20],eax                      ; mov argc2, eax
0041146D    58                pop eax                                                               ; pop eax
0041146E    8BF4              mov esi,esp                                                 
00411470    8B45 E0           mov eax,dword ptr ss:[ebp-20]
00411473    50                push eax
00411474    8B4D EC           mov ecx,dword ptr ss:[ebp-14]
00411477    51                push ecx
00411478    8B55 F8           mov edx,dword ptr ss:[ebp-8]
0041147B    52                push edx
0041147C    68 A8574100       push huibianT.004157A8
00411481    FF15 C8824100     call dword ptr ds:[<&MSVCR80D.printf>]
00411487    83C4 10           add esp,10
0041148A    3BF4              cmp esi,esp
0041148C    E8 BEFCFFFF       call huibianT.0041114F
00411491    C3                retn

当我想对函数内部的int变量赋值的时候,发现会报错,看来这种调用约定是严格保护了ebp和esp的。

那么Release版本优化会不会改变ebp和esp的值呢

00401000 >/$  8B35 A4204000   mov esi,dword ptr ds:[<&MSVCR80.printf>]                            
00401006  |.  68 F4204000     push huibianT.004020F4
0040100B  |.  FFD6            call esi
0040100D  |.  83C4 04         add esp,4
00401010  |.  50              push eax                                                                   ; push eax
00401011  |.  8B4424 04       mov eax,dword ptr ss:[esp+4]                                        ; mov eax, [esp + 0x04]
00401015  |.  8945 F8         mov [local.2],eax                                                   ; mov vEsp, eax
00401018  |.  8B4424 08       mov eax,dword ptr ss:[esp+8]                                        ; mov eax, [esp + 0x08]
0040101C  |.  8945 FC         mov [local.1],eax                                                   ; mov argc1, eax
0040101F  |.  8B4424 0C       mov eax,dword ptr ss:[esp+C]                                        ; mov eax,[esp + 0x0C]
00401023  |.  8945 F4         mov [local.3],eax                                                   ; mov argc2, eax
00401026  |.  58              pop eax                                                                     ; pop eax
00401027  |.  8B45 F4         mov eax,[local.3]                                                   ; printf("%x  --   %d  --  %d /n",vEsp, argc1, argc2);
0040102A  |.  8B4D FC         mov ecx,[local.1]
0040102D  |.  8B55 F8         mov edx,[local.2]
00401030  |.  50              push eax
00401031  |.  51              push ecx
00401032  |.  52              push edx
00401033  |.  68 F8204000     push huibianT.004020F8
00401038  |.  FFD6            call esi
0040103A  |.  83C4 10         add esp,10
0040103D  /.  C3              retn

可以看到ebp和esp都保护得相当完美。

现在基本可以得到结论:

在没有__declspec(naked)调用约定的函数内部操作esp和ebp是不安全的。

而在有__declspec(naked)需要自己维护堆栈,但调用其他自己的函数,堆栈不会受影响

__declspec(naked)和__asm编写实践总结相关推荐

  1. __declspec(naked)详解

    __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 注意点: [cpp] view plaincopy void __declspec(na ...

  2. C++基础巩固__declspec(naked)

    From: http://blog.csdn.net/hgy413/article/details/7921776 __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不 ...

  3. void __declspec(naked)解释

    __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 void __declspec(naked)  mdt { __asm pushad p ...

  4. 裸函数 __declspec(naked)

    在C语言转化成汇编的过程中,编译器会自动处理堆栈,比如以下代码,即使add函数里面一条语句都没有,但是编译器在编译过程中 还是生成了很多指令(00F11380 - 00F1139C) 用于处理堆栈 v ...

  5. __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码

    __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 extern "C" void __declspec(naked) ...

  6. 裸函数 __declspec(naked),C语言是怎么变成汇编的,用裸函数加汇编实现一个最简单的加法函数

    裸函数: 普通函数会自动生成实现堆栈提升.堆栈的缓存区建立.堆栈平衡和函数返回的汇编代码,裸函数不会自动生成任何代码,全部要自己手动写汇编代码. 裸函数定义:__declspec(naked) 加汇编 ...

  7. YJX基础44 __declspec(naked)

    (在写驱动时)如果函数前不加__declspec(naked) 编译时编译器会自作主张补上栈移动和return,这样会破坏栈平衡 void __declspec(naked) NTSTATUS _My ...

  8. 【Win32汇编】__declspec(naked)裸函数

    使用 __declspec(naked) 定义的函数,编译器只会负责参数压栈.执行CALL指令,和释放参数的内存(堆栈平衡),除此之外啥也没有,比如提升堆栈,寄存器的保存和恢复,或者是函数返回,这些都 ...

  9. dart系列之:手写Library,Library编写实践

    文章目录 简介 使用part和part of src中的文件 package中的lib文件 总结 简介 Library是dart用来组织代码的一种非常有用的方式,通过定义不同的Library,可以将非 ...

最新文章

  1. 为什么深度学习是非参数的?
  2. Oracle RAC错误之--oifcfg错误案例
  3. 给Tomcat打开远程debug端口
  4. 怎么把线稿提取出来_如何快速提取漫画线稿?【漫画技巧】
  5. [hihoCoder] 第五十周: 欧拉路·二
  6. ffmpeg 花屏的问题
  7. mysql系统属性,mysql
  8. 动手学深度学习Pytorch Task06
  9. linux 运行有道词典,ubuntu安装有道词典的教程详解
  10. dpkg制作deb包详解
  11. HTML5 终于定稿,八年后我们再一次谈谈怎么改变世界
  12. 微信分享获得积分 jquery微信分享获得积分 原理
  13. 智能重卡产品研发的三种逻辑博弈
  14. buctoj-python 2022.5.19
  15. 企业微信获取客户群里用户的unionid;企业微信获取客户详情
  16. 我与博友们分享我的工作经验
  17. .NET Core Onvif协议C#教程系列之XiaoFeng.Onvif组件库
  18. 攻防世界-看雪看雪看雪
  19. view_video.php,Android_Android使用VideoView播放本地视频和网络视频的方法,1、效果展示2、布局文件- phpStudy...
  20. upload-labs 全1-21关 附详细解析(文件上传漏洞)

热门文章

  1. 问题杂记,不定时更新
  2. python模拟http请求
  3. spoj839 Optimal Marks(最小割,dinic)
  4. Elasticsearch 5.6.5 安装head插件
  5. A. PHP文件运行原理
  6. Tomcat和Weblogic部署纯html文件
  7. 关于直播学习笔记-005-nginx-rtmp-win32在Win10上使用
  8. 运行yum时出现错误,缺失libsasl2.so.2文件
  9. ubuntu下搭建android开发环境(转载)
  10. Android应用程序变量