一 序
分析控制流圖是分析程序必不可少地過程,通過分析控制流圖能夠快速地幫助我們瞭解程序結構,同時利用控制流圖實現漏洞挖掘,Bug分析等也是非常有用地。Angr提供了多種分析工具,其中就有控制流圖分析。
對控制流圖(CFG)的分析分爲兩個方面,靜態控制流圖分析和動態控制流圖分析。
https://docs.angr.io/built-in-analyses/cfg 提供了對CFG的相關說明,本次實際操作看看CFG的生成過程。
二 案例
本文依然採用前面的二進制文件,代碼來源:https://github.com/angr/angr-doc/tree/master/examples/sym-write,將代碼編譯爲二進制文件。
2.1 通過閱讀https://docs.angr.io/built-in-analyses/cfg,自己編寫了一個簡單的程序來試着獲取程序的CFG。注意:在angr中,可以生成兩種類型的CFG:靜態CFG (CFGFast)和動態CFG (cfgem)。
# -*- coding: utf-8 -*-
import angr
import claripy
from angrutils import *
def main():
proj = angr.Project('./test',load_options={"auto_load_libs":False})
print("-----static------")
cfgs = proj.analyses.CFGFast()
print("This is the graph:", cfgs.graph)
print("It has %d nodes and %d edges" % (len(cfgs.graph.nodes()), len(cfgs.graph.edges())))
print("-----dynamic------")
cfgd = proj.analyses.CFGEmulated(keep_state=True)
#cfgd = proj.analyses.CFGAccurate(keep_state=True)
print("This is the graph:", cfgd.graph)
print("It has %d nodes and %d edges" % (len(cfgd.graph.nodes()), len(cfgd.graph.edges())))
if __name__ == "__main__":
print repr(main())
基本上就是把一些代碼粘貼過來組合了一下,看看結果:
竟然報錯了,然後我去這個地址查看原因
結果發現,找到了cfg_fast,但是沒有CFGEmulated,看來是文件名稱換了,最後我找到了cfg_accurate這個,並在代碼中將CFGEmulated換爲CFGAccurate後,運行結果如下:
成功將靜態CFG和動態CFG均打印出來了
2.2 進階,獲取節點信息
# -*- coding: utf-8 -*-
import angr
import claripy
from angrutils import *
def main():
proj = angr.Project('./test',load_options={"auto_load_libs":False})
print("----------static-----------")
cfgs = proj.analyses.CFGFast()
print("This is the graph:", cfgs.graph)
print("It has %d nodes and %d edges" % (len(cfgs.graph.nodes()), len(cfgs.graph.edges())))
print("###-entry node-###")
entry_node = cfgs.get_any_node(proj.entry)
print("There were %d contexts for the entry block" % len(cfgs.get_all_nodes(proj.entry)))
print("###-father node-###")
print("Predecessors of the entry point:", entry_node.predecessors)
print("###-son node-###")
print("Successors of the entry point:", entry_node.successors)
print("Successors (and type of jump) of the entry point:", [ jumpkind + " to " + str(node.addr) for node,jumpkind in cfgs.get_successors_and_jumpkind(entry_node) ])
print("----------dynamic----------")
cfgd = proj.analyses.CFGAccurate(keep_state=True)
print("This is the graph:", cfgd.graph)
print("It has %d nodes and %d edges" % (len(cfgd.graph.nodes()), len(cfgd.graph.edges())))
print("###-entry node-###")
entry_node = cfgd.get_any_node(proj.entry)
print("There were %d contexts for the entry block" % len(cfgd.get_all_nodes(proj.entry)))
print("###-father node-###")
print("Predecessors of the entry point:", entry_node.predecessors)
print("###-son node-###")
print("Successors of the entry point:", entry_node.successors)
print("Successors (and type of jump) of the entry point:", [ jumpkind + " to " + str(node.addr) for node,jumpkind in cfgd.get_successors_and_jumpkind(entry_node) ])
if __name__ == "__main__":
main()
運行結果如下:
因此,我們可以通過 cfgs.get_any_node()來獲取任意節點,通過.predecessors,.successors屬性來訪問該節點的父節點和子節點,.get_successors_and_jumpkind()這個函數還沒找到資料
2.3 CFG可視化
在https://docs.angr.io/built-in-analyses/cfg中,提出了angr-utils 能夠實現CFG(以及其他流圖)的可視化。安裝過程如下:
workon angr
git clone https://github.com/axt/bingraphvis
pip install -e ./bingraphvis
git clone https://github.com/axt/angr-utils
pip install -e ./angr-utils
之後編寫代碼嘗試實現CFG的可視化:
# -*- coding: utf-8 -*-
import angr
import claripy
from angrutils import *
def main():
proj = angr.Project('./test',load_options={"auto_load_libs":False})
print("----------static-----------")
main = proj.loader.main_object.get_symbol("main")
start_state = proj.factory.blank_state(addr=main.rebased_addr)
cfgs = proj.analyses.CFGFast()
plot_cfg(cfgs, "static", asminst=True, remove_imports=True, remove_path_terminator=True)
print("----------dynamic----------")
main = proj.loader.main_object.get_symbol("main")
start_state = proj.factory.blank_state(addr=main.rebased_addr)
cfgs = proj.analyses.CFGAccurate()
plot_cfg(cfgs, "dynamic", asminst=True, remove_imports=True, remove_path_terminator=True)
if __name__ == "__main__":
main()
在文件的目錄下,我找到了關於CFG的靜態流圖和動態流圖:
2.4 函數管理器
CFG結果生成一個名爲Function Manager的對象,可以通過cfg.kb.functions來訪問函數的一些屬性:
# -*- coding: utf-8 -*-
import angr
import claripy
from angrutils import *
def main():
proj = angr.Project('./test',load_options={"auto_load_libs":False})
print("----------static-----------")
cfgs = proj.analyses.CFGFast()
entry_func1 = cfgs.kb.functions[proj.entry]
print(entry_func1.block_addrs)#是屬於函數的基本塊的起始地址集嗎
print(entry_func1.blocks)#是該函數的一組基本塊,可用capstone對其進行探索和拆卸。
print(entry_func1.string_references())#返回函數中任意點引用的所有常量字符串的列表
print(entry_func1.returning)#是一個布爾值,指示函數是否可以返回。
print(entry_func1.callable)#是一個引用此函數的angr可調用對象。
print(entry_func1.name)#函數的名稱
print(entry_func1.get_call_sites())#返回以調用其他函數結束的所有基本塊的地址列表。
#print(entry_func1.transition_graph)#是描述函數內部控制流的NetworkX有向圖。它類似於IDA在每個函數級別上顯示的控制流圖。
#print(entry_func.has_unresolved_calls)#與CFG中檢測不精確有關。
#print(entry.has_unresolved_jumps)#與CFG中檢測不精確有關。
#print(entry_func.get_call_target(callsite_addr))#將從調用站點地址列表中給定callsite_addr,返回該callsite將調用到的位置。
entry_func.get_call_return(callsite_addr))#將從調用站點地址列表中給定callsite_addr,返回該callsite應該返回到的位置。
print("----------dynamic----------")
cfgd = proj.analyses.CFGAccurate(keep_state=True)
entry_func2 = cfgd.kb.functions[proj.entry]
print(entry_func2.block_addrs)
print(entry_func2.blocks)
print(entry_func2.string_references())
print(entry_func2.returning)
print(entry_func2.callable)
print(entry_func2.name)
print(entry_func2.get_call_sites())
if __name__ == "__main__":
main()
運行結果
2.5 關於CFGFast集以CFGAccurate的一些可選屬性
CFGFast :
force_complete_scan#(默認值:True)將整個二進制代碼作爲函數檢測的代碼。如果您有一個blob(例如,混合代碼和數據),您想要關閉它。
function_starts#地址列表,用作分析的入口點。
normalize#對結果函數進行規範化(例如,每個基本塊最多屬於一個函數)
collect_data_references#查找生成CFG期間對數據的所有引用。結果可以讓您找到對數據的交叉引用。
resolve_indirect_jumps#執行附加分析,試圖爲CFG創建過程中發現的每個間接跳轉找到目標。
CFGAccurate:
context_sensitivity_level#這將設置分析的上下文敏感性級別。有關更多信息,請參見下面的上下文敏感性級別一節。默認情況下這是1。(一般有0,1,2,3共四個級別,分別指定調用該函數的前幾個函數)
starts#地址列表,用作分析的入口點。
avoid_runs#要在分析中忽略的地址列表。
call_depth#將分析的深度限制爲若干個調用。這對於檢查特定函數可以直接跳轉到哪個函數非常有用(通過將call_depth設置爲1)。
initial_state#可以向CFG提供初始狀態,它將在整個分析過程中使用該初始狀態。
keep_state#爲了節省內存,默認情況下將丟棄每個基本塊的狀態。如果keep_state爲真,則狀態保存在CFGNode中。
enable_symbolic_back_traversal#是否啓用解決間接跳轉的密集技術
enable_advanced_backward_slicing#是否啓用另一種密集技術來解決直接跳轉
三 總結
本次通過對CFG的學習,能夠學會簡單的CFG操作,這樣在以後對程序進行分析時會方便很多!