今天继续学习汇编语言基础
__stdcall 是一种调用约定(Calling Convention),主要用于 Windows API 和 x86 架构。它规定了函数在调用时,参数如何传递、由谁负责清理栈空间以及函数名如何修饰。
下面让我们看看其特性
1. 参数传递方向:从右向左
当你调用一个 __stdcall 函数时,参数是按照代码中从右到左的顺序被压入栈(Stack)中的。
- 代码示例:
func(a, b, c) - 汇编指令顺序:
push cpush bpush acall func
2. 栈清理责任:被调用者(Callee)清理
这是 __stdcall 与 C 语言默认约定(__cdecl)最大的区别。
- 在
__cdecl中:调用者(Caller)在函数返回后,需要手动把栈指针移回去(例如执行add esp, 12)。 - 在
__stdcall中:被调用函数在内部执行ret n指令(n是参数总字节数)。这意味着函数执行完后,自己就把占用的参数空间“退还”给系统了。
3. 函数名修饰(Name Mangling)
在编译器底层,为了区分不同的调用约定,__stdcall 会对函数名进行特殊的处理。 通常规则是:_函数名@参数字节数。
例子:如果函数是
void __stdcall test(int a, int b),编译后的符号名可能是_test@8(因为两个 int 占 8 字节)。
这次主要学习的是性质2,__stdcall自动清理栈的原理
#include <stdio.h>
#include<iostream>
#include<iomanip>
//__stdcall为被调用者清理栈
//如这个函数用了两个参数,需要push两个参数
//stdcall则在出call时自动add esp,8
void __stdcall fun1(int a, int b) { //用汇编手动模拟编译器分配局部变量的过程
__asm {
sub esp, 8 //开辟8空间
mov[esp], 0x23 //放入临时值
mov[esp + 4], 0x34
push eax
push ebx
pop ebx
pop eax
add esp, 8 //释放
//实际上这些指令其实没对程序有任何影响
}
printf("%d%d\n", a, b);
}
void __stdcall fun2(int a, int b) { //演示如何利用__stdcall漏洞进行内存篡改
__asm {
mov eax, 5555 //5555放进ebp + 8
mov[ebp + 8], eax
push eax
mov eax, 4444 //4444放进ebp + 12
mov[ebp + 12], eax
pop eax
}
printf("%d%d\n", a, b); //这里已经把a,b的参数强行修改了,实现了逆向的破解功能
}
int main() {
printf("hellword\n");
__asm {
push 123
push 1234
call fun1
push 123
push 1234
call fun2
}
return 0;
}
最后输出为
hellword
1234123
55554444



Comments NOTHING