티스토리 뷰

gdb

: 리눅스의 대표적인 디버거.

 

실습예제

// 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;
}

 

gdb debugee로 디버깅 시작

$ gcc -o debugee debugee.c -no-pie
$ gdb debugee

 

리눅스는 실행파일의 형식으로 ELF (Executable and Linkable Format)를 규정하고 있다. ELF는 크게 헤더 와 여러 섹션 들로 구성되어 있다. 헤더에는 실행에 필요한 여러 정보가 적혀 있고, 섹션들에는 컴파일된 기계어 코드, 프로그램 문자열을 비롯한 여러 데이터가 포함되어 있다.

 

ELF의 헤더 중에 진입점(Entry Point, EP)이라는 필드가 있는데, 운영체제는 ELF를 실행할 때, 진입점의 값부터 프로그램을 실행한다. readelf로 확인해본 결과, debugee의 진입점은 0x401050 이다.

$ readelf -h debugee
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x401050
  Start of program headers:          64 (bytes into file)
  Start of section headers:          13912 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30

 

gdb의 entry 명령어는 진입점부터 프로그램을 분석할 수 있게 해주는 gdb의 명령어이다. DISASM영역의 화살표(►)가 가리키는 주소는 현재 rip의 값인데, entry 명령어를 실행하고 보면 0x401050을 가리키고 있다. 이는 앞서 살펴본 프로그램 진입점의 주소와 일치한다.

 

pwndbg> entry
Temporary breakpoint 12 at 0x401050
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 12, 0x0000000000401050 in _start ()
[중략]
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
 ► 0x401050 <_start>       endbr64
   0x401054 <_start+4>     xor    ebp, ebp
   0x401056 <_start+6>     mov    r9, rdx
   0x401059 <_start+9>     pop    rsi
   0x40105a <_start+10>    mov    rdx, rsp
   0x40105d <_start+13>    and    rsp, 0xfffffffffffffff0
   0x401061 <_start+17>    push   rax
   0x401062 <_start+18>    push   rsp
   0x401063 <_start+19>    xor    r8d, r8d
   0x401066 <_start+22>    xor    ecx, ecx
   0x401068 <_start+24>    mov    rdi, main                     <0x401136>
[중략]
pwndbg>

 

프로그램은 실행되면서 레지스터를 비롯한 여러 메모리에 접근한다. 따라서 디버거를 이용하여 프로그램의 실행 과정을 자세히 관찰하려면 컴퓨터의 각종 메모리를 한눈에 파악할 수 있는 것이 좋다. pwndbg는 주요 메모리들의 상태를 프로그램이 실행되고 있는 맥락(Context)이라고 부르며, 이를 가독성 있게 표현할 수 있는 인터페이스를 갖추고 있다.

 

context는 크게 4개의 영역으로 구분된다.

  1. REGISTERS: 레지스터의 상태를 보여준다.
  2. DISASM: rip부터 여러 줄에 걸쳐 디스어셈블된 결과를 보여준다.
  3. STACK: rsp부터 여러 줄에 걸쳐 스택의 값들을 보여준다.
  4. BACKTRACE: 현재 rip에 도달할 때까지 어떤 함수들이 중첩되어 호출됐는지 보여준다.

이들은 어셈블리를 실행할 때마다 갱신되어 방금 실행한 어셈블리 명령어가 메모리에 어떤 영향을 줬는지 쉽게 파악할 수 있게 한다.

context

 

break는 특정 주소에 중단점(breakpoint)을 설정하는 기능이고, continue는 중단된 프로그램을 계속 실행시키는 기능이다. break로 원하는 함수에 중단점을 설정하고, 프로그램을 계속 실행하면 해당 함수까지 멈추지 않고 실행한 다음 중단된다. 그러면 중단된 지점부터 다시 세밀하게 분석할 수 있다.

 

pwndbg> b *main
Breakpoint 2 at 0x401136
pwndbg> c
Continuing.

