文号
t43442
阅读
5414
回复
10
收藏
3
【C  】还我们一个空白的函数!使用NAKED CALL
2012/04/20
IP:浙江
TAG:naked call|naked function|VC|空函数
今天继续调试代码,发现一个奇怪的问题:寄存器里的值在call后会立刻改变,让人百思不得其解,于是只好使用反汇编,发现VC++居然在不知不觉中给一个void型且未使用任何变量的函数添加了一些指令:

0122B2D5 8B F4                mov         esi,esp  
0122B2D7 68 A0 10 2B 01       push        offset pTYPE (12B10A0h)  
0122B2DC 6A 40                push        40h  
0122B2DE 6A 10                push        10h  
0122B2E0 8B 45 F8             mov         eax,dword ptr [pintcache]  
0122B2E3 50                   push        eax  
0122B2E4 FF 15 10 92 2C 01    call        dword ptr [__imp__VirtualProtect@16 (12C9210h)]  
0122B2EA 3B F4                cmp         esi,esp  
0122B2EC E8 D2 D1 FF FF       call        @ILT+5310(__RTC_CheckEsp) (12284C3h
__asm{...}

难怪,寄存器已经被动过了,当然不对了。
好在MS设计的编译器不是太傻,留了后路,可以使用naked function(naked call)调用
首先得在程序开头加上一句:

#define Naked __declspec( naked )

之后,修改函数定义,在之前加上__declspec( naked )
如
void vmload(){...}


__declspec( naked )void vmload(){...}

注意,必须在函数定义处使用__declspec( naked ),不能再声明处。
好了,这下函数开头处就是空白的。
不过要注意,加上__declspec( naked )后得自己进行一些工作,处理堆栈。
另外return;也不能使用,得自己加上ret。
ENDL
phpskycn作者
14年8个月前IP:未同步
389002
1楼
Re:【C  】还我们一个空白的函数!使用NAKED CALL
引用
评论
phpskycn作者
14年8个月前IP:未同步
390548
2楼
再来看看编译器生成的代码做了什么
首先
mov esi,esp
保存了栈顶地址
之后把变量pTYPE的地址压入了栈,顺便还压进两个数0x40  0x10,这里明显可以优化,两个push byte完全可以用push dword替代。
之后取了另外一个变量的值并入栈
还调用了一个内存管理函数
最后又保存了esp
很奇怪的是后面部分似乎没用的上面涉及的变量。。。。
难怪不对了,寄存器都动过了还没还原
引用
评论
14年8个月前IP:未同步
390570
3楼
玩这是不是有点无聊?
引用
评论
phpskycn作者
14年8个月前IP:未同步
391659
4楼
回 3楼(婺源寻芳) 的帖子
这个是实际需要
该函数的参数完全以寄存器传递。。。。得保证其未被改动
引用
评论
14年8个月前IP:未同步
391888
5楼
Re:回 3楼(婺源寻芳) 的帖子
引用第4楼phpskycn于2012-04-29 22:29发表的 回 3楼(婺源寻芳) 的帖子 :
这个是实际需要
该函数的参数完全以寄存器传递。。。。得保证其未被改动

楼主能否从汇编的层面举例说明,函数的调用不需要使用寄存器?
函数调用返回后,预期以外的寄存器值发生了变化,只能说函数本身有缺陷。
当然也可能是写代码的劳工并不清楚该函数会影响哪些内容而莫名惊诧。
引用
评论
phpskycn作者
14年8个月前IP:未同步
392055
6楼
回 5楼(okingkoom) 的帖子
有一种情况叫寄存器的值可抛弃。
后面用不着了,改变一下无妨
like:
;1
movzx edx,al
;2
mov edx,pomap[edx *TYPE int]
;3
call edx
3时edx中的数据已经完成了使命,再也用不到了。
部分寄存器就是当缓存用的
陷在C++编译器中后是无可救药的。。。
引用
评论
phpskycn作者
14年8个月前IP:未同步
392058
7楼
前面已经说得很明白了,编译器自己生成了代码用于平衡堆栈,关函数毛事?
老老实实用C++编写当然没事,因为你不直接操作寄存器。
加上ASM后就不同了
引用
评论
14年8个月前IP:未同步
392070
8楼
回 7楼(phpskycn) 的帖子
正常写代码,用到任何寄存器,函数开头都要先push,结束要pop,考虑到堆栈大小,可能有调用函数层数的限制。
何来惊诧?
倒是很诧异楼主为何惊诧。
引用
评论
phpskycn作者
14年8个月前IP:未同步
392076
9楼
回 8楼(okingkoom) 的帖子
这种行为就是在浪费时间。
寄存器之后有用的保留,无用的抛弃。或者干脆不动那些需要保留的。
具体的策略由需求决定,比如处理顶楼说的问题时后面的函数需要做的基本是处理eax中的数据之后写到特定位置中。写完后eax的里值的数据后面再也用不到了,保留它作什么?
引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的