Calling Convention | 함수 호출 규약
함수 호출 규약 : 함수의 호출 및 반환에 대한 약속
한 함수에서 다른 함수를 호출할 때 프로그램의 실행 흐름이 다른 함수로 이동한다. 이 호출된 함수가 반환되면 다시 원래의 함수로 돌아와서 기존의 실행 흐름을 이어나간다. 이때 호출하는 함수를 호출자(caller), 호출되는 함수를 피호출자(Callee)라고 한다.
호출자는 피호출자가 요구하는 파라미터를 전달해줘야하고, 실행이 종료되면 반환 값을 전달받아야한다.
Segment word size | Calling Convention | Parameters in register | Stack cleanup by |
32bits | __cdecl | Caller | |
__stdcall | Callee | ||
__fastcall | ecx, edx | Callee | |
__thiscall | ecx | Callee | |
64bits | Microsoft x64 Calling Convention | RCX, RDX, R8, R9 나머지 스택 | Caller |
System V AMD64 ABI (Linux x64) | RDI, RSI, RDX, RCX, R8, R9 | Caller |
x86 Calling Convention
Linux의 경우, Calling Convention을 앞에 지정해주는 방식이 아니다.
또한, __fastcall이 존재하지 않는다.
__cdecl
- 대부분의 32bits C프로그램에서 사용하는 사용하는 기본 호출 규약
- 스택은 Caller에 의해 정리됨
- 스택으로 인자가 전달됨
#include <stdio.h>
// __cdecl 제거: 리눅스에서는 필요 없음
int sum(int a, int b) {
return a + b;
}
int main() {
int s = sum(2, 3); // 함수 호출
printf("%d\n", s); // 결과 출력
return 0;
}
Assembly
pwndbg> disassem main
Dump of assembler code for function main:
0x000011b4 <+0>: lea ecx,[esp+0x4]
0x000011b8 <+4>: and esp,0xfffffff0
0x000011bb <+7>: push DWORD PTR [ecx-0x4]
0x000011be <+10>: push ebp
0x000011bf <+11>: mov ebp,esp
0x000011c1 <+13>: push ebx
0x000011c2 <+14>: push ecx
0x000011c3 <+15>: sub esp,0x10
0x000011c6 <+18>: call 0x10a0 <__x86.get_pc_thunk.bx>
0x000011cb <+23>: add ebx,0x2e0d
0x000011d1 <+29>: push 0x3
0x000011d3 <+31>: push 0x2
0x000011d5 <+33>: call 0x119d <sum>
0x000011da <+38>: add esp,0x8
0x000011dd <+41>: mov DWORD PTR [ebp-0xc],eax
0x000011e0 <+44>: sub esp,0x8
0x000011e3 <+47>: push DWORD PTR [ebp-0xc]
0x000011e6 <+50>: lea eax,[ebx-0x1fd0]
0x000011ec <+56>: push eax
0x000011ed <+57>: call 0x1050 <printf@plt>
0x000011f2 <+62>: add esp,0x10
0x000011f5 <+65>: mov eax,0x0
0x000011fa <+70>: lea esp,[ebp-0x8]
0x000011fd <+73>: pop ecx
0x000011fe <+74>: pop ebx
0x000011ff <+75>: pop ebp
0x00001200 <+76>: lea esp,[ecx-0x4]
0x00001203 <+79>: ret
End of assembler dump.
pwndbg> disassem sum
Dump of assembler code for function sum:
0x0000119d <+0>: push ebp
0x0000119e <+1>: mov ebp,esp
0x000011a0 <+3>: call 0x1204 <__x86.get_pc_thunk.ax>
0x000011a5 <+8>: add eax,0x2e33
0x000011aa <+13>: mov edx,DWORD PTR [ebp+0x8]
0x000011ad <+16>: mov eax,DWORD PTR [ebp+0xc]
0x000011b0 <+19>: add eax,edx
0x000011b2 <+21>: pop ebp
0x000011b3 <+22>: ret
End of assembler dump.
Stack Frame
해당 스택을 보면 인자값이 호출하기 전에 push 되는 것을 확인할 수 있다.
또한, 함수 반환 이후, sub esp, 0x8 등으로 Caller가 함수를 정리한다.
__stdcall
- 주로 WinAPI 32 함수들이 __stdcall을 따름
- 스택은 Callee에 의해 정리됨
- 스택으로 인자가 전달됨
#include <stdio.h>
int __stdcall sum(int a, int b)
{
return a + b;
}
int main()
{
int s = sum(2, 3);
printf("%d", s);
return 0;
}
__fastcall
- 두 인자들이 4바이트보다 작거나 같다면 edx, ecx에 담아두고 사용함
- 나머지는 스택으로 전달
- 레지스터를 이용하기 때문에 함수 호출 비용을 줄일 수 있음
- Callee에 의해 스택을 정리함
__thiscall
- C++에서 사용하는 함수 호출 규약
- this 포인터는 ecx에 저장되고 나머지는 스택에 저장
- Callee에 의해 스택을 정리함
x64 Calling Convention
Microsoft x64 Calling Convention
- 정수 파라미터는 RCX, RDX, R8, R9로 전달됨
- 부동 소수점 파라미터는 XMM0L, XMM1L, XMM3L로 전달됨
- 16바이트 파라미터는 참조로 전달됨
- 파라미터가 많으면, 레지스터 이외의 부분은 스택으로 전달됨
- RAX로 반환됨
- Caller가 스택을 정리함
System V amd64 ABI
- RDI, RSI, RDX, RC, R8, R9 로 전달됨
- 파라미터가 많으면, 레지스터 이외의 부분은 스택으로 전달됨
- Caller가 스택을 정리함
출처
- https://learn.microsoft.com/ko-kr/cpp/build/x64-calling-convention?view=msvc-170
x64 호출 규칙
기본 x64 호출 규칙의 세부 정보에 대해 알아봅니다.
learn.microsoft.com
Linux x64 Calling Convention: Stack Frame | Red Team Notes
In 64-bit Linux system, function arguments of type integer/pointers are passed to the callee function in the following way: This lab and conclusions are based on the following C program compiled on a 64-bit Linux machine: Let's now see how arguments are pa
www.ired.team
'CS > Computer Architecture' 카테고리의 다른 글
[Assembly] 산술 연산 (0) | 2024.09.27 |
---|---|
[Assembly] 데이터 이동 (0) | 2024.09.20 |
Static Link vs Dynamic Link (0) | 2024.09.05 |
함수의 프롤로그 & 에필로그 (0) | 2024.08.22 |
Register (0) | 2024.08.16 |