pwndbgを使ってみた

これはUEC Advent Calendar 2021の15日目の記事です。

adventar.org

前日はにゃんさんの記事でした。

thkr64.site

はじめに

GDBプラグインといえばpedapwndbggefあたりが有名です。私は普段peda+Pwngdbを使っていますが、最近のつよつよpwnerがよくpwndbgを使ってる気がしたので試してみることにしました。

環境

推奨環境ではないですが、glibc 2.33を使いたかったのでUbuntu21.04にしました。

$ cat /etc/issue
Ubuntu 21.04 \n \l

$ gcc --version
gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gdb --version
GNU gdb (Ubuntu 10.1-2ubuntu2) 10.1.90.20210411-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ /lib/x86_64-linux-gnu/libc.so.6 
GNU C Library (Ubuntu GLIBC 2.33-0ubuntu5) release release version 2.33.
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 10.2.1 20210320.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

インストール

$ git clone https://github.com/pwndbg/pwndbg
$ cd pwndbg
$ ./setup.sh

サンプルプログラム

#include <stdlib.h>

int main(){
    char *p[0x200];
    int i = 0;

    for(int s=0x98; s>=0x18; s-=0x10){
        for(int j=0; j<14; j++)
            p[i++] = malloc(s);
    }

    while(i > 0)
        free(p[--i]);

    return 0;
}

コンパイルしてGDB起動

$ gcc sample.c 
$ gdb -q ./a.out

コマンド

よく使いそうなコマンドを挙げていきます。

help

使い方が表示されます。

pwndbg> help

コマンド名を与えるとそのコマンドのドキュメントが表示されます。

pwndbg> help heap
Iteratively print chunks on a heap, default to the current thread's active heap.
pwndbg> help bins
Print the contents of all an arena's bins and a thread's tcache, default to the current thread's arena and tcache.
pwndbg> help tele
Recursively dereferences pointers starting at the specified address
    ($sp by default)

apropos

与えられた単語に関連するコマンドを検索します。

pwndbg> apropos heap
heap -- Iteratively print chunks on a heap, default to the current thread's active heap.
set heap-dereference-limit -- Set number of bins to dereference
set memory-heap-color -- Set color for heap memory
show heap-dereference-limit -- number of bins to dereference:
show memory-heap-color -- color for heap memory:
vis_heap_chunks -- Visualize chunks on a heap, default to the current arena's active heap.

disas/disass/disassemble

ディスアセンブルを表示します。disaspwntoolsを入れたら使えなくなりました。

pwndbg> disass main

nearpc

ディスアセンブルを一部だけ表示します。

pwndbg> nearpc

tele/telescope

メモリをいい感じに表示してくれます。アドレスと表示する数を指定できます。

pwndbg> tele  


elfheader

セクションのアドレスが表示されます。下の例ではPIE有効なのでオフセットになってます。

pwndbg> elfheader 
0x318 - 0x334  .interp
0x338 - 0x368  .note.gnu.property
0x368 - 0x38c  .note.gnu.build-id
0x38c - 0x3ac  .note.ABI-tag
0x3b0 - 0x3d4  .gnu.hash
0x3d8 - 0x4b0  .dynsym
0x4b0 - 0x554  .dynstr
0x554 - 0x566  .gnu.version
0x568 - 0x598  .gnu.version_r
0x598 - 0x658  .rela.dyn
0x658 - 0x6a0  .rela.plt
0x1000 - 0x101b  .init
0x1020 - 0x1060  .plt
0x1060 - 0x1070  .plt.got
0x1070 - 0x10a0  .plt.sec
0x10a0 - 0x12e5  .text
0x12e8 - 0x12f5  .fini
0x2000 - 0x2004  .rodata
0x2004 - 0x2048  .eh_frame_hdr
0x2048 - 0x2158  .eh_frame
0x3da8 - 0x3db0  .init_array
0x3db0 - 0x3db8  .fini_array
0x3db8 - 0x3fa8  .dynamic
0x3fa8 - 0x4000  .got
0x4000 - 0x4010  .data
0x4010 - 0x4018  .bss

