> gdb
// 실행은 참 쉬움
pwndbg> exit
// 종료도 참 쉬움
pwndbg> show disassembly-flavor
→ Intel 이면 Intel로 뜸
만약 다른 포맷이면 변경해야 함
// Name : debugee.c
// Compile : gcc -o debugee debugee.c -no-pie
// --------------------------------------------
#include <stdio.h>
int main(void) {
int sum = 0;
int val1 = 1;
int val2 = 2;
sum = val1 + val2;
printf("1 + 2 = %d\n", sum);
return 0;
}
> readelf -h debugee // ELF (Executable and Linkable Format
→ 온갖 정보가 다 뜰거임
📌 Entry point address : 프로그램이 실제로 실행되는 주소(16진수)
> gdb debugee
gdb + 실행파일
pwndbg> start
Temporary breakpoint 1 at 0x40112a # 여기 멈춰 있다는 의미
Temporary breakpoint 1, 0x000000000040112a in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA # 세그먼트 정보들
────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────
# 레지스터 정보들
*RAX 0x401126 (main) ◂— push rbp
*RBX 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
*RCX 0x403e00 (__do_global_dtors_aux_fini_array_entry) —▸ 0x4010f0 (__do_global_dtors_aux) ◂— endbr64
*RDX 0x7fffffffde78 —▸ 0x7fffffffe213 ◂— 'CLUTTER_IM_MODULE=fcitx'
*RDI 0x1
*RSI 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
R8 0x0
*R9 0x7ffff7fcf6a0 (_dl_fini) ◂— push rbp
*R10 0x7ffff7fcb878 ◂— 0xc00120000000e
*R11 0x7ffff7fe18c0 (_dl_audit_preinit) ◂— mov eax, dword ptr [rip + 0x1b552]
R12 0x0
*R13 0x7fffffffde78 —▸ 0x7fffffffe213 ◂— 'CLUTTER_IM_MODULE=fcitx'
*R14 0x403e00 (__do_global_dtors_aux_fini_array_entry) —▸ 0x4010f0 (__do_global_dtors_aux) ◂— endbr64
*R15 0x7ffff7ffd020 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0
*RBP 0x7fffffffdd50 ◂— 0x1 # 레지스터 외워라 좀;
*RSP 0x7fffffffdd50 ◂— 0x1
*RIP 0x40112a (main+4) ◂— sub rsp, 0x10
─────────────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────────────
► 0x40112a <main+4> sub rsp, 0x10 # 여기서 멈춰 있다고 친절하게 알려줌
0x40112e <main+8> mov dword ptr [rbp - 4], 0
0x401135 <main+15> mov dword ptr [rbp - 8], 1
0x40113c <main+22> mov dword ptr [rbp - 0xc], 2
0x401143 <main+29> mov edx, dword ptr [rbp - 8]
0x401146 <main+32> mov eax, dword ptr [rbp - 0xc]
0x401149 <main+35> add eax, edx
0x40114b <main+37> mov dword ptr [rbp - 4], eax
0x40114e <main+40> mov eax, dword ptr [rbp - 4]
0x401151 <main+43> mov esi, eax
0x401153 <main+45> lea rax, [rip + 0xeaa]
───────────────────────────────────────────[ STACK ]───────────────────────────────────────────
00:0000│ rbp rsp 0x7fffffffdd50 ◂— 0x1
01:0008│ 0x7fffffffdd58 —▸ 0x7ffff7df318a (__libc_start_call_main+122) ◂— mov edi, eax
02:0010│ 0x7fffffffdd60 ◂— 0x0
03:0018│ 0x7fffffffdd68 —▸ 0x401126 (main) ◂— push rbp
04:0020│ 0x7fffffffdd70 ◂— 0x100000000
05:0028│ 0x7fffffffdd78 —▸ 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
06:0030│ 0x7fffffffdd80 —▸ 0x7fffffffde68 —▸ 0x7ffffffhack/debugee'
07:0038│ 0x7fffffffdd88 ◂— 0x2181d7828d77fade
─────────────────────────────────────────[ BACKTRACE ]────────
► f 0 0x40112a main+4
f 1 0x7ffff7df318a __libc_start_call_main+122
f 2 0x7ffff7df3245 __libc_start_main+133
f 3 0x401061 _start+33 # entry point + 33 byte
# 실제 프로그램이 시작하는 시점은 main이 아니다!
──────────────────────────────────────────────────────────────
pwndbg>
메인에 걸어두는 습관이 있으면 좋다.
여러개 걸어둘 수 있다. 그리고 순서도 상관 없다.
pwndbg> break *main # 메인 함수에 브레이크 걸라는 명령
Breakpoint 1 at 0x401126 # 메인 함수에서 break 걸었다!
중단점 설정된곳까지 실행
pwndbg> run # 프로그램 실행
Starting program: /home/eugene/Workspace/hack/debugee
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, 0x0000000000401126 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────[ REGISTERS / show-flags off / show-compact-regs off ]────
*RAX 0x401126 (main) ◂— push rbp
*RBX 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
*RCX 0x403e00 (__do_global_dtors_aux_fini_array_entry) —▸ 0x4010f0 (__do_global_dtors_aux) ◂— endbr64
*RDX 0x7fffffffde78 —▸ 0x7fffffffe213 ◂— 'CLUTTER_IM_MODULE=fcitx'
*RDI 0x1
*RSI 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
R8 0x0
*R9 0x7ffff7fcf6a0 (_dl_fini) ◂— push rbp
*R10 0x7ffff7fcb878 ◂— 0xc00120000000e
*R11 0x7ffff7fe18c0 (_dl_audit_preinit) ◂— mov eax, dword ptr [rip + 0x1b552]
R12 0x0
*R13 0x7fffffffde78 —▸ 0x7fffffffe213 ◂— 'CLUTTER_IM_MODULE=fcitx'
*R14 0x403e00 (__do_global_dtors_aux_fini_array_entry) —▸ 0x4010f0 (__do_global_dtors_aux) ◂— endbr64
*R15 0x7ffff7ffd020 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0
*RBP 0x1
*RSP 0x7fffffffdd58 —▸ 0x7ffff7df318a (__libc_start_call_main+122) ◂— mov edi, eax
*RIP 0x401126 (main) ◂— push rbp
─────────────[ DISASM / x86-64 / set emulate on ]─────────────
► 0x401126 <main> push rbp # 여기서 멈췄다! 우리가 원했던 메인함수에 브레이크 걸림
0x401127 <main+1> mov rbp, rsp
0x40112a <main+4> sub rsp, 0x10
0x40112e <main+8> mov dword ptr [rbp - 4], 0
0x401135 <main+15> mov dword ptr [rbp - 8], 1
0x40113c <main+22> mov dword ptr [rbp - 0xc], 2
0x401143 <main+29> mov edx, dword ptr [rbp - 8]
0x401146 <main+32> mov eax, dword ptr [rbp - 0xc]
0x401149 <main+35> add eax, edx
0x40114b <main+37> mov dword ptr [rbp - 4], eax
0x40114e <main+40> mov eax, dword ptr [rbp - 4]
──────────────────────────[ STACK ]───────────────────────────
00:0000│ rsp 0x7fffffffdd58 —▸ 0x7ffff7df318a (__libc_start_call_main+122) ◂— mov edi, eax
01:0008│ 0x7fffffffdd60 ◂— 0x0
02:0010│ 0x7fffffffdd68 —▸ 0x401126 (main) ◂— push rbp
03:0018│ 0x7fffffffdd70 ◂— 0x100000000
04:0020│ 0x7fffffffdd78 —▸ 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
05:0028│ 0x7fffffffdd80 —▸ 0x7fffffffde68 —▸ 0x7fffffffe1ef ◂— '/home/eugene/Workspace/hack/debugee'
06:0030│ 0x7fffffffdd88 ◂— 0xc884d300c9241961
07:0038│ 0x7fffffffdd90 ◂— 0x0
────────────────────────[ BACKTRACE ]─────────────────────────
► f 0 0x401126 main
f 1 0x7ffff7df318a __libc_start_call_main+122
f 2 0x7ffff7df3245 __libc_start_main+133
f 3 0x401061 _start+33
──────────────────────────────────────────────────────────────
pwndbg> continue # 얘를 입력하면 프로그램이 끝까지 실행됨
Continuing.
1 + 2 = 3
[Inferior 1 (process 17990) exited normally]
특정 부분의 어셈블리 코드를 볼 수 있다.
유사한 명령어 : u, nearpc, pdisassemble
pwndbg> disassemble main
Dump of assembler code for function main:
0x0000000000401126 <+0>: push rbp
0x0000000000401127 <+1>: mov rbp,rsp
0x000000000040112a <+4>: sub rsp,0x10
0x000000000040112e <+8>: mov DWORD PTR [rbp-0x4],0x0
0x0000000000401135 <+15>: mov DWORD PTR [rbp-0x8],0x1
0x000000000040113c <+22>: mov DWORD PTR [rbp-0xc],0x2
0x0000000000401143 <+29>: mov edx,DWORD PTR [rbp-0x8]
0x0000000000401146 <+32>: mov eax,DWORD PTR [rbp-0xc]
0x0000000000401149 <+35>: add eax,edx
0x000000000040114b <+37>: mov DWORD PTR [rbp-0x4],eax
0x000000000040114e <+40>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000401151 <+43>: mov esi,eax
0x0000000000401153 <+45>: lea rax,[rip+0xeaa] # 0x402004
0x000000000040115a <+52>: mov rdi,rax
0x000000000040115d <+55>: mov eax,0x0
0x0000000000401162 <+60>: call 0x401030 <printf@plt>
0x0000000000401167 <+65>: mov eax,0x0
0x000000000040116c <+70>: leave
0x000000000040116d <+71>: ret
End of assembler dump.
공통점 : 어세믈리 명령어를 한줄 씩 실행
차이점 :
ni : 서브루틴의 내부로 들어가지 않음
si : 서브루틴의 내부로 진입
pwndbg> b *main+40
Breakpoint 2 at 0x40114e
pwndbg> b *main
Breakpoint 3 at 0x401126
# 일단은 두 군데에 브레이크 걸어둠
# si, ni 사용할때마다 한칸씩 앞으로 감
# continue는 브레이크 걸린 부분까지 하이패스
📌 si로 진입 후 함수 끝까지 한번에 실행하 : finish
x/숫자단위포맷 시작주소
→ rsp에서 80바이트를 8바이트씩 hex형식으로 출력x/10gx $rsp
📌 o : 8진법
x : 16진법
u : 10진법
t : 2진법
b : 1byte 단위
h : 2byte 단위 half word
w : 4byte 단위 word
g : 8byte 단위 giant
i : 역어셈블된 명령어의 명령 메모리를 볼 수 있음
c : ASCII 표의 바이트를 자동으로 볼 수 있음
S : 문자 데이터의 전체 문자열을 보여줌
pwndbg> x/10gx $rsp # rsp에서 80byte를 8byte씩 hex형식으로 출력
0x7fffffffdd98: 0x00007ffff7df318a 0x0000000000000000
0x7fffffffdda8: 0x0000000000401126 0x0000000100000000
0x7fffffffddb8: 0x00007fffffffdea8 0x00007fffffffdea8
0x7fffffffddc8: 0xe93492b27d999a21 0x0000000000000000
0x7fffffffddd8: 0x00007fffffffdeb8 0x0000000000403e00
pwndbg> x/5i $rip # rip부터 5줄의 어셈블리 명령어 출력
=> 0x401126 <main>: push rbp
0x401127 <main+1>: mov rbp,rsp
0x40112a <main+4>: sub rsp,0x10
0x40112e <main+8>: mov DWORD PTR [rbp-0x4],0x0
0x401135 <main+15>: mov DWORD PTR [rbp-0x8],0x1
pwndbg> x/s 0x400000 # 특정 주소의 문자열 출력
0x400000: "\177ELF\002\001\001"
pwndbg> print args
$1 = 2
pwndbg> print argv[0]
$2 = 0x7fffffffe20b "/home/eugene/Workspace/hack/bugfile"
pwndbg> info reg # 레지스터 전체값
rax 0x555555555149 93824992235849
rbx 0x7fffffffde88 140737488346760
rcx 0x555555557dd8 93824992247256
rdx 0x7fffffffdea0 140737488346784
rsi 0x7fffffffde88 140737488346760
rdi 0x2 2
rbp 0x7fffffffdd70 0x7fffffffdd70
rsp 0x7fffffffdd50 0x7fffffffdd50
r8 0x0 0
r9 0x7ffff7fcf6a0 140737353938592
r10 0x7ffff7fcb878 140737353922680
r11 0x7ffff7fe18c0 140737354012864
r12 0x0 0
r13 0x7fffffffdea0 140737488346784
r14 0x555555557dd8 93824992247256
r15 0x7ffff7ffd020 140737354125344
rip 0x555555555158 0x555555555158 <main+15>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
pwndbg> info reg $rsp # 특정 레지스터 값
rsp 0x7fffffffdd50 0x7fffffffdd50
x/[조회하는 메모리 범위]xw[조회하는 메모리 지점]
0x7fffffffdd50: 0xffffde88 0x00007fff 0x00000000 0x00000002
0x7fffffffdd60: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffdd70: 0x00000002 0x00000000 0xf7df318a 0x00007fff
0x7fffffffdd80: 0x00000000 0x00000000 0x55555149 0x00005555
0x7fffffffdd90: 0x00000000 0x00000002 0xffffde88 0x00007fff
0x7fffffffdda0: 0xffffde88 0x00007fff 0xc2fecbe3 0x6cd326ff
0x7fffffffddb0: 0x00000000 0x00000000 0xffffdea0 0x00007fff
0x7fffffffddc0: 0x55557dd8 0x00005555 0xf7ffd020 0x00007fff
어렵지만 재밌다.