符號執行之angr學習-最簡單的angr分析

本文主要通過一個簡單的案例來說明angr的使用,同時,儘可能的將上文中提到的一些指令、代碼實踐下。

一 案例

在上文中,我才發現如果只是利用angr實現間的分析的話,真的很容易做到,上文中對angr的一些指令有了充分的解釋,本文就利用這些解釋來實現我們的第一個程序分析之旅

首先,安裝好angr後,我們需要一個二進制文件,爲方便起見,我直接用了https://github.com/angr/angr-doc/tree/master/examples/sym-write裏面提供的例子,僅需要issue.c這個C文件就好。(感謝胖胖鵬鵬胖胖鵬

第一:基操勿六(皮一下)

第二,開始編寫python腳本

# -*- coding: utf-8 -*-  
import angr
import claripy

def main():
    proj = angr.Project('./issue',load_options={"auto_load_libs":False})
    #創建一個工程並導入二進制文件——issue,選擇不自動加載依賴項(可選)
    #auto_load_libs,用CLE自動解析共享庫依賴關係,默認爲on。
    #except_missing_libs,當二進制文件有無法解析的共享庫依賴項時,將引發異常默認爲on。
    #將字符串列表傳遞給force_load_libs,所列出的內容將作爲未解析的共享庫依賴項處理,
    #將字符串列表傳遞給skip_libs,以防止將該名稱的任何庫解析爲依賴項。
    #main_opts是一個從選項名到選項值的映射
    #lib_opts是一個從庫名到字典的映射,將選項名映射到選項值

    #以下是對二進制文件的一些基本操作(可選)
    print proj.arch #架構
    #proj.entry #二進制程序入口點 
    #proj.filename #程序名稱以及位置
    #proj.loader#是通過CLE模塊將二進制對象加載並映射帶單個內存空間
    #proj.loader.min_addr#proj.loader 的低位地址
    #proj.loader.max_addr#proj.loader 的高位地址
    #proj.loader.all_objects #CLE加載的對象的完整列表
    #proj.loader.shared_objects#這是一個從共享對象名稱到對象的字典映射
    #proj.loader.all_elf_objects#這是從ELF文件中加載的所有對象
    #proj.loader.all_pe_objects#加載一個windows程序
    #proj.loader.main_object#加載main對象
    #proj.loader.main_object.execstack#這個二進制文件是否有可執行堆棧
    #proj.loader.main_object.pic#這個二進制位置是否獨立
    #proj.loader.extern_object#這是“externs對象”,我們使用它來爲未解析的導入和angr內部提供地址
    #proj.loader.kernel_object#此對象用於爲模擬的系統調用提供地址
    #proj.loader.find_object_containing(0x400000)#獲得對給定地址的對象的引用
    
    #以下是對確定的對象進行基本操作(可選)
    obj = proj.loader.main_object#指定main對象
    print obj.entry#獲取地址
    #obj.min_addr, obj.max_addr#地址的地位和高位
    #obj.segments#檢索該對象的段
    #obj.sections#檢索該對象的節
    #obj.find_segment_containing(obj.entry)#通過地址獲得單獨的段
    #obj.find_section_containing(obj.entry)#通過地址獲得單獨的節
    #addr = obj.plt['abort']#通過符號獲取地址
    #obj.reverse_plt[addr]#通過地址獲取符號
    #obj.linked_base
    #obj.mapped_base#顯示對象的預鏈接基以及CLE實際映射到內存中的位置
    
    state = proj.factory.entry_state(add_options={angr.options.SYMBOLIC_WRITE_ADDRESSES})
    #返回一個simstate,SimState包含程序的內存、寄存器、文件系統數據……任何可以通過執行更改的“實時數據”均在SimState。
    #.entry_state的替換:
    #.blank_state()#構造了一個“空白石板”空白狀態,其大部分數據未初始化。
    #.full_init_state()構造一個狀態,該狀態可以通過需要在主二進制的入口點之前運行的任何初始化程序執行,例如共享庫構造函數或預初始化程序。
    #.call_state()構造準備執行給定函數的狀態。

    #通過state來訪問一些寄存器的數值(可選)
    #state.regs.rip
    #state.regs.rax
    #state.regs.rbp = state.regs.rsp#將寄存器rsp的值給rbp
    #注意:這兒採用的bitvectors,並不是python值,後面會說明python和bitvectors的轉換

    u = claripy.BVS("u",8)#建立一個名稱爲u,8位寬的符號變量
    #claripy.BVS和state.solver.BVS有什麼區別還不清楚,需要驗證
    #可以通過.eval(A)的方法將A(bitvectors)轉化位python int型
    #a = state.solver.FPV(3.2, state.solver.fp.FSORT_DOUBLE)#通過FPV來創建浮點型向量
    #raw_to_bv和raw_to_fp方法將位向量解釋爲浮點數,反之亦然:

    state.memory.store(0x601041,u)##使用.memory.store(addr,val)方法將數據val保存在地址位addr的內存中,0x601041代表的是二進制文件中的某個變量的地址,具體獲取見下文

    simgr = proj.factory.simulation_manager(state)#使用的模擬管理器來管理狀態或狀態列表。state被組織成stash,可以forward, filter, merge, and move 。
    #simgr.active#存儲操作
    #simgr.step()#執行一個基本塊的符號執行,即就是所有狀態向前推進一個基本塊(類似單步操作,這兒是單塊操作)
    #simgr.active#以列表的形式更新存儲
    #simgr.run()#.run()方法直接執行程序,直到一切結束,運行simgr會查看返回deadended數目
    
    #active#此存儲區包含默認情況下將逐步執行的狀態,除非指定了備用存儲區,可替換的方法如下
    #deadended#當一個狀態由於某種原因不能繼續執行時,包括沒有任何有效指令、所有後續的未sat狀態或無效的指令指針,它就會進入死區隱藏。
    #pruned#當在lazy_resolve存在的情況下發現一個狀態未sat時,將遍歷該狀態層次結構,以確定在其歷史上,它最初何時成爲unsat。所有這一觀點的後裔州(也將被取消sat,因爲一個州不能成爲取消sat)都將被修剪並放入這一儲備中。
    #unconstrained#無約束的狀態(即,由用戶數據或其他符號數據來源控制的指令指針)放在這裏。
    #unsat#不可滿足的狀態(即,它們有相互矛盾的約束,比如輸入必須同時爲“AAAA”和“BBBB”)。

    while len(simgr.active)!=2:
        simgr.step()#循環運行直到通過.active來判斷是否進入了分支,這是因爲angr在遇到分支時,會將每個分支作爲一個狀態來保存。

    return simgr.active[0].state.se.eval(u)#返回結果爲win的u值,要是想返回lose的u值,將active[0]中0變爲1即可