got

GOTが表示されます。pwntoolsが入ってないと使えないようです。

pwndbg> got

GOT protection: Full RELRO | GOT functions: 3

[0x55a3cb9f2fc0] free@GLIBC_2.2.5 -> 0x7fda9a390740 (free) ◂— endbr64 
[0x55a3cb9f2fc8] __stack_chk_fail@GLIBC_2.4 -> 0x7fda9a4213b0 (__stack_chk_fail) ◂— endbr64 
[0x55a3cb9f2fd0] malloc@GLIBC_2.2.5 -> 0x7fda9a390130 (malloc) ◂— endbr64 

vmmap

メモリマップが表示されます。

pwndbg> vmmap  

retaddr

リターンアドレスが表示されます。

pwndbg> retaddr 
0x7ffcc5200298 —▸ 0x7f392feca565 (__libc_start_main+213) ◂— mov    edi, eax
0x7ffcc5200368 —▸ 0x557757f110ce (_start+46) ◂— hlt   

heap

チャンクが表示されます。

pwndbg> heap  

arena

arenaの情報が表示されます。

pwndbg> arena
{
  mutex = 0,
  flags = 0,
  have_fastchunks = 1,
  fastbinsY = {0x557758fce010, 0x557758fcdd70, 0x557758fcd9f0, 0x557758fcd590, 0x557758fcd050, 0x557758fcca30, 0x557758fcc330, 0x0, 0x0, 0x0},
  top = 0x557758fce1d0,
  last_remainder = 0x0,
  bins = {0x557758fcb290, 0x557758fcbb50, 0x7f3930082c10 <main_arena+112>, 

  ...},
  binmap = {0, 0, 0, 0},
  next = 0x7f3930082ba0 <main_arena>,
  next_free = 0x0,
  attached_threads = 1,
  system_mem = 135168,
  max_system_mem = 135168
}

bins

binsが表示されます。

pwndbg> bins


特定のbinsだけ表示することもできます。

