주어진 어셈블리어
main() - 1
push rbp
rbp는 스택의 첫 시작 주소가 저장되는 포인터 레지스터다. 그래서 push rbp를 통해 main 함수의 시작 주소가 스택에 저장된다.
mov rbp, rsp
Rsp는 항상 스택의 끝 지점 주소를 갖고 있는 포인터 레지스터다. 현재 main을 호출한 상태이므로 가장 끝 지점과 시작 주소가 같을 것이다. 그래서 rsp에 rbp 값을 넣어주어 rbp는 항상 시작 주소를 갖고 있고 rsp는 계속 움직이며 스택의 끝 지점을 가리키고 있게 된다.
sub rsp, 16
이 명령어는 현재 rsp가 가리키는 주소를 16byte만큼 빼 스택을 사용할 수 있도록 준비하는 것이다. (지역변수 공간 확보)
mov DWORD PTR [rbp-4], 1
rbp – 4 주소에 1을 넣는다.
mov eax, DWORD PTR [rpb-4]
방금 rbp-4 주소에 넣은 1을 eax에 복사해준다.
mov edi, eax
1이 저장되어 있는 eax를 또 edi에 복사해준다.
mov eax, 0
eax의 값을 0을 바꿔준다.
call square
square이라는 함수를 호출해준다.
square()
push rbp
mov rbp, rsp
Square 함수로 들어오면 main함수가 시작했을 때처럼 rbp와 rsp를 설정해준다.
mov DWORD PTR [rbp-4], edi
main함수에서 edi에 저장한 숫자 1을 현재 rbp-4 주소에 저장해준다.
mov eax, DWORD PTR [rbp-4]
Rbp-4 주소에 저장한 1을 eax로 복사해준다.
imul eax, eax
imul을 통해 eax와 eax를 곱해준다. 즉, 제곱을 하는 함수였다는 것을 알 수 있다. (결과는 eax에 저장됨)
pop rbp
함수를 호출한 main함수로 돌아갈 준비를 한다.
ret
square 함수를 끝내고 main함수로 돌아간다. (ret에 대해서는 main()에서 설명하도록 하겠다)
main() - 2
mov DWORD PTR [rbp-8], eax
함수의 반환된 값은 eax에 저장이 된다. 그러므로 eax에 저장된 값을 rbp-8주소에 저장한다.
mov eax, 0
eax를 다시 0으로 바꿔준다.
* eax를 계속 0을 바꿔주는 이유는 return 0; 때문이다. Main()도 함수이기 때문에 반환 값이 eax에 들어가는데 return 0;을 해야 함수가 제대로 종료 되므로 eax 값을 계속 0으로 초기화 시켜주는 것이다.
[ 함수를 마무리 시켜주는 에필로그 ]
leave
move ebp, esp
pop ebp
위와 같이 구성되어 사용한 메모리 스택을 비우고 호출한 메모리의 베이스 주소를 ebp에 넣어준다.
ret
pop eip
jmp eip
위와 같이 구성되어 다음 명령 주소로 이동한다.
변환한 C 코드
#include <stdio.h>
int square(int a) {
return a * a;
}
int main() {
int a = 1;
square(a);
return 0;
}
'Study > Reversing' 카테고리의 다른 글
[Reversing] 어셈블리어 C로 변환하기 #2 (0) | 2020.09.22 |
---|---|
[Reversing] PE Header 개념 정리 & wow64 fs redirection (0) | 2020.09.21 |
[Reversing] abex' Crackme #1 (0) | 2020.09.14 |
[Reversing] x86(32 bit) / x64(64 bit) 호출규약 (0) | 2020.09.14 |
[Reversing] Stack 구조와 Stack Frame (0) | 2020.09.08 |
댓글