簡介
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 在台灣似乎譯為「檢測」,但筆者認為中國譯作「插樁」比較符合這個字想表達的意思。
筆者自身的體驗來說,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 的擴充函式庫
- 例如:
drmgr
、drsyms
、drwrap
- 參考清單
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 註冊
dr_register_*_event
dr_unregister_*_event
- 參考文件
應用程式事件
- 行程開始、結束、fork
- 執行緒建立、刪除
- 函式庫載入、卸除
- 例外處理(Windows)
- Client 選擇是否要傳遞、隱匿
- Signal(Linux)
- Client 決定是否要傳遞、隱匿、略過、重導向
- Pre- & post- system call
- 與平台無關的系統呼叫參數存取
- Client 可以修改系統呼叫
- 使用 app 觸發額外系統呼叫
DynamoRIO 事件
- Code Stream
- Client 可以在執行前檢查、修改每個 instruction
- 該事件在 transformation 時期發生
- 修改、插入程式會在 execution 時期發生
- 只專注於特定程式
- Trace 建立事件:可以修改 trace
- 客製化 trace 建立:可以指定 trace 結束條件
- 用於最佳化、分析工具
- Client 可以在執行前檢查、修改每個 instruction
Basic Block 概念
為了加速動態分析、插樁,通常都會將二進位檔中的 instructions 分為許多塊 basic block。
執行架構(Transformation 時期 vs Execution 時期)
程式架構
Client 中首先呼叫的函數一定是 dr_client_main
。
擴充函式庫(Extensions)介紹
現有的擴充函式庫:
drreg
: register stealing and allocatingdrsyms
: symbol table and debug information lookupdrcontainers
: hash table, vector, and tabledrmgr
: multi-instrumentation mediationdrwrap
: function wrapping and replacingdrutil
: memory tracing, string loop expansiondrx
: multi-process management, misc utilitiesdrsyscall
: system call monitoring: system call names, numbers, parameter types, memory referencesdrdecode
: standalone IA32/AMD64/ARM/Thumb/AArch64 decoding/encoding libraryumbra
: shadow memory framework
drmgr
用於組合、協調多階段的插樁流程,以便於將單一 client 拆解成多個 modules,同時提供多個 event callback 的優先順序調整。
大多數時候將 dr_
替換成 drmgr_
就享有普通的優先權,若需要改變優先權則是選擇帶有 _ex
的函數。
drmgr 將分成四個階段:
- Application-to-application transformations
- 修改應用程式的程式碼,以改變其行為、效能
- Application code analysis
- 監控用途
- Instrumentation insertion
- 監控用途,一次一條 instruction
- 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:
- DynamoRIO API
- drsyms API
- drsym_lookup_address()
- drsym_get_type()
- drsym_get_type_by_name()
- drsym_get_func_type()
- drsym_lookup_symbol()
- drsym_search_symbols()
- …
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 - -logdir
Log 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
- Cache Simulator
- Code Coverage Tool
- CPU Simulator
- Dr. Memory Memory Debugging Tool
- System Call Tracer for Windows
- Library Call Tracer
- Symbol Query Tool
Clean Calls
透過保存、還原機器狀態,此操作可以巧無聲息地插入程式碼,不過開銷頗高,可以考慮使用 inline 緩解。
API 使用教學
- average_bb_size,很推
- 講解順序:基本架構 ➡️ 普通用法 ➡️ 使用 clean call ➡️ 使用組語加速 ➡️ 使用多執行序加速
各種範例
使用各種 API 的範例。
建置 Client
DynamoRIO 使用 CMake,因此在 UNIX 上面可以生成 Makefiles,在 Windows 上可以生成 Visual Studio project files。
參考資料
- 官方文件
- 官方教學 CGO 2017 Tutorial: “Building Dynamic Tools with DynamoRIO on x86 and ARMv8”
- DynamoRIO API 介绍与工作机制
comments powered by Disqus