行内联汇编
_asm pushad
_asm mov eax,0x12345678;
块内联汇编
_asm
{
Pushad;
Popad;
}
尝试用内联汇编弹出一个提示框:
MessageBoxA(0,0,0,0);
char str[] = "HelloWorld";
_asm {
push 0;
push 0;
lea eax, [str];
push eax;
push 0;
call MessageBoxA;
}
传入函数前首先应参数入栈,最先入栈的为最后的函数形参,想要push一个字符串进去,我们不能简单的push str,我们应该把字符串的地址传入函数参数。
- lea eax,[str] :把str的地址给到eax
- push eax:eax入栈,保存的是字符串的地址。
裸函数
内容为空的函数
void func()
{
}
我们一般认为它是空的,但是它真的是空的吗?
不是的。
我们查看其反汇编
void func()
{
//保存栈底的地址
00EB1800 push ebp
//提升堆栈空间
00EB1801 mov ebp,esp
00EB1803 sub esp,0C0h
//保存临时变量
00EB1809 push ebx
00EB180A push esi
00EB180B push edi
//填充堆栈空间
00EB180C mov edi,ebp
00EB180E xor ecx,ecx
00EB1810 mov eax,0CCCCCCCCh
00EB1815 rep stos dword ptr es:[edi]
//安全检查
00EB1817 mov ecx,offset _C300FBC1_源@cpp (0EBC0F2h)
00EB181C call @__CheckForDebuggerJustMyCode@4 (0EB1316h)
}
//恢复临时变量
00EB1821 pop edi
00EB1822 pop esi
00EB1823 pop ebx
//降低堆栈空间
00EB1824 add esp,0C0h
00EB182A cmp ebp,esp
00EB182C call __RTC_CheckEsp (0EB123Ah)
00EB1831 mov esp,ebp
//弹出一开始的栈底
00EB1833 pop ebp
00EB1834 ret
我们可以在反汇编层面把一个函数分为三部分:
- 第一部分,进入函数,提升堆栈空间,保存临时变量,并且CC填充堆栈
- 第二部分,函数的内容,包括我们所实现的行为。
- 第三部分,即将返回主函数,降低堆栈的空间,恢复ebp及临时变量。
任何函数都具有这些操作,所以说一个什么都没有的函数其实并不是空的。
所以,我们能否自己实现一个真正的裸函数? 即这个函数里连这些汇编指令都没有。
void _declspec(naked) func()
{
}
- _declspec(naked):添加此表示函数为一个真正的裸函数,它里面什么都没有,只有一堆CC填充对于一个裸函数而言,就是编译器不会为这个函数生成代码,想用汇编怎么写就怎么写,那也就意味着,函数的开辟栈针,平衡堆栈,返回等都是由我们自己来实现。
我们在裸函数里自己实现一个小功能?
弹出一个提示框。
void _declspec(naked) func(char* name)
{
_asm {
push ebp;
mov ebp, esp;
sub esp, 0x20;
push 0;
push 0;
mov eax, [ebp + 8];
push eax;
push 0;
call MessageBoxA;
add esp, 0x20;
mov esp, ebp;
pop ebp;
ret 0x4;
}
}
int main()
{
char str[] = "HelloWorld";
_asm {
lea eax, [str];
push eax;
call func;
}
return 0;
}
我们省略了填充的过程,运行如下;