
shadow は jemalloc ヒープエクスプロイトフレームワークです。jemalloc をヒープアロケータとして使用するターゲットアプリケーション(Android の libc、Firefox、FreeBSD の libc、スタンドアロン jemalloc など)に非依存になるように設計されています。現在のバージョン (2.0) は以下のターゲットで広範囲にテストされています。
このリポジトリにはツールのソースコードに加えて、Android ユーザランドデバッグ環境のセットアップ に関するドキュメント、shadow を使用した Android の jemalloc 構造の簡単な概要、そして double、unaligned、arbitrary free() バグが Android の jemalloc 上でどのように動作するか に関するいくつかのメモも含まれています。
初めて jemalloc 固有のコマンドを発行すると、shadow は既知のすべての jemalloc メタデータを解析し、Python の pickle ファイルに保存します。以降のコマンドでは、メモリから再度メタデータを解析する代わりにこの pickle ファイルを使用して高速化します。
jemalloc メタデータの状態が変化したことがわかっている場合(例えば、いくつかのアロケーションを行った後やガベージコレクションをトリガーした後)、jeparse コマンドを使用してメタデータを再解析し、pickle ファイルを再作成してください。
最初のステップとして、ホストマシンに pyrsistence をインストールします。
root化されたデバイスで以下の操作を行います:
host$ adb shell
phone$ su root
phone# ps -e
ps の出力からプロセスを選択します(例:com.google.process.gapps):
...
u0_a19 4679 3214 1668980 69216 SyS_epoll_ 7fa5f41430 S com.google.process.gapps
...
phone$ cd /data/local/tmp
phone$ ./gdbserver64 :5039 --attach 4679
GDB サーババイナリ(ARM32 および ARM64 用)は "bin" ディレクトリにあります。または、もし信頼できない場合は、以下を実行してください:
host$ git clone http://android.googlesource.com/toolchain/gdb
host$ cd ./gdb/gdb-7.11
host$ mkdir build64; cd build64
host$ ../configure --program-prefix=aarch64-eabi-linux- --target=aarch64-eabi-linux --disable-werror
host$ make
host$ sudo make install
そして、ホストマシンで以下を実行します:
host$ adb forward tcp:5039 tcp:5039
host$ aarch64-eabi-linux-gdb
(gdb) target remote :5039
(gdb) source /dir/with/shadow/gdb_driver.py
(gdb) jeparse -c /dir/with/shadow/cfg/android7_64.cfg
(gdb) jeruns -c
GDB サーバが target remote :5039 コマンドを発行するまでに時間がかかりすぎると、リスニングを停止することがあります。そのため、jeparse コマンド発行時に奇妙なエラーが表示された場合は、最初からやり直してください。
shadow for Windows/Firefox は以下の環境でテストされています:
注:Firefox 36.0 より古いバージョンを使用する場合は、mozjs ブランチを使用してください!
まず、WinDBG を Mozilla のシンボルサーバ でセットアップする必要があります。また、pykd のインストールも必要です。次に、GitHub からクローンした shadow ディレクトリを適当なパス(例:C:\tmp\)にコピーします。
WinDBG 初期化スクリプトの例が "windbg-init.cmd" ファイルにあります。これを C:\tmp\ に配置し、windbg.exe -c "$$>< C:\tmp\windbg-init.cmd" で WinDBG を起動します。
最後に、WinDBG 内で以下のコマンドを発行します:
!load pykd.pyd
!py c:\\tmp\\shadow\\pykd_driver help
[shadow] De Mysteriis Dom jemalloc
[shadow] shadow v2.0
[shadow] Firefox v56.0a1 (x86-64)
[shadow] jemalloc-specific commands:
[shadow] jechunks : dump info on all available chunks
[shadow] jearenas : dump info on jemalloc arenas
[shadow] jerun [-m] <address> : dump info on a single run
[shadow] -m : map content preview to metadata
[shadow] jeruns [-cs] : dump info on jemalloc runs
[shadow] -c : current runs only
[shadow] -s <size class> : runs for the given size class only
[shadow] jebins : dump info on jemalloc bins
[shadow] jebininfo : dump info on bin sizes
[shadow] jesize2bin : convert size to bin index
[shadow] jeregions <size class> : dump all runs that host the regions of
[shadow] the given size class
[shadow] jesearch [-cs] <hex> : search the heap for the given hex dword
[shadow] -c : current runs only
[shadow] -s <size class> : regions of the given size only
[shadow] jeinfo <address> : display all available details for an address
[shadow] jedump [path] : store the heap snapshot to the current
[shadow] working directory or to the specified path
[shadow] jestore [path] : jedump alias
[shadow] jetcaches : dump info on all tcaches
[shadow] jetcache [-bs] <tid> : dump info on single tcache
[shadow] -b <bin index> : info for the given bin index only
[shadow] -s <size class> : info for the given size class only
[shadow] jeparse [-crv] : parse jemalloc structures from memory
[shadow] -c <config file> : jemalloc target config file
[shadow] -r : read content preview
[shadow] -v : produce debug.log
[shadow] Firefox-specific (pykd only) commands:
[shadow] nursery : display info on the SpiderMonkey GC nursery
[shadow] symbol [-vjdx] <size> : display all Firefox symbols of the given size
[shadow] -v : only class symbols with vtable
[shadow] -j : only symbols from SpiderMonkey
[shadow] -d : only DOM symbols
[shadow] -x : only non-SpiderMonkey symbols
[shadow] pa <address> [<length>] : modify the ArrayObject's length (default new length 0x666)
[shadow] Android-specific commands:
[shadow] jefreecheck [-bm] : display addresses that can be passed to free()
[shadow] -b <bin index> : display addresses that will be freed to
[shadow] the tcache bin of <bin index>
[shadow] -m <name> : only search this specific module
[shadow] Generic commands:
[shadow] jeversion : output version number
[shadow] jehelp : this help message
上記のヘルプメッセージが表示されない場合は、何か間違っています ;)
注:この機能は現在 Firefox/Windows のみです!
symbol コマンドを使用すると、特定のサイズの SpiderMonkey および DOM クラス(および構造体)を検索できます。これは、use-after-free バグを悪用しようとしている場合や、上書き/破壊するための興味深い被害オブジェクトを配置したい場合に便利です。
shadow のメインディレクトリには、小さな PDB 解析ユーティリティが 2 つあります: symhex.py と pdbdy.py (より高速)。これらを "xul.pdb" に対して実行し、shadow が "pdb" ディレクトリ内で期待する Python pickle ファイル ("pdb/xul-VERSION.pdb.pkl") を生成します。実行する前に、"msdia90.dll" が登録されていることを確認してください;例えば、Windows 8.1 x86-64 では、管理者プロンプトから以下を実行します:
regsvr32 "c:\Program Files (x86)\Common Files\Microsoft Shared\VC\msdia90.dll"
また、"comtypes" Python モジュールも必要です。pip をインストールし、pip install comtypes を実行してください。
"xul.pdb" を入手するには、WinDBG を Mozilla のシンボルサーバ でセットアップする必要があります。
unmask_jemalloc は当初、3 つの主要なデバッガとプラットフォーム(WinDBG、GDB、LLDB)をサポートするためにモジュラー設計で再設計されました。Firefox/Windows/WinDBG 専用機能が追加された際に、ツールは shadow に改名されました。
以下は新しい設計の概要です(矢印は「インポート」を示します)。目標は、明らかに、すべてのデバッガ依存コードを *_driver および *_engine モジュールに配置することです。
---------------------------------------------------------------------------------------
debugger-required frontend (glue)
+------------+ +-------------+ +-------------+
| gdb_driver | | lldb_driver | | pykd_driver |
+------------+ +-------------+ +-------------+
^ ^ ^
| | |
------+-------------------+-------------------+----------------------------------------
| | |
| +--------+ |
+------------------------ | +-----+ core logic (debugger-agnostic)
| | |
| | |
+-----------------+
+------+ | |
| |---------------> | shadow |<-----+
| util | +------> | | |
| | | +-----------------+ |
+------+ | ^ ^ ^ ^ |
| | | | | | | | | +--------+
| | | +-----+----------+ | +----+--------+---| symbol |
| | | | | | | | +--------+
+-+ | | | +----------+ | | | +---------+
| | | | | jemalloc | | +--------+---| nursery |
| | | | +----------+ | | +---------+
| | | | ^ ^ ^ | |
| | | | | | | | |
| | | | | | +------+--------+ |
| | | | | | | | |
| | +---+---+----+----------+--------+-----+ |
| | | | | | | | |
| +-----+---+----+----+ | | | |
| | | | | | | | |
--+---------+---+----+----+-----+--------+-----+----+----------------------------------
| | | | | | | | |
| | | | | | | | | debugger-dependent APIs
| | | | | | | | |
| | | | | | | | |
| | | | v | | v |
| +------------+ | +-------------+ | +-------------+
+->| gdb_engine | +--| lldb_engine | +--| pykd_engine |
+------------+ +-------------+ +-------------+
^ ^ ^
| | |
+---+ +---------+ +---------------+
| | |
| | |
-------+-------------+-------------+---------------------------------------------------
| | |
| | | debugger-provided backend
| | |
| | |
+-----+ +------+ +------+
| gdb | | lldb | | pykd |
+-----+ +------+ +------+
---------------------------------------------------------------------------------------