函数的2种主要调用方式

2种方式:__cdecl     __stdcall

相同点:调用方按从右向左的顺序把函数参数放入栈中,导致结果函数的第一个参数始终在栈顶(第一个参数在栈顶,并不意味着:函数访问第二个参数,第一个参数就要出栈,通过内存地址访问)

不同点:__cdecl方式的参数数量可变,调用方清楚知道参数个数,按理来说被调用方也知道参数个数(被调用方的代码是固定的,每次调用传入参数是不固定的,返回指令:ret n,n是一个立即数(常量),不能是一个变量(ret不会根据参数的数量动态清除栈中参数)),所以只能由调用方清理参数(在函数外面add esp,一个参数的时候发现编译器喜欢用pop ecx清理)


这个图中strrchr用了2次pop清理2个参数,不知道用的什么狗日的编译器

            __stdcall方式参数数量不可变,调用方、被调用方都清楚知道参数个数,按理来说谁都可以清理参数,但由被调用方清理(可能是规定吧) 


详细查看《IDA Pro权威指南》66页


最近在分析KillDisk的代码时发现大部分函数直接使用调用函数的寄存器值(这些寄存器包括eax、ebx、ecx、edx、edi、esi,但他们的值并没有入栈),ida给出的翻译是__usercall(用户调用),例如:

bool __usercall fun_7_2@<al>(char* pszFullPath@<ebx>, struct CRITICAL_SECTION_EX* g_pCSE2@<edi>)


《IDA权威指南》中有给出解释:

1、如果函数仅供内部使用,则该函数只需要采用只有函数的程序了解的调用约定即可。比如VS中使用GL选项、GNU编译器中使用regparm关键字

2、程序员不嫌麻烦,使用汇编语言,那么,他们就能完全控制如何向他们创建的函数传递参数。

详细查看《IDA Pro权威指南》69页

猜你喜欢

转载自blog.csdn.net/singleyellow/article/details/80271449