if __name__ == "__main__":
    print repr(main())#repr() 函數將對象轉化爲供解釋器讀取的形式。

代碼 state.memory.store(0x601041,u)中地址來源

將生成的test二進制文件在ida中打開,找到.bss段,可以發現

這個u就是我們關注的變量,其地址就是0x601041

程序運行結果:

分別打印出了架構,main地址(並非時16進制),以及打印win時的u值

二 存在的一些問題

1 state = proj.factory.entry_state(add_options={angr.options.SYMBOLIC_WRITE_ADDRESSES})不同於胖胖鵬鵬胖胖鵬中的 state = proj.factory.entry_state(add_options={"SYMBPLIC_WRITE_ADDRESS"}),在初次運行時,我採用了add_options={"SYMBPLIC_WRITE_ADDRESS"}這個,卻報錯:缺失SYMBPLIC_WRITE_ADDRESS,而改爲add_options={angr.options.SYMBOLIC_WRITE_ADDRESSES}後正常運行。

2 obj = proj.loader.main_object#指定main對象
              print obj.entry#獲取地址,打印的地址爲4195488,轉換爲十六進制爲0x4004A0,在ida中的0x4004A0是:

不知道是否有問題。

3 simgr.active[0].state.se.any_int(u)中使用any_int()也會報錯,會提示使用.eval()來打印,即就是simgr.active[0].state.se.eval(u)

三 總結

從上述過程來看,利用angr分析程序時有個一般的流程

1 導入angr

2 導入二進制文件

3 建立狀態

4 定義符號變量並二進制文件相聯繫

5 建立simgr,用於管理state

6 通過.active的變化來找滿足我們要求的目標狀態

7 獲取目標狀態的數值

四 感謝以及參考

小型程序分析並不難,但是其中也遇見一些挫折,感謝胖胖鵬鵬胖胖鵬Badrerangr的博文。

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