실수로 발생한 프로그램의 결함
Linux 의 대표적인 debugger 중 하나
pwndbg
위의 링크를 클릭하고 README 파일에 나와있는 내용대로 따라한다
DreaHack - SystemHacking 커리큘럼에서는 pwndbg 를 기준으로 설명할 것이므로, pwndbg 만 설치해야한다.
$ git clone https://github.com/pwndbg/pwndbg
$ cd pwndbg
$ ./setup.sh
처음에는 이런 오류가 나왔엇는데 잘 이유를 알 수 없었다
그래서 다시 시도 해보니
다음 사진과같이 성공적으로 설치가 되었다.
라고 생각했다...
// Name: debugee.c
// compile: gcc -o debugee debugee.c -no-pie
Unix 계열 운영체제의 실행, 오브젝트 파일, 공유 라이브러리, 또는 코어덤프를 할 수 있게 하는 바이너리 파일이다
ELF 의 헤더 필드 중 하나
운영체제가 ELF 를 실행 할 때, 진입점의 값부터 프로그램을 실행한다.
Entry Point adress: 0x640 주목!!
gdb 의 start 명령어는 진입점부터 프로그램을 분석할 수 있게 해주는 gdb 의 명령어
|> DISASM 영역의 화살표가 가리키는 주소는 현재 rip 의 값이다.
start 명령어 실행시 명령을 찾을수 없다고 떳다. 그래서 구글링을 해보니,
애초에 pwndbg 를 잘못 설치했을 가능성이 높은 것 같았다...
어디서 부터 잘못되었는지 모르겠어서 리눅스를 재설치를 하기로 했다...
기존의 학과 리눅스 수업에 사용하던 "myUbuntu" 리눅스와 시스템해킹 공부를 위해 사용하는 리눅스를 분리하는게 좋을 것 같다고도 생각이 들어서 "SystemHacking" 이라는 새로운 환경을 구축하였다.
$ sudo apt update
$ sudo apt install openssh-server
$ sudo apt update
$ sudo apt install tasksel
$ sudo tasksel install ubuntu-desktop
$ sudo reboot
앞서 readelf 로 확인해본 Entry Point adress 와 start 명령어로 살펴본, DISASM 영역의 화살표가 가리키는 주소 (즉, rip 값) 이 일치한다.
pwndbg == Context 이다
-> 디버거를 이용하여 프로그램의 실행과정을 자세히 관찰하려면 컴퓨터의 각종 메모리를 한눈에 파악 할 수있어야함
-> 따라서 pwndbg (Context) 는 가독성있게 표현 할 수 있는 인터페이스를 갖추고있다.
< Context 의 4 영역>
1. register: 레지스터의 상태
2. disasm: rip 부터 여러 줄에 걸쳐 디스어셈블 된 결과
3. stack: rsp 부터 여러 줄에 걸쳐 스택의 값들을 보여준다
4. gacktrace: 현재 rip 에 도달할 때까지 어떤 함수들이 중첩되어 호출 됐는지 보여준다
-> 이들은 어셈블리를 실행할 때마다 갱신되어 방금 실행한 어셈블리 명령어가 메모리에 어떤 영향을 줬는지 쉽게 파악 할 수 있게 돕는다
특정 주소에 Break Point (중단점) 을 설정하는 기능
원하는 함수에 중단점 설정
중단된 지점부터 다시 세밀하게 분석 가능하다
중단된 프로그램을 계속 실행 시키는 기능
start 는 진입점부터 프로그램을 분석 할 수 있도록 자동으로 중단점 설정
run 은 단순 실행
중단점 설정안해놓으면 프로그램이 끝까지 실행
(지금은 위에서 "b* main" 으로 설정해놓음)
gdb 는 프로그램을 어셈블리 코드 단위로 실행하고 결과를 보여준다.
따라서 gdb 는 기계어를 Disassemble (디스어셈블) 하는 기능 탑재
pwndbg 는 disassemble 된 결과를 가독성 좋게 출력해주는 기능
pwndbg> disassemble main
pwndbg> u
pwndbg> nearpc
명령어를 한줄씩 자세히 분석한다
어셈블리어 명령어를 한줄씩 실행
call 등을 통해 한줄 서브루틴을 호출하는 경우
서브루틴 내부로 들어가지 않는다
서브루틴 내부로 들어간다
pwndbg> b* main+57
pwndbg> ni
pwndbg> ni 시 오류 발생
pwndbg> b* [함수이름] + [상수] = 해당영역에 breakPoint 를 설정한다
참고 블로그
따라서 +[상수] 의 57이 오류를 일으켰을것이라는 생각이 들었다.
그러나 disassemble main 에서 main 함수의 printf 문을 가리키는 상수가 56 이므로 +57 이 맞다는 결과를 얻었다.
다음 사진과 같이 'The Program is not being run ' 라는 error 메시지가 출력이 된다.
구글링 결과
'The Program is not being run' error
실행 또는 시작 명령을 사용하여 프로그램을 재시작 하면 된다고 한다.
따라서,
$ gdb debugee
pwndbg> start
pwndbg> b* main+57
pwndbg> ni
즉, 다시 start 명령어를 쳐주니 정상적으로 실행이 되었다!
프로그램을 분석하다 보면 가상 메모리에 존재하는 임의 주소의 값을 관찰해야할 때가 있는데, 이를 위해서 gdb에서 기본적으로 x라는 명령어를 제공합니다. x를 이용하면 특정 주소에서 원하는 길이만큼의 데이터를 원하는 형식으로 인코딩하여 볼수 있다.
telescope 은 pwndbg 가 제공하는 메모리 덤프기능이다.
특정 주소의 메모리 값들을 보여주는걸 넘어서서 메모리가 참조하고 있는 주소를 재귀적으로 탐색하여 값을 보여준다
vmmap 은 가상메모리의 LayOut 을 보여준다.
mapping 된 파일 영역은 해당 파일의 경로까지 보여준다.
어떤 파일을 메모리에 적재하는 것
위 메모리 레이아웃에서 매핑된 파일은 3가지다
1. /home/dreamhack/debugee
2. /lib/x86_64-linux-gnu/libc-2.27.so,
3. /lib/x86_64-linux-gnu/ld-2.27.so
리눅스에서 ELF 를 실행 할때, 먼저 ELF 의 코드와 여러 데이터를 가상 메모리에 매핑하고, 해당 ELF 에 링크된 공유 오브젝트 (Shared Object, so) 를 추가로 메모리에 매핑한다.
공유 오브젝트(so) 는 윈도우의 DLL 과 대응되는 개념이다.
(자주 사용되는 함수들을 미리 컴파일 해 둔것)
C 언어의 printf, scanf 가 리눅스에서는 libc(library C) 에 구현되어있다.
숫자 또는 알파벳이 아닌 입력값은 gdb 를 통해 입력 할 수 없기 때문에 파이썬으로 입력값을 생성하고, 이를 사용하여야 한다.
run 의 인자 $() 와 함께 파이썬 코드를 입력하면 값을 전달 할 수 있다.
pwndbg> r $(python -c 'print "\xff"*100')
starting program: /home/s0ngsari/a $(python -c 'print "\xff"*100')
argv[1] ?????????????????????????????????????????????
argv[1] 에 임의의 값을 전달하고, 값을 입력하는 명령어