C言語

ELFを解析してmain_arenaのアドレスを取得する方法【x86_64編】

2022年7月31日

はじめに

malloc で確保したメモリに関して問題を解析する場合、切り売りしたメモリを管理する
malloc の main_arena の中身をダンプしたい時があります。

main_arena は、malloc.c の中で定義されている static な変数(ローカル変数)のため、
dlopendlsym を使ってシンボル情報を取得することはできません。

疑問

でも待てよっと、、、GDB は main_arena の存在、アドレスを知っていて、
アクセスすることもダンプすることもできています。なぜ?

 

GDB では ELF ファイルを解析して main_arena の存在やアドレスを知ることができています。
今回は「ELFを解析してmain_arenaのアドレスを取得する方法」について紹介いたします。

SANACHAN
SANACHAN
libc.so.6 内の main_arena だけでなく、他のライブラリやシンボルにも応用できます。

 

動作確認済みの環境

CPUアーキテクチャ x86_64
OS Ubuntu 20.04 LTE
Linux Kernel 5.4.0-122
gcc 9.4.0
gdb 9.2
glibc 2.31

 

サンプルプログラム

各ポイントや処理内容の解説は後述することにして、まずはプログラム全体を記載いたします。

 

処理の流れ

サンプルプログラムは、以下のような処理の流れになっています。

step
1
共有ライブラリのマップ情報取得

  • 共有ライブラリのマップ情報を取得
    dl_iterate_phdr は、現在マップ(ロード)されているライブラリのパス、
    マップアドレス等を取得できます。個々の共有ライブラリごとに callback が呼ばれます。

 

step
2
.note.gnu.build_id セクションを探す

  • 実行バイナリとシンボル情報は別々のファイル
    callback に渡される共有ライブラリや、実行時にマップしているライブラリを解析しても、
    ローカル変数である main_arena の情報はどこにもありません。(hexdumpしても出てこない)
  • 別々のファイルを紐づける build_id
    ライブラリのシンボル情報は、.note.gnu.build_id に書き込まれている build_id を参照して
    格納されている場所を特定することができます。

メモ

build_id は、その名の通りビルドする際に付与される SHA1 の値で、
ビルドされたバイナリやライブラリ内の .note セクションに格納されます。

 

step
3
シンボルファイルからmain_arenaのオフセット取得

  • .symtab を解析
    シンボルファイルの中には、最適化で消えていないシンボル情報が全て入っています。
    .symtab セクションを解析すれば、所望のシンボル名が見つかります。
  • シンボルファイルから取得できるのはオフセット
    所望のシンボル名が st_name に見つかれば、st_value にオフセットが入っています。
    共有ライブラリがマップされている dlpi_addr にオフセットを加算すれば、
    アドレスを算出することができます。

 

実行結果

コンパイル

 command
$ gcc -o test -g -O0 -Wall -Werror get_main_arena.c -lc -ldl

 

gdb で検証

 command
$ gdb test
(gdb) b main
Breakpoint 1 at 0x1a7f: file get_main_arena.c, line 184.
(gdb) run
Starting program: /home/sanachan/test
Breakpoint 1, main () at get_main_arena.c:184
184 main(void) {
(gdb) n
185 dl_iterate_phdr(callback, NULL);
(gdb) n
debug symbol file: path=/usr/lib/debug/.build-id/18/78e6b475720c7c51969e69ab2d276fae6d1dee.debug
main_arena found: 0x7ffff7fb8b80
186 return 0;
(gdb) p/x &main_arena
$1 = 0x7ffff7fb8b80 ★glibc malloc.c 内の main_arena のアドレス
(gdb) p/x my_main_arena
$2 = 0x7ffff7fb8b80 ★ELFファイルを解析して得たアドレス
SANACHAN
SANACHAN
無事に、一致していることが確認できました。

 

おわりに

今回は ELF ファイルを解析し、ローカル変数のアドレス取得する方法をご紹介いたしました。
x86_64 アーキテクチャ限定ですが、ELF ファイルのフォーマットはどのアーキテクチャでも同じですので、
異なるアーキテクチャへの展開時は、解析するセクションなどを合わせ込んでお使いください。

 

こちらの記事もよく読まれています

  • この記事を書いた人
  • 最新記事
SANACHAN

SANACHAN

「生涯一エンジニア」を掲げ、大手グローバル企業でSE/PGとして8年勤め、キャリアアップ転職した現役のエンジニアです。世にあるメジャーな全プログラム言語(コボル除く)を自由に扱えます。一児の父。自分のため、家族のため、日々勉強してます。システムエンジニア、プログラミングに関する情報を蓄積している雑記帳です。

-C言語
-,