angr
- 概述
- 如何學習
- 題目串列
- 00_angr_find
- 01_angr_avoid
- 02_angr_find_condition
- 03_angr_symbolic_registers
- 04_angr_symbolic_stack
- 05_angr_symbolic_memory
- 06_angr_symbolic_dynamic_memory
- 總結
概述
??angr
??簡單介紹一下angr,借助官方的原話就是 – A powerful and user-friendly binary analysis platform!,一個功能強大且用戶友好的二進制分析平臺,這里主要總結一下使用angr解決ctf逆向題的一些用法和套路,
??安裝的話,大家可以直接使用pip安裝,不過這里可能會遇到一個小坑,我在安裝angr之前已經安裝了pwntools,此時再安裝angr,由于有一個依賴庫兩者需要的版本是不一樣的,所以安裝后導致pwntools無法使用,解決的話,大家可以采用python虛擬環境將做題環境分開,推薦使用pipenv,有空的話再寫一個筆記,不過這個操作比較簡單,讀者簡單搜索即可解決,
如何學習
??angr官方檔案
??個人在網上發現了一個名為 – angr_ctf的專案,非常適合新手入門學習,作者也是通過一道道基本題目來幫助讀者入手angr,對于我們這種re手來說,直接去閱讀檔案肯定是效率極低的,所以通過做題的方式來學習我感覺是非常可以的,
??首先直接git clone下整個專案,然后如下截圖所示,是其部分的專案內容截圖,其中帶有數字編號的是題目,一共有18道題,進入每道有編號的題目里,會有相應生成c程式的模板和腳本,dist目錄的話存放的就是已經生成好了的二進制程式,solutions目錄下存放了對應程式和解題腳本,所以我們直接在這個目錄下學習就好,

??如下截圖所示,進入solutions目錄,然后再進入編號為00的題目目錄里,可以看到一共有三個檔案,第一個對應二進制程式,第二個名為scaffold00.py的腳本檔案是需要我們完成的解題腳本,作者在里面給了充分的解釋和代碼來幫助我們學習并解決題目,第三個檔案就是作者給出的完整解題腳本,

??下面直接開始學習,我會將自己的解題腳本貼出來,由于作者已經給了足夠的注釋,我就不詳細介紹每個API的用途,個別需要思考的地方我會單獨注釋,
題目串列
00_angr_find
??如下截圖所示,是該題目的主要邏輯,print_msg函式用于列印提示資訊,然后要求我們輸入密碼,之后會用complex_function對密碼進行變換,最終和程式中給出的字串進行比較,相同即成功,

??下面我們用angr進行求解,腳本如下,
import angr
# Create a Pro --> Create a simu --> Explore
pro = angr.Project("./00_angr_find")
init_state = pro.factory.entry_state() # start() addr
simu = pro.factory.simgr(init_state)
good_addr = 0x804867d
simu.explore(find=good_addr)
# get res
if simu.found:
res = simu.found[0]
for i in range(3):
# print stdin/stdout/stderr, stdin is flag
print(res.posix.dumps(i))
else:
print("No result!")
??如下是程式的運行結果,上面一開始是一些警告資訊,主要是由于angr的求解代碼沒有進一步設定更多的值,這表明我們的代碼確實不是很完善,當然應對簡單的題目足夠,當后面學習更多的知識時再解決,最后三行輸出依次是stdin/stdout/stderr的值,stdin代表輸入的passwd即flag,stdout是程式運行的輸出,程式沒有運行出錯,所以stderr沒有輸出,

01_angr_avoid
??如下截圖所示,由于main函式中邏輯太長無法反編譯,所以只能嘗試分析匯編,可以發現整個邏輯和上題中是一樣的,要求輸入密碼然后變換后和已知字串進行比對,有區別的是最后判斷加入了should_succeed,而avoid_me函式會將其值置為0從而無法達到good_addr,因此這里的angr求解腳本需要避免進入到avoid中,


??求解腳本如下,經過實際測驗不加入avoid也是可以求解的,不過會使用更多的時間,
import angr
pro = angr.Project("./01_angr_avoid")
init_state = pro.factory.entry_state()
simu = pro.factory.simgr(init_state)
good_addr = 0x080485E0
avoid_addr = 0x080485A8 # avoid_me func
simu.explore(find=good_addr, avoid=avoid_addr)
if simu.found:
res = simu.found[0]
for i in range(3):
print(res.posix.dumps(i))
else:
print("No result!")
"""
b'RNGFXITY'
b'placeholder\nEnter the password: '
b''
"""
02_angr_find_condition
??如下截圖所示,是本題的主要邏輯,和前面的題目差不多,此處就不多分析,

??求解腳本如下,這道題的目的主要是學習另外一種條件判斷的方法,前面的題目指定是地址,這里也可以指定為一個條件函式,
import angr
pro = angr.Project("./02_angr_find_condition")
init_state = pro.factory.entry_state()
simu = pro.factory.simgr(init_state)
def success(state):
output = state.posix.dumps(1) # get output from stdout
return b"Good Job." in output
def abort(state):
output = state.posix.dumps(1) # get output from stdout
return b"Try again." in output
simu.explore(find=success, avoid=abort)
if simu.found:
res = simu.found[0]
for i in range(3):
print(res.posix.dumps(i))
else:
print("No result!")
"""
b'UFOHHURD'
b'placeholder\nEnter the password: Good Job.\n'
b''
"""
03_angr_symbolic_registers
??如下截圖所示,是本題的主要邏輯,同樣也是先獲取輸入,然后使用了三個函式進行變換,最后進行判斷,

