보안/System Hacking

[Dream hack] GDB/PWN 정리

pumisj 2026. 4. 3. 02:32

서론

지금부터 정리할 툴들은 물론 깊이 들어가면 복잡하지만 지금은 GDB와 PWN을 겉핥기식으로 정리하도록 하려고한다.

시간이 없어서 며칠간 공부하지 못했지만, 할 거 정리가 끝난 지금 조금이라도 많이 드림핵 강의를 공부해야겠다.

 

그럼 시작하겠다.

 

https://dreamhack.io/lecture/paths/system-hacking-fundamental

GDB

GDB(GNU Debugger)는 리눅스의 대표적인 Debugger이다.

VS나 Eclipse처럼 메모리, 콜 스택 등을 cli상에서 볼 수 있는 도구이다.

 

GDB에는 다양한 플러그인이 존재한다

  • gef
    • 아키텍처에 독립적
    • Heap분석이 가능하다
  • peda
    • 가장 클래식한 도구이다. register, stack, code 흐름을 모두 보여줌
    • pattern create로 오프셋을 보여줌
      • pattern create: 메모리 내 offset을 식별하기 위해 중복되지 않는 고유한 순환 수열을 생성하는 기능
    • 하지만 Heap 분석 기능이 부족함
  • pwndbg
    • Deep Memory 분석을 지원
      • 메모리 주소가 뭐를 가리키는지 추척해서 보여줌
    • Heap 구조를 더 자세히 보여줌

gdb는 다양한 명령어를 제공한다.

run : 디버깅 시작

break : breakpoint 설정

continue : 중단된 프로그램 계속 실행

entry, start: ELF의 EP 필드에 적힌 진입점부터 실행

ni : 한 줄씩 실행하지만 함수 내부로 들어가지 않음

si : 한 줄씩 실행하지만 함수 내부로 들어감

finish : 함수 내부로 들어갔을 때 함수를 종료

info registers, breakpoints : 레지스터나 중단점을 확인. 특정 레지스터만 확인하는것도 가능하다.

disassemble : binary 파일을 어셈블리 코드로 바꿔서 보여줌

x : 특정 주소에서 원하는 길이만큼의 데이터를 원하는 형식으로 인코딩해서 출력함

명령어 x에 대한 인자

다음과 같이 사용할 수 있으며 x/<개수><포맷 및 크기> 형태로 사용할 수 있음

backtrace : 콜 스택을 보여줌

set : 프로세스의 메모리 상태를 직접 변경할 수 있음

pwntools

해커가 특정 시스템이나 프로그램의 취약점을 이용해 제어권을 빼앗는 공격 행위를 Pwn이라고 한다.

이런 행위를 돕는 툴이 pwntools이며, 다양한 기능을 제공한다. (소켓 연결, 입출력, 공격 페이로드 전송 등)

연결을 맺는 함수

process(), remote(), ssh() : 각각 로컬, 호스트의 도메인과 포트, SSH 서버에 접속할 때 사용한다. 성공적으로 실행될 시 pwnlib.tubes 클래스를 반환한다.

데이터 송수신 함수

데이터를 주고받을때에는 모두 binary형태로 전송된다. 따라서 데이터를 송수신할때 b접두사를 붙여 파이썬의 byte 자료형을 사용해야한다.

recv() : 연결된 대상에 대해서 데이터를 수신함.

  • recv(), recvline(), recvn(), recvuntil(), recvall() 등의 함수가 있다.

send() : 연결된 대상에 대해서 데이터를 송신함.

  • send(), sendline(), sendafter(), sendlineafter() 등의 함수가 있다

interactive() : 터미널에서 사용자가 실시간으로 데이터를 송수신할 수 있게 하는 함수

close() : 두 프로세스의 연결을 끊는 함수, 해당 함수를 호출하면 객체 내부의 자원이 정리된다.

로그 출력

context.log_level 을 사용해 수준에 맞게 로그를 출력할 수 있다.

Packing & Unpacking

우리가 데이터를 주고받을 때 정수값을 bytes로 변환하거나 그 반대 작업을 수행하는 경우가 많다. 이를 구현해놓은게 pu 이다.

from pwn import *

s8 = 0x41
s16 = 0x4142
s32 = 0x41424344
s64 = 0x4142434445464748

print(p8(s8))
print(p16(s16))
print(p32(s32))
print(p64(s64))

s8 = b"A"
s16 = b"AB"
s32 = b"ABCD"
s64 = b"ABCDEFGH"

print(hex(u8(s8)))
print(hex(u16(s16)))
print(hex(u32(s32)))
print(hex(u64(s64)))

--------------------------------------------

b'A'
b'BA'
b'DCBA'
b'HGFEDCBA'
0x41
0x4241
0x44434241
0x4847464544434241

패킹 및 언패킹 함수는 기본적으로 리틀 엔디언으로 동작한다. 물론 빅 엔디언으로도 변경할 수 있다.

GDB

pwntools에서는 gdb.attach 함수로 연결중인 프로세스에 대해서 디버깅을 진행할 수 있다.

Assemble & Disassemble

또한 어셈블리를 기계어로, 기계어를 어셈블리어로 변환하는 기능도 제공한다.

context.arch = “amd64” || “i386” || “arm” : 아키텍처 선택

asm() : 해당 어셈블리를 기계어로 변환

disasm() : 해당 기계어를 어셈블리로 변환

ELF

ELF() : ELF파일의 경로를 넣으면 pwnlib.elf.elf.ELF 클래스를 반환하며, 해당 클래스에는 ELF 정보를 담고있는 멤버 변수나 다양한 함수가 있다.

symbols() : pwnlib.elf.elf.ELF클래스의 멤버 변수로 심볼들의 주소를 가지고있는 doctdict 클래스이다. 기존 딕셔너리처럼 사용할 수 있으면서도 속성으로 접근할 수 있다.

from pwn import *

e = ELF("./example")
print(hex(e.symbols['write']))
print(hex(e.symbols.write))

여기서 출력되는 주소는 Virtual Address이다.

 

후기

막 그렇게 어려운 내용은 아니다. (물론 내가 어렵지 않은것만 공부한걸수도 있다. 나중에 더 자세히 찾아봐야지)

 

GDB는 fuzzing하면서 많이 썼던거라 복잡하지 않았는데 PWN은 활용할게 많은거같아서 좀 익숙해져야겠다.

다음은 셸코드인데 이 때 PWN을 써서 OWR 셸코드를 작성하는것도 가능하다.

 

아무튼 빨리 시스템 해킹을 숙달할 수 있으면 좋겠다.