利用IDAPython靜態(tài)分析函數(shù)調(diào)用路徑

在挖掘設(shè)備的固件漏洞時(shí),會(huì)面臨沒(méi)有源代碼、無(wú)法動(dòng)態(tài)跟蹤調(diào)試的情況,此時(shí)就需要進(jìn)行靜態(tài)的人工分析。在靜態(tài)人工分析過(guò)程中,往往需要圍繞危險(xiǎn)函數(shù)、用戶輸入提取需要重點(diǎn)分析的執(zhí)行路徑,以有效縮小分析范圍。本文利用IDA Python腳本,實(shí)現(xiàn)了自動(dòng)提取函數(shù)正、反向調(diào)用關(guān)系的功能,可有效輔助分析危險(xiǎn)函數(shù)調(diào)用路徑,用戶輸入流向等。

創(chuàng)新互聯(lián)建站長(zhǎng)期為數(shù)千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為鹿寨企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè),鹿寨網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。

一、問(wèn)題描述
近期在研究某款設(shè)備,由于該設(shè)備使用MIPS架構(gòu),IDA Pro的F5無(wú)法使用,安裝的RetDec插件也不給力,給出的偽代碼不忍直視;還有不少代碼IDA Pro沒(méi)有識(shí)別出來(lái),且無(wú)法識(shí)別庫(kù)函數(shù);此外,固件使用傳統(tǒng)的嵌入式操作系統(tǒng)(非類Linux)實(shí)現(xiàn),不存在進(jìn)程等高級(jí)概念,因此也沒(méi)有解決動(dòng)態(tài)調(diào)試的問(wèn)題。在這種情況下,似乎只能人工靜態(tài)分析了。

進(jìn)行初步分析之后,發(fā)現(xiàn)需要先解決兩個(gè)問(wèn)題:一是將IDA pro無(wú)法識(shí)別的代碼強(qiáng)制轉(zhuǎn)換成代碼,以完善IDA pro的交叉引用關(guān)系;二是以文本方式提取函數(shù)調(diào)用關(guān)系(正向、反向),取IDA pro的Xrefs graph to和Xrefs graphp from功能,IDA pro的這兩個(gè)功能對(duì)稍微復(fù)雜一定的文件根本不存在實(shí)用性——顯示的圖形根本看不清。

二、強(qiáng)制轉(zhuǎn)換未解析的代碼
針對(duì)第一個(gè)問(wèn)題,考慮到MIPS的指令均為32bit,可利用IDAPython遍歷指定的地址空間,把未定義的部分全部轉(zhuǎn)換成代碼。具體的代碼如下:

def define_func(beg, end):

cur = beg

if beg%4 != 0:

    cur = beg + 4 - beg%4 # 對(duì)齊

end = end - end%4

while cur < end:

    if ida_kernwin.user_cancelled():

        print('Cancelled')

        break

    cur_func = ida_funcs.get_func(cur)

    print("cur 0x%08x" % cur)

    if cur_func is None:

        if ida_funcs.add_func(cur):

            cur = ida_funcs.get_func(cur).endEA

        else:

            cur = cur + 4

    else:

        cur = cur_func.endEA

使用時(shí)步驟如下:

1、按shift+f2,在Execute script窗口中,Script language選擇Python

2、把上述代碼粘貼到Please enter script body中

3、點(diǎn)擊Run,關(guān)閉Execute script窗口。

4、在Output window下方的Python【IDC】按鈕右側(cè),執(zhí)行define_func(0x8000000,0x80002000),參數(shù)僅作示例,根據(jù)實(shí)際情況調(diào)整

三、獲取函數(shù)調(diào)用樹(shù)
1、正向調(diào)用樹(shù)
正向調(diào)用樹(shù)以指定函數(shù)為起點(diǎn),根據(jù)指定遞歸深度,獲取其所有子函數(shù),通常應(yīng)用于跟蹤用戶輸入數(shù)據(jù)的流向。實(shí)現(xiàn)思路如下:遍歷指定函數(shù)(由參數(shù)指定)代碼,如果當(dāng)前指令為函數(shù)調(diào)用,則遞歸,直到達(dá)到遞歸深度或者沒(méi)有子函數(shù)的函數(shù)。具體代碼如下:

import idautils

call_chain = [] # 存放正向調(diào)用鏈信息

def gen_call_chain(func_name, osintneting):

del call_chain[:]

f_call_out = open('d:\\call.csv', 'w')

get_my_callee(func_name, osintneting, f_call_out)

f_call_out.close()

def get_my_callee(func_name, osintneting, fl):

#print('call %s %d' % (func_name, osintneting))

if ida_kernwin.user_cancelled():

    print('Cancelled')

    fl.close()

    exit()

str = '{0}\t'.format(func_name)

call_chain.append(str)

addr = get_name_ea(0, func_name)

# 獲取所有子函數(shù)

dism_addr = list(idautils.FuncItems(addr))

xref_froms = []

for ea in dism_addr:

    if ida_idp.is_call_insn(ea) is False:

        continue

    else:

        callee = get_first_fcref_from(ea)

        if callee != addr:

            xref_froms.append(callee)

xref_froms = set(xref_froms)

# 嵌套結(jié)束條件

osinteneting_end = False

if len(xref_froms) == 0:

    osinteneting_end = True

elif osintneting == -1:

    osinteneting_end = False

elif osintneting == 1:

    osinteneting_end = True

if osinteneting_end is True:

    for callee in call_chain:

        sys.stdout.write(callee)

        fl.write(callee)

    sys.stdout.write('\r\n')

    fl.write('\r\n')

    call_chain.pop()

    return

