[Reversing] 리버싱 핵심원리 [8]. 함수 호출 규약

2020. 3. 3. 01:30보안/리버싱

728x90

내용 : 함수 호출 규약에 대한 정리

 

함수 호출 규약

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

함수호출 전 파라미터를 스택을 통해서 전달한다. PE헤더에 크기가 명시되어 있어, 프로세스가 실행될 때 스택 메모리의 크기가 결정됨.

 

스택에 저장된 값은 임시로 사용하는 값임, 더이상 사용하지 않더라도 값을 지우지 않음 (불필요한 CPU 자원을 소모하게 되므로..)

또한 스택 메모리는 이미 고정되어있으므로 메모리 해제가 불가능함.

 

용어 설명

※ caller : 함수를 호출한 쪽

※ callee : 호출을 당한 함수

 

ex) main() 함수에서 printf() 함수를 호출했다면, Caller : main()  |  Callee : printf() 이다.

 

호출 규약 종류

[cdecl]

◎주로 C언어에서 사용하는 방법, Caller에서 스택을 정리하는 특징을 가지고 있다.

#include <stdio.h>

int add (int a, int b)
{
	return (a+b);
}

int main(int argc, char* argv[])
{
	return add(1, 2);
}

위의 코드를 디버깅 해보면 add() 함수의 파라미터 1, 2 를 역순으로 스택에 입력하고,

ADD ESP, 8 명령으로 스택을 정리한다.

 

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

 

 

[stdcall]

◎WIN32 API 에서 사용되며, Callee에서 스택을 정리하는것이 특징이다.

C언어의 기본 컴파일 방식은 cdecl방식이므로, stdcall로 컴파일을 하고 싶다면 '_stdcall'을 붙여주면 된다.

#include <stdio.h>
int _stdcall add(int a, int b)
{
	return (a+b);
}

int main (int argc, char* argv[])
{
	return add(1, 2);
}

디버깅을 하면, cdecl과 다르게, main()에서 add를 호출 한 이후에 스택 정리코드가 생략되어있다.

스택의 정리는 add()함수 마지막 RETN 8 에서 수행된다.

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

 

WinAPI는 기본적으로 C언어로 작성되었지만, stdcall 방식을 사용하는데, 이는 C언어 이외의 언어등에서 API를 직접 호출할 때 호환성을 좋게 하기 위함이라고 한다.

 

 

 

[fastcall]

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

 

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

 

장점은 좀 더 빠른 함수 호출이 가능하지만, 함수가 복잡해지거나 EDX에 중요한 값이 저장되어있다면 백업해두어야 하는 번거로움이 존재한다고 합니다.

728x90