[Reversing] 함수 호출 규악(Calling Convention)

 

리버스 엔지니어링


(1) 함수 호출 규약

 

1-1. 함수 호출 규약이란?

  • Calling Convention은 우리말로 '함수 호출 규약'이라고 부름

  • "함수를 호출할 때 파라미터를 어떤 식으로 전달하는가?"에 대한 일종의 약속 

이전에 이미 함수 호출 전에 파라미터를 스택을 통해서 전달한다는 것을 진행하였다.

 

스택이란 프로세스에서 정의된 메모리 공간이며 아래 방향(주소가 줄어드는 방향)으로 자란다.

 

또한 PE 헤더에 그 크기가 명시되어 있으며, 즉 프로세스가 실행될 때 스택 메모리의 크기가 결정된다.

(malloc / new 같은 동적 메모리 할당과는 다름)

 

스택에 저장된 값은 임시로 사용하는 값이기 때문에 더 이상 사용하지 않는다고 하더라도 값을 지우거나 하면 불필요하게 CPU 자원을 소모한다.

 

어차피 다음 번에 스택에 다른 값을 입력할 때 저절로 덮어쓰는 데다가 스택 메모리는 이미 고정되어 있기 때문에 메모리 해제를 할 수도 없고 할 필요도 없다.

 

스택 메모리는 고정되어 있고 ESP로 스택의 현재 위치를 가리키는데, 만약 ESP가 스택의 끝을 가리킨다면 더 이상 스택을 사용할 수 없다.

 

함수 호출 후에 ESP(스택 포인터)를 어떻게 정리하는지에 대한 약속이 바로 함수 호출 규약이다.

 

 

주요한 함수 호출 규약은 다음과 같다.

  • cdecl

  • stdcall

  • fastcall

 

# 참고

  • Caller(호출자) : 함수를 호출한 쪽

  • Callee(피호출자) : 호출을 당한 함수

예로, main() 함수에서 print() 함수를 호출하였다면 Caller는 main()이고, Callee는 printf()가 되는 것이다.


1-1-1. cdecl

  • cdecl 방식은 주로 C언어에서 사용되는 방식

  • Caller에서 스택을 정리하는 특징을 가짐

[그림 1] cdecl 예제

 

[그림 2] cdecl.exe 예제 파일

 

[그림 2]의 내용은 다음과 같다.

 

  • 401013 ~ 40101C 주소 영역의 코드를 보면, add() 함수의 파라미터 1, 2를 역순으로 스택에 입력

  • add() 함수(401000)를 호출 후 ADD ESP, 8 명령으로 스택을 정리 

이와 같이 Caller인 main() 함수가 자신이 스택에 입력한 함수 파라미터를 직접 정리하는 방식이 cdecl이다.

 

cdecl 방식의 장점은 C언어의 printf() 함수와 같이 가변 길이 파라미터를 전달할 수 있다는 것이다.

 

이러한 가변 길이 파라미터는 다른 Calling Convention에서는 구현이 어렵다.


1-1-2. stdcall

  • stdcall 방식은 Win32 API에서 사용

  • Callee에서 스택을 정리하는 것이 특징

  • C언어는 기본적으로 cdecl 방식이지만, stdcall 방식으로 컴파일하려면 '_stdcall' 키워드를 붙여주면 됨

[그림 3] stdcall 예제 

 

[그림 4] stdcall.exe 예제 파일

 

[그림 4]의 내용은 다음과 같다.

 

  • 스택의 정리는 add() 함수 마지막(40100A)의 RETN 8 명령에서 수행

  • RETN 8 명령 의미는 RETN + POP 8바이트

  • 즉 리턴 후 지정된 크기만큼 ESP를 증가시키는 것

이와 같이 Callee인 add() 함수 내부에서 스택을 정리하는 방식이 stdcall 방식이다.

 

stdcall 방식의 장점은 호출되는 함수(Callee) 내부에 스택 정리 코드가 존재하므로 함수를 호출할 때마다 ADD, ESP, XXX 명령을 써줘야 하는 cdecl 방식에 비해 코드 크기가 작아진다.

 

Win32 API는 C언어로 된 라이브러리지만, 기본 cdecl 방식이 아닌 stdcall 방식을 사용한다.

 

이는 C 이외의 다른 언어(Delphi(Pascal), Visual Basic 등)에서 API를 직접 호출할 때 호환성을 좋게 하기 위한 것이다.


1-1-3. fastcall

  • 기본적으로 stdcall 방식과 같지만, 함수에 전달하는 파라미터 일부(2개까지)를 스택 메모리가 아닌 레지스터를 이용하여 전달한다는 것이 특징

  • 어떤 함수의 파라미터가 4개라면, 앞의 두 개의 파라미터는 각각 ECX, EDX 파라미터를 이용하여 전달 

fastcall의 장점은 이름 그대로 좀 더 빠른 함수 호출이 가능하다.

(CPU 입장에서는 멀리 있는 메모리보다 CPU와 같이 붙어있는 레지스터에 접근하는 것이 훨씬 더 빠름)

 

그런데 호출 자체만 보면 빠르지만, ECX, EDX 레지스터를 관리하는 추가적인 오브헤드가 필요한 경우가 있다.

 

가령 함수 호출 전에 ECX, EDX에 중요한 값이 저장되어 있다면 백업해 놓아야 한다.

 

또한 함수 내용이 복잡하다면 ECX, EDX 레지스터를 다른 용도로 사용할 필요가 있을 때 역시 이들이 가지고 있는 파라미터 값을 어딘가에 따로 저장할 필요가 생긴다.


# Reference

 

http://www.yes24.com/Product/Goods/7529742

'Reversing' 카테고리의 다른 글

[Reversing] Process Explorer  (0) 2020.03.26
[Reversing] 스택  (0) 2020.03.25
[Reversing] IA-32 Register  (0) 2020.03.25
[Reversing] 리틀 엔디언 표기법  (0) 2020.03.24
[Reversing] OllyDbg 사용법  (0) 2020.03.24

+ Recent posts