티스토리 뷰

함수 호출 규약 _ 기본적인 함수 호출 + 간단한 스택 사용

어셈블리에서의 기본적인 함수 호출 규약

예제

Caller Function
int main(int argc, char* argv[])
{
	sum(1, 2);
	return 0;
}

.text:080483F8                     public main
.text:080483F8     main            proc near
.text:080483F8
.text:080483F8     argc            = dword ptr  8
.text:080483F8     argv            = dword ptr  0Ch
.text:080483F8     envp            = dword ptr  10h
.text:080483F8
.text:080483F8 000                 push    ebp
.text:080483F9 004                 mov     ebp, esp
.text:080483FB 004                 push    2
.text:080483FD 008                 push    1
.text:080483FF 00C                 call    getsum
.text:08048404 00C                 add     esp, 8
.text:08048407 004                 leave
.text:08048408 000                 retn

Callee Function
int getsum(int a, int b)
{
	return a + b;
}

.text:080483EB                     public getsum
.text:080483EB     getsum          proc near
.text:080483EB
.text:080483EB     arg_0           = dword ptr  8
.text:080483EB     arg_4           = dword ptr  0Ch
.text:080483EB
.text:080483EB 000                 push    ebp
.text:080483EC 004                 mov     ebp, esp
.text:080483EE 004                 mov     edx, [ebp+arg_0]
.text:080483F1 004                 mov     eax, [ebp+arg_4]
.text:080483F4 004                 add     eax, edx
.text:080483F6 004                 pop     ebp
.text:080483F7 000                 retn
.text:080483F7     getsum          endp


> Main 함수의 에필로그는 생략.

휘발성 레지스터 : ecx, edx
비휘발성 레지스터 : ebx, esi, edi, ebp
Special : 
eax - Return
esp - 함수 호출 규약에 따라 다름
eip - Next Instruction

push : 현재 ESP + 4h 를 먼저 수행한 후, 해당 공간에 데이터를 쓴다.
pop : 현재 ESP에 있는 데이터를 가져온 후, ESP - 4h 를 수행한다.

! 0x080483FB ~ FD
getsum 함수를 불러오기 전 넘겨줄 인자값을 스택에 넣어준다.
이 때, 마지막 인자를 맨 처음에, 첫번째 인자를 맨 마지막에 넣어준다. (push)
push 명령어 실행 전에는, ESP가 맨 마지막 인자의 스택 위치 - 4h 주소를 가리키고 있음.

! 0x080483FF
call 명령어가 실행 되지 바로 전에는
ESP 가 맨 첫번째 인자의 스택 주소를, EBP 가 현재 스택 프레임을 가리키고 있음.

! 0x080483EB
call 명령어가 실행되면, 내부적으로 다음 명령어의 위치 (eip + 1) 을 스택에 push 한다.

! 0x080483EC
(함수 프롤로그 실행) 
push 명령어로 old stack frame 을 스택에 저장하고, EBP 를 ESP 로 설정한다.
즉 이때의 스택 프레임은 High Address (0xFFFFFFFF) 부터 다음과 같다.
EBP + 8 + ((0~?)*4h)) : Arguments. 가장 낮은 주소가 (EBP + 8h) 처음 인자값이다.
EBP + 4 : Return Address (호출된 주소(call) 의 다음 Instruction 주소를 가지고 있다.
EBP : Old Frame Pointer - (Caller Function 의 Frame Pointer) 를 가지고 있다.
EBP - ((1~?)*4h)) : Local Variables. 가장 높은 주소가 대체로 맨 처음 선언된 변수이다. (정확하지 않음)

! 0x080483F4
x86 어셈블러에서 함수의 리턴값은 EAX 를 통해 전달된다.
이전 두 mov 연산을 통해 ebp 에서 인자값 메모리 주소에 저장되어 있는 값을 각각 eax, edx 레지스터로 가져왔으며,
add eax, edx 를 수행함으로써, 리턴값을 EAX 에 저장해 준다.

! 0x080483F6
(함수 에필로그 실행)
ESP 와 EBP 가 같아진 상태 (ESP 가 Old Frame를 저장하고 있는 영역에 위치한 EBP 의 주소와 같아짐)
에서 pop ebp 를 실행함으로써, esp를 +4h (RET ADDR) 로 이동한 후에, EBP를 Caller Function 의 Frame Pointer 로 설정해 줌으로써.
호출된 서브루틴을 정리하고 이전 함수로 돌아갈 정리를 한다.
단, leave 명령어로 이 명령어를 대신할 수 도 있다.

! 0x080483F7
retn 명령어를 수행함으로써, 현재 ESP 가 가지고 있는 주소 (Return Address)를 EIP로 설정하고, ESP + 4h 를 수행한다.
결과적으로 ESP 가 함수의 맨 첫번째 Argument 에 위치한 상태로, 프로그램의 흐름이 call 명령어 다음으로 이동하게 된다.

이전에 스택에 push 된 Argument 의 처리 방법으로는 (나중에 자세히 후술, 여러가지 함수 호출 규약에 따라 다르다.)
1. Callee Function 에서 "ret n(pt length)" 를 이용하는 방법.
2. Caller Function 에서 call 이후 "add esp, n(pt length)"를 이용하는 방법이 있다.



스택 사용 - 지역 변수 사용

.text:004C4980 sub_4C4980 proc near .text:004C4980 .text:004C4980 var_84= dword ptr -84h .text:004C4980 ptPos= tagPOINT ptr -80h .text:004C4980 var_78= byte ptr -78h .text:004C4980 var_C= dword ptr -0Ch .text:004C4980 var_4= dword ptr -4 .text:004C4980 .text:004C4980 000 push ebp .text:004C4981 004 mov ebp, esp .text:004C4983 004 and esp, 0FFFFFFF8h .text:004C4986 004 mov eax, large fs:0 .text:004C498C 004 push 0FFFFFFFFh .text:004C498E 008 push offset SEH_4C4980 .text:004C4993 00C push eax .text:004C4994 010 mov large fs:0, esp .text:004C499B 010 sub esp, 78h .text:004C499E 088 push esi .text:004C499F 08C call ds:GetTickCount .text:004C49A5 08C push eax ... 이하 생략

IDA 에서 코드를 분석해서 var_X 의 형태로 지역 변수 Offset 을 만들어 두었음.
x86 어셈블러에서는 지역 변수가 존재하는 스택의 위치를 접근할 때,
EBP 레지스터를 이용해 지역 변수가 존재하는 스택 위치를 참조할 때 Offset를 이용해 접근할 수 있도록 했다.

ESP 레지스터를 사용하지 않고, EBP 레지스터를 사용하는 이유는 해당 프레임과 관련 있는 어떤 내용이라도 항상 접근할 수 있는 참조 위치를 가지고 있기 때문이다.
ex) EBP+var_ 4 형태로 오프셋을 이용해 함수 Argument와 지역 변수의 스택에 접근한다.



참고 :

https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)

http://www.cs.virginia.edu/~evans/cs216/guides/x86.html

http://m.blog.naver.com/gunner98/110043634431

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함