Breakpoint 2, 0x0000000000401136 in main ()
[중략]
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
 ► 0x401136 <main>       endbr64
   0x40113a <main+4>     push   rbp
   0x40113b <main+5>     mov    rbp, rsp
   0x40113e <main+8>     sub    rsp, 0x10
   0x401142 <main+12>    mov    dword ptr [rbp - 0xc], 0
   0x401149 <main+19>    mov    dword ptr [rbp - 8], 1
   0x401150 <main+26>    mov    dword ptr [rbp - 4], 2
   0x401157 <main+33>    mov    edx, dword ptr [rbp - 8]
   0x40115a <main+36>    mov    eax, dword ptr [rbp - 4]
   0x40115d <main+39>    add    eax, edx
   0x40115f <main+41>    mov    dword ptr [rbp - 0xc], eax
───────────────────────────────────[ STACK ]────────────────────────────────────
[생략]

 

앞의 start가 진입점부터 프로그램을 분석할 수 있도록 자동으로 중단점을 설정해줬다면, run 은 단순히 실행만 시킨다. 따라서 중단점을 설정해놓지 않았다면 프로그램이 끝까지 멈추지 않고 실행다.

 

pwndbg> r
Starting program: /home/dreamhack/debugee

Breakpoint 2, 0x0000000000401136 in main ()

disassembly

gdb는 프로그램을 어셈블리 코드 단위로 실행하고, 결과를 보여준다. 프로그램의 코드는 기계어로 이루어져 있으므로, gdb는 기계어를 디스어셈블(Disassemble)하는 기능을 기본적으로 탑재하고 있다. 추가로, pwndbg에는 디스어셈블된 결과를 가독성 좋게 출력해주는 기능이 있다. 

 

disassemble은 gdb가 기본적으로 제공하는 디스어셈블 명령어이다. 아래와 같이 함수 이름을 인자로 전달하면 해당 함수가 반환될 때 까지 전부 디스어셈블하여 보여준다.

 

 

gdb의 명령어

  • entry: 진입점에 중단점을 설정한 후 실행
  • break(b): 중단점 설정
  • continue(c): 계속 실행
  • disassemble: 디스어셈블 결과 출력
  • u, nearpc, pd: 디스어셈블 결과 가독성 좋게 출력
  • x: 메모리 조회
  • run(r): 프로그램 처음부터 실행
  • context: 레지스터, 코드, 스택, 백트레이스의 상태 출력
  • nexti(ni): 명령어 실행, 함수 내부로는 들어가지 않음
  • stepi(si): 명령어 실행, 함수 내부로 들어감
  • telescope(tele): 메모리 조회, 메모리값이 포인터일 경우 재귀적으로 따라가며 모든 메모리값 출력
  • vmmap: 메모리 레이아웃 출력
  • start: main() 심볼이 존재하면 main()에 중단점을 설정한 후 실행. main() 심볼이 없으면 진입점에 중단점을 설정한 후 실행
  • main: start 명령어와 동일

 


pwntools

파이썬으로 여러 개의 익스플로잇 스크립트를 작성하다 보면, 자주 사용하게 되는 함수들이 있는데,  이런 함수들을 반복적으로 구현하는 것은 비효율적이다. 그래서 시스템 해커들은 이들을 집대성하여 pwntools 라는 파이썬 모듈을 제작하였다. pwntools 덕분에 익스플로잇 제작은 전과 비교할 수 없을 정도로 간단하고, 쉬워졌다. 

 

  • process & remote: 로컬 프로세스 또는 원격 서버의 서비스를 대상으로 익스플로잇 수행
  • send & recv: 데이터 송수신
  • packing & unpacking: 정수를 바이트 배열로, 또는 바이트 배열을 정수로 변환
  • interactive: 프로세스 또는 서버와 터미널로 직접 통신
  • context.arch: 익스플로잇 대상의 아키텍처
  • context.log_level: 익스플로잇 과정에서 출력할 정보의 중요도
  • ELF: ELF헤더의 여러 중요 정보 수집
  • shellcraft: 다양한 셸 코드를 제공
  • asm: 어셈블리 코드를 기계어로 어셈블

 

 

 

 

 

 

 

'System Hacking' 카테고리의 다른 글

[드림핵] Shellcode  (0) 2024.02.27
helloworld.c 디스어셈블리  (0) 2024.02.25
[프로젝트] 악성코드 - 웜  (0) 2024.02.24
x86 Assembly (2)  (1) 2024.02.17
기본 명령어 및 vi 에디터  (1) 2024.01.28
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
글 보관함