[Dreamhack - System Hacking] STAGE 5 : Calling Convention

eunee22·2023년 7월 16일

Dreamhack/SystemHacking

목록 보기
10/12

함수의 호출 규약

  • 함수의 호출 및 반환에 대한 약속
    • 함수를 호출할 때는 호출자(Caller)의 상태(Stack frame) 및 반환 주소(Return Address)를 저장해야함
    • 호출자는 피호출자(Callee)가 요구하는 인자를 전달해줘야 함
    • 피호출자의 실행이 종료될 때는 반환 값을 전달받아야함
  • 일반적으로 고급 언어로 코드를 작성시, 컴파일러가 호출 규약에 맞게 코드를 컴파일
    • 같은 아키텍쳐 라도, 컴파일러에 따라 적용되는 호출 규약이 다를 수 있음

    • 프로그래머가 코드에 명시하지 않는다면, CPU의 아키텍쳐에 적합한 호출 규약을 선택


    • CPU의 아키텍쳐에 적합한 호출 규약이란?

1. x86 → 피호출자의 인자를 전달하기에는 레지스터의 수가 적으므로, 스택으로 인자를 전달하는 규약을 사용

  • cdecl
  • stdcall
  • fastcall
  • thiscall
함수호출규약사용 컴파일러인자 전달 방식스택 정리적용
stdcallMSVCStackCalleeWINAPI
cdeclGCC, MSVCStackCaller일반 함수
fastcallMSVCECX, EDXCallee최적화된 함수
thiscallMSVCECX(인스턴스),
Stack(인자)Callee클래스의 함수

2. x86-64 → 레지스터가 많으므로 적은 수의 인자는 레지스터만 사용해서 인자를 전달하고, 인자가 너무 많을 때만 스택을 사용

  • System V AMD64 ABI의 Calling Convention
  • MS ABI의 Calling Convention
함수호출규약사용 컴파일러인자 전달 방식스택 정리적용
MS ABIMSVCRCX, RDX, R8, R9Caller일반 함수,
Windows Syscall
System ABIGCCRDI, RSI, RDX, RCX, R8, R9, XMM0–7Caller일반 함수

x86호출 규약: cdecl

  • 레지스터의 수가 적으므로 스택을 통한 인자의 전달
  • 인자 전달을 위해 사용한 스택은 호출자가 정리
  • 인자 전달 시에는 마지막 인자부터 첫 번째 인자까지 거꾸로 스택에 push함

  • callee() 함수를 cdecl 호출 규약으로 호출하기
// Name: cdecl.c
// Compile: gcc -fno-asynchronous-unwind-tables -nostdlib -masm=intel \
//          -fomit-frame-pointer -S cdecl.c -w -m32 -fno-pic -O0

void __attribute__((cdecl)) 
callee(int a1, int a2){}
 // cdecl로 호출

void caller(){
   callee(1, 2);
} // callee 함수를 호출하는 caller 함수
  • attribute가 무엇일까? → 참고한 글
    - 함수에 속성을 부여하여 cdecl로 호출하도록 하는 속성지정자
    - GNU gcc 컴파일러에서 제공

    위의 cdecl.c 파일을 컴파일하여 생성한 cdecl.s 파일의 내용을 보면, caller() 함수가 호출 될 때, callee함수의 인자를 2,1 순서대로(거꾸로) 스택에 push하고 스택을 정리 하는 것을 살펴볼 수 있다.

  • 컴파일
    • 소스코드를 다른 언어의 목적 코드로 번역하는 과정
    • 나는 작성된 소스코드를 컴퓨터가 이해할 수 있도록 만드는 과정으로 이해했다.

x86-64호출 규약: SYSV

  • 리눅스는 SYSTEM V(SYSV) Application Binary Interface(ABI)를 기반으로 제작되었음
    • ELF 포맷, 링킹 방법, 함수 호출 규약 등의 내용을 포함
  • 특징
    1. 6개의 인자를 RDI, RSI, RDX, RCX, R8, R9에 순서대로 저장하여 전달
    2. Caller에서 인자 전달에 사용된 스택을 정리
    3. 함수의 반환 값은 RAX로 전달

  • callee() 함수를 SYSV 호출 규약으로 호출하기
// Name: sysv.c
// Compile: gcc -fno-asynchronous-unwind-tables  -masm=intel \
//         -fno-omit-frame-pointer -S 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(); }
profile
보안 공부하는 대학교 4학년 / 시리즈에서 더욱 편하게 글을 찾아보실 수 있습니다:)

0개의 댓글