angr符号执行用例解析——CSCI-4968-MBE
用例源碼以及二進制文件鏈接:https://github.com/angr/angr-doc/tree/master/examples/CSCI-4968-MBE/challenges
本次用例代碼壓軸的crackme0x05非常棒,前面比較簡單可以跳過去。
crackme0x00a
這個就是一貫的套路了,確定start_addr,find_addr, avoid_addr,然后創建simulation_manager,explore所有滿足路徑,求解就可以了。
不過,不過啊。這個用例代碼比以前要簡單多了,它并沒跳過scanf函數,所以說angr是可以執行scanf函數的?那以前為什么還有跳過啊?這點把我搞蒙了。
而且它根本不需要創建什么符號變量,也不需要找start_addr,直接利用sm就可以執行。最后輸出滿足約束的程序輸入。
(看來以前的代碼是有可以改進的點的,可以將前面幾個用例代碼也改寫為這樣的方式,看能夠得到正確結果。不過,一道題肯定有多種解法,它給出這么多解決fangs,主要還是為了讓大家深入了解各個方法。)
用例源碼:
import angrFIND_ADDR = 0x08048533 # mov dword [esp], str.Congrats_ ; [0x8048654:4]=0x676e6f43 LEA str.Congrats_ ; "Congrats!" @ 0x8048654 AVOID_ADDR = 0x08048554 # mov dword [esp], str.Wrong_ ; [0x804865e:4]=0x6e6f7257 LEA str.Wrong_ ; "Wrong!" @ 0x804865edef main():proj = angr.Project('crackme0x00a', load_options={"auto_load_libs": False})sm = proj.factory.simulation_manager()sm.explore(find=FIND_ADDR, avoid=AVOID_ADDR)return sm.found[0].posix.dumps(0).split('\0')[0] # stdindef test():assert main() == 'g00dJ0B!'if __name__ == '__main__':print(main())crackme0x01
與crackme0x00a一樣,不多說了。
crackme0x02
與crackme0x00a一樣,不多說了。
crackme0x03
與crackme0x00a一樣,只是這里面的字符串被混淆了,不能直接看出來,但是它混淆的函數邏輯很簡單,所以計算一下就可以看出來哪個是find_addr哪個是avoid_addr了。
crackme0x04
與crackme0x00a一樣。不過用例代碼有些小變化,可以學習一下,就是確定find_addr時候,利用的cfg(控制流圖)。
cfg = proj.analyses.CFG()
FIND_ADDR = cfg.kb.functions.function(name="exit").addr
識別最后的出口點地址。這樣我們就不需要去IDA找find_addr,直接讓程序運行至結束就好了。
用例代碼:
import angr import subprocess# from IPython import embed # pop iPython at the enddef main():proj = angr.Project('crackme0x04', load_options={"auto_load_libs": False})cfg = proj.analyses.CFG()FIND_ADDR = cfg.kb.functions.function(name="exit").addrAVOID_ADDR = 0x080484fb # dword [esp] = str.Password_Incorrect__n ; [0x8048649:4]=0x73736150 LEA str.Password_Incorrect__n ; "Password Incorrect!." @ 0x8048649sm = proj.factory.simulation_manager()sm.explore(find=FIND_ADDR, avoid=AVOID_ADDR)# embed()#print sm.found[0].posix.dumps(1)return sm.found[0].posix.dumps(0) # .lstrip('+0').rstrip('B')def test():# it SHOULD just be 96 but the way angr models scanf means that it could technically be any number of formats# so we gotta check against ground truthwith open('input', 'wb') as fp:fp.write(main())assert subprocess.check_output('./crackme0x04 < input', shell=True) == 'IOLI Crackme Level 0x04\nPassword: Password OK!\n'if __name__ == '__main__':print(repr(main()))更有趣的是,我們完全可以不用設置avoid_addr。看它的控制流圖,avoid_addr是到達不了exit函數的,所以最后找到的路徑本身就不包括avoid_addr:
crackme0x05
crackme0x05再次出現新花樣,就是尋找find和avoid,不再是傳入地址了,而是傳入函數。
通過傳入的find和avoid函數判斷,將這條路徑加入find stashes還是avoid stashes。太方便了,再也不需要我們去逆向找find_addr和avoid_addr了!
這個傳入的函數參數是state。
用例給出的判斷邏輯為:這個程序輸入是否存在目標字符串。例如correct函數;
def correct(state):try:return 'Password OK' in state.posix.dumps(1)except:return False如果狀態輸出字符串包含‘Password OK’那么這個state所在的path,就會添加到 found 的path group中。
最后可以通過sm.found來訪問。
用例代碼:
import angr import subprocessdef main():proj = angr.Project('crackme0x05', load_options={"auto_load_libs": False})def correct(state):try:return 'Password OK' in state.posix.dumps(1)except:return Falsedef wrong(state):try:return 'Password Incorrect' in state.posix.dumps(1)except:return Falsesm = proj.factory.simulation_manager()sm.explore(find=correct, avoid=wrong)#print sm.found[0].posix.dumps(1)return sm.found[0].posix.dumps(0) # .lstrip('+0').rstrip('B')def test():# it SHOULD just be two numbers but the way angr models scanf means that it could technically be any number of formats# so we gotta check against ground truthwith open('input', 'wb') as fp:fp.write(main())assert subprocess.check_output('./crackme0x05 < input', shell=True) == 'IOLI Crackme Level 0x05\nPassword: Password OK!\n'if __name__ == '__main__':print(repr(main()))總結
以上是生活随笔為你收集整理的angr符号执行用例解析——CSCI-4968-MBE的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM(2):垃圾收集器与gc
- 下一篇: Grand Theft Auto V