# plugins

repository: medigateio/ida_medigate
file: plugins/ida-referee/referee.py
description: Medigate plugin for c++ reverse engineering and other utils
wanted_name: Referee
comment: Adds struct xref info from decompilation
created_at: 2020-09-30 07:51:22+00:00
pushed_at: 2023-05-25 11:52:11+00:00
url:
https://github.com/medigateio/ida_medigate/blob/63c7318a272640c306bb88912027fef987261f15/plugins/ida-referee/referee.py

content:
# -*- coding: utf-8 -*-
"""
Referee creates struct xrefs for decompiled functions
"""
import logging
import traceback

import idaapi

logging.basicConfig(level=logging.WARN)
log = logging.getLogger("referee")


NETNODE_NAME = '$ referee-xrefs'
NETNODE_TAG = 'X'


def is_assn(t):
    return (
        t == idaapi.cot_asg or
        t == idaapi.cot_asgbor or
        t == idaapi.cot_asgxor or
        t == idaapi.cot_asgband or
        t == idaapi.cot_asgsub or
        t == idaapi.cot_asgmul or
        t == idaapi.cot_asgsshr or
        t == idaapi.cot_asgushr or
        t == idaapi.cot_asgsdiv or
        t == idaapi.cot_asgudiv or
        t == idaapi.cot_asgsmod or
        t == idaapi.cot_asgumod)


def is_incdec(t):
    return (
        t == idaapi.cot_postinc or  # = 53,  ///< x++
        t == idaapi.cot_postdec or  # = 54,  ///< x--
        t == idaapi.cot_preinc  or  # = 55,  ///< ++x
        t == idaapi.cot_predec)     # = 56,  ///< --x


def add_struct_xrefs(cfunc):
    class xref_adder_t(idaapi.ctree_visitor_t):
        def __init__(self, cfunc):
            idaapi.ctree_visitor_t.__init__(self, idaapi.CV_PARENTS)
            self.cfunc = cfunc
            self.node = idaapi.netnode()
            self.clear_struct_xrefs()
            self.xrefs = {}

        def load(self):
            try:
                data = self.node.getblob_ea(self.cfunc.entry_ea, NETNODE_TAG)
                if data:
                    xrefs = eval(data.replace(b"L", b""))
                    log.debug('Loaded {} xrefs'.format(len(xrefs)))
                    return xrefs
            except:
                log.error('Failed to load xrefs from netnode')
                traceback.print_exc()
            return {}

        def save(self):
            try:
                self.node.setblob_ea(repr(self.xrefs).encode(),
                                     self.cfunc.entry_ea,
                                     NETNODE_TAG)
            except:
                log.error('Failed to save xrefs to netnode')
                traceback.print_exc()

        def clear_struct_xrefs(self):
            if not self.node.create(NETNODE_NAME):
                xrefs = self.load()
                for (ea, struct_id, member_id) in xrefs.keys():
                    if member_id is None:
                        idaapi.del_dref(ea, struct_id)
                    else:
                        idaapi.del_dref(ea, member_id)
                self.xrefs = {}
                self.save()
                log.debug('Cleared {} xrefs'.format(len(xrefs)))

        def find_addr(self, e):
            if e.ea != idaapi.BADADDR:
                ea = e.ea
            else:
                while True:
                    e = self.cfunc.body.find_parent_of(e)
                    if e is None:
                        ea = self.cfunc.entry_ea
                        break
                    if e.ea != idaapi.BADADDR:
                        ea = e.ea
                        break
            return ea

        def add_dref(self, ea, struct_id, flags, member_id=None):
            if ((ea, struct_id, member_id) not in self.xrefs or
                    flags < self.xrefs[(ea, struct_id, member_id)]):
                self.xrefs[(ea, struct_id, member_id)] = flags
                strname = idaapi.get_struc_name(struct_id)
                if member_id is None:
                    idaapi.add_dref(ea, struct_id, flags)
                    log.debug((" 0x{:X} \t"
                               "struct {} \t"
                               "{}").format(
                               ea, strname, flags_to_str(flags)))
                else:
                    idaapi.add_dref(ea, member_id, flags)
                    log.debug((" 0x{:X} \t"
                               "member {}.{} \t"
                               "{}").format(
                               ea, strname,
                               idaapi.get_member_name(member_id),
                               flags_to_str(flags)))
            self.save()

        def visit_expr(self, e):
            dr = idaapi.dr_R | idaapi.XREF_USER
            ea = self.find_addr(e)

            # We wish to know what context a struct usage occurs in
            # so we can determine what kind of xref to create. Unfortunately,
            # a post-order traversal makes this difficult.

            # For assignments, we visit the left, instead
            # Note that immediate lvalues will be visited twice,
            # and will be eronneously marked with a read dref.
            # However, it is safer to overapproximate than underapproximate
            if is_assn(e.op) or is_incdec(e.op):
                e = e.x
                dr = idaapi.dr_W | idaapi.XREF_USER

            # &x
            if e.op == idaapi.cot_ref:
                e = e.x
                dr = idaapi.dr_O | idaapi.XREF_USER

            # x.m, x->m
            if (e.op == idaapi.cot_memref or e.op == idaapi.cot_memptr):
                moff = e.m

                # The only way I could figure out how
                # to get the structure/member associated with its use
                typ = e.x.type

                if e.op == idaapi.cot_memptr:
                    typ.remove_ptr_or_array()

                strname = typ.dstr()
                if strname.startswith("struct "):
                    strname = strname[len("struct "):]

                stid = idaapi.get_struc_id(strname)
                struc = idaapi.get_struc(stid)
                mem = idaapi.get_member(struc, moff)

                if struc is not None:
                    self.add_dref(ea, stid, dr)
                    if mem is not None:
                        self.add_dref(ea, stid, dr, mem.id)

                else:
                    log.error(("failure from 0x{:X} "
                               "on struct {} (id: 0x{:X}) {}").format(
                               ea, strname, stid, flags_to_str(dr)))

            elif idaapi.is_lvalue(e.op) and e.type.is_struct():
                strname = e.type.dstr()
                if strname.startswith("struct "):
                    strname = strname[len("struct "):]

                stid = idaapi.get_struc_id(strname)
                struc = idaapi.get_struc(stid)

                if struc is not None:
                    self.add_dref(ea, stid, dr)

            return 0

    adder = xref_adder_t(cfunc)
    adder.apply_to_exprs(cfunc.body, None)


def callback(*args):
    if args[0] == idaapi.hxe_maturity:
        cfunc = args[1]
        mat = args[2]
        if mat == idaapi.CMAT_FINAL:
            log.debug("analyzing function at 0x{:X}".format(
                cfunc.entry_ea))
            add_struct_xrefs(cfunc)
    return 0


class Referee(idaapi.plugin_t):
    flags = idaapi.PLUGIN_HIDE
    comment = "Adds struct xref info from decompilation"
    help = ""

    wanted_name = "Referee"
    wanted_hotkey = ""

    def __init__(self):
        self.inited = False

    def init(self):
        if not idaapi.init_hexrays_plugin():
            return idaapi.PLUGIN_SKIP

        idaapi.install_hexrays_callback(callback)
        log.info(("Hex-Rays version {} has been detected; "
                  "{} is ready to use").format(
                  idaapi.get_hexrays_version(), self.wanted_name))
        self.inited = True
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        # never called
        pass

    def term(self):
        if self.inited:
            idaapi.remove_hexrays_callback(callback)
            idaapi.term_hexrays_plugin()


def PLUGIN_ENTRY():
    return Referee()


def flags_to_str(num):
    match = []
    if num & idaapi.dr_R == idaapi.dr_R:
        match.append('dr_R')
        num ^= idaapi.dr_R
    if num & idaapi.dr_O == idaapi.dr_O:
        match.append('dr_O')
        num ^= idaapi.dr_O
    if num & idaapi.dr_W == idaapi.dr_W:
        match.append('dr_W')
        num ^= idaapi.dr_W
    if num & idaapi.dr_I == idaapi.dr_I:
        match.append('dr_I')
        num ^= idaapi.dr_I
    if num & idaapi.dr_T == idaapi.dr_T:
        match.append('dr_T')
        num ^= idaapi.dr_T
    if num & idaapi.XREF_USER == idaapi.XREF_USER:
        match.append('XREF_USER')
        num ^= idaapi.XREF_USER
    if num & idaapi.XREF_DATA == idaapi.XREF_DATA:
        match.append('XREF_DATA')
        num ^= idaapi.XREF_DATA
    res = ' | '.join(match)
    if num:
        res += ' unknown: 0x{:X}'.format(num)
    return res


def clear_output_window():
    idaapi.process_ui_action('msglist:Clear')


repository: Comsecuris/gdbida
file: ida_gdb_bridge.py
description: gdbida - a visual bridge between a GDB session and IDA Pro's disassembler
wanted_name: IDA debug bridge
created_at: 2017-03-27 08:49:42+00:00
pushed_at: 2018-04-23 12:45:55+00:00
url:
https://github.com/Comsecuris/gdbida/blob/c31781681871f6b1fbfe0c85121f846ddafe9124/ida_gdb_bridge.py

content:
import idautils
import ida_kernwin
import signal
import idc
import idaapi

import socket
import struct
import threading
import time
import Queue

TRACE = False
DEBUG = False
COLOR_CUR = 0x68ff90

# make sure our operations time out so we can actually quit IDA
DEFAULT_TIMEOUT = 3
socket.setdefaulttimeout(DEFAULT_TIMEOUT)

def dprint(s):
	if DEBUG == True:
		print('IDAbridge: ' + s)

# use idaapi.execute_ui_requests to ensure that SetColor and Jump
# are executed in the main thread to ensure thread safety. if we
# ignore that, we will run into btree and other errors.
# thanks for hex-rays for helping with this!
class color_req_t(object):
	def __init__(self, ea, color):
		self.ea = ea
		self.color = color

	def __call__(self):
		idc.SetColor(self.ea, idc.CIC_ITEM, self.color)
		
		return False # Don't reschedule

def safe_setcolor(ea, color):
	ida_kernwin.execute_ui_requests((color_req_t(ea, color),))

class jump_req_t(object):
	def __init__(self, ea):
		self.ea = ea

	def __call__(self):
		idc.Jump(self.ea)
		
		return False # Don't reschedule

def safe_jump(ea):
	ida_kernwin.execute_ui_requests((jump_req_t(ea),))

# TODO: in case of any exceptions it may be nice to have
# a list of all locations where color was changed so that on
# exiting the plugin, we could restore these.
class ColorThread(threading.Thread):
	def __init__(self, ea = -1):
		#print "ColorThread()"
		threading.Thread.__init__(self)
		self.c_queue = Queue.Queue()
		self.ea = ea
		self.running = False
	
	def join(self, timeout = None):
		self.running = False
		threading.Thread.join(self, timeout)

	def run(self):
		self.running = True

		while self.running == True:
			event_ea = self.ea
			try:
				ea = self.c_queue.get(block=True, timeout=DEFAULT_TIMEOUT)
			except:
				continue

			try:
				event_ea = int(struct.unpack("<Q", ea)[0])
			except Exception as e:
				dprint("error pc conversion: %s" %(str(e)))
				continue

			dprint("current position: %x" %(event_ea))
			try:
				safe_jump(event_ea)
			except:
				continue

			# TODO: do some sanity checking on received EAs
			# e.g. make sure that the address is within the image space
			if TRACE == False:
				safe_setcolor(self.ea, 0xffffff)

			safe_setcolor(event_ea, COLOR_CUR)
			self.ea = event_ea


class BridgeThread(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)
		self.server = None
		self.running = False
		self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.c_thread = ColorThread()

	def join(self, timeout = None):
		self.running = False
		self.s.close()
		threading.Thread.join(self, timeout)

	def bind(self):
		try:
			self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			self.s.bind(self.server)
		except:
			dprint("bind() failed")
			self.running = False
			return False

		self.running = True
		return True

	def run(self):
		self.s.listen(1)
		dprint("accepting connections")
		if not self.c_thread.running:
			self.c_thread.start()

		# TODO: make it possible to keep a connection or switch to UDP
		while self.running == True:
			try:
				conn, addr = self.s.accept()
			except socket.timeout:
				continue

			try:
				read_ea = conn.recv(8)
			except Exception as e:
				dprint("closing client/%s connection: %s" % (addr, str(e)))
				conn.close()
				continue

			conn.close()
			self.c_thread.c_queue.put(read_ea)

class ida_gdb_debug_bridge_plugin(idaapi.plugin_t):
	flags = idaapi.PLUGIN_OK
	comment = ""
	help = ""
	wanted_name = "IDA debug bridge"
	wanted_hotkey = "Alt-f"

	def init(self):
		self.bridge_thread = BridgeThread()
		return idaapi.PLUGIN_OK
	
	def run(self, arg):
		if self.bridge_thread.running == True:
			dprint("server already running: %s:%s\n" % (self.bridge_thread.server[0], self.bridge_thread.server[1]))
			return

		ask_port = idc.AskLong(2305, "Enter port number for incoming events")
		if ask_port < 1 or ask_port <= 1024:
			dprint('invalid port number')
			return

		ask_addr = idc.AskStr('0.0.0.0', "Enter address to bind to")

		self.bridge_thread.server = (ask_addr, ask_port)
		if self.bridge_thread.running == False:
			self.bridge_thread.bind()

		if self.bridge_thread.running == False:
			dprint("there was an issue starting the server")
		else:
			self.bridge_thread.start()

		return

	def term(self):
		if self.bridge_thread.c_thread.ea != -1:
			safe_setcolor(self.bridge_thread.c_thread.ea, 0xffffff)
		self.bridge_thread.running = False
		self.bridge_thread.c_thread.running = False
		self.bridge_thread.s.close()
		pass


def PLUGIN_ENTRY():
	return ida_gdb_debug_bridge_plugin()



repository: rivitna/emu_ida
file: emu_ida.py
url: https://github.com/rivitna/emu_ida/blob/44a9d2315652bc23c451baa890cab2a1dd9ba04e/emu_ida.py
comment: IDA code emulator
created_at: 2022-02-07 18:41:31+00:00
pushed_at: 2023-04-08 07:46:55+00:00
content:
"""
Code emulator plugin for IDA Pro

https://github.com/rivitna/emu_ida

The plugin is designed for simple data decryption and getting stack strings.

Special thanks @herrcore for IDAPython plugin template.
"""

import io
import os
import errno
import binascii
from unicorn import *
from unicorn.x86_const import *
import idautils
import idaapi
import ida_kernwin
import ida_ida
import ida_nalt
import ida_segment
import ida_bytes
import ida_ua
import ida_allins
import ida_lines


#****************************************************************************
__AUTHOR__ = '@rivitna'
#****************************************************************************
PLUGIN_NAME = 'Emu'
VERSION = '0.0.7'
PLUGIN_HOTKEY = 'Alt+E'
#****************************************************************************


# Debug
DEBUG = False

# Dump data
DUMP_DATA = True
DUMP_DIR_PATH = './emu_dumps/'


# Comments
COMMENT_DATA_MAX_LEN = 256


# Stack
STACK_SIZE = 0x100000
ESP_INIT_POS = (STACK_SIZE // 2) & ~0xFF
EBP_INIT_POS = (3 * STACK_SIZE // 4) & ~0xFF

# Dummy block (block for zero addresses)
DUMMY_BLOCK_SIZE = 0x100000


# Skipped instructions that are written to memory
SKIP_WRITE_MEM_INSTRUCTIONS = frozenset([
    ida_allins.NN_call,
    ida_allins.NN_callfi,
    ida_allins.NN_callni,
    ida_allins.NN_enterw,
    ida_allins.NN_enter,
    ida_allins.NN_enterd,
    ida_allins.NN_enterq,
    ida_allins.NN_pushaw,
    ida_allins.NN_pusha,
    ida_allins.NN_pushad,
    ida_allins.NN_pushaq,
    ida_allins.NN_pushfw,
    ida_allins.NN_pushf,
    ida_allins.NN_pushfd,
    ida_allins.NN_pushfq,
    ])


class EmuError(Exception):
    """Plugin Error class."""
    pass


#****************************************************************************
# Global settings
#****************************************************************************

def global_settings():

    print('Emu: Configuration.')


#****************************************************************************
# Code emulate
#****************************************************************************

def data_to_text(data):
    """Convert data to text."""

    if (len(data) == 0):
        return ''

    prefix = ''

    data_len = len(data)

    try:
        if (data_len < 2) or (data_len & 1) or (data[1] != 0):
            s = data.decode()
        else:
            s = data.decode('UTF-16-LE')
            prefix = 'L'

        s = s.strip('\0')
        s = s.encode('unicode_escape').decode().replace('\"', '\\"')
        text = prefix + '\"' + s + '\"'

    except UnicodeDecodeError:
        text = ''

    finally:
        ln = min(COMMENT_DATA_MAX_LEN, data_len)
        for block in (data[i : i + 16] for i in range(0, ln, 16)):
            if (text != ''):
                text += '\n'
            text += binascii.hexlify(block, ' ', 1).decode()
        if ln < data_len:
            text += '\n... (%d bytes)' % (data_len - ln)

    return text


def trace_log(text):
    """Trace log."""
    with io.open('emutrace.log', 'a') as f:
        f.write(text + '\n')


def mkdirs(dir):
    """Make directories."""
    try:
        os.makedirs(dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise


def dump_data(code_start_ea, code_end_ea, index, data_ea, data_size, data):
    """Dump data."""
    dump_dir_path = (DUMP_DIR_PATH +
                     '%08X_%08X/' % (code_start_ea, code_end_ea))
    mkdirs(dump_dir_path)
    dump_path = (dump_dir_path +
                 '%02d_%08X_%08x.bin' % (index, data_ea, data_size))
    with io.open(dump_path, 'wb') as f:
        f.write(data)


def hook_mem_write(uc, access, address, size, value, data_entries):
    """Callback for memory write."""

    is_64bit = (uc._mode == UC_MODE_64)

    eip = uc.reg_read(UC_X86_REG_RIP if is_64bit else UC_X86_REG_EIP)

    do_skip = False

    # Decode instruction
    insn = ida_ua.insn_t()
    inslen = ida_ua.decode_insn(insn, eip)
    if (inslen > 0):
        # Skip instruction?
        if (insn.itype in SKIP_WRITE_MEM_INSTRUCTIONS):
            do_skip = True

    if DEBUG:
        # Debug: trace log
        disasm_text = ida_lines.generate_disasm_line(eip, 0)
        if disasm_text:
            disasm_text = ida_lines.tag_remove(disasm_text)
        addr_fmt_str = '%016X' if is_64bit else '%08X'
        val_fmt_str = '%0' + str(2 * size) + 'X'
        log_fmt_str = addr_fmt_str + '\t%s\t' + \
                      val_fmt_str + ' (%d) -> ' + addr_fmt_str
        log_str = log_fmt_str % (eip, disasm_text, value, size, address)
        log_str += '\t' + ('Skipped' if do_skip else 'Processed')
        trace_log(log_str)

    if do_skip:
        return

    start = address
    end = start + size

    for i, data_entry in reversed(list(enumerate(data_entries))):

        data_start = data_entry[0]
        data_end = data_start + data_entry[1]

        if ((data_start <= start <= data_end) or
            (data_start <= end <= data_end)):
            start = min(data_start, start)
            end = max(data_end, end)
            new_entry = (start, end - start)
            if (i < len(data_entries) - 1):
                del data_entries[i]
                data_entries.append(new_entry)
            else:
                data_entries[i] = new_entry
            break

    else:
        data_entries.append((address, size))


def code_emulate():
    """Emulate highlighted code."""

    # Get highlighted range
    selection, start_ea, end_ea = ida_kernwin.read_range_selection(None)

    if not selection:
        ea = ida_kernwin.get_screen_ea()
        start_ea = idaapi.get_item_head(ea)
        end_ea = idaapi.get_item_end(ea)

    print('Emu: Start code emulation...')
    print('Emu: Start address: %08X' % start_ea)
    print('Emu: End address: %08X' % end_ea)

    image_base = ida_nalt.get_imagebase()
    mode = UC_MODE_64 if ida_ida.inf_is_64bit() else UC_MODE_32
    image_size = ida_ida.inf_get_max_ea() - image_base
    image_size = (image_size + 0xFFFF) & ~0xFFFF

    dummy_size = min(DUMMY_BLOCK_SIZE, image_base)
    stack_addr = (dummy_size + 0xFFFF) & ~0xFFFF
    if (stack_addr + STACK_SIZE > image_base):
        stack_addr = (image_base + image_size + 0xFFFF) & ~0xFFFF

    try:

        # Initialize emulator
        mu = Uc(UC_ARCH_X86, mode)

        # Map memory for this emulation
        # Dummy block
        dummy_size = min(DUMMY_BLOCK_SIZE, image_base)
        if dummy_size != 0:
            mu.mem_map(0, dummy_size)
        # Image
        mu.mem_map(image_base, image_size)
        # Stack
        mu.mem_map(stack_addr, STACK_SIZE)

        for n in range(ida_segment.get_segm_qty()):

            seg = ida_segment.getnseg(n)
            if seg and (seg.end_ea > seg.start_ea):
                seg_data = ida_bytes.get_bytes(seg.start_ea,
                                               seg.end_ea - seg.start_ea)

                # Write section data to memory
                mu.mem_write(seg.start_ea, seg_data)

        # Initialize emulator
        if mode == UC_MODE_64:
            esp_id = UC_X86_REG_RSP
            ebp_id = UC_X86_REG_RBP
        else:
            esp_id = UC_X86_REG_ESP
            ebp_id = UC_X86_REG_EBP
        mu.reg_write(esp_id, stack_addr + ESP_INIT_POS)
        mu.reg_write(ebp_id, stack_addr + EBP_INIT_POS)

        data_entries = []

        mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write, data_entries)

        # Emulate machine code in infinite time
        mu.emu_start(start_ea, end_ea)

        if (len(data_entries) >= 1):

            # Select result data entry
            if (len(data_entries) == 1):

                res_data_entry = data_entries[0]

            else:

                res_data_entry = None
                stack_data_entry = None

                for data_entry in data_entries:
                    addr = data_entry[0]

                    # Stack?
                    if (stack_addr <= addr < stack_addr + STACK_SIZE):
                        if ((stack_data_entry is None) or
                            (addr >= stack_data_entry[0])):
                            stack_data_entry = data_entry
                            res_data_entry = data_entry

                    # Image?
                    elif (image_base <= addr < image_base + image_size):
                        res_data_entry = data_entry

                    # Dummy block?
                    elif (addr < DUMMY_BLOCK_SIZE):
                        res_data_entry = data_entry

                if res_data_entry is None:
                    res_data_entry = data_entries[-1]

            res_data = mu.mem_read(*res_data_entry)
            ida_bytes.set_cmt(start_ea, data_to_text(res_data), 1)

            if DUMP_DATA:
                # Save data blocks to files
                for i, data_entry in enumerate(data_entries):
                    # Dump data
                    dump_data(start_ea, end_ea, i,
                              data_entry[0], data_entry[1],
                              mu.mem_read(*data_entry))

        print('Emu: Code emulation done.')

    except UcError as e:

        print('Emu Error: %s' % e)


#****************************************************************************
# Plugin
#****************************************************************************
class EmuPlugin(idaapi.plugin_t):
    """
    IDA Plugin for code emulation.
    """

    comment = 'IDA code emulator'
    help = ''
    wanted_name = PLUGIN_NAME
    # We only want a hotkey for the actual hash lookup
    wanted_hotkey = ''
    flags = idaapi.PLUGIN_KEEP

    def init(self):
        """
        This is called by IDA when it is loading the plugin.
        """

        global g_initialized

        # Check if already initialized 
        if not g_initialized:
            g_initialized = True

            # Print header
            print('IDA code emulator v%s by rivitna.' % VERSION)

            # Initialize the menu actions our plugin will inject
            self._init_action_code_emulate()

            self._init_hooks()
            return idaapi.PLUGIN_KEEP


    def run(self, arg):
        """
        This is called by IDA when the plugin is run from the plugins menu.
        """
        global_settings()


    def term(self):
        """
        This is called by IDA when it is unloading the plugin.
        """
        pass


    #
    # IDA Actions
    #
    ACTION_CODE_EMULATE  = 'emu:code_emulate'

    def _init_action_code_emulate(self):
        """
        Register the code emulation action with IDA.
        """
        action_desc = idaapi.action_desc_t(self.ACTION_CODE_EMULATE,
                                           'Code emulate',
                                           EmuActionHandler(code_emulate),
                                           PLUGIN_HOTKEY,
                                           'Code emulation')
        # Register the action with IDA
        res = idaapi.register_action(action_desc)
        assert res, 'Action registration failed'


    def _init_hooks(self):
        """
        Install plugin hooks into IDA.
        """
        self._hooks = EmuHooks()
        self._hooks.hook()


#****************************************************************************
# Plugin Hooks
#****************************************************************************
class EmuHooks(idaapi.UI_Hooks):

    def finish_populating_widget_popup(self, widget, popup):
        """
        A right click menu is about to be shown. (IDA 7)
        """
        inject_actions(widget, popup, idaapi.get_widget_type(widget))
        return 0


#****************************************************************************
# Prefix Wrappers
#****************************************************************************
def inject_actions(form, popup, form_type):
    """
    Inject actions to popup menu(s) based on context.
    """

    # Disassembly window
    if (form_type == idaapi.BWN_DISASMS):

        # Insert the action entry into the menu

        idaapi.attach_action_to_popup(form,
                                      popup,
                                      EmuPlugin.ACTION_CODE_EMULATE,
                                      'Code emulate',
                                      idaapi.SETMENU_APP)

    return 0


#****************************************************************************
# Plugin Action Handler
#****************************************************************************
class EmuActionHandler(idaapi.action_handler_t):
    """
    A basic Context Menu class to utilize IDA's action handlers.
    """

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

    def activate(self, ctx):
        """
        Execute the embedded action_function when this context menu is invoked.
        """
        self.action_function()
        return 1

    def update(self, ctx):
        """
        Ensure the context menu is always available in IDA.
        """
        return idaapi.AST_ENABLE_ALWAYS


#****************************************************************************
# Plugin Registration
#****************************************************************************

# Global flag to ensure plugin is only initialized once
g_initialized = False

# Register IDA plugin
def PLUGIN_ENTRY():
    return EmuPlugin()


repository: tbxy09/mdnav
file: ftplugin/markdown/mdsug.py
description: Vim plugin for markdown markup suggestion
created_at: 2018-09-04 06:04:19+00:00
pushed_at: 2018-09-10 09:05:43+00:00
url:
https://github.com/tbxy09/mdnav/blob/4e34a1c09fae873d04c75e7c7d8d77bd12636c40/ftplugin/markdown/mdsug.py

content:
from __future__ import print_function

import collections
import json
import os.path
import re
import sys
import subprocess
import webbrowser

try:
    from urllib.parse import urlparse
except ImportError:
    from urlparse import urlparse


reference_definition_pattern = re.compile(r'''
    ^
        \[[^\]]*\]:             # reference def at start of line
        (?P<link>.*)            # interpret everything else as link text
    $
''', re.VERBOSE)

link_pattern = re.compile(r'''
    ^
    (?P<link>
        \[                      # start of link text
            (?P<text>[^\]]*)    # link text
        \]                      # end of link text
        (?:
            \(                  # start of target
                (?P<direct>
                    [^\)]*
                )
            \)                  # collect
            |
            \[
                (?P<indirect>
                    [^\]]*
                )
            \]
        )
    )
    .*                  # any non matching characters
    $
''', re.VERBOSE)

class FakeLogger(object):
    def __init__(self, active=False):
        self.active = active

    def info(self, fmt, *args):
        if not self.active:
            return

        print(fmt % args)


_logger = FakeLogger()
def Vim_window():
    vim.command('execute get(g:, "peekaboo_window", s:default_window)')

def plugin_entry_point():
    import vim

    if int(vim.eval("exists('g:mdnav#Extensions')")):
        extensions = vim.eval('g:mdnav#Extensions')
        extensions = [ext.strip() for ext in extensions.split(',')]

    else:
        extensions = []

    if int(vim.eval("exists('g:mdnav#DebugMode')")):
        _logger.active = vim.eval('g:mdnav#DebugMode') == 'true'

    row, col = vim.current.window.cursor
    cursor = (row - 1, col)
    lines = vim.current.buffer

    target = parse_link(cursor, lines)

    _logger.info('open %s', target)
    action = open_link(
        target,
        current_file=vim.eval("expand('%:p')"),
        open_in_vim_extensions=extensions,
    )
    action()


def open_link(target, current_file, open_in_vim_extensions=set()):
    """
    :returns: a callable that encapsulates the action to perform
    """
    _logger.info(target)
    # if target is not None:
        # target = target.strip()

    # if not target:
    #     _logger.info('no target')
    #     return NoOp(target)

    # if target.startswith('#'):
    #     return JumpToAnchor(target)

    # if has_scheme(target):
    #     _logger.info('has scheme -> open in browser')
    #     return BrowserOpen(target)

    # if not has_extension(target, open_in_vim_extensions):
    #     _logger.info('has no extension for opening in vim')
    #     return OSOpen(anchor_path(target, current_file))

    # if target.startswith('|filename|'):
        # target = target[len('|filename|'):]

    return VimOpen(anchor_path(target, current_file))


def anchor_path(target, current_file):
    # if os.path.isabs(target):
    #     return target

    # _logger.info('anchor path relative to %s', current_file)
    # return os.path.join(os.path.dirname(current_file), target)
    return target


def has_extension(path, extensions):
    if not extensions:
        return True

    path = parse_path(path)
    _, ext = os.path.splitext(path.path)
    return ext in extensions


def has_scheme(target):
    return bool(urlparse(target).scheme)


class Action(object):
    def __init__(self, target):
        self.target = target

    def __eq__(self, other):
        return type(self) == type(other) and self.target == other.target

    def __repr__(self):
        return '{}({!r})'.format(type(self).__name__, self.target)


class NoOp(Action):
    def __call__(self):
        print('<mdnav: no link>')


class BrowserOpen(Action):
    def __call__(self):
        print('<mdnav: open browser tab>')
        webbrowser.open_new_tab(self.target)


class OSOpen(Action):
    def __call__(self):
        if sys.platform.startswith('linux'):
            call(['xdg-open', self.target])

        elif sys.platform.startswith('darwin'):
            call(['open', self.target])

        else:
            os.startfile(self.target)


class VimOpen(Action):
    def __call__(self):
        import vim

        # target = parse_path(self.target)
        target = self.target

        # TODO: make space handling more robust?
        # vim.command('e {}'.format(path.path.replace(' ', '\\ ')))
        # vim.command('execute get(g:, "peekaboo_window","vertical botright 30new")')
        mylist=[
        '# {}'.format(target),
        '## {}'.format(target),
        '### {}'.format(target),
        '#### {}'.format(target),
        '##### {}'.format(target),
        '###### {}'.format(target),
        '**{}**'.format(target),
        '``` {} ```'.format(target),
        "'{}'".format(target),
        '*{}*'.format(target),
        '* {}'.format(target),
        '- {}'.format(target),
        '+ {}'.format(target),
        '1. {}'.format(target),
        "[{0}]({1})".format(target.strip('/').split('/')[-1],target),
        '| ------ |:-----:| -----:|',
        ]

        # mylist=[each.replace('DATA',target) for each in mylist]

        vim.command('execute BPop({})'.format(mylist))
        # if path.line is not None:
        #     try:
        #         line = int(path.line)

        #     except:
        #         print('invalid line number')
        #         return

        #     else:
        #         vim.current.window.cursor = (line, 0)

        # if path.anchor is not None:
        #     JumpToAnchor(path.anchor)()


class JumpToAnchor(Action):
    heading_pattern = re.compile(r'^#+(?P<title>.*)$')
    attr_list_pattern = re.compile(r'{:\s+#(?P<id>\S+)\s')

    def __call__(self):
        import vim
        line = self.find_anchor(self.target, vim.current.buffer)

        if line is None:
            return

        vim.current.window.cursor = (line + 1, 0)

    @classmethod
    def find_anchor(cls, target, buffer):
        needle = cls.norm_target(target)

        for (idx, line) in enumerate(buffer):
            m = cls.heading_pattern.match(line)
            if (
                m is not None and
                cls.title_to_anchor(m.group('title')) == needle
            ):
                return idx

            m = cls.attr_list_pattern.search(line)
            if m is not None and needle == m.group('id'):
                return idx

    @staticmethod
    def title_to_anchor(title):
        return '-'.join(fragment.lower() for fragment in title.split())

    @staticmethod
    def norm_target(target):
        if target.startswith('#'):
            target = target[1:]

        return target.lower()


def call(args):
    """If available use vims shell mechanism to work around display issues
    """
    try:
        import vim

    except ImportError:
        subprocess.call(args)

    else:
        args = ['shellescape(' + json.dumps(arg) + ')' for arg in args]
        vim.command('execute "! " . ' + ' . " " . '.join(args))


def parse_path(path):
    """Parse a path with optional line number of anchor into its parts.

    For example::

        parse_path('foo.md:30') == ParsedPath('foo.md', line=30)
        parse_path('foo.md#anchor') == ParsedPath('foo.md', anchor='anchor')

    """
    path, ext = os.path.splitext(path)
    if '#' in ext:
        ext, anchor = ext.rsplit('#', 1)
        return ParsedPath(path + ext, anchor=anchor)

    if ':' in ext:
        ext, line = ext.rsplit(':', 1)
        return ParsedPath(path + ext, line=line)

    return ParsedPath(path + ext)


class ParsedPath(object):
    def __init__(self, path, line=None, anchor=None):
        self.path = path
        self.line = line
        self.anchor = anchor

    def __repr__(self):
        return 'ParsedPath({!r}, line={}, anchor={!r})'.format(self.path, self.line, self.anchor)


def parse_link(cursor, lines):
    row, column = cursor
    line = lines[row]
    return line

    _logger.info('handle line %s (%s, %s)', line, row, column)
    m = reference_definition_pattern.match(line)
    if m is not None:
        return m.group('link').strip()

    link_text, rel_column = select_from_start_of_link(line, column)

    if not link_text:
        _logger.info('could not find link text')
        return None

    m = link_pattern.match(link_text)

    if not m:
        _logger.info('does not match link pattern')
        return None

    if m.end('link') <= rel_column:
        _logger.info('cursor outside link')
        return None

    _logger.info('found match: %s', m.groups())
    assert (m.group('direct') is None) != (m.group('indirect') is None)

    if m.group('direct') is not None:
        _logger.info('found direct link: %s', m.group('direct'))
        return m.group('direct')

    _logger.info('follow indirect link %s', m.group('indirect'))
    indirect_ref = m.group('indirect')
    if not indirect_ref:
        indirect_ref = m.group('text')

    indrect_link_pattern = re.compile(
        r'^\[' + re.escape(indirect_ref) + r'\]:(.*)$'
    )

    for line in lines:
        m = indrect_link_pattern.match(line)

        if m:
            return m.group(1).strip()

    _logger.info('could not match for indirect link')
    return None





def select_from_start_of_link(line, pos):
    """Return the start of the link string and the new cursor
    """
    if pos < len(line) and line[pos] == '[':
        start = pos

    else:
        start = line[:pos].rfind('[')

    # TODO: handle escapes

    if start < 0:
        return None, pos

    # check for indirect links
    if start != 0 and line[start - 1] == ']':
        alt_start = line[:start].rfind('[')
        if alt_start >= 0:
            start = alt_start

    return line[start:], pos - start


if __name__ == "__main__":
    plugin_entry_point()


repository: doomedraven/Tools
file: Reversing/FullColor.py
description: Combination of different utilities, have fun!
wanted_name: FullColor
comment: Set colors :)
created_at: 2018-09-14 13:05:59+00:00
pushed_at: 2024-02-05 13:29:28+00:00
url:
https://github.com/doomedraven/Tools/blob/b6c41453be8e590729d2bc04ff229b08a27e3bb2/Reversing/FullColor.py

content:
# encoding: utf-8
# http://www.hexblog.com/?p=120
# Default IDA Pro Paths:
# MAC	/Applications/IDA\ Pro\ X/idaq.app/Contents/MacOS/plugins/
# Windows	C:\Program Files (x86)\IDA X\plugins

# to make it autoexec on openfile
# add this to plugins.cfg
# ; Other plugins
#FullColor                        FullColor.py       0       0  SILENT

# thanks @JR0driguezB for help :)

from __future__ import print_function
from idautils import Heads
from idc import get_segm_start, get_segm_end, print_insn_mnem, get_screen_ea, print_operand, set_color, CIC_ITEM
import idaapi

#idaapi.auto_wait()
PLUGIN_TEST = 1

class FullColor_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "Set colors :)"
    help = "No help needed"
    wanted_name = "FullColor"
    wanted_hotkey = ""

    def init(self):
        #idaapi.msg("init() called!\n")
        #self.run(0)
        return idaapi.PLUGIN_OK

    def run(self, arg=0):
        print("hell2")
        idaapi.msg("run() called with %d!\n" % arg)
        heads = Heads(get_segm_start(get_screen_ea()), get_segm_end(get_screen_ea()))
        funcCalls = []
        xor = []
        antiVM = []
        for i in heads:
            # Color the Calls off-white
            if print_insn_mnem(i) == "call":
                funcCalls.append(i)
            # Color Anti-VM instructions Red and print their location
            elif print_insn_mnem(i) in ("sidt", "sgdt",  "sldt", "smsw", "str", "in", "cpuid"):
                antiVM.append(i)
            # Color non-zeroing out xor instructions Orange
            elif print_insn_mnem(i) == "xor" and (print_operand(i,0) != print_operand(i,1)):
                xor.append(i)

        print("Number of calls: %d" % (len(funcCalls)))
        for i in funcCalls:
            set_color(i, CIC_ITEM, 0xc7fdff)

        print("Number of potential Anti-VM instructions: %d" % (len(antiVM)))
        for i in antiVM:
            print("Anti-VM potential at %x" % i)
            set_color(i, CIC_ITEM, 0x0000ff)

        print("Number of xor: %d" % (len(xor)))
        for i in xor:
            set_color(i, CIC_ITEM, 0x00a5ff)

    def term(self):
        idaapi.msg("term() called!\n")

def PLUGIN_ENTRY():
    return FullColor_t()

if PLUGIN_TEST:
    # Create form
    f = PLUGIN_ENTRY()
    f.init()
    f.run()
    f.term()


repository: Russinovich/IDA7.5_SDK
file: python/examples/hexrays/vds19.py
description: SDK and IDAPython for IDA Pro 7.5
wanted_name: optimize x|~x
created_at: 2021-09-01 02:18:48+00:00
pushed_at: 2021-07-23 08:19:09+00:00
url:
https://github.com/Russinovich/IDA7.5_SDK/blob/5244a2b5059113e421b4da4ecf771013a6fde2f8/python/examples/hexrays/vds19.py

content:
#
#      Hex-Rays Decompiler project
#      Copyright (c) 2007-2020 by Hex-Rays, support@hex-rays.com
#      ALL RIGHTS RESERVED.
#
#      Sample plugin for Hex-Rays Decompiler.
#      It installs a custom microcode optimization rule:
#        x | ~x => -1
#
#      To see this plugin in action please use be_ornot_be.idb
#

import ida_hexrays
import ida_idaapi

# recognize "x | ~x" and replace by -1
class subinsn_optimizer_t(ida_hexrays.minsn_visitor_t):
    cnt = 0
    def visit_minsn(self):      # for each instruction...
        ins = self.curins       # take a reference to the current instruction

        # THE CORE OF THE PLUGIN IS HERE:
        # check the pattern "x | ~x"
        if ins.opcode == ida_hexrays.m_or and ins.r.is_insn(ida_hexrays.m_bnot) and ins.l == ins.r.d.l:
            if not ins.l.has_side_effects(): # avoid destroying side effects
                # pattern matched, convert to "mov -1, ..."
                ins.opcode = ida_hexrays.m_mov
                ins.l.make_number(-1, ins.r.size)
                ins.r = ida_hexrays.mop_t()
                self.cnt = self.cnt + 1 # number of changes we made
        return 0 # continue traversal

# a custom instruction optimizer, boilerplate code
class sample_optimizer_t(ida_hexrays.optinsn_t):
    def func(self, blk, ins, optflags):
        opt = subinsn_optimizer_t()
        ins.for_all_insns(opt)
        if opt.cnt != 0:                # if we modified microcode,
            blk.mba.verify(True)        # run the verifier
        return opt.cnt                  # report the number of changes

# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "optimize x|~x"
    wanted_hotkey = ""
    comment = ""
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            self.optimizer = sample_optimizer_t()
            self.optimizer.install()
            print("Installed sample optimizer for 'x | ~x'")
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        self.optimizer.remove()
    def run(self, arg):
        if arg == 1:
            return self.optimizer.remove()
        elif arg == 2:
            return self.optimizer.install()

def PLUGIN_ENTRY():
    return my_plugin_t()


repository: JayRE114514/UserComment
file: UserComment.py
description: An IDA pro plugin to display user-added comments in disassembly and pseudocode views.
wanted_name: Hook and display user-added comments
comment: Hook and display user-added comments
created_at: 2023-06-16 08:09:31+00:00
pushed_at: 2023-06-30 07:36:05+00:00
url:
https://github.com/JayRE114514/UserComment/blob/b8ae2608ba3055b40bd56199888a11900740feb7/UserComment.py

content:
from __future__ import print_function
import ida_idaapi
import ida_kernwin
import ida_idp
import ida_netnode
import idc
import ida_bytes
import ida_hexrays
import ida_nalt
import pickle

title = "Comments"

# def show_warning(msg):
    # ida_kernwin.warning(msg)

class UserAddedComments():
    def __init__(self):
        self.netnode = ida_netnode.netnode()
        self.netnode.create("$ UserAddedComments")
        self.imagebase = ida_nalt.get_imagebase()
        self.load_comments()

    def save_comments(self):
        blob = pickle.dumps(self.comments)
        self.netnode.setblob(blob, 0, 'C')

    def load_comments(self):
        blob = self.netnode.getblob(0, 'C')
        if blob is not None:
            self.comments = pickle.loads(blob)
        else:
            self.comments = {}

    def add_comment(self, ea, cmt_type, comment, line_num=None):
        offset = ea - self.imagebase
        key = (offset, cmt_type, line_num)
        if not comment:
            self.comments.pop(key, 0)
        else:
            self.comments[key] = comment
        self.save_comments()


class UIHooks(ida_kernwin.UI_Hooks):
    def __init__(self, cmt_view):
        ida_kernwin.UI_Hooks.__init__(self)
        self.cmt_view = cmt_view

    def current_widget_changed(self, widget, prev_widget):
        if ida_kernwin.get_widget_title(widget) == title:
            self.cmt_view.Refresh()


class PseudoHooks(ida_hexrays.Hexrays_Hooks):
    def __init__(self, usr_cmt):
        ida_hexrays.Hexrays_Hooks.__init__(self)
        self.usr_cmt = usr_cmt

    def cmt_changed(self, cfunc, loc, cmt):
        self.usr_cmt.add_comment(loc.ea, 'pseudocode', cmt)
        return 0


class DisasmHooks(ida_idp.IDB_Hooks):
    def __init__(self, usr_cmt):
        ida_idp.IDB_Hooks.__init__(self)
        self.usr_cmt = usr_cmt
        self.rebased = False
        
    # hook common and repeatable cmts
    def changing_cmt(self, ea, is_repeatable, new_comment):
        cur_ea = idc.here()
        if cur_ea == ea:
            # solve start_ea problems
            cur = ida_kernwin.get_cursor()
            if (cur != (True, 0, 0)):
                # Fix rebasing bug: Rebase pragram will trigger 'changing_cmt', causing to capture auto cmts at ea.
                if self.rebased:
                    self.rebased = False
                    return 0
                if is_repeatable:
                    self.usr_cmt.add_comment(ea, 'repeatable', new_comment)
                else:
                    self.usr_cmt.add_comment(ea, 'common', new_comment)
        return 0
        
    # hook anterior and posterior cmts
    def extra_cmt_changed(self, ea, line_idx, cmt):
        cur_ea = idc.here()
        if cur_ea == ea:
            cur = ida_kernwin.get_cursor()
            if (cur != (True, 0, 0)):
                if line_idx // 1000 == 1: # line_idx = 1xxx
                    self.usr_cmt.add_comment(ea, 'anterior', cmt, line_num=line_idx % 1000)
                if line_idx // 1000 == 2: # line_idx = 2xxx
                    self.usr_cmt.add_comment(ea, 'posterior', cmt, line_num=line_idx % 1000)
        return 0
        
    # hook Function cmts and repeatable Function cmts
    def changing_range_cmt(self, kind, a, cmt, is_repeatable):
        if is_repeatable:
            self.usr_cmt.add_comment(a.start_ea, 'func_repeatable', cmt)
        else:
            self.usr_cmt.add_comment(a.start_ea, 'func_common', cmt)
        return 0
        
    # program image rebased
    def allsegs_moved(self, info):
        self.rebased = True
        self.usr_cmt.imagebase = ida_nalt.get_imagebase()
        
        
class CommentViewer(ida_kernwin.Choose):
    def __init__(self, usr_cmt):
        ida_kernwin.Choose.__init__(
            self,
            title,
            [ ["Address", 10 | ida_kernwin.Choose.CHCOL_HEX],
              ["Type", 20 | ida_kernwin.Choose.CHCOL_PLAIN],
              ["Comments", 30 | ida_kernwin.Choose.CHCOL_PLAIN]],
            flags = ida_kernwin.Choose.CH_CAN_REFRESH)
        self.usr_cmt = usr_cmt
        self.items = []

    def OnInit(self):
        self.usr_cmt.load_comments()  # load comments again
        self.items = [ [hex(k[0] + self.usr_cmt.imagebase), k[1], v] for k, v in self.usr_cmt.comments.items() ]
        return True

    def OnGetSize(self):
        return len(self.items)

    def OnGetLine(self, n):
        return self.items[n]
        
    def OnRefresh(self, n):
        self.OnInit()
        if self.items:
            return [ida_kernwin.Choose.ALL_CHANGED] + self.adjust_last_item(n)
        return None # call standard refresh

    def OnSelectLine(self, n):
        selected_item = self.items[n]     # for single selection chooser
        addr = int(selected_item[0], 16)
        ida_kernwin.jumpto(addr)


def register_open_action(cmt_view):
    """
    Provide the action that will create the widget
    when the user asks for it.
    """
    class create_widget_t(ida_kernwin.action_handler_t):
        def activate(self, ctx):
            cmt_view.Show()

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

    action_name = "UserAddedComments:Show"
    action_shortcut = "Ctrl-Shift-C"
    ida_kernwin.register_action(
        ida_kernwin.action_desc_t(
            action_name,
            title,
            create_widget_t(),
            action_shortcut))
    ida_kernwin.attach_action_to_menu(
        f"View/Open subviews/{title}",
        action_name,
        ida_kernwin.SETMENU_APP)


class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE                      # Plugin should not appear in the Edit, Plugins menu.
    wanted_name = "Hook and display user-added comments"
    wanted_hotkey = ""
    comment = "Hook and display user-added comments"
    help = ""
    
    def init(self):
        self.usr_cmt = UserAddedComments()              # Create Comments instance here
        
        self.idb_hook = DisasmHooks(self.usr_cmt)       # Hook disassembly comments(common, repeatable, anterior, posterior cmts)
        self.idb_hook.hook()

        self.ray_hook = PseudoHooks(self.usr_cmt)       # Hook pseudo-code comments(cmts in "F5" pseudo-code view)
        self.ray_hook.hook()
        
        self.cmt_view = CommentViewer(self.usr_cmt)     # Create comment viewer instance
        
        register_open_action(self.cmt_view)             # Register to desktop widget and bind shortcut
        
        self.ui_hook = UIHooks(self.cmt_view)           # Refresh commnets viewer in real time 
        self.ui_hook.hook()
        return ida_idaapi.PLUGIN_KEEP                   # Keep us in the memory

    def run(self, arg):
        #self.cmt_view.Show()
        pass
        
    def term(self):
        self.ui_hook.unhook()
        self.ray_hook.unhook()
        self.idb_hook.unhook()
        return


def PLUGIN_ENTRY():
    return my_plugin_t()


repository: mahmoudimus/ida-keybinder
file: keybinder.py
description: Emacs-like keybinding sequences for IDA Pro.
wanted_name: Keybinder
comment: A plugin to enable Emacs-like key sequences and key chord sequences in IDA
created_at: 2024-08-02 18:01:59+00:00
pushed_at: 2024-08-02 18:04:08+00:00
url:
https://github.com/mahmoudimus/ida-keybinder/blob/7072bdb5125b58ca3bb23fa3dec2e6dfd8b43f23/keybinder.py

content:
import re
import sys

from PyQt5 import QtWidgets

import ida_kernwin

# this plugin requires Python 3
SUPPORTED_PYTHON = sys.version_info[0] == 3

# this plugin requires IDA 7.6 or newer
try:
    import ida_pro
    import ida_idaapi

    IDA_GLOBAL_SCOPE = sys.modules["__main__"]
    SUPPORTED_IDA = ida_pro.IDA_SDK_VERSION >= 760
except:
    SUPPORTED_IDA = False

# is this deemed to be a compatible environment for the plugin to load?
SUPPORTED_ENVIRONMENT = bool(SUPPORTED_IDA and SUPPORTED_PYTHON)

# ------------------------------------------------------------------------------
# IDA Plugin Stub
# ------------------------------------------------------------------------------

class KeyHooker(ida_kernwin.UI_Hooks):
    _TILDE_PATTERN_rgx = re.compile(r"~(.*?)~")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.LABEL_TO_ACTION = {}
        self.ACTIONS = {}        

    def enumerate_actions(self):
        # Get the instance of the QApplication
        main_window = QtWidgets.QApplication.instance()

        widgets = [main_window]
        while widgets:
            widget = widgets.pop(0)
            widgets.extend(list(widget.findChildren(QtWidgets.QAction)))
            if not isinstance(widget, QtWidgets.QAction):
                continue
            if widget.shortcut().isEmpty():
                continue

            self.LABEL_TO_ACTION[widget.text()] = widget

    @staticmethod
    def replace_tilde_with_ampersand(text):
        # Define a pattern that matches text between ~ and ~
        # The lambda function prefixes the matched text (group 1) with &
        replaced_text = KeyHooker._TILDE_PATTERN_rgx.sub(lambda match: f"&{match.group(1)}", text)
        return replaced_text

    def run(self):
        # ida_kernwin.update_action_shortcut("JumpOpXref", "Meta+g, x")
        self.enumerate_actions()
        for action_name in ida_kernwin.get_registered_actions():
            if not ida_kernwin.get_action_shortcut(action_name):
                continue
            # labels have ~ between the mnemonic shortkey identifier
            label = ida_kernwin.get_action_label(action_name)
            label = self.replace_tilde_with_ampersand(label)
            action = self.LABEL_TO_ACTION.get(label)
            if not action:
                print(
                    "WARNING: action ",
                    action_name,
                    "(",
                    label,
                    ") not found in action widgets.",
                )
            else:
                self.ACTIONS[action_name] = action

        print(f"[+] {KeybinderPlugin.wanted_name} by mahmoudimus. Setting shortcuts.")

        # Now proceed to set shortcuts
        self.ACTIONS["ChooserEdit"].setShortcut("Meta+e, c")
        self.ACTIONS["Edit/Plugins/IDA Patcher"].setShortcut("Meta+P, P")
        self.ACTIONS["Edit/Plugins/Signature Maker"].setShortcut("Meta+P, S")
        self.ACTIONS["EditEnum"].setShortcut("Meta+E, N")
        self.ACTIONS["EditSegment"].setShortcut("Meta+E, S")
        self.ACTIONS["ExpandStruct"].setShortcut("Meta+E, E")
        self.ACTIONS["JumpEntryPoint"].setShortcut("Meta+G, E")
        self.ACTIONS["JumpFunction"].setShortcut("Meta+G, F")
        self.ACTIONS["JumpOpXref"].setShortcut("Meta+g, x")  # Go to Xref
        self.ACTIONS["JumpText"].setShortcut("Meta+G, T")
        self.ACTIONS["SetSegmentRegister"].setShortcut("Meta+E, R")
        self.ACTIONS["TracingMainTracebufChangeDesc"].setShortcut("Meta+E, T")
        self.ACTIONS["watch:Edit"].setShortcut("Meta+E, W")    

    def ready_to_run(self):
        self.run()

    def get_lines_rendering_info(self, out, widget, rin):
        pass

    def populating_widget_popup(self, widget, popup, ctx):
        pass


class KeybinderPlugin(ida_idaapi.plugin_t):
    """
    The Keybinder plugin stub.
    """

    #
    # Plugin flags:
    # - PLUGIN_PROC: Load / unload this plugin when an IDB opens / closes
    # - PLUGIN_HIDE: Hide this plugin from the IDA plugin menu
    # - PLUGIN_UNL:  Unload the plugin after calling run()
    #
    
    flags = ida_idaapi.PLUGIN_PROC | ida_idaapi.PLUGIN_HIDE | ida_idaapi.PLUGIN_UNL
    comment = "A plugin to enable Emacs-like key sequences and key chord sequences in IDA"
    help = ""
    wanted_name = "Keybinder"
    wanted_hotkey = ""

    def __init__(self):
        self.__updated = getattr(IDA_GLOBAL_SCOPE, "RESTART_REQUIRED", False)

    # --------------------------------------------------------------------------
    # IDA Plugin Overloads
    # --------------------------------------------------------------------------

    def init(self):
        """
        This is called by IDA when it is loading the plugin.
        """
        if not SUPPORTED_ENVIRONMENT or self.__updated:
            return ida_idaapi.PLUGIN_SKIP
        
        # defer loading via hook
        self._ui_hook = KeyHooker()
        self._ui_hook.hook()

        # mark the plugin as loaded
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        """
        This is called by IDA when this file is loaded as a script.
        """
        pass

    def term(self):
        """
        This is called by IDA when it is unloading the plugin.
        """
        pass


def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython plugins.
    """
    if not SUPPORTED_ENVIRONMENT:
        print("plugin", KeybinderPlugin.wanted_name, "is not compatible with this IDA/Python version")
    return KeybinderPlugin()


repository: danielplohmann/idascope
file: IDAscope.py
description: An IDA Pro extension for easier (malware) reverse engineering
wanted_name: IDAscope
created_at: 2019-01-02 11:57:23+00:00
pushed_at: 2022-08-02 11:41:24+00:00
url:
https://github.com/danielplohmann/idascope/blob/19ddae7a430aad3bd75db663317eaabe9d2203d6/IDAscope.py

content:
#!/usr/bin/python
########################################################################
# Copyright (c) 2012
# Daniel Plohmann <daniel.plohmann<at>gmail<dot>com>
# Alexander Hanel <alexander.hanel<at>gmail<dot>com>
# All rights reserved.
########################################################################
#
#  This file is part of IDAscope
#
#  IDAscope is free software: you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see
#  <http://www.gnu.org/licenses/>.
#
########################################################################

import idascope.core.helpers.QtShim as QtShim
QtGui = QtShim.get_QtGui()
QtCore = QtShim.get_QtCore()
QtWidgets = QtShim.get_QtWidgets()

import idc
import idaapi
from idaapi import PluginForm, plugin_t

import idascope.config as config
from idascope.core.helpers.ClassCollection import ClassCollection
from idascope.core.structures.IDAscopeConfiguration import IDAscopeConfiguration
from idascope.core.SemanticIdentifier import SemanticIdentifier
from idascope.core.DocumentationHelper import DocumentationHelper
from idascope.core.WinApiProvider import WinApiProvider
from idascope.core.CryptoIdentifier import CryptoIdentifier
from idascope.core.YaraScanner import YaraScanner
from idascope.core.IdaProxy import IdaProxy
from idascope.core.SemanticExplorer import SemanticExplorer
from idascope.widgets.FunctionInspectionWidget import FunctionInspectionWidget
from idascope.widgets.WinApiWidget import WinApiWidget
from idascope.widgets.CryptoIdentificationWidget import CryptoIdentificationWidget
from idascope.widgets.YaraScannerWidget import YaraScannerWidget
from idascope.widgets.SemanticExplorerWidget import SemanticExplorerWidget

################################################################################
# Core of the IDAscope GUI.
################################################################################

HOTKEYS = None
IDASCOPE = None
NAME = "simpliFiRE.IDAscope v1.3.1"


class IDAscopeForm(PluginForm):
    """
    This class contains the main window of IDAscope
    Setup of core modules and widgets is performed in here.
    """

    def __init__(self):
        super(IDAscopeForm, self).__init__()
        global HOTKEYS
        HOTKEYS = []
        self.idascope_widgets = []
        self.cc = ClassCollection()
        self.ensureRootPathSanity(config.configuration)
        self.config = IDAscopeConfiguration(config.configuration, self.cc)
        self.icon = self.cc.QIcon(self.config.icon_file_path + "idascope.png")

    def ensureRootPathSanity(self, configuration):
        try:
            root_dir = configuration["paths"]["idascope_root_dir"]
            if not self.cc.os.path.exists(root_dir) or not "IDAscope.py" in self.cc.os.listdir(root_dir):
                print("[!] IDAscope.py is not present in root directory specified in \"config.py\", " \
                     + "trying to resolve path...")
                resolved_pathname = self.cc.os.path.dirname(self.cc.sys.argv[0])
                if "IDAscope.py" in self.cc.os.listdir(resolved_pathname):
                    print("[+] IDAscope root directory successfully resolved.")
                    configuration["paths"]["idascope_root_dir"] = resolved_pathname
                else:
                    print("[-] IDAscope.py is not resolvable!")
                    raise Exception()
        except:
            print("[!] IDAscope config is broken. Could not locate root directory. " \
                 + "Try setting the field \"idascope_root_dir\" to the path where \"IDAscope.py\" is located.")
            self.cc.sys.exit(-1)

    def setupSharedModules(self):
        """
        Setup shared IDAscope modules.
        """
        time_before = self.cc.time.time()
        print("[/] setting up shared modules...")
        self.semantic_explorer = SemanticExplorer(self)
        self.documentation_helper = DocumentationHelper(self.config)
        self.semantic_identifier = SemanticIdentifier(self.config)
        self.winapi_provider = WinApiProvider(self.config)
        self.crypto_identifier = CryptoIdentifier(self.config)
        self.yara_scanner = YaraScanner(self.config)
        print("[\\] this took %3.2f seconds.\n" % (self.cc.time.time() - time_before))

    def setupWidgets(self):
        """
        Setup IDAscope widgets.
        """
        time_before = self.cc.time.time()
        print("[/] setting up widgets...")
        self.idascope_widgets.append(SemanticExplorerWidget(self))
        self.idascope_widgets.append(FunctionInspectionWidget(self))
        self.idascope_widgets.append(WinApiWidget(self))
        self.idascope_widgets.append(CryptoIdentificationWidget(self))
        self.idascope_widgets.append(YaraScannerWidget(self))
        self.setupIDAscopeForm()
        print("[\\] this took %3.2f seconds.\n" % (self.cc.time.time() - time_before))

    def setupIDAscopeForm(self):
        """
        Orchestrate the already initialized widgets in tabs on the main window.
        """
        self.tabs = self.cc.QTabWidget()
        self.tabs.setTabsClosable(False)
        for widget in self.idascope_widgets:
            self.tabs.addTab(widget, widget.icon, widget.name)
        layout = self.cc.QVBoxLayout()
        layout.addWidget(self.tabs)
        self.parent.setLayout(layout)

    def OnCreate(self, form):
        """
        When creating the form, setup the shared modules and widgets
        """
        self.printBanner()
        # compatibility with IDA < 6.9
        try:
            self.parent = self.FormToPySideWidget(form)
        except Exception as exc:
            self.parent = self.FormToPyQtWidget(form)
        self.parent.setWindowIcon(self.icon)
        self.setupSharedModules()
        self.setupWidgets()

    def printBanner(self):
        banner = "#############################################\n" \
               + "  ___ ____    _                             \n" \
               + " |_ _|  _ \  / \   ___  ___ ___  _ __   ___ \n" \
               + "  | || | | |/ _ \ / __|/ __/ _ \\| '_ \\ / _ \\\n" \
               + "  | || |_| / ___ \\\\__ \\ (_| (_) | |_) |  __/\n" \
               + " |___|____/_/   \\_\\___/\\___\\___/| .__/ \\___|\n" \
               + "                                |_|         \n" \
               + "#############################################\n" \
               + " by Daniel Plohmann and Alexander Hanel      \n" \
               + "#############################################\n"
        print(banner)
        print("[+] Loading simpliFiRE.IDAscope")

    def OnClose(self, form):
        """
        Perform cleanup.
        """
        global IDASCOPE
        del IDASCOPE

    def Show(self):
        if self.cc.ida_proxy.GetInputMD5() is None:
            return
        else:
            options = 0
            if self.cc.ida_proxy.idaapi.IDA_SDK_VERSION < 700:
                options = PluginForm.FORM_CLOSE_LATER | PluginForm.FORM_RESTORE | PluginForm.FORM_SAVE
            else:
                options = PluginForm.WCLS_CLOSE_LATER | PluginForm.WOPN_RESTORE | PluginForm.WCLS_SAVE

            return PluginForm.Show(self,
                NAME,
                options=options)

################################################################################
# functionality offered to IDAscope's widgets
################################################################################

    def setTabFocus(self, widget_name):
        """
        Can be used by IDAscope widgets to set focus to a widget, identified by name.
        @param widget_name: A widget name
        @type widget_name: str
        """
        for widget in self.idascope_widgets:
            if widget.name == widget_name:
                tab_index = self.tabs.indexOf(widget)
                self.tabs.setCurrentIndex(tab_index)
        return

    def registerHotkey(self, shortcut, py_function_pointer):
        """
        Can be used by IDAscope widgets to register hotkeys.
        Uses a global list HOTKEYS of function pointers that link to the desired functionality.
        Right now, linked functions cannot take parameters and should scrape all information they need by themselves.
        @param shortcut: A string describing a shortcut, e.g. "ctrl+F3"
        @type shortcut: str
        @param py_function_pointer: a python function that shall be called when the shortcut is triggered.
        @type py_function_pointer: a pointer to a python function
        """
        global HOTKEYS
        hotkey_index = len(HOTKEYS)
        hotkey_name = "idascope_HOTKEY_%d" % hotkey_index
        HOTKEYS.append(py_function_pointer)
        self.cc.ida_proxy.CompileLine('static %s() { RunPythonStatement("HOTKEYS[%d]()"); }' % (hotkey_name, hotkey_index))
        self.cc.ida_proxy.AddHotkey(shortcut, hotkey_name)

################################################################################
# Usage as plugin
################################################################################


def PLUGIN_ENTRY():
    return IDAscopePlugin()


class IDAscopePlugin(plugin_t):
    """
    Plugin version of IDAscope. Use this to deploy IDAscope via IDA plugins folder.
    """
    flags = idaapi.PLUGIN_UNL
    comment = NAME
    help = "IDAscope - Different tools to ease reverse engineering."
    wanted_name = "IDAscope"
    wanted_hotkey = "Ctrl-F4"

    def init(self):
        # Some initialization
        self.icon_id = 0
        return idaapi.PLUGIN_OK

    def run(self, arg=0):
        # Create form
        f = IDAscopeForm()
        # Show the form
        f.Show()
        return

    def term(self):
        pass

################################################################################
# Usage as script
################################################################################


def main():
    global IDASCOPE
    try:
        IDASCOPE
        IDASCOPE.OnClose(IDASCOPE)
        print("reloading IDAscope")
        IDASCOPE = IDAscopeForm()
        return
    except Exception:
        IDASCOPE = IDAscopeForm()

    if IDASCOPE.config.idascope_plugin_only:
        print("IDAscope: configured as plugin-only mode, ignoring main function of script. " \
             + "This can be changed in \"idascope/config.py\".")
    else:
        IDASCOPE.Show()


if __name__ == "__main__":
    main()


repository: bloerwald/Analyze-a-tron-2000
file: ida/plugins/AutoRenameMethod.py
description: tiny tools/scripts for static analysis of files and binaries
wanted_name: Auto Rename plugin
comment: This is a comment
created_at: 2017-09-09 14:08:21+00:00
pushed_at: 2023-05-12 20:06:49+00:00
url:
https://github.com/bloerwald/Analyze-a-tron-2000/blob/d8dacedc6524f63dfedd0855ff27f15553bded7f/ida/plugins/AutoRenameMethod.py

content:
# This plugin automatically renames function based on struct type of argument in rcx register
# How to install: copy file into IDA/plugins folder

from __future__ import print_function

import ida_idaapi
import ida_typeinf
import ida_hexrays
import idaapi
import ida_name
import idc

# From enum RegNo
RCX_REG = 1

#Removes usercall calling convention, but doesnt keep order of arguments
# and messes the registers assigned to parameters
def fixFuncType(ea):
    funcEa = GetFunctionAttr(ea, FUNCATTR_START)
    cfunc = idaapi.decompile(funcEa)
    old_func_type = idaapi.tinfo_t()
    cfunc.get_func_type(old_func_type)

    fi = idaapi.func_type_data_t()
    if old_func_type.get_func_details(fi):
        if (fi.cc == idaapi.CM_CC_SPECIAL) or (fi.cc == idaapi.CM_CC_SPECIALE) or (fi.cc == idaapi.CM_CC_SPECIALP):
            fi.cc = idaapi.CM_CC_FASTCALL

            new_func_type = idaapi.tinfo_t()
            new_func_type.create_func(fi)

            idaapi.apply_tinfo2(funcEa, new_func_type, idaapi.TINFO_DEFINITE)

class autoRename_hooks_t(ida_hexrays.Hexrays_Hooks):
    def _shorten(self, cfunc):
        raw = str(cfunc)
        if len(raw) > 20:
            raw = raw[0:20] + "[...snipped...]"
        return raw

    def _format_lvar(self, v):
        parts = []
        if v:
            if v.name:
                parts.append("name=%s" % v.name)
            if v.cmt:
                parts.append("cmt=%s" % v.cmt)
            parts.append("width=%s" % v.width)
            parts.append("defblk=%s" % v.defblk)
            parts.append("divisor=%s" % v.divisor)
        return "{%s}" % ", ".join(parts)

    def lvar_type_changed(self, vu, v, tif):
        if (vu.cfunc):
            func_tif = ida_typeinf.tinfo_t()
            vu.cfunc.get_func_type(func_tif)

            funcdata = idaapi.func_type_data_t()
            got_data = func_tif.get_func_details(funcdata)

            if (not got_data):
                # self._log("Didnt get the data")
                pass

            lvars = vu.cfunc.get_lvars()
            for j in range(len(vu.cfunc.argidx)):
                # for i in vu.cfunc.argidx:
                i = vu.cfunc.argidx[j];
                if (lvars[i].name == v.name):
                    #self._log("lvar_type_changed: function argument changed = %s, index = %s, atype = %s" % (lvars[i].name, i, funcdata[j].argloc.atype()))
                    if(funcdata[i].argloc.atype() == 3):
                        #    self._log("lvar_type_changed: reg is : %s" %(funcdata[i].argloc.reg1()))
                        pass

                    if (funcdata[i].argloc.atype() != 3 or funcdata[i].argloc.reg1() != RCX_REG):
                        break

                    #self._log("applyName = %s" % (applyName))

                    firstPtrRemove = ida_typeinf.remove_pointer(tif)
                    #self._log("type name = %s" % (firstPtrRemove._print()))
                    #self._log("remove_pointer.is_ptr = %s" % (firstPtrRemove.is_ptr()))
                    #self._log("remove_pointer.is_struct = %s" % (firstPtrRemove.is_struct()))
                    if (firstPtrRemove.is_struct() and not firstPtrRemove.is_ptr()):
                        currentFuncName = ida_name.get_ea_name(vu.cfunc.entry_ea)
                        # self._log("before demangle current func name = %s" % (currentFuncName))
                        demangled = idc.demangle_name(currentFuncName,idc.get_inf_attr(idc.INF_SHORT_DN))
                        if (demangled != None):
                            self._log("Overriding mangled name = %s" % (currentFuncName))
                            currentFuncName = demangled
                        # self._log("after demangle current func name = %s" % (currentFuncName))
                        tokens = currentFuncName.split("::")
                        if len(tokens) > 1:
                            currentFuncName = tokens[1]
                        currentFuncName = currentFuncName.split("(")[0]
                        # self._log("current func name = %s" % (currentFuncName))
                        idc.set_name(vu.cfunc.entry_ea, firstPtrRemove._print()+"::"+currentFuncName, idc.SN_NOWARN)
                        idaapi.auto_wait()
                        # self._log("Decomp Res : %s" % idaapi.decompile(vu.cfunc.entry_ea))
                        idaapi.refresh_idaview_anyway()
                        vu.refresh_ctext()
                        idaapi.refresh_idaview_anyway()
                        vu.refresh_ctext()
                        vu.refresh_view(True)

                        current_widget = idaapi.get_current_widget()
                        vu1 = idaapi.get_widget_vdui(current_widget)
                        if vu1:
                            vu1.refresh_ctext()
                    break

        #self._log("lvar_type_changed: vu=%s, v=%s, tinfo=%s" % (vu, self._format_lvar(v), tif._print()))
        return 1

    def _log(self, msg):
        print("### %s" % msg)
        return 0


class autoRenamePlugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "This is a comment"
    help = "This is help"
    wanted_name = "Auto Rename plugin"
    wanted_hotkey = "Alt-F8"
    autoRenameHook = None
    def init(self):
        # idaapi.msg("init() called!\n")
        self.autoRenameHook = autoRename_hooks_t()
        self.autoRenameHook.hook()
        print("Auto rename method hook installed")

        return idaapi.PLUGIN_KEEP
    def run(self, arg):
        # idaapi.msg("run() called with %d!\n" % arg)
        pass
    def term(self):
        # idaapi.msg("term() called!\n")
        self.autoRenameHook.unhook()

def PLUGIN_ENTRY():
    return autoRenamePlugin_t()


repository: Goatman13/pypyc2c
file: ppc2c.py
description: PowerPC to C plugin for IDA
url: https://github.com/Goatman13/pypyc2c/blob/859aa68b112471438129116198057732595c918f/ppc2c.py
created_at: 2023-08-20 16:28:20+00:00
pushed_at: 2024-11-15 14:28:01+00:00
content:
# PPC To C
#
# by working with the disassembled text this plugin should
# work with both big and little endian PPC.

from ida_bytes import *
from idaapi import *
from idc import *
import ida_ida
import idaapi
import ida_bytes
import idc

SIMPLIFY  = 1
MASK32_ALLSET   = 0xFFFFFFFF
MASK64_ALLSET   = 0xFFFFFFFFFFFFFFFF

g_mnem = 0
g_opnd_s0 = 0
g_opnd_s1 = 0
g_opnd_s2 = 0
g_opnd_s3 = 0
g_opnd_s4 = 0

g_RA = 0
g_RS = 0
g_RB = 0
g_SH = 0
g_MB = 0
g_ME = 0


# generates the mask between MaskBegin(MB) and MaskEnd(ME) inclusive
# MB and ME should be values 0 - 31
def GenerateMask32(MB, ME):
	
	if(	MB <  0 or ME <  0 or MB > 31 or ME > 31 ):
		msg("Error with paramters GenerateMask32({:d}, {:d})\n".format(MB, ME))
		return 0

	mask = 0
	if(MB < ME+1):
		# normal mask
		while MB < ME+1:
			mask = mask | (1<<(31-MB))
			MB += 1
	elif(MB == ME+1):
		# all mask bits set
		mask = MASK32_ALLSET
	elif(MB > ME+1):
		# split mask
		mask_lo = GenerateMask32(0, ME)
		mask_hi = GenerateMask32(MB, 31)
		mask = mask_lo | mask_hi

	return mask


# generates the mask between MaskBegin(MB) and MaskEnd(ME) inclusive
# MB and ME should be values 0 - 63
def GenerateMask64(MB, ME):
	
	if(	MB <  0 or ME <  0 or MB > 63 or ME > 63 ):
		msg("Error with paramters GenerateMask64({:d}, {:d})\n".format(MB, ME))
		return 0

	mask = 0
	if(MB < ME+1):
		# normal mask
		while MB < ME+1:
			mask = mask | (1<<(63-MB))
			MB += 1
	elif(MB == ME+1):
		# all mask bits set
		mask = MASK64_ALLSET
	elif(MB > ME+1):
		# split mask
		mask_lo = GenerateMask64(0, ME)
		mask_hi = GenerateMask64(MB, 63)
		mask = mask_lo | mask_hi

	return mask


# generate string showing rotation or shifting within instruction
def GenerateRotate32(src, leftShift, rightShift, mask):
	
	ret_str = 0
	# work out "rotate" part of the instruction
	if(	(leftShift== 0 and rightShift==32) or (leftShift==32 and rightShift== 0 )):
		return src

	if(((MASK32_ALLSET<<leftShift ) & mask) == 0):
		# right shift only
		if((MASK32_ALLSET>>rightShift) == mask):
			mask = MASK32_ALLSET
		ret_str = src + " >> {:d}".format(rightShift)
	elif(((MASK32_ALLSET>>rightShift) & mask) == 0):
		# left shift only
		if((MASK32_ALLSET<<leftShift) == mask):
			mask = MASK32_ALLSET
		ret_str = src + " << {:d}".format(leftShift)
	else:
		# shift both ways
		ret_str = "(" + src + " << {:d}) | (".format(leftShift) + src + " >> {:d})".format(rightShift)
	return ret_str


# generate string showing rotation or shifting within instruction
def GenerateRotate64(src, leftShift, rightShift, mask):
	
	# work out "rotate" part of the instruction
	if((leftShift == 0 and rightShift == 64) or (leftShift == 64 and rightShift == 0)):
		# no rotation
		return src

	if(((MASK64_ALLSET<<leftShift ) & mask) == 0):
		# right shift only
		if((MASK64_ALLSET>>rightShift) == mask):
			mask = MASK64_ALLSET
		ret_str = src + " >> {:d}".format(rightShift)
	elif(((MASK64_ALLSET>>rightShift) & mask) == 0):
		# left shift only
		if((MASK64_ALLSET<<leftShift) == mask):
			mask = MASK64_ALLSET
		ret_str = src + " << {:d}".format(leftShift)
	else:
		# shift both ways
		ret_str = "(" + src + " << {:d}) | (".format(leftShift) + src + " >> {:d})".format(rightShift)
	return ret_str


# register rotate and immediate mask
def Rotate_iMask32(ea, g_mnem, g_RA, g_RS, leftRotate, mb, me):
	
	ret_str = 0
	# calculate the mask
	# if no mask, then result is always 0
	mask = GenerateMask32(mb, me)
	if(mask == 0):
		# no rotation
		ret_str = g_RA + " = 0"
		return ret_str

	# work out "rotate" part of the instruction
	# if all mask bits are set, then no need to use the mask
	rot_str = 0
	rot_str = g_RS + " << " + leftRotate + " | " + g_RS + " >> 32 - " + leftRotate
	if(mask == MASK32_ALLSET):
		ret_str = g_RA + " = " + rot_str
		return ret_str

	# generate mask string
	mask_str = 0
	if (mask < 10):
		mask_str = "{:X}".format(mask)
	else:
		mask_str = "0x{:X}".format( mask)

	# generate the resultant string
	# "%s = (%s) & %s"
	ret_str = g_RA + " = " + "(" + rot_str + ") & " + mask_str
	return ret_str


# immediate rotate and immediate mask
def iRotate_iMask32(ea, g_mnem, g_RA, g_RS, leftRotate, mb, me):
	
	ret_str = 0
	# calculate the mask
	# if no mask, then result is always 0
	mask = GenerateMask32(mb, me)
	if(mask == 0):
		ret_str = g_RA + " = 0"
		return ret_str

	# work out "rotate" part of the instruction
	# if all mask bits are set, then no need to use the mask
	rot_str = 0
	rot_str = GenerateRotate32(g_RS, leftRotate, 32-leftRotate, mask)
	if(mask == MASK32_ALLSET):
		ret_str = g_RA + " = " + rot_str
		return ret_str

	# generate mask string
	mask_str = 0
	if (mask < 10):
		mask_str = "{:X}".format(mask)
	else:
		mask_str = "0x{:X}".format(mask)
	
	# no bracelets
	if (rot_str == g_RS):
		ret_str = g_RA + " = " + rot_str + " & " + mask_str
	else:
		ret_str = g_RA + " = " + "(" + rot_str + ") & " + mask_str
	return ret_str


# insert immediate rotate and immediate mask
def insert_iRotate_iMask32(ea, g_mnem, g_RA, g_RS, leftRotate, mb, me):
	
	ret_str = 0
	# calculate the mask
	# if no mask, then result is always 0
	mask = GenerateMask32(mb, me)
	if(mask == 0):
		ret_str = g_RA + " = " + g_RA
		return ret_str

	# work out "rotate" part of the instruction
	# if all mask bits are set, then no need to use the mask
	rot_str = 0
	rot_str =  GenerateRotate32(g_RS, leftRotate, 32-leftRotate, mask)
	if(mask == MASK32_ALLSET):
		ret_str = g_RA + " = " + rot_str
		return ret_str

	# generate mask strings
	mask_str = 0
	not_mask = 0
	not_mask_str = 0
	
	# generate mask string
	if (mask < 10):
		mask_str = "{:X}".format(mask)
	else:
		mask_str = "0x{:X}".format( mask)
	
	# no bracelets
	if (rot_str == g_RS):
		ret_str = g_RA + " = (" + g_RA + " & ~" + mask_str + ") | (" + rot_str + " & " + mask_str + ")"
	else:
		ret_str = g_RA + " = (" + g_RA + " & ~" + mask_str + ") | ((" + rot_str + ") & " + mask_str + ")"
	return ret_str


# register rotate and immediate mask
def Rotate_iMask64(ea, g_mnem, g_RA, g_RS, leftRotate, mb, me):
	
	ret_str = 0
	# calculate the mask
	# if no mask, then result is always 0
	mask = GenerateMask64(mb, me)
	if(mask == 0):
		ret_str = g_RA + " = 0"
		return ret_str

	# work out "rotate" part of the instruction
	# if all mask bits are set, then no need to use the mask
	rot_str = 0
	rot_str = g_RS + " << " + leftRotate + " | " + g_RS + " >> 64-" + leftRotate
	if(mask == MASK64_ALLSET):
		#qsnprintf(buff, buffSize, "%s = (u32)(%s)", g_RA, rot_str)
		ret_str = g_RA + " = " + rot_str
		return ret_str

	# generate mask string
	mask_str = 0
	if (mask < 10):
		mask_str = "{:X}".format(mask)
	else:
		mask_str = "0x{:X}".format( mask)

	# generate the resultant string
	ret_str = g_RA + " = " + "(" + rot_str + ") & " + mask_str
	return ret_str


# immediate rotate and immediate mask
def iRotate_iMask64(ea, g_mnem, g_RA, g_RS, leftRotate, mb, me):
	
	ret_str = 0
	# calculate the mask
	# if no mask, then result is always 0
	mask = GenerateMask64(mb, me)
	if(mask == 0):
		ret_str = g_RA + " = 0"
		return ret_str

	# work out "rotate" part of the instruction
	# if all mask bits are set, then no need to use the mask
	rot_str = 0
	rot_str = GenerateRotate64(g_RS, leftRotate, 64-leftRotate, mask)
	if(mask == MASK64_ALLSET):
		ret_str = g_RA + " = " + rot_str
		return ret_str

	# generate mask string
	mask_str = 0
	if (mask < 10):
		mask_str = "{:X}".format(mask)
	else:
		mask_str = "0x{:X}".format( mask)

	# no bracelets
	if (rot_str == g_RS):
		ret_str = g_RA + " = " + rot_str + " & " + mask_str
	else:
		ret_str = g_RA + " = " + "(" + rot_str + ") & " + mask_str
	return ret_str


# insert immediate rotate and immediate mask
def insert_iRotate_iMask64(ea, g_mnem, g_RA, g_RS, leftRotate, mb, me):
	
	ret_str = 0
	# calculate the mask
	# if no mask, then result is always 0
	mask = GenerateMask64(mb, me)
	if(mask == 0):
		ret_str = g_RA + " = " + g_RA
		return ret_str

	# work out "rotate" part of the instruction
	# if all mask bits are set, then no need to use the mask
	rot_str = 0
	rot_str =  GenerateRotate64(g_RS, leftRotate, 64-leftRotate, mask)
	if(mask == MASK64_ALLSET):
		ret_str = g_RA + " = " + rot_str
		return ret_str

	# generate mask strings
	mask_str = 0
	not_mask = 0
	not_mask_str = 0
	
	# generate mask string
	if (mask < 10):
		mask_str = "{:X}".format(mask)
	else:
		mask_str = "0x{:X}".format(mask)
	
	# no bracelets
	if (rot_str == g_RS):
		ret_str = g_RA + " = (" + g_RA + " & ~" + mask_str + ") | (" + rot_str + " & " + mask_str + ")"
	else:
		ret_str = g_RA + " = (" + g_RA + " & ~" + mask_str + ") | ((" + rot_str + ") & " + mask_str + ")"
	return ret_str

# ==================================================================
#
# helpers
#
# ==================================================================

def search_for_matching_opcode_backwards(ea, search, g_opnd_s0, g_opnd_s1, g_opnd_s2):
	
	if (SIMPLIFY == 0):
		return 0

	# Find matching opcode
	temp_ea = ea - 4
	while(temp_ea > ea - 0x20):
	
		g_mnem = print_insn_mnem(temp_ea)
		
		# Remove record bit if exist
		dot = "."
		dot = g_mnem.find(dot)
		if(dot != -1):
			g_mnem = g_mnem[0:dot]

		# Check for matching opcode
		if(g_mnem == search and print_operand(temp_ea, 0) == g_opnd_s1):
			return PPCAsm2C(temp_ea)

		temp_ea -= 4
		
	return 0


def simplify64(orig_ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME):

	if (SIMPLIFY == 0):
		return 0

	if (g_mnem == "rldicl" and  g_ME != 63 or g_SH == 0):
		return 0

	if (g_mnem in ["sldi", "extldi"] and  g_SH == 0):
		return 0
	
	# Find matching opcode
	ea = orig_ea + 4
	while(ea < orig_ea + 0x20):

		g_mnem2 = print_insn_mnem(ea)

		# Remove record bit if exist
		dot = "."
		dot = g_mnem2.find(dot)
		if(dot != -1):
			g_mnem2 = g_mnem2[0:dot]
		
		# If RA is modified earlier, we need to break and handle it old way.
		# This helps eliminate false detection when there is not paired rotxdi.
		if(print_operand(ea, 0) == g_RA):
			if ((g_mnem == "rldicl" and g_mnem2 not in ["rotldi", "rotrdi", "rldicl"])
			or (g_mnem  in ["extldi", "sldi"] and g_mnem2 != "extldi")):
				print("Rare case! " + g_mnem2 + " " + print_operand(ea, 0))
				return 0
		
		if((g_mnem == "rldicl" and g_mnem2 in ["rotldi", "rotrdi", "rldicl"])
			or (g_mnem  in ["extldi", "sldi"] and g_mnem2 == "extldi")):
			
			g_opnd_t0 = print_operand(ea, 0)
			g_opnd_t1 = print_operand(ea, 1)
			g_opnd_t2 = print_operand(ea, 2)
			if(g_mnem2 in ["rldicl", "extldi"]):
				comma = ","
				comma = g_opnd_t2.find(comma)
				if(comma != -1):
					g_opnd_t3 = g_opnd_t2[comma+1:]
					g_opnd_t2 = g_opnd_t2[0:comma]
					g_opnd_t2 = int(g_opnd_t2)
					g_opnd_t3 = int(g_opnd_t3)
			else:
				g_opnd_t2 = int(g_opnd_t2)

			# Check if next opcode is using result from first one.
			if(g_opnd_t1 == g_RA):
			
				# Check if "counter rotate" is exatcly the same as rotate.
				# This ensure compiler was trying to do one of supported operations.
				if(g_mnem == "rldicl" and g_mnem2 in ["rotldi", "rldicl"] and g_opnd_t2 != 64-g_SH):
					return 0
				elif(g_mnem == "rldicl" and g_mnem2 == "rotrdi" and g_opnd_t2 != g_SH):
					return 0
				elif(g_mnem in ["sldi", "extldi"] and g_opnd_t3 + g_SH != 64):
					return 0

				mask = GenerateMask64(g_MB, g_ME)
				mask = ((mask >> g_SH) | (mask << (64-g_SH))) & MASK64_ALLSET

				if (g_mnem == "rldicl"):
					mask = ~mask & MASK64_ALLSET
					upper_mask = mask >> 32
					lower_mask = mask & MASK32_ALLSET
					tilde = "~"
					if(g_mnem2 == "rldicl"):
						new_mask = GenerateMask64(g_opnd_t3, 63)
						new_mask = ~new_mask & MASK64_ALLSET
						new_upper_mask = new_mask >> 32
						new_lower_mask = new_mask & MASK32_ALLSET
						upper_mask = new_upper_mask | upper_mask
						lower_mask = new_lower_mask | lower_mask
						upper_mask = ~upper_mask & MASK32_ALLSET
						lower_mask = ~lower_mask & MASK32_ALLSET
						tilde = ""

				elif(g_mnem in ["sldi", "extldi"] and g_mnem2 == "extldi"):
					new_mask = GenerateMask64(0, g_opnd_t2-1)
					mask = mask & new_mask
					upper_mask = mask >> 32
					lower_mask = mask & MASK32_ALLSET
					tilde = ""

				first_mnem_comment = "Paired with " + g_mnem2 + " at 0x{:X}".format(ea)
				if(upper_mask != 0):
					second_mnem_comment = g_opnd_t0 + " = " + g_RS + " & " + tilde + "0x{:X}_{:08X} (".format(upper_mask, lower_mask) + g_RS + " from 0x{:X})".format(orig_ea)
				else:
					second_mnem_comment = g_opnd_t0 + " = " + g_RS + " & " + tilde + "0x{:X} (".format(lower_mask) + g_RS + " from 0x{:X})".format(orig_ea)					
				set_cmt(ea, second_mnem_comment, 0)
				set_cmt(orig_ea        , first_mnem_comment, 0)
				return 1

		ea += 4
	
	# matching opcode not found, fallback to default handling.
	return 0


def rlwinm_andnot(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME):

	if (SIMPLIFY == 0 or g_ME != 31 or g_SH == 0):
		return 0

	mask = GenerateMask32(g_MB, g_ME)
	mask = ((mask >> g_SH) | (mask << (32-g_SH))) & MASK32_ALLSET
	mask = ~mask & MASK32_ALLSET

	# Find matching rotxwi
	rotea = ea + 4
	while(rotea < ea + 0x20):

		# Remove record bit if exist
		is_rotxwi = print_insn_mnem(rotea)[:6]
		
		# If rlwinm RA is modified earlier, we need to break and handle it old way.
		# This helps eliminate false detection when there is not paired rotxwi.
		if(print_operand(rotea, 0) == g_RA and is_rotxwi != "rotlwi" and is_rotxwi != "rotrwi"):
				print("Rare case!")
				return 0
		
		if(is_rotxwi == "rotlwi" or is_rotxwi == "rotrwi"):
			g_opnd_t0 = print_operand(rotea, 0)
			g_opnd_t1 = print_operand(rotea, 1)
			g_opnd_t2 = int(print_operand(rotea, 2))

			# Check if rotate is using result from rlwinm
			if(g_opnd_t1 == g_RA):
			
				# Check if "counter rotate" is exatcly the same as rotate in rlwinm.
				# This ensure compiler was trying to do "and not".
				if(is_rotxwi == "rotlwi" and g_opnd_t2 != 32-g_SH):
					return 0
				elif(is_rotxwi == "rotrwi" and g_opnd_t2 != g_SH):
					return 0
					
				rlwinm_comment = "Paired with " + is_rotxwi + " at 0x{:X}".format(rotea)
				rotxwi_comment = g_opnd_t0 + " = " + g_RS + " & ~0x{:X} (".format(mask) + g_RS + " from 0x{:X})".format(ea)
				set_cmt(rotea, rotxwi_comment, 0)
				set_cmt(ea   , rlwinm_comment, 0)
				return 1

		rotea += 4
	
	# rotxwi not found, fallback to default handling.
	return 0


# ==================================================================
#
# instructions
#
# ==================================================================

def clrlwi(ea, g_mnem, g_RA, g_RS, n):
	
	# Clear left immediate
	# clrlwi RA, RS, n
	# rlwinm RA, RS, 0, n, 31
	g_SH = 0
	g_MB = n
	g_ME = 31

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def clrldi(ea, g_mnem, g_RA, g_RS, n):
	
	# Rotate Left Double Word Immediate then Clear Left
	# clrldi RA, RS, n
	# rldicl RA, RS, 0, n
	g_SH = 0
	g_MB = n
	g_ME = 63

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def clrrwi(ea, g_mnem, g_RA, g_RS, n):
	
	# Clear right immediate
	# clrrwi RA, RS, n
	# rlwinm RA, RS, 0, 0, 31-n
	g_SH = 0
	g_MB = 0
	g_ME = 31-n

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def clrrdi(ea, g_mnem, g_RA, g_RS, n):
	
	# Clear right immediate
	# clrrdi RA, RS, n
	# rldicr RA, RS, 0, 63 - n
	g_SH = 0
	g_MB = 0
	g_ME = 63-n

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def clrlslwi(ea, g_mnem, g_RA, g_RS, b, n):
	
	# Clear left word and shift left immediate
	# clrlslwi RA, RS, b, n
	# rlwinm RA, RS, b-n, 31-n
	g_SH = n
	g_MB = b-n
	g_ME = 31-n

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def clrlsldi(ea, g_mnem, g_RA, g_RS, b, n):
	
	# Clear left double word and shift left immediate
	# clrlsldi RA, RS, b, n
	# rldic RA, RS, n, b - n
	g_SH = n
	g_MB = b-n
	g_ME = 63-n

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def extrwi(ea, g_mnem, g_RA, g_RS, n, b):
	
	# Extract and right justify immediate
	# extrwi RA, RS, n, b
	# rlwinm RA, RS, b+n, 32-n, 31
	g_SH = b+n
	g_MB = 32-n
	g_ME = 31

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def extrdi(ea, g_mnem, g_RA, g_RS, n ,b):
	
	# Extract double word and right justify immediate
	# extrdi RA, RS, n, b
	# rldicl RA, RS, b + n, 64 - n
	g_SH = b+n
	g_MB = 64-n
	g_ME = 63

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def extlwi(ea, g_mnem, g_RA, g_RS, n, b):
	
	# Extract and left justify immediate
	# extlwi RA, RS, n, b
	# rlwinm RA, RS, b, 0, n-1
	g_SH = b
	g_MB = 0
	g_ME = n-1

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def extldi(ea, g_mnem, g_RA, g_RS, n, b):
	
	# Extract double word and left justify immediate
	# extldi RA, RS, n, b
	# rldicr RA, RS, b, n - 1
	g_SH = b
	g_MB = 0
	g_ME = n-1

	if(simplify64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME) == 1):
		return 1
	
	if(search_for_matching_opcode_backwards(ea, "sldi", g_RA, g_RS, n) == 1):
		return 1

	if(search_for_matching_opcode_backwards(ea, "extldi", g_RA, g_RS, n) == 1):
		return 1

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def inslwi(ea, g_mnem, g_RA, g_RS, n, b):
	
	# Insert from left immediate
	# inslwi RA, RS, n, b
	# rlwimi RA, RS, 32-b, b, (b+n)-1
	g_SH = 32-b
	g_MB = b
	g_ME = b+n-1

	return insert_iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def insrwi(ea, g_mnem, g_RA, g_RS, n, b):
	
	# Insert from right immediate
	# insrwi RA, RS, n, b
	# rlwimi RA, RS, 32-(b+n), b, (b+n)-1
	g_SH = 32-(b+n)
	g_MB = b
	g_ME = b+n-1

	return insert_iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def insrdi(ea, g_mnem, g_RA, g_RS, n, b):
	
	# Insert double word from right immediate
	# insrdi RA, RS, n, b
	# rldimi RA, RS, 64 - (b + n), b
	g_SH = 64-(b+n)
	g_MB = b
	g_ME = b+n-1

	return insert_iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rlwinm(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME):
	
	# Rotate Left Word Immediate Then AND with Mask
	# rlwinm RA, RS, SH, MB, ME
	
	if (rlwinm_andnot(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME) == 1):
		return 1

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rlwnm(ea, g_mnem, g_RA, g_RS, g_RB, g_MB, g_ME):
	
	# Rotate Left Word Then AND with Mask
	# rlwnm RA, RS, RB, MB, ME

	return Rotate_iMask32(ea, g_mnem, g_RA, g_RS, g_RB, g_MB, g_ME)


def rotlwi(ea, g_mnem, g_RA, g_RS, n):
	
	# Rotate left immediate
	# rotlwi RA, RS, n
	# rlwinm RA, RS, n, 0, 31
	g_SH = n
	g_MB = 0
	g_ME = 31
	
	if(search_for_matching_opcode_backwards(ea, "rlwinm", g_RA, g_RS, n) == 1):
		return 1

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rotrwi(ea, g_mnem, g_RA, g_RS, n):
	
	# Rotate right immediate
	# rotrwi RA, RS, n
	# rlwinm RA, RS, 32-n, 0, 31
	g_SH = 32-n
	g_MB = 0
	g_ME = 31

	if(search_for_matching_opcode_backwards(ea, "rlwinm", g_RA, g_RS, n) == 1):
		return 1
		
	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rotldi(ea, g_mnem, g_RA, g_RS, n):
	
	# Rotate left immediate
	# rotldi RA, RS, n
	# rldicl RA, RS, n, 0
	g_SH = n
	g_MB = 0
	g_ME = 63

	if(search_for_matching_opcode_backwards(ea, "rldicl", g_RA, g_RS, n) == 1):
		return 1

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rotrdi(ea, g_mnem, g_RA, g_RS, n):
	
	# Rotate right immediate
	# rotrdi RA, RS, n
	# rldicl RA, RS, 64 - n, 0
	g_SH = 64-n
	g_MB = 0
	g_ME = 63

	if(search_for_matching_opcode_backwards(ea, "rldicl", g_RA, g_RS, n) == 1):
		return 1

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rotlw(ea, g_mnem, g_RA, g_RS, g_RB):
	
	# Rotate left
	# rotlw RA, RS, RB
	# rlwnm RA, RS, RB, 0, 31
	g_MB = 0
	g_ME = 31

	return Rotate_iMask32(ea, g_mnem, g_RA, g_RS, g_RB, g_MB, g_ME)


def rotld(ea, g_mnem, g_RA, g_RS, g_RB):
	
	# Rotate Left Double Word
	# rotld RA, RS, RB
	# rldcl RA, RS, RB, 0
	g_MB = 0
	g_ME = 63

	return Rotate_iMask64(ea, g_mnem, g_RA, g_RS, g_RB, g_MB, g_ME)


def rldcr(ea, g_mnem, g_RA, g_RS, g_RB, g_ME):
	
	# Rotate Left Double Word then Clear Right
	# rldcr RA, RS, RB, ME
	g_MB = 0

	return Rotate_iMask64(ea, g_mnem, g_RA, g_RS, g_RB, g_MB, g_ME)


def rldic(ea, g_mnem, g_RA, g_RS, g_SH, g_MB):
	
	# Rotate Left Double Word Immediate then Clear
	# rldic RA, RS, SH, MB
	g_ME = 63 - g_SH

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rldicl(ea, g_mnem, g_RA, g_RS, g_SH, g_MB):
	
	# Rotate Left Double Word Immediate then Clear Left
	# rldicl RA, RS, SH, MB
	g_ME = 63
	
	if (simplify64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME) == 1):
		return 1
	
	if(search_for_matching_opcode_backwards(ea, "rldicl", g_RA, g_RS, g_SH) == 1):
		return 1
	
	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rldicr(ea, g_mnem, g_RA, g_RS, g_SH, g_ME):
	
	# Rotate Left Double Word Immediate then Clear Right
	# rldicr RA, RS, SH, ME
	g_MB = 0

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rldimi(ea, g_mnem, g_RA, g_RS, g_SH, g_MB):
	
	# Rotate Left Double Word Immediate then Mask Insert
	# rldimi RA, RS, SH, MB
	g_ME = 63 - g_SH

	return insert_iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def rlwimi(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME):
	
	# Rotate Left Word Immediate Then Mask Insert
	# rlwimi RA, RS, SH, MB, ME

	return insert_iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def slwi(ea, g_mnem, g_RA, g_RS, n):
	
	# Shift left immediate
	# slwi RA, RS, n
	# rlwinm RA, RS, n, 0, 31-n
	g_SH = n
	g_MB = 0
	g_ME = 31-n

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def srwi(ea, g_mnem, g_RA, g_RS, n):
	
	# Shift right immediate
	# srwi RA, RS, n
	# rlwinm RA, RS, 32-n, n, 31
	g_SH = 32-n
	g_MB = n
	g_ME = 31

	return iRotate_iMask32(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def sldi(ea, g_mnem, g_RA, g_RS, n):
	
	# Shift left double word immediate
	# sldi RA, RS, n
	# rldicr RA, RS, n, 63 - n
	g_SH = n
	g_MB = 0
	g_ME = 63-n

	if (simplify64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME) == 1):
		return 1

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def srdi(ea, g_mnem, g_RA, g_RS, n):
	
	# Shift right double word immediate
	# srdi RA, RS, n
	# rldicl RA, RS, 64 - n, n
	g_SH = 64-n
	g_MB = n
	g_ME = 63

	return iRotate_iMask64(ea, g_mnem, g_RA, g_RS, g_SH, g_MB, g_ME)


def mfocrf(ea, g_mnem, g_RT, g_FXM):
	
	# Move from One Condition Register Field
	# mfocrf RT, FXM

	#FXM is passed as str
	g_FXM  = int(g_FXM, 16)
	if ida_ida.inf_get_procname() != "ppcl":
		if g_FXM & 0x01 != 0:
			string = " = cr7 & 0x0000000F\nbit28 = LT, bit29 = GT, bit30 = EQ, bit31 = SO"
		elif g_FXM & 0x02 != 0:
			string = " = (cr6 << 4) & 0x000000F0\nbit24 = LT, bit25 = GT, bit26 = EQ, bit27 = SO"
		elif g_FXM & 0x04 != 0:
			string = " = (cr5 << 8) & 0x00000F00\nbit20 = LT, bit21 = GT, bit22 = EQ, bit23 = SO"
		elif g_FXM & 0x08 != 0:
			string = " = (cr4 << 12) & 0x0000F000\nbit16 = LT, bit17 = GT, bit18 = EQ, bit19 = SO"
		elif g_FXM & 0x10 != 0:
			string = " = (cr3 << 16) & 0x000F0000\nbit12 = LT, bit13 = GT, bit14 = EQ, bit15 = SO"
		elif g_FXM & 0x20 != 0:
			string = " = (cr2 << 20) & 0x00F00000\nbit8 = LT, bit9 = GT, bit10 = EQ, bit11 = SO"
		elif g_FXM & 0x40 != 0:
			string = " = (cr1 << 24) & 0x0F000000\nbit4 = LT, bit5 = GT, bit6 = EQ, bit7 = SO"
		elif g_FXM & 0x80 != 0:
			string = " = (cr0 << 28) & 0xF0000000\nbit0 = LT, bit1 = GT, bit2 = EQ, bit3 = SO"
		else:
			return 0
	return ".\n" + g_RT + string
	
	#LE new impl
	#if g_FXM & 0x01 != 0:
	#	string = " = (cr7 << 28) & 0xF0000000\nbit0 = LT, bit1 = GT, bit2 = EQ, bit3 = SO"
	#elif g_FXM & 0x02 != 0:
	#	string = " = (cr6 << 24) & 0x0F000000\nbit4 = LT, bit5 = GT, bit6 = EQ, bit7 = SO"
	#elif g_FXM & 0x04 != 0:
	#	string = " = (cr5 << 20) & 0x00F00000\nbit8 = LT, bit9 = GT, bit10 = EQ, bit11 = SO"
	#elif g_FXM & 0x08 != 0:
	#	string = " = (cr4 << 16) & 0x000F0000\nbit12 = LT, bit13 = GT, bit14 = EQ, bit15 = SO"
	#elif g_FXM & 0x10 != 0:
	#	string = " = (cr3 << 12) & 0x0000F000\nbit16 = LT, bit17 = GT, bit18 = EQ, bit19 = SO"
	#elif g_FXM & 0x20 != 0:
	#	string = " = (cr2 << 8) & 0x00000F00\nbit20 = LT, bit21 = GT, bit22 = EQ, bit23 = SO"
	#elif g_FXM & 0x40 != 0:
	#	string = " = (cr1 << 4) & 0x000000F0\nbit24 = LT, bit25 = GT, bit26 = EQ, bit27 = SO"
	#elif g_FXM & 0x80 != 0:
	#	string = " = cr0 & 0x0000000F\nbit28 = LT, bit29 = GT, bit30 = EQ, bit31 = SO"
	#else:
	#	return 0

def mfcr(ea, g_mnem, g_RT):

	string = ".\n"
	string += "cr7 bit28 = LT, bit29 = GT, bit30 = EQ, bit31 = SO\n"
	string += "cr6 bit24 = LT, bit25 = GT, bit26 = EQ, bit27 = SO\n"
	string += "cr5 bit20 = LT, bit21 = GT, bit22 = EQ, bit23 = SO\n"
	string += "cr4 bit16 = LT, bit17 = GT, bit18 = EQ, bit19 = SO\n"
	string += "cr3 bit12 = LT, bit13 = GT, bit14 = EQ, bit15 = SO\n"
	string += "cr2 bit8  = LT, bit9  = GT, bit10 = EQ, bit11 = SO\n"
	string += "cr1 bit4  = LT, bit5  = GT, bit6  = EQ, bit7  = SO\n"
	string += "cr0 bit0  = LT, bit1  = GT, bit2  = EQ, bit3  = SO\n"
	return string
# try to do as much work in this function as possible in order to
# simplify each "instruction" handling function
def PPCAsm2C(ea):
	
	# make sure address is valid and that it points to the start of an instruction
	if(ea == BADADDR):
		return False
	if(is_code(ida_bytes.get_flags(ea)) == 0):
		return False

	# get instruction mnemonic
	g_mnem = print_insn_mnem(ea)
	if(g_mnem == 0):
		return False
	
	# Remove record bit if exist,
	# we need to do this before testing for opcode validity.
	dot = "."
	dot = g_mnem.find(dot)
	if(dot != -1):
		g_mnem = g_mnem[0:dot]
	
	is_ok = False
	accepted = ["clrlwi", "clrldi", "clrrwi", "clrrdi", "clrlslwi", "clrlsldi",
				"extlwi", "extldi", "extrwi", "extrdi", "inslwi", "insrwi", "insrdi",
				"rlwinm", "rlwnm", "rotlw", "rotlwi", "rotrwi", "rotldi", "rotld", "rotrdi", "slwi", "srwi", "sldi",
				"srdi", "rldcr", "rldic", "rldicl", "rldicr", "rldimi", "rlwimi", "mfocrf", "mfcr"]
	for x in accepted:
		if (g_mnem == x):
			is_ok = True
			break
	if (is_ok == False):
		return False

	# get instruction operand strings
	# IDA only natively supports 3 operands
	g_opnd_s0 = print_operand(ea, 0)
	g_opnd_s1 = print_operand(ea, 1)
	g_opnd_s2 = print_operand(ea, 2)

	# use some string manipulation to extract additional operands
	# when more than 3 operands are used
	g_opnd_s4 = 0
	g_opnd_s3 = 0
	comma1 = ","
	comma1 = g_opnd_s2.find(comma1)
	if(comma1 != -1):
		# operand-3 exists
		g_opnd_s3 = g_opnd_s2[comma1+1:]
		g_opnd_s2 = g_opnd_s2[0:comma1]
		
		comma2 = ","
		comma2 = g_opnd_s3.find(comma2)
		if(comma2 != -1):
				# operand-4 exists
			g_opnd_s4 = g_opnd_s3[comma2+1:]
			g_opnd_s3 = g_opnd_s3[0:comma2]
			g_opnd_s4 = int(g_opnd_s4)
		g_opnd_s3 = int(g_opnd_s3)
	
	# convert s2 to int, except when s2 is reg nr.
	if (g_mnem not in ["rldcr", "rotlw",  "rotld", "rlwnm", "mfocrf", "mfcr"]):
		g_opnd_s2 = int(g_opnd_s2)
		
	# below is a list of supported instructions
	
	# clear
	if(g_mnem == "clrlwi"):
		return clrlwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "clrldi"):
		return clrldi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "clrrwi"):
		return clrrwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "clrrdi"):
		return clrrdi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "clrlslwi"):
		return clrlslwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "clrlsldi"):
		return clrlsldi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	
	# extract
	elif(g_mnem == "extlwi"):
		return extlwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "extldi"):
		return extldi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "extrwi"):
		return extrwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "extrdi"):
		return extrdi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	
	# insert
	elif(g_mnem == "inslwi"):
		return inslwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "insrwi"):
		return insrwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "insrdi"):
		return insrdi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)

	# rotate and insert
	elif(g_mnem == "rlwimi"):
		return rlwimi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3, g_opnd_s4)
	elif(g_mnem == "rldimi"):
		return rldimi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	
	# rotate and mask
	elif(g_mnem == "rlwinm"):
		return rlwinm(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3, g_opnd_s4)
	elif(g_mnem == "rlwnm"):
		return rlwnm(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3, g_opnd_s4)
	
	# rotate and clear
	elif(g_mnem == "rldcr"):
		return rldcr(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "rldic"):
		return rldic(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "rldicl"):
		return rldicl(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)
	elif(g_mnem == "rldicr"):
		return rldicr(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2, g_opnd_s3)

	# rotate
	elif(g_mnem == "rotlw"):
		return rotlw(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "rotlwi"):
		return rotlwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "rotrwi"):
		return rotrwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "rotld"):
		return rotld(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "rotldi"):
		return rotldi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "rotrdi"):
		return rotrdi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
		
	# shift
	elif(g_mnem == "slwi"):
		return slwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "srwi"):
		return srwi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "sldi"):
		return sldi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)
	elif(g_mnem == "srdi"):
		return srdi(ea, g_mnem, g_opnd_s0, g_opnd_s1, g_opnd_s2)

	#misc
	elif(g_mnem == "mfocrf"):
		return mfocrf(ea, g_mnem, g_opnd_s0, g_opnd_s1)
	elif(g_mnem == "mfcr"):
		return mfcr(ea, g_mnem, g_opnd_s0)
	return 0


def run_task(start_addr, end_addr, always_insert_comment):

	# convert all instructions within the bounds
	addr = start_addr
	while(addr < end_addr):
		print_str = PPCAsm2C(addr)
		if(print_str != 0 and print_str != 1):
			set_cmt(addr, print_str, False)
		elif (print_str == 0 and always_insert_comment == True):
			msg("0x{:X}: Error converting PPC to C code\n".format(addr))
		addr += 4


def PluginMain():
	
	# select current line or selected lines
	always_insert_comment = False
	start_addr = read_selection_start()
	end_addr = read_selection_end()
	if(start_addr == BADADDR):
		start_addr = get_screen_ea();
		end_addr = start_addr + 4;
		always_insert_comment = True
	
	run_task(start_addr, end_addr, always_insert_comment)


def PluginMainF():
	
	# convert current function
	p_func = get_func(get_screen_ea());
	if(type(p_func) == type(None)):
		msg("Not in a function, so can't do PPC to C conversion for the current function!\n");
		return;
	start_addr = p_func.start_ea;
	end_addr = p_func.end_ea;
	always_insert_comment = False;
	
	run_task(start_addr, end_addr, always_insert_comment)


#/***************************************************************************************************
#*
#*	Strings required for IDA Pro's PLUGIN descriptor block
#*
#***************************************************************************************************/
#
G_PLUGIN_COMMENT = "PPC To C Conversion Assist"
G_PLUGIN_HELP = "This plugin assists in converting PPC instructions into their relevant C code.\nIt is especially useful for the tricky bit manipulation and shift instructions.\n"
G_PLUGIN_NAME = "PPC To C: Selected Lines"

#/***************************************************************************************************
#*
#*	This 'PLUGIN' data block is how IDA Pro interfaces with this plugin.
#*
#***************************************************************************************************/

class ActionHandler(idaapi.action_handler_t):

    def __init__(self, callback):
        
        idaapi.action_handler_t.__init__(self)
        self.callback = callback
    
    def activate(self, ctx):

        self.callback()
        return 1

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

def register_actions():   

    actions = [
        {
            'id': 'start:plg',
            'name': G_PLUGIN_NAME,
            'hotkey': 'F10',
            'comment': G_PLUGIN_COMMENT,
            'callback': PluginMain,
            'menu_location': 'Start Plg'
        },
        {
            'id': 'start:plg1',
            'name': 'pypyc2c unimplemented',
            'hotkey': 'Alt-Shift-F10',
            'comment': G_PLUGIN_COMMENT,
            'callback': PluginMainF,
            'menu_location': 'Start Plg1'
        }
    ]

    for action in actions:

        if not idaapi.register_action(idaapi.action_desc_t(
            action['id'], # Must be the unique item
            action['name'], # The name the user sees
            ActionHandler(action['callback']), # The function to call
            action['hotkey'], # A shortcut, if any (optional)
            action['comment'] # A comment, if any (optional)
        )):

            print('Failed to register ' + action['id'])

        if not idaapi.attach_action_to_menu(
            action['menu_location'], # The menu location
            action['id'], # The unique function ID
            0):

            print('Failed to attach to menu '+ action['id'])

class ppc_helper_t(idaapi.plugin_t):
	flags = idaapi.PLUGIN_HIDE
	comment = G_PLUGIN_COMMENT
	help = G_PLUGIN_HELP
	wanted_name = G_PLUGIN_NAME
	wanted_hotkey = "F10"

	def init(self):
		if (idaapi.ph.id == idaapi.PLFM_PPC):
			register_actions()
			idaapi.msg("pypyc2c: loaded\n")
			return idaapi.PLUGIN_KEEP

		return idaapi.PLUGIN_SKIP
	
	def run(self, arg):
		idaapi.msg("pypyc2c: run\n")
	
	def term(self):
		pass

def PLUGIN_ENTRY():
	return ppc_helper_t()


repository: stevemk14ebr/RETools
file: IdaScripts/plugins/capa_explorer.py
description: My reversing tools. Some custom, some not.
created_at: 2020-05-27 21:10:30+00:00
pushed_at: 2024-01-03 21:54:17+00:00
url:
https://github.com/stevemk14ebr/RETools/blob/9501332bf02688d3a4b31d85a25349671767be98/IdaScripts/plugins/capa_explorer.py

content:
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
# You may obtain a copy of the License at: [package root]/LICENSE.txt
# Unless required by applicable law or agreed to in writing, software distributed under the License
#  is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.

from capa.ida.plugin import CapaExplorerPlugin


def PLUGIN_ENTRY():
    """mandatory entry point for IDAPython plugins

    copy this script to your IDA plugins directory and start the plugin by navigating to Edit > Plugins in IDA Pro
    """
    return CapaExplorerPlugin()


repository: clayne/ida-hexrays_hlight3
file: hexrays_hlight.py
description: ida python hexrays_hlight plugin now support python3
wanted_name: HexLight
comment: highlights the matching brace in Pseudocode-View
created_at: 2022-04-30 18:04:15+00:00
pushed_at: 2024-08-26 18:53:24+00:00
url:
https://github.com/clayne/ida-hexrays_hlight3/blob/ee933e84d7686a7f7700743403fbea4acd131224/hexrays_hlight.py

content:
# highlighting plugin for Hex-Rays Decompiler
# Copyright (c) 2016
# Milan Bohacek <milan.bohacek+hexlight@gmail.com>
# All rights reserved.
# 
# ==============================================================================
# 
# This file is part of Hexlight.
# 
# Hexlight is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 
# ==============================================================================
 
 
import idautils
import idaapi
import idc
 
 
import traceback
 
hexlight_cb_info = None
hexlight_cb = None
 
posledni = 0
 
def jump(custom_viewer, line):
    (pl, x, y) = idaapi.get_custom_viewer_place(custom_viewer, False)
    pl2 = idaapi.place_t_as_simpleline_place_t(pl.clone())
    oldline = pl2.n
    pl2.n = line
    idaapi.jumpto(custom_viewer, pl2, x, y)
    return oldline
 
class hexrays_callback_info(object):
     
    def __init__(self):
        self.vu = None
        self.highlights = {}
        self.highl_brack = {}
        self.hicolor = 0x646464       #0xF2E8BF #0x00ffff00
        self.theotherline = None
        self.safe = False
        return
     
    def clearall(self, ps, refresh=True):
        ctr = 0
        for i in self.highlights:
            try:            
                ps[i].bgcolor = self.highlights[i]
                ctr += 1
            except:
                pass
#               place_t_as_simpleline_place_t
#              ps[i].line.replace("\x04{\x04", "{")
#              ps[i].line.replace("\x04}\x04", "}")
                 
        self.highlights = {}
        self.theotherline = None
        if((ctr > 0) and refresh):
            idaapi.refresh_idaview_anyway()
 
    def clearbracket(self, ps, refresh=True):
        ctr = 0
        for i in self.highl_brack:
            try:            
                ps[i].line = self.highl_brack[i]
                ctr += 1
                #print('clear' + ps[i].line)
            except:
                pass
             
        self.highl_brack = {}
        if((ctr > 0) and refresh):
            idaapi.refresh_idaview_anyway()     
             
    def highlight_bracket2(self, ps, pos_brach, xpos, ypos):
        ln = ps[ypos].line[:]
        # print("ypos",ypos)
        # print("highl_brack",self.highl_brack)
        if (ypos not in self.highl_brack):
            self.clearbracket(ps, True)
            self.highl_brack[ypos] = ln
        else:
            ln = self.highl_brack[ypos]
 
        s1pos = idaapi.tag_advance(ln, pos_brach)
        s2pos = idaapi.tag_advance(ln, xpos)
        line = list(ln)
        while (line[s1pos] != idaapi.SCOLOR_ON or line[s1pos+1] != idaapi.SCOLOR_SYMBOL):
            s1pos += 1
            if (s1pos > len(line)):
                return
        while (line[s2pos] != idaapi.SCOLOR_ON or line[s2pos+1] != idaapi.SCOLOR_SYMBOL):
            s2pos += 1
            if (s2pos > len(line)):
                return
 
        line[s1pos+1] = idaapi.SCOLOR_ERROR
        line[s1pos+4] = idaapi.SCOLOR_ERROR
        line[s2pos+1] = idaapi.SCOLOR_ERROR
        line[s2pos+4] = idaapi.SCOLOR_ERROR
        ps[ypos].line = ''.join(line)        
        idaapi.refresh_idaview_anyway()  
     
    def rfind_match_brack(self, start, strline, brack1, brack2):
        i = 0
        while (start >= 0) :
            if (strline[start] == brack1):
                i = i + 1
            elif (strline[start] == brack2):
                i = i - 1
            if (i == 0) :
                #find match
                return start 
            start = start - 1
             
        return -1    
         
    def find_match_brack(self, start, strline, brack1, brack2):
        i = 0
        while (start < len(strline)) :
            if (strline[start] == brack1):
                i = i + 1
            elif (strline[start] == brack2):
                i = i - 1
            if (i == 0) :
                #find match
                return start 
            start = start + 1
             
        return -1                   
 
    def event_callback(self, event, *args):
        try:
            # print("event: %d"%event)
            if event == idaapi.hxe_keyboard:
                vu, keycode, shift = args
 
                if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("B") and shift == 0:
                    if self.theotherline:
                        self.theotherline = jump(vu.ct, self.theotherline)
                    return 0
 
            # print("查看event")
            if event <= idaapi.hxe_print_func:
                self.safe = False
 
            if event == idaapi.hxe_switch_pseudocode:
                self.safe = False
 
            if event == idaapi.hxe_func_printed:
                self.safe = True
 
            if event == idaapi.hxe_text_ready:
                self.safe = True
 
            if event == idaapi.hxe_curpos:
                if not self.safe:
                    return 0
                #print "1"
                self.vu = args[0]
 
                if not self.vu:
                    return 0
                #print "2"
 
                if self.vu.cfunc.maturity != idaapi.CMAT_FINAL:
                    return 0
                #print "3"
 
                if not self.vu.visible():
                    return 0
                #print "4"
                if not self.vu.refresh_cpos(idaapi.USE_KEYBOARD):
                 #   print "refresh_cpos failed"
                    return 0
                pos = self.vu.cpos
                ypos = pos.lnnum
                xpos = pos.x
                # print("ypos,xpos",ypos,xpos)
                #print "cursor click %d %d %d" % (pos.x, pos.y, pos.lnnum)
 
                
                if ypos in self.highlights:
                    return  0
                #print "5"
 
                ps = self.vu.cfunc.get_pseudocode()
                #print "6"
                #print "ypos:%d"%ypos
                #print "ps[ypos].line: %s"%(ps[ypos].line)
 
                #line = [idaapi.COLSTR("[%02d]"%i, chr(i)) for i in
                #range(1,0x40) ]
                #ps[0].line = ''.join(line);
                #ps[1].line = '\x04'.join(line);
                #line = [idaapi.COLSTR( idaapi.COLSTR("[ \x04%02d\x04 ]"%i,
                #chr(i)), chr(i+1)) for i in range(1,0x40) ]
                #ps[2].line = ''.join(line);
                #ps[3].line = '\x04'.join(line);
                ln = ps[ypos].line[:]
                curline = idaapi.tag_remove(ln)
                #print "7"
 
                #print curline
                 
                if (xpos > 1 and xpos <= len(curline)):
                    chPrev = curline[xpos - 1]
                    # print("chPrev",chPrev)
                     
                    if (chPrev == ')'):
                        pos_brach = self.rfind_match_brack(xpos - 1, curline, ')', '(')
                        if (pos_brach != -1) :
                            self.highlight_bracket2(ps, pos_brach, xpos-1, ypos)
                    elif (chPrev == '('):
                        pos_brach = self.find_match_brack(xpos - 1, curline, '(', ')')
                        if (pos_brach != -1) :
                            self.highlight_bracket2(ps, pos_brach, xpos-1, ypos)
                    elif (chPrev == ']'):
                        pos_brach = self.rfind_match_brack(xpos - 1, curline, ']', '[')
                        if (pos_brach != -1) :
                            self.highlight_bracket2(ps, pos_brach, xpos-1, ypos)
                    elif (chPrev == '['):
                        pos_brach = self.find_match_brack(xpos - 1, curline, '[', ']')
                        if (pos_brach != -1) :
                            self.highlight_bracket2(ps, pos_brach, xpos-1, ypos)
                    else:
                        self.clearbracket(ps, True)
                else:
                    self.clearbracket(ps, True)
                        
                idxO = curline.find('{')
                idxC = curline.find('}')
                #print "O:", idxO, " C: ",idxC
                #there is no need to highlight first and last {
                #print "8"
 
                if (idxO >= 0) or (idxC >= 0):
                #   print "9"
                    self.clearall(ps, False)
 
                    self.highlights[ypos] = ps[ypos].bgcolor
 
                    ps[ypos].bgcolor = self.hicolor
                     
                    dir = 1
                    bracechar = '}'
                    idx = idxO
 
                    if (idxC >= 0):
                        dir = -1
                        bracechar = '{'
                        idx = idxC
 
                    j = ypos + dir
 
                    max = len(ps)
                 #   print "max: ",max
 
                    while (j >= 0) and (j < max):
                #       print "10"
                        #print "j:", j
                        ln = idaapi.tag_remove(ps[j].line)
                        if ln.find(bracechar) == idx:
                            if not(j in self.highlights):
                                self.highlights[j] = ps[j].bgcolor
                            #ps[j].line = ps[j].line.replace(bracechar,
                            #idaapi.COLSTR("\x04"+bracechar+"\x04", "\x27"))
                            #ps[j].line = ps[j].line.replace(bracechar,
                            #idaapi.COLSTR(bracechar, chr(52)))
                            ps[j].bgcolor = self.hicolor
                            self.theotherline = j
                            break
                        j+=dir
                     
                    idaapi.refresh_idaview_anyway()
                else:
                    self.clearall(ps)
                #print "11"
                return 0
        except:
            traceback.print_exc()
         
        return 0
 
def remove():
    if hexlight_cb:
        idaapi.remove_hexrays_callback(hexlight_cb)
 
class HexHLightPlugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_HIDE
    comment = "highlights the matching brace in Pseudocode-View"
    help = "press B to jump to the matching brace"
    wanted_name = "HexLight"
    wanted_hotkey = ""
 
    def init(self):
        # Some initialization
        global hexlight_cb_info, hexlight_cb
 
        if idaapi.init_hexrays_plugin():
            hexlight_cb_info = hexrays_callback_info()
            hexlight_cb = hexlight_cb_info.event_callback
            if not idaapi.install_hexrays_callback(hexlight_cb):
            #    print "could not install hexrays_callback"
                return idaapi.PLUGIN_SKIP
            print("Hexlight plugin installed Mod by Snow;modify by p1ay8y3ar")
            addon = idaapi.addon_info_t()
            addon.id = "milan.bohacek.hexlight"
            addon.name = "Hexlight"
            addon.producer = "p1ay8y3ar"
            addon.url = "https://github.com/p1ay8y3ar"
            addon.version = "7.6"
            idaapi.register_addon(addon)
            return idaapi.PLUGIN_KEEP
        #print "init_hexrays_plugin failed"
        return idaapi.PLUGIN_SKIP
 
    def run(self, arg=0):
        return
 
    def term(self):
        remove()
 
def PLUGIN_ENTRY():
    return HexHLightPlugin_t()


repository: IDAPluginProject/d810
file: D810.py
url: https://github.com/IDAPluginProject/d810/blob/30ff59e3c068d1b12335a9a3d7686e0211e89f24/D810.py
wanted_name: D-810
comment: Interface to the D-810 plugin
created_at: 2022-04-07 12:42:47+00:00
pushed_at: 2024-07-30 06:12:33+00:00
description:
D-810 is an IDA Pro plugin which can be used to deobfuscate code at decompilation time by modifying IDA Pro microcode.

content:
import os
import idaapi
import ida_hexrays
import ida_kernwin


from d810.conf import D810Configuration
from d810.manager import D810State, D810_LOG_DIR_NAME
from d810.log import configure_loggers, clear_logs


D810_VERSION = "0.1"

class D810Plugin(idaapi.plugin_t):
    # variables required by IDA
    flags = 0  # normal plugin
    wanted_name = "D-810"
    wanted_hotkey = "Ctrl-Shift-D"
    comment = "Interface to the D-810 plugin"
    help = ""
    initialized = False

    def __init__(self):
        super(D810Plugin, self).__init__()
        self.d810_config = None
        self.state = None
        self.initialized = False


    def reload_plugin(self):
        if self.initialized:
            self.term()

        self.d810_config = D810Configuration()

        #TO-DO: if [...].get raises an exception because log_dir is not found, handle exception
        real_log_dir = os.path.join(self.d810_config.get("log_dir"), D810_LOG_DIR_NAME)

        #TO-DO: if [...].get raises an exception because erase_logs_on_reload is not found, handle exception
        if self.d810_config.get("erase_logs_on_reload"):
            clear_logs(real_log_dir)

        configure_loggers(real_log_dir)
        self.state = D810State(self.d810_config)
        print("D-810 reloading...")
        self.state.start_plugin()
        self.initialized = True


    # IDA API methods: init, run, term
    def init(self):
        if not ida_hexrays.init_hexrays_plugin():
            print("D-810 need Hex-Rays decompiler. Skipping")
            return idaapi.PLUGIN_SKIP

        kv = ida_kernwin.get_kernel_version().split(".")
        if (int(kv[0]) < 7) or ((int(kv[0]) == 7) and (int(kv[1]) < 5)):
            print("D-810 need IDA version >= 7.5. Skipping")
            return idaapi.PLUGIN_SKIP
        print("D-810 initialized (version {0})".format(D810_VERSION))
        return idaapi.PLUGIN_OK


    def run(self, args):
        self.reload_plugin()


    def term(self):
        print("Terminating D-810...")
        if self.state is not None:
            self.state.stop_plugin()

        self.initialized = False


def PLUGIN_ENTRY():
    return D810Plugin()


repository: KarelSvo/ddt4all-5.6.0
file: ddt4all560/ddtplugins/card_programming.py
created_at: 2024-07-27 18:43:17+00:00
pushed_at: 2025-01-15 16:37:47+00:00
url:
https://github.com/KarelSvo/ddt4all-5.6.0/blob/e33b248ed57fb29ba5428cee8a81b8aa718b44f8/ddt4all560/ddtplugins/card_programming.py

content:
# -*- coding: utf-8 -*-

# Plugin to program keys on Megane II
# (c) 2017
# This is an example plugin


import PyQt4.QtGui as gui
import PyQt4.QtCore as core
import ecu
import elm
import options

_ = options.translator('ddt4all')

plugin_name = _("Megane/Scenic II card programming")
category = _("Keys")
need_hw = True
ecufile = "UCH_84_J84_03_60"

a8_mess = [19, 29, 39, 4, 24, 46, 31, 12,
           16, 28, 7, 10, 15, 0, 40, 26,
           18, 20, 3, 8, 34, 8, 47, 21,
           2, 25, 38, 6, 4, 22, 14, 33,
           17, 1, 44, 11, 32, 41, 36, 42,
           5, 45, 5, 43, 30, 37, 13, 27]

a8_xor = [0x10, 0x20, 0x20, 0x10, 0x81, 0x88]
a8_xor_2 = [0x10, 0x20, 0x28, 0x10, 0x81, 0x88]


def a8(isk_c):
    '''MC9S12DG256 (Megane 2, Scenic 2)'''
    apv = ''
    isk = isk_c.replace(' ', '').decode('hex')
    if len(isk) != 6:
        return None
    bit_i = ''
    for c in isk:
        bit_i += bin(ord(c))[2:].zfill(8)

    # if not (bit_i[ 4]!=bit_i[23] and bit_i[12]==bit_i[43] and
    #        bit_i[ 4]==bit_i[35] and bit_i[ 8]==bit_i[ 9] and bit_i[ 4]==bit_i[ 5]):
    #  return 'Invalid ISK'

    base = ''
    for b in a8_xor:
        base = base + bin(b)[2:].zfill(8)

    i = 0
    for b in base:
        z = bit_i[a8_mess[i]]
        if b != z:
            z = '1'
        else:
            z = '0'
        base = base[:i] + z + base[i + 1:]
        i += 1

    for i in range(0, 6):
        apv += hex(int(base[i * 8:i * 8 + 8], 2))[2:].zfill(2).upper() + ' '

    return apv

def a8_2(isk_c):
    '''MC9S12DG256 (algo2)(Megane 2, Scenic 2)'''
    apv = ''
    isk = isk_c.replace(' ', '').decode('hex')
    if len(isk) != 6:
        return None
    bit_i = ''
    for c in isk:
        bit_i += bin(ord(c))[2:].zfill(8)

    base = ''
    for b in a8_xor_2:
        base = base + bin(b)[2:].zfill(8)

    i = 0
    for b in base:
        z = bit_i[a8_mess[i]]
        if b != z:
            z = '1'
        else:
            z = '0'
        base = base[:i] + z + base[i + 1:]
        i += 1

    for i in range(0, 6):
        apv += hex(int(base[i * 8:i * 8 + 8], 2))[2:].zfill(2).upper() + ' '

    return apv


def get_isk(ecu_response):
    ecu_response = ecu_response.replace(' ', '').strip()
    return ecu_response[19 * 2:25 * 2]


class CardProg(gui.QDialog):
    def __init__(self):
        super(CardProg, self).__init__()
        options.debug = True
        self.apvok = False
        self.megane_ecu = ecu.Ecu_file(ecufile, True)
        self.start_session_request = self.megane_ecu.requests[u'Start Diagnostic Session']
        self.after_sale_request = self.megane_ecu.requests[u'ACCEDER AU MODE APRES-VENTE']
        self.learn_key_request = self.megane_ecu.requests[u'APPRENDRE BADGE']
        self.key_bits_status = self.megane_ecu.requests[u'Status général des opérations badges Bits']
        self.key_bytes_status = self.megane_ecu.requests[u'Status général des opérations badges Octets']
        self.reserved_frame_request = self.megane_ecu.requests[u'Trame AB: Trame réservée']
        self.learn_validate_request = self.megane_ecu.requests[u'SORTIE DU MODE APV : VALIDATION']
        self.learn_cancel_request = self.megane_ecu.requests[u'SORTIE DU MODE APV : ABANDON']

        self.ecu_connect()
        self.start_diag_session()

        self.apv_access_button = gui.QPushButton(_("ENTER AFTER SALE MODE"))
        self.apv_access_button.clicked.connect(self.set_apv_from_input)
        self.pininput = gui.QLineEdit()
        self.pininput.setText("0" * 12)
        self.pininput.setInputMask("H" * 12)
        self.iskoutput = gui.QLineEdit()
        self.iskoutput.setInputMask("H" * 12)
        self.numkeys_label = gui.QLabel("0")
        self.currentcardide = gui.QLineEdit()
        self.currentcardide.setReadOnly(True)
        self.apv_status = gui.QLabel("")
        self.apv_status.setAlignment(core.Qt.AlignHCenter)
        label = gui.QLabel(_("<font color='red'>MEGANE II CARD PROGRAMMING<br>EXPERIMENTAL : NOT TESTED YET</font>"))
        label.setAlignment(core.Qt.AlignHCenter)
        self.learnbutton = gui.QPushButton(_("LEARN"))
        self.validatebutton = gui.QPushButton(_("VALIDATE"))
        self.cancelbutton = gui.QPushButton(_("CANCEL"))

        self.algocheck = gui.QCheckBox(_("Algo 2"))
        self.algocheck.setChecked(False)

        self.learnbutton.clicked.connect(self.learn_action)
        self.validatebutton.clicked.connect(self.validate_action)
        self.cancelbutton.clicked.connect(self.cancel_action)
        self.iskoutput.textChanged.connect(self.calculate_pin)
        self.algocheck.toggled.connect(self.calculate_pin)

        layout = gui.QGridLayout()
        layout.addWidget(label, 0, 0, 1, 0)
        layout.addWidget(gui.QLabel(_("ISK")), 1, 0)
        layout.addWidget(self.iskoutput, 1, 1)
        layout.addWidget(gui.QLabel(_("PIN")), 2, 0)
        layout.addWidget(self.pininput, 2, 1)
        layout.addWidget(self.algocheck, 3, 1)
        layout.addWidget(self.apv_access_button, 4, 0, 1, 0)
        layout.addWidget(self.apv_status, 5, 0, 1, 0)
        layout.addWidget(gui.QLabel(_("Num key learnt")), 6, 0)
        layout.addWidget(self.numkeys_label, 6, 1)
        layout.addWidget(gui.QLabel(_("CARD IDE")), 7, 0)
        layout.addWidget(self.currentcardide, 7, 1)
        layout.addWidget(self.learnbutton, 8, 0, 1, 0)
        layout.addWidget(self.validatebutton, 9, 0)
        layout.addWidget(self.cancelbutton, 9, 1)

        self.setLayout(layout)

        # I have to use a timer here to avoid ECU from escaping session mode
        # A shot every 1.5 seconds is enough, normally
        self.tester_timer = core.QTimer()
        self.tester_timer.setSingleShot(False)
        self.tester_timer.setInterval(1500)
        self.tester_timer.timeout.connect(self.check_all)

        isk = self.get_isk()
        self.iskoutput.setText(isk)

        self.tester_timer.start()

    def calculate_pin(self):
        ISK = str(self.iskoutput.text().toAscii())
        if len(ISK) == 12:
            if self.algocheck.checkState():
                PIN = a8_2(ISK)
            else:
                PIN = a8(ISK)
            if PIN is not None:
                self.pininput.setText(PIN)
                self.iskoutput.setStyleSheet("color: green;")
                return

        self.pininput.setText("000000000000")
        self.iskoutput.setStyleSheet("color: red;")

    def learn_action(self):
        self.learn_key_request.send_request()

    def validate_action(self):
        self.learn_validate_request.send_request()

    def cancel_action(self):
        self.learn_cancel_request.send_request()

    def get_isk(self):
        # No dataitem for ISK :(
        # Need to decode by myself
        if options.simulation_mode:
            reply = "61 AB 02 FC 0D 08 51 4C 86 55 54 00 00 00 00 00 00 00 00 8D E8 EE 16 79 D3 C9 A7 A7 CC " \
                    "F6 AC 00 00 00 00 00 00 2A"
        else:
            reply = options.elm.request(self.reserved_frame_request.sentbytes)

        if reply.startswith('7F') or reply.startswith('WRONG'):
            options.main_window.logview.append(_("Cannot get ISK, check connections and UCH compatibility"))
            return

        isk = reply.replace(' ', '').strip()
        return get_isk(isk)

    def ecu_connect(self):
        connection = self.megane_ecu.connect_to_hardware()
        if not connection:
            options.main_window.logview.append(_("Cannot connect to ECU"))
            self.finished()

    def check_all(self):
        self.check_apv_status()
        self.check_num_key_learnt()

    def set_apv_from_input(self):
        apv = str(self.pininput.text().toAscii())
        if len(apv) != 12:
            return
        self.after_sale_request.send_request({u'Code APV': apv})

    def enable_buttons(self, enable):
        self.learnbutton.setEnabled(enable)
        self.validatebutton.setEnabled(enable)
        self.cancelbutton.setEnabled(enable)

    def check_apv_status(self):
        key_bits_status_values = self.key_bits_status.send_request({}, "61 06 21 21 20 00 00 00 08 00 00 80 03")

        value_apv_ok = key_bits_status_values[u'VSC Code APV_Reconnu']
        value_apv_reaff = key_bits_status_values[u'VSC ModeAPV_ReaffArmé']
        value_apv_uch_reaff = key_bits_status_values[u'VSC ModeAPV_AppUCH_Armé']

        if value_apv_ok == '0':
            self.apv_status.setText(_("<font color='red'>PIN CODE NOT RECOGNIZED</font>"))
            self.apvok = False
            self.enable_buttons(False)
            return

        if value_apv_reaff == '1':
            self.apv_status.setText(_("<font color='green'>PIN CODE OK / KEY LEARNING</font>"))
            self.enable_buttons(True)
            self.apvok = True
            return

        elif value_apv_uch_reaff == '1':
            self.apv_status.setText(_("<font color='green'>PIN CODE OK / UCH LEARNING</font>"))
            self.enable_buttons(True)
            self.apvok = True
            return

        self.apvok = False
        self.enable_buttons(False)

        self.apv_status.setText("<font color='red'>UCH NOT READY</font>")

    def check_num_key_learnt(self):
        key_bytes_status_value = self.key_bytes_status.send_request({}, "61 08 00 00 00 00 00 00 0A 00 00 00 00 00 00 "
                                                                        "00 00 00 00 00 00 00 00 00 00 02 00 00 01 2C "
                                                                        "00 00 01 2C 00 00 2C 00 00 01 2C")

        num_key_learnt = key_bytes_status_value[u'VSC NbTotalDeBadgeAppris']
        current_key_ide = key_bytes_status_value[u'VSC Code_IDE']

        self.numkeys_label.setText(num_key_learnt)
        self.currentcardide.setText(current_key_ide)

    def start_diag_session(self):
        sds_request = self.megane_ecu.requests[u"Start Diagnostic Session"]
        sds_stream = " ".join(sds_request.build_data_stream({}))
        if options.simulation_mode:
            print "SdS stream", sds_stream
            return
        options.elm.start_session_can(sds_stream)

def plugin_entry():
    cp = CardProg()
    cp.exec_()


repository: veritas501/pwndbg_ida_xmlrpc
file: pwndbg_xmlrpc.py
description: plugin format https://github.com/pwndbg/pwndbg/blob/dev/ida_script.py
wanted_name: Pwndbg XML RPC
comment: Pwndbg XML RPC plugin
created_at: 2020-11-04 08:58:46+00:00
pushed_at: 2020-11-04 09:00:28+00:00
url:
https://github.com/veritas501/pwndbg_ida_xmlrpc/blob/4b85e7c621662c0161d2c713e974988d48a3f23e/pwndbg_xmlrpc.py

content:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function

import datetime
import threading
import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer
from xml.sax.saxutils import escape

import idaapi
import idautils
import idc

server = None
thread = None

DEBUG_MARSHALLING = False


def create_marshaller(use_format=None, just_to_str=False):
    assert use_format or just_to_str, 'Either pass format to use or make it converting the value to str.'

    def wrapper(_marshaller, value, appender):
        if use_format:
            marshalled = use_format % value
        elif just_to_str:
            marshalled = '<value><string>%s</string></value>' % escape(
                str(value))

        if DEBUG_MARSHALLING:
            print("Marshalled: '%s'" % marshalled)

        appender(marshalled)

    return wrapper


xmlrpclib.Marshaller.dispatch[type(0L)] = create_marshaller("<value><i8>%d</i8></value>")
xmlrpclib.Marshaller.dispatch[type(0)] = create_marshaller(
    "<value><i8>%d</i8></value>")
xmlrpclib.Marshaller.dispatch[idaapi.cfuncptr_t] = create_marshaller(
    just_to_str=True)

orig_LineA = None


def mod_LineA(*a, **kw):
    global orig_LineA
    v = orig_LineA(*a, **kw)
    if v and v.startswith('\x01\x04; '):
        v = v[4:]
    return v


mutex = threading.Condition()


def wrap(f):
    def wrapper(*a, **kw):
        rv = []
        error = []

        def work():
            try:
                result = f(*a, **kw)
                rv.append(result)
            except Exception as e:
                error.append(e)

        with mutex:
            flags = idaapi.MFF_WRITE
            if f == idc.SetColor:
                flags |= idaapi.MFF_NOWAIT
                rv.append(None)
            idaapi.execute_sync(work, flags)

        if error:
            msg = 'Failed on calling {}.{} with args: {}, kwargs: {}\nException: {}' \
                .format(f.__module__, f.__name__, a, kw, str(error[0]))
            print('[!!!] ERROR:', msg)
            raise error[0]

        return rv[0]

    return wrapper


def decompile(addr):
    """
    Function that overwrites `idaapi.decompile` for xmlrpc so that instead
    of throwing an exception on `idaapi.DecompilationFailure` it just returns `None`.
    (so that we don't have to parse xmlrpc Fault's exception string on pwndbg side
    as it differs between IDA versions).
    """
    try:
        return idaapi.decompile(addr)
    except idaapi.DecompilationFailure:
        return None


def get_decompile_coord_by_ea(cfunc, addr):
    if idaapi.IDA_SDK_VERSION >= 720:
        item = cfunc.body.find_closest_addr(addr)
        y_holder = idaapi.int_pointer()
        if not cfunc.find_item_coords(item, None, y_holder):
            return None
        y = y_holder.value()
    else:
        lnmap = {}
        for i, line in enumerate(cfunc.pseudocode):
            phead = idaapi.ctree_item_t()
            pitem = idaapi.ctree_item_t()
            ptail = idaapi.ctree_item_t()
            ret = cfunc.get_line_item(line.line, 0, True, phead, pitem, ptail)
            if ret and pitem.it:
                lnmap[pitem.it.ea] = i
        y = None
        closest_ea = BADADDR
        for ea, line in lnmap.items():
            if closest_ea == BADADDR or abs(closest_ea - addr) > abs(ea - addr):
                closest_ea = ea
                y = lnmap[ea]

    return y


def register_module(module):
    for name, function in module.__dict__.items():
        if hasattr(function, '__call__'):
            server.register_function(wrap(function), name)


def decompile_context(addr, context_lines):
    cfunc = decompile(addr)
    if cfunc is None:
        return None
    y = get_decompile_coord_by_ea(cfunc, addr)
    if y is None:
        return cfunc
    lines = cfunc.get_pseudocode()
    retlines = []
    for lnnum in range(max(0, y - context_lines), min(len(lines), y + context_lines)):
        retlines.append(idaapi.tag_remove(lines[lnnum].line))
        if lnnum == y:
            retlines[-1] = '>' + retlines[-1][1:]
    return '\n'.join(retlines)


def versions():
    """Returns IDA & Python versions"""
    import sys
    return {
        'python': sys.version,
        'ida': idaapi.get_kernel_version(),
        'hexrays': idaapi.get_hexrays_version() if idaapi.init_hexrays_plugin() else None
    }


class PwndbgRPC:
    def __init__(self):
        global mod_LineA, orig_LineA
        self.host = '0.0.0.0'
        self.port = 31337
        if idc.LineA != mod_LineA:
            orig_LineA = idc.LineA
            idc.LineA = mod_LineA

    def set_host(self, hostname):
        self.host = hostname

    def set_port(self, port):
        self.port = port

    def start_server(self):
        global server
        global thread
        server = SimpleXMLRPCServer(
            (self.host, self.port), logRequests=True, allow_none=True)
        register_module(idc)
        register_module(idautils)
        register_module(idaapi)
        server.register_function(lambda a: eval(
            a, globals(), locals()), 'eval')
        # overwrites idaapi/ida_hexrays.decompile
        server.register_function(wrap(decompile))
        # support context decompile
        server.register_function(wrap(decompile_context), 'decompile_context')
        server.register_function(versions)
        server.register_introspection_functions()

        print('IDA Pro xmlrpc hosted on http://%s:%s' % (self.host, self.port))

        thread = threading.Thread(target=server.serve_forever)
        thread.daemon = True
        thread.start()

    def stop_server(self):
        global server, thread
        server.shutdown()
        server.server_close()


class PwndbgRPC_Form(idaapi.Form):
    def __init__(self):
        idaapi.Form.__init__(self,
                             r"""STARTITEM 0
BUTTON YES* Start
BUTTON NO* Stop
Pwndbg XML RPC
        {FormChangeCb}
        <Host:{host}>
        <Port:{port}>
        """, {
                                 'host': self.StringInput(value='0.0.0.0', swidth=20),
                                 'port': self.NumericInput(value=31337, swidth=20, tp=self.FT_DEC),
                                 'FormChangeCb': self.FormChangeCb(self.OnFormChange)
                             })

        self.Compile()

    # callback to be executed when any form control changed
    def OnFormChange(self, fid):
        return 1


class PwndbgRPC_Plugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = 'Pwndbg XML RPC plugin'
    help = ''
    wanted_name = 'Pwndbg XML RPC'
    wanted_hotkey = ''

    def init(self):
        idaapi.msg('[*] Pwndbg XML RPC plugin loaded.\n')

        if idaapi.init_hexrays_plugin():
            addon = idaapi.addon_info_t()
            addon.id = "com.pwndbg.pwndbg"
            addon.name = "Pwndbg XML RPC"
            addon.producer = "pwndbg"
            addon.url = "https://github.com/pwndbg/pwndbg"
            addon.version = "1.0.0.0"
            idaapi.register_addon(addon)

        self.pwndbgRPC = PwndbgRPC()

        return idaapi.PLUGIN_KEEP

    def term(self):
        pass

    def run(self, arg):
        f = PwndbgRPC_Form()
        btn_status = f.Execute()
        if btn_status == 1:  # start
            print("[*] pwndbg XML RPC started")
            self.pwndbgRPC.set_host(f.host.value)
            self.pwndbgRPC.set_port(f.port.value)
            self.pwndbgRPC.start_server()
        elif btn_status == 0:  # stop
            self.pwndbgRPC.stop_server()
            print("[*] pwndbg XML RPC stoped")
        else:  # -1, cancel
            pass
        f.Free()


def PLUGIN_ENTRY():
    return PwndbgRPC_Plugin()


repository: aaaddress1/funcTracker
file: funcTracker.py
description: Useful Plugin for IDA to Trace Function Call Tree
wanted_name: Function Tracker
created_at: 2018-01-04 19:11:20+00:00
pushed_at: 2018-01-04 19:58:57+00:00
url:
https://github.com/aaaddress1/funcTracker/blob/df2c88573abf60bf456978920fa279e10a8fe2ca/funcTracker.py

content:
'''
	@file:			funcTracker.py
	@author:		aaaddress1@chroot.org
	@repository: 	https://github.com/aaaddress1/funcTracker
'''
from idautils import *
from idaapi import *
from idc import *
import re
MAXDEEPCOUNT = 5
pattCall = re.compile('call[\x20\t]+(.+)')

def guessFuncName(guessAddr, deepCount = 0):
	currGuessNm = ''
	if deepCount > MAXDEEPCOUNT: return currGuessNm # tooooooo deep, stop it!
	
	(startea, endea) = Chunks(guessAddr).next() # function range
	print '%s - %s@%x' % ('\t' * deepCount,GetFunctionName(startea), startea)
	
	for head in Heads(startea, endea):
		opcode = GetDisasm(head)
		matcher = pattCall.match(opcode)
		
		if not matcher:
			continue
			
		funcName = matcher.group(1)
		print '\t' * (deepCount + 1) + 'call ' + funcName
		
		if not 'sub_' in funcName:
			currGuessNm += '|%s\n' % funcName 
			
		else:
			subFuncAddr = LocByName(funcName) # sub function address
			currGuessNm += guessFuncName(subFuncAddr, deepCount + 1)
	
	currGuessNm = re.sub(r"ds:|__imp_", "", currGuessNm)
	return currGuessNm

	
class funcTrackerHandler(idaapi.action_handler_t):	
    def __init__(self):
        idaapi.action_handler_t.__init__(self)

    def activate(self, ctx):
		funcName = GetFunctionName(get_screen_ea())
		funcStrt = LocByName(funcName)
		print 'checking... function %s@%x' % (funcName, funcStrt)
		guessName = guessFuncName(funcStrt)
		set_cmt(funcStrt, guessName, True)
		return 1

    def update(self, ctx):
        return idaapi.AST_ENABLE_FOR_FORM if ctx.form_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_FORM


class funcTrackerPlugin(idaapi.plugin_t):
	flags = idaapi.PLUGIN_UNL
	wanted_hotkey = help = comment = ""

	wanted_name = "Function Tracker"
	act_name = "funcTracker:handler"

	def init(self):
		print '''
		  ___                             
		 /                                
		(___       ___  ___               
		|    |   )|   )|                  
		|    |__/ |  / |__                
										  
		  __                              
		 /|                 /             
		( |  ___  ___  ___ (     ___  ___ 
		  | |   )|   )|    |___)|___)|   )
		  | |    |__/||__  | \  |__  |     
		
		v1.0, powered by aaaddress1@chroot.org
		'''
		if idaapi.register_action(idaapi.action_desc_t(
				self.act_name, 					# Name. Acts as an ID. Must be unique.
				"Function Tracker",   			# Label. That's what users see.
				funcTrackerHandler(), 			# Handler. Called when activated, and for updating
				None,	            			# Shortcut (optional)
				"Trace WTF in this function :)",# Tooltip (optional)
				0)):         					# Icon ID (optional)
			print "Action registered. Attaching to menu."

			class Hooks(idaapi.UI_Hooks):
				def finish_populating_tform_popup(self, form, popup):
					if idaapi.get_tform_type(form) == idaapi.BWN_DISASM:
						idaapi.attach_action_to_popup(form, popup, self.act_name, None)

			self.lazyHooks = Hooks()
			self.lazyHooks.act_name = self.act_name
			self.lazyHooks.hook()
	
		return idaapi.PLUGIN_KEEP
		
	def run(self, arg):
		pass

	def term(self):
		pass

def PLUGIN_ENTRY():
	return funcTrackerPlugin()


repository: gistable/gistable
file: all-gists/793f7ddfc2393ffd0418/snippet.py
description: GitHub gists and the dockerfiles needed to run them.
wanted_name: DataComment
comment: Add comment on initialisation data with \"mov dword ptr[reg + 0x10], 74657465h\
created_at: 2018-02-01 16:19:24+00:00
pushed_at: 2023-02-11 18:20:10+00:00
url:
https://github.com/gistable/gistable/blob/665d39a2bd82543d5196555f0801ef8fd4a3ee48/all-gists/793f7ddfc2393ffd0418/snippet.py

content:
##########################################################################
# An IDAPython plugin that generates "comment" for data
#
# Compiler sometimes optimized initialisation of small strings
# and init them with, this example write "Hello!\n":
#     - mov dword ptr[rax + 0], 6c6c6548h
#     - mov dword ptr[rax + 4], a216fh
#
# This plugin will auto comment the following line as:
#     - mov dword ptr[rax + 0], 6c6c6548h; 'Hell'
#     - mov dword ptr[rax + 4], a216fh; 'o\n\x00\x00'
#
# In a second time, you can choose to concatenate them by select both line
# and press "Ctrl+H" or go the Edit menu and click on "Extract selected data"
#
# This will change comment to:
#     - mov dword ptr[rax + 0], 6c6c6548h; 'Hello\n\x00\x00'
#     - mov dword ptr[rax + 4], a216fh;
#
# @BestPig
##########################################################################
import idc
import idaapi
import idautils
import sys


class DataCommentPlugin(idaapi.plugin_t):
    flags = 0
    comment = "Add comment on initialisation data with \"mov dword ptr[reg + 0x10], 74657465h\""
    help = ""
    wanted_name = "DataComment"
    wanted_hotkey = ""

    NAME = "datacomment.py"

    def init(self):
        self.menu_context_extract = idaapi.add_menu_item("Edit/", "Extract selected data", "Ctrl+H", 0, self.dc_extract, (None,))
        idaapi.autoWait()
        for seg_addr in Segments():
            segment = getseg(seg_addr)
            if not segment.perm & SEGPERM_EXEC:
                continue
            self.dcu_extract_between_addr(segment.startEA, segment.endEA)
        return idaapi.PLUGIN_KEEP

    def term(self):
        idaapi.del_menu_item(self.menu_context_extract)
        return None

    def run(self, arg):
        return None

    def dcu_is_wanted_assign(self, instr, addr):
        if instr.get_canon_mnem() != 'mov':
            return False
        if GetOpType(addr, 1) != o_imm:
            return False
        if not getseg(addr).use32():
            return False
        if not 'dword ptr [' in GetOpnd(addr, 0):
            return False
        if (GetOperandValue(addr, 1) & 0xfffff000) == GetOperandValue(addr, 1):
            return False
        return True

    def dcu_extract_between_addr(self, startAddr, endAddr, toMerge=False, force=False):
        current_addr = startAddr
        merged_comment = ''
        first_mov = None
        while current_addr <= endAddr:
            instr = DecodeInstruction(current_addr)
            if not instr:
                current_addr += 1
                continue
            if self.dcu_is_wanted_assign(instr, current_addr):
                if not first_mov:
                    first_mov = current_addr
                data_str = struct.pack('I', GetOperandValue(current_addr, 1) & 0xffffffff)
                if toMerge:
                    merged_comment += data_str
                current_comment = GetCommentEx(current_addr, 1)
                if not current_comment or toMerge or force:
                    exact_comment = repr(data_str)[0 if sys.version_info.major < 3 else 1:]
                    if toMerge and current_comment == exact_comment:
                        MakeRptCmt(current_addr, ' ')
                    elif (not current_comment and not toMerge) or force:
                        MakeRptCmt(current_addr, '%s' % exact_comment)
            current_addr += instr.size
        if merged_comment and first_mov:
            MakeRptCmt(first_mov, '%s' % repr(merged_comment)[0 if sys.version_info.major < 3 else 1:])

    def dc_extract(self, arg):
        multiple, start, end = idaapi.read_selection()
        if multiple:
            self.dcu_extract_between_addr(start, end, True)
        else:
            self.dcu_extract_between_addr(here(), here(), False, True)
        return

def PLUGIN_ENTRY():
    return DataCommentPlugin()


repository: f1ndr/IDA_Plugin_Feedback
file: hexrays_music.py
description: This project aims to give REer feedback to against boredom.
wanted_name: HexLight
comment: highlights the matching brace in Pseudocode-View
created_at: 2022-04-02 15:41:09+00:00
pushed_at: 2022-04-02 16:29:59+00:00
url:
https://github.com/f1ndr/IDA_Plugin_Feedback/blob/9e956c819cbfd81d5047c45b373e3a92df6b4042/hexrays_music.py

content:
# highlighting plugin for Hex-Rays Decompiler
# Copyright (c) 2016
# Milan Bohacek <milan.bohacek+hexlight@gmail.com>
# All rights reserved.
#
# ==============================================================================
#
# This file is part of Hexlight.
#
# Hexlight is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# ==============================================================================


import idautils
import idaapi
import idc
#from pydub.playback import play
#from pydub import AudioSegment
#import mp3play
import pygame

import traceback

hexlight_cb_info = None
hexlight_cb = None

posledni = 0


def jump(custom_viewer, line):
    (pl, x, y) = idaapi.get_custom_viewer_place(custom_viewer, False)
    pl2 = idaapi.place_t_as_simpleline_place_t(pl.clone())
    oldline = pl2.n
    pl2.n = line
    idaapi.jumpto(custom_viewer, pl2, x, y)
    return oldline


class hexrays_callback_info(object):

    def __init__(self):
        self.vu = None
        self.highlights = {}
        # 0xF2E8BF #0x00ffff00       #ida's rgb is reversed  which is bgr
        self.hicolor = 0xffffff
        self.theotherline = None
        self.safe = False
        return

    def clearall(self, ps, refresh=True):
        ctr = 0
        for i in self.highlights:
            try:
                ps[i].bgcolor = self.highlights[i]
                ctr += 1
            except:
                pass

        self.highlights = {}
        self.theotherline = None
        if((ctr > 0) and refresh):
            idaapi.refresh_idaview_anyway()

    def event_callback(self, event, *args):
        try:
            print("event: %d" % event)
            if event == idaapi.hxe_keyboard:
                vu, keycode, shift = args

                if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("B") and shift == 0:
                    if self.theotherline:
                        self.theotherline = jump(vu.ct, self.theotherline)
                    return 0
            if event == idaapi.hxe_func_printed:
                print("open")
                sound_printed.play()
            if event == idaapi.hxe_curpos:
                sound_slip.play()
            if event <= idaapi.hxe_print_func:
                self.safe = False

            if event == idaapi.hxe_switch_pseudocode:
                self.safe = False

            if event == idaapi.hxe_func_printed:
                self.safe = True

            if event == idaapi.hxe_text_ready:
                self.safe = True

            if event == idaapi.hxe_curpos:
                if not self.safe:
                    return 0
                # print "1"
                self.vu = args[0]

                if not self.vu:
                    return 0
                # print "2"

                if self.vu.cfunc.maturity != idaapi.CMAT_FINAL:
                    return 0
                # print "3"

                if not self.vu.visible():
                    return 0
                # print "4"
                if not self.vu.refresh_cpos(idaapi.USE_KEYBOARD):
                 #   print "refresh_cpos failed"
                    return 0
                pos = self.vu.cpos
                ypos = pos.lnnum
                # print "cursor click %d %d %d" % (pos.x, pos.y, pos.lnnum)

                if self.highlights.__contains__(ypos):
                    return 0
                # print "5"

                ps = self.vu.cfunc.get_pseudocode()
                # print "6"
                # print "ypos:%d"%ypos
                # print "ps[ypos].line: %s"%(ps[ypos].line)

                #line = [idaapi.COLSTR("[%02d]"%i, chr(i)) for i in range(1,0x40) ]
                #ps[0].line = ''.join(line);
                #ps[1].line = '\x04'.join(line);
                #line = [idaapi.COLSTR(  idaapi.COLSTR("[  \x04%02d\x04  ]"%i, chr(i)), chr(i+1)) for i in range(1,0x40) ]
                #ps[2].line = ''.join(line);
                #ps[3].line = '\x04'.join(line);
                ln = ps[ypos].line[:]
                # print "ln"
                curline = idaapi.tag_remove(ln)
                # print "7"

                idxO = curline.find('{')
                idxC = curline.find('}')
                # print "O:", idxO, " C: ",idxC
                # there is no need to highlight first and last {
                # print "8"

                if (idxO >= 0) or (idxC >= 0):
                    #   print "9"
                    self.clearall(ps, False)

                    self.highlights[ypos] = ps[ypos].bgcolor

                    ps[ypos].bgcolor = self.hicolor

                    dir = 1
                    bracechar = '}'
                    idx = idxO

                    if (idxC >= 0):
                        dir = -1
                        bracechar = '{'
                        idx = idxC

                    j = ypos + dir

                    max = len(ps)
                 #   print "max: ",max

                    while (j >= 0) and (j < max):
                        #       print "10"
                        # print "j:", j
                        ln = idaapi.tag_remove(ps[j].line)
                        if ln.find(bracechar) == idx:
                            if not(self.highlights.__contains__(j)):
                                self.highlights[j] = ps[j].bgcolor
                            #ps[j].line = ps[j].line.replace(bracechar, idaapi.COLSTR("\x04"+bracechar+"\x04", "\x27"))
                            #ps[j].line = ps[j].line.replace(bracechar, idaapi.COLSTR(bracechar, chr(52)))
                            ps[j].bgcolor = self.hicolor
                            self.theotherline = j
                            break
                        j += dir

                    idaapi.refresh_idaview_anyway()
                else:
                    self.clearall(ps)
                # print "11"
                return 0
        except:
            traceback.print_exc()

        return 0


def remove():
    if hexlight_cb:
        idaapi.remove_hexrays_callback(hexlight_cb)


class HexHLightPlugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_HIDE
    comment = "highlights the matching brace in Pseudocode-View"
    help = "press B to jump to the matching brace"
    wanted_name = "HexLight"
    wanted_hotkey = ""

    def init(self):
        # Some initialization
        global hexlight_cb_info, hexlight_cb

        if idaapi.init_hexrays_plugin():
            hexlight_cb_info = hexrays_callback_info()
            hexlight_cb = hexlight_cb_info.event_callback
            #a = idaapi.Hexrays_Hooks()
            # print(a.func_printed)

            # if not idaapi.Hexrays_Hooks(hexlight_cb):
            if not idaapi.install_hexrays_callback(hexlight_cb):
                #    print "could not install hexrays_callback"
                return idaapi.PLUGIN_SKIP
            print("Hexlight plugin installed")
            addon = idaapi.addon_info_t()
            addon.id = "milan.bohacek.hexlight"
            addon.name = "Hexlight"
            addon.producer = "Milan Bohacek"
            addon.url = "milan.bohacek+hexlight@gmail.com"
            addon.version = "6.95"
            idaapi.register_addon(addon)
            return idaapi.PLUGIN_KEEP
        # print "init_hexrays_plugin failed"
        return idaapi.PLUGIN_SKIP

    def run(self, arg=0):
        return

    def term(self):
        remove()


def PLUGIN_ENTRY():
    # return idaapi.PLUGIN_SKIP
    global sound_printed, sound_slip
    # song=AudioSegment.from_wav("reload1.wav")
    #song = mp3play.load("reload.mp3")
    pygame.mixer.init()
    sound_printed = pygame.mixer.Sound("reload1.mp3")
    sound_slip = pygame.mixer.Sound("slip.wav")
    sound_slip.set_volume(0.1)
    print("I'm in")
    return HexHLightPlugin_t()


repository: 0xatul/commentator
file: commentator.py
url: https://github.com/0xatul/commentator/blob/4e7d162ad2123757a442bc3cceb27d2efdfac1aa/commentator.py
wanted_name: MSDN commentator
created_at: 2023-11-16 14:04:38+00:00
pushed_at: 2023-12-11 15:05:11+00:00
content:
import os
import sqlite3
from typing import Optional

import ida_kernwin
import ida_hexrays
import ida_name
import idaapi
import idautils
import requests
from bs4 import BeautifulSoup

# Globals
DB_PATH = os.path.join(os.path.join(
    idaapi.get_user_idadir(), "plugins"), "commentator.db")
nimps = idaapi.get_import_module_qty()
decl_map = {}
func_map = {}
conn = None


# Funcs
#  https://github.com/mandiant/FIDL/blob/master/FIDL/decompiler_utils.py#L1062
def decompile(ea=None) -> Optional[idaapi.cfunc_t]:
    """This sets flags necessary to use this programmatically.

    :param ea: Address within the function to decompile
    :type ea: int
    :return: decompilation object
    :rtype: a :class:`cfunc_t`
    """

    if not ea:
        print("Please specify an address (ea)")
        return None

    try:
        cf = idaapi.decompile(
            ea=ea,
            flags=ida_hexrays.DECOMP_NO_WAIT | ida_hexrays.DECOMP_NO_CACHE
        )
        cf.refresh_func_ctext()
    except ida_hexrays.DecompilationFailure as e:
        print("Failed to decompile @ {:X}".format(ea))
        cf = None

    return cf


# Referenced and improved upon from: https://github.com/mandiant/FIDL/blob/master/FIDL/decompiler_utils.py#L2105
def comment(ea, cmnt) -> bool:
    cf = decompile(ea)
    tl = idaapi.treeloc_t()
    tl.ea = ea
    # https://hex-rays.com/products/decompiler/manual/sdk/hexrays_8hpp.shtml#a219c95f85c085e6f539b8d3b96074aee
    for itp in [idaapi.ITP_SEMI, idaapi.ITP_CURLY1, idaapi.ITP_CURLY2, idaapi.ITP_COLON, idaapi.ITP_BRACE1,
                idaapi.ITP_BRACE2, idaapi.ITP_ASM, idaapi.ITP_ELSE, idaapi.ITP_DO, idaapi.ITP_CASE]:
        tl.itp = itp
        cf.set_user_cmt(tl, cmnt)
        cf.save_user_cmts()
        cf.__str__()  # trigger string representation, otherwise orphan comments aren't detected
        if not cf.has_orphan_cmts():
            return True
        cf.del_orphan_cmts()
    return False


def sanitize_name(n: str) -> str:
    t = ida_name.FUNC_IMPORT_PREFIX
    if n.startswith(t):
        n = n[len(t):]
    return n


def add_entry(func_name, decl):
    global conn
    if not conn:
        return
    c = conn.cursor()
    c.execute("INSERT INTO funcs VALUES (?, ?)", (func_name, decl))
    conn.commit()


def get_doc_link(func_name):
    url = f"https://learn.microsoft.com/api/search/rss?search={func_name}&locale=en-us&%24filter=%28category+eq+%27Documentation%27%29"
    r = requests.get(url)
    soup = BeautifulSoup(r.text, features='xml')
    ret = soup.find("item").find("link").text
    if not ret:
        return None
    return ret


def get_decl(func_name):
    ret = None
    c = conn.cursor()
    c.execute("SELECT decl FROM funcs where name = ?", (func_name,))
    e = c.fetchone()
    if e:
        ret = e[0]
    else:
        doc_link = get_doc_link(func_name)  # if we dont then fetch it
        if not doc_link:
            return None
        res = requests.get(doc_link)
        if not res:
            return None
        soup = BeautifulSoup(res.text, "html.parser")
        if not soup:
            return None
        ret = soup.find("code").text
    add_entry(func_name, ret)

    return ret


def cb(ea, name, ordinal):
    if not name:
        return True
    func_map[name] = ea
    return True


def populate_func_map():
    nimps = idaapi.get_import_module_qty()
    print("Populating func map")
    for i in range(0, nimps):
        idaapi.enum_import_names(i, cb)


# Some boiler plate
class Commentator(idaapi.plugin_t):
    flags = 0
    comment = (
        "Adds msdn annotation to all instances of the function in the entire idb")
    help = "Adds msdn annotation to functions"
    wanted_name = "MSDN commentator"
    wanted_hotkey = "Ctrl-Alt-S"

    def init(self):
        print("Initializing")
        global conn
        conn = sqlite3.connect(DB_PATH)
        populate_func_map()
        print("Loaded commentator")
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        v = idaapi.get_current_viewer()
        iden, ok = idaapi.get_highlight(v)
        func_ea = idaapi.BADADDR

        if not ok:
            return

        iden = sanitize_name(iden)
        if iden in func_map.keys():
            func_ea = func_map[iden]
        if func_ea == idaapi.BADADDR:
            print("Function is not imported")
            return

        xrefs = idautils.CodeRefsTo(func_ea, 0)
        for xref in xrefs:
            comment(xref, get_decl(iden))
        ida_kernwin.refresh_idaview_anyway()


    def term(self):
        """DO NOTHING"""
        return


def PLUGIN_ENTRY():
    try:
        return Commentator()
    except Exception as err:
        import traceback
        print('Err: %s\n%s' % (str(err), str(traceback.format_exc())))


repository: dockerizeme/dockerizeme
file: hard-gists/b3143dde185cecda7c1dee7ffbce5d2c/snippet.py
description: Automatically generate environment configurations as Dockerfiles
comment: Decode IOCTL codes!
created_at: 2018-08-12 21:21:04+00:00
pushed_at: 2022-12-07 22:45:41+00:00
url:
https://github.com/dockerizeme/dockerizeme/blob/408f3fa3d36542d8fc1236ba1cac804de6f14b0c/hard-gists/b3143dde185cecda7c1dee7ffbce5d2c/snippet.py

content:
############################################################################################
##
## Quick IOCTL Decoder!
##
## All credit for actual IOCTL decode logic:
##      http://www.osronline.com/article.cfm?article=229
## 
##
## To install:
##      Copy script into plugins directory, i.e: C:\Program Files\IDA 6.8\plugins
##
## To run:
##      Highlight IOCTL then right click and select "Decode IOCTL" or press PLUGIN_HOTKEY
##      The decoded IOCTL data will be added as a comment and displayed in the IDA cli
##
############################################################################################

__VERSION__ = '0.1'
__AUTHOR__ = '@herrcore'

PLUGIN_NAME = "Quick IOCTL Decoder"
PLUGIN_HOTKEY = 'Ctrl-Alt-I'


import idaapi

class IOCTL_Decoder():
    DEVICE = [None]*55
    DEVICE[1]="BEEP"
    DEVICE[2]="CD_ROM"
    DEVICE[3]="CD_ROM_FILE_SYSTEM"
    DEVICE[4]="CONTROLLER"
    DEVICE[5]="DATALINK"
    DEVICE[6]="DFS"
    DEVICE[7]="DISK"
    DEVICE[8]="DISK_FILE_SYSTEM"
    DEVICE[9]="FILE_SYSTEM"
    DEVICE[10]="INPORT_PORT"
    DEVICE[11]="KEYBOARD"
    DEVICE[12]="MAILSLOT"
    DEVICE[13]="MIDI_IN"
    DEVICE[14]="MIDI_OUT"
    DEVICE[15]="MOUSE"
    DEVICE[16]="MULTI_UNC_PROVIDER"
    DEVICE[17]="NAMED_PIPE"
    DEVICE[18]="NETWORK"
    DEVICE[19]="NETWORK_BROWSER"
    DEVICE[20]="NETWORK_FILE_SYSTEM"
    DEVICE[21]="NULL"
    DEVICE[22]="PARALLEL_PORT"
    DEVICE[23]="PHYSICAL_NETCARD"
    DEVICE[24]="PRINTER"
    DEVICE[25]="SCANNER"
    DEVICE[26]="SERIAL_MOUSE_PORT"
    DEVICE[27]="SERIAL_PORT"
    DEVICE[28]="SCREEN"
    DEVICE[29]="SOUND"
    DEVICE[30]="STREAMS"
    DEVICE[31]="TAPE"
    DEVICE[32]="TAPE_FILE_SYSTEM"
    DEVICE[33]="TRANSPORT"
    DEVICE[34]="UNKNOWN"
    DEVICE[35]="VIDEO"
    DEVICE[36]="VIRTUAL_DISK"
    DEVICE[37]="WAVE_IN"
    DEVICE[38]="WAVE_OUT"
    DEVICE[39]="8042_PORT"
    DEVICE[40]="NETWORK_REDIRECTOR"
    DEVICE[41]="BATTERY"
    DEVICE[42]="BUS_EXTENDER"
    DEVICE[43]="MODEM"
    DEVICE[44]="VDM"
    DEVICE[45]="MASS_STORAGE"
    DEVICE[46]="SMB"
    DEVICE[47]="KS"
    DEVICE[48]="CHANGER"
    DEVICE[49]="SMARTCARD"
    DEVICE[50]="ACPI"
    DEVICE[51]="DVD"
    DEVICE[52]="FULLSCREEN_VIDEO"
    DEVICE[53]="DFS_FILE_SYSTEM"
    DEVICE[54]="DFS_VOLUME"

    def __init__(self, control_code):
        self.control_code = control_code

    def decode(self):
        out={}
        device_val = (self.control_code >> 16) & 0xFFF
        funcVal = (self.control_code >> 2) & 0xFFF
        if (device_val <= 54) and (device_val != 0):
            device_string = self.DEVICE[device_val]+ " ("+hex(device_val)+")"
        else:
            device_string = hex(device_val)
        function_string = hex(funcVal)
        out["device"] = device_string
        out["function"] = function_string
        access = (self.control_code >> 14) & 3   
        method = self.control_code & 3
        access_string = ""
        if access == 0:
            access_string = "FILE_ANY_ACCESS"
        elif access == 1:
            access_string = "FILE_READ_ACCESS"
        elif access == 2:
            access_string = "FILE_WRITE_ACCESS"
        elif access == 3:
            access_string = "Read and Write"
        method_string = ""
        if method == 0:
            method_string = "METHOD_BUFFERED"
        elif method == 1:
            method_string = "METHOD_IN_DIRECT"
        elif method == 2:
            method_string = "METHOD_OUT_DIRECT"
        elif method == 3:
            method_string = "METHOD_NEITHER"
        out["access"] = access_string
        out["method"] = method_string
        return out


class IDAIOCTLDecoder():
    @staticmethod
    def decode():
        ea = ScreenEA()
        if ea == idaapi.BADADDR:
            idaapi.msg(PLUGIN_NAME + " ERROR: Could not get get_screen_ea()")
            return

        str_id = idaapi.get_highlighted_identifier()
        if not str_id:
            idaapi.msg(PLUGIN_NAME + " ERROR: No Ioctl Code highlighted!")
            return
        try:
            if str_id[-1] == 'h':
                code = int(str_id[:-1], 16)
            elif str_id[-1] == 'o':
                code = int(str_id[:-1], 8)
            elif str_id[-1] == 'b':
                code = int(str_id[:-1], 2)
            else:
                code = int(str_id)
        except ValueError:
            idaapi.msg(PLUGIN_NAME + " ERROR: Not a valid Ioctl Code: " + str(str_id)) 
            return

        try:
            decoder = IOCTL_Decoder(code)
            ioctl_data = decoder.decode()

            #print decoded IOCTL to cli
            msg_string =  "That IOCTL decodes to: \n\tDevice: %s \n\tFunction: %s \n\tAccess: %s \n\tMethod: %s" 
            idaapi.msg(msg_string % (ioctl_data["device"], ioctl_data["function"], ioctl_data["access"], ioctl_data["method"]))

            #add decoded IOCTL as comment
            comment_string =  "dwIoControlCode: \n\t\tDevice: %s \n\t\tFunction: %s \n\t\tAccess: %s \n\t\tMethod: %s"
            idaapi.set_cmt(ea, comment_string % (ioctl_data["device"], ioctl_data["function"], ioctl_data["access"], ioctl_data["method"]), 0)
        except Exception as e:
             idaapi.msg(PLUGIN_NAME + " ERROR: " + str(e))
        return


class IOCTLDecodeHandler(idaapi.action_handler_t):
    def activate(self, ctx):
        IDAIOCTLDecoder.decode()

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

class QuickIOCTLDecoderHooks(idaapi.UI_Hooks):
    def finish_populating_tform_popup(self, form, popup):
        tft = idaapi.get_tform_type(form)
        if tft == idaapi.BWN_DISASM:
            # Note the 'None' as action name (1st parameter).
            # That's because the action will be deleted immediately
            # after the context menu is hidden anyway, so there's
            # really no need giving it a valid ID.
            desc = idaapi.action_desc_t(None, 'Decode IOCTL', IOCTLDecodeHandler())
            idaapi.attach_dynamic_action_to_popup(form, popup, desc, None)

class QuickIOCTLDecoder(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "Decode IOCTL codes!"

    help = "Highlight IOCTL and right-click 'Decode IOCTL'"
    wanted_name = PLUGIN_NAME
    wanted_hotkey = PLUGIN_HOTKEY
    
 
    def init(self):
        idaapi.msg("Initializing: %s\n" % PLUGIN_NAME)
        global hooks
        hooks = QuickIOCTLDecoderHooks()
        re = hooks.hook()
        return idaapi.PLUGIN_OK

        
    def run(self, arg):
        IDAIOCTLDecoder.decode()
        pass

    def term(self):
        pass

def PLUGIN_ENTRY():
    return QuickIOCTLDecoder()


repository: PwCUK-CTO/iis-helper-plugin
file: iis_helper_plugin.py
description: IDA Pro plugin to aid with the analysis of native IIS modules
wanted_name: IISHelper
comment: IIS Helper Plugin
created_at: 2023-11-13 08:41:42+00:00
pushed_at: 2024-08-01 09:34:59+00:00
url:
https://github.com/PwCUK-CTO/iis-helper-plugin/blob/515bc39654261de9611b08b5534d065d773cfe33/iis_helper_plugin.py

content:
"""
Description: IDA Python plugin to help analyse native IIS modules
Author: @BitsOfBinary
License:
Copyright 2023 PwC International Limited
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import idc
import ida_typeinf
import ida_srclang
import ida_bytes
import idautils
import ida_funcs
import ida_name
import ida_segment
import ida_hexrays
import idaapi
import ida_kernwin

import iis_helper_classes

class IISHelper:

    cglobalmodule_funcs = [
        "OnGlobalStopListening",
        "OnGlobalCacheCleanup",
        "OnGlobalCacheOperation",
        "OnGlobalHealthCheck",
        "OnGlobalConfigurationChange",
        "OnGlobalFileChange",
        "OnGlobalPreBeginRequest",
        "OnGlobalApplicationStart",
        "OnGlobalApplicationResolveModules",
        "OnGlobalApplicationStop",
        "OnGlobalRSCAQuery",
        "OnGlobalTraceEvent",
        "OnGlobalCustomNotification",
        "Terminate\x00",
        "OnGlobalThreadCleanup",
        "OnGlobalApplicationPreload"
    ]
    
    chttpmodule_funcs = [
        "OnBeginRequest",
        "OnPostBeginRequest",
        "OnAuthenticateRequest",
        "OnPostAuthenticateRequest",
        "OnAuthorizeRequest",
        "OnPostAuthorizeRequest",
        "OnResolveRequestCache",
        "OnPostResolveRequestCache",
        "OnMapRequestHandler",
        "OnPostMapRequestHandler",
        "OnAcquireRequestState",
        "OnPostAcquireRequestState",
        "OnPreExecuteRequestHandler",
        "OnPostPreExecuteRequestHandler",
        "OnExecuteRequestHandler",
        "OnPostExecuteRequestHandler",
        "OnReleaseRequestState",
        "OnPostReleaseRequestState",
        "OnUpdateRequestCache",
        "OnPostUpdateRequestCache",
        "OnLogRequest",
        "OnPostLogRequest",
        "OnEndRequest",
        "OnPostEndRequest",
        "OnSendResponse",
        "OnMapPath",
        "OnReadEntity",
        "OnCustomRequestNotification",
        "OnAsyncCompletion",
        "Dispose\x00"
    ]
    
    cglobalmodule_func_prototypes = {
        "OnGlobalStopListening": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalStopListening(CGlobalModule* this, IGlobalStopListeningProvider* pProvider);",
        "OnGlobalCacheCleanup": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalCacheCleanup(CGlobalModule* this);",
        "OnGlobalCacheOperation": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalCacheOperation(CGlobalModule* this, ICacheProvider* pProvider);",
        "OnGlobalHealthCheck": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalHealthCheck(CGlobalModule* this);",
        "OnGlobalConfigurationChange": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalConfigurationChange(CGlobalModule* this, IGlobalConfigurationChangeProvider* pProvider);",
        "OnGlobalFileChange": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalFileChange(CGlobalModule* this, IGlobalFileChangeProvider* pProvider);",
        "OnGlobalPreBeginRequest": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalPreBeginRequest(CGlobalModule* this, IPreBeginRequestProvider* pProvider);",
        "OnGlobalApplicationStart": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalApplicationStart(CGlobalModule* this, IHttpApplicationStartProvider* pProvider);",
        "OnGlobalApplicationResolveModules": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalApplicationResolveModules(CGlobalModule* this, IHttpApplicationResolveModulesProvider* pProvider);",
        "OnGlobalApplicationStop": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalApplicationStop(CGlobalModule* this, IHttpApplicationStopProvider* pProvider);",
        "OnGlobalRSCAQuery": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalRSCAQuery(CGlobalModule* this, IGlobalRSCAQueryProvider* pProvider);",
        "OnGlobalTraceEvent": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalTraceEvent(CGlobalModule* this, IGlobalTraceEventProvider* pProvider);",
        "OnGlobalCustomNotification": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalCustomNotification(CGlobalModule* this, ICustomNotificationProvider* pProvider);",
        "Terminate\x00": "virtual __thiscall void Terminate(CGlobalModule* this);",
        "OnGlobalThreadCleanup": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS OnGlobalThreadCleanup(CGlobalModule* this, IGlobalThreadCleanupProvider* pProvider);",
        "OnGlobalApplicationPreload": "virtual __thiscall GLOBAL_NOTIFICATION_STATUS IGlobalApplicationPreloadProvider(CGlobalModule* this);"
    }
    
    chttpmodule_func_prototypes = {
        "OnBeginRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnBeginRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostBeginRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostBeginRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnAuthenticateRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnAuthenticateRequest(CHttpModule* this, IHttpContext* pHttpContext, IAuthenticationProvider*);",
        "OnPostAuthenticateRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostAuthenticateRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnAuthorizeRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnAuthorizeRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostAuthorizeRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostAuthorizeRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnResolveRequestCache": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnResolveRequestCache(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostResolveRequestCache": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostResolveRequestCache(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnMapRequestHandler": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnMapRequestHandler(CHttpModule* this, IHttpContext* pHttpContext, IMapHandlerProvider* pProvider);",
        "OnPostMapRequestHandler": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostMapRequestHandler(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnAcquireRequestState": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnAcquireRequestState(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostAcquireRequestState": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostAcquireRequestState(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPreExecuteRequestHandler": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPreExecuteRequestHandler(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostPreExecuteRequestHandler": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostPreExecuteRequestHandler(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnExecuteRequestHandler": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnExecuteRequestHandler(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostExecuteRequestHandler": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostExecuteRequestHandler(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnReleaseRequestState": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnReleaseRequestState(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostReleaseRequestState": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostReleaseRequestState(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnUpdateRequestCache": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnUpdateRequestCache(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostUpdateRequestCache": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostUpdateRequestCache(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnLogRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnLogRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostLogRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostLogRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnEndRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnEndRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnPostEndRequest": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnPostEndRequest(CHttpModule* this, IHttpContext* pHttpContext, IHttpEventProvider* pProvider);",
        "OnSendResponse": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnSendResponse(CHttpModule* this, IHttpContext* pHttpContext, ISendResponseProvider* pProvider);",
        "OnMapPath": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnMapPath(CHttpModule* this, IHttpContext* pHttpContext, IMapPathProvider* pProvider);",
        "OnReadEntity": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnReadEntity(CHttpModule* this, IHttpContext* pHttpContext, IReadEntityProvider* pProvider);",
        "OnCustomRequestNotification": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnCustomRequestNotification(CHttpModule* this, IHttpContext* pHttpContext, ICustomNotificationProvider* pProvider);",
        "OnAsyncCompletion": "virtual __thiscall REQUEST_NOTIFICATION_STATUS OnAsyncCompletion(CHttpModule* this, IHttpContext* pHttpContext, DWORD dwNotification, BOOL fPostNotification, IHttpEventProvider* pProvider, IHttpCompletionInfo* pCompletionInfo);",
        "Dispose\x00": "virtual __thiscall REQUEST_NOTIFICATION_STATUS Dispose(CHttpModule* this);"
    }

    def __init__(self):
        self.is_cglobalmodule = False
        self.is_chttpmodule = False
        self.module_str = ""
        self.iis_module_func_names = []
        
        self.not_implemented_func_addresses = {}
        self.implemented_func_addresses = {}
        
        self.ida_strings = idautils.Strings()
        
    def create_classes(self):
        """Add all relevant IIS classes to the local types."""
        # Add initial classes
        ida_srclang.parse_decls_with_parser("<default>", None, iis_helper_classes.IIS_CLASSES, False)
    
        # Cleanup other classes
        # We have to do this, as some of the class definitions rely on each other, and can't be loaded in at the same time
        ida_srclang.parse_decls_with_parser("<default>", None, iis_helper_classes.IIS_CLASSES_UPDATE, False)
        
    def create_enums(self):
        """Add all enumeration types to the database."""
        rdn_enum_id = idc.add_enum(-1, "iis_request_deterministic_notifications", ida_bytes.hex_flag())
        idc.add_enum_member(rdn_enum_id, "RQ_BEGIN_REQUEST", 0x00000001, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_AUTHENTICATE_REQUEST", 0x00000002, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_AUTHORIZE_REQUEST", 0x00000004, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_RESOLVE_REQUEST_CACHE", 0x00000008, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_MAP_REQUEST_HANDLER", 0x00000010, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_ACQUIRE_REQUEST_STATE", 0x00000020, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_PRE_EXECUTE_REQUEST_HANDLER", 0x00000040, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_EXECUTE_REQUEST_HANDLER", 0x00000080, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_RELEASE_REQUEST_STATE", 0x00000100, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_UPDATE_REQUEST_CACHE", 0x00000200, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_LOG_REQUEST", 0x00000400, -1)
        idc.add_enum_member(rdn_enum_id, "RQ_END_REQUEST", 0x00000800, -1)
        
        rndn_enum_id = idc.add_enum(-1, "iis_request_non_deterministic_notifications", ida_bytes.hex_flag())
        idc.add_enum_member(rndn_enum_id, "RQ_CUSTOM_NOTIFICATION", 0x10000000, -1)
        idc.add_enum_member(rndn_enum_id, "RQ_SEND_RESPONSE", 0x20000000, -1)
        idc.add_enum_member(rndn_enum_id, "RQ_READ_ENTITY", 0x40000000, -1)
        idc.add_enum_member(rndn_enum_id, "RQ_MAP_PATH", 0x80000000, -1)
        
        gn_enum_id = idc.add_enum(-1, "iis_global_notifications", ida_bytes.hex_flag())
        idc.add_enum_member(gn_enum_id, "GL_STOP_LISTENING", 0x00000002, -1)
        idc.add_enum_member(gn_enum_id, "GL_CACHE_CLEANUP", 0x00000004, -1)
        idc.add_enum_member(gn_enum_id, "GL_CACHE_OPERATION", 0x00000010, -1)
        idc.add_enum_member(gn_enum_id, "GL_HEALTH_CHECK", 0x00000020, -1)
        idc.add_enum_member(gn_enum_id, "GL_CONFIGURATION_CHANGE", 0x00000040, -1)
        idc.add_enum_member(gn_enum_id, "GL_FILE_CHANGE", 0x00000080, -1)
        idc.add_enum_member(gn_enum_id, "GL_PRE_BEGIN_REQUEST", 0x00000100, -1)
        idc.add_enum_member(gn_enum_id, "GL_APPLICATION_START", 0x00000200, -1)
        idc.add_enum_member(gn_enum_id, "GL_APPLICATION_RESOLVE_MODULES", 0x00000400, -1)
        idc.add_enum_member(gn_enum_id, "GL_APPLICATION_STOP", 0x00000800, -1)
        idc.add_enum_member(gn_enum_id, "GL_RSCA_QUERY", 0x00001000, -1)
        idc.add_enum_member(gn_enum_id, "GL_TRACE_EVENT", 0x00002000, -1)
        idc.add_enum_member(gn_enum_id, "GL_CUSTOM_NOTIFICATION", 0x00004000, -1)
        idc.add_enum_member(gn_enum_id, "GL_THREAD_CLEANUP", 0x00008000, -1)
        idc.add_enum_member(gn_enum_id, "GL_APPLICATION_PRELOAD", 0x00010000, -1)
        
    def determine_iis_module_type(self):
        """Determine the type of IIS module we're dealing with, either a
        'CGlobalModule' or 'CHttpModule', and set relevant variables.
        """
        for ida_string in self.ida_strings:
            if str(ida_string) == ".?AVCGlobalModule@@":
                self.is_cglobalmodule = True
                self.module_str = "CGlobalModule"
                self.iis_module_func_names = self.cglobalmodule_funcs
                return True
                
            elif str(ida_string) == ".?AVCHttpModule@@":
                self.is_chttpmodule = True
                self.module_str = "CHttpModule"
                self.iis_module_func_names = self.chttpmodule_funcs
                return True
                
        ida_kernwin.info("Could not determine IIS module type - sample may not be an IIS module.")
        return False
    
    def label_not_implemented_funcs_via_xref(self):
        """This function will attempt to relabel not implemented IIS methods based
        on the cross references from the logging strings used to detail that the
        IIS method hasn't been implemented.
        """
        
        for ida_string in self.ida_strings:
            
            for iis_module_func_name in self.iis_module_func_names:
                if f"{self.module_str}::{iis_module_func_name}" in str(ida_string):
                    
                    string_address = ida_string.ea
                    
                    string_xrefs = idautils.XrefsTo(string_address)
                    
                    # TODO: handle multiple xrefs
                    for xref in string_xrefs:
                        address_in_func = xref.frm
                        
                    func_name = ida_funcs.get_func_name(address_in_func)
                    func = ida_funcs.get_func(address_in_func)
                    
                    # https://www.hex-rays.com/products/ida/support/idadoc/203.shtml
                    not_implemented_name = f"not_impl_{iis_module_func_name}"
                    print(f"[IIS Relabelling] Renaming address to {not_implemented_name}: {hex(func.start_ea)}")
                    ida_name.set_name(func.start_ea, not_implemented_name, ida_name.SN_FORCE)
                    
                    self.not_implemented_func_addresses[iis_module_func_name].add(func.start_ea)
                    
                    break
                    
        return
            
    def label_not_implemented_funcs(self):
        """Label known not implemented functions, primarily through 
        searching cross references of non-implemented virtual method strings.
        """
        for func_name in self.iis_module_func_names:
            self.not_implemented_func_addresses[func_name] = set()

        self.label_not_implemented_funcs_via_xref()

        return
        
    def label_directional_funcs(self, steps_to_check, next_check_addr, addresses_checked, number_of_steps_in, backward_flag=False):
        """Search forwards or backwards from known addresses for a set number of steps_to_check
        to perform relabelling of known IIS subroutines.
        """
        # If looking forwards, subtract 1 to not overshoot
        if not backward_flag:
            steps_to_check -= 1
            
        for i in range(0, steps_to_check):
            if backward_flag:
                next_check_addr = idc.prev_head(next_check_addr)
                
            else:
                next_check_addr = idc.next_head(next_check_addr)
                
            if next_check_addr in addresses_checked:
                continue
                
            func_addr_to_check = idc.get_qword(next_check_addr)
            func_name_to_check = ida_funcs.get_func_name(func_addr_to_check)
            
            if func_name_to_check.startswith("sub_"):
                if backward_flag:
                    func_name = self.iis_module_func_names[-(i+number_of_steps_in-1)]
                    
                else:
                    func_name = self.iis_module_func_names[i+number_of_steps_in+1]
                
                print(f"[IIS Relabelling] Renaming address to {func_name}: {hex(func_addr_to_check)}")
                ida_name.set_name(func_addr_to_check, func_name, ida_name.SN_FORCE)
                self.implemented_func_addresses[func_name].add(func_addr_to_check)
                
            addresses_checked.add(next_check_addr)
            
        return addresses_checked
        
    def label_implemented_funcs_via_non_implemented_search(self, addresses_checked):
        """Iterate over addresses of non-implemented IIS methods and
        using cross references to these, find nearby implemented IIS methods
        through forwards and backwards searches
        """
        to_forward_check = len(self.iis_module_func_names) - 1
        to_backward_check = 0
        number_of_steps_in = 0
        
        for iis_func_name in self.iis_module_func_names:
            if self.not_implemented_func_addresses[iis_func_name]:
                # TODO: iterate over all not implemented addresses
                xrefs = idautils.XrefsTo(list(self.not_implemented_func_addresses[iis_func_name])[0])
                
                for xref in xrefs:
                    try:
                        segm_name = ida_segment.get_segm_name(ida_segment.getseg(xref.frm))
                    
                        # Rule out a false positive section
                        if segm_name != ".pdata":
                        
                            forward_check_addr = xref.frm
                            backward_check_addr = xref.frm
                        
                            addresses_checked = self.label_directional_funcs(to_forward_check, forward_check_addr, addresses_checked, number_of_steps_in, backward_flag=False)
                            addresses_checked = self.label_directional_funcs(to_backward_check, backward_check_addr, addresses_checked, number_of_steps_in, backward_flag=True)
                            
                    except:
                        continue
                                
                        
            to_forward_check -= 1
            to_backward_check += 1
            number_of_steps_in += 1
            
        return
        
    def label_fixed_block(self, start_addr):
        """Function to label a fixed block of IIS functions
        assuming that we know the start address of the block
        """
        
        current_addr = start_addr
        
        for func_name in self.iis_module_func_names:
            
            func_addr_to_check = idc.get_qword(current_addr)
            func_name_to_check = ida_funcs.get_func_name(func_addr_to_check)
            
            if func_name_to_check and func_name_to_check.startswith("sub_"): 
                ida_name.set_name(func_addr_to_check, func_name, ida_name.SN_FORCE)
                self.implemented_func_addresses[func_name].add(func_addr_to_check)
                
            current_addr = idc.next_head(current_addr)
        
    def check_vftable_block(self, current_addr):
        """Function to start at a candidate IIS method block
        and see if each method is a subroutine which matches the expected
        size of implemented IIS methods
        """
        counter = 1

        candidate_start_addr = current_addr
        
        current_addr = idc.next_head(current_addr)
        
        for i in range(1, len(self.iis_module_func_names)):
            func_addr_to_check = idc.get_qword(current_addr)
            func_name_to_check = ida_funcs.get_func_name(func_addr_to_check)
            
            addr_name = ida_name.get_ea_name(current_addr, ida_name.GN_DEMANGLED)
        
            if func_name_to_check and func_name_to_check.startswith("sub_") and "vftable" not in addr_name:
                
                counter += 1
                current_addr = idc.next_head(current_addr)
                
            else:
                break
                
        if counter == len(self.iis_module_func_names):
            self.label_fixed_block(candidate_start_addr)
            
        return current_addr
        
    def label_implemented_funcs_via_subroutine_block(self):
        """This function will attempt to relabel not implemented IIS methods based on
        sequential blocks of subroutines that fit the expected length of the current
        IIS class that the module has inherited from
        """
        
        section_name = ".rdata"
        
        seg = ida_segment.get_segm_by_name(section_name)
        
        if seg:
            current_addr = seg.start_ea
            end_addr = seg.end_ea
            
            while current_addr <= end_addr:
                    
                # Using GN_DEMANGLED means we can find vftables, e.g.:
                # const CMyHttpModule::`vftable'
                addr_name = ida_name.get_ea_name(current_addr, ida_name.GN_DEMANGLED)
                
                if addr_name:
                    if "vftable" in addr_name:
                        current_addr = self.check_vftable_block(current_addr)
            
                current_addr = idc.next_head(current_addr)
        
        return
        
    def label_implemented_funcs(self):
        """Call multiple functions to label known implemented
        IIS methods
        """
        addresses_checked = set()
        
        for iis_module_func_name in self.iis_module_func_names:
            self.implemented_func_addresses[iis_module_func_name] = set()
        
        self.label_implemented_funcs_via_non_implemented_search(addresses_checked)
        self.label_implemented_funcs_via_subroutine_block()

        return
    
    def relabel_iis_funcs(self):
        """Main method for relabelling IIS functions"""
        
        if not self.determine_iis_module_type():
            return False
        
        self.label_not_implemented_funcs()
        self.label_implemented_funcs()

        return True
        
    def retype_registermodule_export(self):
        """Function to retype the RegisterModule export of an IIS module"""
        for entry in idautils.Entries():
            export_address = entry[2]
            export_name = entry[3]
            
            if export_name == "RegisterModule":
                
                register_module_func = ida_funcs.get_func(export_address)
                
                register_module_export_addr = register_module_func.start_ea
                
                break

        idc.SetType(register_module_export_addr, "HRESULT __stdcall RegisterModule(DWORD dwServerVersion, IHttpModuleRegistrationInfo* pModuleInfo, IHttpServer* pGlobalInfo);")
        
    def retype_iis_funcs(self):
        """Given a list of known implemented IIS methods
        apply known function prototypes to them
        """
        
        self.retype_registermodule_export()
        
        for func_name, addresses in self.implemented_func_addresses.items():
            for func_addr in addresses:
                if self.is_cglobalmodule:
                    idc.SetType(func_addr, self.cglobalmodule_func_prototypes[func_name])
                    
                elif self.is_chttpmodule:
                    idc.SetType(func_addr, self.chttpmodule_func_prototypes[func_name])
                    
    def iis_variable_retype_handler(self, func_ea):
        """Handler to retype variables in known implemented
        IIS methods. We run this method many times to try and
        retype as many variables as we can based on refreshing
        decompiler outputs
        """
        max_iter = 100
        edited_variable_names = []
        
        cfunc_t = ida_hexrays.decompile(func_ea)
        ctree_visitor = iis_ctree_visitor(cfunc_t)
        
        if cfunc_t:
            while max_iter:
                vars_edited = False
                
                ctree_visitor.apply_to(cfunc_t.body, None)
            
                cfunc_t.build_c_tree()
                cfunc_t.refresh_func_ctext()
                
                # These variables may change between updates of the ctree_visitor
                # But it is good enough to make sure we don't run it too many times
                updated_edited_variable_names = ctree_visitor.get_edited_variable_names()
                
                for var_name in updated_edited_variable_names:
                    if var_name not in edited_variable_names:
                        edited_variable_names.append(var_name)
                        vars_edited = True
                        
                if not vars_edited:
                    break
                
                max_iter -= 1
    
    def retype_iis_variables(self):
        """Retype as many variables as we can in IIS implemented methods
        by calling the handler multiple times to fully refresh the 
        decompiler output
        """
        max_retype_rerun = 3
        
        for func_name, addresses in self.implemented_func_addresses.items():
            for func_addr in addresses:
                for i in range(0, max_retype_rerun):
                    self.iis_variable_retype_handler(func_addr)
        
    def main(self):
        """Main method to handle all IIS renaming/retyping"""
        self.create_classes()
        self.create_enums()
        
        if not self.relabel_iis_funcs():
            return
        
        self.retype_iis_funcs()
        self.retype_iis_variables()
        
        return
        
class iis_ctree_visitor(idaapi.ctree_visitor_t):
    
    iis_class_names = [
        "IHttpEventProvider",
        "ICustomNotificationProvider",
        "IHttpStoredContext",
        "IHttpModuleContextContainer",
        "IDispensedHttpModuleContextContainer",
        "IHttpPerfCounterInfo",
        "IHttpApplication",
        "IHttpUrlInfo",
        "IScriptMapInfo",
        "IHttpTokenEntry",
        "IMetadataInfo",
        "IHttpRequest",
        "IHttpCachePolicy",
        "IHttpResponse",
        "IHttpUser",
        "IHttpConnectionStoredContext",
        "IHttpConnectionModuleContextContainer",
        "IHttpConnection",
        "IHttpTraceContext",
        "IHttpFileInfo",
        "IHttpSite",
        "CHttpModule",
        "IHttpContext",
        "IHttpCacheSpecificData",
        "IHttpCacheKey",
        "IHttpFileMonitor",
        "IHttpServer",
        "IHttpCompletionInfo",
        "IAuthenticationProvider",
        "IMapHandlerProvider",
        "IMapPathProvider",
        "ISendResponseProvider",
        "IReadEntityProvider",
        "IPreBeginRequestProvider",
        "IHttpApplicationProvider",
        "IHttpModuleFactory",
        "IHttpApplicationResolveModulesProvider",
        "IGlobalRSCAQueryProvider",
        "IGlobalStopListeningProvider",
        "ICacheProvider",
        "IGlobalConfigurationChangeProvider",
        "IGlobalFileChangeProvider",
        "IGlobalTraceEventProvider",
        "IGlobalThreadCleanupProvider",
        "IGlobalApplicationPreloadProvider",
        "CGlobalModule",
        "IModuleAllocator",
        "IHttpModuleRegistrationInfo"
    ]
    
    def __init__(self, cfunc_t):
        idaapi.ctree_visitor_t.__init__(self, idaapi.CV_FAST)
        self.cfunc_t = cfunc_t
        
        self.variable_names_edited = []
        
    def get_edited_variable_names(self):
        return self.variable_names_edited

    def permanent_change_variable_type(self, variable, cast_type):
        # https://reverseengineering.stackexchange.com/questions/30348/idapython-how-to-reset-pointer-type-for-variables
        variable.set_final_lvar_type(cast_type)

        lsi = ida_hexrays.lvar_saved_info_t()
        lsi.ll = variable
        lsi.type = ida_typeinf.tinfo_t(variable.tif)
        
        ida_hexrays.modify_user_lvar_info(self.cfunc_t.entry_ea, ida_hexrays.MLI_TYPE, lsi)
        
    def get_calling_class_name(self, calling_class_type):
        for iis_class_name in self.iis_class_names:
            if iis_class_name in str(calling_class_type):
                return iis_class_name
                
        return 0
        
    def retype_handler(self, cast_var, var_to_retype, y_op=None):

        calling_class_type = cast_var.type
            
        if not self.get_calling_class_name(calling_class_type):
            return 0
        
        # The try/excepts are pretty hacky, but do a decent enough job
        if y_op == "var":
            try:
                cast_type = cast_var.type.get_rettype()
                
            except:
                cast_type = cast_var.type
        
        else:
            try:
                cast_type = cast_var.x.type.get_rettype()
                
            except:
                try:
                    cast_type = cast_var.x.type
                    
                except:
                    cast_type = cast_var.type
        
        if cast_type and len(str(cast_type)) != 0:
            self.permanent_change_variable_type(var_to_retype, cast_type)
            self.variable_names_edited.append(var_to_retype.name)
    
    def visit_insn(self, i):
        return 0

    def visit_expr(self, e):
        if e.op != idaapi.cot_asg:
            return 0
        
        if e.x.op != idaapi.cot_var:
            return 0
           
        var_to_retype = e.x.v.getv()
        
        if e.y.op == idaapi.cot_call:
                
            if e.y.x.op == idaapi.cot_cast:
                
                if e.y.x:
                    self.retype_handler(e.y.x, var_to_retype, y_op="call")
                
        elif e.y.op == idaapi.cot_cast:
            
            if e.y.x.x:
                self.retype_handler(e.y.x.x, var_to_retype, y_op="cast")
        
        elif e.y.op == idaapi.cot_var:
            self.retype_handler(e.y, var_to_retype, y_op="var")
        
        return 0

class IISHelper_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "IIS Helper Plugin"
    wanted_hotkey = "CTRL-ALT-I"
    help = "Plugin to help analyse native IIS modules"
    wanted_name = "IISHelper"
    hook = None

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        iis_helper = IISHelper()
    
        iis_helper.main()

    def term(self):
        pass

def PLUGIN_ENTRY():
    return IISHelper_t()


repository: bricktea/BDSDevHelper
file: BDSDevHelper.py
description: An IDA plugin to help you develop bedrock dedicated server.
wanted_name: BDSDevHelper
comment: The bedrock server development helper, By: RedbeanW.
created_at: 2022-06-23 15:14:15+00:00
pushed_at: 2023-06-09 15:21:37+00:00
url:
https://github.com/bricktea/BDSDevHelper/blob/af78805d0a1fcdf2a8d80259aa90d8576e794b0f/BDSDevHelper.py

content:
import idaapi
import idautils
import pyperclip as pc
from idc import *
from ida_hexrays import *
from ida_kernwin import *
import tkinter.filedialog as ask_file
import json
import os
import re

_ver = '1.3.0'
_call = '__fastcall'

#------------------------------------------------------------------------------
# IDA Plugin Stub
#------------------------------------------------------------------------------

def PLUGIN_ENTRY():
    return BDSDevHelper()

class BDSDevHelper(ida_idaapi.plugin_t):

    flags = ida_idaapi.PLUGIN_UNL
    comment = 'The bedrock server development helper, By: RedbeanW.'
    help = ''
    wanted_name = 'BDSDevHelper'
    wanted_hotkey = ''

    def init(self):
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        pass

    def term(self):
        pass

class Hooks(idaapi.UI_Hooks):

    def populating_widget_popup(self, widget, popup, ctx):
        
        if ida_kernwin.get_widget_type(widget) != ida_kernwin.BWN_DISASM:
            return

        ida_kernwin.attach_action_to_popup(widget, popup, asTInstanceHook.NAME, 'Add breakpoint', ida_kernwin.SETMENU_APP)
        ida_kernwin.attach_action_to_popup(widget, popup, asTClasslessInstanceHook.NAME, 'Add breakpoint', ida_kernwin.SETMENU_APP)
        ida_kernwin.attach_action_to_popup(widget, popup, asSymCall.NAME, 'Add breakpoint', ida_kernwin.SETMENU_APP)
        ida_kernwin.attach_action_to_popup(widget, popup, asTHook.NAME, 'Add breakpoint', ida_kernwin.SETMENU_APP)
        ida_kernwin.attach_action_to_popup(widget, popup, '-',  asTHook.TEXT, ida_kernwin.SETMENU_INS)

    def ready_to_run(self, *args):
        for action in PLUGIN_ACTIONS:
            desc = ida_kernwin.action_desc_t(
                action.NAME,
                action.TEXT,
                action(self),
                '',
                action.TOOLTIP,
                -1
            )
            if not ida_kernwin.register_action(desc):
                print('[x] Register action "%s" failed!' % action.NAME)
        
        ida_kernwin.attach_action_to_menu('Edit/BDSDevHelper/Export struct size information', exportAllSize.NAME, ida_kernwin.SETMENU_INS)
        ida_kernwin.attach_action_to_menu('Edit/BDSDevHelper/Export all til information', exportTIL.NAME, ida_kernwin.SETMENU_INS)
        
        print('[*] BDS Dev Helper is loaded, ver %s.' % _ver)
        print('[*] By: RedbeanW.')

hooks = Hooks()
hooks.hook()

#------------------------------------------------------------------------------
# Utils
#------------------------------------------------------------------------------

class Utils():

    @staticmethod
    def fixIdaBugAndGetArguments(func):
        m = func.argidx
        n = func.arguments
        if m[0] == 1:
            n[0],n[1] = n[1],n[0]
        return n

    @staticmethod
    def cleanType(name,NoPointer = False):
        rtn = str()
        for t in name.split(' '):
            if t != 'const' and t != 'struct' and t != 'class':
                rtn = rtn + t + ' '
        if NoPointer:
            rtn = rtn.replace('*','')
        return rtn.strip()

    @staticmethod
    def getMemberType(sid, offset):
        struct = ida_struct.get_struc(sid)
        member = ida_struct.get_member(struct, offset)
        tif = ida_typeinf.tinfo_t()
        if ida_struct.get_member_tinfo(tif, member):
            return tif.__str__() 
        return ''

#------------------------------------------------------------------------------
# Registered Actions
#------------------------------------------------------------------------------

class exportAllSize(ida_kernwin.action_handler_t):
    NAME = 'helper:ExportSizeInfor'
    TEXT = 'Analyze and export all size information.'
    TOOLTIP = '...experiment feature...'

    def __init__(self, core):
        ida_kernwin.action_handler_t.__init__(self)
        self.core = core

    def activate(self, ctx):
        # ask file name
        file_name = ask_file.asksaveasfilename(
            title = 'Where to save size data?',
            filetypes = [('Json File','*.json'),('All files','*')],
            initialdir = os.getcwd()
            )
        if file_name[len(file_name) - 5:] != '.json':
            file_name = file_name + '.json'
        # get all valid types
        til = ida_typeinf.get_idati()
        banned_prefix = [
            '_',
            '$',
            '_lambda_',
            'std::',
            'in_addr',
            'tag'
        ]
        def guess_constructor_starts(type_name: str) -> str:
            pos = type_name.rfind('::')
            func_name = ''
            if pos == -1:
                func_name = type_name
            else:
                func_name = type_name[pos + 2:]
            return '%s::%s(' % (type_name, func_name)
        valid_types = {}
        for num in range(1, ida_typeinf.get_ordinal_qty(til) + 1):
            tinfo = ida_typeinf.tinfo_t()
            if not tinfo.get_numbered_type(til, num):
                continue
            name = tinfo.get_type_name()
            try:
                for word in banned_prefix:
                    if name[:len(word)] == word:
                        raise -1
            except:
                continue
            valid_types[name] = {
                'guessed_constructor_starts': guess_constructor_starts(name),
                'matched_constructors': {},
                'final_guessed_size': 0
            }
        first_valid_types_count = len(valid_types)
        print('[+] Found %d valid types.' % first_valid_types_count)
        # get all functions
        count = ida_name.get_nlist_size()
        all_constructor_count = 0
        matched_count = 0
        regex = {
            # match like: *((T *)this + 11), *(T *)(this + 4), *(T1 *)((T2 *)this + 5), got all
            'references_a': re.compile('(((?:\*.+?\((?:\S*) \*\)|\*\(.+?)(?:this|a1) \+ (?:\d*)\)))(?: = )'),
            # match like: this[14], got all
            'references_b': re.compile('((?:this|a1)\[\d+\])'),
            # match like: this[19], got '19'
            'single_a': re.compile('(?:this|a1)\[(\d+)\]'),
            # match like: *((T *)this + 19), got 'T' and '19'
            'single_b': re.compile('\*\(\((.+) \*\)(?:this|a1) \+ (\d+)\)'),
            # match like: *(T *)(this + 8), got 'T' and '8'
            'single_c': re.compile('\*\((.+) \*\)\((?:this|a1) \+ (\d+)\)'),
            # match like: *(T1 *)((T2 *)this + 10), got 'T1', 'T2' and '10'
            'single_d': re.compile('\*\((.+) \*\)\(\((.+) \*\)(?:this|a1) \+ (\d+)\)')

        }
        for i in range(count):
            name:str = ida_name.get_nlist_name(i)
            if name.startswith('??0'): # constructor
                demangled = demangle_name(name, get_inf_attr(INF_SHORT_DN))
                if not demangled:
                    continue
                all_constructor_count += 1
                for j in valid_types:
                    if demangled[:len(valid_types[j]['guessed_constructor_starts'])] == valid_types[j]['guessed_constructor_starts']: # matched.
                        ea = ida_name.get_nlist_ea(i)
                        cfunc = decompile(ea)
                        matched = []
                        for k in cfunc.get_pseudocode():
                            line = ida_lines.tag_remove(k.line)
                            res = re.search(regex['references_a'], line) or re.search(regex['references_b'], line)
                            if res:
                                matched += list(res.groups())
                        valid_types[j]['matched_constructors'][demangled] = {
                            'ea': ea,
                            'guessed_size': 0,
                            'references': list(set(matched))
                        }
                        matched_count += 1
        print('[+] Found %d constructors, matched %d (%.2f%%)' % (all_constructor_count, matched_count, matched_count / all_constructor_count * 100))
        for i in list(valid_types.keys()):
            if len(valid_types[i]['matched_constructors']) == 0:
                valid_types.pop(i)
        second_valid_types_count = len(valid_types)
        print('[!] %d non-matched types removed (%.2f%%)' % (first_valid_types_count - second_valid_types_count, (1 - second_valid_types_count / first_valid_types_count) * 100))
        # analysis structure size
        def guess_size(refs: list) -> int:
            def type2size(type_name: str) -> int:
                data = {
                    'POINTER': 8,
                    'struct UUID': 16,
                    '_DWORD': 4,
                    '_WORD': 2,
                    '_QWORD': 8,
                    '_BYTE': 1,
                    '_OWORD': 16,
                    'char': 1,
                    'float': 4,
                    'double': 8,
                    '__m128d': 16,
                    '__m128i': 16
                }
                if type_name in data:
                    return data[type_name]
                else:
                    print('[!] Can\'t handle unknown type name %s.' % type_name)
                    raise -1
            try:
                largest = 0
                for ref in refs:
                    size = 0
                    res = re.search(regex['single_a'], ref) #a
                    if res:
                        size = (int(res.group(1)) + 1) * type2size('POINTER')
                    else:
                        res = re.search(regex['single_b'], ref) #b
                        if res:
                            type = res.group(1)
                            offset = int(res.group(2))
                            size = type2size(type) * (offset + 1)
                        else:
                            res = re.search(regex['single_c'], ref) #c
                            if res:
                                type = res.group(1)
                                offset = int(res.group(2))
                                size = offset + type2size(type)
                            else:
                                res = re.search(regex['single_d'], ref) #d
                                if res:
                                    type1 = res.group(1)
                                    type2 = res.group(2)
                                    offset = int(res.group(3))
                                    size = offset * type2size(type2) + type2size(type1)
                    if size == 0:
                        print('[!] Unmatched expression found: %s' % ref)
                        raise -1
                    if size > largest:
                        largest = size
                return largest
            except:
                return 0
        for name in valid_types:
            constructors = valid_types[name]['matched_constructors']
            largest_size = 0
            for demangled in constructors:
                size = guess_size(constructors[demangled]['references'])
                constructors[demangled]['guessed_size'] = size
                if size > largest_size:
                    largest_size = size
            valid_types[name]['final_guessed_size'] = largest_size
        # save data.
        with open(file_name, 'w') as file:
            file.write(json.dumps(valid_types, indent = 4))
        print('[+] Data saved to: %s' % file_name)

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

class exportTIL(ida_kernwin.action_handler_t):

    NAME = 'helper:export'
    TEXT = 'Export all til information.'
    TOOLTIP = 'Export all structure & enums & local types information from idb.'

    def __init__(self, core):
        ida_kernwin.action_handler_t.__init__(self)
        self.core = core
    
    def activate(self, ctx):

        # ask file name
        file_name = ask_file.asksaveasfilename(
            title = 'Where to save structure data?',
            filetypes = [('Json File','*.json'),('All files','*')],
            initialdir = os.getcwd()
            )
        if file_name[len(file_name)-5:] != '.json':
            file_name = file_name + '.json'
        
        # global data
        j = {
            'structure': list(),
            'enums': list()
        }
        banned_prefix = [
            '_',
            '$',
            'baseclass_',
            'vn_',
            'vna_',
            'gap',
            'st_',
            'std::',
            'entt::',
            'Elf',
            'gsl::',
            'grpc::',
            'boost::',
            'JsonUtil::JsonSchema',
            'JsonUtil::JsonParseState'
        ]
        temp_added = dict()
        print('[!] Initialization, %d prefixes will be skipped.' % len(banned_prefix))

        # get structure information.
        for idx,sid,struct in idautils.Structs():
            struct = str(struct)
            try:
                for word in banned_prefix:
                    if struct[:len(word)] == word:
                        raise -1
            except:
                continue
            members = []
            for offset,mem,size in idautils.StructMembers(sid):
                mem = str(mem)
                members.append({
                    'name': mem,
                    'type': Utils.getMemberType(sid,offset),
                    'offset': offset,
                    'size': size
                })
            temp_added[struct] = 1
            j['structure'].append({
                    'struct_name': struct,
                    'members': members
                })
        print('[+] %d structures are exported.' % len(j['structure']))
        
        # get enum information.
        for i in range(ida_enum.get_enum_qty()):
            enum = ida_enum.getn_enum(i)
            name = ida_enum.get_enum_name(enum)
            try:
                for word in banned_prefix:
                    if name[:len(word)] == word:
                        raise -1
            except:
                continue
            members = list()

            class visitor(ida_enum.enum_member_visitor_t):
                def visit_enum_member(self, cid, value):
                    members.append({
                        'name': ida_enum.get_enum_member_name(cid),
                        'value': value,
                    })
                    return 0

            ida_enum.for_all_enum_members(enum, visitor())
            j['enums'].append({
                'enum_name': name,
                'members': members,
            })
            temp_added[name] = 1
        print('[+] %d enums are exported.' % len(j['enums']))

        # get local type information(supplement)
        til = ida_typeinf.get_idati()
        count = 0
        for ordinal in range(ida_typeinf.get_ordinal_qty(til)):
            tinfo = ida_typeinf.tinfo_t()
            if tinfo.get_numbered_type(til, ordinal):
                typen = str(tinfo)
                clss = str()
                clss2 = str()
                try:
                    if tinfo.is_enum():
                        clss = 'enums'
                        clss2 = 'enum_name'
                    elif tinfo.is_struct():
                        clss = 'structure'
                        clss2 = 'struct_name'
                    else:
                        raise -3
                    if typen in temp_added:
                        raise -1
                    for word in banned_prefix:
                        if typen[:len(word)] == word:
                            raise -2
                except:
                    continue                
                j[clss].append({
                    '%s'%clss2: typen,
                    'declaration': GetLocalType(ordinal,ida_typeinf.PRTYPE_TYPE + ida_typeinf.PRTYPE_SEMI + ida_typeinf.PRTYPE_MULTI)
                })
                count += 1
        print('[+] %d local types have been added.' % count)
        # save data.
        with open(file_name, 'w') as file:
            file.write(json.dumps(j, indent=4))
        print('[+] Data saved to: %s' % file_name)

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

class asTHook(ida_kernwin.action_handler_t):
    NAME = 'helper:THook'
    TEXT = 'Copy as THook'
    TOOLTIP = 'Generate the THook macro for this function.'

    TEMPLATE = 'THook({return_type}, \"{symbol}\"{format_if_args}{call_args})\n{{\n    {return_if_not_void}original({return_args});\n}}'

    def __init__(self, core):
        ida_kernwin.action_handler_t.__init__(self)
        self.core = core

    def activate(self, ctx):
        ea = here()
        if ea == idaapi.BADADDR:
            print('[x] Get ea failed.')
            return

        # get function.
        symbol = get_name(ea)
        if not symbol or symbol == '':
            print('[x] Get symbol failed.')
            return
        func_data = decompile(ea)
        func_detail = str(func_data.type)
        return_type = Utils.cleanType(func_detail[0:func_detail.find(_call)])
        call_args = str()
        return_args = str()
        FirstArg = True
        for i in Utils.fixIdaBugAndGetArguments(func_data):
            if FirstArg and i.name == 'this':
                arg_name = 'self'
            else:
                arg_name = i.name
            call_args = call_args + Utils.cleanType(str(i.tif)) + ' ' + arg_name + ', '
            return_args = return_args + arg_name + ', '
        call_args = call_args[0:len(call_args)-2]
        return_args = return_args[0:len(return_args)-2]

        # handles.
        if call_args != '':
            format_if_args = ', \n    '
        else:
            format_if_args = ''
        if return_type != 'void':
            return_if_not_void = 'return '
        else:
            return_if_not_void = ''
        
        # format.
        generated = self.TEMPLATE.format(
            return_type = return_type,
            symbol = symbol,
            format_if_args = format_if_args,
            call_args = call_args,
            return_if_not_void = return_if_not_void,
            return_args = return_args
        )

        pc.copy(generated)
        print('[+] THook here is copid.')
        return 1

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

class asTInstanceHook(ida_kernwin.action_handler_t):
    NAME = 'helper:TInstanceHook'
    TEXT = 'Copy as TInstanceHook'
    TOOLTIP = 'Generate the TInstanceHook macro for this function.'

    TEMPLATE = 'TInstanceHook({return_type}, \"{symbol}\", \n    {this_pointer_type}{format_if_args_short}{call_args})\n{{\n    {return_if_not_void}original(this{format_if_args_short}{return_args});\n}}'

    def __init__(self, core):
        ida_kernwin.action_handler_t.__init__(self)
        self.core = core

    def activate(self, ctx):
        ea = here()
        if ea == idaapi.BADADDR:
            print('[x] Get ea failed.')
            return

        # get function.
        symbol = get_name(ea)
        if not symbol or symbol == '':
            print('[x] Get symbol failed.')
            return
        func_data = decompile(ea)
        func_detail = str(func_data.type)
        return_type = Utils.cleanType(func_detail[0:func_detail.find(_call)])
        call_args = str()
        return_args = str()
        FirstArg = True
        for i in Utils.fixIdaBugAndGetArguments(func_data):
            if FirstArg:
                this_pointer_type = Utils.cleanType(str(i.tif),True)
                FirstArg = False
            else:
                call_args = call_args + Utils.cleanType(str(i.tif)) + ' ' + i.name + ', '
                return_args = return_args + i.name + ', '
        call_args = call_args[0:len(call_args)-2]
        return_args = return_args[0:len(return_args)-2]

        # handles.
        if call_args != '':
            format_if_args_short = ', '
        else:
            format_if_args_short = ''
        if return_type != 'void':
            return_if_not_void = 'return '
        else:
            return_if_not_void = ''
        
        # format.
        generated = self.TEMPLATE.format(
            return_type = return_type,
            symbol = symbol,
            this_pointer_type = this_pointer_type,
            format_if_args_short = format_if_args_short,
            call_args = call_args,
            return_if_not_void = return_if_not_void,
            return_args = return_args
        )

        pc.copy(generated)
        print('[+] TInstanceHook here is copid.')
        return 1

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

class asTClasslessInstanceHook(ida_kernwin.action_handler_t):
    NAME = 'helper:TClasslessInstanceHook'
    TEXT = 'Copy as TClasslessInstanceHook'
    TOOLTIP = 'Generate the TClasslessInstanceHook macro for this function.'

    TEMPLATE = 'TClasslessInstanceHook({return_type}, \"{symbol}\"{format_if_args}{call_args})\n{{\n    {return_if_not_void}original(this{format_if_args_short}{return_args});\n}}'

    def __init__(self, core):
        ida_kernwin.action_handler_t.__init__(self)
        self.core = core

    def activate(self, ctx):
        ea = here()
        if ea == idaapi.BADADDR:
            print('[x] Get ea failed.')
            return

        # get function.
        symbol = get_name(ea)
        if not symbol or symbol == '':
            print('[x] Get symbol failed.')
            return
        func_data = decompile(ea)
        func_detail = str(func_data.type)
        return_type = Utils.cleanType(func_detail[0:func_detail.find(_call)])
        call_args = str()
        return_args = str()
        FirstArg = True
        for i in Utils.fixIdaBugAndGetArguments(func_data):
            if FirstArg:
                FirstArg = False
                continue
            call_args = call_args + Utils.cleanType(str(i.tif)) + ' ' + i.name + ', '
            return_args = return_args + i.name + ', '
        call_args = call_args[0:len(call_args)-2]
        return_args = return_args[0:len(return_args)-2]

        # handles.
        if call_args != '':
            format_if_args = ', \n    '
            format_if_args_short = ', '
        else:
            format_if_args = ''
            format_if_args_short = ''
        if return_type != 'void':
            return_if_not_void = 'return '
        else:
            return_if_not_void = ''
        
        # format.
        generated = self.TEMPLATE.format(
            return_type = return_type,
            symbol = symbol,
            format_if_args = format_if_args,
            format_if_args_short = format_if_args_short,
            call_args = call_args,
            return_if_not_void = return_if_not_void,
            return_args = return_args
        )

        pc.copy(generated)
        print('[+] TClasslessInstanceHook here is copid.')
        return 1

    def update(self, ctx):
        return ida_kernwin.AST_ENABLE_FOR_IDB

class asSymCall(ida_kernwin.action_handler_t):
    NAME = 'helper:SymCall'
    TEXT = 'Copy as SymCall'
    TOOLTIP = 'Use SymCall to call this function.'

    TEMPLATE = 'SymCall(\"{symbol}\",\n    {return_type}{format_if_args_short}{call_types}){need_if_args};'

    def __init__(self, core):
        ida_kernwin.action_handler_t.__init__(self)
        self.core = core

    def activate(self, ctx):
        ea = here()
        if ea == idaapi.BADADDR:
            print('[x] Get ea failed.')
            return

        # get function.
        symbol = get_name(ea)
        if not symbol or symbol == '':
            print('[x] Get symbol failed.')
            return
        func_data = decompile(ea)
        func_detail = str(func_data.type)
        return_type = Utils.cleanType(func_detail[0:func_detail.find(_call)])
        call_types = str()
        for i in Utils.fixIdaBugAndGetArguments(func_data):
            call_types = call_types + Utils.cleanType(str(i.tif)) + ', '
        call_types = call_types[0:len(call_types)-2]

        # handles.
        if call_types != '':
            format_if_args_short = ', '
            need_if_args = '()'
        else:
            format_if_args_short = ''
            need_if_args = ''
        
        # format.
        generated = self.TEMPLATE.format(
            return_type = return_type,
            symbol = symbol,
            format_if_args_short = format_if_args_short,
            need_if_args = need_if_args,
            call_types = call_types
        )

        pc.copy(generated)
        print('[+] SymCall here is copid.')
        return 1

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

PLUGIN_ACTIONS = \
[
    exportAllSize,
    exportTIL,
    asTHook,
    asTInstanceHook,
    asTClasslessInstanceHook,
    asSymCall
]


repository: idapython/src
file: examples/disassembler/install_user_defined_prefix.py
description: IDAPython project for Hex-Ray's IDA Pro
wanted_name: user defined prefix
comment: This is a user defined prefix sample plugin
created_at: 2015-03-14 20:09:27+00:00
pushed_at: 2025-03-13 15:18:11+00:00
url:
https://github.com/idapython/src/blob/af978e79e68af701c45bab401afce5145aa4b80b/examples/disassembler/install_user_defined_prefix.py

content:
"""
summary: insert information into listing prefixes

description:
  By default, disassembly line prefixes contain segment + address
  information (e.g., '.text:08047718'), but it is possible to
  "inject" other bits of information in there, thanks to the
  `ida_lines.user_defined_prefix_t` helper type.

level: beginner
"""

import ida_lines
import ida_idaapi

PREFIX = ida_lines.SCOLOR_INV + " " + ida_lines.SCOLOR_INV

class my_user_prefix_t(ida_lines.user_defined_prefix_t):
    def get_user_defined_prefix(self, ea, insn, lnnum, indent, line):
        if (ea % 2 == 0) and indent == -1:
            return PREFIX
        else:
            return ""


class prefix_plugin_t(ida_idaapi.plugin_t):
    flags = 0
    comment = "This is a user defined prefix sample plugin"
    help = "This is help"
    wanted_name = "user defined prefix"
    wanted_hotkey = ""

    def __init__(self):
        self.prefix = None

    def init(self):
        self.prefix = my_user_prefix_t(8)
        print("prefix installed")
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        pass

    def term(self):
        self.prefix = None
        print("prefix uninstalled!")


def PLUGIN_ENTRY():
    return prefix_plugin_t()


repository: Synacktiv-contrib/kernelcache-laundering
file: ios12_kernel_cache_helper.py
description: load iOS12 kernelcaches and PAC code in IDA
wanted_name: iOS12 kernelcache helper
comment: iOS12 kernelcache helper - (C) Synacktiv
created_at: 2018-10-01 08:59:35+00:00
pushed_at: 2018-10-04 09:01:04+00:00
url:
https://github.com/Synacktiv-contrib/kernelcache-laundering/blob/6fde371d3cf04e625d24f5155227bfa017e1cd11/ios12_kernel_cache_helper.py

content:
# Copyright (c) 2018, Eloi Benoist-Vanderbeken - Synacktiv
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Synacktiv nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import idaapi

def fix_relocs(base_address, relocs_address, relocs_size):
	cursor = relocs_address
	end = relocs_address+relocs_size

	multiplier = 4 * (idaapi.get_dword(cursor) & 1) + 4
	cursor += 4
	
	print 'starting to fix relocs...'
	nb_relocs = 0

	delta = idaapi.get_dword(cursor)
	while delta != 0xFFFFFFFF and cursor < end:
		current_reloc = base_address + delta
		while True:
			decorated_addr = idaapi.get_qword(current_reloc)
			if decorated_addr & 0x4000000000000000 == 0:
				if decorated_addr & 0x8000000000000000:
					#tagged ptr
					sign_type = (decorated_addr >> 49) & 3
					real_addr = base_address + (decorated_addr & 0xFFFFFFFF)
					modifier = ((decorated_addr >> 32) & 0xFFFF)
					if decorated_addr & 0x1000000000000:
						if modifier == 0:
							modifier = current_reloc
							modifier_type = 'ptr_addr'
						else:
							modifier_type = '0x%X << 48 | ptr_addr & 0xFFFFFFFFFFFF'%(modifier)
							modifier = (current_reloc & 0xFFFFFFFFFFFF) | (modifier << 48)
					else:
						modifier_type = '0x%X'%modifier
					if sign_type == 0:
						decorator = 'PACIA %s'%modifier_type if modifier else 'PACIZA'
					elif sign_type == 1:
						decorator = 'PACIB %s'%modifier_type if modifier else 'PACIZB'
					elif sign_type == 2:
						decorator = 'PACDA %s'%modifier_type if modifier else 'PACDZA'
					elif sign_type == 3:
						decorator = 'PACDB %s'%modifier_type if modifier else 'PACDZB'
					idaapi.set_cmt(current_reloc , decorator, 1)
				else:
					real_addr = ((decorated_addr << 13) & 0xFF00000000000000) | (decorated_addr & 0x7ffffffffff)
					if decorated_addr & 0x40000000000:
						real_addr |= 0xfffc0000000000
				idaapi.patch_qword(current_reloc, real_addr)
				idaapi.op_offset(current_reloc, 0, idaapi.REF_OFF64)
				nb_relocs += 1
			delta_next_reloc = ((decorated_addr >> 51) & 0x7ff) * multiplier
			if delta_next_reloc == 0:
				break
			current_reloc += delta_next_reloc
		cursor += 4
		delta = idaapi.get_dword(cursor)
	print '%d relocs fixed!'%nb_relocs

def is_iOS_12(mach_header):
	if idaapi.get_dword(mach_header) != 0xFEEDFACF:
		return False
	nb_load_commands = idaapi.get_dword(mach_header + 0x10)
	if nb_load_commands > 100: #arbitrary limit
		return False
	sizeof_load_commands = idaapi.get_dword(mach_header + 0x14)
	if sizeof_load_commands > 0x2000: #arbitrary limit
		return False
	load_command_limit = mach_header + 0x20 + sizeof_load_commands
	current_load_command = mach_header + 0x20
	for i in xrange(nb_load_commands):
		if current_load_command + 8 > load_command_limit:
			return False
		cmd = idaapi.get_dword(current_load_command)
		cmdsize = idaapi.get_dword(current_load_command+4)
		if cmdsize < 8 or current_load_command + cmdsize > load_command_limit:
			return False
		if cmd == 0x32: # LC_BUILD_VERSION
			platform = idaapi.get_dword(current_load_command+8)
			if platform != 2: # PLATFORM_IOS
				return False
			minos = idaapi.get_dword(current_load_command+8+4)
			if minos >> 16 != 12: # 12.X.X
				return False
			sdk = idaapi.get_dword(current_load_command+8+8)
			ntools = idaapi.get_dword(current_load_command+8+12)
			return True
		current_load_command += cmdsize
	else:
		return False

class IDBHook(idaapi.IDB_Hooks):
	is_iOS12 = False
	is_kernelcache = False
	relocs_address = None
	def segm_added(self, segm):
		if idaapi.get_segm_name(segm) == 'HEADER':
			self.base_address = segm.startEA
			self.is_iOS12 = is_iOS_12(self.base_address)
		if idaapi.get_segm_name(segm) == '__thread_starts':
			self.relocs_address = segm.startEA
			self.relocs_size = segm.endEA-segm.startEA
		if idaapi.get_segm_name(segm) == '__kmod_info':
			self.is_kernelcache = True
			if self.is_iOS12 and self.relocs_address is not None:
				print 'iOS12 kernelcache detected!'
				print 'let\'s fix relocs'
				fix_relocs(self.base_address, self.relocs_address, self.relocs_size)
		return 0


class IOS12KernelcacheHelper(idaapi.plugin_t):
	flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE | idaapi.PLUGIN_MOD
	wanted_name = 'iOS12 kernelcache helper'
	comment = "iOS12 kernelcache helper - (C) Synacktiv"
	wanted_hotkey = ""
	help = "Runs transparently"
	idbhook = None

	def init(self):
		if idaapi.ph_get_id() != idaapi.PLFM_ARM or idaapi.cvar.inf.filetype != idaapi.f_MACHO:
			return idaapi.PLUGIN_SKIP
		self.idbhook = IDBHook()
		self.idbhook.hook()
		print self.comment
		return idaapi.PLUGIN_KEEP

	def run(self, arg):
		pass

	def term(self):
		if self.idbhook is not None:
			self.idbhook.unhook()

def PLUGIN_ENTRY():
	return IOS12KernelcacheHelper()


repository: nocsi/tree-cbass
file: SourceCode/trunk/TREE/TREE_Tracer.py
wanted_name: TREE Tracer
comment: TREE Tracer plugin for IDA
created_at: 2016-11-24 04:32:40+00:00
pushed_at: 2016-11-26 00:00:28+00:00
url:
https://github.com/nocsi/tree-cbass/blob/c0a0cc75a7816bcf6d4f12561ccb0686520c1c66/SourceCode/trunk/TREE/TREE_Tracer.py

content:
# TREE - Taint-enabled Reverse Engineering Environment 
# Copyright (c) 2013 Battelle BIT Team - Nathan Li, Xing Li, Loc Nguyen
#
# All rights reserved.
#
# For detailed copyright information see the file license.txt in the IDA PRO plugins folder
#---------------------------------------------------------------------
# TREE_Tracer.py - TREE Tracer plugin for IDA Pro
#---------------------------------------------------------------------

import os
import time

import idaapi
import idc

from idaapi import PluginForm
from PySide import QtGui
from PySide.QtGui import QIcon

from dispatcher.widgets.TraceGeneratorWidget import TraceGeneratorWidget

from dispatcher.core.DebugPrint import dbgPrint, Print

from dispatcher.core.Util import ConfigReader

NAME = "TREE Tracer"

from dispatcher.core.structures.Tracer.Arch.x86.Windows import WindowsApiCallbacks as WindowsApiCallbacks
from dispatcher.core.structures.Tracer.Arch.x86.Linux import LinuxApiCallbacks as LinuxApiCallbacks
from dispatcher.core.structures.Tracer import InteractivemodeCallbacks as InteractivemodeCallbacks

windowsFileIO = None
linuxFileIO = None
interactivemodeCallback = None

#Need to have this first, evaluate everything with Python not IDC
idaapi.enable_extlang_python(True)

class TreeTracerPluginFormClass(PluginForm):
    """
    Tree Tracer plugin form
    """
    
    def __init__(self):
        super(TreeTracerPluginFormClass, self).__init__()
        self.idaPluginDir = os.path.join(GetIdaDirectory(),"plugins")
        print self.idaPluginDir
        self.iconPath = os.path.join(self.idaPluginDir, "dispatcher","icons")
        self.icon = QIcon( self.iconPath )
        ini_path = os.path.join(self.idaPluginDir,"settings.ini")
        print ini_path
        configReader = ConfigReader()
        configReader.Read(ini_path)

        self.version= configReader.version
        
    def setupWidgets(self):
        """
        Setup dispatcher widgets.
        """
        time_before = time.time()
        
        Print ("[/] setting up widgets...")
        global windowsFileIO,windowsNetworkIO,linuxFileIO,interactivemodeCallback
        
        windowsFileIO = WindowsApiCallbacks.FileIO()
        windowsNetworkIO = WindowsApiCallbacks.NetworkIO()
        linuxFileIO = LinuxApiCallbacks.FileIO()
        interactivemodeCallback = InteractivemodeCallbacks.InteractivemodeFunctions()
        functionCallbacks = dict()
        functionCallbacks = {'windowsFileIO':windowsFileIO ,'linuxFileIO':linuxFileIO ,'windowsNetworkIO':windowsNetworkIO , 'interactivemodeCallback':interactivemodeCallback}
        
        layout = QtGui.QVBoxLayout()
        layout.addWidget(TraceGeneratorWidget(self,functionCallbacks))
        self.parent.setLayout(layout)

        Print("[\\] this took %3.2f seconds.\n" % (time.time() - time_before))
        
    def OnCreate(self, form):
        """
        When creating the form, setup the modules and widgets
        """
        print("OnCreate Called.")
        self.printBanner()
        self.parent = self.FormToPySideWidget(form)
        self.parent.setWindowIcon(self.icon)
        self.setupWidgets()

    def OnClose(self, form):
        """
        Called when the plugin form is closed
        """
        print("Plugin form closing.")

    def printBanner(self):
        """
        Prints the banner for the TREE Tracer plugin
        """
        
        banner = "#############################################\n" \
               + " ___________________________________________ \n" \
               + " \__    ___/\______   \_   _____/\_   _____/ \n" \
               + "    |    |    |       _/|    __)_  |    __)_ \n" \
               + "    |    |    |    |   \|        \ |        \ \n" \
               + "    |____|    |____|_  /_______  //_______  / \n" \
               + "                     \/        \/         \/  \n" \
               + "#############################################\n" \
               + " Taint-enabled Reverse Engineering Environment\n" \
               + " by Battelle BIT Team                       \n" \
               + "#############################################\n"
        print banner
        print ("[+] Loading TREE Tracer version %s" % self.version)

    def Show(self):
        """
        Called when the plugin form is visible
        """
        
        if idc.GetInputMD5() == None:
            return
        else:
            return PluginForm.Show(self,
                NAME,
                options=(PluginForm.FORM_CLOSE_LATER | PluginForm.FORM_RESTORE | PluginForm.FORM_SAVE))

class tracer_plugin_t(idaapi.plugin_t):
    """
    TREE Tracer plugin
    """
    flags = idaapi.PLUGIN_UNL
    help = ""
    comment = "TREE Tracer plugin for IDA"
    wanted_name = "TREE Tracer"
    wanted_hotkey = "Ctrl-F7"

    def init(self):
        Print("tracer_plugin_t installed")
        ExTraces = None
        ExTraces = idaapi.netnode("$ ExTraces", 0, False) #Get the execution trace id
        data = ExTraces.getblob(0, 'A')
        if data is None:
            print "This IDB has no TREE Trace. Turn ON TREE Tracer"
            return idaapi.PLUGIN_OK
        else:
            print "This IDB has TREE Trace. Turn OFF TREE Trace"
            return idaapi.PLUGIN_SKIP

    def run(self, arg):
        """
        Called when the plugin runs
        """
        
        Print("tracer_plugin_t run!")
        plg = TreeTracerPluginFormClass()
        plg.Show()
        
        return

    def term(self):
        """
        Called when the plugsin terminates
        """
        
        Print("tracer_plugin_t uninstalled!")

def PLUGIN_ENTRY():
    return tracer_plugin_t()


repository: Pr0214/trace_natives
file: traceNatives.py
description: 一个小脚本，用于trace so中native函数的调用。
wanted_name: traceNatives
comment: traceNatives
created_at: 2021-03-21 14:34:42+00:00
pushed_at: 2021-08-07 11:21:57+00:00
url:
https://github.com/Pr0214/trace_natives/blob/054c7108f90e9f3c2d06333ec5436ce25816c835/traceNatives.py

content:
# -*- coding:utf-8 -*-
import os
from idaapi import plugin_t
from idaapi import PLUGIN_PROC
from idaapi import PLUGIN_OK
import ida_nalt
import idaapi
import idautils
import idc
import time


# 获取SO文件名和路径
def getSoPathAndName():
    fullpath = ida_nalt.get_input_file_path()
    filepath,filename = os.path.split(fullpath)
    return filepath,filename

# 获取代码段的范围
def getSegAddr():
    textStart = []
    textEnd = []

    for seg in idautils.Segments():
        if (idc.get_segm_name(seg)).lower() == '.text' or (
        idc.get_segm_name(seg)).lower() == 'text':
            tempStart = idc.get_segm_start(seg)
            tempEnd = idc.get_segm_end(seg)

            textStart.append(tempStart)
            textEnd.append(tempEnd)

    return min(textStart), max(textEnd)


class traceNatives(plugin_t):
    flags = PLUGIN_PROC
    comment = "traceNatives"
    help = ""
    wanted_name = "traceNatives"
    wanted_hotkey = ""

    def init(self):
        print("traceNatives(v0.1) plugin has been loaded.")
        return PLUGIN_OK

    def run(self, arg):
        # 查找需要的函数
        ea, ed = getSegAddr()
        search_result = []
        for func in idautils.Functions(ea, ed):
            try:
                functionName = str(idaapi.ida_funcs.get_func_name(func))
                if len(list(idautils.FuncItems(func))) > 10:
                    # 如果是thumb模式，地址+1
                    arm_or_thumb = idc.get_sreg(func, "T")
                    if arm_or_thumb:
                        func += 1
                    search_result.append(hex(func))
            except:
                pass

        so_path, so_name = getSoPathAndName()
        search_result = [f"-a '{so_name}!{offset}'" for offset in search_result]
        search_result = " ".join(search_result)

        script_name = so_name.split(".")[0] + "_" + str(int(time.time())) +".txt"
        save_path = os.path.join(so_path, script_name)
        with open(save_path, "w", encoding="utf-8")as F:
            F.write(search_result)

        print("使用方法如下：")
        print(f"frida-trace -UF -O {save_path}")

    def term(self):
        pass


def PLUGIN_ENTRY():
    return traceNatives()


repository: ohjeongwook/binkit
file: src/plugin/binkit_plugin.py
description: Binary Reverse Engineering Data Science Kit
wanted_name: Binkit
comment: Binkit Plugin For IDA
created_at: 2020-05-23 00:10:32+00:00
pushed_at: 2020-10-05 01:35:07+00:00
url:
https://github.com/ohjeongwook/binkit/blob/cfd183d5fa2860f78071d35424d55cae8ca80e60/src/plugin/binkit_plugin.py

content:
import os
import idautils
import idaapi
import idc
import thread
import traceback
import threading

from binkit.viewer import *
from binkit.service import *
import binkit.binaries
import binkit.python

class LoadResultsHandler(idaapi.action_handler_t):
    def __init__(self):
        idaapi.action_handler_t.__init__(self)
        self.viewer_sequence = 0

    def get_filename(self):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(None, "QFileDialog.getOpenFileName()", "","All Files (*);;JSON (*.json)", options=options)
        return filename

    def load_results(self):
        filename = self.get_filename()
        if filename and os.path.isfile(filename):
            viewer = Viewer(filename)
            form_name = "Function Matches-%d" % self.viewer_sequence
            self.viewer_sequence += 1
            viewer.show_functions_match_viewer(form_name)
            idaapi.set_dock_pos(form_name, "Functions window", idaapi.DP_TAB)

    def activate(self, ctx):
        self.load_results()
        return 1

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

class DiffHandler(idaapi.action_handler_t):
    def __init__(self):
        idaapi.action_handler_t.__init__(self)
        self.viewer_sequence = 0
        self.use_process = True

    def get_filename(self):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(None, "QFileDialog.getOpenFileName()", "","All Files (*);;DB (*.db)", options=options)
        return filename

    def match_thread(self, src_filename, target_filename):
        print('match_thread:', src_filename, target_filename)
        binary_matcher = binkit.binaries.Matcher(filenames = (src_filename, target_filename))
        binary_matcher.match("output-diff2.json")

    def activate(self, ctx):
        target_filename = self.get_filename()
        if target_filename and os.path.isfile(target_filename):
            src_filename = os.path.splitext(idc.get_idb_path())[0] + '.db'
            print('Diffing: %s - %s' % (src_filename, target_filename))

            if self.use_process:
                launcher = binkit.python.Launcher()
                script_name = os.path.splitext(binkit.binaries.__file__)[0] + '.py'
                parameters = [script_name, '-o', 'output.json', src_filename, target_filename]
                print(parameters)
                launcher.run(parameters)
            else:
                t = threading.Thread(target=self.match_thread, args=(src_filename, target_filename))
                t.start()

        return 1

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

class BinkitPlugin(idaapi.plugin_t):
    wanted_name = "Binkit"    
    wanted_hotkey = "Alt-F12"
    comment = "Binkit Plugin For IDA"
    help = "Use this plugin to load diffing result files (*.json)..."
    flags = idaapi.PLUGIN_KEEP

    def init(self):
        self.get_connection_filename()
        thread.start_new_thread(start_binkit_server, (self.connection_filename,))

        action_desc = idaapi.action_desc_t('my:action', 'Load Results', LoadResultsHandler(), 'Ctrl+H', 'Load Results', 199)
        idaapi.register_action(action_desc)        
        idaapi.attach_action_to_menu('Edit/Other/', 'my:action', idaapi.SETMENU_APP)

        action_desc = idaapi.action_desc_t('my:diff', 'Diff Results', DiffHandler(), 'Ctrl+D', 'Diff Results', 199)
        idaapi.register_action(action_desc)        
        idaapi.attach_action_to_menu('Edit/Other/', 'my:diff', idaapi.SETMENU_APP)

        return idaapi.PLUGIN_KEEP

    def get_connection_filename(self):
        binkit_profile = os.path.join(os.environ['USERPROFILE'], '.binkit')
        if not os.path.isdir(binkit_profile):
            try:
                os.makedirs(binkit_profile)
            except:
                traceback.print_exc()
        md5 = idc.GetInputMD5().lower()
        self.connection_filename = os.path.join(binkit_profile, "%s-%d.port" % (md5, os.getpid()))

    def run(self, arg):
        pass

    def term(self):
        if os.path.isfile(self.connection_filename):
            try:
                print("Removing %s" % self.connection_filename)
                os.remove(self.connection_filename)
            except:
                traceback.print_exc()

def PLUGIN_ENTRY():
    return BinkitPlugin()


repository: sfinktah/sfida
file: idarest_chunked.py
description: idapro python toolkit
url: https://github.com/sfinktah/sfida/blob/b9a8307b5c3e3cbfeb2372757a0b3d0da84142ea/idarest_chunked.py
wanted_name: IDA Rest API
comment: Interface to IDA Rest API
created_at: 2021-03-01 10:05:12+00:00
pushed_at: 2024-10-23 03:13:14+00:00
content:
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
from queue import Queue
import cgi
import ida_idaapi
import ida_kernwin
import idaapi
import idautils
import idc
import json
import re
import threading
import time
import traceback
import urllib.parse as urlparse


API_PREFIX = '/ida/api/v1.0'
API_PORT = 8901
API_HOST = '127.0.0.1'
API_DEBUG = False

class HTTPRequestError(BaseException):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code

class UnknownApiError(HTTPRequestError):
    pass

class HTTPRequestHandler(BaseHTTPRequestHandler):
    routes = {}
    docs = {}
    prefns = {}
    postfns = {}
    uid = 0

    @staticmethod
    def _get_params(f):
        print(inspect.getsource(ir.handler.routes['call'][1]))

    @staticmethod
    def set_result(uid, value):
        #  HTTPRequestHandler.idarest_queue
        if API_DEBUG: idaapi.msg("[set_result] {}: {}\n".format(uid, value))
        HTTPRequestHandler.idarest_queue.put(value)
        return uid

    @staticmethod
    def get_next(value):
        try:
            v = next(value)
            print("next value: {}".format(v))
            HTTPRequestHandler.set_result(0, v)
        except StopIteration as e:
            HTTPRequestHandler.set_result(0, e)

    @staticmethod
    def wrapped_iter(value):
        while True:
            ida_kernwin.execute_sync(lambda: HTTPRequestHandler.get_next(value), ida_kernwin.MFF_WRITE)
            yield HTTPRequestHandler.get_result(0)
        yield None

    @staticmethod
    def get_result(uid):
        #  global idarest_queue
        try:
            value = HTTPRequestHandler.idarest_queue.get(timeout=8)
            if str(type(value)) == "<class 'generator'>":
                print("[get_result] return wrapped_iter")
                return HTTPRequestHandler.wrapped_iter(value)
        except Exception as e:
            value = {'code': 500, 'msg': 'Unhandled Exception: ({}) {}'.format(type(e), str(e)),
                    "error_trace": traceback.format_exc()}
        except:
            value = "timeout"
        if API_DEBUG: idaapi.msg("[get_result] {}: {}\n".format(uid, value))
        return value 

    @staticmethod
    def build_route_pattern(route):
        return re.compile("^{0}$".format(route))

    @staticmethod
    def route(route_str):
        def decorator(f):
            print("[route] {}: {}".format(route_str, f.__doc__))
            route_path = API_PREFIX + '/' + route_str + '/?'
            route_pattern = HTTPRequestHandler.build_route_pattern(route_path)
            HTTPRequestHandler.routes[route_str] = (route_pattern, f)
            HTTPRequestHandler.docs[route_str] = f.__doc__
            # HTTPRequestHandler.params[route_str] = HTTPRequestHandler._get_params(f)
            return f
        return decorator

    def add_route(self, route_str, f):
        route_path = API_PREFIX + '/' + route_str + '/?'
        route_pattern = self.build_route_pattern(route_path)
        self.routes[route_str] = (route_pattern, f)
        self.docs[route_str] = f.__doc__
        return f

    def remove_route(self, route_str):
        if route_str in self.routes:
            self.routes.pop(route_str)
            return True
        return False


    @staticmethod
    def prefn(route_str):
        def decorator(f):
            HTTPRequestHandler.prefns.setdefault(route_str, []).append(f)
            return f
        return decorator

    @staticmethod
    def postfn(route_str):
        def decorator(f):
            HTTPRequestHandler.postfns.setdefault(route_str, []).append(f)
            return f
        return decorator

    def _get_route_match(self, path):
        for (key, (route_pattern, view_function)) in self.routes.items():
            m = route_pattern.match(path)
            if m:
                return key, view_function
        return None

    def _get_route_prefn(self, key):
        try:
            return self.prefns[key]
        except:
            return []

    def _get_route_postfn(self, key):
        try:
            return self.postfns[key]
        except:
            return []

    def _serve_route(self, args):
        path = urlparse.urlparse(self.path).path
        route_match = self._get_route_match(path)
        if route_match:
            key, view_function = route_match
            # these won't run in the main thread, so could cause issues if they try to interact with the idb
            for prefn in self._get_route_prefn(key):
                args = prefn(self, args)
            
            ida_kernwin.execute_sync(lambda: HTTPRequestHandler.set_result(0, view_function(self, args)), ida_kernwin.MFF_WRITE)
            results = HTTPRequestHandler.get_result(0)
            #  results = view_function(self, args)
            print("initial results: {}".format(results))
            return results
            #  while results != 'timeout':
                #  yield results
                #  results = HTTPRequestHandler.get_result(0)
                #  print("next results: {}".format(results))
            
            # these won't run in the main thread, so could cause issues if they try to interact with the idb
            for postfn in self._get_route_postfn(key):
                results = postfn(self, results)

            return results
        else:
            raise UnknownApiError('Route "{0}" has not been registered'.format(path), 404)

    def _serve(self, args):
        try:
            it = self._serve_route(args)
            # it = _sleep({}, {})
            print("it is iterable: {}".format(isIterable(it)))
            print("it is iterator: {}".format(isIterator(it)))
            iterable = False
            if str(type(it)) == "<class 'generator'>":
                iterable = True

            response = {
                'code' : 200,
                'msg'  : 'OK',
                'iterable' : 'start',
                'data' : None,
            }
            if isinstance(response['data'], dict):
                if 'error' in response['data']:
                    response['msg'] = 'FAIL'
        except UnknownApiError as e:
            self.send_error(e.code, e.msg)
            return
        except HTTPRequestError as e:
            response = {'code': e.code, 'msg' : e.msg}
        except ValueError as e:
            response = {'code': 400, 'msg': 'ValueError: ' + str(e)}
        except KeyError as e:
            response = {'code': 400, 'msg': 'KeyError: ' + str(e)}
        except StopIteration as e:
            response = {'code': 400, 'msg': 'StopIteration: ' + str(e)}
        except Exception as e:
            response = {'code': 500, 'msg': 'Unhandled Exception: ({}) {}'.format(type(e), str(e)),
                    "error_trace": traceback.format_exc()}

        jsonp_callback = self._extract_callback()
        if jsonp_callback:
            content_type = 'application/javascript'
            response_fmt = jsonp_callback + '({0});'
        else:
            content_type = 'application/json'
            response_fmt = '{0}'

        self.send_response(200)
        self.send_header('Content-Type', content_type)
        self.send_header('Transfer-Encoding', 'chunked')
        self.end_headers()

        try:
            while True:
                r = response_fmt.format(response).encode('utf-8')
                l = len(r)
                self.wfile.write(asBytes('{:X}\r\n{}\r\n'.format(l, r)))

                print("wrote: {}".format(r))
                data = next(it)
                if data is None:
                    break
                if isinstance(data, StopIteration):
                    break
                response = {
                    'code' : 200,
                    'msg'  : 'OK',
                    'data' : data,
                }
                if isinstance(response['data'], dict):
                    if 'error' in response['data']:
                        response['msg'] = 'FAIL'
        except StopIteration:
            pass

        response = {
            'code' : 200,
            'msg'  : 'OK',
            'iterable' : 'stop',
            'data' : None,
        }
        r = response_fmt.format(response).encode('utf-8')
        l = len(r)
        self.wfile.write(asBytes('{:X}\r\n{}\r\n'.format(l, r)))
        self.wfile.write(asBytes('0\r\n\r\n'))

    def _extract_post_map(self):
        content_type, _t = cgi.parse_header(self.headers.get('content-type'))
        if content_type != 'application/json':
            raise HTTPRequestError(
                    'Bad content-type, use application/json',
                    400)
        length = int(self.headers.get('content-length'))
        try:
            return json.loads(self.rfile.read(length))
        except ValueError as e:
            raise HTTPRequestError(
                    'Bad or malformed json content',
                    400)

    def _extract_query_map(self):
        query = urlparse.urlparse(self.path).query
        qd = urlparse.parse_qs(query)
        args = {}
        for k, v in qd.items():
            if len(v) != 1:
                raise HTTPRequestError(
                    "Query param specified multiple times : " + k,
                    400)
            args[k.lower()] = v[0]
        return args

    def _extract_callback(self):
        try:
            args = self._extract_query_map()
            return args['callback']
        except:
            return ''

    def do_POST(self):
        try:
            args = self._extract_post_map() 
        except TypeError as e:
            # thrown on no content, just continue on
            args = '{}'
        except HTTPRequestError as e:
            self.send_error(e.code, e.msg)
            return
        self._serve(args)

    def do_GET(self):
        try:
            args = self._extract_query_map() 
        except HTTPRequestError as e:
            self.send_error(e.code, e.msg)
            return
        self._serve(args)


"""
API handlers for IDA

"""
def check_ea(f):
    def wrapper(self, args):
        if 'ea' in args:
            try:
                ea = int(args['ea'], 16)
            except ValueError:
                raise IDARequestError(
                        'ea parameter malformed - must be 0xABCD', 400)
            if ea > idc.MaxEA():
                raise IDARequestError(
                        'ea out of range - MaxEA is 0x%x' % idc.MaxEA(), 400)
            args['ea'] = ea
        return f(self, args)
    return wrapper

def check_color(f):
    def wrapper(self, args):
        if 'color' in args:
            color = args['color']
            try:
                color = color.lower().lstrip('#').rstrip('h')
                if color.startswith('0x'):
                    color = color[2:]
                # IDA Color is BBGGRR, we need to convert from RRGGBB
                color = color[-2:] + color[2:4] + color[:2]
                color = int(color, 16)
            except:
                raise IDARequestError(
                        'color parameter malformed - must be RRGGBB form', 400)
            args['color'] = color
        return f(self, args)
    return wrapper

# this doesn't seem to work at all
def require_params(*params):
    def decorator(f):
        def require_params_wrapper(self, args):
            for x in params:
                if x not in args:
                    raise IDARequestError('missing parameter {0}'.format(x), 400)
            return f(self, args)
        require_params_wrapper.__doc__ = f.__doc__
        return require_params_wrapper
    return decorator

class IDARequestError(HTTPRequestError):
    pass

class IDARequestHandler(HTTPRequestHandler):
    @staticmethod
    def _hex(v):
        return hex(v).rstrip('L')

    @staticmethod
    def _from_hex(v):
        if re.match(r'0x[0-9a-fA-F]+$', v):
            return int(v, 16)
        return v

    @staticmethod
    def superglobals():
        _globals = dict(inspect.getmembers(
                    inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
        return _globals

    @staticmethod
    def _dotted(key):
        pieces = key.split('.')
        return pieces

    @staticmethod
    def _ensure_path(_dict, path):
        if not path:
            if API_DEBUG: idaapi.msg("[_ensure_path] empty path\n")
            return None
        for piece in path:
            try: 
                if piece in _dict:
                    _dict = _dict[piece]
            except TypeError:
                if hasattr(_dict, piece):
                    _dict = getattr(_dict, piece)
                else:
                    return None
        return _dict


    @staticmethod
    def _getplus(key):
        _globals = IDARequestHandler.superglobals()

        if isinstance(key, list):
            path = key
        else:
            path = _dotted(key)

        base = IDARequestHandler._ensure_path(_globals, path)
        return base

    @staticmethod
    def error(e):
        if issubclass(e.__class__, Exception):
            _class = str(type(e))            \
                    .replace("<class '", "") \
                    .replace("'>", "")
            _message = str(e)
            result = {
                    "error": "{}: {}".format(_class, _message),
                    "error_trace": traceback.format_exc(),
            }
        else:
            result = {
                    "error": e,
            }
        return result


    @HTTPRequestHandler.route('info')
    def info(self, args):
        # No args, Return everything we can meta-wise about the ida session
        # file crcs
        result = {
                'md5' : idc.GetInputMD5(),
                'idb_path' : idc.GetIdbPath(),
                'file_path' : idc.GetInputFilePath(),
                'ida_dir' : idc.GetIdaDirectory(),
                'min_ea' : self._hex(idc.MinEA()),
                'max_ea' : self._hex(idc.MaxEA()),
                'segments' : self.segments({})['segments'],
                # idaapi.cvar.inf
                'procname' : idc.GetLongPrm(idc.INF_PROCNAME),
            }
        return result

    @HTTPRequestHandler.route('query')
    def query(self, args):
        # multiple modes
        # with address return everything about that address
        # with name, return everything about that name
        idc.jumpto(ida_ida.cvar.inf.min_ea)
        return idc.here()


    @HTTPRequestHandler.route('cursor')
    @check_ea
    def cursor(self, args):
        # XXX - Doesn't work
        #if 'window' in args:
        #    tform = idaapi.find_tform(args['window'])
        #    if tform:
        #        idaapi.switchto_tform(tform, 1)
        #    else:
        #        raise IDARequestError(
        #            'invalid window - {0}'.format(args['window']), 400)
        result = {}
        if 'ea' in args:
            success = idc.jumpto(ea)
            result['moved'] = success
        else:
            result['error'] = "missing argument: ea"
        result['ea'] = self._hex(idc.here())
        return result

    def _get_segment_info(self, s):
        return {
            'name' : idaapi.get_true_segm_name(s),
            'ida_name' : idaapi.get_segm_name(s),
            'start' : self._hex(s.startEA),
            'end' : self._hex(s.end_ea),
            'size' : self._hex(s.size())
        }

    @HTTPRequestHandler.route('segments')
    @check_ea
    def segments(self, args):
        if 'ea' in args:
            s = idaapi.getseg(args['ea'])
            if not s:
                raise IDARequestError('Invalid address', 400)
            return {'segment': self._get_segment_info(s)}
        else:
            m = {'segments': []}
            for i in range(idaapi.get_segm_qty()):
                s = idaapi.getnseg(i)
                m['segments'].append(self._get_segment_info(s))
            return m

    @HTTPRequestHandler.route('call')
    @require_params('cmd')
    def call(self, args):
        """run callable and return result

        :param cmd: callable
        :param args: [optional] comma seperated list of positional arguments
        :param *: [optional] keyword arguments

        $ wget 'http://127.0.0.1:8901/ida/api/v1.0/eval?cmd=type=idc.GetType(0x1412E9E98)&return=type' -O - -q
        {
            'code': 200,
            'msg': 'OK',
            'data': 'void __fastcall(uint8_t *buffer, uint32_t data, uint32_t bits, int32_t offset)'
        }

        """
        if API_DEBUG: idaapi.msg("[call]\n")
        try:
            if not 'cmd' in args:
                return IDARequestHandler.error('missing parameter \'cmd\'')
            cmd = args.pop('cmd')
            _args = []
            _kwargs = {}
            if 'args' in args:
                _args = args.pop('args').split(',')
                _args = [ IDARequestHandler._from_hex(x) for x in _args ]
                if API_DEBUG: idaapi.msg('_args: {}\n'.format(_args))
            for k, v in args.items():
                _kwargs[k] = IDARequestHandler._from_hex(v)
                if API_DEBUG: idaapi.msg('_kwarg: {}: {}\n'.format(k, v))
            fn = IDARequestHandler._getplus(cmd)
            pp({
                'cmd': cmd,
                'fn': fn,
                'args': _args,
                'kwargs': _kwargs,
            })
            if fn is None:
                return IDARequestHandler.error(NameError("name '{}' is not defined".format(cmd)))
            if not callable(fn):
                return IDARequestHandler.error(NameError("name '{}' is not callable".format(cmd)))
            result = fn(*_args, **_kwargs)
            if API_DEBUG: idaapi.msg('result: {}\n'.format(result))
            return result

        except Exception as e:
            return IDARequestHandler.error(e)
        except:
            return IDARequestHandler.error("Unknown Exception")

    @HTTPRequestHandler.route('eval')
    @HTTPRequestHandler.route('exec')
    @require_params('cmd')
    def eval(self, args):
        """evaluate expression via python exec()

        :param cmd: string to evaluate
        :param return: [optional] name of variable to return

        $ wget 'http://127.0.0.1:8901/ida/api/v1.0/eval?cmd=type=idc.GetType(0x1412E9E98)&return=type' -O - -q
        {
            'code': 200,
            'msg': 'OK',
            'data': 'void __fastcall(uint8_t *buffer, uint32_t data, uint32_t bits, int32_t offset)'
        }

        """
        idaapi.msg("Hello Eval\n")
        try:
            if not 'cmd' in args:
                return IDARequestHandler.error('missing parameter \'cmd\'')
            cmd = args['cmd']   
            if API_DEBUG: idaapi.msg('cmd: {}\n'.format(cmd))
            exec(cmd, IDARequestHandler.superglobals())
            if 'return' in args:
                return getglobal(args['return'], None)
        except Exception as e:
            return IDARequestHandler.error(e)

"""
Threaded HTTP Server and Worker

Use a worker thread to manage the server so that we can run inside of
IDA Pro without blocking execution.

"""
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    allow_reuse_address = True


class Worker(threading.Thread):
    def __init__(self, host=API_HOST, port=API_PORT):
        threading.Thread.__init__(self)
        self.httpd = ThreadedHTTPServer((host, port), IDARequestHandler)

    def run(self):
        self.httpd.serve_forever()

    def stop(self):
        idaapi.msg("httpd shutdown...\n")
        self.httpd.shutdown()
        idaapi.msg("httpd server_close...\n")
        self.httpd.server_close()

"""
IDA Pro Plugin Interface

Define an IDA Python plugin required class and function.
"""

# 
MENU_PATH = 'Edit/Other'
# class idarest_plugin_t(ida_idaapi.plugin_t):
class idarest_plugin_t(object):
    flags = 0
    comment = "Interface to IDA Rest API"
    help = "IDA Rest API for basic RE tool interoperability"
    wanted_name = "IDA Rest API"
    wanted_hotkey = ""

    def init(self):
        super(idarest_plugin_t, self).__init__()

        idaapi.msg("Initializing {}\n".format(self.wanted_name))
        self.state = None
        #  new_ctx1 = ida_kernwin.add_hotkey("Alt-7", lambda *a: self.start())
        #  new_ctx2 = ida_kernwin.add_hotkey("Alt-8", lambda *a: self.stop())
        #  new_ctx2 = ida_kernwin.add_hotkey("Alt-9", lambda *a: self.term())
        #  self.ctxs = [new_ctx1, new_ctx2]
        self.worker = None
        self.port = API_PORT
        self.host = API_HOST
        #  ret = self._add_menus()
        idaapi.msg("Init done\n")
        self.start()
        return idaapi.PLUGIN_KEEP

    def start(self, *args):
        idaapi.msg("Starting IDARest\n")
        if self.worker and self.worker.is_alive():
            idaapi.msg("Already running\n")
            return

        try:
            self.worker = Worker(self.host, self.port)
        except Exception as e:
            idaapi.msg("Error starting worker : \n" + str(e) + "\n")
            return ida_idaapi.PLUGIN_UNL

        self.worker.start()
        idaapi.msg("Worker running\n")
        return ida_idaapi.PLUGIN_KEEP

    def stop(self, *args):
        if self.worker and not self.worker.is_alive():
            idaapi.msg("IDARest worker is not running\n")
            return

        idaapi.msg("Stopping IDARest\n")
        self.worker.stop()
        self.worker.join()
        idaapi.msg("IDARest stopped\n")

    @property
    def handler(self):
        return self.worker.httpd.RequestHandlerClass

    def add_route(self, route_pattern, f):
        self.handler.add_route(self.handler, route_pattern, f)

    def remove_route(self, route_pattern):
        self.handler.remove_route(self.handler, route_pattern)

    def run(self, arg):
        pass

    def term(self):
        idaapi.msg("Terminating %s\n" % self.wanted_name)
        try:
            self.stop()
        except:
            pass
        #  for ctx in self.ctxs:
            #  ida_kernwin.del_hotkey(ctx)

HTTPRequestHandler.idarest_queue = Queue()

#  def PLUGIN_ENTRY():
    #  return idarest_plugin_t()

if __name__ == "__main__":
    if 'ir' in globals() and str(type(globals().get('ir'))).find('idarest_plugin_t') > -1:
        globals()['ir'].term()

    ir = idarest_plugin_t()
    ir.init()
    
    ### example route
    def names(self, args):
        """return all extant names and addresses"""
        m = {'names' : []}
        for n in idautils.Names():
            yield {n[1]: self._hex(n[0])}

    def _sleep(self, args):
        for r in range(5):
            print("[_sleep] {}".format(r))
            yield r
            time.sleep(1)

    ir.add_route('names', names)
    ir.add_route('sleep', _sleep)
    ### end example route

    def relist(self, args):
        return iter_retrace_list(q, once=1)

    ir.add_route('list', relist)


repository: disassembleblog/IDAPython-Scripts
file: x64dbgida.py
wanted_name: x64dbgida
comment: Official x64dbg plugin for IDA Pro
created_at: 2016-12-19 12:04:37+00:00
pushed_at: 2018-07-19 12:33:33+00:00
url:
https://github.com/disassembleblog/IDAPython-Scripts/blob/3f26b0bdc91ac7be86c77e912ea967b763d64342/x64dbgida.py

content:
import idaapi, idautils, json, traceback

initialized = False
BPNORMAL = 0
BPHARDWARE = 1
UE_HARDWARE_EXECUTE = 4
UE_HARDWARE_WRITE = 5
UE_HARDWARE_READWRITE = 6
UE_HARDWARE_SIZE_1 = 7
UE_HARDWARE_SIZE_2 = 8
UE_HARDWARE_SIZE_4 = 9
UE_HARDWARE_SIZE_8 = 10


def Comments():
    lastea = 0
    lastcmt = ""
    for ea in range(MinEA(), MaxEA()):
        cmt1 = Comment(ea)
        cmt2 = RptCmt(ea)
        cmt = ""
        if cmt1:
            cmt += cmt1
        if cmt2:
            cmt += cmt2
        if (cmt):
            skip = ea == lastea + 1 and cmt == lastcmt
            lastea = ea
            lastcmt = cmt
            if not skip:
                yield (ea, cmt)


def Breakpoints():
    count = GetBptQty()
    for i in range(0, count):
        ea = GetBptEA(i)
        bpt = idaapi.bpt_t()
        if not idaapi.get_bpt(ea, bpt):
            continue
        if bpt.type & BPT_SOFT != 0:
            yield (ea, BPNORMAL, 0, Word(ea))
        else:
            bptype = BPNORMAL if bpt.type == BPT_DEFAULT else BPHARDWARE
            hwtype = {
                BPT_WRITE: UE_HARDWARE_WRITE,
                BPT_RDWR: UE_HARDWARE_READWRITE,
                BPT_EXEC: UE_HARDWARE_EXECUTE
            }[bpt.type]
            hwsize = {
                1: UE_HARDWARE_SIZE_1,
                2: UE_HARDWARE_SIZE_2,
                4: UE_HARDWARE_SIZE_4,
                8: UE_HARDWARE_SIZE_8,
            }[bpt.size]
            yield (ea, bptype, (hwtype << 4 | hwsize), 0)


def get_file_mask():
    mask = "*.dd32"
    if idaapi.get_inf_structure().is_64bit():
        mask = "*.dd64"
    return mask


def do_import():
    db = {}
    module = idaapi.get_root_filename().lower()
    base = idaapi.get_imagebase()

    file = AskFile(0, "x64dbg database|%s" % get_file_mask(),
                   "Import database")
    if not file:
        return
    print "Importing database %s" % file

    with open(file) as dbdata:
        db = json.load(dbdata)

    count = 0
    labels = db.get("labels", [])
    for label in labels:
        try:
            if label["module"] != module:
                continue
            ea = int(label["address"], 16) + base
            name = label["text"]
            MakeNameEx(ea, str(name), 0)
            count += 1
        except:
            pass
    print "%d/%d label(s) imported" % (count, len(labels))

    count = 0
    comments = db.get("comments", [])
    for comment in comments:
        try:
            if comment["module"] != module:
                continue
            ea = int(comment["address"], 16) + base
            name = comment["text"]
            MakeRptCmt(ea, str(name))
            count += 1
        except:
            pass
    print "%d/%d comment(s) imported" % (count, len(comments))

    count = 0
    breakpoints = db.get("breakpoints", [])
    for breakpoint in breakpoints:
        try:
            if breakpoint["module"] != module:
                continue
            ea = int(breakpoint["address"], 16) + base
            bptype = breakpoint["type"]
            if bptype == BPNORMAL:
                count += 1
                AddBptEx(ea, 1, BPT_DEFAULT)
            elif bptype == BPHARDWARE:
                titantype = int(breakpoint["titantype"], 16)
                hwtype = (titantype >> 4) & 0xF
                if hwtype == UE_HARDWARE_EXECUTE:
                    hwtype = BPT_EXEC
                elif hwtype == UE_HARDWARE_WRITE:
                    hwtype = BPT_WRITE
                elif hwtype == UE_HARDWARE_READWRITE:
                    hwtype = BPT_RDWR
                else:
                    continue
                hwsize = titantype & 0xF
                if hwsize == UE_HARDWARE_SIZE_1:
                    hwsize = 1
                elif hwsize == UE_HARDWARE_SIZE_2:
                    hwsize = 2
                elif hwsize == UE_HARDWARE_SIZE_4:
                    hwsize = 4
                elif hwsize == UE_HARDWARE_SIZE_8:
                    hwsize = 8
                else:
                    continue
                count += 1
                AddBptEx(ea, hwsize, hwtype)
        except:
            pass
    print "%d/%d breakpoint(s) imported" % (count, len(breakpoints))

    print "Imported database. Done!"


def do_export():
    db = {}
    module = idaapi.get_root_filename().lower()
    base = idaapi.get_imagebase()

    file = AskFile(1, "x64dbg database|%s" % get_file_mask(),
                   "Export database")
    if not file:
        return
    print "Exporting database %s" % file

    db["labels"] = [{
        "text": name,
        "manual": False,
        "module": module,
        "address": "0x%X" % (ea - base)
    } for (ea, name) in Names()]
    print "%d label(s) exported" % len(db["labels"])

    db["comments"] = [{
        "text": comment.replace("{", "{{").replace("}", "}}"),
        "manual": False,
        "module": module,
        "address": "0x%X" % (ea - base)
    } for (ea, comment) in Comments()]
    print "%d comment(s) exported" % len(db["comments"])

    db["breakpoints"] = [{
        "address": "0x%X" % (ea - base),
        "enabled": True,
        "type": bptype,
        "titantype": "0x%X" % titantype,
        "oldbytes": "0x%X" % oldbytes,
        "module": module,
    } for (ea, bptype, titantype, oldbytes) in Breakpoints()]
    print "%d breakpoint(s) exported" % len(db["breakpoints"])

    with open(file, "w") as outfile:
        json.dump(db, outfile, indent=1)
    print "Exported database. Done!"


	
# 1) Create the handler class
class HandlerExportDB(idaapi.action_handler_t):
	def __init__(self):
		idaapi.action_handler_t.__init__(self)

	# Say hello when invoked.
	def activate(self, ctx):
		do_export()
		print "Exporting database."
		return 1

	# This action is always available.
	def update(self, ctx):
		return idaapi.AST_ENABLE_ALWAYS


# 2) Describe the action
action_exportdb = idaapi.action_desc_t(
	'my:exportdb',   # The action name. This acts like an ID and must be unique
	'ExportDB',  # The action text.
	HandlerExportDB(),   # The action handler.
	'',      # Optional: the action shortcut
	'',  # Optional: the action tooltip (available in menus/toolbar)
	)           # Optional: the action icon (shows when in menus/toolbars)

# 3) Register the action
idaapi.register_action(action_exportdb)


# 1) Create the handler class
class HandlerImportDB(idaapi.action_handler_t):
	def __init__(self):
		idaapi.action_handler_t.__init__(self)

	# Say hello when invoked.
	def activate(self, ctx):
		do_import()
		print "Importing database."
		return 1

	# This action is always available.
	def update(self, ctx):
		return idaapi.AST_ENABLE_ALWAYS


# 2) Describe the action
action_importdb = idaapi.action_desc_t(
	'my:importdb',   # The action name. This acts like an ID and must be unique
	'ImportDB',  # The action text.
	HandlerImportDB(),   # The action handler.
	'',      # Optional: the action shortcut
	'',  # Optional: the action tooltip (available in menus/toolbar)
	)           # Optional: the action icon (shows when in menus/toolbars)

# 3) Register the action
idaapi.register_action(action_importdb)

	
class x64dbg_plugin_t(idaapi.plugin_t):
    comment = "Official x64dbg plugin for IDA Pro"
    version = "v1.0"
    website = "https://github.com/x64dbg/x64dbgida"
    help = ""
    wanted_name = "x64dbgida"
    wanted_hotkey = ""
    flags = idaapi.PLUGIN_KEEP

    def init(self):
        global initialized

        if initialized == False:
            initialized = True
            # menu = idaapi.add_menu_item("Edit/x64dbgida/", "About", "", 0,
                                        # self.about, None)
            # if menu is not None:			
            idaapi.attach_action_to_menu("Edit/x64dbgida/", 'my:exportdb', idaapi.SETMENU_APP)
            idaapi.attach_action_to_menu("Edit/x64dbgida/", 'my:importdb', idaapi.SETMENU_APP)
                # idaapi.add_menu_item("Edit/x64dbgida/",
                                     # "Import (uncompressed) database", "", 0,
                                     # self.importdb, None)
            # elif idaapi.IDA_SDK_VERSION < 680:
                # idaapi.add_menu_item("File/Produce file/",
                                     # "Export x64dbg database", "", 0,
                                     # self.exportdb, None)
                # idaapi.add_menu_item("File/Load file/",
                                     # "Import x64dbg database", "", 0,
                                     # self.importdb, None)

        return idaapi.PLUGIN_OK

    def run(self, arg):
        self.about()

    def term(self):
        return

    def importdb(self):
        try:
            do_import()
        except:
            traceback.print_exc()
            print "Error importing database..."

    def exportdb(self):
        try:
            do_export()
        except:
            traceback.print_exc()
            print "Error exporting database..."

    def about(self):
        print self.wanted_name + " " + self.version
        print self.comment
        print self.website


def PLUGIN_ENTRY():
    return x64dbg_plugin_t()


repository: tomsons26/tomsons_RE_scripts
file: cfs.py
comment: Import and export function function hashes.
created_at: 2017-04-02 23:01:59+00:00
pushed_at: 2025-01-27 12:14:22+00:00
url:
https://github.com/tomsons26/tomsons_RE_scripts/blob/8329a2b15eb001a055ae728a07407940da3f9489/cfs.py

content:
#------------------------------------------------------------------------------
# IDA Plugin to import and export function hashes
#
# To export a list select functions in the "Functions View", rightclick and "Export Hash"
# To import a list "File", "Load File", "CFH Hash File"
#
# Maybe TODO:
#   backport it to IDA 7.0
#
# Quite modified version of https://github.com/cra0/ida-scripts/tree/master/plugins
#   Unlike the original this combines import and export features in same file
#   Unlike original this doesn't check for duplicates as that takes all eternity
#   Unlike original, on import this builds a signature db of the current idb,
#   then compares the signatures to the loaded signatures,
#   this compromise was chosen as binary searching large binaries takes all eternity, this takes minutes
# Copy the 'cvutils-cfs-exporter.py' into the plugins directory of IDA
#------------------------------------------------------------------------------

PLUGIN_NAME = "Import/Export Function Signature Plugin"

__VERSION__ = '0.0.2'
__AUTHOR__ = 'tomsons26, cra0'

import os
import sys
import idc
import idaapi
import idautils
import ida_bytes
import ida_funcs
import ida_name
import ida_kernwin
import ida_ua
import ida_ida
import hashlib

from hashlib import sha1

UA_MAXOP=ida_ida.UA_MAXOP

major, minor = map(int, idaapi.get_kernel_version().split("."))
using_ida7api = (major > 6)
using_pyqt5 = using_ida7api or (major == 6 and minor >= 9)

idaver_74newer = (major == 7 and minor >= 4)
idaver_8newer = (major >= 8)

if idaver_74newer or idaver_8newer:
	newer_version_compatible = True
else:
	newer_version_compatible = False

if newer_version_compatible:
	#IDA 7.4+
	#https://hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml
	import ida_ida
	import ida_kernwin

if using_pyqt5:
	import PyQt5.QtGui as QtGui
	import PyQt5.QtCore as QtCore
	import PyQt5.QtWidgets as QtWidgets
	from PyQt5.Qt import QApplication

else:
	import PySide.QtGui as QtGui
	import PySide.QtCore as QtCore
	QtWidgets = QtGui
	QtCore.pyqtSignal = QtCore.Signal
	QtCore.pyqtSlot = QtCore.Slot
	from PySide.QtGui import QApplication

class FunctionHash:
	def __init__(self, address, hash):
		self.address = address
		self.hash = hash

# IDA is annoying sometimes making jump tables at end of function part of it so need to fix end..
def FixupFunctionEnd(start, end):
	func = ida_funcs.func_t(start)
	res = ida_funcs.find_func_bounds(func, ida_funcs.FIND_FUNC_EXIST)

	if res == ida_funcs.FIND_FUNC_UNDEF:
		return end
	else:
		return func.end_ea

#------------------------------------------------------------------------------
# Import handler
#------------------------------------------------------------------------------

class ImportFileMenuHandler(idaapi.action_handler_t):
	def __init__(self):
		idaapi.action_handler_t.__init__(self)

	def get_directory_path(self, file_path):
		"""
		Get the directory path from a given file path.
		"""
		return os.path.dirname(file_path)

	def get_file_name(self, file_path):
		"""
		Get the file name from a given file path.
		"""
		return os.path.basename(file_path)


	def build_sig_list(self):
		sig_list = list()
		sig_maker = SigMaker()

		for i, addr in enumerate(idautils.Functions()):
			start = idc.get_func_attr(addr, idc.FUNCATTR_START)
			end = idc.get_func_attr(addr, idc.FUNCATTR_END)
			end = FixupFunctionEnd(start, end)
			func_name = idc.get_func_name(start)

			# We'll create a signature for the entire function
			sig = sig_maker.make_sig_default(start, end)

			if sig is None or sig == "":
				print(f"Failed to make a signature for function {func_name} at {start:x}")
				continue

			sig_list.append(FunctionHash(start, sha1(sig.encode('utf-8')).hexdigest()))

		return sig_list

	def find_hash_match(self, list, hash):
		"""
		Tries to find current signature in the current IDB signature list.
		TODO: maybe handle duplicates
		"""
		matches = []

		for i in list:
			#print("checking {} against {}".format(i.sig, signature))
			if hash in i.hash:
				matches.append(i.address)
				break

		return matches

	def process_signatures(self, sig_file_path):
		"""
		Process the signatures and resolve function names in IDA.
		"""
		counter = 0
		resolved_count = 0
		error_count = 0
		min_ea = idaapi.cvar.inf.min_ea
		max_ea = idaapi.cvar.inf.max_ea
		is_64bit = idc.__EA64__

		print("Processing Hashes...")

		idaapi.show_wait_box("Processing... Please Wait (This may take a bit)")

		sig_list = self.build_sig_list()

		with open(sig_file_path, "r") as sig_file:
			lines = sig_file.readlines()
			count = len(lines)

			for line in lines:

				ida_kernwin.replace_wait_box("Processing hash %d/%d" % (counter, count))

				if ida_kernwin.user_cancelled():
					break

				line = line.strip()

				if not line or line.startswith("//") or line.startswith("*"):
					continue

				# Hash
				hash, line = line.split(",", 1)
				hash = hash.strip()[1:-1]  # Remove surrounding quotes

				print(hash)

				# Function Name
				func_name = line.strip()[3:-1]  # Remove surrounding quotes and //

				print(func_name)

				# Find all matches
				ea = idaapi.BADADDR
				matches = self.find_hash_match(sig_list, hash)
				matches_count = len(matches)
				if matches_count > 1:
					print("Multiple hash matches[%i] found for [%s] ignoring hash." % (matches_count, func_name))
					continue

				# Set EA if we have only 1 hit, change this if you wish.
				if matches_count == 1:
					ea = matches[0]

				#print(f"({resolved_count}/{counter}) [{ea:X}] [{func_name}] ==> ", end="")

				if ea != idaapi.BADADDR:
					if ida_bytes.get_full_flags(ea) != idaapi.BADADDR:
						func_idb_name_str = ida_name.get_name(ea)

						#TODO: remove?
						if idc.get_func_flags(ea) == -1:
							ida_bytes.del_items(ea, ida_bytes.DELIT_SIMPLE, 1)
							idc.create_insn(ea)
							ida_funcs.add_func(ea)
							ida_name.set_name(ea, func_name, ida_name.SN_FORCE)
							#idc.set_cmt(ea, "SIG-RESOLVED " + func_name, 1)
							resolved_count += 1
							#print("[RESOLVED]")
						elif func_idb_name_str and len(func_idb_name_str) >= 3:
							# all this crud should be renamed, don't touch anything else
							#if func_idb_name_str[:4] == "sub_" or func_idb_name_str[:3] == "__Z" or "unknown_libname" in func_idb_name_str or "@std@" in func_idb_name_str or "@_STL@" in func_idb_name_str:
							if func_idb_name_str[:4] == "sub_" or func_idb_name_str[:3] == "__Z" or "unknown_libname" in func_idb_name_str:
								ida_name.set_name(ea, func_name, ida_name.SN_FORCE)
								#idc.set_cmt(ea, "SIG-RESOLVED " + func_name, 1)
								resolved_count += 1
								#print("[RENAMED+RESOLVED]", func_idb_name_str, "TO", func_name)
							else:
								print("[IGNORED] Function @ 0x{:X} seems named.".format(ea))
							idc.set_color(ea, 2, 0xd7d7d7)
						else:
							print("[UNKNOWN ERROR]")
					else:
						error_count += 1
						print("[BAD!!!] Unable to resolve =>", func_name, "@ [0x{:X}]".format(ea))
				else:
					print("[NOT FOUND] Hash {} not found in the binary".format(counter))

				counter += 1

		del lines
		del sig_list

		print("------------------------------------------")
		print("Resolved ({}/{}) Functions, {} Lines!".format(resolved_count, counter, count))
		if error_count > 0:
			print("Errors ({})".format(error_count))
			return False

		return True

	def main(self):
		sig_file_path = idaapi.ask_file(0, "*.cfh", "Function Hash Definition File")
		if sig_file_path:
			print("------------------------------------------")
			print(f"{PLUGIN_NAME} {__VERSION__} - {__AUTHOR__}")
			print("Parsing:", sig_file_path)

			# Show the "Please wait" dialog before starting the heavy sigfind operation
			#idaapi.show_wait_box("Processing... Please Wait (This may take a while)")

			if not self.process_signatures(sig_file_path):
			   idaapi.warning("Some errors occurred while importing.")

		# Hide the "Please wait" dialog
		idaapi.hide_wait_box()


	# Invoke the main
	def activate(self, ctx):
		self.main()  # call the main function when the action is activated
		return 1

	# This action is always available.
	def update(self, ctx):
		return idaapi.AST_ENABLE_ALWAYS


#------------------------------------------------------------------------------
# Plugin Main
#------------------------------------------------------------------------------

def PLUGIN_ENTRY():
	"""
	Required plugin entry point for IDAPython Plugins.
	"""
	return CFSSignaturePlugin()

class CFSSignaturePlugin(idaapi.plugin_t):

	flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE
	comment = "Import and export function function hashes."
	help = "Select functions right-click, click Export Function Hashes."
	wanted_name = PLUGIN_NAME
	wanted_hotkey = ""

	#--------------------------------------------------------------------------
	# Plugin Overloads
	#--------------------------------------------------------------------------

	def init(self):
		"""
		This is called by IDA when it is loading the plugin.
		"""

		# initialize the menu actions our plugin will inject
		self._init_sig_actions()

		# initialize plugin hooks
		self._init_hooks()

		# done
		idaapi.msg("%s %s initialized...\n" % (PLUGIN_NAME, __VERSION__))
		return idaapi.PLUGIN_KEEP

	def run(self, arg):
		"""
		This is called by IDA when this file is loaded as a script.
		"""
		idaapi.msg("%s cannot be run as a script.\n" % PLUGIN_NAME)

	def term(self):
		"""
		This is called by IDA when it is unloading the plugin.
		"""

		# unhook our plugin hooks
		self._hooks.unhook()

		# unregister our actions & free their resources
		self._del_ACTION_EXPORT_SIGNATURES()
		self._del_ACTION_IMPORT_SIGNATURES()


		# done
		idaapi.msg("%s terminated...\n" % self.wanted_name)

	#--------------------------------------------------------------------------
	# Plugin Hooks
	#--------------------------------------------------------------------------

	def _init_hooks(self):
		"""
		Install plugin hooks into IDA.
		"""
		self._hooks = Hooks()
		self._hooks.hook()

	#--------------------------------------------------------------------------
	# IDA Actions
	#--------------------------------------------------------------------------

	ACTION_EXPORT_SIGNATURES  = "cfh:export_signatures"
	ACTION_IMPORT_SIGNATURES  = "cfh:import_action"
	ACTION_TOOLTIP_ICON = 198

	def _init_sig_actions(self):
		"""
		Register the export sigs action with IDA.
		"""
		# If the action is already registered, unregister it first.
		if idaapi.unregister_action(self.ACTION_EXPORT_SIGNATURES):
			idaapi.msg("Warning: action was already registered, unregistering it first\n")

		if (sys.version_info > (3, 0)):
			# Describe the action using python3 copy
			action_desc1 = idaapi.action_desc_t(
				self.ACTION_EXPORT_SIGNATURES,                              # The action name.
				"Export Hashes",                                        # The action text.
				IDACtxEntry(export_signatures_go),                          # The action handler.
				"",                                                         # Optional: action shortcut
				"Export Hashes",                                        # Optional: tooltip
				35,                                                         # Icon
				ida_kernwin.ADF_NO_UNDO
			)
		else:
			# Describe the action using python2 copy
			action_desc1 = idaapi.action_desc_t(
				self.ACTION_EXPORT_SIGNATURES,                          # The action name.
				"Export Hashes",                                    # The action text.
				IDACtxEntry(export_signatures_go),                      # The action handler.
				"",                                                     # Optional: action shortcut
				"Export Hashes",                                    # Optional: tooltip
				35,                                                     # Icon
				ida_kernwin.ADF_NO_UNDO
			)

		# register the action with IDA
		assert idaapi.register_action(action_desc1), "Action registration failed"

		if (sys.version_info > (3, 0)):
			action_desc2 = idaapi.action_desc_t(
				self.ACTION_IMPORT_SIGNATURES,   # The action name.
				'CFS Signature File...',  # The action text.
				ImportFileMenuHandler(),  # The action handler.
				"",   # Optional: the action shortcut.
				'Import Hashes',  # Optional: the action tooltip.
				self.ACTION_TOOLTIP_ICON,
				ida_kernwin.ADF_NO_UNDO
			)
		else:
			# Describe the action using python2 copy
			action_desc2 = idaapi.action_desc_t(
				self.ACTION_IMPORT_SIGNATURES,   # The action name.
				'CFS Signature File...',  # The action text.
				ImportFileMenuHandler(),  # The action handler.
				"",   # Optional: the action shortcut.
				'Import Hashes',  # Optional: the action tooltip.
				self.ACTION_TOOLTIP_ICON,
				ida_kernwin.ADF_NO_UNDO
			)

		# register the action with IDA
		assert idaapi.register_action(action_desc2), "Action registration failed"

		# Attach the action to a menu item in the File menu.
		idaapi.attach_action_to_menu('File/Load file/',   # The relative path of where to add the action.
									  self.ACTION_IMPORT_SIGNATURES,   # The action ID (declared above).
									  idaapi.SETMENU_APP)   # We want to append the action after.


	def _del_ACTION_EXPORT_SIGNATURES(self):
		"""
		Delete the action from IDA.
		"""
		idaapi.unregister_action(self.ACTION_EXPORT_SIGNATURES)

	def _del_ACTION_IMPORT_SIGNATURES(self):
		"""
		Delete the action from IDA.
		"""
		idaapi.unregister_action(self.ACTION_IMPORT_SIGNATURES)



#------------------------------------------------------------------------------
# Plugin Hooks
#------------------------------------------------------------------------------

class Hooks(idaapi.UI_Hooks):

	def __init__(self):
		# Call the __init__ method of the superclass
		super(Hooks, self).__init__()

		# Get the IDA version
		major, minor = map(int, idaapi.get_kernel_version().split("."))
		self.newer_version_compatible = (major == 7 and minor >= 4) or (major >= 8)

		# If the IDA version is less than 7.4, define finish_populating_tform_popup
		if not self.newer_version_compatible:
			self.finish_populating_tform_popup = self._finish_populating_tform_popup

	def finish_populating_widget_popup(self, widget, popup_handle, ctx=None):
		"""
		A right click menu is about to be shown. (IDA 7.x)
		"""
		inject_export_signatures_actions(widget, popup_handle, idaapi.get_widget_type(widget))
		return 0


	def _finish_populating_tform_popup(self, form, popup):
		"""
		A right click menu is about to be shown. (IDA 6.x)
		"""
		inject_export_signatures_actions(form, popup, idaapi.get_tform_type(form))
		return 0


#------------------------------------------------------------------------------
# Action Wrappers
#------------------------------------------------------------------------------

def inject_export_signatures_actions(widget, popup_handle, widget_type):
	if widget_type == idaapi.BWN_FUNCS:
		idaapi.attach_action_to_popup(
			widget,
			popup_handle,
			CFSSignaturePlugin.ACTION_EXPORT_SIGNATURES,
			"Export Signatures",
			idaapi.SETMENU_APP
		)
	return 0

#------------------------------------------------------------------------------
# Signature Processing Functions
#------------------------------------------------------------------------------

def add_bytes_to_sig(sig, address, size):
	for i in range(size):
		sig.append("{:02X}".format(idaapi.get_byte(address + i)))

def add_white_spaces_to_sig(sig, size):
	for i in range(size):
		sig.append("?")

def get_current_opcode_size(instruction):
	for i in range(UA_MAXOP):
		if instruction.ops[i].type == ida_ua.o_void:
			return 0, i
		if instruction.ops[i].offb != 0:
			return instruction.ops[i].offb, i
	return 0, 0

def match_operands(instruction, operand, size):
	# Check for data reference
	if idaapi.get_first_dref_from(instruction.ea) != idaapi.BADADDR:
		return False

	if idaapi.get_first_cref_from(instruction.ea) != idaapi.BADADDR: # Code reference
		return False

	return True

def add_ins_to_sig(instruction, sig):
	size, count = get_current_opcode_size(instruction)
	if size == 0:
		add_bytes_to_sig(sig, instruction.ea, instruction.size)
	else:
		add_bytes_to_sig(sig, instruction.ea, size)

	if match_operands(instruction, 0, size):
		add_bytes_to_sig(sig, instruction.ea + size, instruction.size - size)
	else:
		add_white_spaces_to_sig(sig, instruction.size - size)

def is_subOrAdd_instruction(insn):
	# Default bytes of those instructions
	opcode_sub = [0x48, 0x83, 0xEC]
	opcode_add = [0x48, 0x83, 0xC4]

	# Get the bytes of the instruction
	insn_bytes = ida_bytes.get_bytes(insn.ea, insn.size)

	# Convert the byte array to a list of integer byte values
	insn_byte_list = [b for b in insn_bytes]

	# Compare the first three bytes of the instruction with the opcode
	return insn_byte_list[:3] == opcode_sub or insn_byte_list[:3] == opcode_add


class SigMaker:
	def __init__(self):
		pass

	def make_sig_default(self, start, end):
		signature = []
		current_address = start

		#if (end - start) < 5:
		#	print("Signature must be greater than 5 bytes")
		#	return ""

		# this is dragging next function instructions in was "while current_address <= end:"
		while current_address < end:
			instruction = ida_ua.insn_t()
			if ida_ua.decode_insn(instruction, current_address) == 0:
				break

			if instruction.size < 5:
				add_bytes_to_sig(signature, current_address, instruction.size)
			else:
				add_ins_to_sig(instruction, signature)

			current_address += instruction.size

		return " ".join(signature)

	def make_sig_smart(self, start, end):
		signature = []
		current_address = start

		#if (end - start) < 5:
		#	print("Signature must be greater than 5 bytes")
		#	return ""

		while current_address <= end:
			instruction = ida_ua.insn_t()
			if ida_ua.decode_insn(instruction, current_address) == 0:
				break

			#handle sub,add
			if is_subOrAdd_instruction(instruction):
				add_bytes_to_sig(signature, current_address, instruction.size - 1)
				add_white_spaces_to_sig(signature, 1)
				current_address += instruction.size
				continue

			if instruction.size < 5:
				add_bytes_to_sig(signature, current_address, instruction.size)
			else:
				add_ins_to_sig(instruction, signature)

			current_address += instruction.size

		return " ".join(signature)

def export_signatures_go(ctx):
	sig_maker = SigMaker()

	selected_funcs = []
	# fill address array
	for pfn_idx in ctx.chooser_selection:
		pfn = ida_funcs.getn_func(pfn_idx)
		if pfn:
			selected_funcs.append(pfn.start_ea)

	if not selected_funcs:
		print("No functions selected.")
		return

	# Prompt for the output file path
	filename = ida_kernwin.ask_file(1, "*.cfh", "Enter the name of the  file:")
	if not filename:
		print("No file selected.")
		return

	idaapi.show_wait_box("Exporting signatures...")

	if ida_kernwin.user_cancelled():
		return

	# Build sigs and export!
	count = 0
	sig_list = list()

	with open(filename, "w") as file:
		for func_ea in selected_funcs:

			start = idc.get_func_attr(func_ea, idc.FUNCATTR_START)
			end = idc.get_func_attr(func_ea, idc.FUNCATTR_END)
			end = FixupFunctionEnd(start, end)
			func_name = idc.get_func_name(start)

			if ida_kernwin.user_cancelled():
				break

			prefix = ""
			# don't need these as signatures import will apply but we do still want to know of them
			if "unknown_libname" in func_name or func_name.startswith("sub_"):
				prefix = "//"

			# We'll create a signature for the entire function
			sig = sig_maker.make_sig_default(start, end)

			if sig is None or sig == "":
				print(f"Failed to make a signature for function {func_name} at {start:x}")
				continue

			prefix = ""
			if (end - start) < 5:
				#print("Signature must be greater than 5 bytes")
				#return ""
				prefix = "*"

			h = sha1(sig.encode('utf-8')).hexdigest()
			#sig_list.append(f"{count},\"{func_name}\",\"{sig}\"\n")
			#sig_list.append(f"{prefix}\"{func_name}\",\"{sig}\"\n")
			#sig_list.append(f"\"{h}\",//\"{func_name}\",\"{sig}\"\n")
			sig_list.append(f"{prefix}\"{h}\",//\"{func_name}\"\n")
			count += 1

		if count:
			# Write the signature to the file
			for line in sig_list:
				file.write(line)

		file.close()

	del sig_list
	del selected_funcs

	idaapi.hide_wait_box()

	print(f"Exported {count} function signatures to {filename}\n")

#------------------------------------------------------------------------------
# IDA ctxt
#------------------------------------------------------------------------------

class IDACtxEntry(idaapi.action_handler_t):

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

	def activate(self, ctx):
		self.action_function(ctx)
		return 1

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


repository: deactivated/idaenv-plugins
file: ipyida/ipyida/ida_plugin.py
description: IDA plugins packaged for idaenv
wanted_name: IPyIDA
created_at: 2018-08-02 23:54:25+00:00
pushed_at: 2018-09-28 04:19:33+00:00
url:
https://github.com/deactivated/idaenv-plugins/blob/bb578f814d04032da51a221a75e98e49bf561db1/ipyida/ipyida/ida_plugin.py

content:
# -*- encoding: utf-8 -*-
#
# IDA plugin definition.
#
# Copyright (c) 2015-2018 ESET
# Author: Marc-Etienne M.Léveillé <leveille@eset.com>
# See LICENSE file for redistribution.

import idaapi
from ipyida import ida_qtconsole, kernel

class IPyIDAPlugIn(idaapi.plugin_t):
    wanted_name = "IPyIDA"
    wanted_hotkey = "Shift-."
    flags = 0
    comment = ""
    help = "Starts an IPython qtconsole in IDA Pro"

    def init(self):
        global _kernel
        self.kernel = _kernel
        self.widget = None
        return idaapi.PLUGIN_KEEP

    def run(self, args):
        if not self.kernel.started:
            self.kernel.start()
        if self.widget is None:
            self.widget = ida_qtconsole.IPythonConsole(self.kernel.connection_file)
        self.widget.Show()

    def term(self):
        if self.widget:
            self.widget.Close(0)
            self.widget = None
        if self.kernel:
            self.kernel.stop()


def PLUGIN_ENTRY():
    return IPyIDAPlugIn()

_kernel = kernel.IPythonKernel()
_kernel.start()

def _do_load():
    ipyida_plugin_path = __file__
    if ipyida_plugin_path.endswith("pyc"):
        # IDA Python can't load pyc, only the Python source so we remove the "c"
        ipyida_plugin_path = ipyida_plugin_path[:-1]
    idaapi.load_plugin(ipyida_plugin_path)

def load():
    """
    Perform necessary steps to load the plugin inside IDA. If no IDB is open, it
    will wait until it is open to load it.
    """
    if idaapi.get_root_filename() is None:
        # No idb open yet
        def handler(event, old=0):
            if event == idaapi.NW_OPENIDB:
                _do_load()
            elif event == idaapi.NW_TERMIDA:
                idaapi.notify_when(idaapi.NW_TERMIDA | idaapi.NW_OPENIDB | idaapi.NW_REMOVE, handler)
        def _install():
            idaapi.notify_when(idaapi.NW_TERMIDA | idaapi.NW_OPENIDB, handler)
            # return -1 to remove the timer
            return -1
        # It's possible we can't use the notify_when API call yet when IDA opens
        # so try register a timer to add the event listner in the proper "state"
        idaapi.register_timer(1, _install)
    else:
        # IDA is fully loaded and an idb is open, just load the plugin.
        _do_load()


repository: miunasu/SUKI_IDA
file: SUKI_IDA_PLUGIN.py
wanted_name: SUKI_IDA
comment: SUKI_IDA
created_at: 2025-04-18 08:55:44+00:00
pushed_at: 2025-04-18 10:41:44+00:00
description:
SUKI IDA is a continuously updated IDA plugin collection. IDA is the most powerful reverse analysis tool, she has a powerful decompilation engine, so i like she so much. Let's make it stronger ! SUKI IDA 是一个持续更新的IDA插件集合。 IDA 是最强大的逆向分析工具，她拥有强大的反编译引擎，所以我非常喜欢她。所以让我们把她变得更强大！

url:
https://github.com/miunasu/SUKI_IDA/blob/e624d313518454106aef3915e089841ed8c9d1e5/SUKI_IDA_PLUGIN.py

content:
import sys
import os
import idaapi
import ida_kernwin

sys.path.append(os.path.dirname(__file__))

from SUKI_IDA import *

# plugin function switch
DynamicVarCheck_switch = True
REAI_switch = True
ExportFuncCheck_switch = True

# DVC hotkey
HOTKEY_block_var_check = "Shift-B"
HOTKEY_func_var_check = "Shift-A"
HOTKEY_sub_func_disassemble = "Shift-F"

# EFC hotkey
HOTKEY_export_func_check = "Ctrl-Alt-E"



def register_actions():
    
# Register actions
    # DVC    
    if DynamicVarCheck_switch == True:
        action1 = idaapi.action_desc_t(
            "DVC:block_var_check",
            "block var check",  
            DynamicVarCheck.block_var_check(),
            HOTKEY_block_var_check, 
            "block var check",
            0
        )

        action2 = idaapi.action_desc_t(
            "DVC:func_var_check",
            "func var check",  
            DynamicVarCheck.func_var_check(),
            HOTKEY_func_var_check, 
            "func var check",
            0
        )
        
        action3 = idaapi.action_desc_t(
            "DVC:sub_func_disassemble",
            "sub func disassemble",  
            DynamicVarCheck.sub_func_disassemble(),
            HOTKEY_sub_func_disassemble, 
            "sub func disassemble",
            0
        )

        idaapi.register_action(action1)
        idaapi.register_action(action2)
        idaapi.register_action(action3)

        idaapi.attach_action_to_menu("Debugger/DVC/", "DVC:block_var_check", idaapi.SETMENU_APP)
        idaapi.attach_action_to_menu("Debugger/DVC/", "DVC:func_var_check", idaapi.SETMENU_APP)
        idaapi.attach_action_to_menu("Debugger/DVC/", "DVC:sub_func_disassemble", idaapi.SETMENU_APP)

    # REAI
    if REAI_switch == True:
        action4 = idaapi.action_desc_t(
            "REAI:func_analyze",
            "func_analyze",  # 
            REAI.func_analyze(),
            "", 
            "AI work for you!",
            0
        )

        action5 = idaapi.action_desc_t(
            "REAI:exception_code_check",
            "exception_code_check",  # 
            REAI.exception_code_check_action(),
            "", 
            "exception code check",
            0
        )

        action6 = idaapi.action_desc_t(
            "REAI:call_topology_print",
            "call_topology_print",  # 
            REAI.call_topology_print(),
            "", 
            "call topology print",
            0
        )

        idaapi.register_action(action4)
        idaapi.register_action(action5)
        idaapi.register_action(action6)

        # EFC
    if ExportFuncCheck_switch == True:
        action7 = idaapi.action_desc_t(
            "EFC:export_func_check",
            "export_func_check",  # 
            ExportFuncCheck.export_func_check(),
            HOTKEY_export_func_check, 
            "export func check",
            0
        )

        idaapi.register_action(action7)

        idaapi.attach_action_to_menu("View/Open subviews/Export Check", "EFC:export_func_check", idaapi.SETMENU_APP)


def unregister_actions():
    # DVC
    if DynamicVarCheck_switch == True:
        idaapi.unregister_action("DVC:block_var_check") 
        idaapi.unregister_action("DVC:func_var_check")
        idaapi.unregister_action("DVC:sub_func_disassemble")
    
    # REAI
    if REAI_switch == True:
        idaapi.unregister_action("REAI:func_analyze")
        idaapi.unregister_action("REAI:exception_code_check") 
        idaapi.unregister_action("REAI:call_topology_print") 

    # EFC
    if ExportFuncCheck_switch == True:
        idaapi.unregister_action("EFC:export_func_check")

class SUKI_IDA_PLUGIN(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = "SUKI_IDA"
    help = "SUKI_IDA"
    wanted_name = "SUKI_IDA"

    def init(self):
        global REAI_switch
        # config check
        # REAI
        if REAI_switch == True and (REAI.API_KEY == "api_key" or REAI.API_URL == "api_url" or REAI.MODEL == "model name"): 
            ida_kernwin.info("You turn on REAI but not set the API_KEY, API_URL and MODEL in REAI.py. REAI will not load")
            REAI_switch = False

        # init
        register_actions()
        # DVC
        if DynamicVarCheck_switch == True:
            print('[DVC] DVC is ready')
            print('[DVC] Block var check: Shift-B\nFunc var check: Shift-A\nSub func disassemble: Shift-F')

        # REAI
        if REAI_switch == True:
            self.menu = ContextMenuHooks()
            self.menu.hook()
            print('[REAI] REAI is ready')
            print('[REAI] Right click on pseudocode view')    
            

        # EFC 
        if ExportFuncCheck_switch == True:
            print('[EFC] EFC is ready')
            print('[EFC] Export func check: Ctrl-Alt-E')

        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        # DVC
        if DynamicVarCheck_switch == True:
            print('[DVC] Block var check: Shift-B\nFunc var check: Shift-A\nSub func disassemble: Shift-F')

        # REAI
        if REAI_switch == True:
            print('[REAI] Right click on pseudocode view')    
    
        # EFC
        if ExportFuncCheck_switch == True:
            print('[EFC] Export func check: Ctrl-Alt-E')

        return

    def term(self):

        unregister_actions()

        # REAI
        if REAI_switch == True:
            self.menu.unhook()

def PLUGIN_ENTRY():
    return SUKI_IDA_PLUGIN()

class ContextMenuHooks(idaapi.UI_Hooks):
    def finish_populating_widget_popup(self, form, popup):
        # Add actions to the context menu of the Pseudocode view
        if idaapi.get_widget_type(form) == idaapi.BWN_PSEUDOCODE:
            # REAI
            if REAI_switch == True:
                idaapi.attach_action_to_popup(form, popup, "REAI:func_analyze", "REAI/")
                idaapi.attach_action_to_popup(form, popup, "REAI:exception_code_check", "REAI/")
                idaapi.attach_action_to_popup(form, popup, "REAI:call_topology_print", "REAI/")


repository: BinaryAnalysisPlatform/bap-ida-python
file: plugins/bap/plugins/bap_clear_comments.py
description: integration with IDA
wanted_name: BAP: Clear comments
comment: removes all BAP comments
created_at: 2016-06-10 18:00:33+00:00
pushed_at: 2021-05-27 15:13:33+00:00
url:
https://github.com/BinaryAnalysisPlatform/bap-ida-python/blob/d8d4679de2f50bb75f556419565821d95404034e/plugins/bap/plugins/bap_clear_comments.py

content:
import idaapi
from idaapi import ASKBTN_YES

from bap.utils import bap_comment
from bap.utils import ida


class BapClearComments(idaapi.plugin_t):
    flags = idaapi.PLUGIN_DRAW | idaapi.PLUGIN_FIX
    comment = "removes all BAP comments"
    help = ""
    wanted_name = "BAP: Clear comments"
    wanted_hotkey = "Ctrl-Shift-S"

    def clear_bap_comments(self):
        """Ask user for confirmation and then clear (BAP ..) comments."""

        if idaapi.askyn_c(ASKBTN_YES,
                          "Delete all `BAP: ..` comments?") != ASKBTN_YES:
            return

        for ea in ida.addresses():  # TODO: store actually commented addresses
            comm = idaapi.get_cmt(ea, 0)
            if comm and comm.startswith('BAP:'):
                idaapi.set_cmt(ea, '', 0)

    def init(self):
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        self.clear_bap_comments()

    def term(self): pass


def PLUGIN_ENTRY():
    return BapClearComments()


repository: firodj/bbtrace
file: plugin/bbtrace_plugin.py
description: Basic Block Trace: DynamoRIO client
wanted_name: BBTrace
comment: BBTrace
created_at: 2016-09-15 23:36:23+00:00
pushed_at: 2021-12-19 03:34:56+00:00
url:
https://github.com/firodj/bbtrace/blob/f7a822d15f5e98c51b3d3f81b37bd1c537061b9d/plugin/bbtrace_plugin.py

content:
import os
import idaapi
import idautils
import idc
import traceback
from bbtrace.Display import Display


PLUGIN_VERSION = "0.0.2"
AUTHORS        = "Fadhil Mandaga"
DATE           = "2019"

# Stunned panda face icon data.
ICON_DATA = "".join([
        "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1F\xF3\xFF\x61\x00\x00\x02\xCA\x49\x44\x41\x54\x78\x5E\x65",
        "\x53\x6D\x48\x53\x6F\x14\x3F\xBA\xB5\xB7\xA0\x8D\x20\x41\xF2\xBA\x5D\xB6\x0F\x56\xF4\x41\xA2\xC0\x9C\xE9\xB4\x29\x4A\x7D\xB0\x22\x7A\x11\x02\x23\x48\x2A\xD4\x74\x53\x33\x3F\xD4",
        "\x3E\x4A\x50\x19\xE4\xB0\xD0\x22\xCD\x44\x45\x4A\x31\x8C\x92\xA2\x3E\x65\x0A\x4D\xCB\x96\x7E\xE8\xD5\x97\xCC\xFE\xFE\x37\xA7\x77\xDB\xBD\xA7\xE7\x3C\xBE\x05\x9E\xED\xB7\xB3\xF3",
        "\x7B\x39\xF7\xEE\x19\x17\xA8\xAC\x56\xDB\x54\x82\x60\x41\xB3\x59\xBC\xFF\xAC\xF9\xCA\xB5\xAE\x86\xCA\xF9\x4E\xAF\x1B\x3B\xEA\x5D\x48\x9D\x66\xE2\x49\x27\x9F\xD5\x66\x9B\xA2\x1C",
        "\x22\x02\xD0\x40\xE4\x81\x6C\x3B\x76\x37\x56\xE3\x37\x5F\x2F\x62\xE8\x0B\xD3\x66\x19\x7E\x53\xA7\x99\x78\xAE\x1F\x64\x3E\x21\x71\x69\x09\x5F\x20\x98\x2D\x58\x70\x24\x07\x07\x7B",
        "\x6F\xB0\x79\x82\x61\x81\x21\xCC\xDE\x21\x54\x16\x02\xD4\x69\x26\x9E\x74\xEE\xCB\xCF\x4D\xC7\x44\xB3\x88\x7C\x81\xC5\x22\xFE\x6C\xB9\xE9\x46\x67\x46\x1A\x8A\x16\x2B\x0A\x5B\x05",
        "\x74\x66\x65\xE1\x98\x6F\x00\x31\x32\x87\x9F\x59\x77\x66\x66\x61\x42\xBC\xC0\xF5\x6C\x47\x1A\x36\xD7\xB9\x51\x14\xC5\x1E\xBE\xA0\xC3\x5B\xD9\x98\x99\xE1\xC0\xCE\xBE\x57\x48\xD7",
        "\x9A\x63\x68\xEA\x7C\x8A\xF6\x14\x3B\x9F\xF6\xA6\xA4\x60\xEB\xE3\x3E\x9C\x5F\xD6\x5A\x7A\xFA\x71\xBF\xC3\x81\x3D\x4D\x35\x0D\x7C\xC1\xF3\x87\x57\x43\xF9\x87\x8F\x21\x95\x5E\xAB",
        "\x41\x83\x4E\x83\x54\xDB\x92\x76\x20\xCA\xBF\xD0\x99\x9D\xBB\x4E\xDB\xBD\xC7\x8E\x2F\x5A\x3D\x74\x3D\x50\x03\x80\x7E\x7A\x7A\x06\x46\x47\xFD\xA0\x33\x6C\x84\x18\x46\x0C\xBD\x1F",
        "\x86\x2D\x71\x71\x00\x52\x10\x16\x17\xE6\xC1\xE7\x1B\x61\x9A\x81\x69\x31\x30\xFC\x61\x14\xB4\x3A\x3D\x20\x82\x1E\x58\xA9\x15\x05\x41\x14\x05\xB8\x58\xEE\x82\x7D\xE9\x99\x20\xCB",
        "\x32\x94\x95\x95\xC3\xA5\xD2\x53\x00\x51\x09\xAA\x4B\x0B\xA1\xB8\xA4\x0C\x52\x53\x33\x40\xA5\x52\x81\xDB\x5D\x01\xA2\x45\x00\x45\x51\x80\x2A\x36\x12\x8D\x42\x49\x51\x01\x44\xE5",
        "\x18\x90\x22\x0A\x98\x8C\x46\xF0\x54\x14\x42\x6D\x7D\x3B\xE4\x1C\x75\x41\xAD\xB7\x1D\x3C\x55\x85\x60\x32\x19\x41\x8A\x2A\xDC\x57\x5C\x74\x12\x28\x47\xA5\x8E\x44\xE4\xF0\x76\x5B",
        "\x82\xA6\xCD\x5B\x0D\xB2\x12\xE6\xE4\x06\xB5\x1A\x66\xA7\x26\x41\x92\xC2\xA0\xD5\x6A\x60\x67\x92\x19\xAE\x7B\xCE\x70\x4D\x15\xAB\x01\xAD\xC1\x08\x3F\x46\x64\x6E\x8E\x9D\xF9\x13",
        "\xE8\x1A\xFF\xE4\x63\x8A\x0E\xE6\x02\x41\xF8\x3F\x18\x82\x40\x28\x04\xFD\xDD\x75\xF0\xB6\xFF\x2E\x75\x9A\x89\x27\x9D\xFB\xC8\x4F\x39\xBE\xE0\xB4\xAB\xCE\x35\xFE\x71\x00\x16\x17",
        "\x25\x76\x50\x26\x76\x6B\x61\x86\x08\xE4\x1D\xAF\x81\xBC\x13\x97\xA9\xD3\x4C\x3C\xE9\xDC\x47\x7E\xCA\xF1\x05\x0C\x5F\x7D\xFE\xEF\x35\x03\xAF\x9F\x00\xB0\x73\x30\x9A\xE2\x81\x0E",
        "\xF6\xC1\xED\x52\xB8\x77\xAB\x98\x3A\xCD\xC4\x73\x9D\x7C\x6F\xDE\xF9\xCF\x53\x0E\xFE\xA9\xCD\xAE\xB3\x87\xCE\x75\x35\x54\xE1\xD0\xCB\x47\x38\x39\x36\x88\xFF\x4D\xF8\x57\x41\x33",
        "\xF1\xA4\x93\x0F\x00\x36\xAD\x3E\x4C\x6B\xC5\xC9\x5D\x77\x6A\x2F\xB4\x31\xA3\xC4\x40\x4F\x21\x0F\xD1\x4C\x3C\xE9\x2B\xE1\xF5\x0B\xD6\x90\xC8\x90\x4C\xE6\x35\xD0\xCC\x79\x5E\xFF",
        "\x2E\xF8\x0B\x2F\x3D\xE5\xC3\x97\x06\xCF\xCF\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82"])
ACT_ICON = idaapi.load_custom_icon(data=ICON_DATA, format="png")



def lex_citem_indexes(line):
    """
    Lex all ctree item indexes from a given line of text.
    -----------------------------------------------------------------------
    The HexRays decompiler output contains invisible text tokens that can
    be used to attribute spans of text to the ctree items that produced them.
    This function will simply scrape and return a list of all the these
    tokens (COLOR_ADDR) which contain item indexes into the ctree.

    https://github.com/gaasedelen/lighthouse/blob/006c46b4724f6f2cb7b36bc3ec6a45fbb49da6b9/plugin/lighthouse/util/ida.py#L131
    """
    i = 0
    indexes = []
    line_length = len(line)

    # lex COLOR_ADDR tokens from the line of text
    while i < line_length:

        # does this character mark the start of a new COLOR_* token?
        if line[i] == idaapi.COLOR_ON:

            # yes, so move past the COLOR_ON byte
            i += 1

            # is this sequence for a COLOR_ADDR?
            if ord(line[i]) == idaapi.COLOR_ADDR:

                # yes, so move past the COLOR_ADDR byte
                i += 1

                #
                # A COLOR_ADDR token is followed by either 8, or 16 characters
                # (a hex encoded number) that represents an address/pointer.
                # in this context, it is actually the index number of a citem
                #

                citem_index = int(line[i:i+idaapi.COLOR_ADDR_SIZE], 16)
                i += idaapi.COLOR_ADDR_SIZE

                # save the extracted citem index
                indexes.append(citem_index)

                # skip to the next iteration as i has moved
                continue

        # nothing we care about happened, keep lexing forward
        i += 1

    # return all the citem indexes extracted from this line of text
    return indexes


class IDACtxEntry(idaapi.action_handler_t):
    """
    A basic Context Menu class to utilize IDA's action handlers.
    """

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

    def activate(self, ctx):
        """
        Execute the embedded action_function when this context menu is invoked.
        """
        self.action_function()
        return 1

    def update(self, ctx):
        """
        Ensure the context menu is always available in IDA.
        """
        return idaapi.AST_ENABLE_ALWAYS


class BBTrace(idaapi.plugin_t):
    """
    The BBTrace IDA Plugin.
    """

    flags = 0
    comment = 'BBTrace'
    help = 'BBTrace'
    wanted_name = 'BBTrace'
    wanted_hotkey = 'Alt+F10'

    ACTION_LOAD_FILE         = "bbtrace:load_file"

    def init(self):
        """
        This is called by IDA when it is loading the plugin.
        """

        # describe a custom IDA UI action
        action_desc = idaapi.action_desc_t(
                self.ACTION_LOAD_FILE,  # Name. Acts as an ID. Must be unique.
                "BbTrace Flow File...",          # Label. That's what users see.
                IDACtxEntry(self.interactive_load_file), # Handler. Called when activated, and for updating
                None,         # Shortcut (optional)
                "Load bbtrace flow file...",  # Tooltip (optional)
                ACT_ICON)         # Icon ID (optional)

        # register the action with IDA
        result = idaapi.register_action(action_desc)
        if not result:
            RuntimeError("Failed to register load_file action with IDA")

        # attach the action to the File-> dropdown menu
        result = idaapi.attach_action_to_menu(
            "File/Load file/",       # Relative path of where to add the action
            self.ACTION_LOAD_FILE,   # The action ID (see above)
            idaapi.SETMENU_APP       # We want to append the action after ^
        )
        if not result:
            RuntimeError("Failed action attach load_file")

        if idaapi.init_hexrays_plugin():
            idaapi.install_hexrays_callback(self.hexrays_event)
        else:
            print('hexrays is not available.')

        self.display = None

        print("BBTrace initialized.")
        return idaapi.PLUGIN_KEEP

    def term(self):
        pass

    def run(self, arg):
        if not self.display:
            self.display = Display()
        self.display.Show("BbTrace Graph")

    def interactive_load_file(self):
        """
        Loading bbtrace.
        """

        print("Done BbTrace.")

    def hexrays_event(self, event, *args):
        try:
            # if event == idaapi.hxe_maturity:
            # cfunc, maturity = args
            # if maturity == idaapi.CMAT_FINAL:
            if event == idaapi.hxe_text_ready:
                # more code-friendly, readable aliases
                vdui = args[0]
                cfunc = vdui.cfunc

                self.paint_hexrays(cfunc)

        except:
            traceback.print_exc()

        return 0

    def paint_hexrays(self, cfunc):
        sv = cfunc.get_pseudocode()

        lines_painted = 0

        for sline in sv:
            indexes = lex_citem_indexes(sline.line)
            for index in indexes:
                try:
                    item = cfunc.treeitems[index]
                    ea = item.ea

                # apparently this is a thing on IDA 6.95
                except IndexError as e:
                    continue

                col = idc.get_color(ea, CIC_ITEM)
                if col != BADADDR:
                    sline.bgcolor = col
                    lines_painted += 1

        if not lines_painted:
            return

        col = 0xccffcc

        for line_number in xrange(0, cfunc.hdrlines):
            sv[line_number].bgcolor = col

        idaapi.refresh_idaview_anyway()


def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython Plugins.
    """
    return BBTrace()


repository: cxiao/ida-rust-untangler
file: ida_rust_untangler.py
description: An IDA plugin which demangles Rust function names
wanted_name: Rust Untangler
created_at: 2023-05-31 19:51:46+00:00
pushed_at: 2023-12-11 21:21:17+00:00
url:
https://github.com/cxiao/ida-rust-untangler/blob/a52ce2e65527d3f0cc32f4e0d00eee2975f148f8/ida_rust_untangler.py

content:
import logging

import rust_demangler
from rust_demangler.rust import TypeNotFoundError

import idaapi
import idautils

logger = logging.getLogger(__name__)


class ActionHandler(idaapi.action_handler_t):
    # Plugin registration code taken from https://gist.github.com/cmatthewbrooks/7e0c5f4e81927b9d1d6acbb702db3bfc
    def __init__(self, callback):
        idaapi.action_handler_t.__init__(self)
        self.callback = callback

    def activate(self, ctx):
        self.callback()
        return 1

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


class RustUntanglerPlugin(idaapi.plugin_t):
    wanted_name = "Rust Untangler"
    wanted_hotkey = ""
    comment = ""
    help = ""
    # Because we set up our own entries in the Edit > Rust Untangler menu,
    # set the PLUGIN_HIDE flag here so that we don't also have an entry in the Edit > Plugins menu.
    flags = idaapi.PLUGIN_HIDE

    def register_plugin_actions(self):
        # Plugin registration code taken from https://gist.github.com/cmatthewbrooks/7e0c5f4e81927b9d1d6acbb702db3bfc
        plugin_actions = [
            {
                "id": "rust_untangler:demangle",
                "name": "Demangle Rust function names",
                "hotkey": "Ctrl+Alt+D",
                "comment": "Demangle all mangled Rust function names in the current database",
                "callback": self.demangle_action,
                "menu_location": "Edit/Rust Untangler/Demangle",
            },
        ]

        for action in plugin_actions:
            if not idaapi.register_action(
                idaapi.action_desc_t(
                    action["id"],  # Must be the unique item
                    action["name"],  # The name the user sees
                    ActionHandler(action["callback"]),  # The function to call
                    action["hotkey"],  # A shortcut, if any (optional)
                    action["comment"],  # A comment, if any (optional)
                )
            ):
                logger.error(f"Failed to register {action['id']}")

            if not idaapi.attach_action_to_menu(
                action["menu_location"],  # The menu location
                action["id"],  # The unique function ID
                0,
            ):
                logger.error(f"Failed to attach to menu {action['id']}")

    def init(self):
        """
        Callback function which IDA runs on plugin load.
        """
        logger.info("Initialized Rust Untangler plugin.")
        self.register_plugin_actions()
        # This is a persistent plugin which registers keyboard shortcuts and actions
        # rather than just running once, so use the PLUGIN_KEEP flag here.
        return idaapi.PLUGIN_KEEP

    def run(self, _arg):
        """
        Callback function which IDA runs when the main plugin action
        is invoked, either via the Edit > Plugins menu or as a script.
        Because we hide our plugin from the Edit > Plugins menu and instead
        register all our actions in the menu ourselves, this function does nothing.
        """
        pass

    def demangle_action(self):
        for func_address in idautils.Functions():
            func_name = idaapi.get_func_name(func_address)
            func_object = idaapi.get_func(func_address)

            logger.debug(f"{func_address:#x}, {func_name}")
            try:
                demangled_name = rust_demangler.demangle(func_name)
                logger.info(f"Demangled: {func_address:#x}, {demangled_name}")

                # Automatically replace invalid characters with `_` (via SN_NOCHECK),
                # and automatically append a numerical suffix to the name if it already exists (via SN_FORCE).
                result = idaapi.set_name(
                    func_address, demangled_name, idaapi.SN_NOCHECK | idaapi.SN_FORCE
                )
                logger.info(
                    f"Renaming name {func_name} at address {func_address:#x} to demangled name {demangled_name}"
                )
                if not result:
                    logger.warn(
                        f"Failed to rename name {func_name} at address {func_address:#x} to demangled name {demangled_name}"
                    )

                # Also write a comment with the actual demangled name,
                # without automatically replaced invalid characters.
                result = idaapi.set_func_cmt(func_object, demangled_name, 1)
                if not result:
                    logger.warn(
                        f"Failed to set comment for name {func_name} at address {func_address:#x} with demangled name {demangled_name}"
                    )

            except TypeNotFoundError as err:
                logger.warn(f"Unable to demangle {func_name}: {err.message}")

            except Exception as err:
                logger.error(f"Unable to demangle {func_name}: {err}")

    def term(self):
        """
        Callback function which IDA runs on plugin unload.
        """
        logger.info("Terminating Rust Untangler plugin.")


def PLUGIN_ENTRY():
    """
    Entry point for IDAPython plugins.
    """
    return RustUntanglerPlugin()


repository: zhefox/MipsROPSeracher-python3
file: mipsrop.py
description: A tool for search MipsROPChain in python3
wanted_name: MIPS ROP Finder
comment: MIPS ROP Finder
created_at: 2022-07-07 06:04:05+00:00
pushed_at: 2022-07-07 06:08:22+00:00
url:
https://github.com/zhefox/MipsROPSeracher-python3/blob/aba6bdf99418880bab37ad7e72a355b0768a1882/mipsrop.py

content:
# IDA plugin for identifying ROP gadgets in Linux MIPS binaries.
#
# Return Oriented Programming in Linux MIPS is more like Jump Oriented
# Programming; the idea is to control enough of the stack/registers in order to
# control various jumps. Since all instructions in MIPS must be 4-byte aligned,
# You cannot "create" new instructions by returning into the middle
# of existing instructions, as is possible with some other architectures.
#
# In any given MIPS function, various registers are saved onto the stack by
# necessity:
#
#    o $s0 - $s7
#    o $fp
#    o $ra
#
# These values are restored from the stack before the function returns, thus,
# during a stack overflow one can control some or all of these register values.
# The subroutine registers ($s*) are of particular interest, as they are
# commonly used by the compiler to store function pointers. By convention, gcc
# will move function pointers into the $t9 register, then call the function
# using jalr:
#
#     move $t9, $s0  <-- If we control $s0, we control where the jump is taken
#    jalr $t9
#
# While there are other jumps that are of use, and which this plugin searches
# for, the premise is the same: control the stack/registers, and you control
# various jumps allowing you to chain various blocks of code together.
#
# With a list of controllable jumps such as these, we then just need to search
# the surrounding instructions to see if they perform some operation which may
# be useful. For example, let's say we need to load the value 1 into the $a0
# register; in this case, we would want to look for a controllable jump such as
# this:
#
#    move $t9, $s1
#    jalr $t9
#    li $a0, 1    <-- Remember MIPS has jump delay slots, so this instruction
#    is executed with the jump
#
# If we return to this piece of code (and if we control $s1), we can pre-load
# $s1 with the address of the next ROP gadget; thus, $a0 will be loaded with
# the value 1 and we can chain this block of code with other gadgets in order
# to perform more complex operations.
#
# This plugin finds all potentially controllable jumps, and then allows you to
# search for desired instructions surrounding these controllable jumps. Example:
#
#    Python> mipsrop.find("li $a0, 1")
#    -------------------------------------------------
#    |  Address     |  Action      |  Control Jump   |
#    -------------------------------------------------
#    |  0x0002F0F8  |  li $a0,1    |  jalr  $s4      |
#    |  0x00057E50  |  li $a0,1    |  jalr  $s1      |
#    -------------------------------------------------
#
# The output shows the offset of each ROP gadget, the instruction within the
# gadget that your search matched, and the effective register that is jumped to
# after that instruction is executed.
#
# The specified instruction can be a full instruction, such as the example
# above, or a partial instruction. Regex is supported for any of the
# instruction mnemonics or operands; for convenience, the dollar signs in front
# of register names are automatically escaped.
#
# If .set_base(int) is called with a non-zero value then the output will
# include base, offset and address.
# Example
# -------------------------------------------------------------------------
# |  Base       + Offset     =  Address     |  Action  |  Control Jump    |
# -------------------------------------------------------------------------
#
# Craig Heffner
# Tactical Network Solutions

import re
import idc
import sys
import idaapi
import idautils

import ida_shims

# Global instance of MIPSROPFinder
mipsrop = None


def add_to_namespace(namespace, name, variable):
    '''
    Add a variable to a different namespace, likely __main__.
    '''
    import importlib

    importer_module = sys.modules[namespace]
    if name in list(sys.modules.keys()):
        if not (sys.version_info.major == 3 and sys.version_info.minor >= 4):
            import imp
            imp.reload(sys.modules[name])
        else:
            importlib.reload(sys.modules[name])
    else:
        m = importlib.import_module(name, None)
        sys.modules[name] = m

    setattr(importer_module, name, variable)


class MIPSInstruction(object):
    '''
    Class for storing info about a specific instruction.
    '''

    def __init__(self, mnem, opnd0=None, opnd1=None, opnd2=None,
                 ea=idc.BADADDR):
        self.mnem = mnem
        self.operands = [opnd0, opnd1, opnd2]
        self.opnd0 = opnd0
        self.opnd1 = opnd1
        self.opnd2 = opnd2
        self.ea = ea

    def __str__(self):
        string = self.mnem + " "

        for op in self.operands:
            if op:
                string += "%s," % op
            else:
                break

        return string[:-1]


class ROPGadget(object):
    '''
    Class for storing information about a specific ROP gadget.
    '''
    def __init__(self, control, jump, operation=None, description="ROP gaget",
                 base=0):
        self.control = control
        self.exit = jump
        self.operation = operation
        self.description = description
        self.base = base
        self.h = '-' * 112
        if self.base != 0:
            self.h += '-' * 27

        if self.control.opnd1:
            self.control.register = self.control.opnd1
        else:
            self.control.register = self.control.opnd0

        if self.exit.opnd1:
            self.exit.register = self.exit.opnd1
        else:
            self.exit.register = self.exit.opnd0

        if self.operation:
            if self.operation.ea < self.control.ea:
                self.entry = self.operation
            else:
                self.entry = self.control
        else:
            self.operation = self.control
            self.entry = self.control


    def header(self):
        if self.base != 0:
            return self.h + "\n|  Base       + Offset     =  Address       " \
                            "|  Action                                     " \
                            "         |  Control Jump                      " \
                            "    |\n" + self.h
        else:
            return self.h + "\n|  Address     |  Action                     " \
                            "                         |  Control Jump       " \
                            "                   |\n" + self.h

    def footer(self):
        return self.h

    def __str__(self):
        if self.base != 0:
            return "|  0x%.8X + 0x%.8X = 0x%.8X     |  %-50s  | " \
                   " %-5s %-30s  |" % \
                   (self.base, self.entry.ea, self.entry.ea + self.base,
                    str(self.operation), self.exit.mnem, self.control.register)
        else:
            return "|  0x%.8X  |  %-50s  |  %-5s %-30s  |" % \
                   (self.entry.ea, str(self.operation), self.exit.mnem,
                    self.control.register)


class BowcasterBuilder(object):
    '''
    Class to generate bowcaster code from a list of selected ROP gadgets. WIP.
    '''
    INSIZE = 4
    SEARCH_DEPTH = 25

    def __init__(self, gadgets):
        self.code = []
        self.gadgets = gadgets

    def build_code(self):
        keys = list(self.gadgets.keys())
        keys.sort()

        for key in keys[::-1]:
            last_instruction = False
            ea = self.gadgets[key]
            end_ea = ea + self.SEARCH_DEPTH

            while ea <= end_ea:
                mnem = idc.print_insn_mnem(ea)
                if mnem in ['jr', 'jalr']:
                    last_instruction = True
                ea += self.INSIZE

    def print_code(self):
        for line in self.code:
            print(line)


class MIPSROPFinder(object):
    '''
    Primary ROP finder class.
    '''
    CODE = 2
    DATA = 3
    INSIZE = 4
    SEARCH_DEPTH = 25

    def __init__(self):
        self.base = 0

        self._initial_find()

        if self.controllable_jumps or self.system_calls:
            print(("MIPS ROP Finder activated, found %d controllable jumps " \
                  "between 0x%.8X and 0x%.8X" % (len(self.controllable_jumps),
                                                 self.start, self.end)))
        else:
            print("No ROP gadgets found!")

    def _initial_find(self):
        self.start = idc.BADADDR
        self.end = idc.BADADDR
        self.system_calls = []
        self.double_jumps = []
        self.controllable_jumps = []
        start = 0
        end = 0

        for (start, end) in self._get_segments(self.CODE):
            self.controllable_jumps += self._find_controllable_jumps(start, end)
            self.system_calls += self._find_system_calls(start, end)
            self.double_jumps += self._find_double_jumps(start, end)
            if self.start == idc.BADADDR:
                self.start = start
        self.end = end

    def _get_segments(self, attr):
        segments = []
        seg = ida_shims.get_first_seg()

        while seg != idc.BADADDR:
            if ida_shims.get_segm_attr(seg, idc.SEGATTR_TYPE) == attr:
                start = ida_shims.get_segm_start(seg)
                end = ida_shims.get_segm_end(seg)
                segments.append((start, end))
            seg = ida_shims.get_next_seg(seg)

        return segments

    def _get_instruction(self, ea):
        return MIPSInstruction(ida_shims.print_insn_mnem(ea),
                               ida_shims.print_operand(ea, 0),
                               ida_shims.print_operand(ea, 1),
                               ida_shims.print_operand(ea, 2), ea)

    def _does_instruction_match(self, ea, instruction, regex=False):
        i = 0
        op_cnt = 0
        op_ok_cnt = 0
        match = False
        insn = ida_shims.decode_insn(ea)
        mnem = ida_shims.print_insn_mnem(ea)
        if (not instruction.mnem) or (instruction.mnem == mnem) or \
                (regex and re.match(instruction.mnem, mnem)):
            for operand in instruction.operands:
                if operand:
                    op_cnt += 1
                    op = ida_shims.print_operand(ea, i)

                    if regex:
                        if re.match(operand, op):
                            op_ok_cnt += 1
                    elif operand == op:
                        op_ok_cnt += 1
                i += 1

            if op_cnt == op_ok_cnt:
                match = True
        return match

    def _is_bad_instruction(self, ea, bad_instructions=['j', 'b'],
                            no_clobber=[]):
        bad = False
        mnem = ida_shims.print_insn_mnem(ea)

        if mnem and mnem[0] in bad_instructions:
            bad = True
        else:
            insn = ida_shims.decode_insn(ea)
            feature = ida_shims.get_canon_feature(insn)
            for register in no_clobber:
                if (feature & idaapi.CF_CHG1) == idaapi.CF_CHG1:
                    if ida_shims.print_operand(ea, 0) == register:
                        bad = True

        return bad

    def _contains_bad_instruction(self, start_ea, end_ea,
                                  bad_instructions=['j', 'b'], no_clobber=[]):
        ea = start_ea

        while ea <= end_ea:
            if self._is_bad_instruction(ea, bad_instructions, no_clobber):
                return True
            else:
                ea += self.INSIZE

        return False

    def _find_prev_instruction_ea(self, start_ea, instruction, end_ea=0,
                                  no_baddies=True, regex=False,
                                  dont_overwrite=[]):
        instruction_ea = idc.BADADDR
        ea = start_ea

        while ea >= end_ea:
            if self._does_instruction_match(ea, instruction, regex):
                instruction_ea = ea
                break
            elif no_baddies and self._is_bad_instruction(
                    ea, no_clobber=dont_overwrite):
                break

            ea -= self.INSIZE

        return instruction_ea

    def _find_next_instruction_ea(self, start_ea, instruction,
                                  end_ea=idc.BADADDR, no_baddies=False,
                                  regex=False, dont_overwrite=[]):
        instruction_ea = idc.BADADDR
        ea = start_ea

        while ea <= end_ea:
            if self._does_instruction_match(ea, instruction, regex):
                instruction_ea = ea
                break
            elif no_baddies and self._is_bad_instruction(
                    ea, no_clobber=dont_overwrite):
                break

            ea += self.INSIZE

        return instruction_ea

    def _find_controllable_jumps(self, start_ea, end_ea):
        controllable_jumps = []
        t9_controls = [
            MIPSInstruction("move", "\$t9"),
            MIPSInstruction("addiu", "\$t9", "^\$"),
        ]
        t9_jumps = [
            MIPSInstruction("jr", "\$t9"),
        ]
        ra_controls = [
            MIPSInstruction("lw", "\$ra"),
        ]
        ra_jumps = [
            # TODO: Search for jumps to registers other than $ra.
            MIPSInstruction("jr", "\$ra"),
        ]
        t9_musnt_clobber = ["$t9"]
        ra_musnt_clobber = ["$ra"]

        if idaapi.IDA_SDK_VERSION >= 740:
            t9_jumps.append(MIPSInstruction("jalr", "\$ra"))
        else:
            t9_jumps.append(MIPSInstruction("jalr", "\$t9"))

        for possible_control_instruction in t9_controls+ra_controls:
            ea = start_ea

            if possible_control_instruction in t9_controls:
                jumps = t9_jumps
                musnt_clobber = t9_musnt_clobber
            else:
                jumps = ra_jumps
                musnt_clobber = ra_musnt_clobber

            while ea <= end_ea:
                ea = self._find_next_instruction_ea(
                    ea, possible_control_instruction, end_ea, regex=True)
                if ea != idc.BADADDR:
                    insn = ida_shims.decode_insn(ea)

                    control_instruction = self._get_instruction(ea)
                    control_register = control_instruction.operands[1]

                    if control_register:
                        for jump in jumps:
                            jump_ea = self._find_next_instruction_ea(
                                ea+insn.size, jump, end_ea, no_baddies=True,
                                regex=True, dont_overwrite=musnt_clobber)

                            if jump_ea != idc.BADADDR:
                                jump_instruction = self._get_instruction(
                                    jump_ea)
                                controllable_jumps.append(
                                    ROPGadget(
                                        control_instruction, jump_instruction,
                                        description="Controllable Jump",
                                        base=self.base))
                                ea = jump_ea

                    ea += insn.size

        return controllable_jumps

    def _find_system_calls(self, start_ea, end_ea):
        system_calls = []
        system_load = MIPSInstruction("la", "$t9", "system")
        stack_arg_zero = MIPSInstruction("addiu", "$a0", "$sp")

        for xref in idautils.XrefsTo(ida_shims.get_name_ea_simple('system')):
            ea = xref.frm
            mnem = ida_shims.print_insn_mnem(ea)
            if mnem and ea >= start_ea and \
                    ea <= end_ea and mnem[0] in ['j', 'b']:
                a0_ea = self._find_next_instruction_ea(
                    ea+self.INSIZE, stack_arg_zero, ea+self.INSIZE)

                if a0_ea == idc.BADADDR:
                    a0_ea = self._find_prev_instruction_ea(
                        ea, stack_arg_zero, ea-(self.SEARCH_DEPTH*self.INSIZE))

                if a0_ea != idc.BADADDR:
                    control_ea = self._find_prev_instruction_ea(
                        ea-self.INSIZE, system_load,
                        ea-(self.SEARCH_DEPTH*self.INSIZE))

                    if control_ea != idc.BADADDR:
                        system_calls.append(
                            ROPGadget(
                                self._get_instruction(control_ea),
                                self._get_instruction(ea),
                                self._get_instruction(a0_ea),
                                description="System call", base=self.base))

                ea += self.INSIZE
            else:
                break

        return system_calls

    def _find_double_jumps(self, start_ea, end_ea):
        double_jumps = []

        for i in range(0, len(self.controllable_jumps)):
            g1 = self.controllable_jumps[i]
            if g1.exit.mnem != 'jalr':
                continue

            for j in range(i+1, len(self.controllable_jumps)):
                g2 = self.controllable_jumps[j]
                distance = (g2.entry.ea - g1.exit.ea)

                if 0 < distance <= (self.SEARCH_DEPTH * self.INSIZE):
                    if g1.control.register != g2.control.register:
                        if not self._contains_bad_instruction(
                                g1.exit.ea+self.INSIZE,
                                g2.control.ea-self.INSIZE,
                                no_clobber=[g2.control.register]):
                            double_jumps.append(g1)
                            break

        return double_jumps

    def _find_rop_gadgets(self, gadget):
        gadget_list = []

        for controllable_jump in self.controllable_jumps:
            gadget_ea = idc.BADADDR

            ea = self._find_next_instruction_ea(
                controllable_jump.entry.ea, gadget,
                controllable_jump.exit.ea+self.INSIZE, regex=True)

            if ea != idc.BADADDR:
                gadget_ea = ea
            else:
                ea = self._find_prev_instruction_ea(
                    controllable_jump.entry.ea, gadget,
                    controllable_jump.entry.ea-(self.SEARCH_DEPTH*self.INSIZE),
                    no_baddies=True, regex=True,
                    dont_overwrite=[controllable_jump.entry.opnd1])

                if ea != idc.BADADDR:
                    gadget_ea = ea

            if gadget_ea != idc.BADADDR:
                gadget_list.append(
                    ROPGadget(
                        controllable_jump.entry, controllable_jump.exit,
                        self._get_instruction(gadget_ea), base=self.base))

        return gadget_list

    def _print_gadgets(self, gadgets):
        if gadgets:
            print((gadgets[0].header()))

        for gadget in gadgets:
            print((str(gadget)))

        if gadgets:
            print((gadgets[0].footer()))

        print(("Found %d matching gadgets" % (len(gadgets))))

    def _get_marked_gadgets(self):
        rop_gadgets = {}

        for i in range(0 if idaapi.IDA_SDK_VERSION >= 690 else 1, 1024):
            marked_pos = ida_shims.get_bookmark(i)
            if marked_pos != idc.BADADDR:
                marked_comment = ida_shims.get_bookmark_desc(i)
                if marked_comment and marked_comment.lower().startswith("rop"):
                    rop_gadgets[marked_comment] = marked_pos
            else:
                break

        return rop_gadgets

    def double(self):
        self.doubles()

    def doubles(self):
        '''
        Prints a list of all "double jump" gadgets (useful for function calls).
        '''
        self._print_gadgets(self.double_jumps)

    def stackfinder(self):
        self.stackfinders()

    def stackfinders(self):
        '''
        Prints a list of all gadgets that put a stack address into a register.
        '''
        self.find("addiu .*, $sp")

    def lia0(self):
        '''
        Prints a list of all gadgets that load an immediate value number into
        $a0 (useful for setting up the argument to sleep).
        '''
        self.find("li $a0")

    def tail(self):
        return self.tails()

    def tails(self):
        '''
        Prints a lits of all tail call gadgets (useful for function calls).
        '''
        return self.iret()

    def iret(self):
        '''
        Prints a lits of all tail gadgets (useful for function calls).
        '''
        tail_gadgets = []

        for gadget in self._find_rop_gadgets(MIPSInstruction("move", "\$t9")):
            if gadget.exit.mnem == 'jr' and gadget.exit.register == '$t9':
                tail_gadgets.append(gadget)

        self._print_gadgets(tail_gadgets)

    def system(self):
        '''
        Prints a list of gadgets that may be used to call system().
        '''
        sys_gadgets = self.system_calls + self._find_rop_gadgets(
            MIPSInstruction("addiu", "\$a0", "\$sp"))
        self._print_gadgets(sys_gadgets)

    def find(self, *args):
        '''
        Locates all potential ROP gadgets that contain the specified
        instruction.

        @instruction_string - The instruction you need executed. This can be
        either a:

                    o Full instruction    - "li $a0, 1"
                    o Partial instruction - "li $a0"
                    o Regex instruction   - "li $a0, .*"
        '''
        count = 0
        good_gadgets = {}
        final_gadgets = []
        registers = ['$v', '$s', '$a', '$t', '$k', '$pc', '$fp', '$ra', '$gp',
                     '$at', '$zero']

        for instruction_string in args:
            comma_split = instruction_string.split(',')
            instruction_parts = comma_split[0].split()
            if len(comma_split) > 1:
                instruction_parts += comma_split[1:]

            for i in range(0, 4):
                if i > len(instruction_parts) - 1:
                    instruction_parts.append(None)
                else:
                    instruction_parts[i] = instruction_parts[i].strip().\
                        strip(',').strip()
                    for reg in registers:
                        instruction_parts[i] = instruction_parts[i].replace(
                            reg, "\\%s" % reg)

            instruction = MIPSInstruction(
                instruction_parts[0], instruction_parts[1],
                instruction_parts[2], instruction_parts[3])

            gadgets = self._find_rop_gadgets(instruction)
            for gadget in gadgets:
                if count == 0:
                    good_gadgets[gadget.control.ea] = {'gadget': gadget,
                                                       'count': 1}
                elif gadget.control.ea in good_gadgets:
                    good_gadgets[gadget.control.ea]['count'] += 1
                    break
            count += 1

        if good_gadgets:
            for (ea, info) in list(good_gadgets.items()):
                if info['count'] == count:
                    final_gadgets.append(info['gadget'])
                else:
                    print(("Skipping gadget at 0x%X" % ea))
        if final_gadgets:
            self._print_gadgets(gadgets)
        else:
            print("No ROP gadgets found!")

    def summary(self):
        '''
        Prints a summary of your currently marked ROP gadgets, in alphabetical
        order by the marked name. To mark a location as a ROP gadget, simply
        mark the position in IDA (Alt+M) with any name that starts with "ROP".
        '''
        rop_gadgets = self._get_marked_gadgets()
        summaries = []
        delim_char = "-"
        headings = {
            'name': "Gadget Name",
            'offset': "Gadget Offset",
            'summary': "Gadget Summary"
        }
        if self.base != 0:
            headings['offset'] = "Gadget Base + Offset = Address       "
        lengths = {
            'name': len(headings['name']),
            'offset': len(headings['offset']),
            'summary': len(headings['summary']),
        }
        total_length = (3 * len(headings)) + 1

        if rop_gadgets:
            gadget_keys = list(rop_gadgets.keys())
            gadget_keys.sort()

            for marked_comment in gadget_keys:
                if len(marked_comment) > lengths['name']:
                    lengths['name'] = len(marked_comment)

                summary = []
                ea = rop_gadgets[marked_comment]
                end_ea = ea + (self.SEARCH_DEPTH * self.INSIZE)

                while ea <= end_ea:
                    summary.append(idc.GetDisasm(ea))
                    mnem = ida_shims.print_insn_mnem(ea)
                    if len(mnem) > 0 and mnem[0].lower() in ['j', 'b']:
                        summary.append(idc.GetDisasm(ea+self.INSIZE))
                        break

                    ea += self.INSIZE

                if len(summary) == 0:
                    summary.append('')

                for line in summary:
                    if len(line) > lengths['summary']:
                        lengths['summary'] = len(line)

                summaries.append(summary)

            for (heading, size) in list(lengths.items()):
                total_length += size

            delim = delim_char * total_length
            line_fmt = "| %%-%ds | %%-%ds | %%-%ds |" % \
                       (lengths['name'], lengths['offset'], lengths['summary'])

            print(delim)
            print((line_fmt % \
                  (headings['name'], headings['offset'], headings['summary'])))
            print(delim)

            for i in range(0, len(gadget_keys)):
                line_count = 0
                marked_comment = gadget_keys[i]
                if self.base != 0:
                    offset = "0x%.8X + 0x%.8X = 0x%.8X" % \
                             (self.base, rop_gadgets[marked_comment],
                              self.base + rop_gadgets[marked_comment])
                else:
                    offset = "0x%.8X" % rop_gadgets[marked_comment]
                summary = summaries[i]

                for line in summary:
                    if line_count == 0:
                        print((line_fmt % (marked_comment, offset, line)))
                    else:
                        print((line_fmt % ('', '', line)))

                    line_count += 1

                print(delim)

    def build(self):
        '''
        WIP.
        '''
        gadgets = self._get_marked_gadgets()
        bc = BowcasterBuilder(gadgets)
        bc.build_code()

    def set_base(self, base=0):
        '''
        Set base address used for display
        '''
        self.base = base
        self._initial_find()

    def help(self):
        '''
        Show help info.
        '''
        delim = "-" * 140

        print("")
        print("mipsrop.find(instruction_string)")
        print(delim)
        print((self.find.__doc__))

        print("")
        print("mipsrop.system()")
        print(delim)
        print((self.system.__doc__))

        print("")
        print("mipsrop.doubles()")
        print(delim)
        print((self.doubles.__doc__))

        print("")
        print("mipsrop.stackfinders()")
        print(delim)
        print((self.stackfinders.__doc__))

        print("")
        print("mipsrop.tails()")
        print(delim)
        print((self.tails.__doc__))

        print("")
        print("mipsrop.set_base()")
        print(delim)
        print((self.set_base.__doc__))

        print("")
        print("mipsrop.summary()")
        print(delim)
        print((self.summary.__doc__))


try:
    class MipsRopHandler(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)

        def activate(self, ctx):
            global mipsrop
            mipsrop = MIPSROPFinder()
            add_to_namespace('__main__', 'mipsrop', mipsrop)
            return 1

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


class mipsropfinder_t(idaapi.plugin_t):
    flags = 0
    comment = 'MIPS ROP Finder'
    help = ''
    action_name = 'mipsrop:action'
    wanted_name = "MIPS ROP Finder"
    wanted_hotkey = ""
    wanted_tooltip = "MIPS ROP Gadget Finder"
    root_tab = 'Search/'
    menu_name = 'mips rop gadgets'
    menu_context = None

    def init(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            # 199 is a default icon.
            action_desc = idaapi.action_desc_t(self.action_name,
                                               self.menu_name,
                                               MipsRopHandler(),
                                               self.wanted_hotkey,
                                               self.wanted_tooltip,
                                               199)

            idaapi.register_action(action_desc)
            idaapi.attach_action_to_menu(
                self.root_tab, self.action_name, idaapi.SETMENU_APP)
        else:
            self.menu_context = idaapi.add_menu_item(
                "Search/", self.menu_name, "", 0, self.run, (None,))
        return idaapi.PLUGIN_KEEP

    def term(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            idaapi.detach_action_from_menu(self.root_tab, self.action_name)
        else:
            if self.menu_context is not None:
                idaapi.del_menu_item(self.menu_context)
        return None

    def run(self, arg):
        global mipsrop
        mipsrop = MIPSROPFinder()


def PLUGIN_ENTRY():
        return mipsropfinder_t()


repository: csl-ugent/TREX
file: containers/ida/plugins/idapatch.py
description: Toolbox for Reverse Engineering
created_at: 2024-08-20 14:39:11+00:00
pushed_at: 2025-02-07 15:39:21+00:00
url:
https://github.com/csl-ugent/TREX/blob/07604b35c559c53bed61c60c7bd574fcad350364/containers/ida/plugins/idapatch.py

content:
# IDAupdate, ida plugin to patch bytes in
from idaapi import BADADDR
from idc import auto_wait, qexit, AU_UNK, AU_CODE, get_item_head, auto_mark_range, patch_byte, plan_and_wait


def run_ida():
    print("[SCRIPT] Waiting for ida to finish auto-analysis..")
    auto_wait()
    print("[SCRIPT] .. resuming script execution")

    with open("IDAupdate.txt", 'r') as fin:
        print("patching..")
        for line in fin:
            # addr, bytes
            tokens = line.split(",")

            # hex(int(tokens[0],16)
            ins_size = int(len(tokens[1]) / 2) - 1
            i = 0
            original_start = get_item_head(int(tokens[0], 16))
            auto_mark_range(original_start, original_start + ins_size, AU_UNK)
            while i < ins_size:
                patch_byte(int(tokens[0], 16) + i, int(tokens[1][2 + 2 * i:2 + 2 * i + 2], 16))
                # create_byte(int(tokens[0],16)+i,1)
                i = i + 1
            auto_mark_range(original_start, original_start + ins_size, AU_CODE)

        print("reanalyzing binary after patching..")
        # ida_auto.plan_and_wait(ida_entry.get_entry(0), BADADDR)
        plan_and_wait(0, BADADDR)

    qexit(0)


def PLUGIN_ENTRY():
    print("plug entry")
    run_ida()


# when this script is loaded as the main program (i.e., a plugin in IDA),
# execute the 'run_ida' function
if __name__ == "__main__":
    print("running as plugin")
    run_ida()
else:
    run_ida()


repository: purseclab/Patcherex2-Extras
file: decompiler-plugins/src/patcherex2/decompiler_plugins/patcherex2_plugin.py
created_at: 2024-04-30 18:06:12+00:00
pushed_at: 2025-01-15 08:25:13+00:00
url:
https://github.com/purseclab/Patcherex2-Extras/blob/4990be0be5aa537caf4686015561563ed07aea6e/decompiler-plugins/src/patcherex2/decompiler_plugins/patcherex2_plugin.py

content:
# Plugin that allows you to use Patcherex2 in the most common decompilers
# @author PurSec Lab
# @category Patcherex2
# @menupath Tools.Patcherex2

# pyright: reportMissingImports=false

plugin_command = "patcherex2-decompiler-plugins -s ghidra"


def create_plugin(*args, **kwargs):
    from patcherex2.decompiler_plugins import create_plugin as _create_plugin

    return _create_plugin(*args, **kwargs)


# =============================================================================
# LibBS generic plugin loader (don't touch)
# =============================================================================

import sys  # noqa E402

# Python 2 has special requirements for Ghidra, which forces us to use a different entry point
# and scope for defining plugin entry points
if sys.version[0] == "2":
    # Do Ghidra Py2 entry point
    import subprocess

    from libbs_vendored.ghidra_bridge_server import GhidraBridgeServer

    GhidraBridgeServer.run_server(background=True)
    process = subprocess.Popen(plugin_command.split(" "))
    if process.poll() is not None:
        raise RuntimeError(
            "Failed to run the Python3 backed. It's likely Python3 is not in your Path inside Ghidra."
        )
else:
    # Try plugin discovery for other decompilers
    try:
        import idaapi  # noqa F401

        has_ida = True
    except ImportError:
        has_ida = False
    try:
        import angrmanagement  # noqa F401

        has_angr = True
    except ImportError:
        has_angr = False

    if not has_ida and not has_angr:
        create_plugin()
    elif has_angr:
        # class AngrBSPluginThunk(BasePlugin):
        #     def __init__(self, workspace):
        #         super().__init__(workspace)
        #         globals()["workspace"] = workspace
        #         self.plugin = create_plugin()

        #     def teardown(self):
        #         pass
        from patcherex2.decompiler_plugins.decompiler_specific.angr.interface import (
            Patcherex2Plugin,
        )

        __all__ = ["Patcherex2Plugin"]


def PLUGIN_ENTRY(*args, **kwargs):  # noqa N802
    """
    This is the entry point for IDA to load the plugin.
    """
    return create_plugin(*args, **kwargs)


repository: kyrus/ida-translator
file: translator.py
url: https://github.com/kyrus/ida-translator/blob/a9fbdb773274f48c885c54ead8acbff15f54dcea/translator.py
wanted_name: Translations
created_at: 2013-01-16 17:41:56+00:00
pushed_at: 2015-02-09 14:32:56+00:00
description:
A plugin for IDA Pro that assists in decoding arbitrary character sets in an IDA Pro database into Unicode, then automatically invoking a web-based translation service (currently Google Translate) to translate that foreign text into English.

content:
#!/usr/bin/python
# -*- coding: utf-8 -*-

from PySide import QtGui, QtCore
from PySide import QtUiTools
from idaapi import PluginForm
import urllib
import json
import idc
import bisect
import pickle
import os
import collections

import chardet

import json

import string
import csv

"""
The user interface of the Translation Plugin. Mostly auto-generated from Qt
Designer, with some custom tweaks.
"""
class Ui_Dialog(object):
	def setupUi(self, Dialog):
		Dialog.setObjectName("Dialog")
		Dialog.resize(487, 732)
		self.verticalLayout_4 = QtGui.QVBoxLayout(Dialog)
		self.verticalLayout_4.setObjectName("verticalLayout_4")
		self.groupBox = QtGui.QGroupBox(Dialog)
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(2)
		sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
		self.groupBox.setSizePolicy(sizePolicy)
		self.groupBox.setObjectName("groupBox")
		self.verticalLayout = QtGui.QVBoxLayout(self.groupBox)
		self.verticalLayout.setObjectName("verticalLayout")
		self.horizontalLayout = QtGui.QHBoxLayout()
		self.horizontalLayout.setObjectName("horizontalLayout")
		self.label = QtGui.QLabel(self.groupBox)
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(0)
		sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
		self.label.setSizePolicy(sizePolicy)
		self.label.setObjectName("label")
		self.horizontalLayout.addWidget(self.label)
		self.encodingComboBox = QtGui.QComboBox(self.groupBox)
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(0)
		sizePolicy.setHeightForWidth(self.encodingComboBox.sizePolicy().hasHeightForWidth())
		self.encodingComboBox.setSizePolicy(sizePolicy)
		self.encodingComboBox.setObjectName("encodingComboBox")
		self.horizontalLayout.addWidget(self.encodingComboBox)
		self.verticalLayout.addLayout(self.horizontalLayout)
		self.originalTextBox = QtGui.QPlainTextEdit(self.groupBox)
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(2)
		sizePolicy.setHeightForWidth(self.originalTextBox.sizePolicy().hasHeightForWidth())
		self.originalTextBox.setSizePolicy(sizePolicy)
		font = QtGui.QFont()
		font.setFamily("Arial")
		font.setPointSize(14)
		self.originalTextBox.setFont(font)
		self.originalTextBox.setObjectName("originalTextBox")
		self.verticalLayout.addWidget(self.originalTextBox)
		self.horizontalLayout_2 = QtGui.QHBoxLayout()
		self.horizontalLayout_2.setObjectName("horizontalLayout_2")

###
# TODO: add custom font support
#		self.label_2 = QtGui.QLabel(self.groupBox)
#		self.label_2.setObjectName("label_2")
#		self.horizontalLayout_2.addWidget(self.label_2)
#		self.fontComboBox = QtGui.QFontComboBox(self.groupBox)
#		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
#		sizePolicy.setHorizontalStretch(0)
#		sizePolicy.setVerticalStretch(0)
#		sizePolicy.setHeightForWidth(self.fontComboBox.sizePolicy().hasHeightForWidth())
#		self.fontComboBox.setSizePolicy(sizePolicy)
#		self.fontComboBox.setObjectName("fontComboBox")
#		self.horizontalLayout_2.addWidget(self.fontComboBox)
#		self.fontSizeSpinBox = QtGui.QSpinBox(self.groupBox)
#		self.fontSizeSpinBox.setObjectName("fontSizeSpinBox")
#		self.horizontalLayout_2.addWidget(self.fontSizeSpinBox)
###

		self.verticalLayout.addLayout(self.horizontalLayout_2)
		self.verticalLayout_4.addWidget(self.groupBox)
		self.translateButton = QtGui.QPushButton(Dialog)
		self.translateButton.setObjectName("translateButton")
		self.verticalLayout_4.addWidget(self.translateButton)
		self.translatedTextBox = QtGui.QPlainTextEdit(Dialog)
		sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
		sizePolicy.setHorizontalStretch(0)
		sizePolicy.setVerticalStretch(0)
		sizePolicy.setHeightForWidth(self.translatedTextBox.sizePolicy().hasHeightForWidth())
		self.translatedTextBox.setSizePolicy(sizePolicy)
		font = QtGui.QFont()
		font.setFamily("MS Shell Dlg 2")
		font.setPointSize(8)
		self.translatedTextBox.setFont(font)
		self.translatedTextBox.setObjectName("translatedTextBox")
		self.verticalLayout_4.addWidget(self.translatedTextBox)
		self.horizontalLayout_3 = QtGui.QHBoxLayout()
		self.horizontalLayout_3.setObjectName("horizontalLayout_3")
		self.verticalLayout_2 = QtGui.QVBoxLayout()
		self.verticalLayout_2.setObjectName("verticalLayout_2")
		self.commentCheckBox = QtGui.QCheckBox(Dialog)
		self.commentCheckBox.setObjectName("commentCheckBox")
		self.verticalLayout_2.addWidget(self.commentCheckBox)
		self.nameXrefCheckBox = QtGui.QCheckBox(Dialog)
		self.nameXrefCheckBox.setObjectName("nameXrefCheckBox")
		self.verticalLayout_2.addWidget(self.nameXrefCheckBox)
		self.horizontalLayout_3.addLayout(self.verticalLayout_2)
		spacerItem = QtGui.QSpacerItem(118, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
		self.horizontalLayout_3.addItem(spacerItem)
		self.verticalLayout_3 = QtGui.QVBoxLayout()
		self.verticalLayout_3.setObjectName("verticalLayout_3")
		self.buttonBox = QtGui.QDialogButtonBox(Dialog)
		self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
		self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
		self.buttonBox.setObjectName("buttonBox")
		self.verticalLayout_3.addWidget(self.buttonBox)
		self.horizontalLayout_3.addLayout(self.verticalLayout_3)
		self.verticalLayout_4.addLayout(self.horizontalLayout_3)

		self.retranslateUi(Dialog)
		QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept)
		QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject)
		QtCore.QMetaObject.connectSlotsByName(Dialog)

	def retranslateUi(self, Dialog):
		Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Translate Text", None, QtGui.QApplication.UnicodeUTF8))
		self.groupBox.setTitle(QtGui.QApplication.translate("Dialog", "Original Text", None, QtGui.QApplication.UnicodeUTF8))
		self.label.setText(QtGui.QApplication.translate("Dialog", "Encoding:", None, QtGui.QApplication.UnicodeUTF8))
#		self.label_2.setText(QtGui.QApplication.translate("Dialog", "Font", None, QtGui.QApplication.UnicodeUTF8))
		self.translateButton.setText(QtGui.QApplication.translate("Dialog", "Google Translate", None, QtGui.QApplication.UnicodeUTF8))
		self.commentCheckBox.setText(QtGui.QApplication.translate("Dialog", "Add comment", None, QtGui.QApplication.UnicodeUTF8))
		self.nameXrefCheckBox.setText(QtGui.QApplication.translate("Dialog", "Add public name xref", None, QtGui.QApplication.UnicodeUTF8))


"""
Translation icon
"""
XLATE_ICON = (
	"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x14\x00\x00\x00\x14\x08\x06\x00\x00\x00\x8d\x89\x1d\r\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00!tEXtSoftware\x00GraphicConverter (Intel)w\x87\xfa\x19\x00\x00\x02.IDATx\x9c\xcc\x94MHTq\x14\xc5\xdf\xaeM\x0b7-\xda\xb4h\xd3\xa2Mp\xdd(\r\xc5\x04\x15\x15T\x88P\x11B\x08)\xa2\x96FI_\x06R\x16T\xf4I\x84\x8b\xa4\x82h\x11\xd5&\xa2E\x11m**\xf3c\x9845m\x1a3-?\x98\x1c'?\xe2t\x8e(<\x9d\xbfo\\\xb8\xe8\xc2ax\xf0\xde\x8f{\xef9w\xbcH\xe3\x1bo1\xb5\xa8\xb0Y\xc0\xd0\xdb\t\xd8\xcb1\xd8\xd3\x11\xd8\xc3a\xd8\xbd_\xb0\xfa\x1f\xb0\x9b=\xb0\xab1\xd8\x85.\xd8\xd9\x0eX\xcdgXu+\xecX\x14\xa1\x13\x11\xcc\x0b\x14\xac09\x89\x82\xde\x14v\xb7%\x90\xffn\x10;\x9e\xf7a\xeb\xa386\xdd\xeeF\xf8Z;Bg\xa2\xc8\xa9jFv\xf1\x07X\xde{\xd8\xa1\x96\x00 ;\x13\xcc.\x8f\xc2j\xd9]u\x1cV\xd5\t\xab\xf8\x04+k\x81\x954\xc2\x8a\x1b`E\x84\xed'l-U\xd1\x14\x00\xe4\x98M\x00&\xa8?\xd4\xc0\xd8$\xcao5`\xdf\xf5\xd7\xd8{\xe9\x15v\x9d\x7f\x81\xbc\xdag\xd8^\xf3\x04[N>\xc6\xc6\xa3\x0f\x10>|\x1f\xa1\x83w\x11\xae\xbc\x83t w\xd6<\rKR\xfd\x04\n\x96\xa9R\xec \xbb\xa8\xce\x01\xa4\x01\x91i\xd8oj\x98Rgs\xcb\xf3\xbcY\xcf\xc9q`M\xe1\r\x07\x90nF}\xb0AJc\xce\x07\x9c\xf9Mp\xa4\xd5\x05W\x1c@F\xa3\xd5\x07\xfbIig3\x1f\xbb\xa4\x1aJ\x01\xab\xf6\\t\x00\x99\xb36\x1f\xac\x8f\x92\x01\x99j`\x14X\x99\x7f\xce\x01dh\xdb}\xb0\xef\x94\xdc\xf4\x8f:\xb7;U\xff\x08\xb0b\xe7i\x07\x90\x17\xd0\xe9\x83\xc5)E#S\xf5r\xe9\xcb\xb7\x9dr\x00yN_|\xb0\xaf\x94r6\xfe7x\x87\xf1\x04\xb0l\xf3q\x07\x90\xb7\xd9\xcd\x17b\xd47\xaa\x87Rh\x95\xb3\xa0\x8a\xd1\xc5\xac\rG\xd2\x81:t\xdd\xa6\xce\xc9\xcayf\xa5\x1f\x91SZ?\x95\xb3\xa0\xea\x1a\x02\x96\xae\xabL\x07\xba\xa4\x0bP\xce\x14\r\xb9)\x03\xb43\x8d\xa9\xce\x04\xeb`,\x96\xe4\x96-\x0c\x98[R7\x15Z\xe5L\xd1\x90\x9b2@;\xd3\x98\xeaL\xb0\xac\xf5\x07\x16\x06\xfc/\xfe\xb1\xff\x01\x00\x00\xff\xff\x03\x00\x831gE\x18\x18k\x00\x00\x00\x00\x00IEND\xaeB`\x82")

"""
The list of encodings that we recognize.
"""
default_encodings = [
 'ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig',
 'mbcs',
 'palmos']


def getString(startea, endea):
	outstr = []
	for i in range(startea, endea):
		outstr.append(chr(Byte(i)))
	return ''.join(outstr)

def getUnicodeTranslatedString(startea, endea):
	thestr = getString(startea, endea)
	the_charset = chardet.detect(thestr)
	encoding = the_charset['encoding']

	return (encoding, thestr.decode(encoding))

def doTranslate(original_string, api_key):
		query_param = urllib.urlencode({'q': original_string.encode('utf8'), 'target': 'en', 'key': api_key})
		url = 'https://www.googleapis.com/language/translate/v2?' + query_param
		u = urllib.urlopen(url)
		d = u.read()
		u.close()

		json_decoder = json.JSONDecoder(encoding='utf-8')
		translation = json_decoder.decode(d)
		# take the first translation
		translated_string = translation['data']['translations'][0]['translatedText']
		return translated_string

"""
Model class that contains all of the translations performed in this IDB file.
The model is initialized when the plugin is initialized from an IDC array 
stored inside of the IDB file, so translations are persistent across IDA
invocations.
"""
class TranslateModel(QtCore.QAbstractTableModel):
	def __init__(self, parent = None):
		super(TranslateModel, self).__init__(parent)

		self.icon = QtGui.QPixmap()
		self.icon.loadFromData(XLATE_ICON, "PNG")

		# initialize from netnodes in the current idb file
		self.xlate_array = GetArrayId('translations')
		if self.xlate_array == -1:
			self.xlate_array = CreateArray('translations')

		self.translations = collections.OrderedDict()

		cur_ea = GetFirstIndex(AR_STR, self.xlate_array)
		while cur_ea != -1:
			entry = pickle.loads(GetArrayElement(AR_STR, self.xlate_array, cur_ea))
			self.translations[entry[0]] = entry
			cur_ea = GetNextIndex(AR_STR, self.xlate_array, cur_ea)

		print '[TranslatePlugin] Loaded %d translations' % len(self.translations)

	def rowCount(self, index=QtCore.QModelIndex()):
		return len(self.translations)

	def columnCount(self, index=QtCore.QModelIndex()):
		return 3

	def data(self, index, role=QtCore.Qt.DisplayRole):
		if not index.isValid():
			return None

		if not 0 <= index.row() < len(self.translations):
			return None

		if role == QtCore.Qt.DisplayRole:
			i = index.row()
			(address, original_string, translation) = self.translations.values()[i]

			if index.column() == 0:
				return '%08x' % address
			elif index.column() == 1:
				return original_string
			elif index.column() == 2:
				return translation
			else:
				return None

		if role == QtCore.Qt.FontRole:
			return QtGui.QFont().setPointSize(30)

		if role == QtCore.Qt.DecorationRole and index.column() == 0:
			return self.icon

	def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
		if role != QtCore.Qt.DisplayRole:
			return None

		if orientation == QtCore.Qt.Horizontal:
			if section == 0:
				return "Address"
			elif section == 1:
				return "Original String"
			elif section == 2:
				return "Translation"
			else:
				return None

	def addTranslation(self, ea, original_text, translated_text):
		element = (ea, original_text, translated_text)
		pos = bisect.bisect(self.translations.keys(), ea)
		if ea not in self.translations:
			# we're adding a new element
			self.beginInsertRows(QtCore.QModelIndex(), pos, pos)
			self.translations[ea] = element
			# Note: OrderedDict is not SortedDict
			self.translations = collections.OrderedDict(sorted(self.translations.iteritems(), key=lambda x: x[0]))
			self.endInsertRows()
		else:
			# modify existing element
			self.translations[ea] = element

		# add to idb
		SetArrayString(self.xlate_array, ea, pickle.dumps(element))


"""
Dialog box that is invoked when the hotkey is pressed. This dialog should have
no direct interactions with IDA, allowing it to be pulled out to other
applications. All interactions with IDA are handled by the Translator class
below.
"""
class TranslatorDialog(QtGui.QDialog):
	def __init__(self, parent, cfg):
		super(TranslatorDialog, self).__init__(parent)

		self.config = cfg
		self.callback_fn = None

		self.ui = Ui_Dialog()
		self.ui.setupUi(self)
		self.ui.commentCheckBox.setChecked(True)
		self.ui.nameXrefCheckBox.setChecked(True)

		self.ui.translateButton.clicked.connect(self.translateButtonPressed)
		self.accepted.connect(self.successCallback)
		self.ui.translatedTextBox.textChanged.connect(self.translatedTextChanged)

		self.detected_encoding = None
		self.encoding = None

		self.isValidEncoding = False

	def translatedTextChanged(self):
		doc = self.ui.translatedTextBox.document()
		self.translated_text = doc.toPlainText()
		charCount = len(self.translated_text)
		if charCount == 0:
			self.enableTranslationCheckboxes(False)
		else:
			self.enableTranslationCheckboxes(True)

		self.translated_text = doc.toPlainText()

	def setEncoding(self, encoding):
		self.encoding = encoding
		new_text = ''
		try:
			new_text = self.original_text.decode(encoding)
			self.isValidEncoding = True
			self.ui.translateButton.setEnabled(True)
		except UnicodeDecodeError as e:
			new_text = "(Error: %s)" % (e,)
			self.ui.translateButton.setEnabled(False)
			self.isValidEncoding = False

		self.ui.originalTextBox.setPlainText(new_text)
		self.clearTranslatedText()

	def encodingChanged(self, new_encoding_idx):
		self.setEncoding(self.ui.encodingComboBox.itemData(new_encoding_idx))

	def translateButtonPressed(self):
		# call translate upon button press
		if self.isValidEncoding:
			translated_string = doTranslate(self.ui.originalTextBox.toPlainText(), self.config.getApiKey())
			self.setTranslatedText(translated_string)

	def setOriginalText(self, original_text, detected_encoding):
		self.original_text = original_text
		self.setEncoding(detected_encoding)

		self.initializeEncodingList(detected_encoding)

	def enableTranslationCheckboxes(self, flag):
		self.ui.commentCheckBox.setEnabled(flag)
		self.ui.nameXrefCheckBox.setEnabled(flag)

	def clearTranslatedText(self):
		self.setTranslatedText('')

	def setTranslatedText(self, translated_text):
		self.ui.translatedTextBox.setPlainText(translated_text)

	def registerSuccessCallback(self, fn):
		self.callback_fn = fn

	def successCallback(self):
		if self.callback_fn != None and self.isValidEncoding:
			self.config.setFallbackEncoding(self.encoding)
			self.callback_fn(self.translated_text, self.original_text.decode(self.encoding),
				self.ui.commentCheckBox.isChecked(), self.ui.nameXrefCheckBox.isChecked())

	def initializeEncodingList(self, detected_encoding):
		for s in default_encodings:
			self.ui.encodingComboBox.addItem(s, s)
		self.detected_encoding = detected_encoding
		self.ui.encodingComboBox.insertSeparator(0)
		self.ui.encodingComboBox.insertItem(0, "%s (auto-detected)" % detected_encoding, detected_encoding)
		self.ui.encodingComboBox.setCurrentIndex(0)
		self.isValidEncoding = True

		self.ui.encodingComboBox.currentIndexChanged.connect(self.encodingChanged)

"""
The controller class that mediates between the TranslateModel,
TranslatorDialog, and the translation functions (doTranslate, etc) that
perform the actual translation.
"""
class Translator(object):
	def __init__(self, parent, model, cfg):
		self.parent = parent
		self.model = model
		self.config = cfg

		self.deletechars=''.join([chr(x) for x in range(0,0x20) + range(0x21, 0x30) + range(0x3a, 0x41) \
			+ range(0x5b, 0x61) + range(0x7b, 0xff)])
		self.transtable = ''.join([chr(x) for x in range(0,256)])

		# read in configuration (api key)
		pass

	def hotkeyPressed(self):
		# XXX: the following line is used to work around an error importing chardet when this
		# function is invoked as a hotkey from IDA. WTF?
		chardet = __import__('chardet', globals(), locals(), [], -1)

		# get the parameters (address, length) from IDA
		ea = SelStart()
		end_ea = SelEnd()
		if ea == BADADDR:
			ea = ScreenEA()
			if ItemSize(ea) > 1:
				end_ea = ea + ItemSize(ea)
			else:
				# find next named byte
				end_ea = NextHead(ScreenEA())
				for i in range(ea+1, end_ea):
					name = GetTrueNameEx(BADADDR, i)
					b = Byte(i)
					if name != '':
						end_ea = i
						break
					elif b == 0:
						end_ea = i
						break

		length = end_ea - ea

		print '[TranslatePlugin] Found string at [%08x:%08x] len %08x' % (ea, end_ea, length)

		self.ea = ea

		try:
			(encoding,original_string) = getUnicodeTranslatedString(ea,end_ea)
		except:
			encoding = self.config.getFallbackEncoding()

		# display the dialog box
		self.displayDialogBox(ea, length, getString(ea,end_ea), encoding)

	def displayDialogBox(self, ea, length, original_string, encoding):
		# build and display dialog box
		dialog = TranslatorDialog(self.parent, self.config)
		dialog.setOriginalText(original_string, encoding)
		dialog.registerSuccessCallback(self.gotTranslatedData)
		dialog.setModal(True)
		dialog.show() # can't do exec() since ida will show its "running python script" window

	def gotTranslatedData(self, translation, original, do_comment, do_xref):
		self.model.addTranslation(self.ea, original, translation)

		if len(translation) > 0:
			if do_comment:
				MakeRptCmt(self.ea, translation.encode('ascii'))
			if do_xref:
				xref_name = string.translate(translation.encode('ascii'), self.transtable, self.deletechars)
				idaapi.do_name_anyway(self.ea, 'a'+''.join([a.title() for a in xref_name.split(' ')]))
		idaapi.refresh_idaview_anyway()

"""
The TranslateTableView class exists only so we can intercept keyPressEvent
in order to handle the case where the user hits Enter on a line in our
QTableView.
"""
class TranslateTableView(QtGui.QTableView):
	def keyPressEvent(self, event):
		if event.key() == QtCore.Qt.Key_Return:
			selectedRows = self.selectionModel().selectedRows()
			if selectedRows:
				selectedRow = selectedRows[0]
				self.doubleClicked.emit(selectedRow)
		else:
			QtGui.QTableView.keyPressEvent(self, event)

"""
TranslateFormClass is a view to the TranslateModel, displaying all
translations in a dockable IDA window. We don't use IDA's built-in choose2
class here since it doesn't support Unicode characters. Instead, we try to
emulate choose2's behavior as closely as possible.
"""
class TranslateFormClass(PluginForm):
	def __init__(self,cfg):
		super(TranslateFormClass, self).__init__()
		self.config = cfg

	def OnCreate(self,form):
		self.parent = self.FormToPySideWidget(form)
		self.PopulateModel()
		self.PopulateMainForm()
		self.CreateContextMenu()
		translator = Translator(self.parent, self.translationModel, self.config)

		# XXX: the following line is used to work around an error importing chardet when this
		# function is invoked as a hotkey from IDA. WTF?
		chardet = __import__('chardet', globals(), locals(), [], -1)

		chardet.detect('')

		# set hotkey
		try:
			hotkey_ctx
			if idaapi.del_hotkey(hotkey_ctx):
				del hotkey_ctx
		except:
			hotkey_ctx = idaapi.add_hotkey("Ctrl-R", translator.hotkeyPressed)
			if hotkey_ctx is None:
				del hotkey_ctx


	def CreateContextMenu(self):
		self.contextMenu = QtGui.QMenu(self.parent)
		copyAction = self.contextMenu.addAction("&Copy")
		exportAction = self.contextMenu.addAction("E&xport...")
		self.contextMenu.addSeparator()

		copyAction.triggered.connect(self.copyItem)
		exportAction.triggered.connect(self.exportAll)

	def getSelectedRow(self):
		selectedRows = self.tableview.selectionModel().selectedIndexes()
		if len(selectedRows) == 0:
			return None
		else:
			return [self.translationModel.data(item) for item in selectedRows]

	def copyItem(self):
		theItem = self.getSelectedRow()
		if theItem != None:
			toCopy = u' '.join([unicode(x) for x in theItem])
			QtGui.QApplication.clipboard().setText(toCopy)

	def exportAll(self):
		outputfilename = QtGui.QFileDialog.getSaveFileName(self.parent, 'Save CSV export to...')
		if outputfilename == None:
			return

		numcolumns = self.translationModel.columnCount()
		with open(outputfilename[0], 'wb') as outputfile:
			outputfile.write(u'\ufeff'.encode('utf8')) # BOM (optional...Excel needs it to open UTF-8 file properly)
			csvout = csv.writer(outputfile)
			# write the header
			csvout.writerow([self.translationModel.headerData(x, QtCore.Qt.Horizontal) for x in range(0,numcolumns)])
			for row in range(0,self.translationModel.rowCount()):
				csvout.writerow([self.translationModel.data(index).encode('utf8') \
					for index in \
					[self.translationModel.index(row, column) for column in range(0,numcolumns)]])
			print '[TranslatePlugin] Exported %d translations to %s' % (self.translationModel.rowCount(), str(outputfilename[0]))

	def showContextMenu(self):
		self.contextMenu.exec_(QtGui.QCursor.pos())

	def PopulateModel(self):
		self.translationModel = TranslateModel()

	def PopulateMainForm(self):
		layout = QtGui.QVBoxLayout()
		tableview = QtGui.QTableView()

		layout.setContentsMargins(0,0,0,0)
		layout.setSpacing(0)

		tableview = TranslateTableView()
		tableview.setModel(self.translationModel)
		tableview.setSortingEnabled(False)
		tableview.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
		tableview.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
		tableview.setShowGrid(False)
		hdr = tableview.verticalHeader()
		hdr.setHighlightSections(False)
		hdr.setDefaultSectionSize(hdr.minimumSectionSize())
		hdr = tableview.horizontalHeader()
		hdr.setHighlightSections(False)
		hdr.setDefaultAlignment(QtCore.Qt.AlignLeft)

		tableview.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
		copyAction = QtGui.QAction("&Copy", self.parent)
		copyAction.setShortcut("Ctrl+C")
		exportAction = QtGui.QAction("E&xport all to CSV...", self.parent)
		exportAction.setShortcut("Shift+Ins")
		separator = QtGui.QAction(self.parent)
		separator.setSeparator(True)
		tableview.addAction(copyAction)
		tableview.addAction(exportAction)
		tableview.addAction(separator)

		copyAction.triggered.connect(self.copyItem)
		exportAction.triggered.connect(self.exportAll)

		layout.addWidget(tableview)
		self.parent.setLayout(layout)

		tableview.doubleClicked.connect(self.rowClicked)
		tableview.resizeColumnsToContents()

		self.tableview = tableview

	def rowClicked(self, index):
		ea = self.translationModel.translations.keys()[index.row()]
		print '[TranslatePlugin] Selected translation @ ea %08x' % ea
		Jump(ea)

	def OnClose(self,form):
		pass

"""
Configuration object. Right now we only store the API key for Google 
Translate.
"""
class TranslatorConfig(object):
	def __init__(self):
		self.doDefaults()
		self.Read()

	def doDefaults(self):
		self.config = {}
		self.config_file_name = idaapi.get_user_idadir() + os.sep + 'translateplugin.conf'

	def Read(self):
		try:
			with open(self.config_file_name, 'rb') as infile:
				self.config = json.load(infile)
		except:
			pass

	def Write(self):
		try:
			with open(self.config_file_name, 'wb') as infile:
				json.dump(self.config, infile)
		except:
			pass

	def getApiKey(self):
		if self.config.has_key('api_key'):
			return self.config['api_key']
		else:
			return None

	def setApiKey(self,new_key):
		self.config['api_key'] = new_key

	def getFallbackEncoding(self):
		if self.config.has_key('fallback_encoding'):
			return self.config['fallback_encoding']
		else:
			return "ascii"

	def setFallbackEncoding(self,encoding):
		self.config['fallback_encoding'] = encoding


plg = None

"""
Main plugin class.
"""
class TranslatePlugin(idaapi.plugin_t):
	flags = 0
	comment = ""
	help = ""
	wanted_name = "Translations"
	wanted_hotkey = "Ctrl-Alt-R"

	def startPlugin(self):
		global plg

		# check to see if we've configured the translator yet.
		tc = TranslatorConfig()
		if tc.getApiKey() == None:
			# show dialog to get API key for Google Translate
			translate_key = AskStr('', 'Enter in an API key for Google Translate')
			if translate_key == 0 or translate_key == '':
				return
			else:
				tc.setApiKey(translate_key)
				tc.Write()

		if not plg:
			plg = TranslateFormClass(tc)

		plg.Show("Translations")

	def init(self):
		global plg
		plg = None

		return idaapi.PLUGIN_OK

	def run(self, arg=0):
		self.startPlugin()

	def term(self):
		pass

def PLUGIN_ENTRY():
	return TranslatePlugin()



repository: Pr0214/Unidbg_FindKey
file: IDAPlugin/functionList.py
description: KeyFinder 是一个Unidbg插件，通过内存检索的方式查找AES的密钥，增强Unidbg分析和还原算法的能力。
wanted_name: getFunctions
comment: getFunctions
created_at: 2021-10-18 06:19:38+00:00
pushed_at: 2021-10-18 06:26:15+00:00
url:
https://github.com/Pr0214/Unidbg_FindKey/blob/e864acd1637f2801b7e02bbdbf542f3a9753b5a5/IDAPlugin/functionList.py

content:
import os
import time

import ida_ida
import ida_nalt
import idaapi
import idautils
from idaapi import plugin_t
from idaapi import PLUGIN_PROC
from idaapi import PLUGIN_OK


def getFunctionList():
    functionlist = ""
    maxAddress = ida_ida.inf_get_max_ea()
    for func in idautils.Functions(0, maxAddress):
        if len(list(idautils.FuncItems(func))) > 50:
            functionName = str(idaapi.ida_funcs.get_func_name(func))
            oneFunction = hex(func) + "!" + functionName + "\t\n"
            functionlist += oneFunction
    return functionlist


# 获取SO文件名和路径
def getSoPathAndName():
    fullpath = ida_nalt.get_input_file_path()
    filepath, filename = os.path.split(fullpath)
    return filepath, filename


class getFunctions(plugin_t):
    flags = PLUGIN_PROC
    comment = "getFunctions"
    help = ""
    wanted_name = "getFunctions"
    wanted_hotkey = ""

    def init(self):
        print("getFunctions(v0.1) plugin has been loaded.")
        return PLUGIN_OK

    def run(self, arg):

        so_path, so_name = getSoPathAndName()
        functionlist = getFunctionList()
        script_name = so_name.split(".")[0] + "_functionlist_" + str(int(time.time())) + ".txt"
        save_path = os.path.join(so_path, script_name)
        with open(save_path, "w", encoding="utf-8") as F:
            F.write(functionlist)
        F.close()
        print(f"location: {save_path}")

    def term(self):
        pass


def PLUGIN_ENTRY():
    return getFunctions()


repository: WanQingGit/IDA7script
file: hexrays_hook.py
description: 常用的比较实用的ida插件脚本
wanted_name: hexrays tools
created_at: 2021-03-21 01:50:41+00:00
pushed_at: 2021-04-03 13:35:03+00:00
url:
https://github.com/WanQingGit/IDA7script/blob/c81b5fcb613201b7f03a75e5865cfbd00c845b5c/hexrays_hook.py

content:
from Qing.common import *
from Qing import action_utils
import idaapi

hexrays_arg = None
hexrays_hk = None
# HL_COLOR = 0xAD8044
HL_COLOR = 0x66ff99
DEF_COLOR = 0xffffffff


def get_item_indexes(line):
    indexes = []
    tag = idaapi.COLOR_ON + chr(idaapi.COLOR_ADDR)
    pos = line.find(tag)
    while pos != -1 and len(line[pos + len(tag):]) >= idaapi.COLOR_ADDR_SIZE:
        item_idx = line[pos + len(tag):pos + len(tag) + idaapi.COLOR_ADDR_SIZE]
        indexes.append(int(item_idx, 16))
        pos = line.find(tag, pos + len(tag) + idaapi.COLOR_ADDR_SIZE)
    return indexes


class HexraysHook(object):

    def __init__(self, sync=True):
        self.sync = sync
        self.actions = []
        self.popactions = []
        self.pseudocode_instances = {}
        self.__handle = self.handle
        self.install()

    def install(self):
        idaapi.install_hexrays_callback(self.__handle)

    def register(self, action):
        idaapi.register_action(
            idaapi.action_desc_t(action.name, action.description, action, action.hotkey)
        )
        if isinstance(action, action_utils.PopupAction):
            self.popactions.append(action)
        else:
            self.actions.append(action)

    def reset_colors(self, idx, ignore_vd=False):
        v = self.pseudocode_instances[idx]
        if v:
            pseudocode, lineno, color, disasm_lines = v
            if not ignore_vd and pseudocode:
                try:
                    if color != HL_COLOR:
                        pseudocode[lineno].bgcolor = color
                    else:
                        pseudocode[lineno].bgcolor = DEF_COLOR
                except Exception as  e:  # wtf
                    print(e)
            for ea, color in disasm_lines:
                if color != HL_COLOR:
                    idaapi.set_item_color(ea, color)
                else:
                    idaapi.set_item_color(ea, DEF_COLOR)
        # pseudocode_instances.pop(idx)
        return

    def reset_all_colors(self, ignore_vd=False):
        pseudocode_instances = self.pseudocode_instances
        # restore colors
        if pseudocode_instances:
            for k, _ in pseudocode_instances.items():
                self.reset_colors(k, ignore_vd)
            pseudocode_instances.clear()
        return

    def apply_colors(self, vd, result):
        self.reset_all_colors()
        if not result:
            return
        pseudocode, lineno, col, item_ea_list = result
        disasm_lines = [(ea, idaapi.get_item_color(ea)) for ea in item_ea_list]
        if len(item_ea_list):
            idaapi.jumpto(item_ea_list[0], -1, idaapi.UIJMP_IDAVIEW | idaapi.UIJMP_DONTPUSH)
        self.pseudocode_instances[vd.view_idx] = (pseudocode, lineno, col, disasm_lines)

        if pseudocode:
            try:
                pseudocode[lineno].bgcolor = HL_COLOR
            except Exception as err:  # wtf
                print(err)
        for ea, _ in disasm_lines:
            idaapi.set_item_color(ea, HL_COLOR)

    def uninstall(self):
        self.reset_all_colors()
        idaapi.remove_hexrays_callback(self.__handle)
        for action in self.actions:
            idaapi.unregister_action(action.name)
        for action in self.popactions:
            idaapi.unregister_action(action.name)
        self.actions = []
        self.popactions = []

    def handle(self, event, *args):
        _dbg.info("hexrays callback ", event, args)
        global hexrays_arg
        hexrays_arg = args
        _dbg()
        if event == idaapi.hxe_populating_popup:
            form, popup, hx_view = args
            for action in self.actions:
                idaapi.attach_action_to_popup(form, popup, action.name, None)
            for action in self.popactions:
                if action.check(hx_view):
                    idaapi.attach_action_to_popup(form, popup, action.name, None)
            return 0
        elif event != idaapi.hxe_double_click:  # 106,idaapi.hxe_right_click 105
            return 0
        if not self.sync:
            return 0
        vd = args[0]
        try:
            lineno = vd.cpos.lnnum
            pseudocode = vd.cfunc.get_pseudocode()
            if pseudocode and lineno != -1:
                color = pseudocode[lineno].bgcolor
                view_idx = vd.view_idx
                if color == HL_COLOR and view_idx in self.pseudocode_instances:
                    self.reset_colors(view_idx)
                    self.pseudocode_instances.pop(view_idx)
                    return 0
                line = pseudocode[lineno].line
                item_idxs = get_item_indexes(line)
                ea_list = set()
                for i in item_idxs:
                    try:
                        item = vd.cfunc.treeitems.at(i)
                        if item and item.ea != idaapi.BADADDR:
                            ea_list.add(item.ea)
                            # ea_list[item.ea] = None
                    except:
                        pass
                self.apply_colors(vd, (pseudocode, lineno, color, sorted(ea_list)))
        except Exception as err:
            self.reset_all_colors()
            print(err)
        return 0


_dbg = Debugger(trace=False, level=DBG_WARN)
_dbg.on()
_dbg()


# -----------------------------------------------------------------------
class HexraysPlugin(idaapi.plugin_t):
    comment = ''
    help = ''
    flags = idaapi.PLUGIN_MOD
    wanted_name = 'hexrays tools'
    wanted_hotkey = 'Ctrl-Shift-S'
    hxehook = None

    def init(self):
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        if not HexraysPlugin.hxehook:
            HexraysPlugin.hxehook = HexraysHook()
            HexraysPlugin.hxehook.register(action_utils.TestAction())
            HexraysPlugin.hxehook.register(action_utils.StructRename())
        else:
            HexraysPlugin.hxehook.uninstall()
            HexraysPlugin.hxehook = None
        idaapi.msg("[+] %s is %sabled now.\n" % (HexraysPlugin.wanted_name, "en" if HexraysPlugin.hxehook else "dis"))

    def term(self):
        idaapi.msg("[+] %s unloaded.\n" % HexraysPlugin.wanted_name)
        hexhook = HexraysPlugin.hxehook
        if hexhook:
            hexhook.uninstall()
            HexraysPlugin.hxehook = None


# -----------------------------------------------------------------------
def PLUGIN_ENTRY():
    return HexraysPlugin()


_dbg()
if "SCRIPT_LOAD" in dir():
    try:
        if hexrays_hk:
            hexrays_hk.uninstall()
            print("remove previous hexrays_hk")
        reload(action_utils)
        raise Exception("Init hook...")
    except:
        hexrays_hk = HexraysHook(sync=True)
        hexrays_hk.register(action_utils.TestAction())
        hexrays_hk.register(action_utils.StructRename())

# idaapi.install_hexrays_callback(hexrays_hk)
# cfunc.arguments
# vars.size()
# hx_view.rename_lvar(lvar, name, True)
# v.tif.dstr()


