__declspec是微软的编译指示符,可以使用__declspec关键字来指定扩展属性。该扩展属性语法简化并标准化了对于C和C++语言的微软特定扩展。
declspec是declaration spec(声明说明),还是declaration specific的缩写?
__declspec (naked)
naked 属性,Microsoft:
For functions declared with the naked attribute, the compiler generates code without prolog and epilog code. You can use this feature to write your own prolog/epilog code sequences using inline assembler code. Naked functions are particularly useful in writing virtual device drivers. Note that the naked attribute is only valid on x86, and is not available on x64 or Itanium.
也就是若函数声明了naked这个属性,则编译器生成的代码中没有prolog and epilog code。(网上找到的资料翻译为“不包含任何注释或标记”,这翻译的是错误的!很容易误导人啊)(《IDA Pro代码破解解密》书中,prologue译为预处理,epilogue译为扫尾。)
但prolog and epilog code到底是什么?熟悉汇编的很容易弄清楚,如下:
观察VC6 Console Code (Debug):
void Test(int i) { i++; } int main(int argc, char* argv[]) { Test(2); return 0; } |
反汇编对Test的调用:
14: Test(2); 00401048 push 2 0040104A call @ILT+0(Test) (00401005) 0040104F add esp,4 15: return 0; 00401052 xor eax,eax 16: } |
反汇编Test代码:
6: void Test(int i) 7: { 0040D470 push ebp 0040D471 mov ebp,esp 0040D473 sub esp,40h 0040D476 push ebx 0040D477 push esi 0040D478 push edi 0040D479 lea edi,[ebp-40h] 0040D47C mov ecx,10h 0040D481 mov eax,0CCCCCCCCh 0040D486 rep stos dword ptr [edi] 8: i++; 0040D488 mov eax,dword ptr [ebp+8] 0040D48B add eax,1 0040D48E mov dword ptr [ebp+8],eax 9: } 0040D491 pop edi 0040D492 pop esi 0040D493 pop ebx 0040D494 mov esp,ebp 0040D496 pop ebp 0040D497 ret |
则绿色的部分则为prolog code,蓝色的部分为epilog code。
●prolog code: 这部分主要为保存原始栈指针esp,这部分是所有函数共有的。而该VC6的(Debug)Console程序,sub esp,40h该条指令为该函数预留了40h,即64bytes的临时存储空间,并初始化为cch。
●epilog code: 恢复原寄存器内容,很重要的是原esp,维持堆栈平衡。然后ret指令返回原调用函数指令处。
若将Test定义为_declspec (naked) Test(int ),观察反汇编代码:
调用_declspec (naked)Test的指令跟普通Test相同:
14: Test(2); 00401048 push 2 0040104A call @ILT+0(Test) (00401005) 0040104F add esp,4 15: return 0; 00401052 xor eax,eax 16: } |
6: _declspec (naked) void Test(int i) 7: { 00401020 mov eax,dword ptr [ebp+8] 00401023 add eax,1 00401026 mov dword ptr [ebp+8],eax ---Nosourcefile---------------------------------------------------------------------- 00401029 int 3 0040102A int 3 0040102B int 3 0040102C int 3 0040102D int 3 0040102E int 3 0040102F int 3 ---F:/Test3/Test3.cpp--------------------------------------------------------------- 8: 9: i++; 10: } 11: |
观察上面指定为_declspec (naked)属性的函数Test,编译器生成的汇编代码只有对于语句“i++;”的实现,前面没有prolog code,后面也没有epilog code。这就是意味着_declspec (naked)修饰的函数无法自动返回到函数调用处,得编码者手动添加代码,才能正确返回。这也就给了我们更加灵活的编码空间。
■总结
●_declspec (naked)关键字修饰的函数,编译器生成的汇编代码中,只生成了对应函数代码的具体实现,并没有添加任何保存栈指针esp等prolog code,也没有添加任何函数结束后恢复相关寄存器、维护栈平衡、ret等epilog code。
若指定我们的函数为naked属性,则在函数中得通过内嵌汇编代码手动编写prolog code和epilog code来保存现场和恢复现场步骤。
●naked属性对于驱动开发是很有用的。如在Kernel inline Hook中,Hook Rountine的头5个字节,实现对OriginalRountine的调用上。
●naked属性只适用于X86系统上。
相关链接:
http://msdn.microsoft.com/en-us/library/dabb5z75(v=vs.80).aspx