Angr:一個具有動態符號執行和靜態分析的二進位制分析工具
什麼是angr:
angr是一個二進位制程式碼分析工具,能夠自動化完成二進位制檔案的分析,並找出漏洞。在二進位制程式碼中尋找並且利用漏洞是一項非常具有挑戰性的工作,它的挑戰性主要在於人工很難直觀的看出二進位制程式碼中的資料結構、控制流資訊等。angr是一個基於python的二進位制漏洞分析框架,它將以前多種分析技術整合進來,它能夠進行動態的符號執行分析(如,KLEE和Mayhem),也能夠進行多種靜態分析。
angr的基本過程:
1)將二進位制程式載入angr分析系統
2)將二進位制程式轉換成中間語言(intermediate representation, IR)
3)將IR語言轉換成語義較強的表達形式,比如,這個程式做了什麼,而不是它是什麼。
4)執行進一步的分析,比如,完整的或者部分的靜態分析(依賴關係分析,程式分塊)、程式空間的符號執行探索(挖掘溢位漏洞)、一些對於上面方式的結合。
如何安裝:
Linux
首先安裝依賴
sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper
安裝angr
mkvirtualenv angr && pip install angr
到此angr安裝結束
Mac OS
首先安裝依賴
pip install -I --no-use-wheel angr-only-z3-custom
安裝angr
pip install angr
安裝完成之後,就可以匯入angr了,但是這個地方我與網上一些教程不同,在匯入angr的時候,有的教程上面說需要先進入angr的虛擬環境 然後再進入python環境中,再匯入angr模組,但是我按照他說的那樣做報錯,是沒有angr模組的,本人用的ubuntu14.04虛擬機器,直接進入python環境才可以匯入angr模組,不需要進入angr的虛擬環境。
angr過程分析
angr-CLE
CLE是angr載入二進位制檔案的組建,在載入二進位制檔案的時候會分析病讀取binary的資訊,包括指令地址、shared library、arch information等等。
>>> import angr
>>> b = angr.Project("./test")
angr_IR
angr用VEX IR將指令轉化為中間語言IR,分析IR並且模擬,搞清楚它是什麼並且做了什麼。
如下的ARM指令
subs R2, R2, #8
轉化為VEX IR
t0 = GET:I32(16)
t1 = 0x8:I32
t3 = Sub32(t0,t1)
PUT(16) = t3
PUT(68) = 0x59FC8:I32
angr-Solver Engine
angr的求解引擎叫Claripy,具體這一步做什麼呢,根據程式所需要的輸入設定符號變數以及收集限制式等等。
>>> import claripy
>>> bv = claripy.BVV(0x41424344, 32)
例子
這個例子說明了angr的簡單用法,用例程式來自https://github.com/angr/angr-doc/tree/master/examples/sym-write,其中有三個檔案,一個是C原始碼,一個是二進位制程式碼,還有一個是python指令碼檔案也就是我們的測試指令碼。
原始碼如下
#include <stdio.h>
char u=0;
int main(void)
{
int i, bits[2]={0,0};
for (i=0; i<8; i++) {
bits[(u&(1<<i))!=0]++;
}
if (bits[0]==bits[1]) {
printf("you win!");
}
else {
printf("you lose!");
}
return 0;
}
這個原始碼很簡單就不介紹了。
接下來就是python指令碼檔案,GitHub上面的指令碼檔案在我的虛擬機器上面執行出錯,據本人分析應該是angr更新之後對於有一些屬性進行了修改,比如指令碼檔案中的sm = p.factory.simgr(state) 這一行程式碼就會報錯,因為沒有simgr這個屬性,以及在main函式return的地方sm.found[0].state.se.any_int(u),這樣修改才不會報錯,不然會出現path沒有se屬性的錯誤。為了便於閱讀,我自己寫了一個python指令碼對這個二進位制程式碼進行分析。
import angr
import claripy
def main():
p = angr.Project('./issue', load_options={"auto_load_libs": False})
state = p.factory.entry_state(add_options={"SYMBOLIC_WRITE_ADDRESSES"})
u = claripy.BVS("u", 8)
state.memory.store(0x804a021, u)
sm = p.factory.path_group(state)
sm.step(until = lambda lpg: len(lpg.active) > 1)
for I in range(len(sm.active)):
print "possible %d: " % I , sm.active[I].state.se.any_int(u)
return sm.active[0].state.se.any_int(u)
if __name__ == '__main__':
print(repr(main()))
程式碼分析:
新建一個angr工程,匯入二進位制檔案,./issue是二進位制檔案的路徑。後面選項是是否自動載入依賴。
p = angr.Project('./issue', load_options={"auto_load_libs": False})
緊接著建立一個SimState物件,SimState的物件在angr其中的一個子模組SimuVEX中,這個物件記錄著符號資訊,符號對應的記憶體資訊,暫存器資訊等等。
state = p.factory.entry_state(add_options={"SYMBOLIC_WRITE_ADDRESSES"})
設定符號變數,讀C原始碼可以知道,變數u其實可以視為一個外部輸入,根據u的值的不同,最後得到的結果也不相同,如果u的二進位制表示中1和0的個數相同就返回win,否則返回lose。所以在此設計符號變數u
u = claripy.BVS("u", 8)
將符號變數儲存在記憶體的指定地址處
state.memory.store(0x804a021, u)
接下來根據狀態可以獲取路徑組物件,這個路徑和你的狀態引數有關,如果是入口狀態那麼你得到的路徑就是入口處的路徑。從獲得的路徑點開始,之後所有此路徑分裂出去的路徑都會包含在此路徑組裡面。
sm = p.factory.path_group(state)
然後開始執行,一直執行到active狀態的路徑超過一條的時候停止,即執行到C原始碼的if(bits[0]==bits[1])處,產生了兩條路徑,然後停止。
sm.step(until = lambda lpg: len(lpg.active) > 1)
然後打印出所有路徑下的的U的取值
for I in range(len(sm.active)):
print "possible %d: " % I , sm.active[I].state.se.any_int(u)
返回結果是win的u的取值
return sm.active[0].state.se.any_int(u)
到此主要程式碼分析結束,在GitHub上面有原始的python指令碼,那個指令碼只是輸出結果為win時候的U的取值,但是那個指令碼有錯誤,並且不太簡介,由此我在此基礎上做了修改,寫了上述指令碼,供大家參考分析。
總結
以上就是angr的基本用法,本人對於angr的理解也還是很淺顯,有些地方也不是特別清楚,如果有錯的地方,希望大家指點。