gdbでメモリダンプして構造体を発見するTips

知りたい構造体が見たくても見れない場合に便利。
例えば、デバッグシンボルがないバイナリ内でSEGVしている場合など。*1
 
こんなコードを用意してみた。
特徴

 

#include <stdio.h>
#include <stdlib.h>

struct hoge {
    int id;
    char *comment;
};

void
throw_segv(char *comment)
{
    int test[1000];
    if(!strcmp(comment, "Hellow!")) test[-100000] = 9;
}

int
main(void)
{
    struct hoge *hg;

    hg = malloc(sizeof(struct hoge));
    hg->id = 2565;
    hg->comment = "Hellow!";
    throw_segv(hg->comment);
    return 1;
}

 
ここでのミッションはhg->commentに入れた値を見つけることにある。
(いや、その見えてるけどさ)
 
コンパイル&実行

nari@nari-laptop ~/p/study> gcc debug_test.c -o debug_test
nari@nari-laptop ~/p/study> ./debug_test 
fish: Job 1, './debug_test ' terminated by signal SIGSEGV (Address boundary error)

落ちた。うへへ。
 
さて、ここでgdb起動&実行

nari@nari-laptop ~/p/study> gdb debug_test
GNU gdb 6.8-debian
Copyright (C) 2008 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.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) r
Starting program: /home/nari/private/study/debug_test 

Program received signal SIGSEGV, Segmentation fault.
0x08048383 in throw_segv ()
Current language:  auto; currently asm
(gdb) bt
#0  0x08048383 in throw_segv ()
#1  0x080483ce in main ()
(gdb) 

落ちる。いひ。
 
さて、ここでローカル変数hgが参照している構造体を見つけ出す。
そのためには関数スタックの辺りをdumpしてしまう。

(gdb) dump binary memory /tmp/dump_test.b 0x080483ce 0x08058383

適当にずらした。何バイトあるのかなぁ。
この「dump binary memory」とかについて資料はめちゃめちゃ少ないので注意(というかどこにあるか教えてほしい)
指定したアドレスの両端のいずれかが、変な所をさした場合、エラーも出さずにファイルを空で出力したりするので中々むかつく。
あと先頭のアドレスを覚えておかないといけない。オフセットとるので。
 
出力したファイルをstringsで見てみる。(ちょっと確認)

nari@nari-laptop ~/p/study> strings /tmp/dump_test.b|less;

色々文字列で見れる。
 
これで準備はOK。
hg->idに2526を入れている事に目を付けて、吐き出したバイナリファイルから2526を検索する。
ここでRubyですよ。Ruby

ruby -e "p ARGF.read.index(/#{[2565].pack('V')}/nm)" /tmp/dump_test.b
=> 4070

4070バイト目に見つかった!
 
そこで基準である「0x080483ce」に「4070」を足す。
ほいだら、4バイトずらして見てみると"Hellow!"が。。。

(gdb) p *(char **)(0x080483ce + 4070 + 4)
$19 = 0xc7f8458b <Address 0xc7f8458b out of bounds>

 
違う場所だった。。orz
きっと定義した所だよねー。って事で。次を検索。

ruby -e "p ARGF.read.index(/#{[2565].pack('V')}/nm, 4071)" /tmp/dump_test.b
=> 7226

 
再度チャレンジ

(gdb) p *(char **)(0x080483ce + 7226 + 4)
$20 = 0x80484a0 "Hellow!"

こんにちわ!
 

実用的な使い方

例えば構造体内で参照しているアドレスとかを一つだけ手に入れたんだけど、肝心の構造体はどこにあんの?
と言うときに役立つのではないかと。
lib系のsoとか-g付けてないのがざらなので、結構こういうシーンはあるような気がする。

*1:もちろん-gを付けてコンパイルし直せばよいわけだが、そんな事できない時がある