<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    簡易IDA Python腳本

    本文由看雪論壇原創。看雪ID:GitRoy

    0x0 寫在前面

    不知道你是否也和我遇到同樣的問題,由于匯編指令不是很熟悉,通常一個樣本會分析很久。白天工作,晚上分析樣本,這時候遇到一個問題,前一天分析的東西,第二天要花好一段時間去復現昨日的現場。

    .idb文件經過多次調試已經被玩壞了,怕分析流程沒了,又不敢刪除,硬著頭皮看已經被搞的不像樣的idb。

    動態加載的so,分析的時候,自己截圖去記錄分析到哪里了。

    每次定位一些函數(例如init_array、jni_onload)都要自己去算一下偏移,然后G,F2……

    樣本中的花指令搞到頭大,看100句花指令才能看到有一句有用的指令。

    好不容易復現昨天的現場,忽然數據線掉了,或者忽然自己F9略過了關鍵函數,然后重頭再來。

    再或者很多拉低效率的問題。

    記得曾經分析第一個樣本的時候,我第一天分析的東西,都截圖記錄下來,第二天晚上回去繼續分析的時候,我要花大概15-20分鐘去復現昨天的現場,如果不小心錯過了重要的函數,就得重頭再來,后面前前后后浪費了很多時間才搞出來,最后分享會演示的時候,直接被老大鞭策了。

    所以才開始了解并編寫ida腳本,不過由于網上能看的資料實在是比較少,踩了不少坑,到今天為止終于可以愉快,并且順暢的寫腳本了。所以把流程分享給大家,希望能幫助像我一樣的人提高分析效率。

    0x1 簡述流程

    我使用的IDA是泄露版本的7.0然后python版本是2.7

    1、首先創建代理腳本.py文件,我這邊是roy_hook_proxy.py文件,內容如下:

    # encoding: utf-8
    import sys
    sys.path.append('/Users/roy/Documents/PycharmProjects/roytool')
    from royhook import RoyHook
    
    def PLUGIN_ENTRY():
        return RoyHook()

    這樣好處顯而易見,直接把代理文件丟進插件目錄,然后引入我們插件的module就好了,這樣就不會直接操作ida的插件目錄,不容易出問題。我這邊真正寫插件的目錄就是roytool

    2、然后將roy_hook_proxy.py文件拷貝到ida插件目錄

    我這邊目錄/Applications/ida.app/Contents/MacOS/plugins。

    3、編寫插件主入口

    下面我們就可以在我們之前append的module(roytool)下編寫腳本了。

    import sys
    # 引入ida提供給我們的api
    import idaapi
    # 引入pyqt,編寫交互界面
    from PyQt5 import QtWidgets
    
    # 這里一定要繼承ida提供的插件的base類
    class RoyHook(idaapi.plugin_t):
        flags = idaapi.PLUGIN_KEEP
        comment = "royhook a ida pro plugin"
        help = ""
        # ida插件的名字
        wanted_name = "royhook"
        # ida插件的快捷鍵
        wanted_hotkey = "Alt+F6"
        windows = None
    
        def __init__(self):
            super(RoyHook, self).__init__()
            flags = idaapi.PLUGIN_KEEP
            pass
    
        # 腳本初始化的時候調用
        def init(self):
            return idaapi.PLUGIN_OK
    
        # 初始化后開始運行的時候調用
        def run(self, arg):
            idaapi.require('view')
            idaapi.require('view.main_view')
            main_window = view.main_view.MainView()
            if self.windows is None or not self.windows.isVisible():
                self.windows = QtWidgets.QMainWindow()
                main_window.setupUi(self.windows)
                self.windows.showNormal()
            pass
        # 腳本結束的時候調用
        def term(self):
            return idaapi.PLUGIN_OK

    打開ida直接驗證效果,如果上面的代碼沒有問題,Edit-Plugins。

    我們會看到上面圖中的樣子,點擊我們的插件(或者用快捷鍵)就會運行init、run函數中的代碼了。

    到此為止,我們插件的基本流程就算完事了。下面繼續介紹。

    4、實時更新插件代碼

    在不使用idaapi.require(module)的情況下,每次更新插件代碼,ida都要大退重啟,才能生效,這時候就需要idaapi.require。

    ex:

    idaapi.require('view')
    idaapi.require('view.logsaver_view')

    注意:既然用了idaapi.require 就不要使用import功能了,不然代碼依然不會實時更新。

    0x2 提高效率

    這里舉個例子,比如我們要快速定位到init_array函數。

    def goInitarray(self):
        # _get_modules是idc提供的接口,如其名
        for module in idc._get_modules():
            # 遍歷所有module,找到linker
            module_name = module.name
            if 'linker' in module_name:
                print 'linker address is ' + str(hex(module.base + 0x2464))
                # 0x2464是Android某個版本的init_array的偏移地址,
                # jumpto可以直接跳轉到目標地址
                idc.jumpto(module.base + 0x2464)
                # 在init_array上下個斷點
                idc.add_bpt(module.base + 0x2464, 1)
                # makecode更不用說了,相當于C
                idaapi.auto_make_code(module.base + 0x2464)

    通過上面的代碼就可以直接定位到init_array了,真滴是又簡單,又方便。

    我在這里可能至少能剩下1-2分鐘的時間/每次調試,而且這里只是舉例init_array, 我們也可以這樣去調試我們要調試的函數。

    2、保存日志、函數名字

    我們分析的過程中,會在匯編指令做個中記錄日志,比如某個寄存器的值了,或者某個函數是做什么的,再或者,我們會直接更改函數的名字,方便每次查看。

    這時候,為了效率,或者說能快速的復現上次調試的現場,我們就可以用這個功能,代碼也非常簡單。

    # 通過起始地址,終止地址,以及偏移地址去保存日志
    def saveDebugMessage(self):
        # create file first
        # 用個輕量級的存儲shelve
        f = shelve.open(self.id)
        # 保存日志的起始地址
        addr_start = int(self.address_start, 16)
        # 保存日志的終止地址
        addr_end = int(self.address_end, 16)
        log_dict = {}
        log_dict_list = []
        for num in range(addr_start, addr_end):
            # 獲取我們當前地址的日志
            com = idc.GetCommentEx(num, True)
            if com != None:
                #獲取函數名
                fun_name = idc.GetFunctionName(num)
                print fun_name
                if fun_name != None and not 'sub' in fun_name:
                    log_dict = {'offset': str(num - addr_start), 'msg': str(com), 'funtion_name': str(fun_name)}
                else:
                    log_dict = {'offset': str(num - addr_start), 'msg': str(com)}
                log_dict_list.append(log_dict)
                pass
        print(log_dict_list)
        # 保存日志
        f['info'] = log_dict_list
        f.close()
     # 通過起始地址即可,會自動判斷長度,并且獲取偏移地址去設置日志
    def loadDebugMessage(self):
        f = shelve.open(self.id)
        data = f['info']
        addr_start = int(self.address_start, 16)
        for num in range(0, len(data)):
            offset = data[num]['offset']
            msg = data[num]['msg']
            fun_name = data[num]['funtion_name']
            idc.MakeRptCmt(addr_start + int(offset), msg)
            if fun_name is not None and fun_name != '':
                idc.SetFunctionCmt(addr_start + int(offset), fun_name, False)

    3、過一些簡單的花指令

    篇幅問題,就簡單的舉幾個常用的,就是想體現很簡單的代碼,就能節省我們大量的分析時間,我們可以找到自己效率低的地方,然后用idc接口去提高分析效率。

    0x3 使用pyqt交互界面,讓腳本更人性化

    先看效果:

    1、推薦工具Qt Designer工具進行畫界面

    最后保存為.ui文件,再使用pyuic5 -o xxx.py xxx.ui,就能直接輸出.py文件了,我們保存到項目中就可以直接用(具體配置可以自行google)。

    2、demo

    class LogSaver_MainWindow(object):
    
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(476, 410)
            self.windows = QtWidgets.QMainWindow()
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.label = QtWidgets.QLabel(self.centralwidget)
            self.label.setGeometry(QtCore.QRect(110, 100, 91, 16))
            self.label.setObjectName("label")
            self.label_2 = QtWidgets.QLabel(self.centralwidget)
            self.label_2.setGeometry(QtCore.QRect(110, 170, 91, 16))
            self.label_2.setObjectName("label_2")
            self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit.setGeometry(QtCore.QRect(210, 100, 131, 21))
            self.textEdit.setObjectName("textEdit")
            self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_2.setGeometry(QtCore.QRect(210, 170, 131, 21))
            self.textEdit_2.setObjectName("textEdit_2")
            self.pushButton = QtWidgets.QPushButton(self.centralwidget)
            self.pushButton.setGeometry(QtCore.QRect(100, 240, 113, 51))
            self.pushButton.setObjectName("pushButton")
            self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
            self.pushButton_2.setGeometry(QtCore.QRect(260, 240, 113, 51))
            self.pushButton_2.setObjectName("pushButton_2")
            self.label_3 = QtWidgets.QLabel(self.centralwidget)
            self.label_3.setGeometry(QtCore.QRect(110, 50, 60, 16))
            self.label_3.setObjectName("label_3")
            self.textEdit_3 = QtWidgets.QTextEdit(self.centralwidget)
            self.textEdit_3.setGeometry(QtCore.QRect(210, 50, 131, 21))
            self.textEdit_3.setObjectName("textEdit_3")
            self.checkBox = QtWidgets.QCheckBox(MainWindow)
            self.checkBox.setGeometry(QtCore.QRect(90, 360, 141, 20))
            self.checkBox.setObjectName("checkBox")
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            print('create windows')
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "日志工具"))
            self.label.setText(_translate("MainWindow", "開始地址:"))
            self.label_2.setText(_translate("MainWindow", "結束地址:"))
            self.pushButton.setText(_translate("MainWindow", "存儲"))
            self.pushButton_2.setText(_translate("MainWindow", "加載"))
            self.label_3.setText(_translate("MainWindow", "唯一id:"))
            self.checkBox.setText(_translate("Dialog", "是否自動保存函數名"))
            self.pushButton.clicked.connect(self.showDialog)
            self.pushButton_2.clicked.connect(self.showDialogLoad)

    方便又簡單。

    OK,到這里就完成了,我相信我里面提到的問題,肯定還有更好的解決方案,不一定要寫腳本,但是由于自己經驗少,可能只能通過腳本來完成,我分享這個是希望告訴像之前的我一樣的小伙伴,工欲善其事,必先利其器!

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类