본문 바로가기

개념정리/System Hacking

[System Hacking] 함수 호출 규약

1. 함수 호출 규약

 함수 호출 규약은 함수의 호출 및 반환에 대한 약속이다. 한 함수에서 다른 함수를 호출하면 프로그램의 실행 흐름은 다른 함수로 이동한다. 그리고 호출한 함수를 반환하면 원래 함수로 돌아와서 기존의 실행 흐름을 이어간다. 그러므로 함수를 호출할 때는 반환된 이후를 위해 호출자의 상태(Stack frame)반환 주소(Return address)를 저장해야 한다. 또한 호출자는 피호출자가 요구하는 인자를 전달해줘야 하며, 피호출자의 실행이 종료될 때는 반환 값을 전달받아야 한다.

함수 호출 규약을 적용하는 것은 일반적으로 컴파일러의 몫이다. 그러나 컴파일러의 도움 없이 어셈블리 코드를 작성하거나 어셈블리로 작성된 코드를 읽고자 한다면 함수 호출 규약을 알아야 할 필요가 있다. 

컴파일러 : 어떤 언어로 작성된 소스 코드를 다른 언어의 목적 코드로 번역하는 것이다. 소스 코드를 어셈블리어 또는 기계어로 번역하는 행위 모두 컴파일의 범주에 포함된다. 

2. 다양한 함수 호출 규약

 CPU의 아키텍처가 같아도 컴파일러가 다르면 적용하는 호출 규약이 다르다. MSVC는 MS x64 호출 규약을 적용하지만 gcc는 SYSTEM V 호출 규약을 적용한다. 리눅스에서 사용하는 대표적인 함수 호출 규약은 1) x862) x86-64로 나뉜다.

1) x86

  • cdecl

    - 주로 C언어에서 사용하는 방식이다.
    - 레지스터의 수가 적으므로, 스택을 통해 인자를 전달한다.
    - 인자를 전달하기 위해 사용한 스택을 호출자(Caller)가 정리한다.
    - 스택을 통해 인자를 전달할 때, 마지막 인자부터 첫 번째 인자까지 거꾸로 스택에 push한다.

  • stdcall

    - 주로 Win32 API에서 사용한다.
    - cdecl와 반대로 인자를 전달하기 위해 사용한 스택을 피호출자(Callee)에서 정리한다.
    - C언어는 기본적으로 cdecl 방식이므로 stdcall 방식을 사용하고 싶으면 함수명 앞에 "__stdcall"을 붙여준다.

  • fastcall

    - 기본적으로 stdcall 방식과 같지만 전달하는 처음 파라미터 2개를 스택 메모리가 아닌 레지스터를 이용하여 전달한다.
    - 파라미터가 5개라면, 앞의 2개의 파라미터는 ECX, EDX 파라미터를 이용하여 전달하고 나머지 인자는 스택을 이용하여 전달한다.
    - 메모리보다 레지스터에 접근하는 것이 빠르므로 빠른 함수 호출이 가능하다는 장점이 있다.
    - 레지스터를 사용함으로써 다른 용도로 레지스터를 사용할 때 다른 곳에 저장해 놓아야 한다는 단점이 있다.

2) x86-64

  • SYSTEM V

    - 6개의 인자를 RDI, RSI, RDX, RCX, R8, R9에 순서대로 저장하여 전달한다.
    - 더 많은 인자를 사용해야 할 때는 스택을 추가로 이용한다.
    - 호출자(Caller)에서 인자 전달에 사용된 스택을 정리한다.
    - 함수의 반환 값은 RAX로 전달한다.

 

'개념정리 > System Hacking' 카테고리의 다른 글

[System Hacking] 쉘 코드 모음(32비트, 64비트)  (0) 2022.08.14
[System Hacking] Stack Buffer Overflow  (0) 2022.08.09
[System Hacking] Shellcode  (0) 2022.08.02
6. pwntools  (0) 2022.08.01
5. gdb 디버거  (0) 2022.07.21