逆向 - 函数调用约定
逆向 - 函数调用约定
基础代码如下
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个参数分别存储在
ECX和EDX寄存器中被调用者使用
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/