Tool : gdb

·2023년 2월 2일
0

System Hacking

목록 보기
9/10

리눅스의 디버거, gdb
: gdb의 기능을 알고, 사용법을 익히기

gdb (GNU debugger)

리눅스의 대표적인 디버거

< Ubuntu 가상 환경 이용하기 >
1. 코드를 작성할 파일 생성하기
1) vi 파일이름.c ex) vi debugee.c
2) a 를 누르면 작성 가능 (입력 상태)
3) 작성이 끝났다면 esc 후 :wq (현재 상태를 저장하고 나간다는 의미)
2. 컴파일 후 gdb debugee 로 디버깅
1) 컴파일 gcc -o "출력파일" "컴파일할 소스파일"
ex) gcc -o debugee debugee.c
2) 디버깅 gdb "출력파일" ex) gdb debugee
3. 코드 실행
1) ./test

** 참고
https://booolean.tistory.com/135
gdb 명령어
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=charm1109&logNo=90048351536

ELF (Executable and Linkable Format)

리눅스가 실행 파일 형식으로 규정, 헤더와 여러 섹션으로 구성
헤더 : 실행에 필요한 여러 정보
섹션 : 컴파일된 기계어 코드, 프로그램 문자열을 비롯한 여러 데이터가 포함됨
-> 운영체제는 ELF 실행 시, ELF의 헤더 중 진입점 (Entry Point, EP) 이라는 필드의 값부터 프로그램을 실행

start

진입점부터 프로그램을 분석할 수 있게 해 주는 gdb의 명령어

DISASM 영역의 화살표 (►) 가 가리키는 주소는 현재 rip의 값인데, start명령어를 실행하면 앞서 살펴본 프로그램 진입점의 주소와 일치

context

주요 메모리들의 상태 = 프로그램이 실행되고 있는 맥락 (CONTEXT) = pwndbg
이를 가독성 있게 표현할 수 있는 인터페이스를 갖추고 있음
: 크게 4개의 영역으로 구분

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

break & continue

break : 특정 주소에 중단점 (breakpoint) 을 설정하는 기능
continue : 중단된 프로그램을 계속 실행시키는 기능

run

앞의 start가 진입점부터 프로그램을 분석할 수 있도록 자동으로 중단점을 설정해 주었다면, run은 단순히 실행만 시킴

dgb 의 명령어 축약

b: break
c: continue
r: run
si: step into
ni: next instruction
i: info
k: kill
pd: pdisas

disassembly

gdb 기능

  • 프로그램을 어셈블리 코드 단위로 실행하고, 결과를 보여줌
  • 기계어를 디스어셈블 (Disassemble) 하는 기능
    (00프로그램의 코드는 기계어로 이루어져 있으므로)

함수 이름을 인자로 전달하면 해당 함수가 반환될 때까지 전부 디스어셈블 하여 보여줌
u, nearpc, pdisassemble는 pwndbg에서 제공하는 디스어셈블 명령어
-> 디스어셈블 된 코드를 가독성 좋게 출력

관찰하고자 하는 함수의 중단점에 도달했으면, 그 지점부터는 명령어를 한 줄씩 자세히 분석
ni (next instruction) / si (step into)
공통점 : 모두 어셈블리 명령어를 한 줄 실행함
차이점 : 만약 call 등을 통해 서브루틴을 호출하는 경우
ni는 서브루틴의 내부로 들어가지 않지만, si는 서브루틴의 내부로 들어감
finish : 함수의 끝까지 한 번에 실행

examine

x : 가상 메모리에 존재하는 임의 주소의 값을, 특정 주소에서 원하는 길이 만큼의 데이터를 원하는 형식으로 인코딩하여 볼 수 있음

Format letters (포맷 형식) : o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), a(address), i(instruction), c(char), s(string) and z(hex, zero padded on the left)
Size letters (크기 형식) : b(byte), h(halfword), w(word), g(giant, 8 bytes)

x/10gx $rsp : rsp부터 80바이트를 8바이트씩 10개를 hex형식으로 출력
x/5i $rip : rip부터 5줄의 어셈블리 명령어 출력
x/s 0x400000 : 0x400000주소의 문자열 출력

telescope

pwndbg가 제공하는 강력한 메모리 덤프 기능
-> 특정 주소의 메모리 값들을 보여주는 것에서 그치지 않고, 메모리가 참조하고 있는 주소를 재귀적으로 탐색하여 값을 보여줌

vmmap

가상 메모리의 레이아웃을 보여주며, 어떤 파일이 매핑된 영역일 경우 해당 파일의 경로까지 보여줌

파일 매핑이란?
: 어떤 파일을 메모리에 적재하는 것
리눅스에서는 ELF를 실행할 때, 먼저 ELF의 코드와 여러 데이터를 가상 메모리에 매핑하고, 해당 ELF에 링크된 공유 오브젝트(Shared Object, so)를 추가로 메모리에 매핑함
공유 오브젝트는 윈도우의 DLL과 대응되는 개념으로, 자주 사용되는 함수들을 미리 컴파일해둔 것
C언어의 printf, scanf 등이 리눅스에서는 libc(library C)에 구현되어 있음
공유 오브젝트에 이미 구현된 함수를 호출할 때는 매핑된 메모리에 존재하는 함수를 대신 호출

gdb / python

  1. gdb를 통해 디버깅할 때 직접 입력할 수 없을 때가 있음
    ex) 숫자와 알파벳이 아닌 값을 입력하는 상황
  2. 이러한 값은 이용자가 직접 입력할 수 없는 값이기 때문에 파이썬으로 입력값을 생성하고, 이를 사용해야함
  3. run 명령어의 인자로 $()와 함께 파이썬 코드를 입력하면 값을 전달할 수 있음

아래는 파이썬에서 print함수를 통해 출력한 값을 run 명령어의 인자로 전달하는 명령어

pwndbg> r $(python -c 'print "\xff"*100')

이전과 같이 $()와 함께 파이썬 코드를 입력하면 값을 입력할 수 있음
입력값으로 전달하기 위해서는 '<<<' 문자를 사용함

아래는 argv[1]에 임의의 값을 전달하고, 값을 입력하는 명령어

r $(python -c 'print "\xff"*100') <<< $(python -c 'print "dreamhack"')

0개의 댓글