Layer7 리버싱 3차시

Layer7 리버싱 3차시 과제 정리

수업 내용 정리

gdb란?

Gnu DeBugger의 약자로, 프로그램을 런타임에서 디버깅하는 도구다.

다양한 유닉스 기반 시스템에서 동작하며 C, C++, Java, Go, Rust 등 다양한 언어를 지원한다.

gdb 명령어

시작과 종료

  • gdb [프로그램명] : 프로그램 시작
  • quit, q : GDB 종료

소스 보기

  • list : main 함수를 기점으로 소스 출력
  • list [라인 번호] : 특정 라인을 기준으로 출력
  • list [함수명] : 함수의 소스를 출력
  • list - : 출력된 행의 이전 행을 출력
  • list [파일명]:[함수명] : 파일의 특정 함수 소스를 출력
  • layout asm : 어셈블리 코드를 보여주는 창 추가

브레이크 포인트 설정:

  • break [함수명] : 특정 함수 시작 부분에 브레이크 포인트 설정
  • break [라인 번호] : 특정 라인에 브레이크 포인트 설정
  • info break : 설정된 브레이크 포인트 확인
  • delete [브레이크 포인트 번호] : 브레이크 포인트 삭제
  • disable/enable [브레이크 포인트 번호] : 브레이크 포인트 활성화/비활성화

프로그램 실행:

  • run, r : 프로그램 실행
  • continue, c : 브레이크 포인트에서 실행을 다시 시작
  • step : 한 라인씩 실행
  • next : 한 라인씩 실행 (함수 호출 시에는 함수 안으로 들어가지 않고, 다음 라인으로 넘어감)
  • finish : 현재 함수를 완료할 때까지 실행
  • return [값] : 함수를 강제로 종료하고 값을 반환
  • advance [라인 번호] : 특정 라인까지 프로그램 실행
  • advance [파일:라인 번호] : 특정 파일의 특정 라인까지 프로그램 실행

변수 확인

  • print [변수명] : 변수 값 출력
  • info variables : 모든 변수 출력
  • watch [변수명] : 변수가 변경될 때 브레이크 포인트 설정

메모리 확인

  • x/n [형식] [주소] : 특정 메모리 주소의 내용을 특정 형식으로 출력
  • info address [변수명] : 변수의 메모리 주소 확인
  • info stack : 스택 상태 확인
  • info frame : 현재 함수의 프레임 정보를 출력

ELF 파일 구조

ELF 파일은 유닉스 계열 운영체제에서 사용되는 표준 바이너리 파일 포맷이다.


ELF 파일은 위와 같이 크게 ELF Header, Program Header Table, Section Header Table로 구성된다.


ELF Header

