ubuntu16
首先是需要python3.6以上的版本 用以下方法更新 (摘抄於https://zhuanlan.zhihu.com/p/51340766)
1.安裝編譯環境
sudo apt-get install zlib1g-dev libbz2-dev libssl-dev libncurses5-dev libsqlite3-dev libreadline-dev tk-dev libgdbm-dev libdb-dev libpcap-dev xz-utils libexpat1-dev liblzma-dev libffi-dev libc6-dev
2.下載Python-3.6.3.tar.xz 國內鏡像
3.解壓
tar xvJf Python-3.7.1.tar.xz
4.配置安裝位置
./configure --prefix=/usr/bin/python3.7
5.編譯及安裝
sudo make && sudo make install
弄完之后直接
pip3 install angr
今天打比賽 空余時間搗鼓 弄了一整天都有成功把去平坦化弄好嗚嗚嗚 再見angr
windows10
直接使用python2.7即可
依次執行以下命令(首先需要安裝pip
pip install --upgrade pip
pip install GitPython
pip install pyvex
pip install unicorn
pip install simuvex
pip install angr
import angr之后會有warning 說明安裝成功
學習
這里使用了Null戰隊編寫的《從0到1》內容
自己總結到一般來說angr跑題到步驟
1.創建一個project對象
p = Project('r100',auto_load_libs = False)
這里后面追加的 auto_load_libs 是否自動載入依賴庫 一般設置為False 以減少angr的工作量
2.設置一個啟動狀態state
p.factory.blank_state(addr=xxx) #可自定義入口地址 一般我解題使用這個 p.factory.entry_state() #從程序入口點開始 默認會使用這個 p.factory.full_init_state() #和entry_一樣 不過這之前會調用每個庫的初始化函數 會給angr增加工作量
3.創建虛擬執行
sm = p.factory.simulation_manager(state) #其中的state就是步驟2里面設置的啟動狀態
4.執行! 去往想去的地方 規避不想去的地方
sm.explore(find = 0x400844,avoid = 0x400855) #find 和 avoid 可以傳數組進來的 如果有多個想去的或者規避的
5.輸出結果
if sm.found[0]: print (sm.found[0].posix.dumps(0).replace(b'\x00',b''))
#成功的結果放在sm.found里面 這是個數組 一般我們想要的結果都是唯一解 然后用posix.dumps(0)輸出 這里的0是標准輸入的意思 replace讓結果清晰
這是最簡單的一個操作 接下來補充一些花里胡哨的操作
- 一些函數對結果沒有影響,比如printf 我們可以直接讓它返回,代碼如下
p.hook_symbol('printf',SIM_PROCEDURES['stubs']['ReturnUnconstrained'](),replace = True) #這里吧需要處理對函數名替換printf就可以了
- 既然可以hook我們認為對結果無影響對函數,那么也可以自己寫一個函數去hook原本對函數,從而達到給angr減少工作量對目的
class my_fgets(SimProcedure): # 固有格式 def run(self,s): # 參數為 (self + 該函數實際參數) simfd = self.state.posix.get_fd(0) # 創建一個標准輸入對對象 data,real_size = simfd.read_data(12) # 注意該函數返回兩個值 第一個是讀到的數據內容 第二個數內容長度 self.state.memory.store(s,data) # 將數據保存到相應參數內 return 12 # 返回原本函數該返回的東西 p.hook_symbol('fgets',my_fgets(),replace = True)
如果是寫scanf的%d如下
class my_sacnf(SimProcedure): # 固有格式 def run(self,fmt,n): # 參數為 (self + 該函數實際參數) simfd = self.state.posix.get_fd(0) # 創建一個標准輸入對對象 data,real_size = simfd.read_data(4) # 注意該函數返回兩個值 第一個是讀到的數據內容 第二個數內容長度 self.state.memory.store(n,data) # 將數據保存到相應參數內 return 1 # 返回原本函數該返回的東西 p.hook_symbol('__isoc99_scanf',my_scanf(),replace = True) # 這里%d對應int 是4個字節 但是讀取到一個int所以返回1 所以這完全是模擬的原來的函數
- 一個優化開關,無法避免無解的情況產生,但是能大大提高腳本的運行效率
sm.one_active.options.add(options.LAZY_SOLVES)
- 自己構造輸入
因為使用標准輸入經常無法推測輸入字符串的長度,會浪費大量時間去嘗試不同長度,所以我們可以自定義輸入 然后作為參數傳入一個函數,這個時候state要設置為call的地址
flag_chars = [BVS('flag_%d'%i,32) for i in range(13)] # BVS類似於z3中的BitVec,第一個參數為變量名,第二個參數為位數(bit) 這里我們知道輸入了13個int 所以申請13個約束變量 for i in range(13): state.mem[state.regs.rsp + i * 4].dword = flag_chars[i] #這里為了方便 先把內容儲存在rsp指向的內存 注意一個int是4字節 state.regs.rdi = state.regs.rsp # 然后傳參給rdi
因為是手動設置的輸入,不能通過dump(0) dump標准輸入來得到輸入,這里使用angr求解器提供的eval函數
flag = ''.join(chr(sm.one_found.solver.eval(c)) for c in flag_chars) # sm.one_found.solver.eval(flag_char[i]) 得到一個int 然后轉為char即可
- 對於開啟了PIE的可執行文件,angr會默認其基地址為0x400000,此時所有操作只需要在原本地址上加上offset即可
- 對內存對儲存魚讀取
state.memory.store(addr,data) # 這里對data可以是一串數據 data🉑️來源於simfd.read_data(標准輸入) text = sm.one_found.solver.eval(sm.one_found.memory.load(addr,len),cast_to = bytes) # sm.one_found.memory.load 加載內存 cast_to = bytes 轉為char