ida python 脚本入门

给IDA写个小插件_ida插件-CSDN博客

测试了一下上面链接的代码

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5 import QtCore, QtGui, QtWidgets

from capstone import *
from keystone import *

import idaapi
import ida_bytes
import ida_funcs
import ida_name
from ida_bytes import get_bytes, patch_byte, patch_dword
from idautils import CodeRefsTo

ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN)
md = Cs(CS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN)

ACTION_FUNEND = "bestida:findfucend"
ACTION_FILLNOP = "bestida:fillnop"

def ks_disasm(dis_str):
    global ks
    encoding, count = ks.asm(dis_str)
    print(f"count   = {count}")
    print(f"encoding   = {encoding}")
    hexdump(encoding)
    return encoding


def hex_cleaner(s):
    s = s.strip()
    s = s.replace("0x", "")
    s = s.replace("h", "")
    s = s.replace("L", "")
    return s

def hexdump(src, length=16):
     result = []
     # 判读输入是否为字符串
     digits = 4 if isinstance(src, str) else 2
     #print("digits = ", digits)      
     for i in range(0, len(src), length):
         # 将字符串切片为16个为一组
         s = src[i:i + length]
         #print("length = ", length) 
         #print("s = ", s) 
         #print("i = ", i)
         
         #用16进制来输出, x是值,digits表示输出宽度(匹配*)
         hexa = ' '.join(["%0*X" % (digits, (x)) for x in s])
         #print("hexa = ", hexa)
         
         hexb = ' '.join(["%02X" % (x) for x in s])
         #print("hexb = ", hexb)
         
          
         # 用来输出原值
         text = ''.join([chr(x) if 0x20 <= x < 0x7F else '.' for x in s])
         #print("text = ", text, "\n""\n""\n")
         
         # %-*s, 星号是length*(digits + 1)的值 
         result.append("%04X    %-*s   |%s|" % (i, length * (digits + 1), hexa, text,))
     print(' \n'.join(result))
                        
#原文链接:https://blog.csdn.net/dp__mcu/article/details/132228450

