PinなどのDBIツールでトレースした結果をIDAにマッピングするプラグイン「Lighthouse」を使ってみた

Lighthouseとは

Lighthouse とは、DBIツールでトレースすることで得たコードカバレッジデータを見やすくIDA内の独自のViewに表示し、IDAのDisassembly View、Graph ViewにマッピングしてくれるIDAプラグインである。

github.com

DBI(Dynamic Binary Instrumentation)は実行時にバイナリの内容を書き換えることによって、CPU命令の単位でトレースなどを行う手法である。 Lighthouseは、PinDynamoRIOFridaの3種類のDBIツールに対応している。この記事ではPinを用いた。

インストールする

git cloneして/plugin以下にあるlighthouse_plugin.pyとlighthouseフォルダをIDAの/pluginsフォルダに入れるとインストールできる。 以下はmacOSでのコマンド例だが、OSが違ってもパスが変わるくらいでそんなに変わらないはず。

$ git clone git@github.com:gaasedelen/lighthouse.git
$ cp -R lighthouse/plugin/ /Applications/IDA\ Pro\ 7.1/ida64.app/Contents/MacOS/plugins/

また、PinのログフォーマットをLighthouseで扱える形式にするためのpintoolをビルドしておく必要がある。64bitのMacOSだと、/lighthouse/coverage/pin/obj-intel64/CodeCoverage.dylibが生成される。

$ export PIN_ROOT=~/pin # Pinのインストールパスを指定
$ export PATH=$PATH:$PIN_ROOT
$ cd ~/lighthouse/coverage/pin
$ make
mkdir -p obj-intel64
/Applications/Xcode.app/Contents/Developer/usr/bin/make objects
make[1]: Nothing to be done for `objects'.
/Applications/Xcode.app/Contents/Developer/usr/bin/make libs
make[1]: Nothing to be done for `libs'.
/Applications/Xcode.app/Contents/Developer/usr/bin/make dlls
make[1]: Nothing to be done for `dlls'.
/Applications/Xcode.app/Contents/Developer/usr/bin/make apps
make[1]: Nothing to be done for `apps'.
/Applications/Xcode.app/Contents/Developer/usr/bin/make tools
...

使ってみる

Pinを使って解析したいプログラムをトレースする。 ここでは、最近、Golangで書いているCPUエミュ cibo をPinにかけた。 実行結果はtrace.logとしてファイルに出力される。ちなみにgolangのバイナリの構造はこれがくわしい。

