DynamoRIO 簡介

簡介

DynamoRIO 名稱由來是 HP Labs 的 Dynamo 與 MIT 的 Runtime Introspection and Optimization 合併的縮寫。這套動態插樁工具歷久彌新,是能夠與 Intel Pin 抗衡的開源工具,除了 IA-32、AMD64 架構外也支援 ARM 與 AArch64,平台上 Linux、Windows、macOS、Android 支援稍有不同,使用上有些限制

其實 Intel Pin 在 2013 年前也支援 ARM 的,只是不知道什麼原因而拔除了,幸好 Wayback Machine 記錄下來。

編按:instrumentation 在台灣似乎譯為「檢測」,但筆者認為中國譯作「插樁」比較符合這個字想表達的意思。

/img/dynamorio_introduction/Untitled.png

筆者自身的體驗來說,Pin 最容易使用文件也比較易懂,如果 Pin 能滿足你的需求那還是用 Pin 就好,別找自己麻煩 😅

需要高效能的話 DynamoRIO 會好一點

使用範例

  • 列出所有記憶體參照資訊,並以純文字形式寫入到檔案
$ ./drrun -c ../samples/bin64/libmemtrace_simple.so -- myProgram.exe
Format: <data address>: <data size>, <(r)ead/(w)rite/opcode>
0x7ffff7fca103:  5, call
0x7fffffffdfd8:  8, w
0x7ffff7fcadf4:  1, push
0x7fffffffdfd0:  8, r
0x7ffff7fcadf8:  2, push
0x7fffffffdfc8:  8, w
0x7ffff7fcadfd:  2, push
0x7fffffffdfc0:  8, w
0x7ffff7fcadff:  2, push
  • 多行程、多執行序線上快取模擬器
$ ./drrun -t drcachesim -cores 1 -L1D_size 64k -- myProgram.exe
---- <application exited with code 0> ----
Cache simulation results:
Core #0 (1 thread(s))
  L1I stats:
    Hits:                          20,3874
    Misses:                           1026
    Invalidations:                       0
    Miss rate:                        0.50%
  L1D stats:
    Hits:                           5,3390
    Misses:                           1744
    Invalidations:                       0
    Prefetch hits:                     222
    Prefetch misses:                  1522
    Miss rate:                        3.16%
LL stats:
    Hits:                              351
    Misses:                           2419
    Invalidations:                       0
    Prefetch hits:                     206
    Prefetch misses:                  1316
    Local miss rate:                 87.33%
    Child hits:                    25,7486
    Total miss rate:                  0.93%
  • 監測最常執行的 instruction
$ ./drrun -c ../samples/bin64/libmemtrace_simple.so -- myProgram.exe
Client opcodes is running
Top 15 opcode execution counts in 64-bit AMD64 mode:
       1316 : shr
       1395 : shl
       1686 : jnbe
       2081 : pop
       2199 : and
       2874 : push
       2874 : jnz
       2921 : lea
       3455 : jz
       4625 : movzx
       4698 : jz
       7091 : jnz
       8283 : test
      12516 : cmp
      13464 : add
      14010 : mov
      14625 : mov

基本介紹

組成

DynamoRIO

  • 解析執行檔
  • 提供跨平台 API 給 clients
    • Event callbacks
    • Utilities

DynamoRIO Extensions

  • DynamoRIO API 的擴充函式庫
  • 例如: drmgrdrsymsdrwrap
  • 參考清單

Client code

  • 透過客製化操作以擴充 DynamoRIO
  • 事件驅動
  • 操作 code stream

備註:在文件中目標執行檔稱為 Application (app)。