class menu_action_handler_t(idaapi.action_handler_t):

    def __init__(self, action):
        idaapi.action_handler_t.__init__(self)
        self.action = action

    def activate(self, ctx):
        if self.action == ACTION_FUNEND:
            # 获得选中的开始地址和结束地址
            t0, t1, view = idaapi.twinpos_t(), idaapi.twinpos_t(), idaapi.get_current_viewer()
            print("t0 = ", t0)
            print("t1 = ", t1)
            print("view = ", view)
            if idaapi.read_selection(view, t0, t1):
                start, end = t0.place(view).toea(), t1.place(view).toea()
                size = end - start
                print("startx %x ,end %x" % (start, end))
                self.findfuncend(start, size)
            else:
                print("选择不正确,请选择栈操作空间的部分")
        elif self.action == ACTION_FILLNOP:
            t0, t1, view = idaapi.twinpos_t(), idaapi.twinpos_t(), idaapi.get_current_viewer()
            if idaapi.read_selection(view, t0, t1):
                start, end = t0.place(view).toea(), t1.place(view).toea()
                #这个时候说明选中了一段代码
                if (start == end):
                    idaapi.patch_bytes(start, b"\x1f\x20\x03\xd5")
                    print("%s处填充了一条nop" % hex(start))
                else:
                    #arm64代码指令块为四字节
                    if(end - start) % 4 != 0:
                        print("请选中4字节对齐的代码")
                    else:
                        for i in range(start, end, 4):
                            idaapi.patch_bytes(i, b"\x1f\x20\x03\xd5")
                        print("start %s ,end %s" % (hex(start), hex(end)))
                print("start %s ,end4 %s" % (hex(start), hex(end)))
                #idaapi.patch_bytes(start, b"\x90" * (end - start))


    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS

    def get_inv_opcode(self, insn):
        return '{} {}'.format(self.ins_map.get(insn.mnemonic), insn.op_str)

    def find_sublist_addr(self, lst, sublist):
        sub_len = len(sublist)
        indices = []
        for index in (i for i, e in enumerate(lst) if e == sublist[0]):
            if lst[index:index + sub_len] == sublist:
                indices.append(index)
        return indices

    def find_sublist(self, lst, sublist):
        len_sublist = len(sublist)
        for i in range(len(lst)):
            if lst[i:i + len_sublist] == sublist:
                return i
        return -1

    def findfuncend(self, start, sz):
        self.ins_map = {"sub": "add", "str": "ldr", "stp": "ldp"}

        target = start
        stackOplen = sz
        print("正在计算funcstart %x 所在的函数尾" % (target))
        print("debug1 0x%x" % (target))
        print("stackOplen 0x%x." % (stackOplen))
           
        encodings = [0xC0, 0x03, 0x5F, 0xD6]  # ret 的指令编码
        for i in md.disasm(ida_bytes.get_bytes(target, stackOplen), 0):
            print( f"i = %s\n" % (i) ) 
            if i.op_str.find('!') == -1:
                print(f"{i.mnemonic} {i.op_str}")
                encodings = ks_disasm(self.get_inv_opcode(i)) + encodings
            else:
                s_new = i.op_str.replace( "]!", "" )
                s_new = s_new.replace(", #-", "],")
                dis_str = '{} {}'.format(self.ins_map.get(i.mnemonic), s_new)    #调用约定的栈平衡指令 
                print( f"dis_str = %s\n" % dis_str )
                print(f"mnemonic = {i.mnemonic} , op_str   = {i.op_str}")
                
                print(f"encodings1   = {encodings}")
                hexdump(encodings)
                encodings = ks_disasm(dis_str) + encodings
                hexdump(encodings)
              
                

        opcodelist = list(ida_bytes.get_bytes(target, 0x100))
        #hexdump(opcodelist)
        
        index = self.find_sublist(opcodelist, encodings)
        func_end = target + index  # 函数结尾的地址
        
        print("target 0x%x." % (target))
        print("index 0x%x." % (index))
        
        print("func end addr : ", hex(func_end))


class UI_Hook(idaapi.UI_Hooks):
    def __init__(self):
        idaapi.UI_Hooks.__init__(self)

    def finish_populating_widget_popup(self, form, popup):
        form_type = idaapi.get_widget_type(form)

        if form_type == idaapi.BWN_DISASM and (ARCH, BITS) in [
                                                               (idaapi.PLFM_ARM, 64), ]:
            idaapi.attach_action_to_popup(form, popup, ACTION_FUNEND, "二进制科学/")
            idaapi.attach_action_to_popup(form, popup, ACTION_FILLNOP, "二进制科学/")


