[포너블] 환경 구축

Chris Kim·2024년 10월 11일

시스템해킹

목록 보기
3/33

본 문서는 드림핵 강의를 요약한 문서입니다.
https://learn.dreamhack.io/55#1

드림핵 시스템 해킹 강의를 위해 아래 주소에서 dbg 다운로드
https://github.com/pwndbg/pwndbg

다운로드가 완료되면 다음과 같은 화면이 나온다.

소스코드

// 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로 디버깅 시작

entry

리눅스의 실행파일은 ELF 형식을 가지고 있다. ELF는 헤더와 여러 섹션으로 구성된다. 헤더에는 진입점(Entry Point, EP)가 있는데 운영체제는 진입점의 값부터 프로그램을 실행한다.
readelf -h debugee 로 파악한 진입점은 0x401050이다.

gdb의 entry 명령어는 진입점부터 프로그램을 분석할 수 있게 해주는 gdb 명령어다. 화살표가 현재 rip의 값이다.

context

공룡책 시리즈 프로세스의 이해
를 참고하면 더 좋다. context는 크게 네 가지 영역으로 나뉜다.

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

break & continue

일반적으로 main함수가 주 기능들을 수행하므로 main함수가 분석 대상이 된다. 하지만 main 함수까지 일일이 한 줄씩 실행하는 것은 효율적이지 못한 디버깅이다. 따라서 중단점(breakpoint)를 설정하고 continue를 활용한다. b *main이라고 입력해서 main함수에 중단점을 설정하고 c를 입력해서 continue를 실행 시키면 main 함수 위치에서 멈춘 것을 볼 수 있다.

run

run은 실행만 시킨다. 중단점을 설정하지 않았으면 끝까지 실행한다. 지금은 중단점을 설정했으므로 실행이 멈춘다.

gdb 명령어 축약

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

disassembly

disassemble는 gdb의 디스어셈블 명령어다. disassemble main 처럼 함수 이름을 인자로 전달하면 해당 함수가 반환될 때 까지 전부 디스어셈블해서 보여준다. u, nearpc, pdisass는 pwndbg에서 제공하는 디스어셈블 명령어로 가독성 좋게 출력해준다.

ni와 si

ni는 서브루틴의 내부로 들어가지 않지만, si는 서브루틴 내부로 들어간다. b *main+64로 새로운 중단점을 설정해서 printf까지 이동할 수 있다. si를 사용하면 함수 내부로 들어가며 BACKTRACE 부분에 printf 함수가 쌓여있는 것을 볼 수 있다.

printf가 출력하는 문자는 stdout의 버퍼에서 잠시 대기한 뒤 이 버퍼는 다음 조건을 만족해야 목적지로 데이터를 이동시킨다.
1. 프로그램이 종료될 때
2. 버퍼가 가득 찼을 때
3. fflush와 같은 함수로 버퍼를 비우도록 명시했을 때
4. 개행문자가 버퍼에 들어왔을 때

finish

finish로 함수 끝까지 한 번에 실행 할 수 있다.

examine

x특정 주소에서 원하는 길이만큼의 데이터원하는 형식으로 인코딩하여 볼 수 있다.

Format letters are 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 are b(byte), h(halfword), w(word), g(giant, 8 bytes).

예시
1. x/10gx $rsp : rsp부터 80바이트를 8바이트씩 hex형태로 출력
2. x/5i $rip: rip부터 5줄의 어셈블리 명령어 출력
3. x/s 0x400000: 특정 주소의 문자열 출력

telescope

tele는 pwndbg가 제공하는 메모리 덤프 기능이다. 메모리가 참조하고 있는 주소를 재귀적으로 탐색하여 값을 보여준다.

pwndbg> tele
00:0000│ rsp  0x7fffffffc228 —▸ 0x7ffff7a05b97 (__libc_start_main+231) ◂— mov    edi, eax
01:0008│      0x7fffffffc230 ◂— 0x1
02:0010│      0x7fffffffc238 —▸ 0x7fffffffc308 —▸ 0x7fffffffc557 ◂— '/home/dreamhack/debugee'
03:0018│      0x7fffffffc240 ◂— 0x100008000
04:0020│      0x7fffffffc248 —▸ 0x4004e7 (main) ◂— push   rbp
05:0028│      0x7fffffffc250 ◂— 0x0
06:0030│      0x7fffffffc258 ◂— 0x71eb993d1f26e436
07:0038│      0x7fffffffc260 —▸ 0x400400 (_start) ◂— xor    ebp, ebp

vmmap

vmmap은 가상 메모리의 레이아웃을 보여준다. 어떤 파일이 매핑된 영역일 경우, 해당 파일의 경로까지 보여준다.

gdb/python

아래 파일을 가지고 gdb에서 파이썬 활용하는 방법을 살펴보자

// Name: debugee2.c
// Compile: gcc -o debugee2 debugee2.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	char name[20];
	if( argc < 2 ) {
		printf("Give me the argv[1]!\n");
		exit(0);
	}
	memset(name, 0, sizeof(name));

	printf("argv[1] %s\n", argv[1]);

	read(0, name, sizeof(name)-1);
	printf("Name: %s\n", name);
	return 0;
}

gdb/python argv

run 명령어의 인자로 $()와 함께 파이썬 코드를 입력하면 값을 전달 할 수 있다.
pwndbg> r $(python3 -c "print('\xff' * 100)")

gdb/python input

위와 마찬가지로 값을 입력할 수 있다. 입력값은 <<<을 사용한다.
pwndbg> r $(python3 -c "print('\xff' * 100)") <<< $(python3 -c "print('dreamhack')")

profile
회계+IT=???

0개의 댓글