符號執行之angr學習-控制流圖

一 序

分析控制流圖是分析程序必不可少地過程,通過分析控制流圖能夠快速地幫助我們瞭解程序結構,同時利用控制流圖實現漏洞挖掘,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操作,這樣在以後對程序進行分析時會方便很多!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章