class MyForm(QDialog):
    def __init__(self, parent=None):
        super(MyForm, self).__init__(parent)
        self.setupUi(self)
        # 点击确定时候找函数尾巴
        self.button_sure.clicked.connect(self.findfuncend)

        # 出入栈对应的指令
        self.ins_map = {"sub": "add", "str": "ldr", "stp": "ldp"}

        # 报错
        self.registered_actions = []

    # 这个弹窗被拉起,如果按esc就会退出,如果按enter,就会执行找函数结尾
    def keyPressEvent(self, event):
        key_code = event.key()
        if key_code == QtCore.Qt.Key_Escape:
            self.close()
        elif key_code == QtCore.Qt.Key_Enter:
            self.findfuncend()

    def get_inv_opcode(self, insn):
        return '{} {}'.format(self.ins_map.get(insn.mnemonic), insn.op_str)

    def find_sublist_addr(self, lst, sublist):
        sub_len = len(sublist)
        indices = []
        for index in (i for i, e in enumerate(lst) if e == sublist[0]):
            if lst[index:index + sub_len] == sublist:
                indices.append(index)
        return indices

    def find_sublist(self, lst, sublist):
        len_sublist = len(sublist)
        for i in range(len(lst)):
            if lst[i:i + len_sublist] == sublist:
                return i
        return -1

    def findfuncend(self):
        target = int(hex_cleaner(self.edit_funstar.text()), 16)
        stackOplen = int(hex_cleaner(self.edit_stacklen.text()), 16)

        print("正在计算funcstart %x 所在的函数尾" % (target))
        print("stackOplen = %x" % (stackOplen))
        
        encodings = [0xC0, 0x03, 0x5F, 0xD6]  # ret 的指令编码
        for i in md.disasm(ida_bytes.get_bytes(target, stackOplen * 4), 0):
            if i.op_str.find('!') == -1:
                print(f"x1{i.mnemonic} {i.op_str}")
                encodings = ks_disasm(self.get_inv_opcode(i)) + encodings
            else:

                s_new = i.op_str.replace("]!", "")
                s_new = s_new.replace(", #-", "],")
                dis_str = '{} {}'.format(self.ins_map.get(i.mnemonic), s_new)
                print(dis_str)
                encodings = ks_disasm(dis_str) + encodings

        opcodelist = list(ida_bytes.get_bytes(target, 0x8000))
        index = self.find_sublist(opcodelist, encodings)
        func_end = target + index  # 函数结尾的地址
        print("func end addr : ", hex(func_end))

    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(242, 117)
        self.edit_funstar = QtWidgets.QLineEdit(Dialog)
        self.edit_funstar.setGeometry(QtCore.QRect(100, 30, 113, 20))
        self.edit_funstar.setObjectName("edit_funstar")
        self.button_sure = QtWidgets.QPushButton(Dialog)
        self.button_sure.setGeometry(QtCore.QRect(90, 90, 75, 23))
        self.button_sure.setObjectName("button_sure")
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(30, 30, 61, 16))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(10, 60, 81, 16))
        self.label_2.setObjectName("label_2")
        self.edit_stacklen = QtWidgets.QLineEdit(Dialog)
        self.edit_stacklen.setGeometry(QtCore.QRect(100, 60, 113, 20))
        self.edit_stacklen.setObjectName("edit_stacklen")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "arm64函数尾"))
        self.button_sure.setText(_translate("Dialog", "确定"))
        self.label.setText(_translate("Dialog", "起始地址"))
        self.label_2.setText(_translate("Dialog", "入栈操作长度"))


def PLUGIN_ENTRY():
    return MyIDAPlugin()


class MyIDAPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = "一个帮助逆向分析的插件"
    help = "This is help"
    wanted_name = "二进制科学"
    wanted_hotkey = "Ctrl-F8"

    def init(self):
        idaapi.msg("二进制科学ida插件加载成功\n")

        self.hexrays_inited = False
        self.registered_actions = []
        self.registered_hx_actions = []

        global ARCH
        global BITS
        ARCH = idaapi.ph_get_id()
        info = idaapi.get_inf_structure()
        if info.is_64bit():
            BITS = 64
        elif info.is_32bit():
            BITS = 32
        else:
            BITS = 16
            # Register menu actions
        menu_actions = (
            idaapi.action_desc_t(ACTION_FUNEND, "find fun end", menu_action_handler_t(ACTION_FUNEND), None,
                                 None, 80),
            idaapi.action_desc_t(ACTION_FILLNOP, "Fill with NOPs", menu_action_handler_t(ACTION_FILLNOP), None, None,
                                 9),
        )

        for action in menu_actions:
            idaapi.register_action(action)
            self.registered_actions.append(action.name)

        # Add ui hook
        self.ui_hook = UI_Hook()
        self.ui_hook.hook()
        # 件已成功加载,并且将在 IDA Pro 运行期间保持活动状态。如果一个插件的 init 方法返回此值,那么插件的 run 方法可以在任何时候被调用。
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        idaapi.msg("My IDA Plugin is running\n")
        form = MyForm()
        form.exec_()

    def term(self):
        idaapi.msg("My IDA Plugin is being unloaded\n")
        if hasattr(self, "ui_hook"):
            self.ui_hook.unhook()

        # Unregister actions
        for action in self.registered_actions:
            idaapi.unregister_action(action)
 

D:\soft\IDA_Pro_Green_jb51\IDA_Pro_7.7\python38>python.exe -m pip install capstone

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值