함수 호출 규약

2026. 5. 16. 12:53·STUDY/CS

호출 및 반환에 대한 약속 

 

함수를 호출할 경우 반환된 이후를 위해서 호출자(Caller)의 상태(Stack Frame)와 반환주소(Return Address)를 저장해야한다.
(원래 흐름으로 돌아와야하니까!)

또한 호출자는 피호출자(Callee)가 요구하는 인자를 전달하고, 피호출자의 실행이 종료될 때 반환 값`을 전달받아야한다. 

 

보통은 코드에 호출 규약을 명시하지 않는다면, 컴파일러가 지원하는 호출 규약 중 CPU 아키텍처에 적합한 것을 자동으로 선택한다.

컴파일러의 도움 없이 직접 어셈블리 코드를 작성하거나, 어셈블리어 코드를 읽으려면  함수 호출 규약을 알 필요가 있다.

(아키텍처/컴파일러 따라 함수 호출 규약 달라지기 때문)

 

*x86 아키텍처는 레지스터로 피호출자 인자를 전달하기에는 레지스터 수가 적어서 스택으로 인자를 전달하는 규약 사용 

 

💡 컴파일?
어떤 언어로 작성된 소스 코드를 다른 언어의 목적 코드(Object Code)로 번역하는 것 

 

x86 호출 규약

cdecl

리눅스 gcc가 x86 바이너리를 컴파일할 때 일반적으로 사용하는 호출 규약 

레지스터 없이 스택만으로 인자 전달(마지막 -> 첫번째 인자 순으로 스택에 push)

인자 전달 위해 사용한 스택을 호출자가 정리

stdcall

윈도우 API에서 기본적3으로 사용되는 함수 호출 규약

인자 전달 위해 사용한 스택을 피호출자가 정리(retn <바이트수>). 인자전달은 cdecl과 동일 

fastcall

함수 호출시 일부 인자를 레지스터로 전달 (->속도 향상)

처음 두개의 인자는 ecx, edx에 전달. 나머지 인자는 마지막부터 스택으로 전달 

stdcall과 스택 정리 동일 

thiscall

C++ 클래스 멤버 함수 위한 함수 호출 규약

this 포인터를 ecx로 넘기고, 나머지 인자는 모두 스택으로 전달

class C
{
public:
	int c;
    int d;
    unsigned long e;
    int foo(int a, int b)
    {
    	return c + d + e + a + b;
    }
};
foo:                        ; __thiscall C::foo(int a, int b)
	...
	mov eax, [ebp + 8]      ; eax에 스택에 있는 a값 대입 (이전에 b -> a 순서로 스택에 push됨)
    add eax, [ebp + 12]     ; eax에 스택에 있는 b값 add
    add eax, [ecx]          ; eax에 this->c 의 값 add
    add eax, [ecx + 4]      ; eax에 this->d 의 값 add
    add eax, [ecx + 8]      ; eax에 this->e 의 값 add
    
    leave
    retn 8                  ; 피호출자가 8바이트만큼 스택 정리

ecx 레지스터는 this 포인터 가리킴. 해당 포인터로 클래스의 멤버 변수에 접근. 일반적으로 [ecx + offset] 형태로 멤버 변수 사용 

코드에서 c는 오프셋 0에 위치한 멤버 변수 

멤버변수가 여러개면 offset 더해서 ecx만 이용하여 모든 멤버 변수에 접근 가능. 접근은 처음 선언된 멤버 변수부터 

 

x86-64 호출 규약

SYSV (System V)

  1. 6개의 인자를 rdi, rsi, rdx, rcx, r8, r9에 순서대로 저장해서 전달. 더 많은 인자가 요구되는 경우 스택 추가로 이용
  2. Caller에서 인자 전달에 사용된 스택 정리
  3. 함수 반환 값은 rax로 전달 

gdb로 동적분석하며 확인해본다. 컴파일할 코드는 아래와 같다.

// Name: sysv.c
// Compile: gcc -fno-asynchronous-unwind-tables -masm=intel -fno-omit-frame-pointer -o sysv sysv.c -fno-pic -O0

#define ull unsigned long long

ull callee(ull a1, int a2, int a3, int a4, int a5, int a6, int a7) {
  ull ret = a1 + a2 + a3 + a4 + a5 + a6 + a7;
  return ret;
}

void caller() { callee(123456789123456789, 2, 3, 4, 5, 6, 7); }

int main() { caller(); }
b caller는 함수 이름(심볼)로 브레이크 포인트를 설정, 함수 프롤로그가 끝난 직후. 즉 첫번째 실행코드에 걸림
b *caller는 * 뒤에 오는 대상을 주소로 해석해라, 함수 가장 첫번째 바이트(주소의 0바이트 지점). 즉 함수 프롤로그의 맨 첫줄에 걸림 (스택 구조 바뀌기 전)

컴파일 후 gdb를 실행하고, *caller에 중단점을 설정해주어 실행한다. 

7번째 인자는 스택으로 전달, 나머지 인자는 caller+10부터 caller+37까지 코드에서 설정한 대로 뒷 순서 인자부터 전달하는 것읋 확인할 수 있다.

*caller+50에 중단점 설정. 마저 실행하면 레지스터들에 인자 값들이 저장된 것을 확인할 수 있다.

si로 다음 명령을 실행하면 rsp의 값이 반환 주소로 저장된 것을 확인할 수 있다. callee() 다음의 명령어 주소이다. 

지정한 주소로부터 아래로 명령어 디스어셈블하여 10개 출력 (해당 주소 -5부터 출력해라)

 

함수 프롤로그를 확인해보면 push rbp로 호출자 rbp를 저장한다. (스택 프레임 저장)

rbp는 스택 프레임의 가장 낮은 주소를 가리키는 포인터로 Stack Frame Pointer(SFP) 라고도 칭한다.

callee()에서 반환될 때 SFP를 꺼내 caller()의 스택 프레임으로 돌아갈 수 있다. 

마저 push rbp를 실행시킨 후 REGISTERS를 확인해보면 

$rbp와 $rsp의 값을 확인해보면 다른 주소를 가리키고 있지만, si로 다음 명령어를 실행하면 rbp와 rsp가 같은 주소를 가리킨다. (스택 프레임 할당)

스택 프레임 저장은 함수A에서 함수B로 이동할 때, 함수B에 진입하자 마자 이전 함수(A)의 rbp값을 스택 메모리에 보관 
스택 프레임 할당은 함수 내부에서 사용할 지역변수 자리 마련. 높은 주소 -> 낮은 주소로 자라기 때문에 rsp에서 값 빼주어야한다. 

 

💡 함수B로 흐름을 옮기려 할 때 기존 함수A로 돌아와야 한다.
때문에 기존 함수A의 가장 바닥(기준점. rbp)를 스택에 push하여 저장하고, 스택 꼭대기 주소(rsp)를 함수B의 바닥으로 삼는다. 
위치 백업 다했으니 함수B의 rbp부터 변수가 선언될 때 마다 sub rsp, 0x10 처럼 스택이 자란다. 

 

b *callee+79 위치에 중단점을 설정하여 함수 에필로그로 이동한다.

함수 종료 직전인 ret에 중단점을 설정해주고 이동하면 rax에 연산 결과가 저장된 것을 확인할 수 있다.

 

반환은 저장한 스택 프레임과 반환 주소를 스택에서 꺼내면서 진행된다. 

일반적으로 leave로 스택 프레임을 꺼낸다. 스택 프레임을 만들지 않았다면 pop rbp로 꺼낼 수도 있다. 

스택 프레임을 꺼낸 후 ret으로 호출자로 복귀한다. (sfp = rbp, 반환주소 = rip)

 

d로 모든 중단점을 제거하고 b *callee+90으로 새로 지정해준다. 

run 후 두 줄 si로 실행해주면 아래와 같이 rbp, rip 값이 바뀌는 것을 확인할 수 있다.

바닥이 callee() 의 것에서 caller()의 바닥을 다시 rbp가 가리키는 것을 확인할 수 있다 -> 스택 프레임 해제됨 

중단점에서 레지스터 값
중단점에서 si
한 번 더 si로 callee() 빠져나가기

 

저작자표시 비영리 변경금지 (새창열림)

'STUDY > CS' 카테고리의 다른 글

Pwntools  (0) 2026.05.11
GDB (GNU Debugger)  (0) 2026.05.11
어셈블리어와 시스템콜  (0) 2026.05.10
리눅스 메모리 레이아웃  (0) 2026.05.10
2진수의 음수 표현 : 부호화-크기 표현, 보수  (0) 2026.03.23
'STUDY/CS' 카테고리의 다른 글
  • Pwntools
  • GDB (GNU Debugger)
  • 어셈블리어와 시스템콜
  • 리눅스 메모리 레이아웃
권 한
권 한
포렌식 같이 하실래요?
  • 권 한
    Kwon5an.log
    권 한
    • 분류 전체보기 (115) N
      • @Xpert (28)
        • 2025 (23)
        • 2026 (5)
      • Forensic (26)
        • Windows (7)
        • Linux (3)
        • Memory (4)
        • Network (0)
      • System Hacking (6)
      • Reversing (0)
      • STUDY (11)
        • 컴퓨터 구조 (2)
        • 운영체제 (0)
        • 알고리즘 자료구조 (0)
        • CS (7)
      • #컴공에서살아남기 (15) N
        • 201 C++ (3)
        • 201 보안기초 (12) N
        • 201 OSS (0)
      • Write-up (15)
        • H4CKING GAME (1)
        • DreamHack (12)
        • bandit (0)
      • PROGRAM (0)
        • K-shield.jr (0)
      • PROJECT (0)
      • 사담.생각.끄적끄적 (7)
  • 공지사항

    • Notice
  • 링크

    • DAILY BLOG
    • Velog
    • NOTION
  • 전체
    오늘
    어제
  • hELLO· Designed By정상우.v4.10.6
권 한
함수 호출 규약
상단으로

티스토리툴바