C言語

malloc/freeをフックする方法、LD_PRELOADの使い方

2022年7月30日

はじめに

ソフトウェアを開発していると、終盤になって解析に時間のかかる障害に出くわします。
その一つが「メモリ破壊によるクラッシュ」。

メモリ破壊の解析は非常に時間がかかるため、細心の注意をはらってコーディングしているものの、
誰しも1度はやらかしてしまうものです。

今回は、破壊されているメモリが「malloc/free」で確保された領域である場合を想定し、
その解析の一助となる「malloc/freeのフック方法」について紹介します。

参考

malloc/free をあまり知らない方は、「malloc:メモリ管理構造」をご参照ください。

 

なぜフックするのか?

メモリ破壊は発生条件や処理タイミングに依存して起きる場合が多く、
メモリ破壊が発生していたソフトウェアからなるべく変更したくないためです。

malloc/free を使用しているシステムでは、動的メモリ確保が常態化していると考えられるため、
何か仕掛けを入れようとすると標準Cライブラリを改造する必要が出てきます。
または、my_malloc などを作成して、呼び出し元すべてを置き換える必要が出てきます。

SANACHAN
SANACHAN
簡単にフックできるのであれば、それに越したことはありませんね。

 

どのようにフックするのか?

ずばり、LD_PRELOAD を使用します。

プログラムを実行する際、必要な共有ライブラリ群をロードする ld というものがあります。
その ld を制御する環境変数 LD_PRELOAD を使用します。

Pre-Load という名前の通り、最初に読込んでほしい共有ライブラリを指定できます。
大半のプログラムは、早い段階で libc.so.6 がロードされるため、
それよりも先に malloc/free のシンボルを勝ち取っておく必要があります。

LD_PRELOAD を使うことで、読込む順番を強制的に変更することができるため、
簡単にフックすることができます

 

malloc/free をフックする際のポイント

フックするため、用意したフック関数から標準Cライブラリの malloc/free を呼び出す必要があります。
自身も標準Cライブラリを使用するため、malloc/free のシンボルを探す方法がポイントとなります。

dlsym() の第1引数であるハンドルに「-1」をセットすると、
自分とは異なる次の共有ライブラリを探してくれます。

この動きを利用して、標準Cライブラリの malloc/free のアドレスを格納します。

 

サンプルプログラム

サンプルでは free が呼び出された場合に、
0 で初期化してから本来の free を呼び出すようにしています。

これにより、本来の所有者以外がアクセスした際、SIGSEGV などで異常を起こすことになり、
メモリを破壊している人が見つかる可能性があります。(運が良ければ・・・w)

 コンパイル
gcc malloc_hook.c -fPIC -shared -Wall -Wextra -Werror -o libmalloc_hook.so -lc -ldl

 

動作確認

動作確認用のプログラムはこちら。

 コンパイル
gcc main.c -o test -Wall -Wextra -Werror

 

実行結果

LD_PRELOAD を指定しなかったとき

 command
$ ./test
======== before
addr=0x0000555b0e3ba2a0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2a8, value=0xffffffffffffffff
addr=0x0000555b0e3ba2b0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2b8, value=0xffffffffffffffff
addr=0x0000555b0e3ba2c0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2c8, value=0xffffffffffffffff
addr=0x0000555b0e3ba2d0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2d8, value=0xffffffffffffffff
======== after
addr=0x0000555b0e3ba2a0, value=0x0000000000000000
addr=0x0000555b0e3ba2a8, value=0x0000555b0e3ba010
addr=0x0000555b0e3ba2b0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2b8, value=0xffffffffffffffff
addr=0x0000555b0e3ba2c0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2c8, value=0xffffffffffffffff
addr=0x0000555b0e3ba2d0, value=0xffffffffffffffff
addr=0x0000555b0e3ba2d8, value=0xffffffffffffffff
SANACHAN
SANACHAN
先頭 16 バイトに malloc の管理情報が書き込まれていますが、
free 後も 0xFF で汚れたままになっています。

 

LD_PRELOAD を指定した時

 command
$ export LD_PRELOAD=./libmalloc_hook.so
$ ./test
======== before
addr=0x00005594800252a0, value=0xffffffffffffffff
addr=0x00005594800252a8, value=0xffffffffffffffff
addr=0x00005594800252b0, value=0xffffffffffffffff
addr=0x00005594800252b8, value=0xffffffffffffffff
addr=0x00005594800252c0, value=0xffffffffffffffff
addr=0x00005594800252c8, value=0xffffffffffffffff
addr=0x00005594800252d0, value=0xffffffffffffffff
addr=0x00005594800252d8, value=0xffffffffffffffff
======== after
addr=0x00005594800252a0, value=0x0000000000000000
addr=0x00005594800252a8, value=0x0000559480025010
addr=0x00005594800252b0, value=0x0000000000000000
addr=0x00005594800252b8, value=0x0000000000000000
addr=0x00005594800252c0, value=0x0000000000000000
addr=0x00005594800252c8, value=0x0000000000000000
addr=0x00005594800252d0, value=0x0000000000000000
addr=0x00005594800252d8, value=0x0000000000000000
SANACHAN
SANACHAN
先ほどの結果と異なり、free 前に 0 初期化されているようですので、
正しくフックできていることが分かります。

 

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

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

SANACHAN

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

-C言語
-, ,