[Pwnable] 5. Background: Calling Convention

Wonder_Land🛕·2022년 10월 19일
0

[Pwnable]

목록 보기
5/21
post-thumbnail

[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.


  1. 서론
  2. x86호출 규약 : cdecl
  3. x86-64호출 규약 : SYSV
  4. Q&A
  5. 마치며

1. 서론

함수 호출 규약은 함수의 호출 및 반환에 대한 규약입니다.

함수를 호출할 때는 반환된 이후를 위해,
호출자(Caller)의 상태(Stack Frame)반환 주소(Return Address)를 저장해야 합니다.

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

함수의 호출 규약을 적용하는 것은 컴파일러의 몫입니다.
만약 프로그래머가 호출 규약을 명시하지 않는다면, 컴파일러는 지원하는 호출 규약 중, CPU의 아키텍처에 적합한 것을 선택합니다.
따라서, 프로그래머는 함수의 호출 규약을 알지 않아도 괜찮습니다.

하지만 만약, 컴파일러의 도움 없이 어셈블리 코드를 작성하거나, 어셈블리로 작성된 코드를 읽고자 한다면,
함수의 호출 규약에 대해 알고 있어야 합니다.

1) 함수 호출 규약의 종류

컴파일러는 지원하는 호출 규약 중,
CPU 아키텍처에 적합한 것을 선택합니다.

x86(32bit) 아키텍처는 레지스터를 통해 피호출자의 인자를 전달하기에는 레지스터의 수가 적으므로,
스택으로 인자를 전달하는 규약을 사용합니다.

반면 x86-64(64bit)는 레지스터의 수가 많으므로, 레지스터만 사용해서 인자를 전달하고,
인자가 너무 많을 때만 스택을 사용합니다.

CPU 아키텍처가 같더라도, 컴파일러가 다르면 적용하는 호출 규약이 달라질 수 있습니다.

C언어를 컴파일할 때,
윈도우는 MSVC를, 리눅스에서는 gcc를 사용합니다.
이 둘은 서로 다른 호출 규약을 적용합니다.


2. x86 호출 규약 : cdecl

x86(32bit) 아키텍처는 레지스터의 수가 적기 때문에, 스택을 통해 인자를 전달합니다.

또한, 인자를 전달하기 위해 사용한 스택을 호출자가 정리하는 특징이 있습니다.

따라서, 스택을 통해 인자를 전달할 때는
마지막 인자부터 첫번째 인자까지 거꾸로 스택에 push합니다.


3. x86-64 호출 규약 : SYSV

리눅스는 SYSTEM V(SYSV) Application Binary Interface(ABI)를 기반으로 만들어졌습니다.

SYSV ABI는 ELF 포맷, 링킹 방법, 함수 호출 규약 등의 내용을 담고 있습니다.

file명령어를 이용하여, 바이너리를 살펴보면 SYSV 문자열이 포함되어 있습니다.

SYSV에서 정의한 함수 호출 규약은 다음 특징을 가집니다.

  1. 6개의 인자를 rdi, rsi, rdx, rcx, r8, r9에 순서대로 저장하여 전달합니다. 더 많은 인자를 사용할 때는 스택을 추가로 이용합니다.

  2. Caller에서 인자 전달에 사용된 스택을 정리합니다.

  3. 함수의 반환값은 rax로 전달합니다.


4. Q&A

-


5. 마치며

[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.

profile
아무것도 모르는 컴공 학생의 Wonder_Land

0개의 댓글