gdb 분석 실습
※ 스택 프레임(stack frame)
메모리의 스택 영역 = 함수 호출과 관계되는 지역 변수와 매개 변수가 저장되는 영역.
함수의 호출과 함께 스택 영역이 할당되며, 호출 완료 시 소멸함.
함수가 호출되면 스택에는
- 함수의 매개변수
- 호출 끝난 후 돌아갈 리턴주소 값
- 함수에서 선언된 지역 변수
등이 저장된다.
스택 프레임 : 스택 영역에 차례대로 저장되는 함수의 호출 정보
-> 함수의 호출이 모두 끝난 뒤에, 호출되기 이전 상태로 되돌아갈 수 있다.
더이상의 여유 공간이 없을 때 또 다시 스택 프레임을 저장하면 데이터는 스택 영역을 넘어가서 저장되게 된다.
-> 프로그램 오동작 / 보안상 취약점 발생
gcc -g test_gdb.c -o test 로 컴파일하고
gdb test
일단 드림핵에서 배운대로 readelf -h를 이용해서 해당 파일의 진입점이 어딘지 확인했다.
진입점은 0x1040이다.
이걸 그냥 start 해버리면 DISASM 파트에서 main부터 시작하는 걸로 나온다.
찾아보니 진입점부터 실행하게 하려면 b _start로 진입점에 브레이크를 걸어주면된다고 한다.
<main>이 아닌 <_start>로 되어있다.
list로 소스 내용을 10줄 씩 보여주도록 했다.
l function : function 함수의 소스를 출력한다. 그냥 list로 출력하는 것과 별반 차이가 없어 보인다.
l 10 : 10행 '{' 을 기준으로 이전 5행, 이후 5행을 출력한다. 코드 자체가 13줄까지밖에 없다보니 더 길었다면 15행까지 출려했겠지만 여기서는 위와같이.
오른쪽 사진도 그냥 어떤 파일에서 출력할 것인지 파일명만 추가해준거라고 보면된다. 출력 결과는 왼쪽과 동일하다.
r로 프로그램을 실행해봤더니 위와 같은 문구가 뜨면서 뭐 나오는게 없이 종료된다.
그래서 function 함수에 브레이크 포인트를 설정하고 실행해봤다.
disas [함수이름]을 이용하면 위와 같이 안하고 디스어셈블 결과를 얻을 수 있다.
일단 프롤로그, 에필로그는 함수 준비과정, 마무리 과정을 의미한다.
RSP는 현재 스택 주소의 맨 윗쪽 주소를 의미한다고 배웠는데, 그러면
mov rbp, rsp는 현재 스택의 맨 위 주소를 rbp에 대입해주는건가?
disas main에서 화살표로 표시된 부분부터 <+14>까지는 1, 2, 3이라는 값을 각각 edi, esi, edx에 저장한다.
값을 저장하고 난 후 function 함수를 call하는 것으로 보인다.
<+24>에는 nop라고 나오는데 nop는 공백처리하는 부분이다. CPU에서 쉬어가는 부분.
pop은 스택에 존재하는 값을 가져오는 명령어이다.
pop rbp; ret은 같이 보는 것이 좋다고 한다. 이 명령어 세트는 이전 스택 프레임으로 복귀한다는 의미이다.
-> 함수의 에필로그 과정
disas function도 해석해보자
일단 첫 두 줄은 프롤로그.
edi의 값을 DWORD PTR [rbp-0x24]에 넣고, esi와 edx도 각각 그 앞에 쓰인 곳에 저장한다.
main에서 edi, esi, edx에 숫자 값을 넣어줬던 걸 생각하면 이 인자들이 fuction함수에서 어떤 위치에 저장되는 것으로 보인다.
소스코드를 보면 buffer1과 buffer2가 존재하니 그것과 연관이 있지 않을까 생각한다.
DWORD PTR [주소] = 해당 주소에서 double word(32비트)를 읽겠다는 뜻.
SYSV 호출 규약을 따르는 파일이다.
따라서 이 파일은 다음과 같은 특징을 갖는다.
1. 6개의 인자를 RID, RSI, RDX, RCX, R8, R9 순서대로 저장하여 전달한다.
2. 호출자가 인자 전달에 사용된 스택을 정리한다.
3. 함수의 반환 값은 RAX로 전달한다.
현재 이 파일에서는 function함수에 인자 세개를 전달한다.
레지스터를 확인하면 순서대로 저장하여 전달한다더니, 정말로
첫 번째 인자 1 -> rdi
두 번째 인자 2 -> rsi
세 번째 인자 3 -> rdx
이 순으로 저장돼있는 모습을 확인할 수 있다.
(gdb) x/4gx $rsp examines 4 hex quadwords from stack starting at current rsp
이건 드림핵 보고 x/4gx가 뭔가 해서 구글링해본 결과인데 사실 이걸 봐도 완벽히 이해는 안된다.
pwndbg에서 run시켰을 때 나오는 stack, registers 부분이 좀 이해하기 어려웠다. 레지스터는 아무래도 호출 규약과 관련있어보인다는 건 느꼈는데 스택이 정말헷갈린다. 스택은 후입선출 방식이라는 것때문에 그런지 스택 부분을 읽을 때 아래서부터 읽을지 위에서부터 읽어야할지 잘 모르겠다. 그리고 모르겠는 숫자들도 너무 많고..
머리를 좀 굴려봤는데, b _start를 하지 않고 start 명령을 내리면 진입점이 아닌 main에서부터 시작한다는 게 _start 함수가 main보다 먼저 실행돼서 라는 데 그러면 이 함수 실행 구조가
1. __libc_start_main
2. main
3. function
4. main
5. __libc_start_main
이런 식으로 돌아가는 건가 싶다. 이게 맞는지는 잘 모르겠지만..
이 화면은 또 si가 아닌 ni로 계속 실행시켜서 나온 스택이라 또 다르지 않을까 싶다.