$ pin -t obj-intel64/CodeCoverage.dylib -- cibo hoge/samples/asm/simple_call
CodeCoverage tool by Agustin Gianni (agustingianni@gmail.com)
Logging code coverage information to: trace.log
Loaded image: 0x1000000:0x12fe707 -> cibo
Loaded image: 0x59e1000:0x5a7efff -> dyld
Loaded image: 0x7fff6a7ed000:0x7fff6a820fff -> libclosured.dylib
Loaded image: 0x7fff6acff000:0x7fff6ad00fff -> libSystem.B.dylib
Loaded image: 0x7fff6af33000:0x7fff6af89fff -> libc++.1.dylib
Loaded image: 0x7fff6af8a000:0x7fff6afaefff -> libc++abi.dylib
Loaded image: 0x7fff6c300000:0x7fff6c6eefff -> libobjc.A.dylib
Loaded image: 0x7fff6cd9b000:0x7fff6cd9ffff -> libcache.dylib
Loaded image: 0x7fff6cda0000:0x7fff6cdaafff -> libcommonCrypto.dylib
Loaded image: 0x7fff6cdab000:0x7fff6cdb2fff -> libcompiler_rt.dylib
Loaded image: 0x7fff6cdb3000:0x7fff6cdbbfff -> libcopyfile.dylib
Loaded image: 0x7fff6cdbc000:0x7fff6ce41fff -> libcorecrypto.dylib
Loaded image: 0x7fff6cec9000:0x7fff6cf02fff -> libdispatch.dylib
Loaded image: 0x7fff6cf03000:0x7fff6cf20fff -> libdyld.dylib
Loaded image: 0x7fff6cf21000:0x7fff6cf21fff -> libkeymgr.dylib
Loaded image: 0x7fff6cf2f000:0x7fff6cf2ffff -> liblaunch.dylib
Loaded image: 0x7fff6cf30000:0x7fff6cf34fff -> libmacho.dylib
Loaded image: 0x7fff6cf35000:0x7fff6cf37fff -> libquarantine.dylib
Loaded image: 0x7fff6cf38000:0x7fff6cf39fff -> libremovefile.dylib
Loaded image: 0x7fff6cf3a000:0x7fff6cf51fff -> libsystem_asl.dylib
Loaded image: 0x7fff6cf52000:0x7fff6cf52fff -> libsystem_blocks.dylib
Loaded image: 0x7fff6cf53000:0x7fff6cfdcfff -> libsystem_c.dylib
Loaded image: 0x7fff6cfdd000:0x7fff6cfe0fff -> libsystem_configuration.dylib
Loaded image: 0x7fff6cfe1000:0x7fff6cfe4fff -> libsystem_coreservices.dylib
Loaded image: 0x7fff6cfe5000:0x7fff6cfe6fff -> libsystem_darwin.dylib
Loaded image: 0x7fff6cfe7000:0x7fff6cfedfff -> libsystem_dnssd.dylib
Loaded image: 0x7fff6cfee000:0x7fff6d037fff -> libsystem_info.dylib
Loaded image: 0x7fff6d038000:0x7fff6d05dfff -> libsystem_kernel.dylib
Loaded image: 0x7fff6d05e000:0x7fff6d0a9fff -> libsystem_m.dylib
Loaded image: 0x7fff6d0aa000:0x7fff6d0c9fff -> libsystem_malloc.dylib
Loaded image: 0x7fff6d0ca000:0x7fff6d16efff -> libsystem_network.dylib
Loaded image: 0x7fff6d16f000:0x7fff6d179fff -> libsystem_networkextension.dylib
Loaded image: 0x7fff6d17a000:0x7fff6d183fff -> libsystem_notify.dylib
Loaded image: 0x7fff6d184000:0x7fff6d18bfff -> libsystem_platform.dylib
Loaded image: 0x7fff6d18c000:0x7fff6d197fff -> libsystem_pthread.dylib
Loaded image: 0x7fff6d198000:0x7fff6d19bfff -> libsystem_sandbox.dylib
Loaded image: 0x7fff6d19c000:0x7fff6d19dfff -> libsystem_secinit.dylib
Loaded image: 0x7fff6d19e000:0x7fff6d1a5fff -> libsystem_symptoms.dylib
Loaded image: 0x7fff6d1a6000:0x7fff6d1b9fff -> libsystem_trace.dylib
Loaded image: 0x7fff6d1bb000:0x7fff6d1c0fff -> libunwind.dylib
Loaded image: 0x7fff6d1c1000:0x7fff6d1edfff -> libxpc.dylib

生成されたtrace.logをIDAのメニューバーFile -> Load file -> Code coverage fileより読み込む。 f:id:TAKEmaru:20181011233401p:plain

Coverage OverviewというカスタムViewが表示される。Golang製のバイナリで試したため、Coverage率の上位はruntime系の関数が大部分を占めている。シンボルがないバイナリでは、Coverage率が上位の関数をザッと見るのは処理系によっては効率悪いかも。

f:id:TAKEmaru:20181011183102p:plain

フィルタすることもできる。パッケージ名になっているコマンド名でフィルタすると本質っぽい関数だけが表示されていいかんじだった。

f:id:TAKEmaru:20181011183127p:plain

Disassembly View、Graph Viewの中で実際に実行された部分に色をつけてくれる。これは本質ではないアセンブリを読み飛ばすことができるので、すごく便利そう。 f:id:TAKEmaru:20181011183140p:plain

f:id:TAKEmaru:20181011194339p:plain

感想

DBIツールによる動的解析結果を、IDAでの静的解析時に効果的に利用できる点がすばらしいと感じた。 Disassembly View、Graph Viewの中で実際に実行された部分に色をつけてくれるので、読まなくていいアセンブリを無駄に読むことがなくなり解析の効率をあげられそうであった。

また、今回は試してないが複数のトレースログの差分を表示することもできるようで、これによってシンボルがないバイナリでも、Coverage Overviewに本質のルーチンだけをフィルタして表示できそうである。そのうち試してみたい。