??這道題最主要的區別在于scanf讀取了三個引數,以前的angr版本不支持scanf獲取多個引數,所以會采用設定暫存器求解的方法,如下腳本所示,不過現在安裝最新版本的話是可以支持scanf獲取多個引數的,所以仍然可以用上面的方法直接求解,
import angr
import claripy
pro = angr.Project("./03_angr_symbolic_registers")
start_addr = 0x080488D1
init_state = pro.factory.blank_state(addr=start_addr) # blank_state, not entry_state
# create three vars
size_in_bits = 32
passwd0 = claripy.BVS("passwd0", size_in_bits)
passwd1 = claripy.BVS("passwd1", size_in_bits)
passwd2 = claripy.BVS("passwd2", size_in_bits)
# scanf --> eax/ebx/edx
init_state.regs.eax = passwd0
init_state.regs.ebx = passwd1
init_state.regs.edx = passwd2
simu = pro.factory.simgr(init_state)
def success(state):
output = state.posix.dumps(1)
return b"Good Job." in output
def abort(state):
output = state.posix.dumps(1)
return b"Try again." in output
simu.explore(find=success, avoid=abort)
if simu.found:
res = simu.found[0]
solu0 = res.solver.eval(passwd0) # you can also use init_state.regs.eax replace passwd0
solu1 = res.solver.eval(passwd1)
solu2 = res.solver.eval(passwd2)
solu = " ".join(map("{:x}".format, [solu0, solu1, solu2]))
print(solu)
else:
print("No result!")
"""
db01abf7 4930dc79 d17de5ce
"""
04_angr_symbolic_stack
??如下截圖所示,是該程式的主邏輯,這道題和上面一道題類似,同樣需要設定引數來求解,

??腳本如下,由于設定引數是在堆疊上,所以我們需要在初始狀態時模擬程式本身的堆疊情況,即在正確的堆疊位置將引數傳進入,主要看esp和ebp兩個指標,
import angr
import claripy
pro =angr.Project("./04_angr_symbolic_stack")
start_addr = 0x08048697
init_state = pro.factory.blank_state(addr=start_addr)
# 初始時ebp無值,esp有值,將兩個置于同一個值
init_state.regs.ebp = init_state.regs.esp
passwd0 = claripy.BVS("passwd0", 32)
passwd1 = claripy.BVS("passwd1", 32)
# 安裝程式的運行邏輯構造正確的堆疊幀
init_state.regs.esp -= 8
init_state.stack_push(passwd0)
init_state.stack_push(passwd1)
# test esp
# 因為程式是用ebp索引區域變數,所以區域變數和ebp的位置一定要準確
# 而對于esp,只需要保證ebp索引區域變數時esp的值是小于該區域變數的位置即可
# init_state.regs.esp -= 0x100
# print(init_state.regs.esp, init_state.regs.ebp)
simu = pro.factory.simgr(init_state)
def success(state):
output = state.posix.dumps(1)
return b"Good Job." in output
def abort(state):
output = state.posix.dumps(1)
return b"Try again." in output
simu.explore(find=success, avoid=abort)
if simu.found:
res = simu.found[0]
solu0 = res.solver.eval(passwd0)
solu1 = res.solver.eval(passwd1)
solu = " ".join(map(str, [solu0, solu1]))
print(solu)
else:
print("No result!")
"""
1213922930 1153451551
"""
05_angr_symbolic_memory
??如下截圖所示,是本題的主邏輯,同樣也是scanf讀取多個引數,其中有四個引數都在bss段中,

??求解腳本如下,
import angr
import claripy
pro = angr.Project("./05_angr_symbolic_memory")
start_addr = 0x08048603
init_state = pro.factory.blank_state(addr=start_addr)
# symbolics
syms = []
for i in range(4):
syms.append(claripy.BVS(str(i), 64))
# scanf 引數位置
bss = [0x0A29FAA0, 0x0A29FAA8, 0x0A29FAB0, 0x0A29FAB8]
for i in range(4):
init_state.memory.store(bss[i], syms[i])
good = 0x08048677
bad = 0x08048665
simu = pro.factory.simgr(init_state)
simu.explore(find=good, avoid=bad)
if simu.found:
res = simu.found[0]
flag = b""
for i in range(4):
flag += res.solver.eval(syms[i], cast_to=bytes) + b" " # cast_to only bytes or int
print(flag)
else:
print("No result!")
"""
QLVJFWGI YPEKZGCV LCWKCULV STBDTTXE
"""
06_angr_symbolic_dynamic_memory
??如下截圖所示,是本題的主要邏輯,scanf會讀取兩個引數,而兩個引數的記憶體位置是由malloc動態分配的,

??求解腳本如下,
import angr
import claripy
pro = angr.Project("./06_angr_symbolic_dynamic_memory")
start_addr = 0x0804869B
init_state = pro.factory.blank_state(addr=start_addr)
syms = []
for i in range(2):
syms.append(claripy.BVS(str(i), 64))
# 假的未使用的地址
fake_heap_addr = [0x4444444, 0x4444454]
# 引數指標地址
buffer_addr = [0x0A79A118, 0x0A79A120]
for i in range(2):
# angr 默認為大端,這里要使用程式的arch
init_state.memory.store(buffer_addr[i], fake_heap_addr[i], endness=pro.arch.memory_endness)
init_state.memory.store(fake_heap_addr[i], syms[i])
good = 0x08048763
bad = 0x08048751
simu = pro.factory.simgr(init_state)
simu.explore(find=good, avoid=bad)
if simu.found:
res = simu.found[0]
flag = b""
for i in range(2):
flag += res.solver.eval(syms[i], cast_to=bytes) + b" "
print(flag)
else:
print("No result!")
"""
IDMRHRCZ PLBQSLBO
"""
總結
不忘初心,砥礪前行!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/300416.html
標籤:其他
上一篇:隴劍杯Writeup(部分)
