gdbの使い方のメモ
よく使いそうなものの覚書。
実行ファイルを指定して起動する
$ gdb a.out
次のようにオプションを指定すると、
ようにできる。
$ gdb -q -ex 'set disassembly-flavor intel' -ex 'disp/i $pc' a.out
起動中のプロセスにアタッチする
$ gdb -p [PID]
ヘルプ、コマンド検索
(gdb) help break (gdb) apropos breakpoint
実行する
main関数で止める場合。引数にはシェルの構文がそのまま使える。
(gdb) start AAAA
main関数で止めない場合。
(gdb) run AAAA (gdb) r AAAA
引数を別途セットすることもできる。
(gdb) set args AAAA (gdb) start
起動時に引数をセットするには、実行ファイルの前に--args
をつける。
$ gdb --args a.out AAAA
シェルコマンドを実行する
(gdb) shell ls (gdb) !ls
コマンドを指定しなければ、シェルそのものが起動する。
(gdb) shell
(gdb) shell python
アセンブリコードを表示する
アセンブリコードをIntel形式で表示させる。 デフォルトではAT&T形式になっている。
(gdb) set disassembly-flavor intel
対象が関数の場合。
(gdb) disassemble main (gdb) disas main
引数を指定しない場合は、現在のフレームに対応する関数のアセンブリコードが表示される。
(gdb) disas
対象が関数でない場合はxコマンドを使う。 引数に$pcを指定すれば、直後に実行される命令列が見れる。
(gdb) x/10i $pc
停止するたびに特定の値を表示するようにする(Auto Display)
直後に実行される命令を表示するようにする。
(gdb) display/i $pc (gdb) disp/i $pc
引数を指定しない場合、設定されているものを再度表示する。
(gdb) disp
ブレークポイントをセットする
アドレスはいろいろな形式で指定できる。
(gdb) break main (gdb) b main (gdb) b *main+100 (gdb) b *0x08048695
tbreakを使うと、一度限りで消えるブレークポイントをセットできる。
(gdb) tbreak *main+100 (gdb) tb *main+100
特定のメモリアドレスの値について、watchで書き換え時、rwatchで読み取り時、awatchで読み書き時にブレークできる(watchpoint)。 メモリブレークポイント、データブレークポイントとも呼ばれる。
(gdb) watch *0xbffff76c (gdb) rwatch *0xbffff76c (gdb) awatch *0xbffff76c
catchを使うと、特定のシステムコール呼び出し時にブレークできる(catchpoint)。
(gdb) catch syscall write
セットされているブレークポイントを表示したり、削除したりする。
(gdb) info breakpoints (gdb) i b
(gdb) delete breakpoints 1 (gdb) d 1
break時に特定のコマンド列を実行させる。 silentでブレーク時の表示抑制、continueまたはcで止めずに処理の継続ができる。 endで入力終了。 たとえば、次の例ではブレークのたびにalレジスタに入っているascii文字を表示し、処理を継続させる。
(gdb) commands 1 >silent >printf "%c", $al >c >end
特定のブレークポイントを指定した回数だけ無視する(ここでは1番を10回)。
(gdb) ignore 1 10
ステップ実行
関数の中に入らない。
(gdb) nexti (gdb) ni
関数の中に入る。
(gdb) stepi (gdb) si
処理を継続する。
(gdb) continue (gdb) c
関数を抜けるまで処理を継続する。
(gdb) finish (gdb) fin
特定のアドレスに到達するまで処理を継続する。
(gdb) until *main+43 (gdb) u *main+43
レジスタの値を表示する
(gdb) info registers (gdb) i r
表示させるレジスタを絞ることもできる。
(gdb) i r esp ebp
値同士の計算をする
16進表示するには/x
をつける必要がある。
(gdb) p/x $ebp-$esp
メモリの内容を表示する
32bit単位で、100個16進表示する。
(gdb) x/100xw $esp
64bit単位で、100個16進表示する。
(gdb) x/100xg $esp
espレジスタの位置に置かれたアドレスの先にあるデータを表示する。
(gdb) x/4xw {int}$esp (gdb) x/4xw *(int *)$esp
レジスタの値を書き換える
(gdb) set $eax=42
メモリの中身を書き換える
ワード単位で書き換える場合。
(gdb) set {int}0x804a000=42
バイト単位で書き換える場合。
(gdb) set {char}0x804a000='A'
メモリの中身を検索する
実行ファイルが置かれたメモリ領域0x8048000-0x804b000から、__libc_start_main関数のアドレス0xb7e423e0
を探す。
(gdb) find 0x8048000,0x804b000-1,0xb7e423e0 0x804a00c <__libc_start_main@got.plt> 1 pattern found.
バイト列を探す場合は、/b
をつける。
(gdb) find/b 0x8048000,0x804b000-1,0xe4,0xb7 0x804a00e <__libc_start_main@got.plt+2> 1 pattern found.
実際に入っている値を表示する。
(gdb) x/wx $_-2 0x804a00c <__libc_start_main@got.plt>: 0xb7e423e0
特定のアドレスにジャンプする(eipを変更する)
ジャンプして処理を継続する。
(gdb) jump *main+100
処理を止めたまま、eipのみを変える。
(gdb) set $pc=*main+100
バックトレースを表示する
(gdb) backtrace (gdb) bt
特定のフレームに移動する。
(gdb) frame 4 (gdb) f 4
上下のフレームに移動する。
(gdb) up (gdb) down
メモリ配置を確認する
(gdb) info proc mappings (gdb) i proc map
各メモリ領域のアクセス保護属性も確認するには、実行中プロセスのPIDを調べた上で/proc/$PID/maps
を直接表示させる。
(gdb) i proc (gdb) shell cat /proc/[PID]/maps
また、次のようにすると、各アドレスがどのライブラリのどのセクションに対応しているかを調べることができる。
(gdb) i files
forkした子プロセスを追いかける
デフォルトでは追いかけないようになっている。
(gdb) set follow-fork-mode child
子プロセスのmain関数でブレークするには次のようにする。
(gdb) set follow-fork-mode child (gdb) b main (gdb) r (gdb) c
ASLRを有効にする
デフォルトではASLRは無効化されている。
(gdb) set disable-randomization off
Reverse Execution
まず最初に、recordコマンドで記録を開始しておく。
(gdb) record
以降、rni/rsiで前の状態に戻れるようになる。
(gdb) reverse-nexti (gdb) rni
(gdb) reverse-stepi (gdb) rsi
異常終了時におけるレジスタ、メモリの内容を調べる
あらかじめ、プロセスが異常終了した際にコアダンプを書き出すように設定しておく。
$ ulimit -c unlimited
プロセスに対応するコアダンプを与えて起動する。
$ gdb a.out core