pwndbg> fastbins
fastbins
0x20: 0x561a1f410010 —▸ 0x561a1f410030 —▸ 0x561a1f410050 —▸ 0x561a1f410070 —▸ 0x561a1f410090 ◂— ...
0x30: 0x561a1f40fd70 —▸ 0x561a1f40fda0 —▸ 0x561a1f40fdd0 —▸ 0x561a1f40fe00 —▸ 0x561a1f40fe30 ◂— ...
0x40: 0x561a1f40f9f0 —▸ 0x561a1f40fa30 —▸ 0x561a1f40fa70 —▸ 0x561a1f40fab0 —▸ 0x561a1f40faf0 ◂— ...
0x50: 0x561a1f40f590 —▸ 0x561a1f40f5e0 —▸ 0x561a1f40f630 —▸ 0x561a1f40f680 —▸ 0x561a1f40f6d0 ◂— ...
0x60: 0x561a1f40f050 —▸ 0x561a1f40f0b0 —▸ 0x561a1f40f110 —▸ 0x561a1f40f170 —▸ 0x561a1f40f1d0 ◂— ...
0x70: 0x561a1f40ea30 —▸ 0x561a1f40eaa0 —▸ 0x561a1f40eb10 —▸ 0x561a1f40eb80 —▸ 0x561a1f40ebf0 ◂— ...
0x80: 0x561a1f40e330 —▸ 0x561a1f40e3b0 —▸ 0x561a1f40e430 —▸ 0x561a1f40e4b0 —▸ 0x561a1f40e530 ◂— ...
pwndbg> tcachebins
tcachebins
0x20 [  7]: 0x561a1f410100 —▸ 0x561a1f410120 —▸ 0x561a1f410140 —▸ 0x561a1f410160 —▸ 0x561a1f410180 —▸ 0x561a1f4101a0 —▸ 0x561a1f4101c0 ◂— 0x0
0x30 [  7]: 0x561a1f40fed0 —▸ 0x561a1f40ff00 —▸ 0x561a1f40ff30 —▸ 0x561a1f40ff60 —▸ 0x561a1f40ff90 —▸ 0x561a1f40ffc0 —▸ 0x561a1f40fff0 ◂— 0x0
0x40 [  7]: 0x561a1f40fbc0 —▸ 0x561a1f40fc00 —▸ 0x561a1f40fc40 —▸ 0x561a1f40fc80 —▸ 0x561a1f40fcc0 —▸ 0x561a1f40fd00 —▸ 0x561a1f40fd40 ◂— 0x0
0x50 [  7]: 0x561a1f40f7d0 —▸ 0x561a1f40f820 —▸ 0x561a1f40f870 —▸ 0x561a1f40f8c0 —▸ 0x561a1f40f910 —▸ 0x561a1f40f960 —▸ 0x561a1f40f9b0 ◂— 0x0
0x60 [  7]: 0x561a1f40f300 —▸ 0x561a1f40f360 —▸ 0x561a1f40f3c0 —▸ 0x561a1f40f420 —▸ 0x561a1f40f480 —▸ 0x561a1f40f4e0 —▸ 0x561a1f40f540 ◂— 0x0
0x70 [  7]: 0x561a1f40ed50 —▸ 0x561a1f40edc0 —▸ 0x561a1f40ee30 —▸ 0x561a1f40eea0 —▸ 0x561a1f40ef10 —▸ 0x561a1f40ef80 —▸ 0x561a1f40eff0 ◂— 0x0
0x80 [  7]: 0x561a1f40e6c0 —▸ 0x561a1f40e740 —▸ 0x561a1f40e7c0 —▸ 0x561a1f40e840 —▸ 0x561a1f40e8c0 —▸ 0x561a1f40e940 —▸ 0x561a1f40e9c0 ◂— 0x0
0x90 [  7]: 0x561a1f40df50 —▸ 0x561a1f40dfe0 —▸ 0x561a1f40e070 —▸ 0x561a1f40e100 —▸ 0x561a1f40e190 —▸ 0x561a1f40e220 —▸ 0x561a1f40e2b0 ◂— 0x0
0xa0 [  7]: 0x561a1f40d700 —▸ 0x561a1f40d7a0 —▸ 0x561a1f40d840 —▸ 0x561a1f40d8e0 —▸ 0x561a1f40d980 —▸ 0x561a1f40da20 —▸ 0x561a1f40dac0 ◂— 0x0
pwndbg> unsortedbin 
unsortedbin
all: 0x561a1f40d290 —▸ 0x561a1f40db50 —▸ 0x7fc6cc86cc00 (main_arena+96) ◂— 0x561a1f40d290
pwndbg> largebins 
largebins
empty
pwndbg> smallbins 
smallbins
empty

glibc 2.33にも対応しているようです。

malloc_chunk

チャンクの情報が表示されます。

pwndbg> malloc_chunk 0x557758fcbb50
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x557758fcbb50
Size: 0x3f1
fd: 0x7f3930082c00
bk: 0x557758fcb290

tcache

tcacheの情報が表示されます。

pwndbg> tcache
{
  counts = {7, 7, 7, 7, 7, 7, 7, 7, 7, 0 <repeats 55 times>},
  entries = {0x561a1f410100, 0x561a1f40fed0, 0x561a1f40fbc0, 0x561a1f40f7d0, 0x561a1f40f300, 0x561a1f40ed50, 0x561a1f40e6c0, 0x561a1f40df50, 0x561a1f40d700, 0x0 <repeats 55 times>}
}

top_chunk

topチャンクが表示されます。

pwndbg> top_chunk 
Top chunk
Addr: 0x557758fce1d0
Size: 0x1de31

try_free

freeしたときのチェックをしてくれます。

pwndbg> try_free 0x557758fce100
General checks
Tcache checks
Will do checks for tcache double-free (memory_tcache_double_free)
----------
Errors found!

vis_heap_chunks

チャンクをいい感じに表示してくれます。

pwndbg> vis_heap_chunks

おわりに

もうpedaは捨てました。

明日はExitさんです。