逆向:windows堆栈平衡问题
常见的调用约定
调用约定 | 参数压栈顺序 | 平衡堆栈 |
---|---|---|
__cdecl | 从右至左 | 调用者平衡(外平栈) |
__stdcall | 从右至左 | 自身清理堆栈(内平栈) |
__fastcall | ecx/edx传递前两个参数,剩下参数从右至左入栈 | 自身清理堆栈(内平栈) |
__cdecl
#include <stdio.h>
//声明一个裸函数,编译器不对其生成汇编代码
int __cdecl fun(int a, int b){
return a + b;
}
int main(){
fun(1, 2);
return 0;
}
调用处汇编代码
函数内汇编代码
总结
内部代码中没有堆栈平衡的操作,平衡堆栈只在调用处
__stdcall
#include <stdio.h>
//声明一个裸函数,编译器不对其生成汇编代码
int __stdcall fun(int a, int b){
return a + b;
}
int main(){
fun(1, 2);
return 0;
}
调用处汇编代码
函数内汇编代码
总结
在调用中进行堆栈平衡,因此又被称作:内平栈
__fastcall
#include <stdio.h>
//声明一个裸函数,编译器不对其生成汇编代码
int __fastcall fun(int a, int b){
return a + b;
}
int main(){
fun(1, 2);
return 0;
}
调用处汇编代码(参数少于等于两个)
函数内汇编代码(参数少于等于两个)
#include <stdio.h>
//声明一个裸函数,编译器不对其生成汇编代码
int __fastcall fun(int a, int b,int c){
return a + b + c;
}
int main(){
fun(1, 2, 3);
return 0;
}
调用处汇编代码(参数大于两个)
函数内汇编代码(参数大于两个)
总结
如果传入的参数小于等于两个,那么不需要对堆栈进行平衡;如果传入的参数个数大于两个,那么将会在函数调用结束时用ret进行堆栈的调节