作者:Yimi Hu & Light @ PwnMonkeyLabs
原文鏈接:https://mp.weixin.qq.com/s/PPWd5wGwbPLffgMNPO70EA

簡介

在本專題第8篇中,我們解析了家庭版果加智能門鎖的固件代碼,在其中找到了一個語音提示函數,即函數sub_800D40C。選中該函數按快捷鍵’x’,可以查找該函數的交叉引用,但這里有一個問題是,我們無法查看調用該函數時傳遞的參數,截圖如下:

圖片

圖1-1 函數sub_800D40C的交叉引用

如果我們可以列出每處調用時傳給該函數的參數,那么分析效率就會提升許多。正好我們可以借此機會入門一下idapython,為以后的工作打好基礎。

開發環境

如果不是使用記事本直接寫代碼的大佬,建議還是配置一個合適的開發環境,工欲善其事,必先利其器。Python的開發環境有很多,我們這里選擇WingIDE作為其開發環境,其他環境如pycharm也是可以的,但這里不做過多演示。WingIDE的官網是:http://wingware.com,在官網上可以找到3個版本,一個收費版WingIDE pro和兩個免費版WingIDE personal和WingIDE 101,理所當然的,WingIDE pro功能要強大一些,但免費版也滿足我們的需求。下載完成安裝之后,WingIDE會自動查找到python的安裝路徑,完成python開發的配置工作。

然后我們需要再配置一下,使WingIDE可以調試和開發idapython腳本,參考WingIDE官方的配置方法:http://wingware.com/doc/howtos/idapython。首先,我們將WingIDE安裝目錄下的wingdbstub.py文件復制到IDA安裝目錄下python文件夾中。這是因為我們的idapython腳本需要引用wingdbstub庫,如果該文件不在對應的目錄下,就會引用失敗。然后,我們打開WingIDE的遠程調試功能,如下圖所示:

圖片

圖2-1 開啟WingIDE遠程調試

此后,我們寫的idapython腳本只要額外加上兩行代碼,就可以使用WingIDE作為調試工具了,需要添加的代碼如下:

圖片

圖2-2 idapython腳本中啟用WingIDE調試

最后,我們wingIDE寫一個小腳本,并在腳本中下一個斷點,用以驗證我們之前的配置工作是否成功,代碼截圖如下:

圖片

圖2-3 測試腳本用于驗證配置是否成功

上圖中,可以在紅框之后的代碼段下斷或者單步調試;測試腳本的含義是查找地址0x800D40C的交叉引用,在下一章中,會詳細解釋idapython的編寫入門。

在IDA中運行該腳本,如下圖所示:

圖片

圖2-4 IDA加載測試腳本

可以看到wingIDE中,代碼順利斷在了第9行斷點處,可以直接查看代碼中的變量值,如下圖所示:

圖片

圖2-5 wingIDE調試idapython腳本

刪除斷點,待腳本運行完畢之后,就可以在IDA output窗口觀測到腳本輸出內容。

到此,我們的開發環境就完成了。

idapython腳本編寫

開發idapython腳本,首先需要掌握python腳本的編寫,我們這里假設所有讀者具備了基本的python腳本開發能力,起碼寫個hello world沒啥問題。此外還需要熟悉idapython獨有的各個模塊。

idapython的所有模塊都在其安裝目錄下的python文件夾中,如下圖所示:

圖片

圖3-1 idapython提供的各個模塊

在IDA 6.95之前的版本中,該文件夾中并沒有這么多文件,僅有idaapi.py、idc.py和idautils.py,所有的idapython提供的功能全部都由這3個模塊實現。而在IDA 7.0之后的版本,IDA將idapython提供的各種函數分門別類的放在了ida_開頭的py文件中,雖然也保留了idaapi.py等文件,但看起來只是為了向下兼容而保留的。此外,這些文件的名字,如ida_auto.py,看起來就是對應idc SDK中定義的頭文件的名字,如截圖中的ida_auto.py對應idc SDK中auto.hpp文件。鑒于此,我們在開發idapython腳本的時候,應該減少使用idaapi.py等文件,因為該文件也許在下個版本中該文件就會消失。

所有idapython提供的模塊可以在hex-rays官網上找到說明,地址如下:https://www.hex-rays.com/products/ida/support/idapython_docs/。還可以在idapython的github上找到相關的樣例,地址如下:https://github.com/idapython/src。當然,最快的上手方法還是直接拿別人的代碼讀一讀改一改,跑起來就行。下面將圖2-2的代碼稍作調整:

import ida_xref
import ida_idc

import wingdbstub
wingdbstub.Ensure()

def ListXref(FuncAddr):
    index = 0
    for xref in XrefsTo(FuncAddr,   0):
        FuncArg = find_function_arg(xref.frm)
        FormatStr = '%02d,  0x%08X calls sub_800D40C(0x%02X)' % (index,   xref.frm, FuncArg)
        print(FormatStr)
        index += 1

def find_function_arg(addr):
    for i in range(20):
        addr = PrevHead(addr)
        if GetMnem(addr) ==   "MOVS" and "R0" in GetOpnd(addr, 0):
            FuncArg = GetOpnd(addr,   1)
            return   int(FuncArg[1:],16)
    return ""


if __name__=='__main__':
    print('List the Xref to   sub_800D40C')
    ListXref(0x800D40C)
    print('List end')

代碼3-1 用于顯示函數調用參數的idapython代碼

在上圖中的代碼中,函數find_function_arg就是我們直接參考其他人的代碼修改而來;PrevHead、GetMnem、GetOpnd、XrefsTo等都是idapython提供的函數或變量,相信大家看了這段程序就能理解這幾個關鍵字的定義,分別是獲取上一個指令或數據的地址、獲取該地址的指令、獲取該地址的操作對象、查找交叉引用的意思,詳細解析可以去官網查詢或者直接查看idapython庫中的注釋內容。這段idapython代碼的最終運行效果如下圖所示:

圖片

圖3-2 代碼運行效果

可以從上圖中看到,所有調用sub_800D40C函數的地址和調用參數已經展示出來,滿足了我們的需求。

小結

本篇是胖猴小玩鬧專題的第一篇番外,我們主要介紹了idapython的基本功能。在本專題的后續文章中,我們將在很多地方使用idapython幫助我們分析,所以正好借此機會對idapython做個簡要的介紹。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1535/