ELF 파일의 시작 부분에 위치하며, 파일의 구조 및 속성을 정의한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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:                              DYN (Position-Independent Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x10c0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          14616 (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

파일의 종류를 나타내는 매직넘버와 데이터 저장 방식, OS, 아키텍처, 헤더의 크기 등 파일에 관한 정보들이 담겨있다.


Program Header Table

실행에 필요한 정보를 담고 있으며, 커널이 ELF 파일을 실행할 때 이 정보를 참고해서 메모리에 어떻게 적재할지를 결정한다.

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000888 0x0000000000000888  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x00000000000002e9 0x00000000000002e9  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x00000000000000ec 0x00000000000000ec  R      0x1000
  LOAD           0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
                 0x0000000000000288 0x00000000000004f8  RW     0x1000
  DYNAMIC        0x0000000000002d98 0x0000000000003d98 0x0000000000003d98
                 0x0000000000000200 0x0000000000000200  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000030 0x0000000000000030  R      0x8
  NOTE           0x0000000000000368 0x0000000000000368 0x0000000000000368
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000030 0x0000000000000030  R      0x8
  GNU_EH_FRAME   0x0000000000002008 0x0000000000002008 0x0000000000002008
                 0x0000000000000034 0x0000000000000034  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002d88 0x0000000000003d88 0x0000000000003d88
                 0x0000000000000278 0x0000000000000278  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .plt.sec .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   06     .dynamic
   07     .note.gnu.property
   08     .note.gnu.build-id .note.ABI-tag
   09     .note.gnu.property
   10     .eh_frame_hdr
   11
   12     .init_array .fini_array .dynamic .got

구조체에는 아래와 같은 필드들이 저장되어 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct Elf64_Phdr {
    Elf64_Word p_type;   
    Elf64_Word p_flags;  
    Elf64_Off p_offset;  
    Elf64_Addr p_vaddr;  
    Elf64_Addr p_paddr;  
    Elf64_Xword p_filesz;
    Elf64_Xword p_memsz; 
    Elf64_Xword p_align; 
};
  • p_type : 세그먼트의 타입 (ex. PT_LOAD, PT_DYNAMIC 등)
  • p_flags : 세그먼트 접근 권한 (읽기, 쓰기, 실행)
  • p_offset : 파일 내 세그먼트 시작 오프셋
  • p_vaddr : 메모리에 로드될 가상 주소
  • p_paddr : 물리 주소 (대부분 시스템에선 무시됨)
  • p_filesz : 파일에 존재하는 세그먼트 크기
  • p_memsz : 메모리에 할당될 세그먼트 크기
  • p_align : 정렬 조건 (페이지 단위 정렬 등)

Section Header Table

.text, .data와 같은 섹션들에 관한 정보들이 저장된다.

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.pr[...] NOTE             0000000000000338  00000338
       0000000000000030  0000000000000000   A       0     0     8
  [ 3] .note.gnu.bu[...] NOTE             0000000000000368  00000368
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000038c  0000038c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003b0  000003b0
       0000000000000030  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003e0  000003e0
       0000000000000150  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000530  00000530
       000000000000015e  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           000000000000068e  0000068e
       000000000000001c  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          00000000000006b0  000006b0
       0000000000000070  0000000000000000   A       7     2     8
  [10] .rela.dyn         RELA             0000000000000720  00000720
       0000000000000108  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             0000000000000828  00000828
       0000000000000060  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000050  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001070  00001070
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001080  00001080
       0000000000000040  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         00000000000010c0  000010c0
       000000000000021c  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         00000000000012dc  000012dc
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000007  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         0000000000002008  00002008
       0000000000000034  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002040  00002040
       00000000000000ac  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003d88  00002d88
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003d90  00002d90
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003d98  00002d98
       0000000000000200  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003f98  00002f98
       0000000000000068  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004040  00003010
       0000000000000240  0000000000000000  WA       0     0     64
  [27] .comment          PROGBITS         0000000000000000  00003010
       000000000000002b  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003040
       0000000000000450  0000000000000018          29    21     8
  [29] .strtab           STRTAB           0000000000000000  00003490
       0000000000000367  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  000037f7
       000000000000011a  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)

각 구조체에는 아래와 같은 필드들이 저장되어 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
typedef struct {
  Elf64_Word	sh_name;        
  Elf64_Word	sh_type;        
  Elf64_Xword	sh_flags;       
  Elf64_Addr	sh_addr;        
  Elf64_Off	sh_offset;      
  Elf64_Xword	sh_size;        
  Elf64_Word	sh_link;        
  Elf64_Word	sh_info;        
  Elf64_Xword	sh_addralign;   
  Elf64_Xword	sh_entsize;     
} Elf64_Shdr;
  • sh_name : 섹션 이름 (문자열 테이블의 인덱스)
  • sh_type : 섹션 타입 (ex. SHT_PROGBITS, SHT_SYMTAB 등)
  • sh_flags : 섹션 속성 (읽기/쓰기/실행 등)
  • sh_addr : 실행 시 메모리에서의 가상 주소
  • sh_offset : 파일 내 섹션 시작 오프셋
  • sh_size : 섹션 크기 (바이트 단위)
  • sh_link : 관련된 다른 섹션의 인덱스
  • sh_info : 섹션에 대한 부가 정보
  • sh_addralign : 섹션 정렬 조건
  • sh_entsize : 항목 크기 (테이블 형식일 경우)



실습용 코드 작성 후 명령어 실습 후 정리

실습용 코드

두 정수 값을 입력받고, 두 정수를 더하는 함수를 통해 더한 값을 출력하는 코드를 작성하였다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main() {
    int x, y;

    scanf("%d %d", &x, &y);
    printf("%d", add(x, y));

    return 0;
}

명령어 실습

gcc test.c -o test -g 명령어로 컴파일한 뒤 gdb ./test 명령어로 gdb를 실행하였다.


list 명령어를 통해 심볼이 남아있는 것을 확인할 수 있다.
b main 명령어로 main 함수에 브레이크 포인트를 설정하였다.


disas 명령어로 어셈블리어 코드를 확인할 수 있다.


layout asm 명령어로 어셈블리어 코드 창을 띄워 코드를 실행하면서 확인할 수도 있다.


n 명령어로 한 줄씩 실행해볼 수 있다. x, y에 각각 3과 7을 입력한 뒤, add 함수에 브레이크 포인트를 설정하여 함수 안으로 진입해보았다.


info stackinfo frame 명령어로 함수가 호출된 것을 확인할 수 있고, 매개변수들과 지역변수의 정보들을 확인할 수 있다.


print 명령어로 변수의 주소를 확인하고 이를 x/w [주소] 명령어를 통해 메모리 상에 위치한 값을 직접 확인할 수 있다.


스택 프레임의 포인터인 rbp를 통해서도 매개변수가 위치한 메모리를 확인할 수 있다.


return 0 명령어를 통해 add 함수를 강제로 0으로 반환해보았다. 원래라면 10(3 + 7)이 출력되어야 하지만 0이 출력되는 것을 확인할 수 있다.