Files
idapython-src/Scripts/FindInstructions.py
2025-03-13 16:17:53 +01:00

162 lines
5.0 KiB
Python

"""
A script to help you find desired opcodes/instructions in a database
The script accepts opcodes and assembly statements (which will be assembled) separated by semicolon
The general syntax is:
find(asm or opcodes, x=Bool, asm_where=ea)
* Example:
find("asm_statement1;asm_statement2;de ea dc 0d e0;asm_statement3;xx yy zz;...")
* To filter-out non-executable segments pass x=True
find("jmp dword ptr [esp]", x=True)
* To specify in which context the instructions should be assembled, pass asm_where=ea:
find("jmp dword ptr [esp]", asm_where=here())
Copyright (c) 1990-2025 Hex-Rays
ALL RIGHTS RESERVED.
"""
from __future__ import print_function
import re
import sys
import ida_idaapi
import ida_lines
import ida_segment
import ida_kernwin
import ida_bytes
import ida_ua
import ida_ida
import ida_funcs
import idautils
# -----------------------------------------------------------------------
def FindInstructions(instr, asm_where=None):
"""
Finds instructions/opcodes
@return: Returns a tuple(True, [ ea, ... ]) or a tuple(False, "error message")
"""
if not asm_where:
# get first segment
seg = ida_segment.get_first_seg()
asm_where = seg.start_ea if seg else ida_idaapi.BADADDR
if asm_where == ida_idaapi.BADADDR:
return (False, "No segments defined")
# regular expression to distinguish between opcodes and instructions
re_opcode = re.compile('^[0-9a-f]{2} *', re.I)
# split lines
lines = instr.split(";")
# all the assembled buffers (for each instruction)
bufs = []
for line in lines:
if re_opcode.match(line):
# convert from hex string to a character list then join the list to form one string
buf = bytes(bytearray([int(x, 16) for x in line.split()]))
else:
# assemble the instruction
ret, buf = idautils.Assemble(asm_where, line)
if not ret:
return (False, "Failed to assemble:"+line)
# add the assembled buffer
bufs.append(buf)
# join the buffer into one string
buf = b''.join(bufs)
# take total assembled instructions length
tlen = len(buf)
# convert from binary string to space separated hex string
bin_str = ' '.join(["%02X" % (ord(x) if sys.version_info.major < 3 else x) for x in buf])
# find all binary strings
print("Searching for: [%s]" % bin_str)
ea = ida_ida.inf_get_min_ea()
ret = []
while True:
ea = ida_bytes.find_bytes(bin_str, ea, radix=16)
if ea == ida_idaapi.BADADDR:
break
ret.append(ea)
ida_kernwin.msg(".")
ea += tlen
if not ret:
return (False, "Could not match [%s]" % bin_str)
ida_kernwin.msg("\n")
return (True, ret)
# -----------------------------------------------------------------------
# Chooser class
class SearchResultChoose(ida_kernwin.Choose):
def __init__(self, title, items):
ida_kernwin.Choose.__init__(
self,
title,
[["Address", 30], ["Function (or segment)", 25], ["Instruction", 20]],
width=250)
self.items = items
def OnGetSize(self):
return len(self.items)
def OnGetLine(self, n):
i = self.items[n]
ea = i.ea
return [
hex(i.ea),
i.funcname_or_segname,
i.text
]
def OnSelectLine(self, n):
ida_kernwin.jumpto(self.items[n].ea)
# -----------------------------------------------------------------------
# class to represent the results
class SearchResult:
def __init__(self, ea):
self.ea = ea
self.funcname_or_segname = ""
self.text = ""
if not ida_bytes.is_code(ida_bytes.get_flags(ea)):
ida_ua.create_insn(ea)
# text
t = ida_lines.generate_disasm_line(ea)
if t:
self.text = ida_lines.tag_remove(t)
# funcname_or_segname
n = ida_funcs.get_func_name(ea) \
or ida_segment.get_segm_name(ida_segment.getseg(ea))
if n:
self.funcname_or_segname = n
# -----------------------------------------------------------------------
def find(s=None, x=False, asm_where=None):
b, ret = FindInstructions(s, asm_where)
if b:
# executable segs only?
if x:
results = []
for ea in ret:
seg = ida_segment.getseg(ea)
if (not seg) or (seg.perm & ida_segment.SEGPERM_EXEC) == 0:
continue
results.append(SearchResult(ea))
else:
results = [SearchResult(ea) for ea in ret]
title = "Search result for: [%s]" % s
ida_kernwin.close_chooser(title)
c = SearchResultChoose(title, results)
c.Show(True)
else:
print(ret)
# -----------------------------------------------------------------------
print("Please use find('asm_stmt1;xx yy;...', x=Bool,asm_where=ea) to search for instructions or opcodes. Specify x=true to filter out non-executable segments")