1. Function Calling Convention
함수를 호출하기 위해서는 실행할 함수의 주소 값 그리고 그 주소 값을 가져올 수
있는 함수 이름, 현재 처리되고 있는 함수의 정보를 저장할 공간, 함수의 실행이 끝
난 뒤 리턴 받을 공간, 함수의 실행에 필요한 인자를 넘겨줄 공간이 등이 필요 할
것이다.
main 함수에서 다른 함수로 실행이 넘어간 뒤에 다시 원래의 main 함수로 돌아오
려면 호출자가 사용하던 데이터를 복구할 필요가 있다. 함수를 연달아서 호출한 경
우 나중에 호출된 함수의 데이터는 항상 호출한 함수의 데이터보다 먼저 제거된다.
함수 호출에 관련된 데이터의 이런 특성이 스택이 갖는 속성들과 맞기 때문에 함수
호출 과정에서 현재 함수에서 사용 하는 로컬 변수나 호출될 함수에게 전달한 인자
를 저장하기 위한 데이터 구조에서 스택(Stack)을 주로 사용 한다.
함수 호출 규약은 함수를 호출할 때 인자를 넘겨주는 방식과 함수를 다사용한 후에
스택에 있던 인자를 제거하는 방식으로 나눌 수 있다.
하수 호출 규약을 위 방법대로 나누면 5가지 __cdecl, __stdcall, __fastcall,
thiscall, naked 가 있다.
호출 규약 인수 전달 스택 정리 이름 규칙
__cdecl 오른쪽부터 호출원 _함수명
__stdcall 오른쪽부터 함수 _함수명@인수크기
ECX, EDX에 처음
__fastcall 2개 전달, 나머지는 함수 @함수명@인수크기
오른쪽 먼저
오른쪽부터, this를 C++ 이름 규칙을
thiscall 함수
ECX에 저장 따름
naked 오른쪽부터 함수 없음
표출처:winapi
자주 쓰이는 cdecl방식과 stdcall방식을 실제로 코딩하여 디버깅 해서 확인해 보겠
습니다.
2. cdecl
3: int add(int a, int b)
0040B4F0 push ebp <--add() 시작
0040B4F1 mov ebp,esp
0040B4F3 sub esp,40h
0040B4F6 push ebx
0040B4F7 push esi
0040B4F8 push edi
9: int main(int argc, char* argv[])
00401050 push ebp <--main() 시작
00401051 mov ebp,esp
00401053 sub esp,40h
00401056 push ebx
00401057 push esi
00401058 push edi
11: add(1,2);
00401068 push 2
0040106A push 1
0040106C call @ILT+0(add) (00401005) <--add() 호출
00401071 add esp,8 <--Stack 정리
12: return 0;
00401074 xor eax,eax
00401071주소를 보면 8byte만큼 ESP를 보정해 줍니다. -->(add(1,2))
cdecl 방식의 장점은 printf() 함수와 같이 가변길이 파라미터를 전달 할 수 있다는
것입니다.(ex>printf 같은 함수가 가변 인수가 전달되는 경우인데, printf는 몇 개의 인수가
전달되는 정해져 있지 않습니다. 몇 개의 인수가 전달되는지 알 수가 없기 때에
호출하는 쪽에서 판단하는 편이 유용합니다.)
3. stdcall
3: int _stdcall add(int a, int b) <-- add() 시작
0040B7D0 push ebp
0040B7D1 mov ebp,esp
0040B7D3 sub esp,40h
0040B7D6 push ebx
0040B7D7 push esi
0040B7D8 push edi
.
.
0040B80D pop ebp
0040B80E ret 8 <-- 스택정리
9: int main(int argc, char* argv[]) <-- main() 시작
00401050 push ebp
00401051 mov ebp,esp
00401053 sub esp,40h
00401056 push ebx
00401057 push esi
00401058 push edi
11: add(1,2); <-- add() 호출
00401068 push 2
0040106A push 1
0040106C call @ILT+10(add) (0040100f)
.
.
00401082 pop ebp
00401083 ret
스택의 정리는 add() 0040B80E 주소에서 RET 8 명령에서 수행됩니다.
RET 8 명령의 의미는 RET + POP 입니다. 즉, 리턴 후 지정된 크기만큼 ESP
를 증가시키는 것입니다.
stdcall 방식의 장점은 호출되는 함수(Callee) 내부에 스택 정리 코드가 존재하므로
함수 호출할 때마다 ADD ESP, XXX 명령을 써줘야 하는 cdecl 방식에 비해서 코
드 크기가 작아집니다.
참고한 사이트:http://www.reversecore.com,http://beforu.egloos.com/2117375
Copyright ⓒ 2011. 육승찬. All rights Reserved