# 深度優(yōu)先

for xref_from in xref_froms:

    callee_name = get_func_name(xref_from)

    if osintneting == -1:

        get_my_callee(callee_name, -1, fl)

    else:

        get_my_callee(callee_name, osintneting - 1, fl)

call_chain.pop()

使用方法參照“強(qiáng)制轉(zhuǎn)換未解析的代碼”一節(jié)中的方法,調(diào)用gen_call_chain函數(shù)即可。gen_call_chain函數(shù)的第一個(gè)參數(shù)是函數(shù)名,第二參數(shù)是遞歸的次數(shù)限制,如果為-1,則會(huì)一直遞歸到葉子函數(shù)(無(wú)子函數(shù)的函數(shù))。在生成調(diào)用樹(shù)時(shí),每條調(diào)用路徑對(duì)應(yīng)一行文本,在IDA pro的Output window的輸出如下

Python>gen_call_chain('start', 5)

start sub_4010E0 sub_400DD0 sub_401B40 sub_401B80

start sub_4010E0 sub_400DD0 sub_401B40 sub_401A00

start sub_4010E0 sub_400DD0 sub_444750 sub_472EB0

start sub_4010E0 sub_400DD0 sub_444750 sub_43F8C0

start sub_4010E0 sub_400DD0 sub_444750 sub_472FE0

start sub_4010E0 sub_400DD0 sub_444750 sub_43F920

start sub_4010E0 sub_400DD0 sub_40EFF0 sub_40EE10

2、反向調(diào)用樹(shù)
反向調(diào)用樹(shù)以指定函數(shù)為起點(diǎn),根據(jù)指定遞歸深度,獲取其所有父函數(shù),通常應(yīng)用于跟蹤危險(xiǎn)函數(shù)被調(diào)用的路徑。實(shí)現(xiàn)思路如下:先獲取引用指定函數(shù)(由參數(shù)指定)的函數(shù),然后依次遞歸,直到達(dá)到遞歸深度或者沒(méi)有父函數(shù)的函數(shù)。具體代碼如下:

import idautils

r_call_chain = [] # 存放反向調(diào)用鏈信息

def gen_r_call_chain(func_name, osintneting):

del r_call_chain[:]

f_r_call_out = open('d:\\r_call.csv', 'w')

get_my_caller(func_name, osintneting, f_r_call_out)

f_r_call_out.close()

def get_my_caller(func_name, osintneting, fl):

if ida_kernwin.user_cancelled():

    print('Cancelled')

    fl.close()

    exit()

str = '{0}\t'.format(func_name)

r_call_chain.append(str)

addr = get_name_ea(0, func_name)

addr_ref_to = get_first_fcref_to(addr)

# 嵌套結(jié)束條件 

osinteneting_end = False

if addr_ref_to == BADADDR:

    osinteneting_end = True

elif osintneting == -1:

    osinteneting_end = False

elif osintneting == 1:

    osinteneting_end = True

if osinteneting_end is True:

    length = len(r_call_chain)

    for idx in range(length):

        fl.write(r_call_chain[length - idx - 1])

        sys.stdout.write(r_call_chain[length - idx - 1])

    fl.write("\n")

    sys.stdout.write('\r\n')

    r_call_chain.pop()

    return

# 深度優(yōu)先

while (addr_ref_to != BADADDR) and (addr_ref_to != addr):

    parent_func_name = get_func_name(addr_ref_to)

    get_my_caller(parent_func_name, osintneting - 1, fl)

    addr_ref_to = get_next_fcref_to(addr, addr_ref_to)

    if addr_ref_to == BADADDR:

        r_call_chain.pop() # 如果沒(méi)有引用函數(shù),彈出當(dāng)前函數(shù)

        break

使用方法參照“強(qiáng)制轉(zhuǎn)換未解析的代碼”一節(jié)中的方法,調(diào)用gen_r_call_chain函數(shù)即可。gen_r_call_chain函數(shù)的第一個(gè)參數(shù)是函數(shù)名,第二參數(shù)是遞歸的次數(shù)限制,如果為-1,則會(huì)一直遞歸到頂層函數(shù)(無(wú)父函數(shù)的函數(shù))。在生成調(diào)用樹(shù)時(shí),每條調(diào)用路徑對(duì)應(yīng)一行文本,在IDA pro的Output window的輸出如下:

Python>gen_r_call_chain('sub_4432E0', 5)

start sub_4010E0 sub_400DD0 sub_4432E0

sub_47D3F0 sub_4767D0 sub_474770 sub_4432E0

sub_496370 sub_4767D0 sub_474770 sub_4432E0

sub_480930 sub_47D020 sub_4432E0

sub_48C450 sub_47D020 sub_4432E0

sub_499C60 sub_47D020 sub_4432E0

sub_480930 sub_47D020 sub_4432E0

sub_48C450 sub_47D020 sub_4432E0

四、小結(jié)
通過(guò)上述IDAPython腳本,可方便獲取指定函數(shù)的調(diào)用樹(shù)。調(diào)用樹(shù)的輸出有兩處,一處IDA pro的Output window;另一處是指定的文件,文件路徑是硬編碼的,各位看官可自行修改。暫時(shí)沒(méi)有以插件方式實(shí)現(xiàn),有興趣的同學(xué)可以嘗試下。

網(wǎng)頁(yè)名稱:利用IDAPython靜態(tài)分析函數(shù)調(diào)用路徑
網(wǎng)站URL:http://bm7419.com/article46/jddshg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)標(biāo)簽優(yōu)化、ChatGPT網(wǎng)站排名、服務(wù)器托管網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)