符號執行最大的問題就是路徑爆炸,當一個程序存在循環結構時,即使邏輯十分簡單也可能會產生規模十分巨大的執行路徑,本文記錄三種處理路徑爆炸的方法:
1.避免進入循環結果,手動添加約束條件
循環結果如下
該循環結構在程序中比較兩個字符串是否相等,每次循環都會有if語句生成兩個不同的分支,路徑成指數級增長,該函數可以通過手動添加約束條件來避免執行
solution.found[0].solver.add(string1=='BWYRUBQCMVSBRGFU')
使程序停止在該函數之前,再添加約束條件后求解,得出效果與執行完該函數一致
import angr import sys import claripy def Go(): p=angr.Project("./08con",auto_load_libs=False) istate=p.factory.blank_state(addr=0x08048627) buff_addr=0x0804A050 passwd0=claripy.BVS('passwd0',16*8) istate.memory.store(buff_addr,passwd0) sm=p.factory.simulation_manager(istate) solution=sm.explore(find=0x08048678) //提前停止 print(len(solution.found)) if solution.found: string1=solution.found[0].memory.load(buff_addr,16) //讀出此刻狀態下的字符串值 solution.found[0].solver.add(string1=='BWYRUBQCMVSBRGFU') //對此刻字符串值條件約束條件 solution0=solution.found[0].solver.eval(passwd0,cast_to=bytes) //求解該字符串 print("[+] Success! Solution is: {}".format(solution0)) else: raise Exception("error") if __name__=="__main__": Go()
2.hooks方法
就是用我們自己設計的函數去取代被hook的函數
找到會導致路徑爆炸的函數,用自己編寫的函數取代該函數即可,可以利用函數地址hook,也可以利用函數名進行hook
利用函數地址hook:
@p.hook(check_equals_called_address, length=instruction_to_skip_length) def skip_check(state): flag=state.memory.load(buff_addr,16) string1='XKSPZSJKJYQCQXZV' state.regs.eax=claripy.If( string1==flag, claripy.BVV(1,32), claripy.BVV(0,32) )
@p.hook() 第一個參數是需要被hook的函數地址,length是call該函數指令占據的字節大小
如圖,addr=0x80486B8,length=D-8
state.regs.eax=claripy.If()
設置寄存器eax的值,使該函數與被hook的函數具有一樣的效果,函數的返回值被保存在eax中,字符串相等則返回BVV(1,32),否則返回BVV(0,32)
利用函數名hook
當一個需要被hook的函數被調用了很多次時,利用地址hook便會出現問題,而通過程序的符號表利用函數名完全可以解析出地址
hook函數名的方法:
class Replace_check(angr.SimProcedure): def run(self,user_input,length): user_input_addr=user_input flag=self.state.memory.load(user_input_addr,length) string1='WQNDNKKWAWOLXBAC' self.state.regs.eax=claripy.If( string1==flag, claripy.BVV(1,32), claripy.BVV(0,32) ) check_funcname='check_equals_WQNDNKKWAWOLXBAC' p.hook_symbol(check_funcname,Replace_check())
class Replace_check(angr.SimProcedure) 需要替換的函數名 Replace_check()
def run(self,user_input,length) run函數名固定,self后面的參數與被hook的函數參數保持一致
p.hook_symbol(check_funcname,Replace_check()) 第一個參數時被替換的函數名,第二個參數是構造出來的函數
完成exp
import angr import sys import claripy def Go(): p=angr.Project("./10sim",auto_load_libs=False) istate=p.factory.entry_state() class Replace_check(angr.SimProcedure): def run(self,user_input,length): user_input_addr=user_input flag=self.state.memory.load(user_input_addr,length) string1='WQNDNKKWAWOLXBAC' self.state.regs.eax=claripy.If( string1==flag, claripy.BVV(1,32), claripy.BVV(0,32) ) check_funcname='check_equals_WQNDNKKWAWOLXBAC' p.hook_symbol(check_funcname,Replace_check()) sm=p.factory.simulation_manager(istate) def succ(state): output1=state.posix.dumps(1) if b'Good Job.' in output1: return True else: return False def _abort(state): output2=state.posix.dumps(1) if b'Try again.' in output2: return True else: return False solution=sm.explore(find=succ,avoid=_abort) print(len(solution.found)) if solution.found: solution0=solution.found[0].posix.dumps(0) print(solution0) else: raise Exception("error") if __name__=="__main__": Go()
3.veritesting技術處理路徑爆炸
簡單粗暴,只需要在angr里我們只要在構造模擬管理器時,啟用Veritesting了就行
sm=p.factory.simulation_manager(istate,veritesting=True)
忽視循環結構,直接硬懟,耗時較長
完整版
import angr import sys import claripy def Go(): p=angr.Project("./12ver",auto_load_libs=False) istate=p.factory.entry_state() sm=p.factory.simulation_manager(istate,veritesting=True) def succ(state): output1=state.posix.dumps(1) if b'Good Job.' in output1: return True else: return False def _abort(state): output2=state.posix.dumps(1) if b'Try again.' in output2: return True else: return False solution=sm.explore(find=succ,avoid=_abort) print(len(solution.found)) if solution.found: solution0=solution.found[0].posix.dumps(0) print(solution0) else: raise Exception("error") if __name__=="__main__": Go()