[시스템 해킹] Calling Convention

zzoni·2022년 7월 21일
0

시스템해킹

목록 보기
6/15

◼ 함수 호출 규약이란?

  • 함수의 호출 및 반환에 대한 약속
  • 함수를 호출할 때는 반환된 이후를 위해 스택 프레임과 반환주소를 저장해야 한다.
  • 또한, 호출자(caller)는 피호출자(callee)가 요구하는 인자를 전달해줘야 하며, 피호출자의 실행이 종료될 때는 반환 값을 전달받아야 한다.

◼ 함수 호출 규약의 종류

컴파일러는 지원하는 호출 규약 중, CPU 아키텍처에 적합한 것을 선택한다.
CPU의 아키텍처가 같아도, 컴파일러가 다르면 적용하는 호출규약이 다를 수 있다.

x86 아키텍처는 레지스터 수가 적으므로, 스택으로 인자를 전달하는 규약을 사용하는 반면,
x86-64 아키텍처에서는 레지스터가 많으므로 적은 수의 인자는 레지스터만 사용하고, 안자가 많을 때만 스택을 사용한다.


◼ x86 호출 규약: cdecl

C Declaration

gcc에서 x86 바이너리를 컴파일할 때 사용하는 규약

  • x86 아키텍처는 레지스터 수가 적으므로, 스택을 통해 인자를 전달한다.
  • 또한, 인자를 전달하기 위해 사용한 스택을 caller가 정리 하는 특징이 있다.
  • 스택을 통해 인자를 정리할 때는, 마지막 인자부터 첫 번째 인자까지 거꾸로 스택에 push한다.
void __attribute__((cdecl)) callee(int a1, int a2){ // cdecl로 호출
}
void caller(){
   callee(1, 2);
}

위의 코드를 어셈블리어로 컴파일한 후 확인!

💡 컴파일의 정확한 의미

어떤 언어로 작성된 소스코드를 다른 언어의 목적 토드로 번역하는 것

  • cdecl 함수 호출 처리 순서
    • caller가 전달할 argument를 stack에 push
    • callee prolog
    • callee epilog
    • caller가 call과정에서 사용한 stack 정리

◼ x86-64 호출 규약: SYSV

x86-64 바이너리를 컴파일할 때 사용하는 규약

◼ 특징

  1. 6개의 인자를 rdi,rsi,rdx,rcx,r8,r9에 순서대로 저장하여 전달
    더 많은 인자 전달시 스택 사용
  2. caller에서 인자 전달에 사용된 스택 정리
  3. 함수의 반환 값은 rax로 전달

◼ SYSV 상세 분석

callee 함수 호출 전 후

스택프레임 저장

  • push rbp
    : caller의 rbp 저장 => callee에서 반환될 때 sfp를 꺼내어 caller의 스택 프레임으로 돌아갈 수 있다.

스택프레임 할당

  • mov rbp, rsp
    • 이 예제에서 callee 는 지역 변수를 사용하지 않으므로, 새로운 스택 프레임을 만들지 않는다.
profile
모든 게시물은 다크모드에서 작성되었습니다!

0개의 댓글