__declspec(naked)和__asm编写实践总结
__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编写实践总结相关推荐
- __declspec(naked)详解
__declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 注意点: [cpp] view plaincopy void __declspec(na ...
- C++基础巩固__declspec(naked)
From: http://blog.csdn.net/hgy413/article/details/7921776 __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不 ...
- void __declspec(naked)解释
__declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 void __declspec(naked) mdt { __asm pushad p ...
- 裸函数 __declspec(naked)
在C语言转化成汇编的过程中,编译器会自动处理堆栈,比如以下代码,即使add函数里面一条语句都没有,但是编译器在编译过程中 还是生成了很多指令(00F11380 - 00F1139C) 用于处理堆栈 v ...
- __declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码
__declspec(naked)是用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码 extern "C" void __declspec(naked) ...
- 裸函数 __declspec(naked),C语言是怎么变成汇编的,用裸函数加汇编实现一个最简单的加法函数
裸函数: 普通函数会自动生成实现堆栈提升.堆栈的缓存区建立.堆栈平衡和函数返回的汇编代码,裸函数不会自动生成任何代码,全部要自己手动写汇编代码. 裸函数定义:__declspec(naked) 加汇编 ...
- YJX基础44 __declspec(naked)
(在写驱动时)如果函数前不加__declspec(naked) 编译时编译器会自作主张补上栈移动和return,这样会破坏栈平衡 void __declspec(naked) NTSTATUS _My ...
- 【Win32汇编】__declspec(naked)裸函数
使用 __declspec(naked) 定义的函数,编译器只会负责参数压栈.执行CALL指令,和释放参数的内存(堆栈平衡),除此之外啥也没有,比如提升堆栈,寄存器的保存和恢复,或者是函数返回,这些都 ...
- dart系列之:手写Library,Library编写实践
文章目录 简介 使用part和part of src中的文件 package中的lib文件 总结 简介 Library是dart用来组织代码的一种非常有用的方式,通过定义不同的Library,可以将非 ...
最新文章
- 为什么深度学习是非参数的?
- Oracle RAC错误之--oifcfg错误案例
- 给Tomcat打开远程debug端口
- 怎么把线稿提取出来_如何快速提取漫画线稿?【漫画技巧】
- [hihoCoder] 第五十周: 欧拉路·二
- ffmpeg 花屏的问题
- mysql系统属性,mysql
- 动手学深度学习Pytorch Task06
- linux 运行有道词典,ubuntu安装有道词典的教程详解
- dpkg制作deb包详解
- HTML5 终于定稿,八年后我们再一次谈谈怎么改变世界
- 微信分享获得积分 jquery微信分享获得积分 原理
- 智能重卡产品研发的三种逻辑博弈
- buctoj-python 2022.5.19
- 企业微信获取客户群里用户的unionid;企业微信获取客户详情
- 我与博友们分享我的工作经验
- .NET Core Onvif协议C#教程系列之XiaoFeng.Onvif组件库
- 攻防世界-看雪看雪看雪
- view_video.php,Android_Android使用VideoView播放本地视频和网络视频的方法,1、效果展示2、布局文件- phpStudy...
- upload-labs 全1-21关 附详细解析(文件上传漏洞)