测试了一下上面链接的代码
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