스택 프레임(Stack Frame)이란 함수가 호출될 때, 그 함수만의 스택 영역을 구분하기 위하여 생성되는 공간이다. 이 공간에는 함수와 관계되는 지역 변수, 매개변수가 저장되며, 함수 호출 시 할당되고, 함수가 종료될때 소멸한다. 이렇게 스택 영역에 차례대로 저장되는 함수의 호출 정보를 스택 프레임이라고 하며, 스택 프레임 덕분에 함수의 호출이 모두 끝난 뒤에, 해당 함수가 호출되기 이전 상태로 되돌아갈 수 있다.
아래에서 부터는 자주 사용하는 어셈블리어에 대해 알아보고 간단한 예제를 통해 스택 프레임에 대해 알아보겠다.
자주 사용하는 어셈블리 명령어와 사용 예시
push
push eax: eax의 값을 스택에 저장한다.
pop
pop eax: 스택의 가장 상위에 있는 값을 꺼내서 eax에 저장한다.
mov
mov eax, ebx: eax에 ebx의 값을 넣는다.
inc
inc eax: eax의 값을 1증가시킨다.
dec
dec eax: eax의 값을 1감소시킨다.
add
add eax,ebx:eax와 ebx의 값을 더하여 eax에 저장한다.
sub
sub eax, ebx: eax에 ebx를 뺀 값을 eax에 저장한다.
call
call proc: 프로시저를 호출한다.
ret: 호출했던 바로 다음 지점으로 이동한다
cmp
cmp eax, ebx eax와 ebx의 값을 비교한다.
jump
jump proc: 특정한 곳으로 분기한다.
nop: 아무것도 하지않는다.
참고한 블로그: https://jiravvit.tistory.com/entry/%EC%8A%A4%ED%83%9D-%ED%94%84%EB%A0%88%EC%9E%84-Stack-Frame
먼저 간단한 예제를 작성한 후
sum.c를 어셈블리어인 sum.a로 변환해주었다.
실습을 위해 -fno-stack-protector 옵션을 이용하여 보호기능을 꺼주었다.
main()함수에서 sum()함수를 호출했을 때의 스택모양을 간단히 나타내면 다음과 같다.
이제 코드에서 main()에서 sum()을 call하는 부분까지를 살펴보겠다.
pushq %rbp // RBP를 push해서 RET 위로 집어넣는다.
movq %rsp, %rbp // rsp를 rbp로 이동시켜 동일한 위치를 가리키게 한다.
subq $16, %rsp // rsp를 16만큼 빼주어서 16만큼의 공간을 확보한다.
movl $2, %esi //esi에 2를 넣는다.
movl $1, %edi // edi에 1을 넣는다.
call sum // sum 함수를 호출한다.
이걸 그림으로 나타내면 아래와 같다.
이제 sum 함수를 살펴보자.
pushq %rbp // RBP를 push해서 RET 위로 들어가게 된다.
movq %rsp, %rbp // rsp를 rbp로 이동시켜 같은 위치를 가리키게 한다.
movl %edi, -4(%rbp) // 1을 넣는다
movl %esi, -8(%rbp) // 2를 넣는다
movl -4(%rbp), %edx // 1의 값을 edx 레지스터에 넣는다.
movl -8(%rbp), %eax // 2의 값을 eax 레지스터에 넣는다.
addl %edx, %eax // edx값을 eax에 더해준다.
popq %rbp // rbp를 pop하여 ret으로 돌아가게 한다.
이제 아까 보지 못했던 main()의 남은 부분을 살펴보자.
movl %eax, -4(%rbp) // eax에 저장된 3을 rbp-4의 위치에 넣는다.
movl -4(%rbp), %eax // 3을 다시 eax에 넣는다.
leave
ret // 3이 반환된다.
'.study > pwnable' 카테고리의 다른 글
[포너블 기초] 함수 호출 규약 (0) | 2023.09.14 |
---|---|
[포너블 기초] Docker (0) | 2023.09.13 |
[포너블 기초] x86-32, x86-64 레지스터 (0) | 2023.09.12 |
[포너블 기초] 메모리 구조 (0) | 2023.09.11 |
[포너블 기초] 컴퓨터 구조 (0) | 2023.09.10 |