事件

  • 應用程式事件
    • Runtime event 會在執行期間發生
    • 例如 pre/post system call
  • DynamoRIO 事件
    • Code stream(Basic block 的建立)
    • Nudge(外部程式可以與 client 通訊,以觸發特定 handler,參考 dr_nudge_process
  • Event callback 註冊

應用程式事件

  • 行程開始、結束、fork
  • 執行緒建立、刪除
  • 函式庫載入、卸除
  • 例外處理(Windows)
    • Client 選擇是否要傳遞、隱匿
  • Signal(Linux)
    • Client 決定是否要傳遞、隱匿、略過、重導向
  • Pre- & post- system call
    • 與平台無關的系統呼叫參數存取
    • Client 可以修改系統呼叫
    • 使用 app 觸發額外系統呼叫

DynamoRIO 事件

  • Code Stream
    • Client 可以在執行前檢查、修改每個 instruction
      • 該事件在 transformation 時期發生
      • 修改、插入程式會在 execution 時期發生
    • 只專注於特定程式
      • Trace 建立事件:可以修改 trace
      • 客製化 trace 建立:可以指定 trace 結束條件
      • 用於最佳化、分析工具

Basic Block 概念

為了加速動態分析、插樁,通常都會將二進位檔中的 instructions 分為許多塊 basic block

/img/dynamorio_introduction/Untitled%201.png

執行架構(Transformation 時期 vs Execution 時期)

/img/dynamorio_introduction/Untitled%202.png

程式架構

/img/dynamorio_introduction/Untitled%203.png

Client 中首先呼叫的函數一定是 dr_client_main

擴充函式庫(Extensions)介紹

現有的擴充函式庫:

  • drreg: register stealing and allocating
  • drsyms: symbol table and debug information lookup
  • drcontainers: hash table, vector, and table
  • drmgr: multi-instrumentation mediation
  • drwrap: function wrapping and replacing
  • drutil: memory tracing, string loop expansion
  • drx: multi-process management, misc utilities
  • drsyscall: system call monitoring: system call names, numbers, parameter types, memory references
  • drdecode: standalone IA32/AMD64/ARM/Thumb/AArch64 decoding/encoding library
  • umbra: shadow memory framework

drmgr

用於組合、協調多階段的插樁流程,以便於將單一 client 拆解成多個 modules,同時提供多個 event callback 的優先順序調整。

大多數時候將 dr_ 替換成 drmgr_ 就享有普通的優先權,若需要改變優先權則是選擇帶有 _ex 的函數。

drmgr 將分成四個階段:

  1. Application-to-application transformations
    • 修改應用程式的程式碼,以改變其行為、效能
  2. Application code analysis
    • 監控用途
  3. Instrumentation insertion
    • 監控用途,一次一條 instruction
  4. Instrumentation-to-instrumentation transformations
    • 最佳化插樁程式碼

範例:

bool drmgr_register_bb_instrumentation_ex_event (
	drmgr_app2app_ex_cb_t app2app_func,
	drmgr_ilist_ex_cb_t analysis_func,
	drmgr_insertion_cb_t insertion_func,
	drmgr_ilist_ex_cb_t instru2instru_func,
	drmgr_priority_t *priority
)

drsyms

Symbol Access Library 可以從 Linux ELF、Mac Mach-O、Windows PDB、PECOFF 中讀取 symbol 資訊。

其實 DynamoRIO API 已經有提供類似功能而且速度更快,只差不能讀取所有 exported functions。在 Linux 中 drsyms 會搜尋 .dynsym 與 .symtab,不過 DynamoRIO API 只會搜尋 .dynsym,也就是會找不到 global symbol,除非 gcc 編譯時有加上 -rdynamic 使得 global symbol 保存在 .dynsym 中。

如要知道執行檔中某個 function 索引是否有在 .dynsym 中,可以使用 readelf -s myProgram

如果目標是 C++ 程式,則須考慮到名字修飾問題(name mangling),drsyms 有提供相關緩解功能。

條列一些相關 API:

額外閱讀:程式載入基本名詞執行檔格式介紹

drwrap

提供 function wrapping 與 replacing。

不管是 wrapping 或 replacing,都需要知道目標 function 的位址,可以透過 dr_get_proc_address() 或是 drsyms 相關 API。

功能介紹:

  • drwrap_replace()
    • 就如同其他 application code 一樣,替換的程式碼也是以 basic block 的形式執行
    • 舊的程式碼可能依然存在 basic block 當中只是不會被執行
    • 需要注意的是新 function 最好依照舊 function 的呼叫慣例(參數型態、個數)
  • drwrap_wrap()
    • 必須提供 pre-function 或 post-function 回呼函數
    • 多層 wrap 的執行順序與註冊順序相反,不過如有個 pre-function 的行為是跳過此 function,那後繼的 pre-function、原本 function、post-function 都不會被執行

效能:

可以視情況設定兩個 global flag 以加速運行,會縮限部分功能

  • DRWRAP_NO_FRILLS
    • 單一地址只有一個 wrap request,除非是使用相同的 pre & post callback function
    • Wrapping 須要在任何的執行之前(starup 或 module 載入時)
    • Unwrapping 必須要在 module 卸除時
  • DRWRAP_FAST_CLEANCALLS
    • 其假設所有的 wrap request 都是針對 function entrance point,且那些 function 都有符合標準 ABI
    • 也就是 caller-saved registers 可能不會被保存下來

droption

易於使用的選項解析器,典型型態可為 bool, int, std::string 等。

使用方式:

static droption_t<unsigned int> op_x
(DROPTION_SCOPE_CLIENT, "x", 0, 0, 64, "Some param",
 "Longer desc of some param.");
static droption_t<std::string> op_y
(DROPTION_SCOPE_CLIENT, "y", "", "Another param",
 "Longer desc of another param.");
static droption_t<int> op_z
(DROPTION_SCOPE_CLIENT, "foo", 42, "Yet another param",
 "Longer desc of yet another param.");

std::string parse_err;
if (!droption_parser_t::parse_argv(DROPTION_SCOPE_CLIENT, argc, argv, &parse_err)) {
    dr_fprintf(STDERR, "Usage error: %s", parse_err.c_str());
    dr_abort();
}

if (op_x.get_value() > 4) {
}

對於布林選項 foo 的否決,可以使用下列方式:

  • nofoo
  • no_foo
  • –nofoo
  • –no_foo

雜項

DynamoRIO 執行參數

  • -t Registers a pre-configured tool (client with configuration file) to run alongside DR.
  • -c
  • -root DR root directory
  • -logdirLog files will be stored in this directory
  • -m Kill the application if it runs longer than the specified number of minutes
  • -help

基於 DynamoRIO 的工具

$ bin64/drrun -t drcov -- application arg1 arg2

Clean Calls

透過保存、還原機器狀態,此操作可以巧無聲息地插入程式碼,不過開銷頗高,可以考慮使用 inline 緩解。

API 使用教學

  • average_bb_size,很推
    • 講解順序:基本架構 ➡️ 普通用法 ➡️ 使用 clean call ➡️ 使用組語加速 ➡️ 使用多執行序加速

各種範例

使用各種 API 的範例。

建置 Client

DynamoRIO 使用 CMake,因此在 UNIX 上面可以生成 Makefiles,在 Windows 上可以生成 Visual Studio project files。

參考資料


comments powered by Disqus