逆向 - 函数调用约定

逆向 - 函数调用约定

基础代码如下

CPP
#include <iostream>

int test(int a, int b, int c, int d, int f, int g)
{
	int ret = a + b - c - d + f + g;
	return ret;
}

int main()
{
	int result = test(1, 2, 3, 4, 5, 6);
	std::cout << "Result: " << result << std::endl;
}

x86 部分

cdecl 调用约定

  • 参数从右向左入栈

  • 调用者清理栈

ASM
004F2726  push        6
004F2728  push        5
004F272A  push        4
004F272C  push        3
004F272E  push        2
004F2730  push        1                              // 参数压栈
004F2732  call        test (04F1447h)                // 调用 test 函数
004F2737  add         esp,18h                        // 清理栈
004F273A  mov         dword ptr [result],eax         // 存储返回值

stdcall 调用约定

  • 参数从右向左入栈

  • 被调用者使用 ret 清理栈

ASM
00612726  push        6
00612728  push        5
0061272A  push        4
0061272C  push        3
0061272E  push        2
00612730  push        1                              // 参数压栈
00612732  call        0061144C                       // 调用 test 函数
// 没有清理栈, 调用者清理栈
00612737  mov         dword ptr [ebp-8],eax          // 存储返回值

>> 0061144C
....
006126E1  ret         18h                            // 清理栈, 并返回
....

fastcall 调用约定

  • 参数从右向左入栈

  • 第1个参数和第2个参数分别存储在 ECXEDX 寄存器中

  • 被调用者使用 ret 清理栈

ASM
00D32726  push        6
00D32728  push        5
00D3272A  push        4
00D3272C  push        3                              // 后4个参数压栈
00D3272E  mov         edx,2
00D32733  mov         ecx,1                          // 前2个参数寄存器存储
00D32738  call        00D31451
00D3273D  mov         dword ptr [ebp-8],eax          // 存储返回值

>> 00D31451
....
00D326E9  ret         10h                            // 清理栈, 并返回
....

x64 部分

x64调用 只有 fastcall 一种调用约定

  • 参数从右向左入栈

  • 1~4 个参数分别存储在 RCX, RDX, R8, R9 寄存器中

  • 其他参数压栈

  • 被调用者使用 ret 清理栈

ASM
00007FF67AB1342C  mov         dword ptr [rsp+28h],6
00007FF67AB13434  mov         dword ptr [rsp+20h],5   // 多余的参数压栈
00007FF67AB1343C  mov         r9d,4
00007FF67AB13442  mov         r8d,3
00007FF67AB13448  mov         edx,2
00007FF67AB1344D  mov         ecx,1                   // 前4个参数寄存器存储
00007FF67AB13452  call        00007FF67AB1142E
00007FF67AB13457  mov         dword ptr [rbp+4],eax
ASM
00007FF606B62450  mov         dword ptr [rsp+20h],r9d
00007FF606B62455  mov         dword ptr [rsp+18h],r8d
00007FF606B6245A  mov         dword ptr [rsp+10h],edx
00007FF606B6245E  mov         dword ptr [rsp+8],ecx     // 4个参数放入 Shadow Space
00007FF606B62462  push        rbp
00007FF606B62463  push        rdi
00007FF606B62464  sub         rsp,108h
00007FF606B6246B  lea         rbp,[rsp+20h]
00007FF606B62470  lea         rcx,[00007FF606B7307Bh]
00007FF606B62477  call        00007FF606B6143D
00007FF606B6247C  nop
00007FF606B6247D  mov         eax,dword ptr [rbp+0000000000000108h]
00007FF606B62483  mov         ecx,dword ptr [rbp+0000000000000100h]
00007FF606B62489  add         ecx,eax
00007FF606B6248B  mov         eax,ecx
00007FF606B6248D  sub         eax,dword ptr [rbp+0000000000000110h]
00007FF606B62493  sub         eax,dword ptr [rbp+0000000000000118h]
00007FF606B62499  add         eax,dword ptr [rbp+0000000000000120h]
00007FF606B6249F  add         eax,dword ptr [rbp+0000000000000128h]
00007FF606B624A5  mov         dword ptr [rbp+4],eax
00007FF606B624A8  mov         eax,dword ptr [rbp+4]
00007FF606B624AB  lea         rsp,[rbp+00000000000000E8h]
00007FF606B624B2  pop         rdi
00007FF606B624B3  pop         rbp
00007FF606B624B4  ret

在调用前其栈如下

rsp

当前

0

+8

+10

+18

参数6

+20

参数5

+28

在调用call后, RSP上移动, 此时栈如下

rsp

RETADDR

0

+8

+10

+18

+20

参数6

+28

参数5

+30

然后放入参数4, 参数3, 参数2, 参数1

rsp

RETADDR

0

参数1 ecx

+8

参数2 edx

+10

参数3 r8d

+18

参数4 r9d

+20

参数6

+28

参数5

+30

压入rbp和rdi

rsp

old rdi

0

old rbp

+8

RETADDR

+10

参数1 ecx

+18

参数2 edx

+20

参数3 r8d

+28

参数4 r9d

+30

参数6

+38

参数5

+40

分配栈空间

rsp

+20

+18

+10

+8

rbp

+0

…​

old rdi

+E8

old rbp

+F0

RETADDR

+F8

参数1 ecx

+100

参数2 edx

+108

参数3 r8d

+110

参数4 r9d

+118

参数6

+120

参数5

+128

即可对应到栈上的参数

ASM
00007FF606B6247D  mov         eax,dword ptr [rbp+0000000000000108h]
00007FF606B62483  mov         ecx,dword ptr [rbp+0000000000000100h]
00007FF606B62489  add         ecx,eax
00007FF606B6248B  mov         eax,ecx
00007FF606B6248D  sub         eax,dword ptr [rbp+0000000000000110h]
00007FF606B62493  sub         eax,dword ptr [rbp+0000000000000118h]

在返回清理时, rsp恢复到old rdi位置, 并弹出rdi和rbp, 最后返回

ASM
00007FF606B624AB  lea         rsp,[rbp+00000000000000E8h]
00007FF606B624B2  pop         rdi
00007FF606B624B3  pop         rbp
00007FF606B624B4  ret

逆向 - 函数调用约定
https://simonkimi.githubio.io/posts/20260222073634/
作者
simonkimi
发布于
2026年2月22日
许可协议