PinなどのDBIツールでトレースした結果をIDAにマッピングするプラグイン「Lighthouse」を使ってみた
Lighthouseとは
Lighthouse とは、DBIツールでトレースすることで得たコードカバレッジデータを見やすくIDA内の独自のViewに表示し、IDAのDisassembly View、Graph ViewにマッピングしてくれるIDAプラグインである。
DBI(Dynamic Binary Instrumentation)は実行時にバイナリの内容を書き換えることによって、CPU命令の単位でトレースなどを行う手法である。 Lighthouseは、Pin、DynamoRIO、Fridaの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
より読み込む。
Coverage OverviewというカスタムViewが表示される。Golang製のバイナリで試したため、Coverage率の上位はruntime系の関数が大部分を占めている。シンボルがないバイナリでは、Coverage率が上位の関数をザッと見るのは処理系によっては効率悪いかも。
フィルタすることもできる。パッケージ名になっているコマンド名でフィルタすると本質っぽい関数だけが表示されていいかんじだった。
Disassembly View、Graph Viewの中で実際に実行された部分に色をつけてくれる。これは本質ではないアセンブリを読み飛ばすことができるので、すごく便利そう。
感想
DBIツールによる動的解析結果を、IDAでの静的解析時に効果的に利用できる点がすばらしいと感じた。 Disassembly View、Graph Viewの中で実際に実行された部分に色をつけてくれるので、読まなくていいアセンブリを無駄に読むことがなくなり解析の効率をあげられそうであった。
また、今回は試してないが複数のトレースログの差分を表示することもできるようで、これによってシンボルがないバイナリでも、Coverage Overviewに本質のルーチンだけをフィルタして表示できそうである。そのうち試してみたい。