Angr-Learn-0x01
介紹
本文可以理解為官方文件的簡單翻譯+一部分個人理解,並不會在此介紹angr
該怎麼使用(如果想快速開始angr
,可以嘗試從angr_ctf中學習),而是打算簡單說說它的設計。
以程式設計的理念來分析二進位制檔案必須克服幾個問題,它們大致是:
- 將二進位制檔案載入到分析程式中
- 將二進位制檔案轉換為中間表示(
IR
) - 進行確切的分析
- 對程式部分或者全部的靜態分析
- 對程式狀態空間的符號探索
- 上面兩種的組合
但angr
可以應對上訴這些問題
About Angr
Angr
框架的總體架構包含如下幾個部分:
-
載入器—
CLE
:用於解析載入二進位制檔案,識別檔案格式,從ELF/PE頭中提取架構、程式碼段和資料段等程式資訊 -
架構資料庫—
Archinfo
:根據程式架構資訊,載入對應的CPU架構模型,包括暫存器、位寬、大小端等資料 -
翻譯器—
PyVEX
:將程式機器碼翻譯成中間語言VEX,VEX是開源二進位制插樁工具Valgrind所使用的中間語言,angr需要處理不同的架構,所以它選擇一種中間語言來進行它的分析 -
模擬執行引擎—
SimEngine
:對VEX指令進行解釋執行,支援具體值執行和符號值執行,執行時支援自定義函式Hook和斷點,支援自定義路徑探索策略 -
約束求解器—
Claripy
:將符號執行中生成的路徑約束轉化成SMT公式,使用Z3進行求解 -
OS模擬器—
SimOS
:用於模擬程式與系統環境互動,提供了許多模擬的libc函式和系統呼叫,使用者也可以自行編寫Hook函式進行模擬
解析二進位制檔案 -> 獲取架構資訊 -> 使用翻譯器翻譯
核心概念
執行以下語句將二進位制檔案載入:
>>import angr
>>proj = angr.Project('/bin/true')
基本屬性
這裡的基本屬性是指二進位制檔案的基本屬性,如下:
- CPU架構(arch)
- 檔名(filename)
- 入口點地址(entry)
>>import monkeyhex # this will format numerical results in hexadecimal
>>proj.arch
<Arch AMD64 (LE)>
>>proj.entry
0x401670
>>proj.filename
'/bin/true'
載入
angr
利用CLE
模組對二進位制檔案進行載入,CLE
的執行結果是載入後的程式,可以透過.loader獲取各種屬性:
>>proj.loader
<Loaded true, maps [0x400000:0x5004000]>
>>proj.loader.shared_objects # may look a little different for you!
{'ld-linux-x86-64.so.2': <ELF Object ld-2.24.so, maps [0x2000000:0x2227167]>,
'libc.so.6': <ELF Object libc-2.24.so, maps [0x1000000:0x13c699f]>}
>>proj.loader.min_addr
0x400000
>>proj.loader.max_addr
0x5004000
>>proj.loader.main_object # we've loaded several binaries into this project. Here's the main one!
<ELF Object true, maps [0x400000:0x60721f]>
>>proj.loader.main_object.execstack # sample query: does this binary have an executable stack?
False
>>proj.loader.main_object.pic # sample query: is this binary position-independent?
True
factory
factory
是angr中比較重要的一個類。
塊
可以用project.factory.block(addr)
從給定地址獲取基本塊資訊,它的返回值就是基本塊。
>>block = proj.factory.block(proj.entry) # lift a block of code from the program's entry point
<Block for 0x401670, 42 bytes>
>>block.pp() # pretty-print a disassembly to stdout
0x401670: xor ebp, ebp
0x401672: mov r9, rdx
0x401675: pop rsi
0x401676: mov rdx, rsp
0x401679: and rsp, 0xfffffffffffffff0
0x40167d: push rax
0x40167e: push rsp
0x40167f: lea r8, [rip + 0x2e2a]
0x401686: lea rcx, [rip + 0x2db3]
0x40168d: lea rdi, [rip - 0xd4]
0x401694: call qword ptr [rip + 0x205866]
>>block.instructions # how many instructions are there?
0xb
>>block.instruction_addrs # what are the addresses of the instructions?
[0x401670, 0x401672, 0x401675, 0x401676, 0x401679, 0x40167d, 0x40167e, 0x40167f, 0x401686, 0x40168d, 0x401694]
>>block.capstone # capstone disassembly
<CapstoneBlock for 0x401670>
>>block.vex # VEX IRSB (that's a Python internal address, not a program address)
<pyvex.block.IRSB at 0x7706330>
狀態
前面的project物件其實只是程式碼了程式“初始化記憶體映像”,而當我們使用angr進行程式執行的時候,代表我們在使用SimState模擬程式狀態,下面這行程式碼就是執行SimState的起點。
>>state = proj.factory.entry_state()
<SimState @ 0x401670>
SimState
的狀態包含程式的記憶體、暫存器、檔案系統資料等任何可以透過執行更改的“實時資料,我們可以使用例如:state.regs和state.mem來訪問該狀態的暫存器和記憶體:
>>state.regs.rip # get the current instruction pointer
<BV64 0x401670> # or symbolic variable:<BV64 reg_48_11_64{UNINITIALIZED}>
>>state.regs.rax
<BV64 0x1c>
>>state.mem[proj.entry].int.resolved # interpret the memory at the entry point as a C int
<BV32 0x8949ed31>
但值得注意的是,返回值都是位向量而不是python的整數型別,因此如果我們要進行對暫存器或者記憶體的賦值,我們也要將資料轉換為位向量。
>>state.regs.rsi = state.solver.BVV(3, 64)
模擬管理
模擬管理可以簡單理解為模擬執行管理,它是angr
中的主要結構,用於狀態的執行、模擬。我們可以建立我們要使用的模擬管理器,傳入的引數應該是一個狀態列表。
>>simgr = proj.factory.simulation_manager(state)
<SimulationManager with 1 active>
>>simgr.active
[<SimState @ 0x401670>]
模擬管理可以包含多個狀態。上面這段程式碼中active是預設傳入的狀態的狀態(因為一個狀態有不同的狀態)。
然後我們可以透過simgr.step()
基本塊的符號執行,執行後可以看到儲存的狀態會發生更新。
分析
我們可以利用angr進行各種分析,從而從程式中提取一些有趣的資訊。
>>proj.analyses. # Press TAB here in ipython to get an autocomplete-listing of everything:
proj.analyses.BackwardSlice proj.analyses.CongruencyCheck proj.analyses.reload_analyses
proj.analyses.BinaryOptimizer proj.analyses.DDG proj.analyses.StaticHooker
proj.analyses.BinDiff proj.analyses.DFG proj.analyses.VariableRecovery
proj.analyses.BoyScout proj.analyses.Disassembly proj.analyses.VariableRecoveryFast
proj.analyses.CDG proj.analyses.GirlScout proj.analyses.Veritesting
proj.analyses.CFG proj.analyses.Identifier proj.analyses.VFG
proj.analyses.CFGEmulated proj.analyses.LoopFinder proj.analyses.VSA_DDG
proj.analyses.CFGFast proj.analyses.Reassembler
以下是構建和使用快速控制流圖的一個例子
# Originally, when we loaded this binary it also loaded all its dependencies into the same virtual address space
# This is undesirable for most analysis.
>>> proj = angr.Project('/bin/true', auto_load_libs=False)
>>> cfg = proj.analyses.CFGFast()
<CFGFast Analysis Result at 0x2d85130>
# cfg.graph is a networkx DiGraph full of CFGNode instances
# You should go look up the networkx APIs to learn how to use this!
>>> cfg.graph
<networkx.classes.digraph.DiGraph at 0x2da43a0>
>>> len(cfg.graph.nodes())
951
# To get the CFGNode for a given address, use cfg.get_any_node
>>> entry_node = cfg.get_any_node(proj.entry)
>>> len(list(cfg.graph.successors(entry_node)))
2