# plugins

repository: hugsy/stuff
file: ida_scripts/WinIoCtlDecoder.py
description: Unsorted, raw, ugly & probably poorly usable tools for reversing, exploit and pentest
wanted_name: Windows IOCTL code decoder
created_at: 2014-05-01 00:10:53+00:00
pushed_at: 2024-08-28 20:11:06+00:00
url:
https://github.com/hugsy/stuff/blob/0530b13a65fc5c7028ebf2c9a9a42b64f7be1fc7/ida_scripts/WinIoCtlDecoder.py

content:
#!/usr/bin/python
"""Decodes a given 32-Bit Windows Device I/O control code

Author: Satoshi Tanda (adjusted for IDA 7.4 support)

Source:
    https://github.com/tandasat/WinIoCtlDecoder/blob/master/plugins/WinIoCtlDecoder.py

Description:
    Decodes Windows Device I/O control code into DeviceType, FunctionCode,
    AccessType and MethodType.

Usage:
    1. Select an interesting IOCTL code in the disassemble window.
    2. Hit Ctrl-Alt-D or select Edit/Plugins/Windows IOCTL code decoder
    or
    Call winio_decode function directly from the Python CLI window.
        Python>windows_ioctl_decode(0x220086)


Example:
    Python>windows_ioctl_decode(0x222057)
    '#define Ioctl_0x00222057 CTL_CODE("FILE_DEVICE_UNKNOWN", 0x815, METHOD_NEITHER, FILE_ANY_ACCESS)'
"""

import sys
import idc
import idaapi

import ida_idaapi
import ida_bytes


def windows_ioctl_decode(ioctl_code):
    """Decodes IOCTL code and print it."""
    access_names = [
        'FILE_ANY_ACCESS',
        'FILE_READ_ACCESS',
        'FILE_WRITE_ACCESS',
        'FILE_READ_ACCESS | FILE_WRITE_ACCESS',
    ]
    method_names = [
        'METHOD_BUFFERED',
        'METHOD_IN_DIRECT',
        'METHOD_OUT_DIRECT',
        'METHOD_NEITHER',
    ]
    device_name_unknown = '<UNKNOWN>'
    device_names = [
        device_name_unknown,                # 0x00000000
        'FILE_DEVICE_BEEP',                 # 0x00000001
        'FILE_DEVICE_CD_ROM',               # 0x00000002
        'FILE_DEVICE_CD_ROM_FILE_SYSTEM',   # 0x00000003
        'FILE_DEVICE_CONTROLLER',           # 0x00000004
        'FILE_DEVICE_DATALINK',             # 0x00000005
        'FILE_DEVICE_DFS',                  # 0x00000006
        'FILE_DEVICE_DISK',                 # 0x00000007
        'FILE_DEVICE_DISK_FILE_SYSTEM',     # 0x00000008
        'FILE_DEVICE_FILE_SYSTEM',          # 0x00000009
        'FILE_DEVICE_INPORT_PORT',          # 0x0000000a
        'FILE_DEVICE_KEYBOARD',             # 0x0000000b
        'FILE_DEVICE_MAILSLOT',             # 0x0000000c
        'FILE_DEVICE_MIDI_IN',              # 0x0000000d
        'FILE_DEVICE_MIDI_OUT',             # 0x0000000e
        'FILE_DEVICE_MOUSE',                # 0x0000000f
        'FILE_DEVICE_MULTI_UNC_PROVIDER',   # 0x00000010
        'FILE_DEVICE_NAMED_PIPE',           # 0x00000011
        'FILE_DEVICE_NETWORK',              # 0x00000012
        'FILE_DEVICE_NETWORK_BROWSER',      # 0x00000013
        'FILE_DEVICE_NETWORK_FILE_SYSTEM',  # 0x00000014
        'FILE_DEVICE_NULL',                 # 0x00000015
        'FILE_DEVICE_PARALLEL_PORT',        # 0x00000016
        'FILE_DEVICE_PHYSICAL_NETCARD',     # 0x00000017
        'FILE_DEVICE_PRINTER',              # 0x00000018
        'FILE_DEVICE_SCANNER',              # 0x00000019
        'FILE_DEVICE_SERIAL_MOUSE_PORT',    # 0x0000001a
        'FILE_DEVICE_SERIAL_PORT',          # 0x0000001b
        'FILE_DEVICE_SCREEN',               # 0x0000001c
        'FILE_DEVICE_SOUND',                # 0x0000001d
        'FILE_DEVICE_STREAMS',              # 0x0000001e
        'FILE_DEVICE_TAPE',                 # 0x0000001f
        'FILE_DEVICE_TAPE_FILE_SYSTEM',     # 0x00000020
        'FILE_DEVICE_TRANSPORT',            # 0x00000021
        'FILE_DEVICE_UNKNOWN',              # 0x00000022
        'FILE_DEVICE_VIDEO',                # 0x00000023
        'FILE_DEVICE_VIRTUAL_DISK',         # 0x00000024
        'FILE_DEVICE_WAVE_IN',              # 0x00000025
        'FILE_DEVICE_WAVE_OUT',             # 0x00000026
        'FILE_DEVICE_8042_PORT',            # 0x00000027
        'FILE_DEVICE_NETWORK_REDIRECTOR',   # 0x00000028
        'FILE_DEVICE_BATTERY',              # 0x00000029
        'FILE_DEVICE_BUS_EXTENDER',         # 0x0000002a
        'FILE_DEVICE_MODEM',                # 0x0000002b
        'FILE_DEVICE_VDM',                  # 0x0000002c
        'FILE_DEVICE_MASS_STORAGE',         # 0x0000002d
        'FILE_DEVICE_SMB',                  # 0x0000002e
        'FILE_DEVICE_KS',                   # 0x0000002f
        'FILE_DEVICE_CHANGER',              # 0x00000030
        'FILE_DEVICE_SMARTCARD',            # 0x00000031
        'FILE_DEVICE_ACPI',                 # 0x00000032
        'FILE_DEVICE_DVD',                  # 0x00000033
        'FILE_DEVICE_FULLSCREEN_VIDEO',     # 0x00000034
        'FILE_DEVICE_DFS_FILE_SYSTEM',      # 0x00000035
        'FILE_DEVICE_DFS_VOLUME',           # 0x00000036
        'FILE_DEVICE_SERENUM',              # 0x00000037
        'FILE_DEVICE_TERMSRV',              # 0x00000038
        'FILE_DEVICE_KSEC',                 # 0x00000039
        'FILE_DEVICE_FIPS',                 # 0x0000003A
        'FILE_DEVICE_INFINIBAND',           # 0x0000003B
        device_name_unknown,                # 0x0000003C
        device_name_unknown,                # 0x0000003D
        'FILE_DEVICE_VMBUS',                # 0x0000003E
        'FILE_DEVICE_CRYPT_PROVIDER',       # 0x0000003F
        'FILE_DEVICE_WPD',                  # 0x00000040
        'FILE_DEVICE_BLUETOOTH',            # 0x00000041
        'FILE_DEVICE_MT_COMPOSITE',         # 0x00000042
        'FILE_DEVICE_MT_TRANSPORT',         # 0x00000043
        'FILE_DEVICE_BIOMETRIC',            # 0x00000044
        'FILE_DEVICE_PMI',                  # 0x00000045
    ]
    device_names2 = [
        {'name': 'MOUNTMGRCONTROLTYPE', 'code': 0x0000006d},
    ]

    device = (ioctl_code >> 16) & 0xffff
    access = (ioctl_code >> 14) & 3
    function = (ioctl_code >> 2) & 0xfff
    method = ioctl_code & 3

    if device >= len(device_names):
        device_name = device_name_unknown
        for dev in device_names2:
            if device == dev['code']:
                device_name = dev['name']
                break
    else:
        device_name = device_names[device]

    # print ('winio_decode(0x%08X)' % (ioctl_code))
    # print ('Device   : %s (0x%X)' % (device_name, device))
    # print ('Function : 0x%X' % (function))
    # print ('Method   : %s (%d)' % (method_names[method], method))
    # print ('Access   : %s (%d)' % (access_names[access], access))
    return '#define Ioctl_0x{0:08x} CTL_CODE("{1:s}", {2:#x}, {3:s}, {4:s})'.format(
        ioctl_code,
        device_name,
        function,
        method_names[method],
        access_names[access]
    )



class IoctlDecodePlugin_t(ida_idaapi.plugin_t):
    """Class for IDA Pro plugin."""
    flags = idaapi.PLUGIN_UNL
    comment = ('Decodes Windows Device I/O control code into DeviceType, FunctionCode, AccessType and MethodType.')
    help = 'Quickly parse Windows IOCTL codes'
    wanted_name = 'Windows IOCTL code decoder'
    wanted_hotkey = 'Ctrl-Alt-D'

    def __init__(self):
        #raise Exception("foo")
        return

    def init(self):
        raise Exception("foo")
        print("init")
        return idaapi.PLUGIN_OK


    def run(self, arg=0):
        print("running")
        ea = idc.get_screen_ea()
        if idc.GetOpType(ea, 1) != 5:   # Immediate
            return
        value = idc.GetOperandValue(ea, 1) & 0xffffffff
        ida_bytes.set_cmt(ea, windows_ioctl_decode(value), 1)
        return


    def term(self):
        pass


def PLUGIN_ENTRY():
    return IoctlDecodePlugin_t()


if __name__ == "__main__":
    PLUGIN_ENTRY()


repository: jonpalmisc/ida_hexi
file: hexi.py
description: Hex-Rays decompiler inspector
url: https://github.com/jonpalmisc/ida_hexi/blob/860f2c40d6374b524f786a5d8422b0b9f683020c/hexi.py
wanted_name: hexi
comment: Hex-Rays decompiler inspector
created_at: 2024-05-09 21:18:08+00:00
pushed_at: 2024-05-10 14:15:08+00:00
content:
from hexi_core import dumper, grapher

import ida_idaapi
import ida_kernwin
import ida_hexrays

from typing import Optional


def get_pseudo_tree(
    ctx: ida_kernwin.action_ctx_base_t, use_subtree: bool
) -> Optional[ida_hexrays.citem_t]:
    """
    Get the pseudocode tree for an entire function or a subtree depending on
    user preferences and the current UI context.
    """

    if ctx.widget_type != ida_kernwin.BWN_PSEUDOCODE:
        return None
    if not (vdui := ida_hexrays.get_widget_vdui(ctx.widget)):
        return None

    # Return entire tree if a subtree was not requested.
    if not use_subtree:
        return vdui.cfunc.body

    # Try to determine the currently-focused subtree.
    vdui.get_current_item(ida_hexrays.USE_KEYBOARD)
    if vdui.item.is_citem():
        return vdui.item.e

    return None


class dump_tree_handler_t(ida_kernwin.action_handler_t):
    use_subtree: bool

    def __init__(self, use_subtree: bool = False):
        ida_kernwin.action_handler_t.__init__(self)
        self.use_subtree = use_subtree

    def activate(self, ctx):  # pyright: ignore
        if tree := get_pseudo_tree(ctx, self.use_subtree):
            dumper.print_tree(tree)

        return 1

    def update(self, ctx):
        return (
            ida_kernwin.AST_ENABLE
            if get_pseudo_tree(ctx, self.use_subtree)
            else ida_kernwin.AST_DISABLE
        )


class view_tree_handler_t(ida_kernwin.action_handler_t):
    use_subtree: bool

    def __init__(self, use_subtree: bool = False):
        ida_kernwin.action_handler_t.__init__(self)
        self.use_subtree = use_subtree

    def activate(self, ctx):  # pyright: ignore
        if tree := get_pseudo_tree(ctx, self.use_subtree):
            grapher.show_tree(tree)

        return 1

    def update(self, ctx):
        return (
            ida_kernwin.AST_ENABLE
            if get_pseudo_tree(ctx, self.use_subtree)
            else ida_kernwin.AST_DISABLE
        )


##===----------------------------------------------------------------------===##


class hexi_ui_hooks_t(ida_kernwin.UI_Hooks):
    popup_actions = []

    def finish_populating_widget_popup(self, widget, popup):  # pyright: ignore
        if ida_kernwin.get_widget_type(widget) != ida_kernwin.BWN_PSEUDOCODE:
            return

        for action_id, action_name in self.popup_actions:
            ida_kernwin.attach_action_to_popup(
                widget,
                popup,
                action_id,
                f"&Hexi/{action_name}",
                ida_kernwin.SETMENU_APP,
            )


class hexi_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_DRAW | ida_idaapi.PLUGIN_HIDE
    help = ""
    comment = "Hex-Rays decompiler inspector"
    wanted_name = "hexi"
    wanted_hotkey = ""

    ui_hooks: hexi_ui_hooks_t

    def register_action(
        self,
        id: str,
        name: str,
        help: str,
        handler,
    ):
        """Register an action and add it to the popup menu all at once."""

        ida_kernwin.register_action(
            ida_kernwin.action_desc_t(id, name, handler, None, help)
        )
        self.ui_hooks.popup_actions.append((id, name))

    def init(self):
        self.ui_hooks = hexi_ui_hooks_t()

        self.register_action(
            "hexi:ViewPseudoTree",
            "View pseudocode ~t~ree",
            "View the pseudocode tree as a graph",
            view_tree_handler_t(),
        )
        self.register_action(
            "hexi:ViewPseudoSubtree",
            "View pseudocode ~s~ubtree",
            "View the selected pseudocode subtree as a graph",
            view_tree_handler_t(use_subtree=True),
        )
        self.register_action(
            "hexi:DumpPseudoTree",
            "Dump pseudocode tree",
            "Dump the pseudocode tree to the output window",
            dump_tree_handler_t(),
        )
        self.register_action(
            "hexi:DumpPseudoSubtree",
            "Dump pseudocode subtree",
            "Dump the selected pseudocode subtree to the output window",
            dump_tree_handler_t(use_subtree=True),
        )

        self.ui_hooks.hook()
        return ida_idaapi.PLUGIN_KEEP

    def run(self):  # pyright: ignore
        print("Plugin cannot be run as a script!")

    def term(self):
        self.ui_hooks.unhook()


def PLUGIN_ENTRY():
    return hexi_plugin_t()


repository: clovme/WTools
file: software/Reverse Cracking/IDA Professional 9.0/python/examples/cvt64/py_cvt64_sample.py
wanted_name: IDAPython: CVT64 sample
comment: IDAPython: An example how to implement CVT64 functionality
created_at: 2025-03-27 00:14:54+00:00
pushed_at: 2025-05-11 10:18:21+00:00
url:
https://github.com/clovme/WTools/blob/f12f01c833a6a6241799a9d01e9117cc28a65dbc/software/Reverse%20Cracking/IDA%20Professional%209.0/python/examples/cvt64/py_cvt64_sample.py

content:
"""
summary: This file contains the CVT64 examples.

description:
  For more infortmation see SDK/plugins/cvt64_sample example
"""

import idaapi
import ida_idaapi
import ida_netnode

SAMPLE_NETNODE_NAME = "$ cvt64 py_sample netnode"
DEVICE_INDEX   = idaapi.BADADDR     # -1
IDPFLAGS_INDEX = idaapi.BADADDR     # -1
HASH_COMMENT   = "Comment"
HASH_ADDRESS   = "Address"

#--------------------------------------------------------------------------
class idp_listener_t(idaapi.IDP_Hooks):
    def __init__(self):
        idaapi.IDP_Hooks.__init__(self)

    def ev_cvt64_hashval(self, node, tag, name, data):
        helper = idaapi.netnode(SAMPLE_NETNODE_NAME)
        if helper == node and tag == ida_netnode.htag:
            if name == HASH_COMMENT:
                comment = helper.hashstr(name)
                helper.hashset_buf(name, comment)
                return 1
            if name == HASH_ADDRESS:
                address = helper.hashval_long(name)
                if address == ida_idaapi.BADADDR32: 
                    address = ida_idaapi.BADADDR
                    helper.hashset_idx(name, address)
                return 1
        return 0

    def ev_cvt64_supval(self, node, tag, idx, data):
        helper = idaapi.netnode(SAMPLE_NETNODE_NAME)
        if helper == node:
            if tag == ida_netnode.stag and idx == ida_idaapi.BADADDR32: 
                helper.supset(DEVICE_INDEX, data)
                return 1
            if tag == ida_netnode.atag and len(data):
                if idx == ida_idaapi.BADADDR32: 
                    idx = IDPFLAGS_INDEX
                val = int.from_bytes(data, 'little')
                if val == ida_idaapi.BADADDR32: 
                    val = ida_idaapi.BADADDR
                helper.altset(idx, val)
                return 1
        return 0

#--------------------------------------------------------------------------
# This class is instantiated once per each opened database.
class cvt64_ctx_t(idaapi.plugmod_t):

    def __init__(self):
        self.prochook = idp_listener_t()
        self.prochook.hook()

    def __del__(self):
        self.prochook.unhook()

    def run(self, arg):
        pass

#--------------------------------------------------------------------------
# This class is instantiated when IDA loads the plugin.
class cvt64_sample_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_MULTI | idaapi.PLUGIN_MOD
    comment = "IDAPython: An example how to implement CVT64 functionality"
    wanted_name = "IDAPython: CVT64 sample"
    wanted_hotkey = ""
    help = ""

    def init(self):
        return cvt64_ctx_t()

#--------------------------------------------------------------------------
def PLUGIN_ENTRY():
    return cvt64_sample_t()


repository: keowu/swiftstringinspector
file: swift_string_inspector.py
wanted_name: Swift String Inspector
comment: A simple plugin to deal with strings, optimized strings and arrays while reversing Swift
created_at: 2025-03-26 13:51:06+00:00
pushed_at: 2025-03-26 14:12:53+00:00
description:
A simple plugin for working with Swift Strings, optimized Swift Strings, and Swift Arrays during the reverse engineering of iOS binaries in Hex-Rays IDA.

url:
https://github.com/keowu/swiftstringinspector/blob/27d7af279d2fac9cfef70c645918e51c374f8185/swift_string_inspector.py

content:
# (C) @Keowu - github.com/keowu/swiftstringinspector - 2025
import idaapi
import ida_kernwin
import idautils
import ida_ida
import struct
import idc
from PyQt5 import QtWidgets

# IDA CONSTANTS SUCKS. please Ifak do something.
NN_adr = 81
NN_mov = 80
NN_sub = 12
NM_x8 = 137
o_mem = 5
o_reg = 1
dt_qword = 7
# CONSTANTS
OFFSET_CONSTANT = 0x20
ADDRESSING_MASK = 0x8000000000000000
# DEBUG MODE
DEBUG_ENABLED = False

class SwiftInspectorChoose(idaapi.Choose):
    def __init__(self, title, n=12, flags=0, embedded=False, width=None, height=None, items_arg=None):
        super().__init__(title, [["Address", 10], ["Array Address", 30], ["Info", 40]],
                         flags=flags | idaapi.Choose.CH_RESTORE,
                         embedded=embedded, width=width, height=height)

        self.data = items_arg if items_arg is not None else []

    def OnGetLine(self, n):
        return self.data[n]

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

    def OnSelectLine(self, sel):
        idaapi.jumpto(int(self.data[sel][0], 16))

class SwiftStringInspectorForm(ida_kernwin.PluginForm):
    def OnCreate(self, form):
        self.parent = self.FormToPyQtWidget(form)
        self.init_ui()

    def init_ui(self):
        layout = QtWidgets.QVBoxLayout()
        self.btnInspectSwiftString = QtWidgets.QPushButton("Inspect Swift::String")
        self.btnInspectSwiftArray = QtWidgets.QPushButton("Inspect Swift::String Array")
        self.btnInspectOptSwiftString = QtWidgets.QPushButton("Inspect Optimized Swift::String")
        self.btnAbout = QtWidgets.QPushButton("About")
        self.btnInspectSwiftString.clicked.connect(self.on_swift_string_inspector_click)
        self.btnInspectSwiftArray.clicked.connect(self.on_swift_string_array_inspector_click)
        self.btnInspectOptSwiftString.clicked.connect(self.on_opt_swift_string_click)
        self.btnAbout.clicked.connect(self.on_about)
        layout.addWidget(self.btnInspectSwiftString)
        layout.addWidget(self.btnInspectSwiftArray)
        layout.addWidget(self.btnInspectOptSwiftString)
        layout.addWidget(self.btnAbout)

        central_widget = QtWidgets.QWidget()
        central_widget.setLayout(layout)
        main_layout = QtWidgets.QVBoxLayout()
        main_layout.addWidget(central_widget)
        self.parent.setLayout(main_layout)

    def render_swift_chooser(self, items):
        SwiftInspectorChoose("Swift String Inspector", n=len(items), items_arg=items).Show(modal=True)

    def on_about(self):
        idaapi.info("Swift String Inspector was developer by @Keowu(www.github.com/keowu/swiftstringinspector)")

    def on_swift_string_inspector_click(self):
        self.render_swift_chooser(self.inspect_all_swift_strings())
        
    def on_swift_string_array_inspector_click(self):
        self.render_swift_chooser(self.inspect_all_swift_array_strings())
        
    def on_opt_swift_string_click(self):
        self.render_swift_chooser(self.inspect_all_swift_optn_swift_strings())
        
    def inspect_all_swift_strings(self):
        ea = ida_ida.inf_get_min_ea()
        end_ea = ida_ida.inf_get_max_ea()
        
        items = []

        while ea < end_ea:
            inst = idautils.DecodeInstruction(ea)
    
            if not inst:
                ea += 4 # Instructin size default lá do arm64
                continue

            # ADRL X8, some_ptr
            if inst.itype == NN_adr and \
                inst[0].reg == NM_x8 and \
                inst[1].type == idaapi.o_imm:
        
                inst2 = idautils.DecodeInstruction(ea + inst.size)
        
                # SUB X8, X8, OFFSET_CONSTANT
                if inst2.itype == NN_sub and \
                    inst2[0].reg == NM_x8 and \
                    inst2[2].value == OFFSET_CONSTANT:
                    string_bytes = idc.get_strlit_contents(inst[1].value, -1, idc.STRTYPE_C)
                    if string_bytes:
                        if DEBUG_ENABLED: print(f"(Swift::String) - 0x{ea:x} - 0x{inst[1].value - OFFSET_CONSTANT:x} - {string_bytes.decode()}")
                        items.append([f'0x{ea:x}', f'0x{inst[1].value:x}', f'{string_bytes.decode()}'])
                
            ea += inst.size
            
        return items
        
    def inspect_all_swift_array_strings(self):
        ea = ida_ida.inf_get_min_ea()
        end_ea = ida_ida.inf_get_max_ea()
        
        items = []
    
        while ea < end_ea:
        
            inst = idautils.DecodeInstruction(ea)
        
            if not inst:
                ea += 4
                continue
            
            # ADRL some_reg, some_off
            if inst.itype == NN_adr and inst[0].type == o_reg and inst[1].type == o_mem:
                value_swift = struct.unpack('<Q', idaapi.get_bytes(inst[1].value, 8))[0]
                # All array references use the same mask!
                if (value_swift & ADDRESSING_MASK) == ADDRESSING_MASK:
                    value_swift -= ADDRESSING_MASK
                    if (value_swift & 0xFF00000000000000) == 0: # The result needs to be always zero for be a valid swift array
                        if DEBUG_ENABLED: print(f"(Swift::String_SwiftArrayStorage_HEAD) - 0x{ea:x} -> p:[0x{value_swift:x}]")
                        items.append([f'0x{ea:x}', f'0x{value_swift:x}', 'Swift::String->SwiftArrayStorage->HEAD'])
            
            ea += inst.size
        
        return items

    def inspect_all_swift_optn_swift_strings(self):
        ea = ida_ida.inf_get_min_ea()
        end_ea = ida_ida.inf_get_max_ea()
    
        items = []
    
        while ea < end_ea:
        
            inst = idautils.DecodeInstruction(ea)
        
            if not inst:
                ea += 4
                continue
            
            # MOV X1, 0xIMM_with_8BYTES
            if inst.itype == NN_mov and inst[0].dtype == dt_qword and inst[1].dtype == dt_qword:
                big_endian = inst[1].value
                num_bytes = (big_endian.bit_length() + 7) // 8
                value_bytes = big_endian.to_bytes(num_bytes, byteorder='big')
                little_endian_value = int.from_bytes(value_bytes[::-1], byteorder='big')
                
                ascii_count = 0
                for i in range(num_bytes): 
                    if 0x00 <= (little_endian_value >> (8 * (num_bytes - i - 1))) & 0xff <= 0x7F: ascii_count += 1
            
                if ascii_count >= 4:
                    result_string = little_endian_value.to_bytes((little_endian_value.bit_length() + 7) // 8, byteorder='big').decode('ascii', errors='replace')
                    if DEBUG_ENABLED: print(f"IMM Optimized Swift::String: 0x{ea:x} 0x{inst[1].value:x} - 0x{little_endian_value:x} - {result_string}")
                    items.append([f'0x{ea:x}', f'0x{inst[1].value:x}', f'{result_string}'])
            
            ea += inst.size
            
        return items

    def Show(self):
        return ida_kernwin.PluginForm.Show(self, "Swift String Inspector", options=ida_kernwin.PluginForm.WOPN_PERSIST)

class SwiftInspectorPlugin(idaapi.plugmod_t):
    def __init__(self):
        super().__init__()
        self.form = None

    def run(self, arg):
        if self.form is None:
            self.form = SwiftStringInspectorForm()
            self.form.Show()

        return 0

class PluginEntry(idaapi.plugin_t):
    flags = idaapi.PLUGIN_MULTI
    comment = "A simple plugin to deal with strings, optimized strings and arrays while reversing Swift"
    wanted_name = "Swift String Inspector"
    wanted_hotkey = "Ctrl+0"

    def init(self):
        return SwiftInspectorPlugin()

def PLUGIN_ENTRY():
    return PluginEntry()


repository: nihilus/tree-cbass
file: SourceCode/trunk/TREE/TREE_Analyzer.py
wanted_name: TREE Analyzer
created_at: 2014-08-31 10:22:54+00:00
pushed_at: 2014-08-31 11:00:11+00:00
url:
https://github.com/nihilus/tree-cbass/blob/139406a025ca74cd8e4d78638ddc26c71faef402/SourceCode/trunk/TREE/TREE_Analyzer.py

content:
#!/usr/bin/python
############################
#
#
############################
import os
import sys
import time

import idc
import idaapi
import idautils
from idaapi import PluginForm, plugin_t
from PySide import QtGui
from PySide.QtGui import QIcon

from dispatcher.widgets.AnalyzerWidget import AnalyzerWidget
from dispatcher.widgets.VisualizerWidget import VisualizerWidget

HOTKEYS = None
DISPATCHER = None
NAME = "TREE Analyzer v0.2"

class DispatcherForm(PluginForm):
    """
    This class contains the main window of TaintVisualizer QT Graph
    Setup of core modules and widgets will be performed in here
    """
    
    def __init__(self):
        super(DispatcherForm, self).__init__()
        global HOTKEYS
        HOTKEYS = []
        self.dispatcher_widgets = []
        self.idaPluginDir = os.path.join(GetIdaDirectory(),"plugins")
        
        self.iconPath = os.path.join(self.idaPluginDir ,"dispatcher","icons")
        path = os.path.join(self.iconPath ,"dispatcher.png")
        self.icon = QIcon(path)

    def setupWidgets(self):
        """
        Setup dispatcher widgets.
        """
        time_before = time.time()
        print ("[/] setting up widgets...")
        self.dispatcher_widgets.append(AnalyzerWidget(self))
        self.dispatcher_widgets.append(VisualizerWidget(self))
        self.setupDispatcherForm()
        print("[\\] this took %3.2f seconds.\n" % (time.time() - time_before))
        
    def setupDispatcherForm(self):
        """
        Organize the initialized widgets into tabs
        """
        self.tabs = QtGui.QTabWidget()
        self.tabs.setTabsClosable(False)
        for widget in self.dispatcher_widgets:
            self.tabs.addTab(widget, widget.icon, widget.name)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.tabs)
        self.parent.setLayout(layout)
        
    def OnCreate(self, form):
        """
        When creating the form, setup the modules and widgets
        """
        self.printBanner()
        self.parent = self.FormToPySideWidget(form)
        self.parent.setWindowIcon(self.icon)
        self.setupWidgets()
        
    def printBanner(self):
        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 Analyzer.QT")
        
    def OnClose(self, form):
        """
        Perform cleanup.
        """
        global DISPATCHER
        del DISPATCHER
        
    def Show(self):
        if idc.GetInputMD5() == None:
            return
        else:
            return PluginForm.Show(self,
                NAME,
                options=(PluginForm.FORM_CLOSE_LATER | PluginForm.FORM_RESTORE | PluginForm.FORM_SAVE))
    
 #########################################################
 # functionality for widgets
 #########################################################
 
    def setTabFocus(self, widget_name):
        """
        Can be used by Dispatcher widgets to set focus to a widget, identified by name.
        @param widget_name: A widget name
        @type widget_name: STR
        """
        for widget in self.dispatcher_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):
        """
        Used by widgets to register hotkeys.
        Global list of HOTKEYS of function pointers
        Functions cannot take parameters atm
        @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 = "TREE_Analyzer_HOTKEY_%d" % hotkey_index
        HOTKEYS.append(py_function_pointer)
        #self.ida_proxy.CompileLine('static %s() { RunPythonStatement("HOTKEY[%d]()"); }' % (hotkey_name, hotkey_index))
        #self.ida_proxy.Addhotkey(shortcut, hotkey_name)
        
    def passTaintGraph(self, t, widget_name, prop_policy):
        """
        Pass the taintgraph from the analyzer to visualizer
        """
        for widget in self.dispatcher_widgets:
            if widget.name == widget_name:
                widget.setTaintGraph(t, prop_policy)
                
    def passBranchData(self, in_taint_chain, widget_name):
        """
        Pass the taintgraph from the analyzer to visualizer
        """
        for widget in self.dispatcher_widgets:
            if widget.name == widget_name:
                widget.setBranchData(in_taint_chain)
####################################################################
#   Plugin
####################################################################
def PLUGIN_ENTRY():
    return DispatcherPlugin()
    
class DispatcherPlugin(plugin_t):
    """
    Plugin version.
    """
    flags = idaapi.PLUGIN_UNL
    comment = NAME
    help = ""
    wanted_name = "TREE Analyzer"
    wanted_hotkey = "Ctrl-F4"
    
    def init(self):
        self.icon_id = 0
        ExTraces = None
        ExTraces = idaapi.netnode("$ ExTraces", 0, False) #Get the execution trace id
        data = ExTraces.getblob(0, 'A') #Get the execution trace data, use str(data) to convert to data to a str
        if (data is None):
            print "This IDB has no TREE trace. Turn OFF TREE Analyzer"        
            return idaapi.PLUGIN_SKIP
        else:
            print "This IDB has TREE trace in. Turn ON TREE Analyzer!" 
            return idaapi.PLUGIN_OK
        
    def run(self, arg=0):
        f = DispatcherForm()
        f.Show()
        return
        
    def term(self):
        pass
####################################################################
#   Script usage
####################################################################

def main():

    global DISPATCHER
    try:
        DISPATCHER
        DISPATCHER.OnClose(DISPATCHER)
        print ("reloading Dispatcher")
        DISPATCHER = DispatcherForm()
        return
    except Exception:
        DISPATCHER = DispatcherForm()
    """    
    if DISPATCHER.config.dispatcher_plugin_only:
        print "Dispatcher: configured as plugin-only mode, ignoring main function of script. " \
            + "This can be changed in \"cida/config.py\"."
    else:
        DISPATCHER.Show()
    """
    DISPATCHER.Show()
    
if __name__ == "__main__":
    main()


repository: carlosgprado/JARVIS
file: IDAPlugin/jarvis/jarvis.py
description: "Just Another ReVersIng Suite" or whatever other bullshit you can think of
wanted_name: JARVIS
comment: Jarvis Plugin. Your personal IDA butler.
created_at: 2015-05-20 09:08:58+00:00
pushed_at: 2023-04-28 06:02:25+00:00
url:
https://github.com/carlosgprado/JARVIS/blob/29ffc6e92d2e251d1a29ded71f52d634482a2a57/IDAPlugin/jarvis/jarvis.py

content:
#!/usr/bin/python
#
# Name: jarvis.py
# Description: This is the MAIN FILE for IDA's jarvis plugin
#

from idc import *
from idautils import *
from idaapi import *

from PyQt5.QtWidgets import QTabWidget, QVBoxLayout
from PyQt5.QtGui import QIcon

from jarvis.Config import JConfig

from jarvis.core.BinaryAnalysis import BinaryAnalysis
from jarvis.core.VulnDetection import VulnDetection
from jarvis.core.ImportExport import ImportExport
from jarvis.core.Firmware import Firmware

from jarvis.widgets.VulnDetectionWidget import VulnDetectionWidget
from jarvis.widgets.BinaryAnalysisWidget import BinaryAnalysisWidget
from jarvis.widgets.ImportExportWidget import ImportExportWidget
from jarvis.widgets.OptionsWidget import OptionsWidget
from jarvis.widgets.FirmwareWidget import FirmwareWidget

from jarvis.core.helpers.UI import install_ui_hooks

__VERSION__ = 0.8


#################################################################
class JarvisPluginForm(PluginForm):
    """
    Jarvis main window.
    Setup of core modules and widgets is performed in here.
    """

    def __init__(self):
        """
        Initialization. Dough.
        """
        super(JarvisPluginForm, self).__init__()
        self.jarvisWidgets = []
        self.config = JConfig()
        self.icon_path = self.config.icons_path
        self.tabs = None
        self.parent = None

    def showBanner(self):
        """
        Old school is cool
        """
        banner = (
                "=============================================\n"
                "= Jarvis starting...\n\n"
                )

        print banner

    def OnCreate(self, form):
        """
        This is called on form creation (obviously)
        """
        self.showBanner()
        self.parent = self.FormToPyQtWidget(form)
        self.parent.setWindowIcon(QIcon(self.icon_path + 'user-ironman.png'))

        self.setupCore()
        self.setupWidgets()
        self.setupUI()

    def setupCore(self):
        """
        Initializes all internal functionality
        """
        print "* Instantiating core modules...\n"

        self.binary_analysis = BinaryAnalysis()
        self.vuln_detection = VulnDetection()
        self.import_export = ImportExport()
        self.firmware = Firmware()

    def setupWidgets(self):
        """
        Instantiates all widgets
        """
        print "* Creating / Loading individual widgets..."

        # TODO: programmatically load the desired widgets
        # Append to the list every widget you have
        self.jarvisWidgets.append(BinaryAnalysisWidget(self))
        self.jarvisWidgets.append(VulnDetectionWidget(self))
        self.jarvisWidgets.append(ImportExportWidget(self))
        self.jarvisWidgets.append(FirmwareWidget(self))
        self.jarvisWidgets.append(OptionsWidget(self))

        self.setupJarvisForm()

    def setupJarvisForm(self):
        """
        Already initialized widgets are arranged in tabs on the main window.
        """
        self.tabs = QTabWidget()
        self.tabs.setTabsClosable(False)

        for widget in self.jarvisWidgets:
            self.tabs.addTab(widget, widget.icon, widget.name)

        layout = QVBoxLayout()
        layout.addWidget(self.tabs)

        self.parent.setLayout(layout)

    def setupUI(self):
        """
        Manages the IDA UI extensions / modifications.
        NOTE: This uses some GUI functionality introduced
        in IDA 6.7
        """
        install_ui_hooks()

    def Show(self):
        """
        Overload this method to specify form options
        """
        return PluginForm.Show(self,
            ":: JARVIS ::",
            options = (
                PluginForm.FORM_CLOSE_LATER |
                PluginForm.FORM_RESTORE |
                PluginForm.FORM_SAVE
                )
            )

    def OnClose(self, form):
        """
        Perform some cleanup here, if necessary
        """
        print "* JarvisPluginForm closed *"


#################################################################
class JarvisPlugin(plugin_t):
    """
    This is the skeleton plugin as seen by IDA
    """
    flags = 0
    comment = "Jarvis Plugin. Your personal IDA butler."
    help = "It saves time... and headaches."
    wanted_name = "JARVIS"
    wanted_hotkey = "Ctrl-Alt-F8"

    def init(self):
        self.icon_id = 0
        return PLUGIN_KEEP

    def run(self, arg = 0):
        f = JarvisPluginForm()
        f.Show()

    def term(self):
        idaapi.msg("[*] JarvisPlugin terminated")


#################################################################
def PLUGIN_ENTRY():
    """
    Entry point for IDA
    """
    return JarvisPlugin()


#################################################################
# Usage as script (through Alt+F7)
#################################################################
def main():

    global JARVIS

    try:
        # There is an instance, reload it
        JARVIS
        JARVIS.OnClose(JARVIS)
        JARVIS = JarvisPluginForm()

    except:
        # There is no instance yet
        JARVIS = JarvisPluginForm()

    JARVIS.Show()


if __name__ == '__main__':
    main()


repository: Goatman13/ida-psx-gte
file: gte.py
description: IDA Pro plugin that implements disassembly of PlayStation CP2 MIPS instructions
url: https://github.com/Goatman13/ida-psx-gte/blob/431064975e6030ba3612f6c69fd501571ed7f92a/gte.py
wanted_name: PSX GTE CP2 instructions disassembler
created_at: 2024-05-23 20:30:43+00:00
pushed_at: 2024-08-10 09:01:25+00:00
content:
import idaapi
import ida_ida
import ida_allins
import ida_idp
import ida_bytes
import ida_ua

ITYPE_START = ida_idp.CUSTOM_INSN_ITYPE + 0x100
MNEM_WIDTH = 13

class GTE_disassemble(idaapi.IDP_Hooks):

	def __init__(self):
		idaapi.IDP_Hooks.__init__(self)

		class idef:
			def __init__(self, opcode, name, cmt):
				self.opcode = opcode
				self.name = name
				self.cmt = cmt

		self.itable = [
			# Coprocessor Calculation Instructions
			idef(0x01,  "RTPS",  "Perspective Transformation single"),
			idef(0x06,  "NCLIP", "Normal clipping"),
			idef(0x0C,  "OP",    "Outer product of 2 vectors"),
			idef(0x10,  "DPCS",  "Depth Cueing single"),
			idef(0x11,  "INTPL", "Interpolation of a vector and far color vector"),
			idef(0x12 , "MVMVA", "Multiply vector by matrix and add vector (see below)"),
			idef(0x13,  "NCDS",  "Normal color depth cue single vector"),
			idef(0x14,  "CDP",   "Color Depth Que"),
			idef(0x16,  "NCDT",  "Normal color depth cue triple vectors"),
			idef(0x1B,  "NCCS",  "Normal Color Color single vector"),
			idef(0x1C,  "CC",    "Color Color"),
			idef(0x1E,  "NCS",   "Normal color single"),
			idef(0x20,  "NCT",   "Normal color triple"),
			idef(0x28,  "SQR",   "Square of vector IR"),
			idef(0x29,  "DCPL",  "Depth Cue Color light"),
			idef(0x2A,  "DPCT",  "Depth Cueing triple (should be fake=08h, but isn't)"),
			idef(0x2D,  "AVSZ3", "Average of three Z values"),
			idef(0x2E,  "AVSZ4", "Average of four Z values"),
			idef(0x30,  "RTPT",  "Perspective Transformation triple"),
			idef(0x3D,  "GPF",   "General purpose interpolation"),
			idef(0x3E,  "GPL",   "General purpose interpolation with base"),
			idef(0x3F,  "NCCT",  "Normal Color Color triple vector"),
		]

		self.DATA_REG = 0
		self.CTRL_REG = 1

	def ev_ana_insn(self, insn):

		dword = ida_bytes.get_wide_dword(insn.ea)

		if (dword >> 0x19 == 0x25):
			opcode = dword & 0x3F
			for i in range(0, len(self.itable)):
				if (self.itable[i].opcode == opcode):
					insn.itype = ITYPE_START + i
					insn.Op1.type = ida_ua.o_void
					insn.size = 4
					return insn.size

		return 0

	def ev_get_autocmt(self, insn):
		if (insn.itype >= ITYPE_START and insn.itype < ITYPE_START + len(self.itable)):
			print(self.itable[insn.itype-ITYPE_START].cmt)
			return self.itable[insn.itype-ITYPE_START].cmt
		#return 0

	def ev_emu_insn(self, insn):
		if (insn.itype >= ITYPE_START and insn.itype < ITYPE_START + len(self.itable)):
			insn.add_cref(insn.ea + insn.size, 0, 21); # 21 Ordinary flow
			return 1
		return 0

	def get_register(self, op, ctx):

		ctrl_regs = ["RT11RT12", "RT13RT21", "RT22RT23", "RT31RT32", "RT33", "TRX", "TRY", "TRZ",
					"LM11LM12", "LM13LM21", "LM22LM23", "LM31LM32", "LM33", "RBK", "BBK", "GBK",
					"LR1LR2", "LR3LG1", "LG2LG3", "LB1LB2", "LB3", "RFC", "GFC", "BFC",
					"OFX", "OFY", "H", "DQA", "DQB", "ZSF3", "ZSF4", "FLAG"]
		
		data_regs = ["VXY0", "VZ0", "VXY1", "VZ1", "VXY2", "VZ2" ,"RGBC" ,"OTZ" ,"IR0", "IR1",
					"IR2", "IR3", "SXY0", "SXY1", "SXY2", "SXYP", "SZ0", "SZ1", "SZ2", "SZ3",
					"RGB0", "RGB1", "RGB2", "(RES1)", "MAC0", "MAC1", "MAC2",
					"MAC3", "IRGB", "ORGB", "LZCS", "LZCR"]

		
		if (op.specval == self.DATA_REG):
			return data_regs[op.reg].lower()
		elif (op.specval == self.CTRL_REG):
			return ctrl_regs[op.reg].lower()
		else:
			return "UNK"

	def decode_sf_lm(self, dword):

		sf = (dword >> 19) & 1
		lm = (dword >> 10) & 1
		return "sf={:d}, lm={:d}".format(sf, lm)

	def decode_mvmva_bits(self, dword):

		sf = (dword >> 19) & 1
		mm = (dword >> 17) & 3
		mv = (dword >> 15) & 3
		tv = (dword >> 13) & 3
		lm = (dword >> 10) & 1
		return "sf={:d}, mm={:d}, mv={:d}, tv={:d}, lm={:d}".format(sf, mm, mv, tv, lm)

	def ev_out_operand(self, ctx, op):
		if (ctx.insn.itype >= ITYPE_START and ctx.insn.itype < ITYPE_START + len(self.itable) and op.n == 0):

			if (self.itable[ctx.insn.itype - ITYPE_START].name.lower() == "mvmva"):
				s=self.decode_mvmva_bits(ida_bytes.get_wide_dword(ctx.insn.ea))
				ctx.out_line(s, 4)
				return 1
			else:
				s=self.decode_sf_lm(ida_bytes.get_wide_dword(ctx.insn.ea))
				ctx.out_line(s, 4)
				return 1

		elif (op.type == ida_ua.o_idpspec1 and op.reg < 32):

			# First we need to fix instructions (badly) disassembled by mips.dll
			if (ctx.insn.itype == ida_allins.MIPS_cfc2 and op.n == 1):
				op.specval = self.CTRL_REG
				ctx.out_register(self.get_register(op, ctx))
			elif (ctx.insn.itype == ida_allins.MIPS_ctc2 and op.n == 1):
				op.specval = self.CTRL_REG
				ctx.out_register(self.get_register(op, ctx))
			elif (ctx.insn.itype == ida_allins.MIPS_mtc2 and op.n == 1):
				op.specval = self.DATA_REG
				ctx.out_register(self.get_register(op, ctx))
			elif (ctx.insn.itype == ida_allins.MIPS_mfc2 and op.n == 1):
				op.specval = self.DATA_REG
				ctx.out_register(self.get_register(op, ctx))
			elif (ctx.insn.itype == ida_allins.MIPS_lwc2 and op.n == 0):
				op.specval = self.DATA_REG
				ctx.out_register(self.get_register(op, ctx))
			elif (ctx.insn.itype == ida_allins.MIPS_swc2 and op.n == 0):
				op.specval = self.DATA_REG
				ctx.out_register(self.get_register(op, ctx))
			elif (ctx.insn.itype >= ITYPE_START and ctx.insn.itype < ITYPE_START + len(self.itable)):
				ctx.out_register(self.get_register(op, ctx))
			else:
				return 0

			return 1

		return 0

	def ev_out_mnem(self, ctx):
		if (ctx.insn.itype >= ITYPE_START and ctx.insn.itype < ITYPE_START + len(self.itable)):
			
			ctx.out_custom_mnem(self.itable[ctx.insn.itype - ITYPE_START].name.lower(), MNEM_WIDTH, "")
			return 1

		# We do this to fix width of other instructions
		ctx.out_mnem(MNEM_WIDTH)
		return 1

class gte_plugin_t(idaapi.plugin_t):
	flags = idaapi.PLUGIN_UNL
	comment = ""
	help = ""
	wanted_name = "PSX GTE CP2 instructions disassembler"
	wanted_hotkey = ""

	def __init__(self):
		self.gte = None

	def init(self):
		
		if (ida_ida.inf_get_procname() == 'mipsl'):
			self.gte = GTE_disassemble()
			self.gte.hook()
			print("PSX GTE CP2 instructions disassembler loaded.")
			return idaapi.PLUGIN_KEEP

		return idaapi.PLUGIN_SKIP

	def run(self, arg):
		pass

	def term(self):
		if (self.gte != None):
			self.gte.unhook()
			self.gte = None

def PLUGIN_ENTRY():
	return gte_plugin_t()


repository: 10cks/Gemini4IDA
file: Gemini4IDA.py
description: Gemini4IDA: Gemini for IDA disassembly and program analysis
url: https://github.com/10cks/Gemini4IDA/blob/623540a390aecafaf831f289feb330d2973735cb/Gemini4IDA.py
wanted_name: Gemini4IDA
comment: Gemini4IDA comment
created_at: 2023-12-25 01:29:03+00:00
pushed_at: 2023-12-29 09:30:59+00:00
content:
# 可用代码
import idaapi
import ida_hexrays
import idc
import idautils
import os
import google.generativeai as genai 
from datetime import datetime

os.environ['http_proxy'] = 'http://127.0.0.1:7890'
os.environ['https_proxy'] = 'http://127.0.0.1:7890'

  # 填入自己的api_key
API_KEY=''
genai.configure(api_key=API_KEY)

def GeminiAnalysis(inputCode):

    #模型参数 
    generation_config = { 
        "temperature": 0.9, 
        "top_p": 1, 
        "top_k": 1, 
        "max_output_tokens": 2048, 
    } 

    # 威胁程度
    safety_settings = [ 
        {
            "category": "HARM_CATEGORY_HARASSMENT", 
            "threshold": "BLOCK_MEDIUM_AND_ABOVE" 
        }, 
        { 
            "category": "HARM_CATEGORY_HATE_SPEECH", 
            "threshold": "BLOCK_MEDIUM_AND_ABOVE" 
        }, 
        { 
            "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", 
            "threshold": "BLOCK_MEDIUM_AND_ABOVE" 
        }, 
        {
            "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
            "threshold": "BLOCK_ONLY_HIGH"
        },
    ]

    model = genai.GenerativeModel(model_name="gemini-pro", 
                                generation_config=generation_config, 
                                safety_settings=safety_settings)

    identity='''
    Identity: A Study in Binary Security
    Goal: My goal is to help improve the security of software and systems by analyzing and studying binaries to find and resolve security vulnerabilities.
    Specific tasks and responsibilities include:
    Analyzing binaries: I can analyze binaries using a variety of tools and techniques, including disassembly, debugging, and dynamic analysis.
    Identifying security vulnerabilities: When analyzing binaries, I pay special attention to looking for pieces of code that can lead to security vulnerabilities, such as buffer overflows, integer overflows, format string vulnerabilities, etc.
    Identity: A Study in Binary Security
    Goal: My goal is to help improve the security of software and systems by analyzing and studying binaries to find and resolve security vulnerabilities.
    Specific tasks and responsibilities include:
    Analyzing binaries: I can analyze binaries using a variety of tools and techniques, including disassembly, debugging, and dynamic analysis.
    Identifying security vulnerabilities: When analyzing binaries, I pay special attention to looking for pieces of code that can lead to security vulnerabilities, such as buffer overflows, integer overflows, format string vulnerabilities, etc.
    Assessing the severity of the vulnerability: I evaluate the severity of the discovered security vulnerabilities and classify them based on their nature and potential impact.
    Develop exploit methods: I will try to develop exploit methods to prove the existence of a security vulnerability and demonstrate how the vulnerability can be exploited.
    Report security vulnerabilities: I will report the discovered security vulnerabilities to the software or system developers and assist them in fixing the vulnerabilities.
    Participation in security research: I will actively participate in security research and share my research results with other security researchers.
    Provide security advice: I provide security advice to software and system developers to help them improve the security of their software and systems.
    My job is important to software and system security because I can help find and resolve security vulnerabilities that can be exploited by malicious attackers to compromise software and systems. Assessing the severity of the vulnerability: I evaluate the severity of the discovered security vulnerabilities and classify them based on their nature and potential impact.
    Develop exploit methods: I will try to develop exploit methods to prove the existence of a security vulnerability and demonstrate how the vulnerability can be exploited.
    Report security vulnerabilities: I will report the discovered security vulnerabilities to the software or system developers and assist them in fixing the vulnerabilities.
    Participation in security research: I will actively participate in security research and share my research results with other security researchers.
    Provide security advice: I provide security advice to software and system developers to help them improve the security of their software and systems.
    My job is important to software and system security because I can help find and resolve security vulnerabilities that can be exploited by malicious attackers to compromise software and systems.
    '''

    code=inputCode

    #输入提示
    prompt_parts = ["Forget the previous question and go back to the analysis.\n"+ 
                    "Your identity is: \n" + 
                    identity + "\n" + 
                    "Don't use markdown syntax to answer questions. Do two things with the following code: 1. Analyze the code flow. 2.The flow of the code is there a vulnerability? If there are vulnerabilities, where are the possible places. \n"+
                    "Please reply in Chinese:\n" +
                    code]

    #输出回答
    response = model.generate_content(prompt_parts) 
    response_str = str(response.text)
    # print(response.text)
    return response_str

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

    def activate(self, ctx):
        vu = idaapi.get_widget_vdui(ctx.widget)
        cfunc = ida_hexrays.decompile(vu.cfunc.entry_ea)
        entry_ea = cfunc.entry_ea
        tl = ida_hexrays.treeloc_t()
        tl.ea = entry_ea 

        # 获取伪代码
        pseudocode_text = str(cfunc)

        # 收集所有汇编指令
        assembly_instructions = []
        func_items = list(idautils.FuncItems(entry_ea))  # 获取函数中所有指令的地址

        func_name = idc.get_func_name(entry_ea)
        assembly_instructions.append(f"{func_name} proc near")

        for ea in func_items:
            disasm_line = idc.generate_disasm_line(ea, 0)
            if disasm_line:
                assembly_instructions.append("    " + disasm_line)

        assembly_instructions.append(f"{func_name} endp")

        # 将汇编指令列表转换为字符串
        assembly_text = '\n'.join(assembly_instructions)

        # 注释包含伪代码及汇编代码
        # comment_text = f"Pseudo Code:\n{pseudocode_text}\n\nAssembly:\n{assembly_text}"

        # 注释仅包含伪代码
        comment_text = f"Pseudo Code:\n{pseudocode_text}"
        comment = ida_hexrays.citem_cmt_t(GeminiAnalysis(comment_text))
        cfunc.user_cmts[tl] = comment
        cfunc.save_user_cmts()  
        vu.refresh_ctext()
        return 1  

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

class Hooks(idaapi.UI_Hooks):
    def finish_populating_widget_popup(self, widget, popup_handle):
        if idaapi.get_widget_type(widget) == idaapi.BWN_PSEUDOCODE:
            idaapi.attach_action_to_popup(widget, popup_handle, 'Gemini4IDA:Comment', "")

hooks = Hooks()
hooks.hook()

class MyPlugin(idaapi.plugin_t):

    flags = idaapi.PLUGIN_FIX
    comment = "Gemini4IDA comment"
    help = "Gemini4IDA help"
    wanted_name = "Gemini4IDA"
    wanted_hotkey = ""

    def init(self):
        print("Gemini4IDA v1.0 by 10cks/bwner, 2023")
        print("Gemini4IDA's shortcut key is Ctrl-Shift-H")
        if ida_hexrays.init_hexrays_plugin():
            action_desc = idaapi.action_desc_t(
                'Gemini4IDA:Comment', 
                'Gemini4IDA Analysis',
                AddComent(),
                'Ctrl+Shift+G', 
                None,
                -1)
            idaapi.register_action(action_desc)
            idaapi.attach_action_to_menu("Edit/Other/", 'Gemini4IDA:Comment', idaapi.SETMENU_APP)
            return idaapi.PLUGIN_KEEP
        else:
            print("Unable to initialize Hex-Rays plugin.")
            return idaapi.PLUGIN_SKIP

    def run(self, arg):
        AddComent()
        pass

    def term(self):
        idaapi.unregister_action('Gemini4IDA:Comment')
        print("Gemini4IDA: Plugin has terminated")


def PLUGIN_ENTRY():
    return MyPlugin()


repository: twisted-fun/IDARE
file: VulnCandidateFinder.py
description: Automation plugins for various reversing tasks in IDA.
wanted_name: IDARE: Vuln Candidate Finder
created_at: 2021-09-05 07:19:58+00:00
pushed_at: 2021-09-07 20:46:33+00:00
url:
https://github.com/twisted-fun/IDARE/blob/60675316368be4fcb155d9b413803d4a383cb021/VulnCandidateFinder.py

content:
import idc
import idaapi
import idautils
from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import (
    QTreeView,
    QVBoxLayout,
    QLineEdit,
    QMenu,
    QInputDialog,
    QAction,
    QTabWidget,
)


def is_vuln_candidate(function_name, ea):
    if function_name == "system":
        prev_ea = idautils.DecodePreviousInstruction(ea).ea
        disasm = idc.GetDisasm(prev_ea)
        if '; "' not in disasm:
            print(disasm)
            return True

    return False


def get_addr_width():
    return "16" if idaapi.cvar.inf.is_64bit() else "8"


def force_name(ea, new_name):
    if not ea or ea == idaapi.BADADDR:
        return
    if idaapi.IDA_SDK_VERSION >= 700:
        return idaapi.force_name(ea, new_name, idaapi.SN_NOCHECK)
    return idaapi.do_name_anyway(ea, new_name, 0)


class IdaReIDPHooks(idaapi.IDP_Hooks):
    """
    Hooks to keep view updated if some function is updated
    """

    def __init__(self, view, *args):
        super(IdaReIDPHooks, self).__init__(*args)
        self._view = view

    def __on_rename(self, ea, new_name):
        if not self._view:
            return
        items = self._view._model.findItems(
            "0x{:X}".format(ea), QtCore.Qt.MatchRecursive
        )
        if len(items) != 1:
            return

        item = items[0]
        index = self._view._model.indexFromItem(item)
        if not index.isValid():
            return

        name_index = index.sibling(index.row(), 1)
        if not name_index.isValid():
            return

        self._view._model.setData(name_index, new_name)

    def ev_rename(self, ea, new_name):
        """callback for IDA >= 700"""
        self.__on_rename(ea, new_name)
        return super(IdaReIDPHooks, self).ev_rename(ea, new_name)

    def rename(self, ea, new_name):
        """callback for IDA < 700"""
        self.__on_rename(ea, new_name)
        return super(IdaReIDPHooks, self).rename(ea, new_name)


class VulnCandidateFinderView(idaapi.PluginForm):
    ADDR_ROLE = QtCore.Qt.UserRole + 1

    OPT_FORM_PERSIST = (
        idaapi.PluginForm.FORM_PERSIST
        if hasattr(idaapi.PluginForm, "FORM_PERSIST")
        else idaapi.PluginForm.WOPN_PERSIST
    )
    OPT_FORM_NO_CONTEXT = (
        idaapi.PluginForm.FORM_NO_CONTEXT
        if hasattr(idaapi.PluginForm, "FORM_NO_CONTEXT")
        else idaapi.PluginForm.WCLS_NO_CONTEXT
    )

    def __init__(self, data):
        super(VulnCandidateFinderView, self).__init__()
        self._data = data
        self.tv = None
        self._model = None
        self._idp_hooks = None

    def Show(self):
        return idaapi.PluginForm.Show(
            self, "Vuln Candidate Finder", options=self.OPT_FORM_PERSIST
        )

    def _get_parent_widget(self, form):
        return self.FormToPyQtWidget(form)

    def OnCreate(self, form):
        self.parent = self._get_parent_widget(form)

        self._idp_hooks = IdaReIDPHooks(self)
        if not self._idp_hooks.hook():
            print("IDP_Hooks.hook() failed")

        self.tv = QTreeView()
        self.tv.setExpandsOnDoubleClick(False)

        root_layout = QVBoxLayout(self.parent)
        # self.le_filter = QLineEdit(self.parent)

        # root_layout.addWidget(self.le_filter)
        root_layout.addWidget(self.tv)

        self.parent.setLayout(root_layout)

        self._model = QtGui.QStandardItemModel()
        self._init_model()
        self.tv.setModel(self._model)

        self.tv.setColumnWidth(0, 200)
        self.tv.setColumnWidth(1, 300)
        self.tv.header().setStretchLastSection(True)

        self.tv.expandAll()

        self.tv.doubleClicked.connect(self.on_navigate_to_method_requested)
        # self.le_filter.textChanged.connect(self.on_filter_text_changed)
        self.tv.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tv.customContextMenuRequested.connect(
            self._tree_customContextMenuRequesssted
        )

        rename_action = QAction("Rename...", self.tv)
        rename_action.setShortcut("n")
        rename_action.triggered.connect(self._tv_rename_action_triggered)
        self.tv.addAction(rename_action)

        edit_comment_action = QAction("Edit comment...", self.tv)
        edit_comment_action.setShortcut(";")
        edit_comment_action.triggered.connect(self._tv_edit_comment_action_triggered)
        self.tv.addAction(edit_comment_action)

    def _tree_customContextMenuRequesssted(self, pos):
        idx = self.tv.indexAt(pos)
        if not idx.isValid():
            return

        addr = idx.data(role=self.ADDR_ROLE)
        if not addr:
            return
        caller_func = idaapi.get_func(addr)
        if not caller_func:
            return
        addr = caller_func.start_ea

        name_idx = idx.sibling(idx.row(), 1)
        old_name = name_idx.data()

        comment_idx = idx.sibling(idx.row(), 2)

        menu = QMenu()
        rename_action = menu.addAction("Rename `%s`..." % old_name)
        rename_action.setShortcut("n")
        edit_comment_action = menu.addAction("Edit comment")
        edit_comment_action.setShortcut(";")
        action = menu.exec_(self.tv.mapToGlobal(pos))
        if action == rename_action:
            return self._rename_ea_requested(addr, name_idx)
        elif action == edit_comment_action:
            return self._edit_comment_requested(addr, comment_idx)

    def _tv_edit_comment_action_triggered(self):
        selected = self.tv.selectionModel().selectedIndexes()
        if not selected:
            return

        idx = selected[0]
        if not idx.isValid():
            return

        addr = idx.data(role=self.ADDR_ROLE)
        if not addr:
            return

        comment_idx = idx.sibling(idx.row(), 2)
        if not comment_idx.isValid():
            return

        return self._edit_comment_requested(addr, comment_idx)

    def _edit_comment_requested(self, addr, comment_idx):
        old_comment = comment_idx.data()

        if idaapi.IDA_SDK_VERSION >= 700:
            new_comment = idaapi.ask_str(str(old_comment), 0, "New comment:")
        else:
            new_comment = idaapi.askstr(0, str(old_comment), "New comment:")

        if new_comment is None:
            return

        if not idaapi.set_cmt(addr, new_comment, 1):
            return
        renamed_comment = idaapi.get_cmt(addr, 1)
        comment_idx.model().setData(comment_idx, renamed_comment)

    def _tv_rename_action_triggered(self):
        selected = self.tv.selectionModel().selectedIndexes()
        if not selected:
            return

        idx = selected[0]
        if not idx.isValid():
            return

        addr = idx.data(role=self.ADDR_ROLE)
        if not addr:
            return
        caller_func = idaapi.get_func(addr)
        if not caller_func:
            return
        addr = caller_func.start_ea

        name_idx = idx.sibling(idx.row(), 1)
        if not name_idx.isValid():
            return

        return self._rename_ea_requested(addr, name_idx)

    def _rename_ea_requested(self, addr, name_idx):
        old_name = name_idx.data()

        if idaapi.IDA_SDK_VERSION >= 700:
            new_name = idaapi.ask_str(str(old_name), 0, "New name:")
        else:
            new_name = idaapi.askstr(0, str(old_name), "New name:")

        if new_name is None:
            return

        force_name(addr, new_name)
        renamed_name = idaapi.get_ea_name(addr)
        name_idx.model().setData(name_idx, renamed_name)

    def OnClose(self, form):
        if self._idp_hooks:
            self._idp_hooks.unhook()

    def _tv_init_header(self, model):
        item_header = QtGui.QStandardItem("Address")
        model.setHorizontalHeaderItem(0, item_header)

        item_header = QtGui.QStandardItem("Caller Function")
        model.setHorizontalHeaderItem(1, item_header)

        item_header = QtGui.QStandardItem("Comment")
        model.setHorizontalHeaderItem(2, item_header)

    # noinspection PyMethodMayBeStatic
    def _tv_make_tag_item(self, name):
        rv = QtGui.QStandardItem(name)

        rv.setEditable(False)
        return [rv, QtGui.QStandardItem(), QtGui.QStandardItem()]

    def _tv_make_ref_item(self, tag, ref):
        ea_item = QtGui.QStandardItem("0x{:X}".format(ref["ea"]))
        ea_item.setEditable(False)
        ea_item.setData(ref["ea"], self.ADDR_ROLE)

        name_item = QtGui.QStandardItem(ref["caller"])
        name_item.setEditable(False)
        name_item.setData(ref["ea"], self.ADDR_ROLE)

        api_name = QtGui.QStandardItem(ref["comment"])
        api_name.setEditable(False)
        api_name.setData(ref["ea"], self.ADDR_ROLE)

        return [ea_item, name_item, api_name]

    def _init_model(self):
        self._model.clear()

        root_node = self._model.invisibleRootItem()
        self._tv_init_header(self._model)

        for tag, refs in self._data.items():
            item_tag_list = self._tv_make_tag_item(tag)
            item_tag = item_tag_list[0]

            root_node.appendRow(item_tag_list)

            for ref in refs:
                ref_item_list = self._tv_make_ref_item(tag, ref)

                item_tag.appendRow(ref_item_list)

    def on_navigate_to_method_requested(self, index):
        addr = index.data(role=self.ADDR_ROLE)
        if addr is not None:
            idaapi.jumpto(addr)


DANGEROUS_FUNCTIONS = ["system", "strcpy", "gets", "printf"]


class vuln_candidate_finder_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = ""
    help = ""
    wanted_name = "IDARE: Vuln Candidate Finder"
    wanted_hotkey = ""

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        view_data = {}
        for function_addr in idautils.Functions():
            function_name = idaapi.get_name(function_addr)
            if function_name in DANGEROUS_FUNCTIONS:
                print("{} @ 0x{:x}".format(function_name, function_addr))
                print("CodeRefsTo")
                xrefs = idautils.CodeRefsTo(function_addr, False)
                for xref in xrefs:
                    if is_vuln_candidate(function_name, xref):
                        print("@ 0x{:x}".format(xref))

                        # Get caller function
                        caller_func = idaapi.get_func(xref)
                        if caller_func is not None:
                            caller_func_name = idaapi.get_name(caller_func.start_ea)
                        else:
                            caller_func_name = "?????"

                        # Get repeatable comments on xref
                        comment = idaapi.get_cmt(xref, 1)

                        if function_name not in view_data:
                            view_data[function_name] = []
                        view_data[function_name].append(
                            {"ea": xref, "caller": caller_func_name, "comment": comment}
                        )

        plg = VulnCandidateFinderView(view_data)
        plg.Show()

    def term(self):
        pass


def PLUGIN_ENTRY():
    return vuln_candidate_finder_t()


repository: claroty/ResearchTools
file: IDA/Renamer/renamer.py
wanted_name: Renamer
created_at: 2020-03-26 08:32:10+00:00
pushed_at: 2020-09-30 11:19:18+00:00
url:
https://github.com/claroty/ResearchTools/blob/156b26975f1f0e565f58e2496902d90693448c09/IDA/Renamer/renamer.py

content:
import re
from collections import defaultdict
import string

import ida_kernwin
from idaapi import *
from idautils import *
from idc import *


############ TODO ##########
# 1. Bug: when selecting a line in the Chooser and moving to the next function, the same line (index wise) will not be clickable (only after double-click).
############################


def clean_string(st):
    st = st.replace("()", "")
    st = st.replace("\\", "")
    st = st.replace("/", "")
    if " " in st:
        st = string.capwords(st)
    st = st.replace(" ", "")
    return st


class StringsManager():
    def __init__(self):
        # FunctionAddress: ["str1", "str2", "str3"..]
        self.dict_strings = defaultdict(list)
        self.current_function_index = 0
        self.list_keys = []
        self.count_total_strings = 0
        self.count_processed_strings = 0
        self.build_strings_tree()

    def build_strings_tree(self):
        for st in Strings():
            str_addr = st.ea
            str_name = str(st)
            func_addr_name = self.get_parent_func(str_addr)
            func_addr = self.func_name_to_addr(func_addr_name)
            if func_addr:
                self.dict_strings[func_addr].append(str_name)
                self.count_total_strings += 1
        self.list_keys = self.dict_strings.keys()
        self.list_keys.sort()

    def func_name_to_addr(self, func_name):
        for func_addr in Functions():
            curr_func_name = GetFunctionName(func_addr)
            if curr_func_name == func_name:
                return func_addr    
        return None
        
    def get_xref_list(self, addr):
        xrefs = []
        for addr in XrefsTo(addr, flags=0):
            xrefs.append(addr.frm)
        return xrefs

    def get_func_name(self, addr):
        try:
            return GetFunctionName(addr)
        except Exception as e:
            return ""

    def get_parent_func(self, addr):
        xref_list = self.get_xref_list(addr)
        if not xref_list:
            return None
        fname = self.get_func_name(xref_list[0])
        return fname

    def rename_current_address(self, new_name):
        addr = self.get_current_function_addr()
        MakeNameEx(addr, new_name, 0x800)

    def get_strings_for_current_function(self):
        addr = self.get_current_function_addr()
        return self.dict_strings.get(addr)

    def get_current_function_name(self):
        return self.get_func_name(self.list_keys[self.current_function_index])

    def get_current_function_addr(self):
        return self.list_keys[self.current_function_index]

    def next_function(self):
        self.current_function_index = min(len(self.list_keys)-1, self.current_function_index + 1)
        self.claim_strings_of_current_function()

    def prev_function(self):
        self.current_function_index = max(0, self.current_function_index - 1)

    def set_function_index(self, index):
        self.current_function_index = index

    def claim_strings_of_current_function(self):
        self.count_processed_strings += len(self.get_strings_for_current_function())
    
    def is_empty(self):
        return len(self.list_keys) == 0

    def get_function_names(self):
        list_funcs = []
        for key_func_addr in self.list_keys:
            func_name = self.get_func_name(key_func_addr)
            list_funcs.append("{} ({})".format(hex(key_func_addr), func_name))
        return list_funcs

# --------------------------------------------------------------------------

# https://github.com/idapython/src/blob/master/pywraps/py_kernwin_choose.py
class ChooserStrings(ida_kernwin.Choose):
    """
    A simple chooser to be used as an embedded chooser
    """
    def __init__(self, title, init_items):
        ida_kernwin.Choose.__init__(self, 
            title,
            [ ["Strings", 50 | ida_kernwin.Choose.CHCOL_PLAIN], ],
            flags=ida_kernwin.Choose.CH_QFLT | ida_kernwin.Choose.CH_CAN_REFRESH | ida_kernwin.Choose.CHCOL_PLAIN | ida_kernwin.Choose.CH_NOIDB,
            embedded=True, # Must be Embedded because it's inside a Form
            width=50,
            height=8)
        self.items = init_items #[ ["teststring %04d" % x] for x in range(1000) ]
        self.icon = 0

    def GetItems(self):
        return self.items

    def SetItems(self, items):
        self.items = [] if items is None else items

    def OnClose(self):
        pass

    def OnGetLine(self, n):
        return self.items[n]

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

    def OnSelectLine(self, n):
        return (ida_kernwin.Choose.ALL_CHANGED, )

    def OnEditLine(self, n):
        return (ida_kernwin.Choose.ALL_CHANGED, )


class RenameForm(ida_kernwin.Form):
    def __init__(self):
        self.strings_manager = StringsManager()
        self.invert = False
        F = ida_kernwin.Form
        F.__init__(
            self,
        r"""Form Rename Helper
{FormChangeCb}
This form will help you to rename all the functions in your binary. You can even use ctrl-f to search for strings quickly.
Overall strings: {cStringsProcessed}/{cStringsTotal} ({cStringsPercentage}) strings processed.

Now working on function: {cCurrentFunction} ({cCurrentFunctionStatus})
<Functions:{cbDropdownFunctionsReadonly}>
<Jump to function:{iButtonJumpTo}>

<Select the string which matches the most to the required function name#Strings:{cEStringsChooser}>

<Write a function name:{iFunctionNameToSet}>

                 <Previous:{iButtonBack}> <Apply Name:{iButtonApply}> <Next:{iButtonContinue}>
        """, {
            'cStringsProcessed': F.StringLabel("0"),
            'cStringsTotal': F.StringLabel(str(self.strings_manager.count_total_strings)),
            'cStringsPercentage': F.StringLabel("0.0%"),
            'cCurrentFunction': F.StringLabel(self._get_current_function_formatted()),
            'cCurrentFunctionStatus': F.StringLabel(self._get_current_function_status()),
            'cbDropdownFunctionsReadonly': F.DropdownListControl(items=self.strings_manager.get_function_names(), readonly=True, selval=0),
            'iFunctionNameToSet': F.StringInput(),
            'cEStringsChooser' : F.EmbeddedChooserControl(ChooserStrings("Function Strings", self._get_chooser_strings())),
            'iButtonApply': F.ButtonInput(self.OniButtonApply),
            'iButtonContinue': F.ButtonInput(self.OniButtonContinue),
            'iButtonBack': F.ButtonInput(self.OniButtonBack),
            'iButtonJumpTo': F.ButtonInput(self.OniButtonJumpTo),
            'FormChangeCb': F.FormChangeCb(self.OnFormChange),
        })

    def _get_chooser_strings(self):
        return [ [st] for st in self.strings_manager.get_strings_for_current_function() ]
    
    def _get_current_function_status(self):
        return "{}/{} functions".format(self.strings_manager.current_function_index + 1, len(self.strings_manager.list_keys))

    def _get_current_function_formatted(self):
        current_func_addr = self.strings_manager.get_current_function_addr()
        current_func_name = self.strings_manager.get_current_function_name()
        return current_func_name

    def _update_view(self):
        current_func_addr = self.strings_manager.get_current_function_addr()
        current_func_name = self.strings_manager.get_current_function_name()
        
        # Jump to relavent function
        jumpto(current_func_addr)

        # Update function dropdown
        self.SetControlValue(self.cbDropdownFunctionsReadonly, self.strings_manager.current_function_index)

        # Update texts
        self.SetControlValue(self.cStringsProcessed, str(self.strings_manager.count_processed_strings))
        self.SetControlValue(self.cStringsTotal, str(self.strings_manager.count_total_strings))
        self.SetControlValue(self.cStringsPercentage, "{:.2%}".format(1.0 * self.strings_manager.count_processed_strings / self.strings_manager.count_total_strings))
        self.SetControlValue(self.cCurrentFunction, self._get_current_function_formatted())
        self.SetControlValue(self.cCurrentFunctionStatus, self._get_current_function_status())

        # Update chooser
        str_list_for_chooser = [ [st] for st in self.strings_manager.get_strings_for_current_function() ]
        self.cEStringsChooser.chooser.SetItems(str_list_for_chooser)
        self.RefreshField(self.cEStringsChooser)

    def OniButtonJumpTo(self, code=0):
        jumpto(self.strings_manager.get_current_function_addr())

    def OniButtonApply(self, code=0):
        current_func_addr = self.strings_manager.get_current_function_addr()
        current_func_name = self.strings_manager.get_current_function_name()
        func_name_to_set = self.GetControlValue(self.iFunctionNameToSet)
        if ida_kernwin.ask_yn(0, "Rename function '{}' --to--> '{}' ?".format(current_func_name, func_name_to_set)) == 1:
            # user pressed 'Yes'
            self.strings_manager.rename_current_address(func_name_to_set)
            self.strings_manager.next_function()
            self._update_view()

    def OniButtonContinue(self, code=0):
        self.strings_manager.next_function()
        self._update_view()

    def OniButtonBack(self, code=0):
        self.strings_manager.prev_function()
        self._update_view()

    def OnFormChange(self, fid):
        if fid == self.cEStringsChooser.id:
            chooser_index = self.GetControlValue(self.cEStringsChooser)[0]
            current_item_str = self.strings_manager.get_strings_for_current_function()[chooser_index]
            self.SetControlValue(self.iFunctionNameToSet, clean_string(current_item_str))
        elif fid == self.cbDropdownFunctionsReadonly.id:
            # user asked to move to a different address
            sel_idx = self.GetControlValue(self.cbDropdownFunctionsReadonly)
            self.strings_manager.set_function_index(sel_idx)
            self._update_view()
        return 1


###########################################
###########################################
###########################################
# -----------------------------------------------------------------------
def is_ida_version(requested):
    rv = requested.split(".")
    kv = get_kernel_version().split(".")

    count = min(len(rv), len(kv))
    if not count:
        return False

    for i in xrange(count):
        if int(kv[i]) < int(rv[i]):
            return False
    return True

class FunctionRenamer(ida_idaapi.plugin_t):
    flags = 0
    comment = ''
    help = ''
    flags = PLUGIN_MOD
    wanted_name = 'Renamer'
    wanted_hotkey = 'Ctrl-Shift-n'
    
    SHOW_MODEL = False # Show popup window ?
    NON_MODAL_INSTANCE = None # In case we open it as non-modal, we want only 1 instance


    def init(self):
        required_ver = "7.0"
        if not is_ida_version(required_ver) or not init_hexrays_plugin():
            msg ("[!] '%s' is inactive (IDA v%s required).\n" % (FunctionRenamer.wanted_name, required_ver))
            return PLUGIN_SKIP
        msg("[+] '%s' loaded. %s activates renamer.\n" % (FunctionRenamer.wanted_name, FunctionRenamer.wanted_hotkey))
        return PLUGIN_OK

    def run(self, arg):
        # Popup window
        if FunctionRenamer.SHOW_MODEL:
            # Init
            rf = RenameForm()
            # Compile (in order to populate the controls)
            rf.Compile()
            # Execute the form
            ok = rf.Execute()
            # Dispose the form
            rf.Free()
        else:
            # Init
            rf = RenameForm()
            # Set flags
            rf.modal = False
            rf.openform_flags = ida_kernwin.PluginForm.FORM_TAB
            # Compile (in order to populate the controls)
            rfc, _ = rf.Compile()
            FunctionRenamer.NON_MODAL_INSTANCE = rfc
            # Execute the form
            FunctionRenamer.NON_MODAL_INSTANCE.Open()

    def term(self):
        msg("[+] %s unloaded.\n" % (FunctionRenamer.wanted_name))

# Plugin entry
def PLUGIN_ENTRY():
    return FunctionRenamer()


repository: cellebrite-labs/PPLorer
file: pplorer.py
description: IDA plugin that resolves PPL calls to the actual underlying PPL function.
url: https://github.com/cellebrite-labs/PPLorer/blob/b27c5f1177fad28afd19e9dcd0e8ff426663190b/pplorer.py
wanted_name: Pplorer
comment: find xrefs for ppl gates
created_at: 2023-02-27 14:22:00+00:00
pushed_at: 2023-02-28 08:47:26+00:00
content:
from __future__ import print_function
import idaapi
import idc
import idautils
from netnode import Netnode
import sark
import ida_funcs
from functools import wraps, partial
import struct
import sys

PYTHON3 = sys.version_info.major == 3
if PYTHON3:
    import pickle

    def iterkeys(dct):
        return iter(dct.keys())

    def iteritems(dct):
        return iter(dct.items())
else:
    import cPickle as pickle

    iterkeys = dict.iterkeys
    iteritems = dict.iteritems

NETNODE = "$ pplorer"

class PickleNetNode(Netnode):
    @staticmethod
    def _encode(data):
        return pickle.dumps(data)

    @staticmethod
    def _decode(data):
        return pickle.loads(data)

    @staticmethod
    def cached(cache_key):
        """Decorator for returning cached values from self.cache[cache_key] for method with no arguments"""
        def decorator(method):
            @wraps(method)
            def wrapper(self):
                if hasattr(self, 'log'):
                    log = self.log.info
                else:
                    log = print

                cached_val = self.cache.get(cache_key)
                if cached_val is not None:
                    log("Using cached value for %s" % cache_key)
                    return cached_val
                log("No cached value for %s, generating..." % cache_key)
                result = method(self)
                self.cache[cache_key] = result
                log("Value for %s generated" % cache_key)
                return result
            return wrapper
        return decorator


class PplAnalyzer(object):

    def __init__(self, cache=None):
        self.ppl_dispatch = sark.Function(self.find_ppl_dispatch())
        self.ppl_table = []
        self.ppl_to_func_dict = {}  # link ppl func to a ppl gate call
        self.func_to_ppl_dict = {}  # link ppl gate call to an actual ppl func behind it
        self.analysis_done = False

    def find_ppl_dispatch(self):
        relevant_string = "ppl_dispatch: failed due to bad arguments/state"
        all_strings = idautils.Strings()
        for ida_string in all_strings:
            if relevant_string in str(ida_string):
                l = sark.Line(ida_string.ea)
                for dref in l.drefs_to:
                    dref_line = sark.Line(dref)
                    if dref_line.insn.operands[0].text == "X0":
                        if dref_line.prev.insn.mnem == "B":
                            # this is ppl exit and not what we need
                            continue
                        else:
                            try:
                                func = sark.Function(dref_line.ea)
                            except sark.exceptions.SarkNoFunction as e:
                                func = None
                                mark_line = dref_line
                                for i in range(50):
                                    mark_line = mark_line.prev
                                    if("CMP             X15," in mark_line.disasm):
                                        ida_funcs.add_func(mark_line.ea)
                                        func = sark.Function(dref_line.ea)
                                        break
                                if func is None:
                                    raise Exception("Failed to detect ppl dispatch, requires new start to find")
                            return func.ea
        return None

    def find_ppl_table(self):
        for line in self.ppl_dispatch.lines:
            if "ADRL            X9," in line.disasm:
                return line.insn.operands[1].value
        return None
    
    def find_ppl_table_size(self):
        for line in self.ppl_dispatch.lines:
            value = line.insn.operands[1].value
            if "CMP             X15," in line.disasm and value > 3:
                # the > 3 is to prevent false positives
                return value
        return None

    def get_ppl_table_by_order(self):
        entry_size = 8
        size = self.find_ppl_table_size()
        ppl_table = self.find_ppl_table()
        table = []
        for i in range(size):
            entry = ppl_table + i * entry_size
            line = sark.Line(entry)
            if all(x == 0 for x in line.bytes): # there are some empty entries.
                table.append(0)
            else:
                table.append(struct.unpack("<Q", line.bytes)[0])
        self.ppl_table = table

    def find_all_ppl_calls(self, ppl_dispatch):
        dispatch_wrapper = next(ppl_dispatch.crefs_to)
        line = sark.Line(dispatch_wrapper)
        while "PACIBSP" not in line.disasm:
            """
            We are backtracking the code to find the start to the func.
            We can't use sark.Function on the line because IDA can't identify the PPL gate as a func.
            """
            line = line.prev
        all_calls = list(line.crefs_to)
        return all_calls

    def mark_all_ppl_calls(self):

        def edit_comment(comment, wanted, delete):
            if comment is None:
                comment = ""
            if not delete and wanted not in comment:
                comment = comment.rstrip()
                if comment:
                    comment += '\n'
                comment += wanted
            elif delete and wanted in comment:
                parts = comment.split(wanted)
                comment = '\n'.join((x.rstrip() for x in parts))
            return comment

        for ppl_gate_call_ea in self.find_all_ppl_calls(self.ppl_dispatch):
            line = sark.Line(ppl_gate_call_ea)
            func_selector = line.prev.insn.operands[1].value
            if func_selector < len(self.ppl_table):
                actual_call = self.ppl_table[func_selector]
                comment = idc.get_cmt(ppl_gate_call_ea, True)
                new_comment = edit_comment(comment, "has xref to PPL", delete=False)
                idc.set_cmt(line.ea, new_comment, True)
                self.func_to_ppl_dict[ppl_gate_call_ea] = actual_call
                if actual_call in self.ppl_to_func_dict.keys():
                    self.ppl_to_func_dict[actual_call].append(ppl_gate_call_ea)
                else:
                    self.ppl_to_func_dict[actual_call] = [ppl_gate_call_ea]
            else:
                idaapi.warning(f"Tried to get bad index from ppl table. Index: {func_selector} PPL table len: {len(self.ppl_table)}\n")

    def analyze(self):
        self.get_ppl_table_by_order()
        self.mark_all_ppl_calls()
        self.analysis_done = True

    def can_xref_from_gate_ea(self, ea):
        if ea in self.func_to_ppl_dict.keys():
            return ea

    def can_xref_from_ppl_ea(self, ea):
        if ea in self.ppl_to_func_dict.keys():
            return ea


class PplorerPlugin(idaapi.plugin_t, idaapi.UI_Hooks):
    plugin_initialized = False
    flags = idaapi.PLUGIN_MOD | idaapi.PLUGIN_HIDE
    comment = "find xrefs for ppl gates"
    help = ""
    wanted_name = "Pplorer"
    wanted_hotkey = ""

    class MenuBase(idaapi.action_handler_t):
        label = None
        shortcut = None
        tooltip = None
        icon = -1

        def __init__(self, plugin):
            self.plugin = plugin
            self.name = self.plugin.wanted_name + ':' + self.__class__.__name__
            self.register()

        def register(self):
            return idaapi.register_action(idaapi.action_desc_t(
                self.name,  # Name. Acts as an ID. Must be unique.
                self.label,  # Label. That's what users see.
                self,  # Handler. Called when activated, and for updating
                self.shortcut,  # shortcut,
                self.tooltip,  # tooltip
                self.icon  # icon
            ))

        def unregister(self):
            """Unregister the action.
            After unregistering the class cannot be used.
            """
            idaapi.unregister_action(self.__name__)

        def activate(self, ctx):
            # dummy method
            return 1

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

        def path(self):
            return "Edit/Plugins/" + self.plugin.wanted_name + "/" + self.label

        def get_name(self):
            return self.name

    class AnalyzeMenu(MenuBase):
        label = 'Analyze IDB...'

        def activate(self, ctx):
            self.plugin.analyze()
            return 1

    class JumpXrefMenu(MenuBase):
        label = 'Jump to PPL XREFs...'
        shortcut = 'Ctrl+Shift+X'
        icon = 151

        def activate(self, ctx):
            self.plugin.choose_window_here()
            return 1

        def update(self, ctx):
            return idaapi.AST_ENABLE if self.plugin.can_find_xrefs_here() else idaapi.AST_DISABLE

    def __init__(self):
        self.analysis_done = False
        self.ppl_analyzer = None
        self.ui_hook = False
        self.analyze_menu = None
        self.jump_xref_menu = None

    def init(self):
        """plugin_t init() function"""
        super(PplorerPlugin, self).__init__()

        typename = idaapi.get_file_type_name().lower()
        if 'arm64e' not in typename:
            print('%s: IDB deemed unsuitable (not an ARM64e binary). Skipping...' % self.wanted_name)
            return idaapi.PLUGIN_SKIP

        if not PplorerPlugin.plugin_initialized:
            self.analyze_menu = self.AnalyzeMenu(self)
            self.jump_xref_menu = self.JumpXrefMenu(self)

            self.ui_hook = True
            self.hook()
            print('%s: IDB deemed suitable. Initializing...' % self.wanted_name)

        return idaapi.PLUGIN_KEEP

    def run(self, arg=0):
        """plugin_t run() implementation"""
        return

    def term(self):
        """plugin_t term() implementation"""
        if self.ui_hook:
            self.unhook()
            self.ui_hook = False
        return

    def ready_to_run(self):
        """UI_Hooks function.
        Attaches actions to plugin in main menu.
        """

        idaapi.attach_action_to_menu(self.analyze_menu.path(), self.analyze_menu.get_name(), idaapi.SETMENU_APP)
        idaapi.attach_action_to_menu(self.jump_xref_menu.path(), self.jump_xref_menu.get_name(), idaapi.SETMENU_APP)
        PplorerPlugin.plugin_initialized = True
        self.analyze(only_cached=True)

    def finish_populating_widget_popup(self, widget, popup_handle):
        """UI_Hooks function
        Attaches the Find Xref action to the dissasembly right click menu.
        """
        if not self.analysis_done:
            return
        if idaapi.get_widget_type(widget) == idaapi.BWN_DISASM and self.can_find_xrefs_here():
            idaapi.attach_action_to_popup(widget, popup_handle, "-", None, idaapi.SETMENU_FIRST)
            idaapi.attach_action_to_popup(widget, popup_handle, self.jump_xref_menu.get_name(), None,
                                          idaapi.SETMENU_FIRST)

    def analyze(self, only_cached=False):
        cache = PickleNetNode(NETNODE)
        # cache.kill()

        if only_cached:
            if not cache.get('analysis_done', False):
                return
            print('%s: this IDB had been previously analyzed, loading from cache' % self.wanted_name)
        elif self.analysis_done:
            answer = idc.ask_yn(idaapi.ASKBTN_NO, "HIDECANCEL\nRe-analyze the IDB?")
            if answer != idaapi.ASKBTN_YES:
                return
            cache.kill()
            self.analysis_done = False

        should_hide_wait = False
        if not cache.get('analysis_done', False):
            should_hide_wait = True
            idaapi.show_wait_box("HIDECANCEL\n%s analyzing..." % self.wanted_name)

        # not catching exceptions, just want to call the finally block to hide the wait_box
        try:
            self.ppl_analyzer = PplAnalyzer(cache)

            self.ppl_analyzer.analyze()
            if len(self.ppl_analyzer.ppl_table) == 0:
                idaapi.warning(('%s\nUnable to find ppl functions.\n') % self.wanted_name)
                return

            cache['analysis_done'] = True
            self.analysis_done = True
        finally:
            if should_hide_wait:
                idaapi.hide_wait_box()
            if not self.analysis_done:
                cache.kill()

    def choose_ppl_gate_to_func(self, ea):
        ppl_func = self.ppl_analyzer.func_to_ppl_dict[ea]
        if ppl_func == 0:
            return None
        return int(hex(ppl_func), 16)

    def choose_xref_to_ppl_func(self, ea):
        xrefs = self.ppl_analyzer.ppl_to_func_dict[ea]
        if not xrefs:
            return None

        candidates = [[idc.get_func_off_str(addr), "0x%016x" % addr]
                      for addr in xrefs]

        title = 'PPL xrefs to 0x%016X' % ea
        chooser = FuncXrefChooser(title, candidates)
        chosen = chooser.show()

        if chosen is None:
            return None
        return int(chosen[1], 16)

    def pick_choose_func_for_ea(self, ea):
        ref_addr = self.ppl_analyzer.can_xref_from_gate_ea(ea)
        if ref_addr:
            return partial(self.choose_ppl_gate_to_func, ref_addr)

        ref_addr = self.ppl_analyzer.can_xref_from_ppl_ea(ea)
        if ref_addr:
            return partial(self.choose_xref_to_ppl_func, ref_addr)

        return None

    def choose_by_ea(self, ea):
        choose_func = self.pick_choose_func_for_ea(ea)
        if choose_func is None:
            return

        addr = choose_func()
        if addr is not None:
            idc.jumpto(addr)

    def choose_window_here(self):
        if not self.analysis_done:
            return
        self.choose_by_ea(idc.here())

    def can_find_xrefs_here(self):
        if not self.analysis_done:
            return False
        return self.pick_choose_func_for_ea(idc.here()) is not None


class _Choose(idaapi.Choose):
    # Fix Choose.UI_Hooks_Trampoline to work with modal dialogs
    class UI_Hooks_Trampoline(idaapi.Choose.UI_Hooks_Trampoline):
        def populating_widget_popup(self, form, popup_handle):
            chooser = self.v()
            if hasattr(chooser, "OnPopup") and \
                    callable(getattr(chooser, "OnPopup")):
                chooser.OnPopup(form, popup_handle)

    class chooser_handler_t(idaapi.action_handler_t):
        def __init__(self, handler):
            idaapi.action_handler_t.__init__(self)
            self.handler = handler

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

        def update(self, ctx):
            return idaapi.AST_ENABLE_FOR_WIDGET \
                if idaapi.is_chooser_widget(ctx.widget_type) \
                else idaapi.AST_DISABLE_FOR_WIDGET

    def __init__(self, title, items, columns):
        idaapi.Choose.__init__(
            self,
            title,
            columns,
            flags=idaapi.Choose.CH_RESTORE)

        self.items = items

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

    def OnGetLine(self, n):
        return self.items[n]

    def show(self):
        selected = self.Show(modal=True)
        if selected < 0:
            return None
        return self.items[selected]


class FuncXrefChooser(_Choose):
    def __init__(self, title, items):
        _Choose.__init__(
            self,
            title,
            items,
            [["Address", 30 | idaapi.Choose.CHCOL_PLAIN], ["Address (Hex)", 20 | idaapi.Choose.CHCOL_HEX]])


def PLUGIN_ENTRY():
    return PplorerPlugin()


repository: techbliss/Free_the_Debuggers
file: Free_The_Debuggers.py
description: Free_the_Debuggers
wanted_name: Free the Debugger
created_at: 2014-06-01 20:39:04+00:00
pushed_at: 2015-02-07 21:52:41+00:00
url:
https://github.com/techbliss/Free_the_Debuggers/blob/60d40fdb2b6fa88dbff0c04fed810cdd4a235516/Free_The_Debuggers.py

content:
import sys
import idaapi
import idc


class Freesome(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = ""

    help = ""
    wanted_name = "Free the Debugger"
    wanted_hotkey = "Alt-F6"

    def init(self):
        idaapi.msg("Free the Debugger is found. \n")
        return idaapi.PLUGIN_OK

    def run(self, arg):
        idaapi.msg("run() called with %d!\n" % arg)

    def term(self):
        idaapi.msg("")

    if __EA64__:
        idaapi.load_and_run_plugin("windbg_user.p64", 0)
        idaapi.load_and_run_plugin("armlinux_stub.p64", 0)
        idaapi.load_and_run_plugin("gdb_user.p64", 0)
        idaapi.load_and_run_plugin("linux_stub.p64", 0)
        idaapi.load_and_run_plugin("mac_stub.p64", 0)
        idaapi.load_and_run_plugin("win32_stub.p64", 0)
        idaapi.load_and_run_plugin("win32_user.p64", 0)
        idaapi.load_and_run_plugin("wince_stub.p64", 0)
        idaapi.load_and_run_plugin("bdescr.p64", 0)
        idaapi.load_and_run_plugin("epoc_user.p64", 0)
        idaapi.load_and_run_plugin("bochs_user.p64", 0)
        idaapi.load_and_run_plugin("mac_stub.p64", 0)
        idaapi.load_and_run_plugin("replay_user.p64", 0)
        idaapi.load_and_run_plugin("pin_user.p64", 0)
        idaapi.load_and_run_plugin("dalvik_user.p64", 0)
        idaapi.load_plugin("dalvik_user.plw")
        #idc.LoadDebugger("gdb", 1)
        
##'lets give this baby some debuggers'''
    else:
        idaapi.load_and_run_plugin("windbg_user.plw", 0)
        idaapi.load_and_run_plugin("armlinux_stub.plw", 0)
        idaapi.load_and_run_plugin("gdb_user.plw", 0)
        idaapi.load_and_run_plugin("linux_stub.plw", 0)
        idaapi.load_and_run_plugin("mac_stub.plw", 0)
        idaapi.load_and_run_plugin("win32_stub.plw", 0)
        idaapi.load_and_run_plugin("win32_user.plw", 0)
        idaapi.load_and_run_plugin("wince_stub.plw", 0)
        idaapi.load_and_run_plugin("bdescr.plw", 0)
        idaapi.load_and_run_plugin("epoc_user.plw", 0)
        idaapi.load_and_run_plugin("bochs_user.plw", 0)
        idaapi.load_and_run_plugin("mac_stub.plw", 0)
        idaapi.load_and_run_plugin("replay_user.plw", 0)
        idaapi.load_and_run_plugin("pin_user.plw", 0)
        idaapi.load_and_run_plugin("dalvik_user.plw", 0)
        idaapi.load_plugin("dalvik_user.plw")
        #idc.LoadDebugger("windbg", 1)

	
def PLUGIN_ENTRY():
    return Freesome()

print "Finally We Are Free At Last"


repository: Thehepta/D810Learn
file: D810_demo/genmc.py
comment: Display microcode
created_at: 2024-06-02 06:36:20+00:00
pushed_at: 2025-05-11 10:47:50+00:00
url:
https://github.com/Thehepta/D810Learn/blob/52ad29245037f279411a2ddd6b8b6e3f15e9bc9a/D810_demo/genmc.py

content:
# genmc - Display Hex-Rays Microcode
#
# Requires IDA and decompiler(s) >= 7.3
#
# Based on code/ideas from:
# - vds13.py from Hexrays SDK
# - https://github.com/RolfRolles/HexRaysDeob
# - https://github.com/NeatMonster/MCExplorer

__author__ = "Dennis Elser"

# -----------------------------------------------------------------------------
import os, shutil, errno, re

import ida_idaapi
import ida_bytes
import ida_range
import ida_kernwin as kw
import ida_hexrays as hr
import ida_funcs
import ida_diskio
import ida_ida
import ida_graph
import ida_lines
import ida_moves
from ida_pro import IDA_SDK_VERSION

PLUGIN_NAME = "genmc"


# -----------------------------------------------------------------------------
def get_mcode_name(mcode):
    """returns the name of the mcode_t passed in parameter."""
    for x in dir(hr):
        if x.startswith('m_'):
            if mcode == getattr(hr, x):
                return x
    return None


# -----------------------------------------------------------------------------
def get_mopt_name(mopt):
    """returns the name of the mopt_t passed in parameter."""
    for x in dir(hr):
        if x.startswith('mop_'):
            if mopt == getattr(hr, x):
                return x
    return None


# -----------------------------------------------------------------------------
def is_plugin():
    """returns True if this script is executed from within an IDA plugins
    directory, False otherwise."""
    return "__plugins__" in __name__


# -----------------------------------------------------------------------------
def get_target_filename():
    """returns destination path for plugin installation."""
    return os.path.join(
        ida_diskio.get_user_idadir(),
        "plugins",
        "%s%s" % (PLUGIN_NAME, ".py"))


# -----------------------------------------------------------------------------
def is_installed():
    """checks whether script is present in designated plugins directory."""
    return os.path.isfile(get_target_filename())


# -----------------------------------------------------------------------------
SELF = __file__


def install_plugin():
    """Installs script to IDA userdir as a plugin."""
    if is_plugin():
        kw.msg("Command not available. Plugin already installed.\n")
        return False

    src = SELF
    if is_installed():
        btnid = kw.ask_yn(kw.ASKBTN_NO,
                          "File exists:\n\n%s\n\nReplace?" % get_target_filename())
        if btnid is not kw.ASKBTN_YES:
            return False
    dst = get_target_filename()
    usrdir = os.path.dirname(dst)
    kw.msg("Copying script from \"%s\" to \"%s\" ..." % (src, usrdir))
    if not os.path.exists(usrdir):
        try:
            os.makedirs(usrdir)
        except OSError as e:
            if e.errno != errno.EEXIST:
                kw.msg("failed (mkdir)!\n")
                return False
    try:
        shutil.copy(src, dst)
    except:
        kw.msg("failed (copy)!\n")
        return False
    kw.msg(("done\n"
            "Plugin installed - please restart this IDA instance now.\n"))
    return True


# -----------------------------------------------------------------------------
def is_ida_version(min_ver_required):
    return IDA_SDK_VERSION >= min_ver_required


# -----------------------------------------------------------------------------
def is_compatible():
    """Checks whether script is compatible with current IDA and
    decompiler versions."""
    min_ida_ver = 730
    return is_ida_version(min_ida_ver) and hr.init_hexrays_plugin()


# -----------------------------------------------------------------------------
class printer_t(hr.vd_printer_t):
    """Converts microcode output to an array of strings."""

    def __init__(self, *args):
        hr.vd_printer_t.__init__(self)
        self.mc = []

    def get_mc(self):
        return self.mc

    def _print(self, indent, line):
        self.mc.append(line)
        return 1


# -----------------------------------------------------------------------------
class microcode_insnviewer_t(ida_graph.GraphViewer):
    """Displays the graph view of Hex-Rays microcode."""

    def __init__(self, mba, mmat_name, fn_name, block, serial):
        title = "Microinstruction: %s - %d.%d (%s)" % (fn_name, block, serial, mmat_name)
        ida_graph.GraphViewer.__init__(self, title, True)
        self.mblock = mba.get_mblock(block)
        self.minsn = self.get_minsn(serial)

    def get_minsn(self, serial):
        curr = self.mblock.head
        for i in range(serial):
            curr = curr.next
        return curr

    def _insert_mop(self, mop, parent):
        if mop.t == hr.mop_z:
            return -1

        text = get_mopt_name(mop.t) + '\n' + mop._print()
        node_id = self.AddNode(text)
        self.AddEdge(parent, node_id)

        if mop.t == hr.mop_d:  # result of another instruction
            dst_id = self._insert_minsn(mop.d)
            if dst_id:
                self.AddEdge(node_id, dst_id)
        elif mop.t == hr.mop_f:  # list of arguments
            for arg in mop.f.args:
                self._insert_mop(arg, node_id)
        elif mop.t == hr.mop_a:  # mop_addr_t: address of operand
            self._insert_mop(mop.a, node_id)
        elif mop.t == hr.mop_p:  # operand pair
            self._insert_mop(mop.pair.lop, node_id)
            self._insert_mop(mop.pair.hop, node_id)
        return node_id

    def _insert_minsn(self, minsn):
        if minsn:
            text = get_mcode_name(minsn.opcode) + '\n' + minsn._print()
            node_id = self.AddNode(text)

            self._insert_mop(minsn.l, node_id)
            self._insert_mop(minsn.r, node_id)
            self._insert_mop(minsn.d, node_id)
            return node_id
        return None

    def OnRefresh(self):
        self.Clear()
        self._insert_minsn(self.minsn)
        return True

    def OnGetText(self, node_id):
        return self._nodes[node_id]


# -----------------------------------------------------------------------------
class microcode_graphviewer_t(ida_graph.GraphViewer):
    """Displays the graph view of Hex-Rays microcode."""

    def __init__(self, mba, title, lines):
        title = "Microcode graph: %s" % title
        ida_graph.GraphViewer.__init__(self, title, True)
        self._mba = mba
        self._mba.set_mba_flags(hr.MBA_SHORT)
        self._process_lines(lines)
        if mba.maturity == hr.MMAT_GENERATED or mba.maturity == hr.MMAT_PREOPTIMIZED:
            mba.build_graph()

    def _process_lines(self, lines):
        self._blockcmts = {}
        curblk = "-1"
        self._blockcmts[curblk] = []
        for i, line in enumerate(lines):
            plain_line = ida_lines.tag_remove(line).lstrip()
            if plain_line.startswith(';'):
                # msg("%s" % plain_line)
                re_ret = re.findall("BLOCK ([0-9]+) ", plain_line)
                if len(re_ret) > 0:
                    curblk = re_ret[0]
                    self._blockcmts[curblk] = [line]
                else:
                    self._blockcmts[curblk].append(line)
        if "0" in self._blockcmts:
            self._blockcmts["0"] = self._blockcmts["-1"] + self._blockcmts["0"]
        del self._blockcmts["-1"]

    def OnRefresh(self):
        self.Clear()
        qty = self._mba.qty
        for src in range(qty):
            self.AddNode(src)
        for src in range(qty):
            mblock = self._mba.get_mblock(src)
            for dest in mblock.succset:
                self.AddEdge(src, dest)
        return True

    def OnGetText(self, node):
        mblock = self._mba.get_mblock(node)
        vp = hr.qstring_printer_t(None, True)
        mblock._print(vp)

        node_key = "%d" % node
        if node_key in self._blockcmts:
            return ''.join(self._blockcmts[node_key]) + vp.s
        else:
            return vp.s


# -----------------------------------------------------------------------------
class microcode_viewer_t(kw.simplecustviewer_t):
    """Creates a widget that displays Hex-Rays microcode."""

    def Create(self, mba, title, mmat_name, fn_name, lines=[]):
        self.title = "Microcode: %s" % title
        self._mba = mba
        self.mmat_name = mmat_name
        self.fn_name = fn_name
        if not kw.simplecustviewer_t.Create(self, self.title):
            return False
        self.lines = lines
        for line in lines:
            self.AddLine(line)
        return True

    def _fit_graph(self, graph):
        if graph:
            gv = graph.GetWidget()
            ida_graph.viewer_fit_window(gv)
            ida_graph.refresh_viewer(gv)
            return True
        return False

    def _dock_widgets(self, graph, dockpos=kw.DP_RIGHT):
        if graph:
            gv = graph.GetWidget()
            kw.set_dock_pos(kw.get_widget_title(gv), self.title, dockpos)

            gli = ida_moves.graph_location_info_t()
            if ida_graph.viewer_get_gli(gli, gv):
                gli.zoom = 0  # auto-position
                ida_graph.viewer_set_gli(gv, gli, ida_graph.GLICTL_CENTER)
                ida_graph.refresh_viewer(gv)
            return True
        return False

    """TODO: it's better to handle keyboard input by
    registering an "action" and assigning it a hotkey"""

    def OnKeydown(self, vkey, shift):
        if vkey == ord("G"):
            g = microcode_graphviewer_t(self._mba, self.title, self.lines)
            if g:
                g.Show()
                self._fit_graph(g)
                self._dock_widgets(g,
                                   dockpos=kw.DP_FLOATING if shift else kw.DP_RIGHT)
            return True
        elif vkey == ord("I"):
            """TODO: at some point, the textual representation of the mba
                 should manually be created.
              -> we would no longer have to parse the textual output
                 that is created by the gen_microcode() function
              .> we may insert COLOR_ADDR tags which would allow us to
                 contextually link different viewers"""
            widget = self.GetWidget()
            line = kw.get_custom_viewer_curline(widget, False)
            line = ida_lines.tag_remove(line)
            p = line.find(" ")
            if p != -1 and '.' in line[:p]:
                block, serial = line.split('.')[:2]
                serial = serial.strip().split(' ')[0]
                g = microcode_insnviewer_t(self._mba, self.mmat_name, self.fn_name, int(block), int(serial))
                if g:
                    g.Show()
                    self._fit_graph(g)
                    self._dock_widgets(g,
                                       dockpos=kw.DP_FLOATING if shift else kw.DP_TAB)
            else:
                message = ("There is something wrong with the output generated by gen_microcode()!\n"
                           "Please rerun '%s.py'!" % PLUGIN_NAME)
                if line.startswith(";") or not (len(line)):
                    message = "Please position the cursor on a microcode instruction."
                kw.warning(message)
            return True
        return False


# -----------------------------------------------------------------------------
def ask_desired_maturity():
    """Displays a dialog which lets the user choose a maturity level
    of the microcode to generate."""
    maturity_levels = [
        ["MMAT_GENERATED", hr.MMAT_GENERATED],
        ["MMAT_PREOPTIMIZED", hr.MMAT_PREOPTIMIZED],
        ["MMAT_LOCOPT", hr.MMAT_LOCOPT],
        ["MMAT_CALLS", hr.MMAT_CALLS],
        ["MMAT_GLBOPT1", hr.MMAT_GLBOPT1],
        ["MMAT_GLBOPT2", hr.MMAT_GLBOPT2],
        ["MMAT_GLBOPT3", hr.MMAT_GLBOPT3],
        ["MMAT_LVARS", hr.MMAT_LVARS]]

    class MaturityForm(kw.Form):
        def __init__(self):
            self.title = "Display Microcode"
            form = ("STARTITEM {id:mat_lvl}\n"
                    "%s\n"
                    " \n"
                    "<Maturity level:{mat_lvl}>\n\n"
                    "<##Options##Output includes comments:{flags_short}>{chkgroup_flags}>\n\n" %
                    self.title)

            dropdown_ctl = kw.Form.DropdownListControl(
                [text for text, _ in maturity_levels])
            chk_ctl = kw.Form.ChkGroupControl(("flags_short",))

            controls = {"mat_lvl": dropdown_ctl,
                        "chkgroup_flags": chk_ctl}

            kw.Form.__init__(self, form, controls)

    form = MaturityForm()
    form, args = form.Compile()
    form.flags_short.checked = True
    ok = form.Execute()

    mmat = None
    text = None
    flags = 0
    if ok == 1:
        text, mmat = maturity_levels[form.mat_lvl.value]
    flags |= 0 if form.flags_short.checked else hr.MBA_SHORT
    form.Free()
    return (text, mmat, flags)


# -----------------------------------------------------------------------------
def show_microcode():
    """Generates and displays microcode for an address range.
    An address range can be a selection of code or that of
    the current function."""
    sel, sea, eea = kw.read_range_selection(None)
    pfn = ida_funcs.get_func(kw.get_screen_ea())
    if not sel and not pfn:
        return (False, "Position cursor within a function or select range")

    if not sel and pfn:
        sea = pfn.start_ea
        eea = pfn.end_ea

    addr_fmt = "%016x" if ida_ida.inf_is_64bit() else "%08x"
    fn_name = (ida_funcs.get_func_name(pfn.start_ea)
               if pfn else "0x%s-0x%s" % (addr_fmt % sea, addr_fmt % eea))

    F = ida_bytes.get_flags(sea)
    if not ida_bytes.is_code(F):
        return (False, "The selected range must start with an instruction")

    text, mmat, mba_flags = ask_desired_maturity()
    if text is None and mmat is None:
        return (True, "Cancelled")

    if not sel and pfn:
        mbr = hr.mba_ranges_t(pfn)
    else:
        mbr = hr.mba_ranges_t()
        mbr.ranges.push_back(ida_range.range_t(sea, eea))

    hf = hr.hexrays_failure_t()
    ml = hr.mlist_t()
    mba = hr.gen_microcode(mbr, hf, ml, hr.DECOMP_WARNINGS, mmat)
    if not mba:
        return (False, "0x%s: %s" % (addr_fmt % hf.errea, hf.desc()))
    vp = printer_t()
    mba.set_mba_flags(mba.get_mba_flags() | mba_flags)
    mba._print(vp)
    mcv = microcode_viewer_t()
    if not mcv.Create(mba, "%s (%s)" % (fn_name, text), text, fn_name, vp.get_mc()):
        return (False, "Error creating viewer")

    mcv.Show()
    return (True,
            "Successfully generated microcode for 0x%s..0x%s" % (addr_fmt % sea, addr_fmt % eea))


# -----------------------------------------------------------------------------
def create_mc_widget():
    """Checks minimum requirements for the script/plugin to be able to run.
    Displays microcode or in case of failure, displays error message.
    This function acts as the main entry point that is invoked if the
    code is run as a script or as a plugin."""
    if not is_compatible():
        kw.msg("%s: Unsupported IDA / Hex-rays version\n" % (PLUGIN_NAME))
        return False
    success, message = show_microcode()
    output = kw.msg if success else kw.warning
    output("%s: %s\n" % (PLUGIN_NAME, message))
    return success


# -----------------------------------------------------------------------------
class genmc(ida_idaapi.plugin_t):
    """Class that is required for the code to be recognized as
    a plugin by IDA."""
    flags = 0
    comment = "Display microcode"
    help = comment
    wanted_name = PLUGIN_NAME
    wanted_hotkey = "Ctrl-Shift-M"

    def init(self):
        return (ida_idaapi.PLUGIN_OK if
                is_compatible() else ida_idaapi.PLUGIN_SKIP)

    def run(self, arg):
        create_mc_widget()

    def term(self):
        pass


# -----------------------------------------------------------------------------
def PLUGIN_ENTRY():
    """Entry point of this code if launched as a plugin."""
    return genmc()


# -----------------------------------------------------------------------------
def SCRIPT_ENTRY():
    """Entry point of this code if launched as a script."""
    if not is_plugin():
        if not is_installed():
            kw.msg(("%s: Available commands:\n"
                    "[+] \"install_plugin()\" - install script to ida_userdir/plugins\n") % (
                       PLUGIN_NAME))
        create_mc_widget()
        return True
    return False


# -----------------------------------------------------------------------------
SCRIPT_ENTRY()


repository: hoangcuongflp/Stuff
file: ida-master/plugins/fluorescence/fluorescence.py
description: because I've orderliness obsession :(
wanted_name: fluorescence
comment: Highlights function calls
created_at: 2015-10-12 16:35:32+00:00
pushed_at: 2014-12-22 00:32:06+00:00
url:
https://github.com/hoangcuongflp/Stuff/blob/ee100760a05f524ff73837222d80f1dd12da3cee/ida-master/plugins/fluorescence/fluorescence.py

content:
# Plugin to highlight and un-highlight call instructions.
import idc
import idaapi
import idautils

class CallHighlighter(object):

    COLOR = 0xFF99FF #BBGGRR

    def highlight(self, color=COLOR):
        for ea in idautils.Heads():
            if idaapi.isCode(idaapi.getFlags(ea)) and idaapi.is_call_insn(ea):
                current_color = idaapi.get_item_color(ea)
                if current_color == self.COLOR:
                    idaapi.set_item_color(ea, idc.DEFCOLOR)
                elif current_color == idc.DEFCOLOR:
                    idaapi.set_item_color(ea, self.COLOR)

class fluorescence_blower_t(idaapi.plugin_t):

    flags = 0
    comment = "Highlights function calls"
    help = ''
    wanted_name = 'fluorescence'
    wanted_hotkey = ''

    def init(self):
        self.highlighted = False
        self.context_menu = idaapi.add_menu_item("Options/", "Un/highlight call instructions", "", 0, self.run, (None,))
        return idaapi.PLUGIN_KEEP

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

    def run(self, arg):
        CallHighlighter().highlight()

def PLUGIN_ENTRY():
    return fluorescence_blower_t()


repository: H4lo/Away_From_Sub_Function_IN_IDA
file: Away_From_Sub_Function_IN_IDA.py
created_at: 2022-12-09 07:13:52+00:00
pushed_at: 2024-07-01 01:26:43+00:00
description:
Use openai to help you better translate function meanings and restore symbol tables from sub_xxxx functions

url:
https://github.com/H4lo/Away_From_Sub_Function_IN_IDA/blob/4f1ea9ee53b2754d66c2b648cf11b1c1d06fc9a0/Away_From_Sub_Function_IN_IDA.py

content:
import openai
import json
import idc
from idc import *
import idaapi
from idautils import *


openai.api_key = "" # modify it! Refer https://mp.weixin.qq.com/s/E4n63jltBPbAo8ZIMH10ig to register openai.

# Get a list of all functions in the binary
functions = Functions()

MAX_LINE_TOKEN = 60

class ExplainHandler(idaapi.action_handler_t):

    def __init__(self):
        idaapi.action_handler_t.__init__(self)
    def activate(self, ctx):
        search_noname_function()
        return True
    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS

class OpenAIVeryGOOD(idaapi.plugin_t):
    explain_action_name = "OpenAIVeryGOOD:rename_function"

    explain_menu_path = "Edit/OpenAIVeryGOOD/Please help me Auto Recover Sub Function"

    explain_action = idaapi.action_desc_t(explain_action_name,
                                    'Please help me Auto Recover Sub Function',
                                    ExplainHandler(),
                                    "Ctrl+Alt+K",
                                    'Use davinci-003 to explain the currently selected function',
                                    199)

    idaapi.register_action(explain_action)

    idaapi.attach_action_to_menu(explain_menu_path, explain_action_name, idaapi.SETMENU_APP)


def query_model(pseudocode):
    query = "Can you explain what the following C function does and suggest a better name for it?\n%s"%(pseudocode)
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=query,
        temperature=0.6,
        max_tokens=2500,
        top_p=1,
        frequency_penalty=1,
        presence_penalty=1,
        timeout=60  # Wait 60 seconds maximum
    )
    jobj = json.loads(str(response))

    func_comment,func_name = jobj['choices'][0]['text'],jobj['choices'][0]['text'].split()[-1]
    return (func_comment,func_name)


def search_noname_function():
    # Iterate over the functions
    for f in functions:
        # Get the name of the function
        name = GetFunctionName(f)

        # Check if the name starts with "sub_"
        if name.startswith("sub_"):
            # Print the function name and its address
            
            # Use the idaapi.decompile function to get the pseudocode
            pseudocode = idaapi.decompile(f)

            # Print the pseudocode
            #print(pseudocode)

            # Count the number of lines in the pseudocode
            lines = str(pseudocode).split("\n")
            
            if(len(lines)<MAX_LINE_TOKEN):
                func_comment,new_func_name = query_model(pseudocode)
                new_func_name = new_func_name.replace("\"","").replace("(","").replace(")","").replace("'","").replace(".","")
                print("Function {} found at 0x{:X}, ready rename function: {}".format(name, f, new_func_name))
                MakeName(eval("0x{:X}".format(f)),new_func_name)

                new_cmt = ""
                for i in range(len(func_comment)//100+1):
                    new_cmt += func_comment[i*100:(i+1)*100]+"\n"
                set_func_cmt(eval("0x{:X}".format(f)),new_cmt,0)
            else:
                print("[-] Max line limited, Pass!")


def PLUGIN_ENTRY():
    OpenAIVeryGOOD()


repository: gaasedelen/microavx
file: plugins/microavx.py
description: An AVX Lifter for the Hex-Rays Decompiler
wanted_name: MicroAVX
comment: AVX support for the Hex-Rays x64 Decompiler
created_at: 2020-07-22 15:09:27+00:00
pushed_at: 2023-04-28 11:47:33+00:00
url:
https://github.com/gaasedelen/microavx/blob/71750e63b043ef25d874cf60e811d90427b470c1/plugins/microavx.py

content:
import sys

import idc
import ida_ua
import ida_ida
import ida_idp
import ida_funcs
import ida_allins
import ida_idaapi
import ida_loader
import ida_kernwin
import ida_typeinf
import ida_hexrays

#-----------------------------------------------------------------------------
# Util
#-----------------------------------------------------------------------------

# an empty / NULL mop_t
NO_MOP = ida_hexrays.mop_t()

# EVEX-encoded instruction, intel.hpp (ida sdk)
AUX_EVEX = 0x10000

# register widths (bytes)
XMM_SIZE = 16
YMM_SIZE = 32
ZMM_SIZE = 64

# type sizes (bytes)
FLOAT_SIZE = 4
DOUBLE_SIZE = 8
DWORD_SIZE = 4
QWORD_SIZE = 8

def size_of_operand(op):
    """
    From ...
        https://reverseengineering.stackexchange.com/questions/19843/how-can-i-get-the-byte-size-of-an-operand-in-ida-pro
    """
    tbyte = 8
    dt_ldbl = 8
    n_bytes = [ 1, 2, 4, 4, 8,
            tbyte, -1, 8, 16, -1,
            -1, 6, -1, 4, 4,
            dt_ldbl, 32, 64 ]
    return n_bytes[op.dtype]

def is_amd64_idb():
    """
    Return true if an x86_64 IDB is open.
    """
    if ida_idp.ph.id != ida_idp.PLFM_386:
        return False
    return ida_ida.cvar.inf.is_64bit()

def bytes2bits(n):
    """
    Return the number of bits repersented by 'n' bytes.
    """
    return n * 8

def is_mem_op(op):
    """
    Return true if the given operand *looks* like a mem op.
    """
    return op.type in [ida_ua.o_mem, ida_ua.o_displ, ida_ua.o_phrase]

def is_reg_op(op):
    """
    Return true if the given operand is a register.
    """
    return op.type in [ida_ua.o_reg]

def is_avx_reg(op):
    """
    Return true if the given operand is a XMM or YMM register.
    """
    return bool(is_xmm_reg(op) or is_ymm_reg(op))

def is_xmm_reg(op):
    """
    Return true if the given operand is a XMM register.
    """
    if op.type != ida_ua.o_reg:
        return False
    if op.dtype != ida_ua.dt_byte16:
        return False
    return True

def is_ymm_reg(op):
    """
    Return true if the given operand is a YMM register.
    """
    if op.type != ida_ua.o_reg:
        return False
    if op.dtype != ida_ua.dt_byte32:
        return False
    return True

def is_avx_512(insn):
    """
    Return true if the given insn_t is an AVX512 instruction.
    """
    return bool(insn.auxpref & AUX_EVEX)

#-----------------------------------------------------------------------------
# Microcode Helpers
#-----------------------------------------------------------------------------

def get_ymm_mreg(xmm_mreg):
    """
    Return the YMM microcode register for a given XMM register.
    """
    xmm_reg = ida_hexrays.mreg2reg(xmm_mreg, XMM_SIZE)
    xmm_name = ida_idp.get_reg_name(xmm_reg, XMM_SIZE)
    xmm_number = int(xmm_name.split("mm")[-1])

    # compute the ymm mreg id
    ymm_reg = ida_idp.str2reg("ymm%u" % xmm_number)
    ymm_mreg = ida_hexrays.reg2mreg(ymm_reg)

    # sanity check...
    xmm_name = ida_hexrays.get_mreg_name(xmm_mreg, XMM_SIZE)
    ymm_name = ida_hexrays.get_mreg_name(ymm_mreg, YMM_SIZE)
    assert xmm_name[1:] == ymm_name[1:], "Reg escalation did not work... (%s, %s)" % (xmm_name, ymm_name)
 
    # return the ymm microcode register id
    return ymm_mreg

def clear_upper(cdg, xmm_mreg, op_size=XMM_SIZE):
    """
    Extend the given xmm reg, clearing the upper bits (through ymm).
    """
    ymm_mreg = get_ymm_mreg(xmm_mreg)

    xmm_mop = ida_hexrays.mop_t(xmm_mreg, op_size)
    ymm_mop = ida_hexrays.mop_t(ymm_mreg, YMM_SIZE)

    return cdg.emit(ida_hexrays.m_xdu, xmm_mop, NO_MOP, ymm_mop)

def store_operand_hack(cdg, op_num, new_mop):
    """
    XXX: why is there a load_operand(), but no inverse.. ?
    """

    # emit a 'load' operation...
    memX = cdg.load_operand(op_num)
    assert memX != ida_hexrays.mr_none, "Invalid op_num..."

    # since this is gonna be kind of hacky, let's make sure a load was actually emitted
    ins = cdg.mb.tail
    if ins.opcode != ida_hexrays.m_ldx:
        if ins.prev.opcode != ida_hexrays.m_ldx:
            raise ValueError("Hehe, hack failed :-( (insn 0x%08X op 0x%02X)" % (cdg.insn.ea, ins.opcode))
        prev = ins.prev
        cdg.mb.make_nop(ins)
        ins = prev
    assert ins.d.size == new_mop.size, "%u vs %u" % (new_mop.size, ins.d.size)

    # convert the load to a store :^)
    ins.opcode = ida_hexrays.m_stx
    ins.d = ins.r   # d = op mem offset
    ins.r = ins.l   # r = op mem segm 
    ins.l = new_mop # l = value to store (mop_t)

    return ins

#-----------------------------------------------------------------------------
# Intrinsic Helper
#-----------------------------------------------------------------------------

class AVXIntrinsic(object):
    """
    This class helps with generating simple intrinsic calls in microcode.
    """

    def __init__(self, cdg, name):
        self.cdg = cdg

        # call info, sort of like func_type_data_t()
        self.call_info = ida_hexrays.mcallinfo_t()
        self.call_info.cc = ida_typeinf.CM_CC_FASTCALL
        self.call_info.callee = ida_idaapi.BADADDR
        self.call_info.solid_args = 0
        self.call_info.role = ida_hexrays.ROLE_UNK
        self.call_info.flags = ida_hexrays.FCI_SPLOK | ida_hexrays.FCI_FINAL | ida_hexrays.FCI_PROP

        # the actual 'call' microcode insn
        self.call_insn = ida_hexrays.minsn_t(cdg.insn.ea)
        self.call_insn.opcode = ida_hexrays.m_call
        self.call_insn.l.make_helper(name)
        self.call_insn.d.t = ida_hexrays.mop_f
        self.call_insn.d.f = self.call_info

        # temp return type
        self.call_info.return_type = ida_typeinf.tinfo_t()
        self.call_insn.d.size = 0

    def set_return_reg(self, mreg, type_string):
        """
        Set the return register of the function call, with a type string.
        """
        ret_tinfo = ida_typeinf.tinfo_t()
        ret_tinfo.get_named_type(None, type_string)
        return self.set_return_reg_type(mreg, ret_tinfo)

    def set_return_reg_basic(self, mreg, basic_type):
        """
        Set the return register of the function call, with a basic type assigned.
        """
        ret_tinfo = ida_typeinf.tinfo_t(basic_type)
        return self.set_return_reg_type(mreg, ret_tinfo)

    def set_return_reg_type(self, mreg, ret_tinfo):
        """
        Set the return register of the function call, with a complex type.
        """
        self.call_info.return_type = ret_tinfo
        self.call_insn.d.size = ret_tinfo.get_size()

        self.mov_insn = ida_hexrays.minsn_t(self.cdg.insn.ea)
        self.mov_insn.opcode = ida_hexrays.m_mov
        self.mov_insn.l.t = ida_hexrays.mop_d
        self.mov_insn.l.d = self.call_insn
        self.mov_insn.l.size = self.call_insn.d.size
        self.mov_insn.d.t = ida_hexrays.mop_r
        self.mov_insn.d.r = mreg
        self.mov_insn.d.size = self.call_insn.d.size

        if ret_tinfo.is_decl_floating():
            self.mov_insn.set_fpinsn()

    def add_argument_reg(self, mreg, type_string):
        """
        Add a regeister argument with a given type string to the function argument list.
        """
        op_tinfo = ida_typeinf.tinfo_t()
        op_tinfo.get_named_type(None, type_string)
        return self.add_argument_reg_type(mreg, op_tinfo)

    def add_argument_reg_basic(self, mreg, basic_type):
        """
        Add a regeister argument with a basic type to the function argument list.
        """
        op_tinfo = ida_typeinf.tinfo_t(basic_type)
        return self.add_argument_reg_type(mreg, op_tinfo)

    def add_argument_reg_type(self, mreg, op_tinfo):
        """
        Add a register argument of the given type to the function argument list.
        """
        call_arg = ida_hexrays.mcallarg_t()
        call_arg.t = ida_hexrays.mop_r
        call_arg.r = mreg
        call_arg.type = op_tinfo
        call_arg.size = op_tinfo.get_size()

        self.call_info.args.push_back(call_arg)
        self.call_info.solid_args += 1

    def add_argument_imm(self, value, basic_type):
        """
        Add an immediate value to the function argument list.
        """
        op_tinfo = ida_typeinf.tinfo_t(basic_type)

        mop_imm = ida_hexrays.mop_t()
        mop_imm.make_number(value, op_tinfo.get_size())
        
        call_arg = ida_hexrays.mcallarg_t()
        call_arg.make_number(value, op_tinfo.get_size())
        call_arg.type = op_tinfo

        self.call_info.args.push_back(call_arg)
        self.call_info.solid_args += 1

    def emit(self):
        """
        Emit the intrinsic call to the generated microcode.
        """
        self.cdg.mb.insert_into_block(self.mov_insn, self.cdg.mb.tail)

#-----------------------------------------------------------------------------
# AVX Lifter
#-----------------------------------------------------------------------------

class AVXLifter(ida_hexrays.microcode_filter_t):
    """
    A Hex-Rays microcode filter to lift AVX instructions during decompilation.
    """

    def __init__(self):
        super(AVXLifter, self).__init__()
        self._avx_handlers = \
        {

            # Compares (Scalar, Single / Double-Precision)
            ida_allins.NN_vcomiss: self.vcomiss,
            ida_allins.NN_vcomisd: self.vcomisd,
            ida_allins.NN_vucomiss: self.vucomiss,
            ida_allins.NN_vucomisd: self.vucomisd,

            # Conversions
            ida_allins.NN_vcvttss2si: self.vcvttss2si,
            ida_allins.NN_vcvtdq2ps: self.vcvtdq2ps,
            ida_allins.NN_vcvtsi2ss: self.vcvtsi2ss,
            ida_allins.NN_vcvtps2pd: self.vcvtps2pd,
            ida_allins.NN_vcvtss2sd: self.vcvtss2sd,

            # Mov (DWORD / QWORD)
            ida_allins.NN_vmovd: self.vmovd,
            ida_allins.NN_vmovq: self.vmovq,

            # Mov (Scalar, Single / Double-Precision)
            ida_allins.NN_vmovss: self.vmovss,
            ida_allins.NN_vmovsd: self.vmovsd,

            # Mov (Packed Single-Precision, Packed Integers)
            ida_allins.NN_vmovaps: self.v_mov_ps_dq,
            ida_allins.NN_vmovups: self.v_mov_ps_dq,
            ida_allins.NN_vmovdqa: self.v_mov_ps_dq,
            ida_allins.NN_vmovdqu: self.v_mov_ps_dq,

            # Bitwise (Packed Single-Precision)
            ida_allins.NN_vorps: self.v_bitwise_ps,
            ida_allins.NN_vandps: self.v_bitwise_ps,
            ida_allins.NN_vxorps: self.v_bitwise_ps,

            # Math (Scalar Single-Precision)
            ida_allins.NN_vaddss: self.v_math_ss,
            ida_allins.NN_vsubss: self.v_math_ss,
            ida_allins.NN_vmulss: self.v_math_ss,
            ida_allins.NN_vdivss: self.v_math_ss,

            # Math (Scalar Double-Precision)
            ida_allins.NN_vaddsd: self.v_math_sd,
            ida_allins.NN_vsubsd: self.v_math_sd,
            ida_allins.NN_vmulsd: self.v_math_sd,
            ida_allins.NN_vdivsd: self.v_math_sd,

            # Math (Packed Single-Precision)
            ida_allins.NN_vaddps: self.v_math_ps,
            ida_allins.NN_vsubps: self.v_math_ps,
            ida_allins.NN_vmulps: self.v_math_ps,
            ida_allins.NN_vdivps: self.v_math_ps,

            # Square Root
            ida_allins.NN_vsqrtss: self.vsqrtss,
            ida_allins.NN_vsqrtps: self.vsqrtps,

            # Shuffle (Packed Single-Precision) 
            ida_allins.NN_vshufps: self.vshufps,

        }

    def match(self, cdg):
        """
        Return true if the lifter supports this AVX instruction.
        """
        if is_avx_512(cdg.insn):
            return False
        return cdg.insn.itype in self._avx_handlers

    def apply(self, cdg):
        """
        Generate microcode for the current instruction.
        """
        cdg.store_operand = lambda x, y: store_operand_hack(cdg, x, y)
        return self._avx_handlers[cdg.insn.itype](cdg, cdg.insn)

    def install(self):
        """
        Install the AVX codegen lifter.
        """
        ida_hexrays.install_microcode_filter(self, True)
        print("Installed AVX lifter... (%u instr supported)" % len(self._avx_handlers))

    def remove(self):
        """
        Remove the AVX codegen lifter.
        """
        ida_hexrays.install_microcode_filter(self, False)
        print("Removed AVX lifter...")

    #--------------------------------------------------------------------------
    # Compare Instructions
    #--------------------------------------------------------------------------

    #
    # the intel manual states that all of these comparison instructions are
    # effectively identical to their SSE counterparts. because of this, we
    # simply twiddle the decoded insn to make it appear as SSE and bail.
    #
    # since the decompiler appears to operate on the same decoded instruction
    # data that we meddled with, it will lift the instruction in the same way
    # it would lift the SSE version we alias each AVX one to.
    #

    def vcomiss(self, cdg, insn):
        """
        VCOMISS xmm1, xmm2/m32
        """
        insn.itype = ida_allins.NN_comiss
        return ida_hexrays.MERR_INSN

    def vucomiss(self, cdg, insn):
        """
        VUCOMISS xmm1, xmm2/m32
        """
        insn.itype = ida_allins.NN_ucomiss
        return ida_hexrays.MERR_INSN

    def vcomisd(self, cdg, insn):
        """
        VCOMISD xmm1, xmm2/m64
        """
        insn.itype = ida_allins.NN_comisd
        return ida_hexrays.MERR_INSN

    def vucomisd(self, cdg, insn):
        """
        VUCOMISD xmm1, xmm2/m64
        """
        insn.itype = ida_allins.NN_ucomisd
        return ida_hexrays.MERR_INSN

    #-------------------------------------------------------------------------
    # Conversion Instructions
    #-------------------------------------------------------------------------

    def vcvttss2si(self, cdg, insn):
        """
        CVTTSS2SI r64, xmm1/m32
        CVTTSS2SI r32, xmm1/m32
        """
        insn.itype = ida_allins.NN_cvttss2si
        return ida_hexrays.MERR_INSN

    def vcvtdq2ps(self, cdg, insn):
        """
        VCVTDQ2PS xmm1, xmm2/m128
        VCVTDQ2PS ymm1, ymm2/m256
        """
        op_size = XMM_SIZE if is_xmm_reg(insn.Op1) else YMM_SIZE

        # op2 -- m128/m256
        if is_mem_op(insn.Op2):
            r_reg = cdg.load_operand(1)

        # op2 -- xmm2/ymm2
        else:
            assert is_avx_reg(insn.Op2)
            r_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1/ymm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        #
        # intrinsics:
        #     __m128 _mm_cvtepi32_ps (__m128i a)
        #     __m256 _mm256_cvtepi32_ps (__m256i a)
        #

        bit_size = bytes2bits(op_size)
        bit_str = str(bit_size) if op_size == YMM_SIZE else ""
        intrinsic_name = "_mm%s_cvtepi32_ps" % bit_str

        avx_intrinsic = AVXIntrinsic(cdg, intrinsic_name)
        avx_intrinsic.add_argument_reg(r_reg, "__m%ui" % bit_size)
        avx_intrinsic.set_return_reg(d_reg, "__m%u" % bit_size)
        avx_intrinsic.emit()

        # clear upper 128 bits of ymm1
        if op_size == XMM_SIZE:
            clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    def vcvtsi2ss(self, cdg, insn):
        """
        VCVTSI2SS xmm1, xmm2, r/m32
        VCVTSI2SS xmm1, xmm2, r/m64
        """
        src_size = size_of_operand(insn.Op3)

        # op3 -- m32/m64
        if is_mem_op(insn.Op3):
            r_reg = cdg.load_operand(2)

        # op3 -- r32/r64
        else:
            assert is_reg_op(insn.Op3)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

        # op2 -- xmm2 
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        # create a temp register to compute the final result into
        t0_result = cdg.mba.alloc_kreg(XMM_SIZE)
        t0_mop = ida_hexrays.mop_t(t0_result, FLOAT_SIZE)

        # create a temp register to downcast a double to a float (if needed)
        t1_i2f = cdg.mba.alloc_kreg(src_size)
        t1_mop = ida_hexrays.mop_t(t1_i2f, src_size)

        # copy xmm2 into the temp result reg, as we need its upper 3 dwords
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, l_reg, 0, t0_result, 0)

        # convert the integer (op3) to a float/double depending on its size
        cdg.emit(ida_hexrays.m_i2f, src_size, r_reg, 0, t1_i2f, 0)

        # reduce precision on the converted floating point value if needed (only r64/m64)
        cdg.emit(ida_hexrays.m_f2f, t1_mop, NO_MOP, t0_mop)

        # transfer the fully computed temp register to the real dest reg 
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, t0_result, 0, d_reg, 0)
        cdg.mba.free_kreg(t0_result, XMM_SIZE)
        cdg.mba.free_kreg(t1_i2f, src_size)
        
        # clear upper 128 bits of ymm1
        clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    def vcvtps2pd(self, cdg, insn):
        """
        VCVTPS2PD xmm1, xmm2/m64
        VCVTPS2PD ymm1, ymm2/m128
        """
        src_size = QWORD_SIZE if is_xmm_reg(insn.Op1) else XMM_SIZE

        # op2 -- m64/m128
        if is_mem_op(insn.Op2):
            r_reg = cdg.load_operand(1)

        # op2 -- xmm2/ymm2
        else:
            assert is_avx_reg(insn.Op2)
            r_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1/ymm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        #
        # intrinsics:
        #   - __m128d _mm_cvtps_pd (__m128 a)
        #   - __m256d _mm256_cvtps_pd (__m128 a)
        #
  
        bit_size = bytes2bits(src_size * 2)
        bit_str = "256" if (src_size * 2) == YMM_SIZE else ""
        intrinsic_name = "_mm%s_cvtps_pd" % bit_str

        avx_intrinsic = AVXIntrinsic(cdg, intrinsic_name)
        avx_intrinsic.add_argument_reg(r_reg, "__m128")
        avx_intrinsic.set_return_reg(d_reg, "__m%ud" % bit_size)
        avx_intrinsic.emit()
        
        # clear upper 128 bits of ymm1
        if src_size == QWORD_SIZE:
            clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    def vcvtss2sd(self, cdg, insn):
        """
        VCVTSS2SD xmm1, xmm2, r/m32
        """

        # op3 -- m32
        if is_mem_op(insn.Op3):
            r_reg = cdg.load_operand(2)

        # op3 -- r32
        else:
            assert is_reg_op(insn.Op3)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)
        
        r_mop = ida_hexrays.mop_t(r_reg, FLOAT_SIZE)

        # op2 -- xmm2 
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        # create a temp register to compute the final result into
        t0_result = cdg.mba.alloc_kreg(XMM_SIZE)
        t0_mop = ida_hexrays.mop_t(t0_result, DOUBLE_SIZE)

        # copy xmm2 into the temp result reg, as we need its upper quadword
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, l_reg, 0, t0_result, 0)

        # convert float (op3) to a double, storing it in the lower 64 of the temp result reg
        cdg.emit(ida_hexrays.m_f2f, r_mop, NO_MOP, t0_mop)

        # transfer the fully computed temp register to the real dest reg 
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, t0_result, 0, d_reg, 0)
        cdg.mba.free_kreg(t0_result, XMM_SIZE)

        # clear upper 128 bits of ymm1
        clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    #-------------------------------------------------------------------------
    # Mov Instructions
    #-------------------------------------------------------------------------

    def vmovss(self, cdg, insn):
        """
        VMOVSS xmm1, xmm2, xmm3	
        VMOVSS xmm1, m32
        VMOVSS xmm1, xmm2, xmm3	
        VMOVSS m32, xmm1
        """
        return self._vmov_ss_sd(cdg, insn, FLOAT_SIZE)

    def vmovsd(self, cdg, insn):
        """
        VMOVSD xmm1, xmm2, xmm3
        VMOVSD xmm1, m64
        VMOVSD xmm1, xmm2, xmm3
        VMOVSD m64, xmm1
        """
        return self._vmov_ss_sd(cdg, insn, DOUBLE_SIZE)

    def _vmov_ss_sd(self, cdg, insn, data_size):
        """
        Templated handler for scalar float/double mov instructions.
        """

        # op form: X, Y -- (2 operands)
        if insn.Op3.type == ida_ua.o_void:

            # op form: xmm1, m32/m64
            if is_xmm_reg(insn.Op1):
                assert is_mem_op(insn.Op2)

                # op2 -- m32/m64
                l_reg = cdg.load_operand(1)
                l_mop = ida_hexrays.mop_t(l_reg, data_size)

                # op1 -- xmm1
                d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
                d_mop = ida_hexrays.mop_t(d_reg, XMM_SIZE)

                # xmm1[:data_size] = [mem]
                insn = cdg.emit(ida_hexrays.m_xdu, l_mop, NO_MOP, d_mop)
        
                # clear xmm1[data_size:] bits (through ymm1)
                clear_upper(cdg, d_reg, data_size)

                return ida_hexrays.MERR_OK

            # op form: m32/m64, xmm1
            else:
                assert is_mem_op(insn.Op1) and is_xmm_reg(insn.Op2)

                # op2 -- xmm1
                l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
                l_mop = ida_hexrays.mop_t(l_reg, data_size)

                # store xmm1[:data_size] into memory at [m32/m64] (op1)
                insn = cdg.store_operand(0, l_mop)
                insn.set_fpinsn()

                return ida_hexrays.MERR_OK

        # op form: xmm1, xmm2, xmm3 -- (3 operands)
        else:
            assert is_xmm_reg(insn.Op1) and is_xmm_reg(insn.Op2) and is_xmm_reg(insn.Op3)

            d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
            l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

            # create a temp register to compute the final result into
            t0_result = cdg.mba.alloc_kreg(XMM_SIZE)

            # emit the microcode for this insn
            cdg.emit(ida_hexrays.m_mov, XMM_SIZE, l_reg, 0, t0_result, 0)
            cdg.emit(ida_hexrays.m_f2f, data_size, r_reg, 0, t0_result, 0)
            cdg.emit(ida_hexrays.m_mov, XMM_SIZE, t0_result, 0, d_reg, 0)
            cdg.mba.free_kreg(t0_result, XMM_SIZE)
                
            # clear xmm1[data_size:] bits (through ymm1)
            clear_upper(cdg, d_reg, data_size)

            return ida_hexrays.MERR_OK

        # failsafe
        assert "Unreachable..."
        return ida_hexrays.MERR_INSN 

    def vmovd(self, cdg, insn):
        """
        VMOVD xmm1, r32/m32
        VMOVD r32/m32, xmm1
        """
        return self._vmov(cdg, insn, DWORD_SIZE)
    
    def vmovq(self, cdg, insn):
        """
        VMOVQ xmm1, r64/m64
        VMOVQ r64/m64, xmm1
        """
        return self._vmov(cdg, insn, QWORD_SIZE)

    def _vmov(self, cdg, insn, data_size):
        """
        Templated handler for dword/qword mov instructions.
        """

        # op form: xmm1, rXX/mXX
        if is_xmm_reg(insn.Op1):

            # op2 -- m32/m64
            if is_mem_op(insn.Op2):
                l_reg = cdg.load_operand(1)

            # op2 -- r32/r64
            else:
                l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

            # wrap the source micro-reg as a micro-operand of the specified size
            l_mop = ida_hexrays.mop_t(l_reg, data_size)

            # op1 -- xmm1
            d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
            d_mop = ida_hexrays.mop_t(d_reg, XMM_SIZE)

            # emit the microcode for this insn
            cdg.emit(ida_hexrays.m_xdu, l_mop, NO_MOP, d_mop)

            # clear upper 128 bits of ymm1
            clear_upper(cdg, d_reg)

            return ida_hexrays.MERR_OK

        # op form: rXX/mXX, xmm1
        else:
            assert is_xmm_reg(insn.Op2)

            # op2 -- xmm1
            l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
            l_mop = ida_hexrays.mop_t(l_reg, data_size)

            # op1 -- m32/m64
            if is_mem_op(insn.Op1):
                cdg.store_operand(0, l_mop)

            # op1 -- r32/r64
            else:
                d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
                d_mop = ida_hexrays.mop_t(d_reg, data_size)
                cdg.emit(ida_hexrays.m_mov, l_mop, NO_MOP, d_mop)

                #
                # TODO: the intel manual doesn't make it entierly clear here
                # if the upper bits of a r32 operation need to be cleared ?
                # 

            return ida_hexrays.MERR_OK
        
        # failsafe
        assert "Unreachable..."
        return ida_hexrays.MERR_INSN 

    def v_mov_ps_dq(self, cdg, insn):
        """
        VMOVAPS xmm1, xmm2/m128
        VMOVAPS ymm1, ymm2/m256
        VMOVAPS xmm2/m128, xmm1
        VMOVAPS ymm2/m256, ymm1

        VMOVUPS xmm1, xmm2/m128
        VMOVUPS ymm1, ymm2/m256
        VMOVUPS xmm2/m128, xmm1
        VMOVUPS ymm2/m256, ymm1

        VMOVDQA xmm1, xmm2/m128
        VMOVDQA xmm2/m128, xmm1
        VMOVDQA ymm1, ymm2/m256
        VMOVDQA ymm2/m256, ymm1

        VMOVDQU xmm1, xmm2/m128
        VMOVDQU xmm2/m128, xmm1
        VMOVDQU ymm1, ymm2/m256
        VMOVDQU ymm2/m256, ymm1
        """

        # op form: reg, [mem]
        if is_avx_reg(insn.Op1):
            op_size = XMM_SIZE if is_xmm_reg(insn.Op1) else YMM_SIZE

            # op2 -- m128/m256
            if is_mem_op(insn.Op2):
                l_reg = cdg.load_operand(1)

            # op2 -- xmm1/ymm1
            else:
                assert is_avx_reg(insn.Op2)
                l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

            # wrap the source micro-reg as a micro-operand
            l_mop = ida_hexrays.mop_t(l_reg, op_size)

            # op1 -- xmmX/ymmX
            d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
            d_mop = ida_hexrays.mop_t(d_reg, op_size)

            # emit the microcode for this insn
            cdg.emit(ida_hexrays.m_mov, l_mop, NO_MOP, d_mop)

            # clear upper 128 bits of ymm1
            if op_size == XMM_SIZE:
                clear_upper(cdg, d_reg)

            return ida_hexrays.MERR_OK

        # op form: [mem], reg
        else:
            assert is_mem_op(insn.Op1) and is_avx_reg(insn.Op2)
            op_size = XMM_SIZE if is_xmm_reg(insn.Op2) else YMM_SIZE

            # op1 -- xmm1/ymm1
            l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
            l_mop = ida_hexrays.mop_t(l_reg, op_size)

            # [m128/m256] = xmm1/ymm1
            cdg.store_operand(0, l_mop)
            return ida_hexrays.MERR_OK

        # failsafe
        assert "Unreachable..."
        return ida_hexrays.MERR_INSN 

    #-------------------------------------------------------------------------
    # Bitwise Instructions
    #-------------------------------------------------------------------------

    def v_bitwise_ps(self, cdg, insn):
        """
        VORPS xmm1, xmm2, xmm3/m128
        VORPS ymm1, ymm2, ymm3/m256

        VXORPS xmm1, xmm2, xmm3/m128
        VXORPS ymm1, ymm2, ymm3/m256

        VANDPS xmm1, xmm2, xmm3/m128
        VANDPS ymm1, ymm2, ymm3/m256
        """
        assert is_avx_reg(insn.Op1) and is_avx_reg(insn.Op2)
        op_size = XMM_SIZE if is_xmm_reg(insn.Op1) else YMM_SIZE

        # op3 -- m128/m256
        if is_mem_op(insn.Op3):
            r_reg = cdg.load_operand(2)

        # op3 -- xmm3/ymm3
        else:
            assert is_avx_reg(insn.Op3)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

        itype2mcode = \
        {
            ida_allins.NN_vorps: ida_hexrays.m_or,
            ida_allins.NN_vandps: ida_hexrays.m_and,
            ida_allins.NN_vxorps: ida_hexrays.m_xor,
        }

        # get the hexrays microcode op to use for this instruction
        mcode_op = itype2mcode[insn.itype]

        # wrap the source micro-reg as a micro-operand
        r_mop = ida_hexrays.mop_t(r_reg, op_size)

        # op2 -- xmm2/ymm2
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
        l_mop = ida_hexrays.mop_t(l_reg, op_size)

        # op1 -- xmm1/ymm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
        d_mop = ida_hexrays.mop_t(d_reg, op_size)

        # emit the microcode for this insn
        cdg.emit(mcode_op, l_mop, r_mop, d_mop)

        # clear upper 128 bits of ymm1
        if op_size == XMM_SIZE:
            clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    #-------------------------------------------------------------------------
    # Arithmetic Instructions
    #-------------------------------------------------------------------------

    def v_math_ss(self, cdg, insn):
        """
        VADDSS    xmm1, xmm2, xmm3/m32
        VSUBSS    xmm1, xmm2, xmm3/m32
        VMULSS    xmm1, xmm2, xmm3/m32
        VDIVSS    xmm1, xmm2, xmm3/m32
        """
        return self._v_math_ss_sd(cdg, insn, FLOAT_SIZE)

    def v_math_sd(self, cdg, insn):
        """
        VADDSD    xmm1, xmm2, xmm3/m64
        VSUBSD    xmm1, xmm2, xmm3/m64
        VMULSD    xmm1, xmm2, xmm3/m64
        VDIVSD    xmm1, xmm2, xmm3/m64
        """
        return self._v_math_ss_sd(cdg, insn, DOUBLE_SIZE)

    def _v_math_ss_sd(self, cdg, insn, op_size):
        """
        Templated handler for scalar float/double math instructions.
        """
        assert is_avx_reg(insn.Op1) and is_avx_reg(insn.Op2)

        # op3 -- m32/m64
        if is_mem_op(insn.Op3):
            r_reg = cdg.load_operand(2)

        # op3 -- xmm3
        else:
            assert is_xmm_reg(insn.Op3)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

        # op2 -- xmm2
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        itype2mcode = \
        {
            ida_allins.NN_vaddss: ida_hexrays.m_fadd,
            ida_allins.NN_vaddsd: ida_hexrays.m_fadd,

            ida_allins.NN_vsubss: ida_hexrays.m_fsub,
            ida_allins.NN_vsubsd: ida_hexrays.m_fsub,

            ida_allins.NN_vmulss: ida_hexrays.m_fmul,
            ida_allins.NN_vmulsd: ida_hexrays.m_fmul,

            ida_allins.NN_vdivss: ida_hexrays.m_fdiv,
            ida_allins.NN_vdivsd: ida_hexrays.m_fdiv,
        }

        # get the hexrays microcode op to use for this instruction
        mcode_op = itype2mcode[insn.itype]
        op_dtype = ida_ua.dt_float if op_size == FLOAT_SIZE else ida_ua.dt_double

        # create a temp register to compute the final result into
        t0_result = cdg.mba.alloc_kreg(XMM_SIZE)

        # emit the microcode for this insn
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, l_reg, 0, t0_result, 0)
        cdg.emit_micro_mvm(mcode_op, op_dtype, l_reg, r_reg, t0_result, 0)
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, t0_result, 0, d_reg, 0)
        cdg.mba.free_kreg(t0_result, 16)

        # clear upper 128 bits of ymm1
        assert is_xmm_reg(insn.Op1)
        clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    def v_math_ps(self, cdg, insn):
        """
        VADDPS    xmm1, xmm2, xmm3/m128
        VADDPS    ymm1, ymm2, ymm3/m256

        VSUBPS    xmm1, xmm2, xmm3/m128
        VSUBPS    ymm1, ymm2, ymm3/m256

        VMULPS    xmm1, xmm2, xmm3/m128
        VMULPS    ymm1, ymm2, ymm3/m256

        VDIVPS    xmm1, xmm2, xmm3/m128
        VDIVPS    ymm1, ymm2, ymm3/m256
        """
        assert is_avx_reg(insn.Op1) and is_avx_reg(insn.Op2)
        op_size = XMM_SIZE if is_xmm_reg(insn.Op1) else YMM_SIZE

        # op3 -- m128/m256
        if is_mem_op(insn.Op3):
            r_reg = cdg.load_operand(2)

        # op3 -- xmm3/ymm3
        else:
            assert is_avx_reg(insn.Op3)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

        # op2 -- xmm2/ymm2
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
        
        # op1 -- xmm1/ymm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)
        d_mop = ida_hexrays.mop_t(d_reg, op_size)

        itype2name = \
        {
            ida_allins.NN_vaddps: "_mm%u_add_ps",
            ida_allins.NN_vsubps: "_mm%u_sub_ps", 
            ida_allins.NN_vmulps: "_mm%u_mul_ps",
            ida_allins.NN_vdivps: "_mm%u_div_ps",
        }

        # create the intrinsic
        bit_size = bytes2bits(op_size)
        bit_str = "256" if op_size == YMM_SIZE else ""
        intrinsic_name = itype2name[insn.itype] % bytes2bits(op_size)

        avx_intrinsic = AVXIntrinsic(cdg, intrinsic_name)
        avx_intrinsic.add_argument_reg(l_reg, "__m%u" % bit_size)
        avx_intrinsic.add_argument_reg(r_reg, "__m%u" % bit_size)
        avx_intrinsic.set_return_reg(d_reg, "__m%u" % bit_size)
        avx_intrinsic.emit()
        
        # clear upper 128 bits of ymm1
        if op_size == XMM_SIZE:
            clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    #-------------------------------------------------------------------------
    # Misc Instructions
    #-------------------------------------------------------------------------

    def vsqrtss(self, cdg, insn):
        """
        VSQRTSS xmm1, xmm2, xmm3/m32
        """
        assert is_xmm_reg(insn.Op1) and is_xmm_reg(insn.Op2)

        # op3 -- xmm3
        if is_xmm_reg(insn.Op3):
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

        # op3 -- m32
        else:
            assert is_mem_op(insn.Op3)
            r_reg = cdg.load_operand(2)

        # op2 - xmm2
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)
        
        # op1 - xmm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        # create a temp register to compute the final result into
        t0_result = cdg.mba.alloc_kreg(XMM_SIZE)

        # populate the dest reg
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, l_reg, 0, t0_result, 0)

        # mov.fpu call !fsqrt<fast:float xmm1_4.4>.4, t0_result_4.4
        avx_intrinsic = AVXIntrinsic(cdg, "fsqrt")
        avx_intrinsic.add_argument_reg_basic(r_reg, ida_typeinf.BT_FLOAT)
        avx_intrinsic.set_return_reg_basic(t0_result, ida_typeinf.BT_FLOAT)
        avx_intrinsic.emit()

        # store the fully computed result 
        cdg.emit(ida_hexrays.m_mov, XMM_SIZE, t0_result, 0, d_reg, 0)
        cdg.mba.free_kreg(t0_result, XMM_SIZE)

        # clear upper 128 bits of ymm1
        clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    def vsqrtps(self, cdg, insn):
        """
        VSQRTPS xmm1, xmm2/m128
        VSQRTPS ymm1, ymm2/m256
        """
        op_size = XMM_SIZE if is_xmm_reg(insn.Op1) else YMM_SIZE

        # op2 -- m128/m256
        if is_mem_op(insn.Op2):
            r_reg = cdg.load_operand(1)

        # op2 -- xmm2/ymm2
        else:
            assert is_avx_reg(insn.Op2)
            r_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1/ymm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        # intrinsic: __m256 _mm256_cvtepi32_ps (__m256i a)
        bit_size = bytes2bits(op_size)
        bit_str = str(bit_size) if op_size == YMM_SIZE else ""
        intrinsic_name = "_mm%s_sqrt_ps" % bit_str

        avx_intrinsic = AVXIntrinsic(cdg, intrinsic_name)
        avx_intrinsic.add_argument_reg(r_reg, "__m%u" % bit_size)
        avx_intrinsic.set_return_reg(d_reg, "__m%u" % bit_size)
        avx_intrinsic.emit()

        # clear upper 128 bits of ymm1
        if op_size == XMM_SIZE:
            clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

    def vshufps(self, cdg, insn):
        """
        VSHUFPS xmm1, xmm2, xmm3/m128, imm8
        VSHUFPS ymm1, ymm2, ymm3/m256, imm8
        """
        op_size = XMM_SIZE if is_xmm_reg(insn.Op1) else YMM_SIZE

        # op4 -- imm8
        assert insn.Op4.type == ida_ua.o_imm
        mask_value = insn.Op4.value
        
        # op3 -- m128/m256
        if is_mem_op(insn.Op3):
            r_reg = cdg.load_operand(2)

        # op3 -- xmm3/ymm3
        else:
            assert is_avx_reg(insn.Op3)
            r_reg = ida_hexrays.reg2mreg(insn.Op3.reg)

        # op2 -- xmm2/ymm2
        l_reg = ida_hexrays.reg2mreg(insn.Op2.reg)

        # op1 -- xmm1/ymm1
        d_reg = ida_hexrays.reg2mreg(insn.Op1.reg)

        # 
        # intrinsics:
        #   __m128 _mm_shuffle_ps (__m128 a, __m128 b, unsigned int imm8)
        #   __m256 _mm256_shuffle_ps (__m256 a, __m256 b, const int imm8)
        #

        bit_size = bytes2bits(op_size)
        bit_str = str(bit_size) if op_size == YMM_SIZE else ""
        intrinsic_name = "_mm%s_shuffle_ps" % bit_str

        avx_intrinsic = AVXIntrinsic(cdg, intrinsic_name)
        avx_intrinsic.add_argument_reg(l_reg, "__m%u" % bit_size)
        avx_intrinsic.add_argument_reg(r_reg, "__m%u" % bit_size)
        avx_intrinsic.add_argument_imm(mask_value, ida_typeinf.BT_INT8)
        avx_intrinsic.set_return_reg(d_reg, "__m%u" % bit_size)
        avx_intrinsic.emit()

        # clear upper 128 bits of ymm1
        if op_size == XMM_SIZE:
            clear_upper(cdg, d_reg)

        return ida_hexrays.MERR_OK

#-----------------------------------------------------------------------------
# Plugin
#-----------------------------------------------------------------------------

def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython Plugins.
    """
    return MicroAVX()

class MicroAVX(ida_idaapi.plugin_t):
    """
    The IDA plugin stub for MicroAVX.
    """

    flags = ida_idaapi.PLUGIN_PROC | ida_idaapi.PLUGIN_HIDE
    comment = "AVX support for the Hex-Rays x64 Decompiler"
    help = ""
    wanted_name = "MicroAVX"
    wanted_hotkey = ""
    loaded = False

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

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

        # only bother to load the plugin for relevant sessions
        if not is_amd64_idb():
            return ida_idaapi.PLUGIN_SKIP

        # ensure the x64 decompiler is loaded
        ida_loader.load_plugin("hexx64")
        assert ida_hexrays.init_hexrays_plugin(), "Missing Hexx64 Decompiler..."

        # initialize the AVX lifter 
        self.avx_lifter = AVXLifter()
        self.avx_lifter.install()
        sys.modules["__main__"].lifter = self.avx_lifter

        # mark the plugin as loaded
        self.loaded = True
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        """
        This is called by IDA when this file is loaded as a script.
        """
        ida_kernwin.warning("%s cannot be run as a script in IDA." % self.wanted_name)

    def term(self):
        """
        This is called by IDA when it is unloading the plugin.
        """
        if not self.loaded:
            return

        # hex-rays automatically cleans up decompiler hooks, so not much to do here...
        self.avx_lifter = None


repository: idapython/src
file: examples/decompiler/vds17.py
description: IDAPython project for Hex-Ray's IDA Pro
wanted_name: Structure offsets (IDAPython)
comment: Sample plugin17 for Hex-Rays decompiler
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/decompiler/vds17.py

content:
"""
summary: invoke the structure offset-choosing dialog from decompilation

description:
  Registers an action opens the "Select offsets" widget
  (select_udt_by_offset() call).

  This effectively repeats the functionality already available
  through Alt+Y.

  Place cursor on the union field and press Shift+T

level: advanced
"""

import ida_idaapi
import ida_hexrays
import ida_lines
import ida_typeinf
import ida_kernwin

# --------------------------------------------------------------------------
class func_stroff_ah_t(ida_kernwin.action_handler_t):
    def __init__(self):
        ida_kernwin.action_handler_t.__init__(self)

    def activate(self, ctx):
        # get the current item
        vu = ida_hexrays.get_widget_vdui(ctx.widget)
        vu.get_current_item(ida_hexrays.USE_KEYBOARD)

        # REGION1, will be referenced later
        # check that the current item is a union field
        if not vu.item.is_citem():
            ida_kernwin.warning("Please position the cursor on a union member")
            return 0
        e = vu.item.e
        while True:
            op = e.op
            if op != ida_hexrays.cot_memptr and op != ida_hexrays.cot_memref:
                ida_kernwin.warning("Please position the cursor on a union member")
                return 0
            e = e.x
            if op == ida_hexrays.cot_memptr:
                if e.type.is_union():
                    break
            else:
                if ida_typeinf.remove_pointer(e.type).is_union():
                    break
            if not e.type.is_udt():
                ida_kernwin.warning("Please position the cursor on a union member")
                return 0
        # END REGION1

        # REGION2
        # calculate the member offset
        off = 0
        e = vu.item.e
        while True:
            e2 = e.x
            tif = ida_typeinf.remove_pointer(e2.type)
            if not tif.is_union():
                off += e.m
            e = e2
            if e2.op != ida_hexrays.cot_memptr and e2.op != ida_hexrays.cot_memref:
                break
            if not e2.type.is_udt():
                break
        # END REGION2

        # REGION3
        # go up and collect more member references (in order to calculate the final offset)
        p = vu.item.e
        while True:
            p2 = vu.cfunc.body.find_parent_of(p)
            if p2.op == ida_hexrays.cot_memptr:
                break
            if p2.op == ida_hexrays.cot_memref:
                e2 = p2.cexpr
                tif = ida_typeinf.remove_pointer(e2.x.type)
                if not tif.is_union():
                    off += e2.m
                p = p2
                continue
            if p2.op == ida_hexrays.cot_ref:
                # handle &a.b + N (this expression may appear if the user previously selected
                #                  a wrong field)
                delta = 0
                add = vu.cfunc.body.find_parent_of(p2)
                if add.op == ida_hexrays.cot_cast:
                    add = vu.cfunc.body.find_parent_of(add)
                if add.op == ida_hexrays.cot_add and add.y.op == ida_hexrays.cot_num:
                    delta = add.y.numval()
                    objsize = add.type.get_ptrarr_objsize()
                    nbytes = delta * objsize
                    off += nbytes
            # we could use helpers like WORD/BYTE/... to calculate a more precise offset
            # if ( p2->op == cot_call && (e2->exflags & EXFL_LVALUE) != 0 )
            break
        # END REGION3

        # REGION4
        ea = vu.item.e.ea
        # the item itself may be unaddressable.
        # TODO: find its addressable parent
        if ea == ida_idaapi.BADADDR:
            ida_kernwin.warning("Sorry, the current item is not addressable")
            return 0
        # END REGION4

        # REGION5
        # prepare the text representation for the item,
        # use the neighborhoods of cursor
        line = ida_lines.tag_remove(ida_kernwin.get_custom_viewer_curline(vu.ct, False))
        line_len = len(line)
        x = max(0, vu.cpos.x - 10)
        l = min(10, line_len - vu.cpos.x) + 10
        line = line[x:x+l]
        # END REGION5

        # REGION6
        ops = ida_hexrays.ui_stroff_ops_t()
        op = ops.push_back()
        op.offset = off
        op.text = line
        # END REGION6

        # REGION7
        class set_union_sel_t(ida_hexrays.ui_stroff_applicator_t):
            def __init__(self, ea):
                ida_hexrays.ui_stroff_applicator_t.__init__(self)
                self.ea = ea

            def apply(self, opnum, path, top_tif, spath):
                typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, top_tif, '', '')
                ida_kernwin.msg("User selected %s of type %s\n" % (spath, typename))
                if path.empty():
                    return False
                vu.cfunc.set_user_union_selection(self.ea, path)
                vu.cfunc.save_user_unions()
                return True
        # END REGION7

        # REGION8
        su = set_union_sel_t(ea)
        res = ida_hexrays.select_udt_by_offset(None, ops, su)
        if res != 0:
            # regenerate ctree
            vu.refresh_view(True)
        # END REGION8

        return 1

    def update(self, ctx):
        return ida_kernwin.AST_ENABLE_FOR_WIDGET if \
            ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE else \
            ida_kernwin.AST_DISABLE_FOR_WIDGET


# --------------------------------------------------------------------------
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "Structure offsets (IDAPython)"
    wanted_hotkey = ""
    comment = "Sample plugin17 for Hex-Rays decompiler"
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            print("Hex-rays version %s has been detected, Structure offsets ready to use" % ida_hexrays.get_hexrays_version())
            ida_kernwin.register_action(
                ida_kernwin.action_desc_t(
                    "vds17:strchoose",
                    "Structure offsets",
                    func_stroff_ah_t(),
                    "Shift+T"))
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        pass
    def run(self, arg):
        pass

def PLUGIN_ENTRY():
    return my_plugin_t()

"""
# A few notes about the VDS17 sample

You can find two VDS17 samples in the IDA Pro install directory:

    python/examples/hexrays/vds17.py
    plugins/hexrays_sdk/plugins/vds17

The former is an IDAPython plugin and the latter is a C++ IDA Pro plugin.
Actually they have the same functionality.
Just to be more concrete the vds17.py plugin will be used below.


## How the user interface works

Let us suppose that we have the following local types:

    1 C     struct {int c0;int c1;}
    2 U     union {int u0;__int16 u1;}
    3 D     struct {int d0;C d1;U d2;}
    4 E     struct {int e0;D e1;}
    5 res_t struct {int r0;int r1;__int16 r2;}

and the decompiler generates the following pseudocode:

    void __cdecl f(res_t *r)
    {
      r->r0 = e->e1.d1.c0;
      r->r1 = e->e1.d2.u0;
      r->r2 = e->e1.d2.u1;
    }

As we see, it looks good, the decompiler did a really good job.

But let us imagine that we need reference to the ```e1.d2.u0``` union member on the last line.

At first we need to load the VDS17 plugin. For that,
place the cursor at the ```u1``` union member on the last line
and use ```Shift-T```.

The "Structure offsets" dialog appears on the screen.
The left pane of the dialog contains the available local types
and the right pane has the following view:

    [checked] e->e1.d2.u1; | 10h | |

Use the left pane to select ```E```, expand it, and select (Double-click) on the ```u0``` member.

The right pane will change to:

    [checked] e->e1.d2.u1; | 10h | [checke] E.e1.d2.u0 |

Since this is what we want, press the ```OK``` button and the pseudocode gets changed:

    void __cdecl f(res_t *r)
    {
      r->r0 = e->e1.d1.c0;
      r->r1 = e->e1.d2.u0;
      r->r2 = e->e1.d2.u0;
    }


### What do we need this plugin for?

All union members have the same offset in the parent structure.
The decompiler selects the first suitable union member.
Sometimes we need to change this selection and use another union member.


## API details

Let us look into ```python/examples/hexrays/vds17.py```.

* REGION1: check that the cursor points to the union member
* REGION2: calculate the member offset
* REGION3: sometimes there is a need to adjust the offset
* REGION4: the pointed item must be addressable

Then the magic begins:

* REGION5: we need something to show in the ```Operand``` column of the right pane.
    The text around the cursor looks like a good compromise.
* REGION6: we are ready to fill the right pane: we have calculated the offset and
    have prepared the descriptive text for the operand.
* REGION7: it is a callback that informs us about the user selection (will be described later)
* REGION8: activate the "Structure offsets" dialog, let the user select,
    the callback specified above will update the union member, refresh pseudocode


### The devil is in the details

The main part of callback is the ```apply``` method (REGION7).
It receives two arguments:

1. ```opnum``` is the number of the selected operand (line) in the right pane of the dialog.
     We need something to map the line number to our operand.
     In the case of VDS17 ```ea``` performs this role.
2. ```path```  the path that describes the union selection.
3. ```top_tif``` typeinfo of the top-level UDT which user selected
4. ```spath``` the field names path to the selected member

The union selection path is denotes a concrete member inside a UDT.

For structure types there is no need in the union selection path because the member
offset uniquely denotes the desired member.

For unions, on the other hand, the member offset is not enough because all union
members start at the same offset zero. For them, we remember the ordinal number of the
union member in the path. E.g., for the local types given above, the following holds:

* e1.d1.c0 is denoted by an empty path because it does not have any unions
* e1.d2.u0 is denoted by path consisting of 0: we use the first member of U
* e1.d2.u1 is denoted by path consisting of 1: we use the second member of U

You can retrieve the union selection path using API call ```get_user_union_selection```
or apply it using ```set_user_union_selection```.
"""


repository: WanQingGit/IDA7script
file: oregami/regname_plugin.py
description: 常用的比较实用的ida插件脚本
wanted_name: reGname
comment: reGname
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/oregami/regname_plugin.py

content:
import idaapi
import sark
#from reg_refs_forms import regRefPluginStarter, supported_procs
from oregami.oregami_gen import *
from oregami.regname import *


def AskStr(def_val, q_str):
    #without this line, waiting a few seconds for user input will cause the 'Please wait...' msgbox to jump and make the system freeze.
    orig_timeout = idaapi.set_script_timeout(0x7fffffff)
    ret = idc.AskStr(def_val, q_str)
    idaapi.set_script_timeout(orig_timeout)
    return ret
    
def get_regs(ea, all=True):
    reg = get_cursor_reg(ea)    

    if reg is None:
        return {}
        
    reg_set = set([reg])
    
    regs = proc_ops.get_basic_regs(ea, reg_set)
    
    
    if all:
        return regs
        
    if len(regs)<=1:
        return regs
        
    #need to pick only one reg
    reg = sorted(list(regs))[0] #first reg (alphabetically)
    reg = get_reg_user_name(ea, reg) #if renamed before, use it as default
    reg = AskStr(reg, 'Pick register')
    

    if reg is None: #exit
        return None
        
    #TODO: add check for user named reg
    if reg in regs:
        return [reg]
        
    for e_reg in regs:
        if reg==e_reg or reg==get_reg_user_name(ea, e_reg):
            return [e_reg]
    else:
        print 'No such reg in line'
        return []


def regnamePluginStarter(ea):
    #Get wanted reg
    regs = get_regs(ea, False)
    
    if regs is None:
        print 'Escaped'
        return
        
    regs = sorted(list(regs))
    
    assert len(regs)<=1
    if len(regs)==0:
        print 'No regs'
        return
    
    reg = regs[0]
    
    #Get new name    
    reg_new_name = AskStr(get_reg_user_name(ea, reg), 'New name for %s' % get_reg_full_name(ea, reg))
    
    if reg_new_name is None:
        return
        

    #Do renaming
    rename_reg(ea, reg, reg_new_name)
    
    
    
class RegnamePlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = "reGname"
    help = "Rename registers in their usage frame - only when used as a specific variable"
    wanted_name = "reGname"
    wanted_hotkey = "Shift+N"

    def init(self):
        self._prev_struct_name = ""
        
        proc_name = sark.idaapi.get_inf_structure().procName
        if proc_name not in supported_procs:
            print 'Regname plugin: No full support for this processor. Will do our best :)'
            #return idaapi.PLUGIN_SKIP

        return idaapi.PLUGIN_OK
        
    def term(self):
        pass

    def run(self, arg):
        start, _ = sark.get_selection()


        regnamePluginStarter(start)
        #regRefPluginStarter(start, tabMode=True, recursive_bool=False)
        

def PLUGIN_ENTRY():
    return RegnamePlugin()


repository: maiyao1988/IDAScripts
file: plugin/step2jump.py
description: Common IDA scripts for effective Reverse Enginnering
wanted_name: step to jump
comment: debug step to jump
created_at: 2020-01-31 07:55:49+00:00
pushed_at: 2023-02-15 14:03:25+00:00
url:
https://github.com/maiyao1988/IDAScripts/blob/f351d983c39b64e1c8ff596d532c74505c369d47/plugin/step2jump.py

content:
# -*- coding:utf-8 -*-
import idautils
import idaapi
import idc
import ida_dbg

def my_get_reg_value(register):
    rv = ida_idd.regval_t()
    ida_dbg.get_reg_val(register, rv)
    regval = rv.ival
    return regval

def is_modify_pc(addr):
    opnd = GetOpnd(addr, 0)
    if (opnd == "PC"):
        return True
    mnem = GetMnem(addr)
    if (mnem.startswith("POP") or mnem.startswith("LDMFD")):
        i = 0
        while True:
            opnd = GetOpnd(addr, i)
            
            if (opnd == ""):
                return False
            elif (opnd.find("PC") >-1):
                return True
            #
            i = i + 1
        #
    #
    return False
#

def is_jump(addr):
    mnem = GetMnem(addr)
    if (mnem.startswith("B") and mnem not in ("BFC", "BFI", "BIC") or mnem.startswith("IT") or mnem.startswith("CB") or mnem.startswith("TBB")):
        return True
    #
    elif (is_modify_pc(addr)):
        return True
    #
    return False
#

#runto to jump instruction when debugging
class Step2Jump(idaapi.plugin_t):
    flags=0                       #插件类别 或者特性
    wanted_name="step to jump"  #展示名称
    wanted_hotkey="6"        #其快捷键
    comment="debug step to jump"         #插件描述
    help="Something helpful"     #帮助信息
 
    #初始化时运行的，可以判断是否要启用这个插件，比如你这个插件主要是针对x86的，
    #那你就要判断当前分析的是不是x86，然后在决定是否要显示或者启用这个插件
    def init(self):
        return idaapi.PLUGIN_OK
 
    #插件退出时要做的操作，比如说你打开了某个文件，要到插件结束时才能关闭，
    #这里就给你这个机会
    def term(self):
        pass
 
    #按快捷键等 插件运行时 的地方
    def run(self,arg):
        if (not ida_dbg.is_debugger_on()):
            print("[step to jump] should run in debug mode.")
        else:
            curentEA = my_get_reg_value("PC")                
            if (is_jump(curentEA)):
                #当前是一个jump或者函数调用，直接做一个step_over
                ida_dbg.step_over()
            #
            else:
                addr = idc.next_head(curentEA)
                while addr != idc.BADADDR:
                    if (not idaapi.isCode(idaapi.getFlags(addr))):
                        line = "next_addr Find 0x%08x is not code addr, can not do anything"%addr
                        print(line)
                        break
                    #
                    if (is_jump(addr)):
                        ida_dbg.run_to(addr)
                        print("run to jump")
                        break
                    #
                    addr = idc.next_head(addr)
                #
            #
            #两个dbg事件之间要加上这句话才两个都生效
            #ida_dbg.wait_for_next_event(ida_dbg.WFNE_SUSP, -1)
    #
#插件入口      
def PLUGIN_ENTRY():
    return Step2Jump()
#


repository: notify-bibi/TriggerBug
file: PythonFrontEnd/ida-plugins/tr_dumper.py
wanted_name: Tr memdumper
comment: This is a comment bin
created_at: 2019-05-08 06:08:11+00:00
pushed_at: 2025-02-12 08:28:25+00:00
description:
Fast-Symbolic-Emulation-Engine. 主要用于自动化逆向约束求解，目的是解决angr的各种不足之处，大学时使用本工具solve了很多ctf题目，毕业后不打ctf了，很少维护了，后面可能会用来做一些其他的程序分析，反混淆也是不错. ( tips: repo设置private再public会丢失全部star, 此操作丢失了40个stars ... ) 

url:
https://github.com/notify-bibi/TriggerBug/blob/9f8a22dfa19ebcdb16b2ca6ab98cbd3bf5a11ee0/PythonFrontEnd/ida-plugins/tr_dumper.py

content:
# encoding:utf-8
from __future__ import print_function
import idaapi
import idc
import ida_dbg
import ida_bytes
from idautils import cpu
import ida_loader
import idautils
import sys
import struct
import time
import re
# pip install pyvex<=8.18.10.5
# import pyvex
# import archinfo  # 没有pyvex则archinfo无法工作

import ctypes
import re
import base64
import keystone


arch = None
mode = None
GDT_MAP_ADDR = (1<<47) | 0xfff00000
X86G_CC_OP_COPY = 0

FSMSR = 0xC0000100
GSMSR = 0xC0000101

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

amd64_rnames = {"rax": (16, 8), "rcx": (24, 8), "rdx": (32, 8), "rbx": (40, 8), "rsp": (48, 8), "rbp": (56, 8), "rsi": (64, 8), "rdi": (72, 8), "r8": (80, 8), "r9": (88, 8), "r10": (96, 8), "r11": (104, 8), "r12": (112, 8), "r13": (120, 8), "r14": (128, 8), "r15": (136, 8), "cc_op": (144, 8), "cc_dep1": (152, 8), "cc_dep2": (160, 8), "cc_ndep": (168, 8), "dflag": (176, 8), "rip": (184, 8), "acflag": (192, 8), "idflag": (200, 8), "fs_const": (208, 8), "sseround": (216, 8), "ymm0": (224, 32), "ymm1": (256, 32), "ymm2": (288, 32), "ymm3": (320, 32), "ymm4": (352, 32), "ymm5": (384, 32), "ymm6": (416, 32), "ymm7": (448, 32), "ymm8": (480, 32), "ymm9": (512, 32), "ymm10": (544, 32), "ymm11": (576, 32), "ymm12": (608, 32), "ymm13": (640, 32), "ymm14": (672, 32), "ymm15": (704, 32), "ymm16": (736, 32), "ftop": (768, 4), "fpreg": (776, 64), "fptag": (840, 8), "fpround": (848, 8), "fc3210": (856, 8), "emnote": (864, 4), "cmstart": (872, 8), "cmlen": (880, 8), "nraddr": (888, 8), "sc_class": (896, 8), "gs_const": (904, 8), "ip_at_syscall": (912, 8), "ldt": (920, 8), "gdt": (928, 8), "cs": (936, 2), "ds": (938, 2), "es": (940, 2), "fs": (942, 2), "gs": (944, 2), "ss": (946, 2)}
ppc_rnames = {'gpr0': (16, 8), 'r0': (16, 8), 'gpr1': (24, 8), 'r1': (24, 8), 'sp': (24, 8), 'gpr2': (32, 8), 'r2': (32, 8), 'rtoc': (32, 8), 'gpr3': (40, 8), 'r3': (40, 8), 'gpr4': (48, 8), 'r4': (48, 8), 'gpr5': (56, 8), 'r5': (56, 8), 'gpr6': (64, 8), 'r6': (64, 8), 'gpr7': (72, 8), 'r7': (72, 8), 'gpr8': (80, 8), 'r8': (80, 8), 'gpr9': (88, 8), 'r9': (88, 8), 'gpr10': (96, 8), 'r10': (96, 8), 'gpr11': (104, 8), 'r11': (104, 8), 'gpr12': (112, 8), 'r12': (112, 8), 'gpr13': (120, 8), 'r13': (120, 8), 'gpr14': (128, 8), 'r14': (128, 8), 'gpr15': (136, 8), 'r15': (136, 8), 'gpr16': (144, 8), 'r16': (144, 8), 'gpr17': (152, 8), 'r17': (152, 8), 'gpr18': (160, 8), 'r18': (160, 8), 'gpr19': (168, 8), 'r19': (168, 8), 'gpr20': (176, 8), 'r20': (176, 8), 'gpr21': (184, 8), 'r21': (184, 8), 'gpr22': (192, 8), 'r22': (192, 8), 'gpr23': (200, 8), 'r23': (200, 8), 'gpr24': (208, 8), 'r24': (208, 8), 'gpr25': (216, 8), 'r25': (216, 8), 'gpr26': (224, 8), 'r26': (224, 8), 'gpr27': (232, 8), 'r27': (232, 8), 'gpr28': (240, 8), 'r28': (240, 8), 'gpr29': (248, 8), 'r29': (248, 8), 'gpr30': (256, 8), 'r30': (256, 8), 'gpr31': (264, 8), 'r31': (264, 8), 'bp': (264, 8), 'vsr0': (272, 16), 'v0': (272, 16), 'fpr0': (272, 8), 'vsr1': (288, 16), 'v1': (288, 16), 'fpr1': (288, 8), 'vsr2': (304, 16), 'v2': (304, 16), 'fpr2': (304, 8), 'vsr3': (320, 16), 'v3': (320, 16), 'fpr3': (320, 8), 'vsr4': (336, 16), 'v4': (336, 16), 'fpr4': (336, 8), 'vsr5': (352, 16), 'v5': (352, 16), 'fpr5': (352, 8), 'vsr6': (368, 16), 'v6': (368, 16), 'fpr6': (368, 8), 'vsr7': (384, 16), 'v7': (384, 16), 'fpr7': (384, 8), 'vsr8': (400, 16), 'v8': (400, 16), 'fpr8': (400, 8), 'vsr9': (416, 16), 'v9': (416, 16), 'fpr9': (416, 8), 'vsr10': (432, 16), 'v10': (432, 16), 'fpr10': (432, 8), 'vsr11': (448, 16), 'v11': (448, 16), 'fpr11': (448, 8), 'vsr12': (464, 16), 'v12': (464, 16), 'fpr12': (464, 8), 'vsr13': (480, 16), 'v13': (480, 16), 'fpr13': (480, 8), 'vsr14': (496, 16), 'v14': (496, 16), 'fpr14': (496, 8), 'vsr15': (512, 16), 'v15': (512, 16), 'fpr15': (512, 8), 'vsr16': (528, 16), 'v16': (528, 16), 'fpr16': (528, 8), 'vsr17': (544, 16), 'v17': (544, 16), 'fpr17': (544, 8), 'vsr18': (560, 16), 'v18': (560, 16), 'fpr18': (560, 8), 'vsr19': (576, 16), 'v19': (576, 16), 'fpr19': (576, 8), 'vsr20': (592, 16), 'v20': (592, 16), 'fpr20': (592, 8), 'vsr21': (608, 16), 'v21': (608, 16), 'fpr21': (608, 8), 'vsr22': (624, 16), 'v22': (624, 16), 'fpr22': (624, 8), 'vsr23': (640, 16), 'v23': (640, 16), 'fpr23': (640, 8), 'vsr24': (656, 16), 'v24': (656, 16), 'fpr24': (656, 8), 'vsr25': (672, 16), 'v25': (672, 16), 'fpr25': (672, 8), 'vsr26': (688, 16), 'v26': (688, 16), 'fpr26': (688, 8), 'vsr27': (704, 16), 'v27': (704, 16), 'fpr27': (704, 8), 'vsr28': (720, 16), 'v28': (720, 16), 'fpr28': (720, 8), 'vsr29': (736, 16), 'v29': (736, 16), 'fpr29': (736, 8), 'vsr30': (752, 16), 'v30': (752, 16), 'fpr30': (752, 8), 'vsr31': (768, 16), 'v31': (768, 16), 'fpr31': (768, 8), 'vsr32': (784, 16), 'v32': (784, 16), 'vsr33': (800, 16), 'v33': (800, 16), 'vsr34': (816, 16), 'v34': (816, 16), 'vsr35': (832, 16), 'v35': (832, 16), 'vsr36': (848, 16), 'v36': (848, 16), 'vsr37': (864, 16), 'v37': (864, 16), 'vsr38': (880, 16), 'v38': (880, 16), 'vsr39': (896, 16), 'v39': (896, 16), 'vsr40': (912, 16), 'v40': (912, 16), 'vsr41': (928, 16), 'v41': (928, 16), 'vsr42': (944, 16), 'v42': (944, 16), 'vsr43': (960, 16), 'v43': (960, 16), 'vsr44': (976, 16), 'v44': (976, 16), 'vsr45': (992, 16), 'v45': (992, 16), 'vsr46': (1008, 16), 'v46': (1008, 16), 'vsr47': (1024, 16), 'v47': (1024, 16), 'vsr48': (1040, 16), 'v48': (1040, 16), 'vsr49': (1056, 16), 'v49': (1056, 16), 'vsr50': (1072, 16), 'v50': (1072, 16), 'vsr51': (1088, 16), 'v51': (1088, 16), 'vsr52': (1104, 16), 'v52': (1104, 16), 'vsr53': (1120, 16), 'v53': (1120, 16), 'vsr54': (1136, 16), 'v54': (1136, 16), 'vsr55': (1152, 16), 'v55': (1152, 16), 'vsr56': (1168, 16), 'v56': (1168, 16), 'vsr57': (1184, 16), 'v57': (1184, 16), 'vsr58': (1200, 16), 'v58': (1200, 16), 'vsr59': (1216, 16), 'v59': (1216, 16), 'vsr60': (1232, 16), 'v60': (1232, 16), 'vsr61': (1248, 16), 'v61': (1248, 16), 'vsr62': (1264, 16), 'v62': (1264, 16), 'vsr63': (1280, 16), 'v63': (1280, 16), 'cia': (1296, 8), 'ip': (1296, 8), 'pc': (1296, 8), 'lr': (1304, 8), 'ctr': (1312, 8), 'xer_so': (1320, 1), 'xer_ov': (1321, 1), 'xer_ca': (1322, 1), 'xer_bc': (1323, 1), 'cr0_321': (1324, 1), 'cr0_0': (1325, 1), 'cr0': (1325, 1), 'cr1_321': (1326, 1), 'cr1_0': (1327, 1), 'cr1': (1327, 1), 'cr2_321': (1328, 1), 'cr2_0': (1329, 1), 'cr2': (1329, 1), 'cr3_321': (1330, 1), 'cr3_0': (1331, 1), 'cr3': (1331, 1), 'cr4_321': (1332, 1), 'cr4_0': (1333, 1), 'cr4': (1333, 1), 'cr5_321': (1334, 1), 'cr5_0': (1335, 1), 'cr5': (1335, 1), 'cr6_321': (1336, 1), 'cr6_0': (1337, 1), 'cr6': (1337, 1), 'cr7_321': (1338, 1), 'cr7_0': (1339, 1), 'cr7': (1339, 1), 'fpround': (1340, 1), 'dfpround': (1341, 1), 'c_fpcc': (1342, 1), 'vrsave': (1344, 4), 'vscr': (1348, 4), 'emnote': (1352, 4), 'cmstart': (1360, 8), 'cmlen': (1368, 8), 'nraddr': (1376, 8), 'nraddr_gpr2': (1384, 8), 'redir_sp': (1392, 8), 'redir_stack': (1400, 256), 'ip_at_syscall': (1656, 8), 'sprg3_ro': (1664, 8), 'tfhar': (1672, 8), 'texasr': (1680, 8), 'tfiar': (1688, 8), 'ppr': (1696, 8), 'texasru': (1704, 4), 'pspb': (1708, 4)}
mips_rnames = {'zero': (16, 8), 'r0': (16, 8), 'at': (24, 8), 'r1': (24, 8), 'v0': (32, 8), 'r2': (32, 8), 'v1': (40, 8), 'r3': (40, 8), 'a0': (48, 8), 'r4': (48, 8), 'a1': (56, 8), 'r5': (56, 8), 'a2': (64, 8), 'r6': (64, 8), 'a3': (72, 8), 'r7': (72, 8), 't0': (80, 8), 'r8': (80, 8), 't1': (88, 8), 'r9': (88, 8), 't2': (96, 8), 'r10': (96, 8), 't3': (104, 8), 'r11': (104, 8), 't4': (112, 8), 'r12': (112, 8), 't5': (120, 8), 'r13': (120, 8), 't6': (128, 8), 'r14': (128, 8), 't7': (136, 8), 'r15': (136, 8), 's0': (144, 8), 'r16': (144, 8), 's1': (152, 8), 'r17': (152, 8), 's2': (160, 8), 'r18': (160, 8), 's3': (168, 8), 'r19': (168, 8), 's4': (176, 8), 'r20': (176, 8), 's5': (184, 8), 'r21': (184, 8), 's6': (192, 8), 'r22': (192, 8), 's7': (200, 8), 'r23': (200, 8), 't8': (208, 8), 'r24': (208, 8), 't9': (216, 8), 'r25': (216, 8), 'k0': (224, 8), 'r26': (224, 8), 'k1': (232, 8), 'r27': (232, 8), 'gp': (240, 8), 'r28': (240, 8), 'sp': (248, 8), 'r29': (248, 8), 's8': (256, 8), 'r30': (256, 8), 'fp': (256, 8), 'bp': (256, 8), 'ra': (264, 8), 'r31': (264, 8), 'lr': (264, 8), 'pc': (272, 8), 'ip': (272, 8), 'hi': (280, 8), 'lo': (288, 8), 'f0': (296, 8), 'f1': (304, 8), 'f2': (312, 8), 'f3': (320, 8), 'f4': (328, 8), 'f5': (336, 8), 'f6': (344, 8), 'f7': (352, 8), 'f8': (360, 8), 'f9': (368, 8), 'f10': (376, 8), 'f11': (384, 8), 'f12': (392, 8), 'f13': (400, 8), 'f14': (408, 8), 'f15': (416, 8), 'f16': (424, 8), 'f17': (432, 8), 'f18': (440, 8), 'f19': (448, 8), 'f20': (456, 8), 'f21': (464, 8), 'f22': (472, 8), 'f23': (480, 8), 'f24': (488, 8), 'f25': (496, 8), 'f26': (504, 8), 'f27': (512, 8), 'f28': (520, 8), 'f29': (528, 8), 'f30': (536, 8), 'f31': (544, 8), 'fir': (552, 4), 'fccr': (556, 4), 'fexr': (560, 4), 'fenr': (564, 4), 'fcsr': (568, 4), 'cp0_status': (572, 4), 'ulr': (576, 8), 'emnote': (584, 4), 'cond': (588, 4), 'cmstart': (592, 8), 'cmlen': (600, 8), 'nraddr': (608, 8), 'ip_at_syscall': (616, 8)}
s390_rnames = {'ia': (336, 8), 'ip': (336, 8), 'pc': (336, 8), 'r0': (192, 8), 'r1': (200, 8), 'r2': (208, 8), 'r3': (216, 8), 'r4': (224, 8), 'r5': (232, 8), 'r6': (240, 8), 'r7': (248, 8), 'r8': (256, 8), 'r9': (264, 8), 'r10': (272, 8), 'r11': (280, 8), 'bp': (280, 8), 'r12': (288, 8), 'r13': (296, 8), 'r14': (304, 8), 'r15': (312, 8), 'sp': (312, 8), 'f0': (64, 8), 'f1': (72, 8), 'f2': (80, 8), 'f3': (88, 8), 'f4': (96, 8), 'f5': (104, 8), 'f6': (112, 8), 'f7': (120, 8), 'f8': (128, 8), 'f9': (136, 8), 'f10': (144, 8), 'f11': (152, 8), 'f12': (160, 8), 'f13': (168, 8), 'f14': (176, 8), 'f15': (184, 8), 'a0': (0, 4), 'a1': (4, 4), 'a2': (8, 4), 'a3': (12, 4), 'a4': (16, 4), 'a5': (20, 4), 'a6': (24, 4), 'a7': (28, 4), 'a8': (32, 4), 'a9': (36, 4), 'a10': (40, 4), 'a11': (44, 4), 'a12': (48, 4), 'a13': (52, 4), 'a14': (56, 4), 'a15': (60, 4), 'nraddr': (384, 8), 'cmstart': (392, 8), 'cmlen': (400, 8), 'ip_at_syscall': (408, 8), 'emnote': (416, 4)} 

        
def convert_f80le_to_f64le(f80):
    # Bool  isInf;
    # Int   bexp, i, j;
    # UChar sign;
    isInf = None
    f64 = [None for i in range(8)]
    toUChar = lambda x: x & 0xff

    sign = toUChar((f80[9] >> 7) & 1);
    bexp = ((f80[9]) << 8) | f80[8];
    bexp &= 0x7FFF;

    """ If the exponent is zero, either we have a zero or a denormal.
      But an extended precision denormal becomes a double precision
      zero, so in either case, just produce the appropriately signed
      zero. """
    if (bexp == 0):
        f64[7] = toUChar(sign << 7);
        f64[6] = f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0;
        return f64;

    """ If the exponent is 7FFF, this is either an Infinity, a SNaN or
      QNaN, as determined by examining bits 62:0, thus:
          10  ... 0    Inf
          10X ... X    SNaN
          11X ... X    QNaN
      where at least one of the Xs is not zero.
    """
    if (bexp == 0x7FFF):
        isInf = (f80[7] & 0x7F) == 0 and f80[6] == 0 and f80[5] == 0 and f80[4] == 0 and f80[3] == 0 and f80[2] == 0 and \
                f80[1] == 0 and f80[0] == 0

        if (isInf):
            if (0 == (f80[7] & 0x80)):
                # goto wierd_NaN;
                f64[7] = (1 << 7) | 0x7F;
                f64[6] = 0xF8;
                f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0;
                return f64;
            """ Produce an appropriately signed infinity:
                S 1-=11 (11)  0-=10 (52)
            """
            f64[7] = toUChar((sign << 7) | 0x7F);
            f64[6] = 0xF0;
            f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0;
            return f64;

        """ So it's either a QNaN or SNaN.  Distinguish by considering
            bit 61.  Note, this destroys all the trailing bits
            (identity?) of the NaN.  IEEE754 doesn't require preserving
            these (it only requires that there be one QNaN value and one
            SNaN value), but x87 does seem to have some ability to
            preserve them.  Anyway, here, the NaN's identity is
            destroyed.  Could be improved. """
        if (f80[7] & 0x40):
            """ QNaN.  Make a canonical QNaN:
                S 1-=11 (11)  1  0-=10 (51) 
            """
            f64[7] = toUChar((sign << 7) | 0x7F);
            f64[6] = 0xF8;
            f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0x00;
        else:
            """ SNaN.  Make a SNaN:
                S 1-=11 (11)  0  1-=11 (51) 
            """
            f64[7] = toUChar((sign << 7) | 0x7F);
            f64[6] = 0xF7;
            f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0xFF;

        return f64;

    """ If it's not a Zero, NaN or Inf, and the integer part (bit 62) is
      zero, the x87 FPU appears to consider the number denormalised
      and converts it to a QNaN. """
    if (0 == (f80[7] & 0x80)):
        # wierd_NaN:
        # /* Strange hardware QNaN:
        # S 1-=11 (11)  1  0-=10 (51) 
        # */
        # /* On a PIII, these QNaNs always appear with sign==1.  I have
        # no idea why. */
        f64[7] = (1 << 7) | 0x7F;
        f64[6] = 0xF8;
        f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0;
        return f64;

    """It's not a zero, denormal, infinity or nan.  So it must be a 
      normalised number.  Rebias the exponent and consider. """
    bexp -= (16383 - 1023);
    if (bexp >= 0x7FF):
        """ It's too big for a double.  Construct an infinity. """
        f64[7] = toUChar((sign << 7) | 0x7F);
        f64[6] = 0xF0;
        f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0;
        return f64;

    if (bexp <= 0):
        """ It's too small for a normalised double.  First construct a
        zero and then see if it can be improved into a denormal.  """
        f64[7] = toUChar(sign << 7);
        f64[6] = f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0;

        if (bexp < -52):
            """ Too small even for a denormal. """
            return f64;

        """ Ok, let's make a denormal.  Note, this is SLOW. */
        /* Copy bits 63, 62, 61, etc of the src mantissa into the dst, 
            indexes 52+bexp, 51+bexp, etc, until k+bexp < 0. */
        /* bexp is in range -52 .. 0 inclusive """
        i = 63
        while (i >= 0):
            j = i - 12 + bexp;
            if (j < 0):
                break
            """ We shouldn't really call vassert from generated code. """
            assert (j >= 0 and j < 52);
            write_bit_array(f64,
                            j,
                            read_bit_array(f80, i));
            i -= 1

        """and now we might have to round ..."""
        if (read_bit_array(f80, 10 + 1 - bexp) == 1):
            if (f64[0] != 0xFF):
                f64[0] += 1;

            elif (f64[0] == 0xFF and f64[1] != 0xFF):
                f64[0] = 0
                f64[1] += 1

            elif (f64[0] == 0xFF and f64[1] == 0xFF and f64[2] != 0xFF):
                f64[0] = 0
                f64[1] = 0
                f64[2] += 1

        return f64;

    """ Ok, it's a normalised number which is representable as a double.
    Copy the exponent and mantissa into place. */
    """

    f64[0] = toUChar((f80[1] >> 3) | (f80[2] << 5));
    f64[1] = toUChar((f80[2] >> 3) | (f80[3] << 5));
    f64[2] = toUChar((f80[3] >> 3) | (f80[4] << 5));
    f64[3] = toUChar((f80[4] >> 3) | (f80[5] << 5));
    f64[4] = toUChar((f80[5] >> 3) | (f80[6] << 5));
    f64[5] = toUChar((f80[6] >> 3) | (f80[7] << 5));

    f64[6] = toUChar(((bexp << 4) & 0xF0) | ((f80[7] >> 3) & 0x0F));

    f64[7] = toUChar((sign << 7) | ((bexp >> 4) & 0x7F));

    """ Now consider any rounding that needs to happen as a result of
    truncating the mantissa. """
    if (f80[1] & 4):

        """ If the bottom bits of f80 are "100 0000 0000", then the
        infinitely precise value is deemed to be mid-way between the
        two closest representable values.  Since we're doing
        round-to-nearest (the default mode), in that case it is the
        bit immediately above which indicates whether we should round
        upwards or not -=1 if 0, we don't.  All that is encapsulated
        in the following simple test. """
        if ((f80[1] & 0xF) == 4 and f80[0] == 0):
            return f64;

        # do_rounding:
        """ Round upwards.  This is a kludge.  Once in every 2^24
        roundings (statistically) the bottom three bytes are all 0xFF
        and so we don't round at all.  Could be improved. """
        if (f64[0] != 0xFF):
            f64[0] += 1;

        elif (f64[0] == 0xFF and f64[1] != 0xFF):
            f64[0] = 0
            f64[1] += 1

        elif (f64[0] == 0xFF and f64[1] == 0xFF and f64[2] != 0xFF):
            f64[0] = 0
            f64[1] = 0
            f64[2] += 1

            """ else we don't round, but we should. """
    return f64
    
    
def Breakpoints():
    count = idc.get_bpt_qty()
    for i in range(0, count):
        ea = idc.get_bpt_ea(i)
        bpt = idaapi.bpt_t()
        if not idaapi.get_bpt(ea, bpt):
            continue
        if bpt.type & idc.BPT_SOFT != 0:
            yield (ea, BPNORMAL, 0, idaapi.get_64bit(ea))
        else:
            bptype = BPNORMAL if bpt.type == idc.BPT_DEFAULT else BPHARDWARE
            hwtype = {
                idc.BPT_WRITE: UE_HARDWARE_WRITE,
                idc.BPT_RDWR: UE_HARDWARE_READWRITE,
                idc.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)


class ArchInfo:
    def __init__(self, ks_arch, register_names):
        self.ks_arch = ks_arch
        self.register_names = register_names
        

class Dump:
    def __init__(self, arch_info, mode):
        self.arch_info = arch_info
        self.mode = mode
        self.mem_mmap = {}

    def Regs_register_names(self):
        return {}
        
    def getRegs(self):
        assert (0)

    def init_segm_mem(self):
        return {}
        
    @staticmethod
    def getDbgMem(ea, size):
        b = b''
        for i in range(0, (size & (~7)), 8):
            b += struct.pack("<Q", idc.get_qword(ea + i))

        for i in range(size & 7):
            b += struct.pack("<B", idaapi.get_byte(ea + (size & (~7)) + i))

        return b

    @staticmethod
    def getSegName(seg, segm):
        count = 0
        h = ''
        while (idaapi.get_segm_name(seg, 0) + h) in segm.keys():
            count += 1
            h = str(count)
        name = idaapi.get_segm_name(seg, 0) + h
        return name

    @staticmethod
    def getDbgMemPage(address, length):
        data = []
        L = 0
        data.append([idaapi.dbg_read_memory(address, 0x400 - (address & 0x3ff)), address, 0x400 - (address & 0x3ff)])
        L += data[-1][2]
        for ea in range((address + 0x400) & (~0x3ff), ((address + length) & (~0x3ff)), 0x400):
            data.append([idaapi.dbg_read_memory(ea, 0x400), ea, 0x400])
            L += 0x400
        data.append([idaapi.dbg_read_memory((address + length) & (~0x3ff), (address + length) & (0x3ff)),
                     (address + length) & (~0x3ff), (address + length) & (0x3ff)])
        L += data[-1][2]
        assert (L == length)
        return data

    @staticmethod
    def reg_ug_str_value2hex(v, f = lambda x : x):
        v = v.replace('`', "").split(" ")
        return f("".join(v[::-1]))
        
    @staticmethod
    def reg_ug_str_value_convert(orig, regvals_dict, f = lambda x : x):
        for rn, rv in regvals_dict.items():
            orig[rn] = f(Dump.reg_ug_str_value2hex(rv))
        return orig
        
    def writeMem(self, binfile):
        register_names = self.Regs_register_names()
        regs = self.getRegs()
        segm = self.init_segm_mem()
        
        # idc.get_bpt_qty() GetBptEA(n):
        nameoffset_p = 0
        dataoffset_p = 0
        all_ab_name = 0


        
        const_show_length = 0
        for n in range(idaapi.get_segm_qty()):
            seg = idaapi.getnseg(n)
            if seg:
                name = Dump.getSegName(seg, segm)
                if const_show_length<len(name):
                    const_show_length = len(name)
                    
        _len = const_show_length//2-2
        print("+ %s +----------------------+--------------------+----------+--------+"%("-"*const_show_length))
        print("| %s |          VA          |        size        |   flag   | status |"%(("-"*_len)+"segment"+"-"*(_len))[:const_show_length])
        print("+ %s +----------------------+--------------------+----------+--------+"%("-"*const_show_length))

        idaapi.refresh_debugger_memory()
        for n in range(idaapi.get_segm_qty()):
            seg = idaapi.getnseg(n)
            if seg:
                name = Dump.getSegName(seg, segm)
                address = seg.start_ea
                length = seg.end_ea - seg.start_ea

                db_data = idaapi.dbg_read_memory(address, length)
                print(("| %-"+str(const_show_length)+"s |  %18x  | %8x -> %5dkb|    %2d    |   ") % (
                name, address, length, length / 1024, seg.flags), end="")
                if db_data:
                    segm[name] = [address, length, db_data]
                    print('ok   |')
                    length = len(db_data)
                    segm[name] = [address, length, db_data]
                else:
                    if (length >= 0x400):
                        print(
                            "war  |\n+ %s +----------------------+--------------------+----------+--------+"%("-"*const_show_length))
                        data = Dump.getDbgMemPage(address, length)
                        data.append([b"", 0, 0])
                        is_unmap = False
                        tmp = b''
                        begin = address
                        fbegin = address
                        fsize = 0
                        for i, d in enumerate(data):
                            db, ea, size = d
                            if is_unmap:
                                if db:  # 0 1
                                    is_unmap = False
                                    begin = ea
                                    tmp = db
                                    print(("| %-"+str(const_show_length)+"s |  %18x  | %8x -> %5dkb|    %2d    |  faild |") % (
                                    name, fbegin, fsize, fsize / 1024, seg.flags))
                                else:  # 0 0
                                    fsize += size
                                    if (i == len(data) - 1):
                                        print(("| %-"+str(const_show_length)+"s |  %18x  | %8x -> %5dkb|    %2d    |  faild |") % (
                                        name, fbegin, fsize, fsize / 1024, seg.flags))
                                    pass
                            else:
                                if db:  # 1 1
                                    is_unmap = False
                                    tmp += db
                                else:  # 1 0
                                    fbegin = ea
                                    fsize = size
                                    is_unmap = True
                                    if tmp:
                                        name = Dump.getSegName(seg, segm)
                                        segm[name] = [begin, len(tmp), tmp]
                                        print(("| %-"+str(const_show_length)+"s |  %18x  | %8x -> %5dkb|    %2d    |   ok   |") % (
                                        name, begin, len(tmp), len(tmp) / 1024, seg.flags))
                                    else:
                                        print(("| %-"+str(const_show_length)+"s |  %18x  | %8x -> %5dkb|    %2d    |  faild |") % (
                                        name, fbegin, fsize, fsize / 1024, seg.flags))
                                        break

                        print("+ %s +----------------------+--------------------+----------+--------+"%("-"*const_show_length))
                    else:
                        print('  faild')
                    continue

        print("+ %s +----------------------+--------------------+----------+--------+"%("-"*const_show_length))
        
        for regName, INT  in regs.items():
            regAddress, size = register_names[regName]
            #print(regName)
            print(" (%-10s IR_offset: %-5d) (val: %32x nb: %2d) " % (regName, regAddress, (INT), size))
            up = None
            if INT < 0:
                up = lambda x: x.lower()
            else:
                up = lambda x: x

            try:
                if size == 1:
                    db_data = struct.pack(up("<B"), INT)
                elif size == 2:
                    db_data = struct.pack(up("<H"), INT)
                elif size == 4:
                    db_data = struct.pack(up("<I"), INT)
                elif size == 8:
                    db_data = struct.pack(up("<Q"), INT)
                elif size == 16:
                    db_data = struct.pack(up("<QQ"), int(INT & 0xffffffffffffffff), int(INT >> 64))
                elif size == 32:
                    db_data = struct.pack(up("<QQQQ"), INT & 0xffffffffffffffff, (INT >> 64) & 0xffffffffffffffff,
                                          (INT >> 128) & 0xffffffffffffffff, INT >> 192)
                else:
                    continue
                segm['registers' + str(regAddress)] = [regAddress, len(db_data), db_data]
            except Exception as e:
                print("-=1-=1-=1-=1- error:", e, regName, hex(INT), size, "-=1-=1-=1-=1- ")

        
        
        def find_segm(va, length, bpt_l):
            global BPNORMAL
            for ea, bptype, hwtype_hwsize, code in bpt_l:
                if va <= ea and ea < va + length:
                    if bptype == BPNORMAL:
                        yield (code, ea - va)

        bpt_list = [i for i in Breakpoints()]
        for name in segm:
            address, length, db_data = segm[name]
            ab_name = (name + '\x00').encode('utf-8')
            all_ab_name += len(ab_name)
            # 将软件断点pass掉
            len_db_data = len(db_data)
            for code, offset in find_segm(address, length, bpt_list):
                bpt_code = db_data[offset:offset+8]
                db_data = db_data[:offset] + struct.pack("<Q", code) + db_data[offset + 8:]
                segm[name] = [address, length, db_data]
                assert (len(db_data) == len_db_data)
                print ("bkp found: address: %x : bpt_mem -%s- <= +%s+"%(address+offset, bpt_code.hex(), struct.pack("<Q", code).hex()))


        for name in segm:
            address, length, db_data = segm[name]
            ab_name = (name + '\x00').encode('utf-8')
            nameoffset = len(segm) * 32 + nameoffset_p
            dataoffset = len(segm) * 32 + all_ab_name + dataoffset_p
            db1 = struct.pack("<Q", nameoffset)
            db2 = struct.pack("<Q", address)
            db3 = struct.pack("<Q", length)
            db4 = struct.pack("<Q", dataoffset)
            self.mem_mmap[name] = [address, dataoffset, length]
            binfile.write(db1)
            binfile.write(db2)
            binfile.write(db3)
            binfile.write(db4)
            nameoffset_p += len(ab_name)
            dataoffset_p += length
        for name in segm:
            address, length, db_data = segm[name]
            ab_name = (name + '\x00').encode('utf-8')
            binfile.write(ab_name)
        for name in segm:
            address, length, db_data = segm[name]
            binfile.write(db_data)

    def init_segm_mem(self):
        return {}


class gdt32():
    def __init__(self, GDT_ADDR_write):
        self.SegDiscriptions = {}
        self.gdt = []
        self.GDT_SIZE = 0
        self.GDT_ADDR_write = GDT_ADDR_write

    @staticmethod
    def create_selector(idx, TI, RPL):  # TI:1 LDT 0:GDT   PRL:最高级:00  11最低级
        to_ret = RPL & 0b11
        to_ret |= TI & 0b1 << 2
        to_ret |= (idx & 0b1111111111111) << 3
        return to_ret

    def segReg2base(self, reg):
        value = self.uc.reg_read(reg)
        try:
            return self.SegDiscriptions[value >> 3]
        except:
            return 0

    @staticmethod
    def create_gdt_entry(base, limit, DPL, S, TYPE, flags):
        to_ret = limit & 0xffff;  # [:16]      limit[:16]
        to_ret |= (base & 0xffffff) << 16;  # [16:40]    base[:24]
        to_ret |= (TYPE & 0xf) << 40;  # TYPE 段的类型特征和存取权限
        to_ret |= (S & 0xb1) << 44;  # S: 如果s=0 这是一个系统段 1 是普通代码段或数据段
        to_ret |= (DPL & 0xb11) << 45;  # DPL 描述符特权级 0~3
        to_ret |= (1 & 0xb1) << 47;  # 1
        to_ret |= ((limit >> 16) & 0xf) << 48;  # [48:52]    limit[16:20]: 存放段最后一个内存单元的偏移量
        to_ret |= (flags & 0xf) << 52;  # [52:56]    flag: Granularity<<3|Default_Big<<2|Reserved_0<<1|Sys (各1bit )如果G=0那么段大小为0~1MB, G=1段大小为4KB~4GB  D或B 这个我还没搞懂 以后再说只要知道32位访问为1 AVL: linux忽略这个
        to_ret |= ((base >> 24) & 0xff) << 56;  # [56:64]    base[24:32]
        return struct.pack('<Q', to_ret)

    def addSegDiscription(self, GDT_index, base, limit, DPL, S, TYPE, flags):
        seg_selector = GDT_index
        seg_selector >>= 3
        if seg_selector >= self.GDT_SIZE:
            for c in range(seg_selector - self.GDT_SIZE + 1):
                self.gdt.append(None)
            self.GDT_SIZE = seg_selector
        self.gdt[seg_selector] = gdt32.create_gdt_entry(base, limit, DPL, S, TYPE, flags)

    def get_gdt(self):
        ret = b''
        for tab in self.gdt:
            if (tab):
                ret += tab
            else:
                ret += b"\x00\x00\x00\x00\x00\x00\x00\x00"
        return {"gdt_table": [self.GDT_ADDR_write, len(ret), ret]}


class AMD64_dump(Dump):
    def __init__(self, arch_info, mode):
        Dump.__init__(self, arch_info, mode)
        self.reg_vals = AMD64_dump.windbg_get_regs()
        
        
    def Regs_register_names(self):
        register_names = self.arch_info.register_names
        fpreg_off, fpreg_size = register_names["fpreg"]
        self.arch_info.register_names.pop("fpreg")
        for i in range(8):
            register_names["st%d" % i] = (fpreg_off + i * 8, 8)
        return register_names
        
    @staticmethod
    def _windbg_get_sreg_desc():
        # min 0 max 80
        cmdline = "dg 0 80"
        b, res = ida_dbg.send_dbg_command(cmdline)
        result = []
        for desc in res.split("\n"):
            # print(desc)
            regex = re.match(r'(?P<sel>[a-f0-9]+) (?P<base>[a-f0-9`]+) (?P<limit>[a-f0-9`]+)[\w| ]+ (?P<flags>[a-f0-9]+)', desc)
            if regex:
                reg = regex.groupdict()
                # print(reg)
                result.append(reg)
        return result
    
    
    @staticmethod
    def _windbg_get_teb64_gs_const():
        # min 0 max 80
        cmdline = "!teb"
        b, res = ida_dbg.send_dbg_command(cmdline)
        for line in res.split("\n"):
            # print(desc)
            regex = re.match(r'Wow64 TEB at (?P<teb>[a-f0-9]+)', line)
            if regex:
                teb = regex.groupdict()
                return int(teb["teb"], 16)
        return 0
        
    """
    https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/r--registers-
    WINDBG>rm?
           1 - Integer state (32-bit) or
           2 - Integer state (64-bit), 64-bit takes precedence
           4 - Floating-point state
           8 - Segment registers
          10 - MMX registers
          20 - Debug registers and, in kernel, CR4
          40 - SSE XMM registers
         200 - AVX YMM registers
         400 - AVX YMM Integer registers
         800 - AVX XMM Integer registers
        1000 - AVX-512 ZMM registers
        2000 - AVX-512 ZMM Integer registers
        4000 - AVX-512 Opmask registers
        8000 - Shadow Stack Registers
    """
    @staticmethod
    def _windbg_get_regs(mask):
        cmdline = "rm{:x}".format(mask)
        b, res = ida_dbg.send_dbg_command(cmdline)
        cmdline = "r"
        b, res = ida_dbg.send_dbg_command(cmdline)
        res = " ".join(res.split("\n")[:-2])
        # print(text)
        regex = re.findall(r'(?P<rn>[a-z]+[a-z0-9]*)=(?P<rv>[a-f0-9]+)', res)
        if regex:
            return dict(regex)
          
    @staticmethod  
    def _windbg_get_regs_same(regs, fms = "uq"):
        cmdline = "r {:}:type".format(":type, ".join(regs)).replace("type", fms)
        
        b, res = ida_dbg.send_dbg_command(cmdline)
        text = " ".join(res.split("\n")[:6])
        # print(text)
        regex = re.findall(r'(?P<rn>[a-z0-9]+)=(?P<rv>[a-f0-9 ]+)', text)
        if regex:
            reg = dict(regex)
            return reg
        print(cmdline)
        raise "error"


    @staticmethod   
    def _get_fpu_regs():
        fps = {}
        for rname in ["st%d"%n for n in range(8)]:
            cmdline = "r "+ rname
            b, res = ida_dbg.send_dbg_command(cmdline)
            regex = re.search(r'\((?P<sym>[a-f0-9]{1}):(?P<hi>[a-f0-9]{4}):(?P<lo>[a-f0-9]{16})\)', res, flags = re.DOTALL)
            if regex:
                val = regex.groupdict()
                val = val["hi"]+val["lo"]
                f80_l = bytes.fromhex(val)[::-1]
                f80 = int(val, 16);
                f64 = 0
                f64_l = convert_f80le_to_f64le(f80_l)
                for i in range(8):
                    f64 = (f64 << 8) | f64_l[7 - i]
                print(rname, hex(f80), hex(f64))
                fps[rname] = "%x"%f64
            else:
                print(cmdline, res, "error")
        return fps


    @staticmethod   
    def windbg_get_regs():
        general_state = AMD64_dump._windbg_get_regs(2)
        # fpcw fpsw fptw
        floating_state_fp = AMD64_dump._windbg_get_regs(4)
        # st0-st7
        floating_state_st = AMD64_dump._get_fpu_regs()
        # sregs cs ss ds es fs gs efl
        segment_registers = AMD64_dump._windbg_get_regs(8)
        # sreg_desc
        segment_desc = AMD64_dump._windbg_get_sreg_desc()
        # Ymm0-15
        AVX_state_st = AMD64_dump._windbg_get_regs_same(["ymm%s"%n for n in range(16)])
        # mm0-7
        MMX_registers = AMD64_dump._windbg_get_regs_same(["mm%s"%n for n in range(8)])
        # other DF mxcsr
        other_regs = AMD64_dump._windbg_get_regs_same(["df", "mxcsr"], fms = 'ud')
        
        return {"general":general_state, "fp":floating_state_fp, "st":floating_state_st, "sregs":segment_registers, "segm_desc":segment_desc, "avx":AVX_state_st, "mmx": MMX_registers, "other" : other_regs }
    
    
    
    
    def getRegs(self):
        reg_vals = {}
        f = lambda x : int(x, 16)
        reg_vals = Dump.reg_ug_str_value_convert(reg_vals, self.reg_vals["general"], f = f)
        reg_vals = Dump.reg_ug_str_value_convert(reg_vals, self.reg_vals["st"], f = f)
        reg_vals = Dump.reg_ug_str_value_convert(reg_vals, self.reg_vals["fp"], f = f)
        reg_vals = Dump.reg_ug_str_value_convert(reg_vals, self.reg_vals["sregs"], f = f)
        reg_vals = Dump.reg_ug_str_value_convert(reg_vals, self.reg_vals["avx"], f = f)
        reg_vals = Dump.reg_ug_str_value_convert(reg_vals, self.reg_vals["other"], f = f)
        
        # for rn, v in reg_vals.items():
        #    print(rn, v )
        

        def getftop(tag):
            # fld
            # dbgtop 0   7   6
            # dbgtag 00  80  0xc0
            # IRftop 8   7   6  
            dbgtop = (tag >> 11) & 0b111
            return  8 if dbgtop == 0 else dbgtop


        def getfpu_tags(tag):
            re = 0
            for i in range(8):
                f = ((tag >> i)&0b1) << (8 * i)
                re = (re) | f
            return re
            

        reg_vals.pop("iopl")
        # reg_vals.pop("efl")
        """
        X86_EFLAGS_CF = 1 << 0
        X86_EFLAGS_FIXED = 1 << 1
        X86_EFLAGS_PF = 1 << 2
        X86_EFLAGS_AF = 1 << 4
        X86_EFLAGS_ZF = 1 << 6
        X86_EFLAGS_SF = 1 << 7
        X86_EFLAGS_TF = 1 << 8
        X86_EFLAGS_IF = 1 << 9
        X86_EFLAGS_DF = 1 << 10
        X86_EFLAGS_OF = 1 << 11
        X86_EFLAGS_IOPL = 1 << 12
        X86_EFLAGS_IOPL_MASK = 3 << 12
        X86_EFLAGS_NT = 1 << 14
        X86_EFLAGS_RF = 1 << 16
        X86_EFLAGS_VM = 1 << 17
        X86_EFLAGS_AC = 1 << 18
        X86_EFLAGS_VIF = 1 << 19
        X86_EFLAGS_VIP = 1 << 20
        X86_EFLAGS_ID = 1 << 21
        """
        efl = reg_vals['efl']
        reg_vals.pop("efl")
        
        reg_vals["cc_op"] = X86G_CC_OP_COPY
        reg_vals["cc_dep1"] = efl
        reg_vals["cc_dep2"] = 0
        reg_vals["cc_ndep"] = 0
        
        mxcsr = reg_vals['mxcsr']
        reg_vals.pop("mxcsr")
        
        reg_vals["idflag"] = efl & (1 << 21)
        reg_vals["acflag"] = efl & (1 << 18)
        
        reg_vals['fptag'] = getfpu_tags(reg_vals['fptw'])
        reg_vals.pop("fptw")
        
        
        reg_vals['ftop'] = getftop(reg_vals['fpsw'])
        reg_vals.pop("fpsw")
        
        
        reg_vals['fpround'] = (reg_vals['fpcw'] >> 10) & 0b11
        reg_vals.pop("fpcw")
        
        reg_vals["sseround"] = (mxcsr >> 13) & 0b11
        
        reg_vals["gdt"] = GDT_MAP_ADDR
        
        reg_vals["dflag"] = -1 if reg_vals["df"] else 0x1
        reg_vals.pop("df")
        
        
        
        reg_vals['gs_const'] = AMD64_dump._windbg_get_teb64_gs_const()
        reg_vals['fs_const'] = 0
        
        print(reg_vals)
        return reg_vals

    def init_segm_mem(self):
        gdt = gdt32(GDT_MAP_ADDR)
        descs = self.reg_vals["segm_desc"]
        DPL = 1
        S = 0
        TYPE = 0
        for desc in descs:
            descs = Dump.reg_ug_str_value_convert({}, desc, lambda x:int(x, 16))
            sel, base, limit, flags = descs["sel"], descs["base"], descs["limit"], descs["flags"]
            print(".sel :{:x}  .base :0x{:x}  .limit :{:x}  .flags :{:x}".format(sel, base, limit, flags))
            Granularity = (limit & 0xfff) == 0xfff
            Default_Big = 0
            Reserved_0 = 0
            Sys = 0
            flags = Granularity<<3|Default_Big<<2|Reserved_0<<1|Sys 
            gdt.addSegDiscription(sel, base, limit, DPL, S, TYPE, flags=flags)
        
        return gdt.get_gdt()
        
        
        
        
def get_hardware_mode():
    (arch, mode) = (None, None)
    info = idaapi.get_inf_structure()
    # heuristically detect hardware setup
    info = idaapi.get_inf_structure()
    dumper = None
    try:
        cpuname = info.procname.lower()
    except:
        cpuname = info.procName.lower()

    try:
        # since IDA7 beta 3 (170724) renamed inf.mf -> is_be()/set_be()
        is_be = idaapi.cvar.inf.is_be()
    except:
        # older IDA versions
        is_be = idaapi.cvar.inf.mf
    # print("Keypatch BIG_ENDIAN = %s" %is_be)
    # print("ArchInfo(\n    ", arch.register_names,",\n    ", arch.registers, ")")
    if cpuname == "metapc":
        if info.is_64bit():
            #arch = archinfo.ArchAMD64()
            arch = ArchInfo( keystone.KS_ARCH_X86, amd64_rnames)
            mode = keystone.KS_MODE_64
            dumper = AMD64_dump(arch, mode)
        elif info.is_32bit():
            #arch = archinfo.ArchX86()
            arch = ArchInfo( keystone.KS_ARCH_X86, amd64_rnames)
            mode = keystone.KS_MODE_32
            dumper = AMD64_dump(arch, mode)
        else:
            #arch = archinfo.ArchNotFound()
            arch = ArchInfo(keystone.KS_ARCH_X86, {}, {})
            mode = keystone.KS_MODE_16

    elif cpuname.startswith("ppc"):
        if info.is_64bit():
            # arch = archinfo.ArchPPC64()
            arch = ArchInfo(keystone.KS_ARCH_PPC, ppc_rnames)
            mode = keystone.KS_MODE_PPC64
        else:
            # arch = archinfo.ArchPPC32()
            arch = ArchInfo(keystone.KS_ARCH_PPC, ppc_rnames)
            mode = keystone.KS_MODE_PPC32
        if cpuname == "ppc":
            # do not support Little Endian mode for PPC
            mode += keystone.KS_MODE_BIG_ENDIAN

    elif cpuname.startswith("mips"):
        if info.is_64bit():
            # arch = archinfo.ArchMIPS64()
            arch = ArchInfo(keystone.KS_ARCH_MIPS, mips_rnames)
            mode = keystone.KS_MODE_MIPS64
        else:
            # arch = archinfo.ArchMIPS32()
            arch = ArchInfo(keystone.KS_ARCH_MIPS, mips_rnames)
            mode = keystone.KS_MODE_MIPS32
    elif cpuname.startswith("systemz") or cpuname.startswith("s390x"):
        # arch = archinfo.ArchS390X()
        arch = ArchInfo(keystone.KS_ARCH_S390, s390_rnames)
        mode = keystone.KS_MODE_BIG_ENDIAN

    return (dumper, arch, mode)


class memdumper(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "This is a comment bin"

    help = "This is help bin"
    wanted_name = "Tr memdumper"
    wanted_hotkey = "Shift-3"

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        dump_class_obj, arch, mode = get_hardware_mode()
        if not idaapi.is_debugger_on():
            idaapi.warning("tr : Please run the process first!")
            return
        if idaapi.get_process_state() != -1:
            idaapi.warning("tr : Please suspend the debugger first!")
            return
        
        bin_path = ida_loader.get_path(ida_loader.PATH_TYPE_CMD)
        bin_path = bin_path
        binfile = open(bin_path + '.dump', 'wb+')
        dump_class_obj.writeMem(binfile)
        binfile.close()
        print('dump success: ', bin_path + '.dump')

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


def PLUGIN_ENTRY():
    return memdumper()


repository: mahmoudimus/.idapro
file: plugins/keybinder.py
wanted_name: Keybinder
comment: A plugin to enable Emacs-like key sequences and key chord sequences in IDA
created_at: 2025-02-16 21:33:20+00:00
pushed_at: 2025-04-09 18:15:30+00:00
url:
https://github.com/mahmoudimus/.idapro/blob/e0bf0d58fb6de4eb6af0b87ffbd869d8c7deef08/plugins/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.set_shortcut("ChooserEdit", "Meta+E, C")
        self.set_shortcut("EditEnum", "Meta+E, N")
        self.set_shortcut("EditSegment", "Meta+E, S")
        self.set_shortcut("ExpandStruct", "Meta+E, E")
        self.set_shortcut("SetSegmentRegister", "Meta+E, R")

        self.set_shortcut("Edit/Plugins/IDA Patcher", "Meta+P, P")
        self.set_shortcut("Edit/Plugins/Signature Maker", "Meta+P, S")

    def set_shortcut(self, action_name, shortcut_key):
        try:
            self.ACTIONS[action_name].setShortcut(shortcut_key)
        except KeyError as ke:
            print(f"{action_name} not found, unable to set shortcut {shortcut_key}. is action loaded?")
            
    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: shuakabane/stelftools
file: ida_stelftools.py
description: cross-architecture static library detector for IoT malware
wanted_name: Stelftools
comment: toolchain config file
created_at: 2020-05-30 06:20:34+00:00
pushed_at: 2023-11-06 16:23:27+00:00
url:
https://github.com/shuakabane/stelftools/blob/817f1d534f0d93f36e98c3b11131de6eab94acc7/ida_stelftools.py

content:
#! /usr/bin/env python3

import idc
import idaapi
import idautils
import ida_nalt
from shims import ida_shims

import os
import time
import json

from func_ident import *
from libfunc_info_create import *

def overwrite_func_label(match_info):
    same_name_idx = 0
    FUNC_LIB = 0x00000004
    for ea, f_info in match_info.items():
        if ida_shims.set_name(ea, f_info['names']):
            ida_shims.set_func_flags(ea, FUNC_LIB)
        else:
            same_name_idx += 1
            ida_shims.set_name(ea, f_info['names'] + '_' + str(same_name_idx))
            ida_shims.set_func_flags(ea, FUNC_LIB)

def arch_pattern_length(arch):
    length = 0
    if arch in ['aarch64']:
        length = 9
    elif arch in ['arm', \
            'armv4eb', 'armv4l', 'armv4tl', \
            'armv5-eabi', 'armv5l', \
            'armv6-eabihf', 'armv6l', \
            'armv7-eabihf', 'armv7l', 'armv7m' \
            ]:
        length = 4
    elif arch in ['x86', 'x86-i686', 'i386', 'i486', 'i586', 'i686', 'x86-core2', '80386']:
        length = 4
    elif arch in ['mips', 'mips32', 'mipsel', 'mips32el']:
        length = 9
    elif arch in ['mips64', 'mips64el']:
        length = 9
    elif arch in ['ppc', 'powerpc', 'powerpc-440fp', 'powerpc-e300c3', 'powerpc-e500mc']:
        length = 8
    elif arch in ['ppc64', 'powerpc64', 'powerpc64-e6500', 'powerpc64-pwoer8']:
        length = 16
    elif arch in ['risc-v', 'riscv', 'risc-v-32', 'risc-v-64']:
        length = 9
    elif arch in ['sparc', 'sparc64']:
        length = 9
    elif arch in ['x86_64', 'x86-64', 'x86-64-core-i7']:
        length = 8
    elif arch in ['arc']:
        length = 4
    elif arch in ['sh4']:
        length = 4
    elif arch in ['m68k', 'm68k-q800', 'm68k-mcf', 'm68k-mcf5208', 'm68000']:
        length = 4
    return length

def func_ident(tc_cfg_path):
    tc_cfg_info = {}
    with open(tc_cfg_path) as cfg_fp:
        tc_cfg_info = json.load(cfg_fp)

    # load config file
    target_path      = ida_nalt.get_input_file_path()
    target_arch      = tc_cfg_info['arch']
    yara_path        = STELFTOOLS_PATH + tc_cfg_info['yara_path']
    compiler_path    = tc_cfg_info['compiler_path']
    alias_list_path  = STELFTOOLS_PATH + tc_cfg_info['alias_list_path']
    depend_list_path = STELFTOOLS_PATH + tc_cfg_info['dependency_list_path']

    alias_flag = False
    linkorder_flag = False
    depend_flag = False

    if os.path.exists(alias_list_path) == True:
        alias_flag = True
    if os.path.exists(compiler_path) == True:
        linkorder_flag = True
    if os.path.exists(depend_list_path) == True:
        depend_flag = True

    print("delete alias :", alias_flag)
    print("function name identification by link order :", linkorder_flag)
    print("function name identification by dependency :", depend_flag)

    start_rule_length = arch_pattern_length(target_arch)

    target = get_target_fp(target_path) # target
    bin_target = target.read()
    # get symbol table information
    try:
        symtab_info = get_symtab_info_by_capstone(target_path) # get vaddr
    except exceptions.ELFParseError as e:
        symtab_info = get_symtab_info_by_reaelf(target_path) # get vaddr
    base_vaddr = symtab_info[0][2]
    # get function call information
    call_map, top_inst_addr, bot_inst_addr  = get_func_addr(target, base_vaddr)
    # get target file size
    target_size = int(target.seek(0, os.SEEK_END))
    # do matching
    for _length in range(start_rule_length, 0, -1):
        yara_rules, risc_v_flag = get_yara_rule(yara_path, 'func', _length) # rule
        # matching
        _match_res = yara_matching(yara_rules, target) # do matching
        # format matching result
        _functions = format_match_res(_match_res, symtab_info, risc_v_flag)
        #print('---')
        #for _addr in sorted(_functions.keys()):
        #    print('dbg', _length, ':', hex(_addr), _functions[_addr])
        if _length == start_rule_length:
            #functions = marge_nomatch_functions(_functions, call_map, base_vaddr)
            functions = marge_nomatch_functions(_functions, call_map)
        else:
            functions = marge_functions(functions, _functions)
    # delete mismatch signature
    functions = del_mismatch(functions)
    # close target fp
    target.close()

    # function name identification
    # set function alias list
    alias_list = []
    if alias_flag == True:
        alias_list = get_alias_list(alias_list_path)
    # delete alias function name
    functions = del_alias(functions, alias_list)
    #identifying the function name
    id_loop_count = 0
    exclude_func_list = []
    # identifying the function name based on the link order
    while True:
        # identifying the function name based on the link order
        id_l_num = 0
        if linkorder_flag == True:
            functions, id_l_num, link_order_list = id_func_name_for_linkorder(\
                    functions, target_path, compiler_path, \
                    alias_list, call_map, id_loop_count, exclude_func_list \
                    )
        # identifying the function name based on the dependency
        id_d_num = 0
        if depend_flag == True:
            functions, id_d_num = id_func_name_for_depend( \
                    functions, call_map, depend_list_path, alias_list \
                    )
        if id_l_num == id_d_num == 0:
            break
        id_loop_count += 1

    # save checked target dump info
    targets_info = {'name' : target_path, \
            'functions' : functions, \
            'size' : target_size, \
            'base_vaddr' : base_vaddr, \
            }
    match_info = output(targets_info, target_path, 'ida') # output result
    return match_info

def stelftools_create(tc_path, tc_name, arch, tc_compiler_path):
    print('start stelftools : create toolchain items -->')
    start = time.time()
    yara_rule_path = mkrule(tc_path, tc_name)
    depend_list_path, alias_list_path = mkother(tc_path, tc_name)
    #print(yara_rule_path)
    #print(depend_list_path)
    #print(alias_list_path)

    create_toolchain_cfg_file( \
            tc_name, \
            arch, \
            yara_rule_path, \
            tc_compiler_path, \
            alias_list_path, \
            depend_list_path \
            )

    end = time.time()
    print("<-- finish stelftools %.2f seconds" % (end-start))

def stelftools_ident(tc_cfg_path):
    print('start stelftools : function identification -->')
    start = time.time()
    match_info = func_ident(tc_cfg_path)
    overwrite_func_label(match_info)
    end = time.time()
    print("<-- finish stelftools %.2f seconds" % (end-start))

def stelftools_produce(arg=None):
    tc_name = ida_shims.ask_ident("Please input toolchain name", "name")
    tc_compiler_path = ida_shims.ask_file(0, "*", "Please input toolchain compiler path")
    _path = tc_compiler_path.split('/')
    tc_path = "/".join( _path[0:(len(_path)-2)])
    arch = ida_shims.ask_ident("Please input toolchain architecture", "arch")
    #print(tc_name)
    #print(tc_path)
    #print(tc_compiler_path)
    #print(arch)
    if tc_path and tc_name and arch and tc_compiler_path:
        stelftools_create(tc_path, tc_name, arch, tc_compiler_path)

def stelftools_load(arg=None):
    tc_cfg_path = ida_shims.ask_file(0, "*.json", "Load toolchain config file")
    if tc_cfg_path:
        stelftools_ident(tc_cfg_path)

try:
    class ProduceStelftoolsAction(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)
        def activate(self, ctx):
            stelftools_produce()
            return 1
        def update(self, ctx):
            return idaapi.AST_ENABLE_ALWAYS
    class LoadStelftoolsAction(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)
        def activate(self, ctx):
            stelftools_load()
            return 1
        def update(self, ctx):
            return idaapi.AST_ENABLE_ALWAYS
except AttributeError:
    pass

class StelftoolsPlugin(idaapi.plugin_t):
    flags = 0
    comment = "toolchain config file"
    help = ""
    wanted_name = "Stelftools"
    wanted_hotkey = ""
    produce_action_name = 'producestelftools:action'
    load_action_name = 'loadstelftools:action'
    menu_name = "Stelftools toolchain config file..."
    produce_tooltip = "Create stelftools rules, etc."
    load_tooltip = "Load stelftools toolchain config file."
    menu_tab = 'File/'
    menu_context = []

    def init(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            produce_desc = idaapi.action_desc_t(self.produce_action_name,
                                                self.menu_name,
                                                ProduceStelftoolsAction(),
                                                self.wanted_hotkey,
                                                self.produce_tooltip,
                                                150)

            load_desc = idaapi.action_desc_t(self.load_action_name,
                                             self.menu_name,
                                             LoadStelftoolsAction(),
                                             self.wanted_hotkey,
                                             self.load_tooltip,
                                             150)

            idaapi.register_action(produce_desc)
            idaapi.register_action(load_desc)

            idaapi.attach_action_to_menu(
                os.path.join(self.menu_tab, 'Produce file/'),
                self.produce_action_name,
                idaapi.SETMENU_APP)
            idaapi.attach_action_to_menu(
                os.path.join(self.menu_tab, 'Load file/'),
                self.load_action_name,
                idaapi.SETMENU_APP)
        else:
            self.menu_context.append(
                idaapi.add_menu_item(
                    os.path.join(self.menu_tab, 'Produce file/'),
                    "Stelftools toolchain path...", "", 0, stelftools_produce, (None,)))

            self.menu_context.append(
                idaapi.add_menu_item(
                    os.path.join(self.menu_tab, 'Load file/'),
                    "Stelftools toolchain config file...", "", 0, stelftools_load, (None,)))

        return idaapi.PLUGIN_KEEP

    def term(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            idaapi.detach_action_from_menu(
                self.menu_tab, self.produce_action_name)
            idaapi.detach_action_from_menu(
                self.menu_tab, self.load_action_name)
        else:
            if self.menu_context is not None:
                idaapi.del_menu_item(self.menu_context)
        return None

    def run(self, arg):
        return None

    def stelftools_script(self):
        idaapi.IDAPython_ExecScript(self.script, globals())

def PLUGIN_ENTRY():
    return StelftoolsPlugin()


repository: AntoineBlaud/TheCodexRebirth
file: plugins/tenet/integration/ida_loader.py
description: Taint Analysis Engine and Trace Exploration : Overcome Obfuscation
wanted_name: Tenet
comment: Trace Explorer
created_at: 2023-09-18 14:33:05+00:00
pushed_at: 2025-03-24 20:43:00+00:00
url:
https://github.com/AntoineBlaud/TheCodexRebirth/blob/6d04f320b582bc1530d13b604475288e34443914/plugins/tenet/integration/ida_loader.py

content:
import time
import logging

import ida_idaapi
import ida_kernwin

from tenet.util.log import pmsg
from tenet.integration.ida_integration import TenetIDA

logger = logging.getLogger("Tenet.IDA.Loader")

# ------------------------------------------------------------------------------
# IDA Plugin Loader
# ------------------------------------------------------------------------------
#
#    This file contains a stub 'plugin' class for the plugin as required by
#    IDA Pro. Practically speaking, there should be little to *no* logic placed
#    in this file because it is disassembler-specific.
#
#    When IDA Pro is starting up, it will import all python files placed in its
#    root plugin folder. It will then attempt to call PLUGIN_ENTRY() on each of
#    the imported 'plugins'. We import PLUGIN_ENTRY into tenet_plugin.py
#    so that IDA can see it.
#
#    PLUGIN_ENTRY() is expected to return a plugin object (TenetIDAPlugin)
#    derived from ida_idaapi.plugin_t. IDA will register the plugin, and
#    interface with the plugin object to load / unload the plugin at certain
#    times, per its configuration (flags, hotkeys).
#
#    There should be virtually no reason for you to modify this file.
#


def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython Plugins.
    """
    return TenetIDAPlugin()


class TenetIDAPlugin(ida_idaapi.plugin_t):
    """
    The IDA plugin stub for Tenet.
    """

    #
    # Plugin flags:
    # - PLUGIN_MOD:  The plugin may modify the database
    # - PLUGIN_PROC: Load/unload the plugin when an IDB opens / closes
    # - PLUGIN_HIDE: Hide the plugin from the IDA plugin menu
    #

    flags = ida_idaapi.PLUGIN_PROC | ida_idaapi.PLUGIN_MOD | ida_idaapi.PLUGIN_HIDE
    comment = "Trace Explorer"
    help = ""
    wanted_name = "Tenet"
    wanted_hotkey = ""

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

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

        try:
            self.core = TenetIDA()
            self.core.load()
        except Exception as e:
            pmsg("Failed to initialize Tenet")
            logger.exception("Exception details:")

        #
        # we return PLUGIN_KEEP here regardless of success/failure. this is to
        # ensure that IDA will not try to reload the plugin again.
        #

        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        """
        This is called by IDA when this file is loaded as a script.
        """
        ida_kernwin.warning("Tenet cannot be run as a script in IDA.")

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

        start = time.time()
        logger.debug("-" * 50)

        try:
            self.core.unload()
            self.core = None
        except Exception as e:
            logger.exception("Failed to cleanly unload Tenet from IDA.")

        end = time.time()
        logger.debug("-" * 50)

        logger.debug("IDA term done... (%.3f seconds...)" % (end - start))


repository: JPCERTCC/AutoYara4FLIRT
file: CLI_AutoYara/_Tools/AutoYara4FLIRT.py
wanted_name: AutoYara4FLIRT
created_at: 2023-05-29 07:58:21+00:00
pushed_at: 2023-12-21 03:26:39+00:00
url:
https://github.com/JPCERTCC/AutoYara4FLIRT/blob/f011836ba7c402a9155a52ef861ebddd8b7f4a29/CLI_AutoYara/_Tools/AutoYara4FLIRT.py

content:
#---------------------------------------------------------------------------------
# TOOL Name: AutoYara4FLIRT
# This tool has been tested in the following versions and environments.
# IDA Pro Version 8.2, Windows10

#---------------------------------------------------------------------------------
__date__ = "20230426"
__version__ = "1.0"
__author__ = "Yuma Masubuchi JPCERT/CC"
#---------------------------------------------------------------------------------
# Import module
#---------------------------------------------------------------------------------
from idautils import *
from idaapi import *
from idc import *
import idaapi
import ida_kernwin
import ida_nalt
import os
import re
#---------------------------------------------------------------------------------

#---------------------------------------------------------------------------------
# Config
#---------------------------------------------------------------------------------
class ConfigVar:
    """
    A number that determines whethre the hunt is acceptable or not based on how many of
    the byte sequences created match by AutoYara4FLIRT.
    """
    matchNumber = 4
    
    """
    To hit the various libraries, this tool extracts a sequence of bytes from among all
    functions divided into quarters.A number determines how many byte sequences to use
    for each quarter.
    ie 
    """
    NumFunc_eachPart = 2
    
    """
    In yara conditions, the file size to be hit is detemined in Mega Bytes.
    """
    LessthanSize_MB = 6

    """
    Output filename
    """
    YaraFileName = "Rule_AutoYara.yara"

    """
    Other
    """
    Other_RuleName = "AutoYaraRule"
    Other_Description = "Hunting no stripped ELF Binaries by AutoYara4FLIRT JPCERT/CC"
    Other_Auther = "AutoYara4FLIRT"
    Other_Usage = "Hunting"

    # On command prompt by use of idat.exe or idat64.exe
    BATCH_MODE = True

    # Target only Sub_* Functions
    TARGET_SUBFUNC = True
#---------------------------------------------------------------------------------
  
#---------------------------------------------------------------------------------


class YaraCompo:
    GENERATED_YARA = ""
    TEMPLATE = '''

import "[MODULE]"

rule [RULENAME] {
    meta:
        description = "[DESCRIPTION]"
        author = "[AUTHOR]"
        rule_usage = "[USAGE]"
        hash = "[HASH]"
    
    strings:
[STRINGS]
    condition:
        [CONDITION]
}
    '''
    MODULE = ""
    #RULENAME = ConfigVar.Other_RuleName
    DESCRIPTION = ConfigVar.Other_Description
    AUTHOR = ConfigVar.Other_Auther
    USAGE = ConfigVar.Other_Usage
    HASH = ""
    STRINGS = ""
    CONDITION = ""

    def set_RULENAME(self, in_Rulename):
        self.RULENAME = in_Rulename

    def set_MODULE(self, in_module):
        self.MODULE = in_module

    def set_HASH(self, in_hash):
        self.HASH = in_hash

    def set_STRINGS(self, in_strings):
        self.STRINGS = in_strings

    def set_CONDITION(self, in_condition):
        self.CONDITION = in_condition

    def print_all_compo(self):
        print("[*] INFO MODULE: {}".format(self.MODULE))
        print("[*] INFO RULENAME: {}".format(self.RULENAME))
        print("[*] INFO DESCRIPTION: {}".format(self.DESCRIPTION))
        print("[*] INFO AUTHOR: {}".format(self.AUTHOR))
        print("[*] INFO USAGE: {}".format(self.USAGE))
        print("[*] INFO HASH: {}".format(self.HASH))
        print("[*] INFO STRINGS:\n{}".format(self.STRINGS))
        print("[*] INFO CONDITION:\n{}".format(self.CONDITION))

    def print_generated_yara_text(self):
        print("[*] GENERATED_YARA:\n{}".format(self.GENERATED_YARA))

    def get_generated_yara_text(self):
        return self.GENERATED_YARA

    def generate_yara_text(self):
        self.GENERATED_YARA = self.TEMPLATE 
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[MODULE]", self.MODULE)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[RULENAME]", self.RULENAME)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[DESCRIPTION]", self.DESCRIPTION)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[AUTHOR]", self.AUTHOR)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[USAGE]", self.USAGE)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[HASH]", self.HASH)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[STRINGS]", self.STRINGS)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[MODULE]", self.MODULE)
        self.GENERATED_YARA = self.GENERATED_YARA.replace("[CONDITION]", self.CONDITION)
         
   
class YaraModule():
    import_module = ""
    
    def set_import_module(self):
        fileformat = idaapi.get_file_type_name()
        if "ELF" in fileformat:
            self.import_module = "elf"
        elif "PE" in fileformat:
            self.import_module = "pe"
        else:
            self.import_module == "elf" # default
            print("[*] Warning: The file is not PE or ELF format. please check the file format.")
        
    def get_import_module(self):
        return self.import_module
        
        
class YaraHash():
    sha256hash = ""
    
    def Convert_str_from_Hex_bytes(self, bytes):
        for byte in bytes:
            self.sha256hash += '{:02X}'.format(byte)
    
    def set_hash_sha256(self):
        sha256_bytes = ida_nalt.retrieve_input_file_sha256()
        if sha256_bytes != None:
            self.Convert_str_from_Hex_bytes(sha256_bytes)
        else:
            self.sha256hash = "NoFile"

    def get_hash_sha256(self):
        return self.sha256hash


class IDAPythoOperandCtrl:
    opTypeDict = { 0:"NoOp",  # No Operand
                   1:"GER",   # General Register 
                   2:"DATA",  # Direct Memory Reference (DATA)
                   3:"MEM",   # Memory Ref [Base Reg + Index Reg]
                   4:"VAR",   # Memory Reg [Base Reg + Index Reg + Displacement]
                   5:"IMM",   # Immediate Value
                   6:"FADDR", # Immediate Far Address (CODE)
                   7:"NADDR", # Immediate Near Address (CODE)
                   8:"PSY1",  # processor specific type
                   9:"PSY2",  # processor specific type
                   10:"PSY3", # processor specific type
                   11:"PSY4", # processor specific type
                   12:"PSY5", # processor specific type
                   13:"PSY6", # processor specific type
                 }

    ReplaceNumOperand = [2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13]


class YaraStrings:
    yara_strings = ''

    instructions_offset = 2 
    convert_flag_offset = 0
    inst_length_offset = 1
    bytes_offset = 2
    instruction_offset = 3
    function_all_info = []
    extracted_most_long_inst = []
    yara_bytes = []

    sp8 = ' ' * 8
    func_comment01 = sp8 + '/* Function Address: '
    func_comment02 = sp8 + '*/\n'
    func_bytes01 = sp8 + '$func'
    func_bytes02 = ' = { '
    func_bytes03 = '}\n\n'
 
    def Convert_Hex_bytes_to_Yaraformat(self, bytes):
        ret_str = ''
        for byte in bytes:
            ret_str += '{:02X} '.format(byte)
        return ret_str
    
    def inst_extract(self, funcea):    
        ret_extracted = []
        f = ida_funcs.get_func(funcea)

        for ea in Heads(f.start_ea, f.end_ea):
            insn = idaapi.insn_t()
            # get length 
            length = decode_insn(insn, ea)
            # get bytes(strings)
            inst_bytes = get_bytes(ea, length)   
            if inst_bytes == None:
                break
            inst_bytes_str = self.Convert_Hex_bytes_to_Yaraformat(inst_bytes)            
            # get instruction
            re_inst = generate_disasm_line(ea, 0)

            # get convert instruction
            convert_flag = 0 
            re_inst_conv = re_inst
            for opNo in range(0, 3):
                if ( get_operand_type(ea, opNo) in IDAPythoOperandCtrl.ReplaceNumOperand ):
                    re_inst_conv = re_inst.replace( print_operand(ea, opNo),
                                            IDAPythoOperandCtrl.opTypeDict[get_operand_type(ea, opNo)],
                                            1)
                    convert_flag = 1
            ret_extracted.append([convert_flag, length, inst_bytes_str, re_inst, re_inst_conv])
        return ret_extracted
    
    def get_all_function_info(self):
        for segea in Segments():
            for funcea in Functions(segea, get_segm_end(segea)):
                functionName = get_func_name(funcea)

                # Target functions are sub_* 
                if (ConfigVar.TARGET_SUBFUNC == True):
                    if ((functionName.startswith("sub_")) == False):
                        continue

                # get a flag status on functions to exclude library functions for making yara rule
                flags = idc.get_func_flags(funcea)
                if (flags & FUNC_LIB) == 0:
                    function_info = []
                    function_info = self.inst_extract(funcea)
                    
                    self.function_all_info.append(["0x{:02x}".format(funcea), functionName, function_info])
 
    def extract_long_inst4yara(self):        
        numberOfFunc = len(self.function_all_info)
        for fnum in range(numberOfFunc):
            noConvINst_count = max_noConv_count = max_inum = 0
            
            numberOfInst = len(self.function_all_info[fnum][self.instructions_offset])
            
            # extract most long instrunction length and the inum into the function
            current_inum_head = current_len = max_len = max_inum_head = 0
            for inum in range(numberOfInst):
                convert_flag = self.function_all_info[fnum][self.instructions_offset][inum][self.convert_flag_offset]
                inst_length = self.function_all_info[fnum][self.instructions_offset][inum][self.inst_length_offset]
                if convert_flag == 0:
                    current_len += inst_length
                    if max_len < current_len:
                        max_len = current_len
                        max_inum_head = current_inum_head
                elif convert_flag == 1:
                    current_inum_head = inum + 1
                    current_len = 0
                else:
                    print("[*] Error: extract_long_inst4yara()")
                    return             
            self.extracted_most_long_inst.append([fnum, max_inum_head, max_len])    
    
    def pickup_yara_bytes(self): 
        split_len = len(self.extracted_most_long_inst) // 4
        
        part1 = self.extracted_most_long_inst[:split_len]
        part2 = self.extracted_most_long_inst[split_len:2*split_len]
        part3 = self.extracted_most_long_inst[2*split_len:3*split_len]
        part4 = self.extracted_most_long_inst[3*split_len:]
        
        part1.sort(key=lambda x:x[2], reverse=True)   
        part2.sort(key=lambda x:x[2], reverse=True)   
        part3.sort(key=lambda x:x[2], reverse=True)
        part4.sort(key=lambda x:x[2], reverse=True)   
            
        concat_allpart = [part1[:ConfigVar.NumFunc_eachPart], part2[:ConfigVar.NumFunc_eachPart], part3[:ConfigVar.NumFunc_eachPart], part4[:ConfigVar.NumFunc_eachPart]]
        concat_allpart = sum(concat_allpart, [])
        
        for long_funcinfo in concat_allpart:
            fnum = long_funcinfo[0]
            inum = long_funcinfo[1]
            
            bytes_strings = ""
            instructions_strings = ""
        
            funcAddr = self.function_all_info[fnum][0]
            funcName = self.function_all_info[fnum][1]

            func_Addr_and_Name = funcAddr +" : "+ funcName
            for n in range(300):
                try:
                    convert_flag = self.function_all_info[fnum][self.instructions_offset][inum + n][self.convert_flag_offset]
                except:
                    break
                if convert_flag == 1:
                    break
    
                current_bytes = self.function_all_info[fnum][self.instructions_offset][inum + n][self.bytes_offset]
                bytes_strings += current_bytes
                instructions_strings += self.sp8 + "{:36}".format(current_bytes) + "{:30}".format(self.function_all_info[fnum][self.instructions_offset][inum + n][self.instruction_offset]) + "\n"

            # exclude same bytes_strings in the list(yara_bytes)
            sameFlag = True
            for ele in self.yara_bytes:
                if bytes_strings == ele[1]:
                    sameFlag = False
            if sameFlag:
                self.yara_bytes.append([func_Addr_and_Name, bytes_strings, instructions_strings])
    
    
    def print_main_list(self):
        print("[*] INFO function_all_info[] : {}".format(self.function_all_info))
        print("[*] INFO extracted_most_long_inst[] : {}".format(self.extracted_most_long_inst))
        print("[*] INFO yara_bytes[] : {}".format(self.yara_bytes))

    def generate_yara_strings(self):
        # set each function bytes
        numberOfFunction = len(self.yara_bytes)
        for num in range(numberOfFunction):
            self.yara_strings += self.func_comment01
            # function addr
            self.yara_strings += self.yara_bytes[num][0] + "\n"
            # function instructions
            self.yara_strings += self.yara_bytes[num][2]
            self.yara_strings += self.func_comment02
            self.yara_strings += self.func_bytes01
            self.yara_strings += str(num)
            self.yara_strings += self.func_bytes02
            # funciton bytes
            self.yara_strings += self.yara_bytes[num][1]
            self.yara_strings += self.func_bytes03

    def get_yara_strings(self):
        return self.yara_strings


class YaraCondition():
    yara_condition = ""
    file_format = ""
    sp8 = ' ' * 8

    filefomrmat_elf = '(uint32(0) == 0x464C457F)\n'
    filefomrmat_dos = '(uint16(0) == 0x5A4D)\n'

    elf_x64 = sp8+ 'and (elf.machine == elf.EM_X86_64)\n'
    elf_x86 = sp8+ 'and (elf.machine == elf.EM_386)\n'
    elf_ARM = sp8+ 'and (elf.machine == elf.EM_ARM)\n'
    elf_MIPS = sp8+ 'and (elf.machine == elf.EM_MIPS)\n'
    elf_arch = ""

    con_filesize = sp8+ 'and (filesize < ' + str(ConfigVar.LessthanSize_MB) + 'MB)\n'
    elf_symbol = '''        and for 2 i in (0 .. elf.number_of_sections) : (
            ((elf.sections[i].name == ".symtab") and (elf.sections[i].type == elf.SHT_SYMTAB))
		    or ((elf.sections[i].name == ".strtab") and (elf.sections[i].type == elf.SHT_STRTAB))
	    )
        and not ( for 1 i in (0 .. elf.number_of_sections) : (
		            ( (elf.sections[i].name == ".dynamic") and (elf.sections[i].type == elf.SHT_DYNAMIC) )
	            )
        )
'''
    func_number01 = sp8 + 'and ( '
    match_numberOfFunc = ""
    func_number02 = ' of ($func*) )\n'
    
    def set_file_format(self):
        file_t = idaapi.get_file_type_name()
        if "ELF" in file_t:
            self.file_format = "ELF"
        elif "PE" in file_t:
            self.file_format = "PE"
        else:
            self.file_format == "None"

    def set_condition_arch(self):
        info = get_inf_structure()
        self.proc_name = info.procname
        if self.proc_name == "metapc":
            if info.is_64bit() == True:
                self.elf_arch = self.elf_x64
            elif info.is_32bit() == True:
                self.elf_arch = self.elf_x86
            else:
                print("[*] Error: cpu architecture is not found()")
        elif self.proc_name == "ARM":
            self.elf_arch = self.elf_ARM
        elif self.proc_name == "mipsb":
            self.elf_arch = self.elf_MIPS
        else:
            print("[*] Error: cpu architecture is not found. detect cpu: {}".format(info.procname))

    def set_yara_condition_matchNumber(self):
        self.match_numberOfFunc = str(ConfigVar.matchNumber)

    def generate_yara_condition(self): 
        if self.file_format == "ELF":
            self.yara_condition += self.filefomrmat_elf
            self.yara_condition += self.elf_arch
            self.yara_condition += self.con_filesize
            self.yara_condition += self.elf_symbol
        elif self.file_format == "PE":
            self.yara_condition += self.filefomrmat_dos
            self.yara_condition += self.con_filesize
        else:
            self.yara_condition += self.con_filesize
            
        self.yara_condition += self.func_number01
        self.yara_condition += self.match_numberOfFunc
        self.yara_condition += self.func_number02

    def get_yara_condition(self):
        return self.yara_condition
    

class OutputYaraFile:
    yarafilename = ConfigVar.YaraFileName
    currentdir = ""
    filepath = ""
    yara_strings = ""

    def __init__(self):
        self.currentdir = os.getcwd()
        self.filepath = self.currentdir + "\\" + self.yarafilename

    def reset_filepath(self, input_text):
        self.filepath = self.currentdir + "\\" + input_text

    def set_yara_text(self, input_text):
        self.yara_strings = input_text
        
    def get_filepath(self):
        return self.filepath

    def output_yarafile(self):
        textfile = open(self.filepath, "w")
        textfile.write(self.yara_strings)
        textfile.close()
        print("[*] Generated Yara File! : {}".format(self.filepath))


class SimpleSubViewer(simplecustviewer_t):
    filepath = ""

    def set_filepath(self, in_filepath):
        self.filepath = in_filepath

    def Create(self):
        title = "Auto Yara Rule"
        simplecustviewer_t.Create(self, title)
        yarafile = open(self.filepath, "r")
        yaraLines = yarafile.readlines()
        yarafile.close()
        
        for line in yaraLines:
            self.AddLine(line)
        self.Show()

    def OnDblClick(self, value):
        curWord = self.GetCurrentWord()
        lineN = self.GetLineNo()
        if not curWord:
            curWord = "<None>"
        elif curWord[:2] == '0x':
            jumpto(int(curWord, 16))
            
        return True
    
    def OnKeydown(self, vkey, shift):
        if vkey == ord('E'):
            lineN = self.GetLineNo()
            if lineN is not None:
                lineS = self.GetCurrentLine(notags=1)
                lineSnew = ida_kernwin.ask_str("{}".format(lineS), 0, "Edit")
                self.edit_line(lineN, lineS, lineSnew)                
                
        elif vkey == ord('I'):
            lineN = self.GetLineNo()
            lineS = self.GetCurrentLine(notags=1)
            if lineN is not None:
                self.insert_line(lineN, lineS)
                            
        elif vkey == 46: # Delete
            lineN = self.GetLineNo()
            lineS = self.GetCurrentLine(notags=1)
            if lineN is not None:
                yn = True #ida_kernwin.ask_yn(0, "Delete line?")
                if yn == True:
                    self.delete_line(lineN, lineS)
                    print("[*] INFO delete >> {}".format(lineS))

        elif vkey == 27: # Escape
            print("[*] INFO window Quit")
            self.Close()

        return True

    def save_file(self, lines):
        yara_text = ""
        for line in lines:
            yara_text += line
        textfile = open(self.filepath, "w")
        textfile.write(yara_text)
        textfile.close()
        print("[*] Save Yara File! : {}".format(self.filepath))
        
    def refresh_file_contents(self):
        yaraLines = self.get_yarafile_lines()
        self.ClearLines()
        
        for line in yaraLines:
            self.AddLine(line)
        self.Refresh()
       
    def get_yarafile_lines(self):
        yarafile = open(self.filepath, "r")
        yaraLines = yarafile.readlines()
        yarafile.close()
        return yaraLines
        
    def edit_line(self, lineNum, oldcontents, newcontents):
        yaraLines = self.get_yarafile_lines()
        # check file contetnts
        if yaraLines[lineNum] == oldcontents:
            newcontents = newcontents.replace("\n", "")
            yaraLines[lineNum] = newcontents + "\n"
            self.save_file(yaraLines)    
            self.refresh_file_contents()

    def insert_line(self, lineNum, oldcontents):
        yaraLines = self.get_yarafile_lines()
        # check file contetnts
        if yaraLines[lineNum] == oldcontents:
            yaraLines[lineNum] = yaraLines[lineNum] + "\n"
            self.save_file(yaraLines)    
            self.refresh_file_contents()
            
    def delete_line(self, lineNum, oldcontents):
        yaraLines = self.get_yarafile_lines()       
        # check file contetnts
        if yaraLines[lineNum] == oldcontents:
            yaraLines[lineNum] = ""
            self.save_file(yaraLines)    
            self.refresh_file_contents()    
    
    def OnHint(self, lineno):
        return (6, "  E  : edit line\n  I  : insert line\n[Del]: delete line\n[ESC]: quit")


def main_run():
    print("[*] Start AutoYara4FLIRT!!")
    # batch mode
    if ConfigVar.BATCH_MODE:
        ida_auto.auto_wait()

    # Generate yaraCompo Class
    YaraCompoClass = YaraCompo()
    
    # Set MODULE
    YaraModuleClass = YaraModule()
    YaraModuleClass.set_import_module()
    module_t = YaraModuleClass.get_import_module()
    YaraCompoClass.set_MODULE(module_t)

    # Set HASH
    YaraHashClass = YaraHash()
    YaraHashClass.set_hash_sha256()
    hash_t = YaraHashClass.get_hash_sha256()
    YaraCompoClass.set_HASH(hash_t)

    # add!   batch mode
    YaraCompoClass.set_RULENAME("RULE_" + hash_t)
    
    # Set Strings
    YaraStringsClass = YaraStrings()
    YaraStringsClass.get_all_function_info()
    YaraStringsClass.extract_long_inst4yara()
    YaraStringsClass.pickup_yara_bytes()
    YaraStringsClass.generate_yara_strings()
    strings_t = YaraStringsClass.get_yara_strings()
    YaraCompoClass.set_STRINGS(strings_t)
    
    # Set Condition
    YaraConditionClass = YaraCondition()
    YaraConditionClass.set_file_format()
    YaraConditionClass.set_condition_arch()
    YaraConditionClass.set_yara_condition_matchNumber()
    YaraConditionClass.generate_yara_condition()
    condition_t = YaraConditionClass.get_yara_condition()
    YaraCompoClass.set_CONDITION(condition_t)

    # Generate yara text data
    YaraCompoClass.generate_yara_text()
    text_t = YaraCompoClass.get_generated_yara_text()

    
    # Output .yara file
    OutputYaraFileClass = OutputYaraFile()
     # add!   batch mode
    OutputYaraFileClass.reset_filepath("RULE_" + hash_t + ".yara")
    OutputYaraFileClass.set_yara_text(text_t)
    OutputYaraFileClass.output_yarafile()
    
    # Open Subviewer on IDA
    SimpleSubViewerClass = SimpleSubViewer()
    filepath_t = OutputYaraFileClass.get_filepath()
    SimpleSubViewerClass.set_filepath(filepath_t)
    SimpleSubViewerClass.Create()
    
    print("[*] Finish!!")

    # batch mode
    if ConfigVar.BATCH_MODE:
        idc.exit()


if __name__ == '__main__': 
    main_run()


class AutoYara4FLIRTPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_DRAW
    comment = ""
    help = "No help is needed"
    wanted_name = "AutoYara4FLIRT"
    wanted_hotkey = "" # = "Alt-F9"

    def init(self):
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        main_run()
        return

    def term(self):
        pass

def PLUGIN_ENTRY():
    return AutoYara4FLIRTPlugin()


repository: tmr232/Sark
file: plugins/function_flow.py
description: IDAPython Made Easy
wanted_name: Function Flow
comment: Show Flow in Functions
created_at: 2015-01-19 09:13:48+00:00
pushed_at: 2025-02-22 10:17:16+00:00
url:
https://github.com/tmr232/Sark/blob/950e2c17e520ab637a3cdcd0da312d224b951f13/plugins/function_flow.py

content:
import networkx as nx

import idaapi

from sark import CodeBlock, FlowChart, get_block_start, get_nx_graph

try:
    from sark.ui import ActionHandler

    use_new_ui = True
except:
    use_new_ui = False

COLOR_REACHABLE = 0x66EE11
COLOR_UNREACHABLE = 0x6611EE
COLOR_REACHING = 0x11EE66
COLOR_NOT_REACHING = 0x1166EE
COLOR_SOURCE = 0xEE6611
COLOR_NONE = 0xFFFFFFFF
COLOR_EXIT = 0x000048


def iter_exit_nodes(ea):
    for block in FlowChart(ea):
        # Check if there are successors
        for successor in block.next:
            break
        else:
            yield block


def clear_func(ea):
    for block in FlowChart(ea):
        block.color = COLOR_NONE


def mark_not_reaching_nodes(ea, source_color=COLOR_SOURCE, other_color=COLOR_NOT_REACHING):
    graph = get_nx_graph(ea)
    graph = graph.reverse()
    block_ea = get_block_start(ea)
    reaching = nx.descendants(graph, block_ea)

    try:
        graph_nodes_iter = graph.nodes()
    except:
        graph_nodes_iter = graph.nodes_iter()

    for node_ea in graph_nodes_iter:
        if node_ea not in reaching:
            CodeBlock(node_ea).color = other_color

    CodeBlock(ea).color = source_color


def mark_reaching_nodes(ea, source_color=COLOR_SOURCE, other_color=COLOR_REACHING):
    graph = get_nx_graph(ea)
    graph = graph.reverse()
    block_ea = get_block_start(ea)
    for descendant in nx.descendants(graph, block_ea):
        CodeBlock(descendant).color = other_color

    CodeBlock(ea).color = source_color


def mark_unreachable_nodes(ea, source_color=COLOR_SOURCE, other_color=COLOR_UNREACHABLE):
    graph = get_nx_graph(ea)
    block_ea = get_block_start(ea)
    descendants = nx.descendants(graph, block_ea)
    for block in FlowChart(ea):
        if block.start_ea not in descendants:
            block.color = other_color

    CodeBlock(ea).color = source_color


def mark_reachable_nodes(ea, source_color=COLOR_SOURCE, other_color=COLOR_REACHABLE):
    graph = get_nx_graph(ea)
    block_ea = get_block_start(ea)
    for descendant in nx.descendants(graph, block_ea):
        CodeBlock(descendant).color = other_color

    CodeBlock(ea).color = source_color


def mark_exit_nodes(ea, node_color=COLOR_EXIT):
    for block in iter_exit_nodes(ea):
        block.color = node_color


class MarkReachableNodesHandler(ActionHandler):
    TEXT = "Reachable"

    def _activate(self, ctx):
        clear_func(ctx.cur_ea)
        mark_reachable_nodes(ctx.cur_ea)


class MarkUnReachableNodesHandler(ActionHandler):
    TEXT = "Unreachable"

    def _activate(self, ctx):
        clear_func(ctx.cur_ea)
        mark_unreachable_nodes(ctx.cur_ea)


class MarkReachingNodesHandler(ActionHandler):
    TEXT = "Reaching"

    def _activate(self, ctx):
        clear_func(ctx.cur_ea)
        mark_reaching_nodes(ctx.cur_ea)


class MarkNotReachingNodesHandler(ActionHandler):
    TEXT = "Not Reaching"

    def _activate(self, ctx):
        clear_func(ctx.cur_ea)
        mark_not_reaching_nodes(ctx.cur_ea)


class MarkClearHandler(ActionHandler):
    TEXT = "Clear"

    def _activate(self, ctx):
        clear_func(ctx.cur_ea)


class MarkExits(ActionHandler):
    TEXT = "Exits"

    def _activate(self, ctx):
        clear_func(ctx.cur_ea)
        mark_exit_nodes(ctx.cur_ea)

        idaapi.msg("\n" * 2)

        for block in iter_exit_nodes(ctx.cur_ea):
            idaapi.msg("Exit at 0x{:08X}\n".format(block.start_ea))


class Hooks(idaapi.UI_Hooks):
    def populating_widget_popup(self, form, popup):
        # You can attach here.
        pass

    def finish_populating_widget_popup(self, form, popup):
        # Or here, after the popup is done being populated by its owner.

        if idaapi.get_widget_type(form) == idaapi.BWN_DISASM:
            idaapi.attach_action_to_popup(form, popup, MarkReachableNodesHandler.get_name(), "Mark/")
            idaapi.attach_action_to_popup(form, popup, MarkUnReachableNodesHandler.get_name(), "Mark/")
            idaapi.attach_action_to_popup(form, popup, MarkReachingNodesHandler.get_name(), "Mark/")
            idaapi.attach_action_to_popup(form, popup, MarkNotReachingNodesHandler.get_name(), "Mark/")
            idaapi.attach_action_to_popup(form, popup, MarkExits.get_name(), "Mark/")
            idaapi.attach_action_to_popup(form, popup, MarkClearHandler.get_name(), "Mark/")


class FunctionFlow(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = "Show Flow in Functions"
    help = "Show code flow inside functions"
    wanted_name = "Function Flow"
    wanted_hotkey = ""

    def init(self):
        MarkReachableNodesHandler.register()
        MarkUnReachableNodesHandler.register()
        MarkReachingNodesHandler.register()
        MarkNotReachingNodesHandler.register()
        MarkExits.register()
        MarkClearHandler.register()

        self.hooks = Hooks()
        self.hooks.hook()
        return idaapi.PLUGIN_KEEP

    def term(self):
        MarkReachableNodesHandler.unregister()
        MarkUnReachableNodesHandler.unregister()
        MarkReachingNodesHandler.unregister()
        MarkNotReachingNodesHandler.unregister()
        MarkExits.unregister()
        MarkClearHandler.unregister()

    def run(self, arg):
        pass


def PLUGIN_ENTRY():
    return FunctionFlow()


repository: neoni/gamma
file: gamma.py
description: IDA Hexrays plugin for highlighting your interesting variables or struct members
url: https://github.com/neoni/gamma/blob/5a0499ca430d6e4efc0800ec407379dceb547251/gamma.py
created_at: 2020-07-29 10:12:00+00:00
pushed_at: 2020-07-31 05:38:40+00:00
content:
import shutil, re, os, errno

import ida_hexrays
import ida_idaapi
import ida_kernwin as kw
import ida_lines as il
import ida_diskio
import ida_lines
from idaapi import *

__author__ = "neoni"

PLUGIN_NAME = "gamma"

COLOR_VARS = []
COLOR_MEMS = []

# -----------------------------------------------------------------------------
def is_plugin():
    """returns True if this script is executed from within an IDA plugins
    directory, False otherwise."""
    return "__plugins__" in __name__

# -----------------------------------------------------------------------------
def get_dest_filename():
    """returns destination path for plugin installation."""
    return os.path.join(
        ida_diskio.get_user_idadir(),
        "plugins",
        "%s%s" % (PLUGIN_NAME, ".py"))

# -----------------------------------------------------------------------------
def is_installed():
    """checks whether script is present in designated plugins directory."""
    return os.path.isfile(get_dest_filename())

# -----------------------------------------------------------------------------
def is_ida_version(requested):
    """Checks minimum required IDA version."""
    rv = requested.split(".")
    kv = kw.get_kernel_version().split(".")

    count = min(len(rv), len(kv))
    if not count:
        return False

    for i in range(count):
        if int(kv[i]) < int(rv[i]):
            return False
    return True

# -----------------------------------------------------------------------------
def is_compatible():
    """Checks whether script is compatible with current IDA and
    decompiler versions."""
    min_ida_ver = "7.2"
    return is_ida_version(min_ida_ver) and ida_hexrays.init_hexrays_plugin()

# -----------------------------------------------------------------------------
SELF = __file__
def install_plugin():
    """Installs script to IDA userdir as a plugin."""
    dst = get_dest_filename()
    src = SELF
    if is_installed():
        btnid = kw.ask_yn(kw.ASKBTN_NO,
            "File exists:\n\n%s\n\nReplace?" % dst)
        if btnid is not kw.ASKBTN_YES:
            return False
    else:
        btnid = kw.ask_yn(kw.ASKBTN_NO,
            "This plugin is about to be installed to:\n\n%s\n\nInstall now?" % dst)
        if btnid is not kw.ASKBTN_YES:
            return False

    usrdir = os.path.dirname(dst)
    kw.msg("%s: copying script from \"%s\" to \"%s\" ..." % (PLUGIN_NAME, src, usrdir))
    if not os.path.exists(usrdir):
        try:
            os.makedirs(usrdir)
        except OSError as e:
            if e.errno != errno.EEXIST:
                kw.msg("failed (mkdir)!\n")
                return False
    try:
        shutil.copy(src, dst)
    except:
        kw.msg("failed (copy)!\n")
        return False
    kw.msg(("done\n"
        "Plugin installed - please restart this instance of IDA.\n"))
    return True


# -----------------------------------------------------------------------------
class gamma_hooks_t(ida_hexrays.Hexrays_Hooks):
    """class for handling decompiler events."""

    def _color_var(self, vu, item):
        global COLOR_VARS
        if not item:
            return
        pc = vu.cfunc.get_pseudocode()
        if item in COLOR_VARS:
            for sl in pc:
                sl.line = sl.line.replace(ida_lines.COLSTR(item, ida_lines.SCOLOR_ERROR), item)
            COLOR_VARS.remove(item)
        else:
            for sl in pc:
                pos = 0
                while True:
                    pos = sl.line.find(item, pos)
                    if pos < 0:
                        break
                    if not sl.line[pos+len(item)].isalnum():
                        sl.line = sl.line[:pos] + ida_lines.COLSTR(item, ida_lines.SCOLOR_ERROR) + sl.line[pos+len(item):]
                        pos += len(ida_lines.COLSTR(item, ida_lines.SCOLOR_ERROR))
                    else:
                        pos += 1
                # sl.line = sl.line.replace(item, ida_lines.COLSTR(item, ida_lines.SCOLOR_ERROR))
            COLOR_VARS.append(item)
        refresh_idaview_anyway()

    def _contains_item(self, pc, items, remove=False):
        for sl in pc:
            line = self._strip_line(sl.line)
            s = ''.join(items)
            count = line.count(s)
            if count <= 0:
                continue
            #print(count, line)
            start = 0
            fact = 0
            for i in range(13):  # max 13 times to try to match
                if fact == count:
                    continue # find all arguments
                pos = len(sl.line)
                found = True
                pos = sl.line.find(items[-1], start, pos)
                old_start = start
                start = pos + 1
                if pos < 0 or (pos+len(items[-1])<len(sl.line) and sl.line[pos+len(items[-1])].isalnum()) or \
                    ((pos-1) >= 0 and sl.line[pos-1].isalnum()):
                    continue
                for item in items[-2::-1]:
                    pos = sl.line.rfind(item, old_start, pos)
                    if pos < 0 or (pos+len(item)<len(sl.line) and sl.line[pos+len(item)].isalnum()) or \
                        ((pos-1) >= 0 and sl.line[pos-1].isalnum()):
                        found = False
                        break
                if found:
                    fact += 1
                    for item in items:
                        pos = sl.line.find(item, pos, start)
                        if pos+len(item)==len(sl.line) or not sl.line[pos+len(item)].isalnum() or pos - 1 < 0 or not sl.line[pos-1].isalnum():
                            if remove:
                                sl.line = sl.line[:pos-2] + item + sl.line[pos+len(item)+2:]
                                start -= 4
                            else:
                                sl.line = sl.line[:pos] + ida_lines.COLSTR(item, ida_lines.SCOLOR_ERROR) + sl.line[pos+len(item):]
                                pos += len(ida_lines.COLSTR(item, ida_lines.SCOLOR_ERROR))
                                start += 4
                        elif pos < 0:
                            continue
                        else:
                            pos += 1


    def _color_mem(self, vu, items):
        global COLOR_MEMS
        if not items:
            return
        if len(items) == 1:
            self._color_var(vu, items[0])
            return
        pc = vu.cfunc.get_pseudocode()
        if items in COLOR_MEMS:
            self._contains_item(pc, items, True)
            COLOR_MEMS.remove(items)
        else:
            self._contains_item(pc, items, False)
            COLOR_MEMS.append(items)

        refresh_idaview_anyway()

    def _strip_line(self, sline):
        line = re.sub(r'\(0000............', '', sline)
        line = ''.join([c for c in line if ord(c) >= 0x20])
        line = line.replace('[ ', '[').replace(' ]', ']')
        line = re.sub(r'(?<=[^ ])([ ]{2})(?=[^ ]|$)', ' ', line) # replace two space
        return line

    def right_click(self, vu):
        if vu.get_current_item(USE_MOUSE):
            item = None
            cit = vu.item.citype
            if cit == VDI_LVAR:
                item = vu.item.l.name
                self._color_var(vu, item)
            elif cit == VDI_EXPR:
                if vu.item.e.v:
                    lvars = vu.cfunc.get_lvars()
                    var = lvars[vu.item.e.v.idx]
                    if var:
                        item = var.name
                        self._color_var(vu, item)
                else:
                    sl = vu.cfunc.get_pseudocode()[vu.cpos.lnnum]
                    line = self._strip_line(sl.line)
                    items = []
                    x = vu.cpos.x # maybe inaccuracy
                    while x < len(line) and ((not line[x].isalnum()) and (line[x] not in ['[', '_', ' '])):
                        x += 1
                    if x >= len(line):
                        return 0
                    #print(sl.line)
                    #print(x)
                    #print(line)
                    tmp = line[x]
                    for i in range(x-1, -1, -1):
                        if not line[i].isalnum() and line[i] != '_':
                            break
                        tmp = line[i] + tmp
                    for i in range(x+1, len(line)):
                        if line[i] == ' ':
                            if tmp:
                                items.append(tmp)
                                tmp = ''
                        elif line[i] == '[':
                            if tmp:
                                items.append(tmp)
                                tmp = ''
                            items.append(line[i])
                        elif line[i] == ']':
                            if tmp:
                                items.append(tmp)
                            if '[' in items:
                                items.append(line[i])
                            break
                        elif line[i] == '-':
                            if tmp:
                                items.append(tmp)
                            if line[i+1] == '>':
                                tmp = '-'
                            else:
                                break
                        elif line[i] == '>':
                            if tmp == '-':
                                tmp = ''
                                items.append('->')
                            else:
                                break
                        elif line[i].isalnum() or line[i] == '_':
                            tmp = tmp + line[i]
                        else:
                            if tmp:
                                items.append(tmp)
                            break
                    #print(items)
                    self._color_mem(vu, items)
        return 0


class gamma_plugin_t(ida_idaapi.plugin_t):
    """plugin class."""
    flags = ida_idaapi.PLUGIN_HIDE
    comment = PLUGIN_NAME
    help = PLUGIN_NAME
    wanted_name = PLUGIN_NAME
    wanted_hotkey = ""

    def init(self):
        self.gamma_hooks = None
        if not is_compatible():
            kw.msg("%s: decompiler not available, skipping." % PLUGIN_NAME)
            return ida_idaapi.PLUGIN_SKIP

        self.gamma_hooks = gamma_hooks_t()
        self.gamma_hooks.hook()
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        return

    def term(self):
        if self.gamma_hooks:
            self.gamma_hooks.unhook()
        return

# -----------------------------------------------------------------------------
def PLUGIN_ENTRY():
    """plugin entry point."""
    return gamma_plugin_t()

# -----------------------------------------------------------------------------
def SCRIPT_ENTRY():
    """script entry point."""
    if not is_plugin():
        (kw.info("Success!") if install_plugin() else
            kw.warning("Error! Plugin could not be installed!"))
    return

# -----------------------------------------------------------------------------
HL_FLAGS = kw.HIF_LOCKED | kw.HIF_NOCASE if is_ida_version("7.4") else kw.HIF_LOCKED
SCRIPT_ENTRY()


repository: 19h/ida-plugins
file: destruct/destruct.py
wanted_name: DedupStructHeuristicFinder_Optimized
comment: Deduplicated heuristic structure recovery (optimized clustering).
created_at: 2025-04-08 22:54:27+00:00
pushed_at: 2025-04-08 23:07:17+00:00
url:
https://github.com/19h/ida-plugins/blob/b045b6f6bc9ae2a53104986fb74f3fe1198e5d2e/destruct/destruct.py

content:
#!/usr/bin/env python
"""
IDA 9.0 Plugin – Deduplicated Heuristic Structure Recovery (Optimized)

This plugin scans functions for displacement‐based memory accesses to
heuristically recover structure layouts. It groups memory accesses by
(function, base register), computes candidate structure offsets and a
common field spacing, and then deduplicates similar candidates by normalizing
their offset signatures. For large programs the candidate clustering has been
optimized: candidates are first grouped by the number of fields, and only
candidates of equal length are compared using an element‑wise near‑match test
(allowing up to 20% mismatches). Finally, one structure is created per cluster
using the union of normalized offsets.

Enhanced offset handling:
  • Converts each operand’s displacement to a signed integer (32‑ or 64‑bit).
  • Computes a common field size (via the GCD of differences) and selects a
    data type flag accordingly.
  • Overlapping members are avoided by not adding a new member if its offset
    is within a previously added member’s range.

Usage:
  • Load the plugin (Options → Load Plugin).
  • Execute it using hotkey "Alt‑F" (or via the Plugins menu).

Example:
  Multiple functions that access a structure at mem, mem+24, mem+48, etc.
  may be merged into one recovered structure.
"""

import idaapi
import idautils
import idc
import math
import hashlib
from functools import reduce

# Convert an unsigned number to signed based on bit width.
def to_signed(n, bits):
    sign_bit = 1 << (bits - 1)
    if n & sign_bit:
        return n - (1 << bits)
    return n

# Compute the greatest common divisor of a list of numbers.
def list_gcd(numbers):
    if not numbers:
        return 0
    return reduce(math.gcd, numbers)

# Choose an IDA data flag based on a given number of bytes.
def get_member_flag(nbytes):
    if nbytes == 1:
        return idc.FF_DATA | idc.FF_BYTE
    elif nbytes == 2:
        return idc.FF_DATA | idc.FF_WORD
    elif nbytes == 4:
        return idc.FF_DATA | idc.FF_DWORD
    elif nbytes == 8:
        return idc.FF_DATA | idc.FF_QWORD
    else:
        return idc.FF_DATA | idc.FF_BYTE

# A simple near-match test for two normalized signatures (tuples).
# Returns True if the number of mismatches (element‑wise) is <= floor(0.2 * length)
def is_similar(norm1, norm2, threshold=0.8):
    if len(norm1) != len(norm2):
        return False
    allowed = math.floor((1 - threshold) * len(norm1))
    mismatches = sum(1 for a, b in zip(norm1, norm2) if a != b)
    return mismatches <= allowed

class StructHeuristicFinderPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = "Deduplicated heuristic structure recovery (optimized clustering)."
    help = "Scans disassembly for overlapping accesses, groups similar candidates, and creates structures."
    wanted_name = "DedupStructHeuristicFinder_Optimized"
    wanted_hotkey = "Alt-F"

    def init(self):
        idaapi.msg("DedupStructHeuristicFinder_Optimized initialized.\n")
        return idaapi.PLUGIN_OK

    def run(self, arg):
        self.find_structs()

    def term(self):
        idaapi.msg("DedupStructHeuristicFinder_Optimized terminated.\n")

    def find_structs(self):
        # Determine bit-width (64 or 32) via the new accessor.
        bits = 64 if idaapi.inf_is_64bit() else 32

        # Scan functions: mapping (func_ea, base_reg) -> set of displacement offsets (signed)
        reg_access_map = {}
        idaapi.msg("Starting heuristic structure analysis...\n")
        for func_ea in idautils.Functions():
            func = idaapi.get_func(func_ea)
            if not func:
                continue
            for head in idautils.Heads(func.start_ea, func.end_ea):
                insn = idaapi.insn_t()
                if idaapi.decode_insn(insn, head) == 0:
                    continue
                for op in insn.ops:
                    if op.type == idaapi.o_displ:
                        base_reg = op.reg
                        offset = to_signed(op.addr, bits)
                        key = (func_ea, base_reg)
                        reg_access_map.setdefault(key, set()).add(offset)

        # Build candidate structures.
        # Each candidate: (func_ea, reg_name, sorted_offsets, common_diff, norm, norm_set)
        candidates = []
        for (func_ea, base_reg), offsets in reg_access_map.items():
            if len(offsets) < 2:
                continue
            sorted_offsets = sorted(offsets)
            diffs = [j - i for i, j in zip(sorted_offsets, sorted_offsets[1:])]
            common_diff = list_gcd(diffs)
            if common_diff > 0 and common_diff < 1024:
                reg_name = idaapi.get_reg_name(base_reg, 4)
                norm = tuple(off - sorted_offsets[0] for off in sorted_offsets)
                norm_set = set(norm)
                candidates.append((func_ea, reg_name, sorted_offsets, common_diff, norm, norm_set))
                idaapi.msg("Candidate in 0x%X (%s): Offsets %s, Field size %d\n" %
                           (func_ea, reg_name,
                            [hex(o & ((1 << bits) - 1)) for o in sorted_offsets],
                            common_diff))

        if not candidates:
            idaapi.msg("No candidate structures found.\n")
            return

        # Optimize candidate clustering by grouping by length first.
        length_groups = {}
        for cand in candidates:
            length = len(cand[4])
            length_groups.setdefault(length, []).append(cand)

        clusters = []
        # Process each length group separately.
        for length, group in length_groups.items():
            # For each candidate in this group, use a simple near-match test.
            group_clusters = []  # each item: {"candidates": [cand, ...], "signature_set": set, "representative": norm tuple}
            allowed_mismatches = math.floor(0.2 * length)
            for cand in group:
                rep = cand[4]
                assigned = False
                for cluster in group_clusters:
                    # Use our near-match test.
                    if is_similar(cluster["representative"], rep, threshold=0.8):
                        cluster["candidates"].append(cand)
                        cluster["signature_set"].update(cand[5])
                        assigned = True
                        break
                if not assigned:
                    group_clusters.append({
                        "candidates": [cand],
                        "signature_set": set(cand[5]),
                        "representative": rep
                    })
            clusters.extend(group_clusters)
        idaapi.msg("Deduplication resulted in %d candidate clusters.\n" % len(clusters))

        # Create structures for each cluster.
        for cluster in clusters:
            # Choose representative candidate with maximum fields.
            rep = max(cluster["candidates"], key=lambda c: len(c[4]))
            union_set = cluster["signature_set"]
            union_sorted = sorted(union_set)
            # Normalize: ensure lowest offset is 0.
            if union_sorted and union_sorted[0] < 0:
                shift = union_sorted[0]
                union_sorted = [off - shift for off in union_sorted]
            # Recompute field size from union if possible.
            if len(union_sorted) > 1:
                diffs_union = [j - i for i, j in zip(union_sorted, union_sorted[1:])]
                union_field_size = list_gcd(diffs_union)
                if union_field_size == 0:
                    union_field_size = rep[3]
            else:
                union_field_size = rep[3]
            sig_str = "_".join(str(x) for x in union_sorted)
            sig_hash = hashlib.md5(sig_str.encode("utf-8")).hexdigest()[:8]
            struct_name = "struct_dedup_%s" % sig_hash
            self.create_structure(struct_name, union_sorted, union_field_size, bits)

        idaapi.msg("Heuristic structure analysis complete.\n")

    def create_structure(self, struct_name, offsets, field_size, bits):
        sid = idc.add_struc(-1, struct_name, 0)
        if sid == idaapi.BADADDR:
            idaapi.msg("Failed to create structure %s\n" % struct_name)
            return

        flag = get_member_flag(field_size)
        next_free = 0
        added = []
        for off in offsets:
            if off < next_free:
                continue
            field_name = "field_%X" % off
            res = idc.add_struc_member(sid, field_name, off, flag, -1, field_size)
            if res != 0:
                idaapi.msg("Failed to add member %s at offset 0x%X to structure %s\n" %
                           (field_name, off, struct_name))
            else:
                added.append(off)
                next_free = off + field_size
        idaapi.msg("Created deduplicated structure: %s with members at offsets: %s\n" %
                   (struct_name, [hex(o) for o in added]))

def PLUGIN_ENTRY():
    return StructHeuristicFinderPlugin()


repository: hoangcuongflp/Stuff
file: ida-master/plugins/localxrefs/localxrefs.py
description: because I've orderliness obsession :(
wanted_name: Localized Xrefs
comment: IDA Localized Xrefs
created_at: 2015-10-12 16:35:32+00:00
pushed_at: 2014-12-22 00:32:06+00:00
url:
https://github.com/hoangcuongflp/Stuff/blob/ee100760a05f524ff73837222d80f1dd12da3cee/ida-master/plugins/localxrefs/localxrefs.py

content:
# IDA Plugin to search for cross references only within the current defined function.
#
# Useful, for example, to find instructions that use a particular register, or that reference a literal value.
#
# Invoke by highlighting the desired text in IDA, then going to Jump->List local xrefs, or by pressing Alt+8.
# Highlighting is also supported; once xrefs are found, type the following in the Python command window:
#
#	Python> localxrefs.highlight()       <-- Highlight all xrefs
#	Python> localxrefs.highlight(False)  <-- Un-highlight all xrefs
#
# Craig Heffner
# Tactical Network Solutions

import idc
import idaapi

localxrefs = None

class LocalXrefs(object):

	UP   = 'Up  '
	DOWN = 'Down'
	THIS = '-   '

	READ    = 'r'
	WRITE   = 'w'

	OPND_WRITE_FLAGS = {
			0	: idaapi.CF_CHG1,
			1	: idaapi.CF_CHG2,
			2	: idaapi.CF_CHG3,
			3	: idaapi.CF_CHG4,
			4	: idaapi.CF_CHG5,
			5	: idaapi.CF_CHG6,
	}

	def __init__(self):
		self.xrefs = {}
		self.function = ''
		self._profile_function()

	def _profile_function(self):
		current_ea = ScreenEA()
		current_function = idc.GetFunctionName(current_ea)
		current_function_ea = idc.LocByName(current_function)

		if current_function:
			self.function = current_function

		ea = start_ea = idc.GetFunctionAttr(current_function_ea,  idc.FUNCATTR_START)
		end_ea = idc.GetFunctionAttr(current_function_ea, idc.FUNCATTR_END)

		self.highlighted = idaapi.get_highlighted_identifier()

		while ea < end_ea and ea != idc.BADADDR and self.highlighted:

			i = 0
			match = False
			optype = self.READ
			comment = None

			idaapi.decode_insn(ea)
			
			mnem = idc.GetMnem(ea)

			if self.highlighted in mnem:
				match = True
			elif idaapi.is_call_insn(ea):
				for xref in idautils.XrefsFrom(ea):
					if xref.type != 21:
						name = idc.Name(xref.to)
						if name and self.highlighted in name:
							match = True
							break
			else:	
				while True:
					opnd = idc.GetOpnd(ea, i)
					if opnd:
						if self.highlighted in opnd:
							match = True
							if (idaapi.insn_t_get_canon_feature(idaapi.cmd.itype) & self.OPND_WRITE_FLAGS[i]):
								optype = self.WRITE
						i += 1
					else:
						break

			if not match:
				comment = idc.GetCommentEx(ea, 0)
				if comment and self.highlighted in comment:
					match = True
				else:
					comment = idc.GetCommentEx(ea, 1)
					if comment and self.highlighted in comment:
						match = True
					else:
						comment = None

			if match:
				if ea > current_ea:
					direction = self.DOWN
				elif ea < current_ea:
					direction = self.UP
				else:
					direction = self.THIS

				self.xrefs[ea] = {
					'offset' 	: idc.GetFuncOffset(ea),
					'mnem'	 	: mnem,
					'type'		: optype,
					'direction'	: direction,
					'text'		: idc.GetDisasm(ea),
				}

			ea += idaapi.cmd.size

	def highlight(self, highlight=True, mnem=None, optype=None, direction=None, text=None):
		for (ea, info) in self.xrefs.iteritems():
			if mnem and info['mnem'] != mnem:
				highlight = False
			elif optype and info['optype'] != optype:
				highlight = False
			elif direction and info['direction'] != direction:
				highlight = False
			elif text and info['text'] != text:
				highlight = False

			if highlight:
				color = 0x00ff00
			else:
				color = idc.DEFCOLOR

			idc.SetColor(ea, idc.CIC_ITEM, color)

	def unhighlight(self):
		self.highlight(False)
		
	
class localizedxrefs_t(idaapi.plugin_t):
	flags = 0
	comment = "IDA Localized Xrefs"
	help = ""
	wanted_name = "Localized Xrefs"
	wanted_hotkey = ""

	DELIM = '-' * 86
	HEADER = '\nXrefs to %s from %s:'

	def init(self):
		self.menu_context = idaapi.add_menu_item("Jump/", "List local xrefs", "Alt-8", 0, self.run, (None,))
		return idaapi.PLUGIN_KEEP

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

	def run(self, arg):
		global localxrefs
		fmt = ''

		r = LocalXrefs()
		localxrefs = r

		offsets = r.xrefs.keys()
		offsets.sort()

		if r.highlighted:
			print self.HEADER % (r.highlighted, r.function)
			print self.DELIM
			
			for ea in offsets:
				info = r.xrefs[ea]
	
				if not fmt:
					fmt = "%%s   %%s   %%-%ds   %%s" % (len(info['offset']) + 15)

				print fmt % (info['direction'], info['type'], info['offset'], info['text'])
	
			print self.DELIM

def PLUGIN_ENTRY():
	return localizedxrefs_t()


repository: NoneShell/function_locator
file: function_locator.py
wanted_name: Function Locator
comment: Function Locator
created_at: 2024-10-24 02:45:25+00:00
pushed_at: 2024-10-24 08:36:38+00:00
description:
This is a plugin designed to help IoT security researchers locate which dynamic library a target function belongs to within a root filesystem (rootfs).

url:
https://github.com/NoneShell/function_locator/blob/4dcd0a8bc59616fcff9c66d43505cc70e6b8e9f6/function_locator.py

content:
import idaapi
import ida_kernwin
import ida_nalt
import idautils
import idc
import os
import function_locator
import importlib
from collections import defaultdict, deque
from elftools.elf.elffile import ELFFile

icon = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xc8\x00\x00\x00\xc8\x08\x06\x00\x00\x00\xadX\xae\x9e\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x0cZIDATx^\xed\x9d\xfb}\xd4:\x10\x85\xa5\xd4s\xb9-\x005@\rp\xbb\x08\xdb\x05\xa4\x86\xa4\x86,-\x10\xea\x89\xefO\xebx\xd7\xf1z\xad\x875\xa3\x19\xcd\xe1O\xa2\x87u4\x9f\xceH~\xacw\xf8\x07\x05\xa0\xc0M\x05<\xb4\x81\x02P\xe0\xb6\x02\x00\x04\xd1\x01\x056\x14\x00 \x08\x0f(\x00@\x10\x03P\xa0L\x018H\x99n\xa8eD\x01\x00bd\xa21\xcc2\x05\x00H\x99n\xa8eD\x01\x00bd\xa21\xcc2\x05\x00H\x99n\xa8eD\x01\x00bd\xa21\xcc2\x05\x00H\x99n\xa8eD\x01\x00bd\xa21\xcc2\x05\x00H\x99n\xd5j\xfd\xf3\xf5\xbfO[\x8d\xfd}\xfcy\xac\xd6\x19\x1a\xcaV\x00\x80dKVVa\x02\xc1\x0f\xc3\xfd\xd8\xc2\xb0\t\xc6u/\xfe\xe8\xfd\xf0;\xfc\xff\xab\xbb;\x02\x9c\xb2y\xc8\xad\x05@r\x15K,\xff\x1e\x88\\\x18\x12;q#4\x7f\x1e\x1f~\xa4\xd6@\xb9<\x05\x00H\x9e^\xd1\xd2\x01\x8c\xd1%\xa8\xa0\xb8u\t\x80%:9\x05\x05\x00H\x81h\xcb*\xed\xa0X\xbfx\xef\xdd\x01\xaeRab\x9ds\x00d\x87\x8e\xd2\xc0X\x0e%\x80\x82\xfd\xca\x8e\t\x06 \xe5\xe2\xfd\xfb\xf5\xdb\x8fapo\x1b\xee\xf2v8j\xc2Q\xcaU\x86\x83dj\xa7\t\x8c5GA\xea\x957\xe1\x00$C\xaf\x0f_\xbe?\xf3o\xbe3.0\xa1(\xdc$A\xa4Y\x11\x00\x92\xa0\xd7\xb8\xd7x}N(\xaa\xa6\x08@I\x9b*\x00\x12\xd1IsJ\x15\x0b\x01@\x12S\x08\xa7X\x9b\n\xf5\x0c\xc74p@\xb2\r\t\x1c\xe4\x86>=\xec7\xe2\xeb\xe3\x19\x93\xe3\xcb\xd3\xaf\xcf\xe9\xe5\xed\x94\x04 +sm\x0b\x0e@\xb2\x85;\x00Y\xa8c\x13\x0e@r\x0b\x12\x002S\xc6\xc2\x9e#\x9e\x1cy\xa4[8\xe6\xbd\x0e\x13\xc0q\xd1\x04\x1b\xf7\x99\x16\xf1\x15\xa5\xff\x12\x80\xe3z\x8e\x01\xc9\xa8\x89\xf9\x14\xab\xc7\x9b\x80\xb5\x96\xb4\xc1\xdf}\xb6\xfeb\x96y@lo\xcac(a?b\x1a\x10\xa4V1@\x9c\xb3\x9ej\x99\x06\xe4\xc3\x97oC<D\xa8K\xf8\xf3G\x19\xa6w\xceC\x8f\xc3\xe0?\x8e=s\xbf\x99x=^\xcb\xa9\x96Y@\xda\xb9\xc7\x08\xc4\xe0\xfd!5\xbf\x0f\xd7:B\xd3\xea\xfd\x13\xbb\xa9\x96I@\xda\xc0\xe1\x8f9P\xdc\xf2\xadV\xb0Xu\x11\x93\x80\xf0\xa6Vu\xc0X\x02\xc3\x0f\x8aM\x171\x07\x08\xa7{plp9\xc7c\xd1E\x00\x08\xd1\x1e\x9c3\x98\xf8 \xb1\xe7"\xa6\x00\xe1\t$\x9a\x94*\xc61\xd7\x17V8\xc1\x8f\x8d\x99\xe3\xef\x00\xa4\xb2\xca/O\x0f\xcd4\xe5y*\xc0\x96\x8b4\x9b\xcc\xcaq\x99\xd4\x1c\xf5\xe6\x9cc\xcf\x11\x1b(\x07$-\x17\x81\xd8\xf8k\xff\xdd\x0c \xd4\xe9\x95\x048\xa6\xe0\xa0\x1e\xab\xa54\x0b\x80TXr$\xc11\r\x87\xf6\x193;i\x96\x19@(\xd3+\x89)\x07u\xaa%q\xcc\x15\xd6\xba\xab&L\x00B\x19,\x12\xdd\x83\xc3E\xac\xa4Y&\x00\xa1\xcc\xc9%\xaf\xa4V\x17\x86\x9aNb\x02\x10\xaa|\\\xb2{\xd0\xbb\x88\x8d}\x88\x11@h\x1ek\xd7\x00\x08\xa5\x8bHv\xcfZ.\x02@v(\xa9%@\xa8\x0e(\xb4\x8c\x7f\xc7\x14\xf7\xffN:\xdd\n\xaa\'\xc5\xa0J1-l\xd4\xbbw\x10\xaa\r\xba\x86\xf4\x8a\xfa\xc6!\x00\xd9\xe3MB\xea\x02\x10\xe7\xa8\\T\xd3"Q\x1a\x8ep\x90B\xe54\x05\x07\x00)\x9cd\x0b\xdf\xc5\xa2r\x10M\xe9\x05\x15 \xce\xe9\xd9\x87\x95"\x02\x07)T\x0e\x80\x04\xe1\x00Ha\xf8\xc8\xa9\x06\x07\xc1\x1edO4\xc2A\n\xd5\xd3\xe4 T\x8b\x84\xa6}X\xe14\xe3>H\xb1p\xde\x1d\xb4\xfc\xa42\x00)\x9de\x03\x1f\xaf\xc6\x06\xd59*@4\xb9h)"\xdd\xa7X\x00\xc49\xaaGM\x00H)v\x82\xea\xd1\x01\x12>\x1f\xaa\xe3\xe7\x01\x00Hy@v\xef A\x1a\xcb\x01B\x95^\x05]\xf1\xb0b9x\xa2jR=\xac\xa7\xe1>\x00\x1d \xfd\xdf\x039\xdd\xe9\x11\x15\xc9D\x17C\x17$\xb2\xd3,\xcaq[8\xe25\x03\x08\xe5>D\xb2\x8b\x00\x90\xfd+\xae\t\x07\xa1\xdc\x87\x84\xb6%n\xd6)\xe1\xb0\xb2\xff0\xe3 # \xdf\x9f\xe9~\xadI^>Nu01\xae\xc9\xf2\xc6\xbb\xdf+\xd6[0\xe3 \xd4+\xaa\xa4\xa0\xa1\x1e\xab\x95\xfd\x87)\x07\xa1\xdd\x87\xbc\xad\xab\x02\x1e?\xa1\x86\xe3\x144\x02\xc6I\xe5\x18\xcbv\xcd8\x08}\x9a\xd5\x1e\x12\x0e8$9%\x07$\xa6\x00\xe1p\x91V+,\x0f\x1c\xb6\xdc\xc3T\x8a5\xad6\xb4\x9b\xf5\xcb\x9a\xc6\x99\x86p\xc1a\xe9\xf4j\x9aIS\x0e\x12\x06\xcd\xe5"\xd3iO\xf8\xeds\xaa\xc7\xe2\xb9~U\xea\x1c,\x86\xf6\x1ef\x01\xa1\xbe\'\xb2\x96\x17\xd7v\x13n0\xa61Yx\xf6\xca\xf4&\x9d;\xcd\xba\x12\xdb\xbb\xc3\xab\xbb;\xfe}\xfcy,\xd9`\xb6\x02\xa3\xd5\xbe\xaaD\xa3\xdau\xcc\xa5X\x17Hh\xbe\xd7\x9b:A\xc1UB\xd9-`\x02\x10\xa7\xe0\x1c\x86{\xba\x9b\x9ciWl\xd1=Ln\xd2\xa7p\xe0\xdd\x8b\xa4\x05\xa1\xd4R\xb5SD\xa9\xe3\\M\x8f5]l\xedk\xe5:\xd1\xaa}\xdd\x9c\xedY\x86\xc3\xb4\x83\xf0\x9fhq\x86u\xbd\xbe\xac\xa6V\xa6O\xb1\xe6\xe1\xc3y\x0f\xa1^\xd8\xf2\xb4d\xdd=\xcc;H\xebS-\x9e0/\xed\xc5\xce\x13\xbb[\n\x99=\xc5Z\x8a\x82\xfd\xc8{E\xac\xa7VH\xb1\x16\x84\xe0T\xeb"\x88\xc4\x17\xc0J}po=8\xc8LA@"\xf3\xed\xc8\xbdA\xbe\xa7>\x00\x81\x93\x9c\x15\x80s\\\xa3\x04@V\x96\x17\x8bN\x82\x13\xabu\x9f\x01 7\xfc\xd7\xd2\xf1/\xe0\xb8\x9d\x84\x01\x90\x8d\x04\xb5\xe5\xc3\x81{\xf2\xe6\xf4\xba\xfe8x\x7f(}x2\xbd\x1f\xbd%\x01H\xc2\xdc\xf5\xe9&\xb8\xcf\x910\xf56\xbe\xac\x98"D\xacLO\x90 \xa5\x8a\xcd\xf6\xe5\xefp\x90t\xad\x98\xdfF\xcc\xb8\xb0\x8c\xa28\xa9\xca\x10\xcb\xca\xb7y\xf3$\x89\x97\xd6\xe8&p\x8d\xf8\xbc\xae\x95\x80\x83\x94\xe9v\xaa\xa5\x01\x14\x80\xb1c\x82\xe1 \xfb\xc4\x9bj\xcb\x04\x05\'T5f\x17\x0eRC\xc5\xb76\xda\x83\xe2O\xef\xba\xe3\xe8\xb6\xde\xa4\x02\x90zZ\x9e[\n\xf7O\xee\xdc\xeb\xa7ap\xf7\x04\xcd/\x9a\x04\x14\x94\x1a\x03\x10Ju\x89\xbf\xc3\x85\x13)\xe2\xc9\xc3\x1e\x84^\xe0\xd0\x03\xd5O\x11\xe0\x9d\r\xfa\xf9\x83\x83\xd0k\x0c@\x184\xa6\xea\x02\x80P);k\x17\x0e\xc2 2Q\x17\x00\x84H\xd8y\xb3\x00\x84Ad\xa2.\x00\x08\x91\xb0\x00\x84AX\x86.\x00\x08\x83\xc8p\x10\x06\x91\x89\xba\x00 D\xc2\xc2A\x18\x84e\xe8\x02\x800\x88\x0c\x07a\x10\x99\xa8\x0b\x00B$,\x1c\x84AX\x86.\x00\x08\x83\xc8p\x10\x06\x91\x89\xba\x00 D\xc2\xc2A\x18\x84e\xe8\x02\x800\x88\x0c\x07a\x10\x99\xa8\x0b\x00B$,\x1c\x84AX\x86.\x00\x08\x83\xc8p\x10\x06\x91\x89\xba\x00 D\xc2\xc2A\x18\x84e\xe8\x02\x800\x88\x0c\x07a\x10\x99\xa8\x0b\x00B$,\x1c\x84AX\x86.\x00\x08\x83\xc8p\x10\x06\x91\x89\xba\x00 D\xc2\xc2A\x18\x84e\xe8\x02\x800\x88\x0c\x07a\x10\x99\xa8\x0b\x00B$,\x1c\x84AX\x86.\x00\x08\x83\xc8p\x10\x06\x91\x89\xba(\x02$|\xf7\x89\xe8z\xaa7+\xe1\xb7/z\x06\xa4\xf7X\xc8\x06D\xd3\xcf\x93I\xf9.m\xcf\x80\xb4\xff\x9ad\xfa\x9aZ\xf2\x1d\xb1n\x01\x91\x02G\x98\xbe\x9e\x01\t\xe3\xd3\x02\t\x00y[L$\xc1a\x01\x10-\x90\x00\x90\xf0\xa9H\xef\x0e\x7f\x1e\x1f~\xa4\x1b/}\xc9\xde\x1ddRP\xba\x93\x98\x07D"\x1cV\x1cD\x03$\xa6\x01\x91\n\x875@$\xa7[f\x01\x91\x0c\x87E@\xa4Bb\x12\x10\xe9pX\x05D"$\xe6\x00\xd1\x00\x87e@\xa4Ab\n\x10-pX\x07D\x12$f\x00\xd1\x04\x07\x00\x19\xcf\xb7$\x1c\x01\x9b\x00D\x1b\x1c\x00\xe4r\x9f\xe9\xc3\x97\xef\xcf\xce\r\xcd\x9e\xe33\x01\x88s\xfe\xf8\xf2\xf4\xeb3\xfd\xed\xbdz=X\xb9Q\x18S\x0c\x80\xc4\x14\xaa\xf4wm.\x02@\x90bU\n\xfd\xf4fJ\xec2\xbd\xf5\xba%\xad\x03"\xe5\t\xf0\x92\x98Q\xfc4\xaf\x9eT\xcb: \xadS\xabi\xb93\x06\x88\xcc\x07\x13\xd7\xbc\xc72 \x12N\xaf\xcc\x02\x12\x06^\xb2*\xd4M\xa0\xe2\xadY\x05DJje\x1a\x10\r\x90X\x05\x84j\xdc\xf1%i\xbdD\xc9b\xaax\x0f2\x17A\xf6~\x84*P^\x9e\x1e\xb2\xe7\xaf4\xb8r\xebI\xd9w\xcc\xaf\xdb0 A\x06\xb9\x90X\x03D\xd2\xbe\x03\x80\xcc\x14\x90z\x7f\xc4\x12 R\xe1(M\xc5\xb3-Z\xda\xc6ki\xfd\x12!\xb1\x02\x88d8\x00\x88`\'\xb1\x00\x88t8\x00\xc8\xc2J$9I\xef\x80h\x80\x03\x80\xac\x1c\xb3H\x81\xa4g@\xb4\xc0\x01@n\x9cCJ\x80\xa4W@4\xc1\x01@6\x0e\xea[C\xd2# \xda\xe0\x00 \xd1;Y\xed\xee\x93\xf4\x06\x88\xc4\x9b\x80\xd1\xe9/|,\xa9\xbbc\xde\x98P-\xdc\xa4\x17@\xa4\x1f\xf1\xc7\xe6^\xec\x9d\xf4\x10\x94\'\x8b\x1b\xdc}l\x10\x1c\x7f\xe7\x86\xa4\x07@$\xa5T\xa5\xf1$\x12\x90y0J\xb2fNH\xb4\x03"i\xde\xe6\x8f\x14\xe5B+\x0e\x90\xb5 \x94%6\xcf;%Z\x01\x19S\xaa\xe1\xbe\xe5\x87\x16\x96\x19\xc5\xf2\x01\xcd\x1cHD\x01rk\x85\x96\x98\xc7R\xbb\x896@$\x82\xb1u\n\x95\n\x89\x18@b\x01\'\x11\x92\xd3\xf3\xc0D?\x9d\xa0\t\x90\xd4`\xe3\xd8+\xbe{\xa1!27)\xd7-\x02\x90\xd4 K\x19\x10\xf7$L\xfd\xa5\x8e!\xf5\xfa4\x00"\xd55r\x16\xaeXL5\x07$7\xb0b\x03J\r@\xaar\xb9\xe3\xb9u\x1d\x92\x01\x91\x0cF\x0e\x1c\x93\xf6[1\xd5\x14\x90\xd2`\x92\xb6i_\x0b\xf2\xd2\xb1MmI\x04D:\x18\xa3ve7woA\xd2\x0c\x90\xbd\x01$\xddI\xf6\xa6^R\x00\tP\xdc\xb9\xd7OR\xeeGm;\x7f\x19\x1c[N\xd2\x04\x90\xbdp\xa4X#U\nU\xde\xae?z?\xfcN\xfd-\xc4\x96\x80L\xbfc.\xed\xb8vK{\xaa\x98b\x07\xa4\xd6@tBr\xf6\x95(,-\x00\xd1\x91B]cB\x19S\xac\x80\xd4\x1e\x88nH.\x13==\x06\xf1\xea\xee\x8e\x7f\x1f\x7f\x1e\xc3_\xa8\x01y\xef\x12\xa7;\x06\xcd\xbe\xa0^\xee\xcat\xc7\xecS\n\xcf\x06H\xc8cS\xd3\x8b\x12\xc1\xb4\xecI\xd2\xc6\xe6\x8ft\x01K\xd9v\xda\xe8j\x95\xa2Zp\xe7\x0b\xef|\xd1J\xbd\xee\xec\xa7yS\x1b\xde[\xae/H\xf6\xaa\xd1w}j8\xf6\xa8\'\x16\x900(\xa9w\xdc\xf7\x08\x8e\xba\xef\x15(I{85\x14\r\xc8$\x84\x86{%\x9c\x93\xd6G_\xfe8x\x7f\x98\xf6iR\xc7\xa4\x02\x90 \x1eR.\xa9!\x94\x7f]\x92S\xaa\xe5h\xd4\x00\x02H\xf2\x03Qb\r\xe9)\x95j@\xe6\'\x12:\xee\x06K\x0c\xd1V\xd7\xb4\xef\xcex\xb3\xabn\xd5\xf1\xde~\x91r\xedU\x90\xaf\xbe\xa6\x94\xaa\x0b\x07\x99\x0f\x02\x1bx\xbe@\xcf\xefI\xc7F|k\\\xaa\xf6 \xb7\x06\x027\xc9\x0f]\xea\x1a\xda\xf6\x1a\xb7\xf4\xe8\x02\x10\xecM\xa8\xc3=\xbd}\xcd\xe9\xd4\xda(\xbb\x02\x04\xa0\xa4\x07r\xfd\x92:7\xe11\x1d\xba\x04\x04\xa0\xc4\xa6\xbd\xe6\xdf\xf5\xef3\xba\xdf\x83l\r0\xecO\xc2\xdfq,\\\x13\x8a\xd0V\xdf`Lju\xed \xf3\x90\x00(\xb5\x00\xb1\x01\x869@\x96\xb0\xc0Qr\x80\x19\xdf\xa0,y\\<\xa7\x17\x89e\xcd8\xc8\x9a\xf8p\x95XH\xdar\x0b3\xa7X\xb1i\xbf\x05\x0b\\\xe5\x9cX\xa8x\xd2\xb6d\x9es\xeb\x98v\x905\xb1._\xfe\xf0\x1f\xe9\xde\x04\xcc\x9d&\xea\xf2\xe1\xcdD\xe74<~N\xad\xc4\xb2}\x00\x12Q\xbc\xdf4lL\x9f\xc2\xf0\xa5\xbf\x93\xc1\r\xc5\xbc?\x00\x92\xa1\xbenw\x81KdL\xf5\xb9(\x00)Q\xed\xad\xce\x04\xcc)=\x19\xa4\xa4d#\x08\xd3\xa9\x13\x1cb\xc7\x04\x07\x1d\xf7UG\xed\xad}\xcc\xf4\xb7\x11\x9e\x13F\x15>\xc73\x020\xfd\x03\x08\xb41\x08@h\xf5Mj}\xfa\xae\xd5\xb20\xf6\x06I\xf2\x91\x16\x02 \xa4\xf2\xa2q\xed\n\x00\x10\xed3\x88\xeb\'U\x00\x80\x90\xca\x8b\xc6\xb5+\x00@\xb4\xcf \xae\x9fT\x01\x00B*/\x1a\xd7\xae\x00\x00\xd1>\x83\xb8~R\x05\x00\x08\xa9\xbch\\\xbb\x02\x00D\xfb\x0c\xe2\xfaI\x15\x00 \xa4\xf2\xa2q\xed\n\x00\x10\xed3\x88\xeb\'U\x00\x80\x90\xca\x8b\xc6\xb5+\x00@\xb4\xcf \xae\x9fT\x81\xff\x01i\xaa\x9aA\x89@\xd8b\x00\x00\x00\x00IEND\xaeB`\x82'
icon_id = idaapi.load_custom_icon(data=icon, format="png")

class Utils:    
    def get_func_name(ea):
        # Get pretty function name
        func_name = Utils.get_pretty_func_name(idc.get_func_name(ea))
        if not func_name:
            func_name = Utils.get_pretty_func_name(idc.get_name(ea))
        return func_name

    def get_pretty_func_name(name):
        # Demangle function name
        demangled_name = idc.demangle_name(name, idc.get_inf_attr(idc.INF_SHORT_DN))
        # Return as is if failed to demangle
        if not demangled_name:
            return name
        # Cut arguments
        return demangled_name[:demangled_name.find("(")]
    
    def prep_func_name(name):
        if name[0] != "." and name[0] != "_":
            # Name does not start with dot or underscore
            return [name,f".{name}",f"_{name}"]
        else:
            return [name[1:],f".{name[1:]}",f"_{name[1:]}"]

class Graph():
    def __init__(self):
        self.graph = defaultdict(list)
        self.all_nodes = set()
    
    def add_edge(self, u, v):
        self.all_nodes.add(u)
        self.all_nodes.add(v)
        self.graph[u].append(v)

    def is_path_exists(self, source, target, visited=None):
        if visited is None:
            visited = set()
        if source == target:
            return True
        visited.add(source)
        for neighbor in self.graph[source]:
            if neighbor not in visited:
                if self.is_path_exists(neighbor, target, visited):
                    return True
        return False

    def is_node_exists(self, node):
        return node in self.all_nodes
    
    def get_neighbors(self, node):
        return self.graph[node]
    
    def get_all_nodes(self):
        return list(self.all_nodes)


class LibraryAnalyzer():
    def __init__(self):
        self.bin_path = None
        self.rootfs_path = None
        self.libraries_graph = None

    def init_analyzer(self, bin_path, rootfs_path):
        self.bin_path = bin_path
        self.rootfs_path = rootfs_path
        self.libraries_graph = Graph()
        
        self.generate_libraries_graph()

    def get_needed_libraries(self, bin_path):
        # parse .dynamic section of the binary
        needed_libraries = []
        with open(bin_path, 'rb') as f:
            elffile = ELFFile(f)
            for segment in elffile.iter_segments():
                if segment.header.p_type == 'PT_DYNAMIC':
                    dynamic_segment = segment
                    break
            if not dynamic_segment:
                print("%s: No dynamic segment found" % bin_path)
                return []
            for tag in dynamic_segment.iter_tags():
                if tag.entry.d_tag == 'DT_NEEDED':
                    library_name = elffile.get_section_by_name('.dynstr').get_string(tag.entry.d_val)
                    if library_name not in needed_libraries:
                        library_path = self.locate_bin_in_rootfs(library_name)
                        # print("%s: depends on %s" % (bin_path, library_path))
                        needed_libraries.append(library_path)

        return needed_libraries
    
    def generate_libraries_graph(self):
        queue = deque()
        visited = set()
        queue.append(self.bin_path)

        while queue:
            current_bin_path = queue.popleft()
            if current_bin_path in visited:
                continue
            visited.add(current_bin_path)
            needed_libraries = self.get_needed_libraries(current_bin_path)
            for library_path in needed_libraries:
                self.libraries_graph.add_edge(current_bin_path, library_path)
                # print("Adding edge: %s -> %s" % (current_bin_path, library_path))
                if library_path not in visited:
                    queue.append(library_path)
                
        
        # print("Graph: %s" % self.libraries_graph.graph)
        # print("All nodes: %s" % self.libraries_graph.get_all_nodes())

    def locate_bin_in_rootfs(self, bin_name):
        result_path = None
        for dirpath, dirnames, filenames in os.walk(self.rootfs_path):
            if bin_name in filenames:
                bin_path = os.path.join(dirpath, bin_name)
                if os.path.islink(bin_path):
                    real_path = os.path.realpath(bin_path)
                    if os.path.exists(real_path):
                        return real_path
                    # else:
                    #     print("Symbolic link %s points to non-exist file" % bin_path)
                else:
                    result_path = bin_path
        if result_path:
            return result_path
        # print("[!] %s not found in rootfs" % bin_name)
        return None
                    

class FunctionAnalyzer():
    def __init__(self):
        self.bins_graph = None
        self.exported_functions = None

    def init_analyzer(self, bins_graph):
        self.bins_graph = bins_graph
        self.exported_functions = defaultdict(list)
        
        self.init_exported_functions(bins_graph)
    
    def init_exported_functions(self, bin_path):
        for bin_path in self.bins_graph.get_all_nodes():
            if bin_path not in self.exported_functions:
                self.exported_functions[bin_path] = []

            with open(bin_path, 'rb') as f:
                elffile = ELFFile(f)
                # find .dynsym section and parse it
                for section in elffile.iter_sections():
                    if section.name == '.dynsym':
                        for sym in section.iter_symbols():
                            if sym.entry.st_info['type'] == 'STT_FUNC'  and sym.entry['st_shndx'] != 'SHN_UNDEF':
                                self.exported_functions[bin_path].append(sym.name)
        # print(self.exported_functions)

    def locate_function_in_bin(self, function_name):
        if not self.exported_functions:
            print("No exported functions found in any binary")
            return None
        for bin_path, functions in self.exported_functions.items():
            for func in functions:
                if function_name in Utils.prep_func_name(func):
                    print("Function: %s -> %s" % (function_name, bin_path))
                    return bin_path
        print("Function: %s not found in any binary" % function_name)
        return None

class Locator():
    def __init__(self):
        self.libraries_analyzer = LibraryAnalyzer()
        self.functions_analyzer = FunctionAnalyzer()
        self.rootfs_path = None
        self.bin_path = None

    def init_libraries_analyzer(self, bin_path, rootfs_path):
        self.rootfs_path = rootfs_path
        self.bin_path = bin_path
        self.libraries_analyzer.init_analyzer(bin_path, rootfs_path)

    def init_functions_analyzer(self, bins_graph):
        self.functions_analyzer.init_analyzer(bins_graph)

class LocatorHandler(idaapi.action_handler_t):
    def __init__(self, function_name):
        idaapi.action_handler_t.__init__(self)
        self.function_name = function_name

    def activate(self, ctx):
        global locator
        # print(locator.libraries_analyzer.libraries_graph.graph)
        # print(locator.functions_analyzer.exported_functions)

        if locator.rootfs_path is None:
            ida_kernwin.warning("Please init plugin: input the rootfs path \nEdit -> Plugins -> Function Locator")
            return 0

        target_bin_path = locator.functions_analyzer.locate_function_in_bin(self.function_name)
        if not target_bin_path:
            ida_kernwin.warning("Function: %s not found in any binary" % self.function_name)
            return 0
        target_bin_relative_path = os.path.relpath(target_bin_path, locator.rootfs_path)
        ida_kernwin.info("Function: %s -> %s" % (self.function_name, target_bin_relative_path))        

        return 1

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

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

    def finish_populating_widget_popup(self, form, popup):
        function_ea = idc.here()
        function_name = Utils.get_func_name(function_ea)

        action_text = f"Locate '{function_name}' in rootfs"
        try:
            # Get selected symbol
            selected_symbol, _ = ida_kernwin.get_highlight(ida_kernwin.get_current_viewer())
            # Check if it is a function name
            for function in idautils.Functions():
                if Utils.get_func_name(function) in Utils.prep_func_name(selected_symbol):
                    action_text = f"Locate '{Utils.get_func_name(function)}' in rootfs"
                    function_name = Utils.get_func_name(function)
        except:
            pass
        action_desc = idaapi.action_desc_t(
            'Function Locator',   # The action name. This acts like an ID and must be unique
            action_text,    # The action text.
            LocatorHandler(function_name),       # The action handler.
            '',             # Optional: the action shortcut
            'locate the library that contains the function in the rootfs',  # Optional: the action tooltip (available in menus/toolbar)
            icon_id         # Optional: the action icon (shows when in menus/toolbars)
        )
        idaapi.unregister_action("Function Locator")
        idaapi.register_action(action_desc)
        if ida_kernwin.get_widget_type(form) == idaapi.BWN_DISASM or ida_kernwin.get_widget_type(form) == idaapi.BWN_PSEUDOCODE:
            idaapi.attach_action_to_popup(form, popup, "Function Locator", "")


class FunctionLocatorPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "Function Locator"
    help = "a plugin hels IOT Security Researchers to locate the library that contains the function in the rootfs"
    wanted_name = "Function Locator"
    wanted_hotkey = "Alt-F"

    def init(self):
        self.rootfs_path = None
        self.bin_path = None
        return idaapi.PLUGIN_OK

    def run(self, arg):
        global locator
        importlib.reload(function_locator)
        self.bin_path = ida_nalt.get_input_file_path()
        self.rootfs_path = ida_kernwin.ask_str("", 0, "Enter the path to the rootfs")
        if not self.rootfs_path or not os.path.exists(self.rootfs_path) or not os.path.isdir(self.rootfs_path):
            ida_kernwin.warning("Invalid rootfs path")
            return
        
        locator.init_libraries_analyzer(self.bin_path, self.rootfs_path)
        bins_graph = locator.libraries_analyzer.libraries_graph
        locator.init_functions_analyzer(bins_graph)
            
    def term(self):
        pass

hooks = Hooks()
hooks.hook()
locator = Locator()

def PLUGIN_ENTRY():
    return FunctionLocatorPlugin()


repository: Russinovich/IDA7.5_SDK
file: python/examples/hexrays/vds6.py
description: SDK and IDAPython for IDA Pro 7.5
wanted_name: Hex-Rays space remover (IDAPython)
comment: Sample plugin6 for Hex-Rays decompiler
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/vds6.py

content:
"""
This is a crude (and not very pythonic) reimplementation of the example
hexrays plugin 'hexrays_sample6.cpp', shipped with the Hex-Rays decompiler.

It modifies the decompilation output: removes some space characters.
"""
from __future__ import print_function

import idautils
import idc
import ida_idaapi
import ida_hexrays
import ida_lines

def dbg(msg):
    #print(msg)
    pass

def is_cident_char(c):
    return c.isalnum() or c == '_'

def my_tag_skipcodes(l, storage):
    n = ida_lines.tag_skipcodes(l)
    dbg("Skipping %d chars ('%s')" % (n, l[0:n]))
    storage.append(l[0:n])
    return l[n:]

def remove_spaces(sl):
    dbg("*" * 80)
    l = sl.line
    out = []

    def push(c):
        dbg("Appending '%s'" % c)
        out.append(c)

    # skip initial spaces, do not compress them
    while True:
        l = my_tag_skipcodes(l, out)
        if not l:
            break
        c = l[0]
        if not c.isspace():
            break
        push(c)
        l = l[1:]

    # remove all spaces except in string and char constants
    delim = None # if not None, then we are skipping until 'delim'
    last = None # last seen character
    while True:
        # go until comments
        if l.startswith("//"):
            push(l)
            break
        dbg("-" * 60)
        nchars = ida_lines.tag_advance(l, 1)
        push(l[0:nchars])
        l = l[nchars:]
        l = my_tag_skipcodes(l, out)
        if not l:
            break
        c = l[0]
        dbg("c: '%s', last: '%s', l: '%s'" % (c, last, l))
        if delim:
            # we're inside a literal.
            if c == delim:
                delim = None # literal ended
        elif c == '"' or c == "'":
            delim = c # string/char literal started
        elif c.isspace():
            end = l.lstrip()
            nptr = my_tag_skipcodes(end, out)
            dbg("end: '%s', nptr: '%s'" % (end, nptr))
            # do not concatenate idents
            if not is_cident_char(last) or not is_cident_char(nptr[0]):
                l = end
                c = l[0] if l else ''
                dbg("new l: '%s'" % l)
        last = l[0] if l else ''

    sl.line = "".join(out)


class vds6_hooks_t(ida_hexrays.Hexrays_Hooks):
    def func_printed(self, cfunc):
        for sl in cfunc.get_pseudocode():
            remove_spaces(sl);
        return 0

# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "Hex-Rays space remover (IDAPython)"
    wanted_hotkey = ""
    comment = "Sample plugin6 for Hex-Rays decompiler"
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            self.vds6_hooks = vds6_hooks_t()
            self.vds6_hooks.hook()
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        self.vds6_hooks.unhook()
    def run(self, arg):
        pass

def PLUGIN_ENTRY():
    return my_plugin_t()


repository: 19h/ida-plugins
file: xrefdump-fw/xrefdump.py
wanted_name: X CallGraph Exporter
comment: Exports a Graphviz DOT file representing function call relationships based on code xrefs.
created_at: 2025-04-08 22:54:27+00:00
pushed_at: 2025-04-08 23:07:17+00:00
url:
https://github.com/19h/ida-plugins/blob/b045b6f6bc9ae2a53104986fb74f3fe1198e5d2e/xrefdump-fw/xrefdump.py

content:
import idaapi
import idautils
import idc
import ida_kernwin
import ida_xref

def export_call_graph():
    """
    Enumerates all functions in the current IDB, inspects code xrefs of type fl_CN and fl_CF
    from each instruction, and for each call xref pointing to a valid function, records an
    edge from the caller to the callee. The complete set of edges is then written to a
    Graphviz DOT file.
    """
    # Prompt user for output file
    outfile = ida_kernwin.ask_file(False, "*.dot", "Select output DOT file")
    if not outfile:
        print("No output file selected.")
        return

    # Set to store unique edges (caller -> callee)
    edges = set()
    
    # Iterate over all function entry points in the database
    for func_ea in idautils.Functions():
        src_name = idc.get_func_name(func_ea)
        if not src_name:
            continue  # Skip unnamed functions
        
        # Iterate over all instructions in the function
        for ea in idautils.FuncItems(func_ea):
            # Get all xrefs emanating from this instruction
            for xref in idautils.XrefsFrom(ea):
                # Filter for call xrefs (near or far calls)
                if xref.type == ida_xref.fl_CN or xref.type == ida_xref.fl_CF:
                    tgt_ea = xref.to  # Target address of the xref
                    tgt_func = idaapi.get_func(tgt_ea)
                    if not tgt_func:
                        continue  # Skip if target is not within a function
                    tgt_name = idc.get_func_name(tgt_func.start_ea)
                    if not tgt_name:
                        continue  # Skip unnamed target functions
                    edges.add((src_name, tgt_name))

    # Write the call graph to the DOT file
    try:
        with open(outfile, "w") as f:
            f.write("digraph CallGraph {\n")
            f.write("    rankdir=LR;\n")
            f.write("    node [shape=box];\n")
            f.write("    edge [arrowhead=vee];\n")
            f.write("    splines=ortho;\n")
            f.write("    overlap=false;\n")
            for (src, tgt) in sorted(edges):
                f.write('    "{}" -> "{}";\n'.format(src, tgt))
            f.write("}\n")
        print("[*] Call graph exported to: {}".format(outfile))
    except Exception as e:
        print("Error writing DOT file: {}".format(e))

class XRefDumpPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = "Exports a Graphviz DOT file representing function call relationships based on code xrefs."
    help = ("This plugin iterates over all functions, inspects code xrefs of type fl_CN and fl_CF, "
            "and exports the call relationships in Graphviz DOT format.")
    wanted_name = "X CallGraph Exporter"
    wanted_hotkey = ""

    def init(self):
        print("[*] CallGraph Exporter plugin initialized.")
        return idaapi.PLUGIN_OK

    def run(self, arg):
        export_call_graph()

    def term(self):
        print("[*] CallGraph Exporter plugin terminated.")

def PLUGIN_ENTRY():
    return XRefDumpPlugin()


repository: REMnux/distro
file: ppasrc/ghidra/ghidra-10.2-focal/ghidra/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2014-09-12 19:41:54+00:00
pushed_at: 2025-02-26 18:45:43+00:00
description:
This repository contains supplemental files for the REMnux distro and the source files for the Debian packages that the distro installs from the REMnux package repository on Launchpad.

url:
https://github.com/REMnux/distro/blob/9789bbc22b57362bc26e7025059bf72ac2f7bcd9/ppasrc/ghidra/ghidra-10.2-focal/ghidra/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py

content:
## ###
#  IP: GHIDRA
# 
#  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.
##
#---------------------------------------------------------------------
# xmlexp.py - IDA XML Exporter plugin
#---------------------------------------------------------------------
"""
Plugin for IDA which exports a XML PROGRAM document file from a database.
"""

import idaapi
import idautils
import idc
import datetime
import sys
import time


XML_EXPORTER_VERSION = "4.1.2"
BASELINE_IDA_VERSION = 620
IDA_SDK_VERSION = idaapi.IDA_SDK_VERSION
BADADDR = idaapi.BADADDR


class XmlExporterPlugin(idaapi.plugin_t):
    """
    XML Exporter plugin class
    """
    flags = 0
    comment = "Export database as XML file"
    help = "Export database as XML <PROGRAM> document"
    wanted_name = "XML Exporter"
    wanted_hotkey = "Ctrl-Shift-x"


    def init(self):
        """
        init function for XML Exporter plugin.
        
        Returns:
            Constant PLUGIN_OK if this IDA version supports the plugin,
            else returns PLUGIN_SKIP if this IDA is older than the supported
            baseline version.
        """
        if IDA_SDK_VERSION < BASELINE_IDA_VERSION:
            idaapi.msg('\nXML Exporter plugin (xmlexp.py) not supported ' +
                        'by this version of IDA\n')
            return idaapi.PLUGIN_SKIP
        return idaapi.PLUGIN_OK


    def run(self, arg):
        """
        run function for XML Exporter plugin.
        
        Args:
            arg: Integer, non-zero value enables auto-run feature for
                IDA batch (no gui) processing mode. Default is 0.
        """
        xml = XmlExporter(arg)
        try:
            try:
                xml.export_xml()
            except Cancelled:
                idaapi.hide_wait_box()
                msg = "XML Export cancelled!"
                print "\n" + msg
                idaapi.warning(msg)
            except:
                idaapi.hide_wait_box()
                msg = "***** Exception occurred: XML Exporter failed! *****"
                print "\n" + msg + "\n", sys.exc_type, sys.exc_value
                idaapi.warning(msg)
        finally:
            xml.cleanup()
            idaapi.setStat(xml.state)


    def term(self):
        pass


def PLUGIN_ENTRY():
    return XmlExporterPlugin()


class Cancelled(Exception):
    pass


class FileError(Exception):
    pass


class XmlExporter:
    """
    XML Exporter contains methods to export an IDA database as a
        XML PROGRAM document.
    """
    def __init__(self, arg):
        """
        Initializes the XmlExporter attributes

        Args:
            arg: Integer, non-zero value enables auto-run feature for
                IDA batch (no gui) processing mode. Default is 0.
        """
        self.debug = False
        self.autorun = False
        if arg != 0:
            self.autorun = True
        self.state = idaapi.setStat(idaapi.st_Work)
        self.indent_level = 0
        self.seg_addr = False
        self.has_overlays = False
        self.hexrays = False
        self.options = None
        self.xmlfile = 0
        self.elements = {}
        self.counters = []
        self.tags = []
        
        # initialize class variables from database
        self.inf = idaapi.get_inf_structure()
        self.min_ea = self.inf.minEA
        self.max_ea = self.inf.maxEA
        self.cbsize = (idaapi.ph_get_cnbits()+7)/8
        self.processor = str.upper(idaapi.get_idp_name())
        self.batch = idaapi.cvar.batch


    def export_xml(self):
        """
        Exports the IDA database to a XML PROGRAM document file.
        """
        self.check_and_load_decompiler()
        self.display_xml_exporter_version()
        self.display_database_info()
        
        self.get_options()
    
        if (self.autorun == True):
            (self.filename, ext) = os.path.splitext(idaapi.cvar.database_idb)
            self.filename += ".xml"
        else:
            self.filename=idaapi.askfile_c(1, "*.xml",
                                           "Enter name of export xml file:")
            
        if self.filename == None or len(self.filename) == 0:
            raise Cancelled
        self.xmlfile = self.open_file(self.filename, "w")
        
        idaapi.show_wait_box("Exporting XML <PROGRAM> document ....")
        idaapi.msg("\n------------------------------------------------" +
                   "-----------")
        idaapi.msg("\nExporting XML <PROGRAM> document ....")
        begin = time.clock()
        
        self.write_xml_declaration()
        self.export_program()
        
        # export database items based on options
        if (self.options.DataTypes.checked         == True or
            self.options.DataDefinitions.checked   == True or 
            self.options.Functions.checked         == True):
            self.export_datatypes()
        if (self.options.MemorySections.checked    == True or
            self.options.MemoryContent.checked     == True):
            self.export_memory_map()
        if (self.options.RegisterValues.checked    == True):
            self.export_register_values()   
        if (self.options.CodeBlocks.checked        == True):
            self.export_code()  
        if (self.options.DataDefinitions.checked   == True):
            self.export_data()  
        if (self.options.Comments.checked          == True):
            self.export_comments()
            self.export_bookmarks()     
        if (self.options.EntryPoints.checked       == True):
            self.export_program_entry_points()  
        if (self.options.Symbols.checked           == True):
            self.export_symbol_table()  
        if (self.options.Functions.checked         == True):
            self.export_functions() 
        if (self.options.MemoryReferences.checked  == True or 
            self.options.StackReferences.checked   == True or
            self.options.Manual.checked            == True or
            self.options.DataTypes.checked         == True):
            self.export_markup()    
        self.end_element(PROGRAM)
        
        idaapi.msg('\n%35s' % 'Total ')
        self.display_cpu_time(begin)
        idaapi.hide_wait_box()  
        self.display_summary()


    def check_and_load_decompiler(self):
        """
        Checks for the presence of a decompiler plugin for the database.
        
        Note: The decompiler must be loaded by the XML Exporter plugin
            if it is running in batch mode. IDA will load the decompiler
            plugin automatically if not in batch mode.
        
        Note: There was no support for decompiler plugins in IDAPython until
            IDA 6.6, so skip if this is an older version.
        
        Note: Currently the 4 decompiler plugins for the  x86, x64,
            ARM32, and ARM64 are supported.
        """
        if self.batch == 0 or IDA_SDK_VERSION < 660:
            return
        plugin = ''
        if self.processor == 'PC':
            if self.inf.is_64bit():
                plugin = "hexx64"
            elif self.inf.is_32bit():
                plugin = 'hexrays'
        elif self.processor == 'ARM':
            if self.inf.is_64bit():
                plugin = "hexarm64"
            elif self.inf.is_32bit():
                plugin = "hexarm"
        if len(plugin) > 0:
            try:
                idaapi.load_plugin(plugin)
                self.hexrays = idaapi.init_hexrays_plugin()
            except:
                return
    

    def check_char(self, ch):
        """
        Replaces a special XML character with an entity string.
        
        Args:
            ch: String containing the character to check.
            
        Returns:
            String containing either the character or the entity
            substition string.
        """
        if ((ord(ch) < 0x20) and (ord(ch) != 0x09 and
             ord(ch) != 0x0A and ord(ch) != 0x0D)): return ''
        elif ch == '&' :  return '&amp;'
        elif ch == '<' :  return "&lt;"
        elif ch == '>' :  return "&gt;"
        elif ch == '\'' : return "&apos;"
        elif ch == '"' :  return "&quot;"
        elif ch == '\x7F': return ''
        elif ord(ch) > 0x7F: return '&#x' + format(ord(ch),"x") + ";"
        return ch
    

    def check_for_entities(self, text):
        """
        Checks all characters in a string for special XML characters.
        
        Args:
            text: String to check for special XML characters.
            
        Returns:
            String containing original string with substitutions for
                any special XML characters.
        """
        new = ''
        for c in text:
            new += self.check_char(c)
        return new
    

    def check_if_seg_contents(self, seg):
        """
        Determines if any address in a segment contains a value.
        
        Args:
            seg: IDA segment object
            
        Returns:
            True if any address in a segment contains a value.
            False if no address in a segment contains a value.
        """
        for addr in idautils.Heads(seg.startEA, seg.endEA):
            if idaapi.hasValue(idaapi.getFlags(addr)) == True:
                return True
        return False


    def check_stack_frame(self, sframe):
        """
        Determines if stack frame contains any parameters or local variables.
        
        Args:
            sframe: IDA stack frame for a function.
            
        Returns:
            True if stack frame has parameters or local variables.
            False if stack frame has no parameters or local variables.
        """
        n = sframe.memqty
        for i in range(n):
            member = sframe.get_member(i)
            if member == None:
                continue
            mname = idaapi.get_member_name(member.id)
            if mname != None and len(mname) > 0: 
                if mname != " s" and mname != " r":
                    return True
        return False


    def cleanup(self):
        """
        Frees memory and closes message box and XML file at termination.
        """
        if self.options != None:
            self.options.Free()
        idaapi.hide_wait_box()
        self.close_xmlfile()


    def close_binfile(self):
        """
        Closes the binary data file for the XML Exporter.
        """
        if self.binfile != 0:
            self.binfile.close()
            self.binfile = 0


    def close_tag(self, has_contents=False):
        """
        Closes the start tag for an XML element.
        
        Args:
            has_contents: Boolean indicating if the element has
            sub-elements or text.
        """
        if has_contents:
            self.write_to_xmlfile(">")
            self.indent_level += 1
        else:
            self.write_to_xmlfile(" />")


    def close_xmlfile(self):
        """
        Closes the XML data file for the XML Exporter.
        """
        if self.xmlfile != 0:
            self.xmlfile.close()
            self.xmlfile = 0


    def dbg(self, message):
        """
        Outputs debug message if debug flag is enabled.
        
        Args:
            message: String containing the debug message.
        """
        if (self.debug == True):
            idaapi.msg(message)


    def display_cpu_time(self, start):
        """
        Displays the elapsed CPU time since the start time.
        
        Args:
            start: Floating-point value representing start time in seconds.
        """
        idaapi.msg('CPU time: %6.4f' % (time.clock() - start))
        

    def display_database_info(self):
        """
        Displays information about the database in IDA output window.
        """
        idaapi.msg('\nProcessor Module: ' + self.processor)


    def display_message(self, message):
        """
        Displays an indented message to IDA output window.
        
        Args:
            message: String containing message to display.
        """
        line = "    " + message
        idaapi.msg('\n%-35s' % line)
        

    def display_summary(self):
        """
        Displays summary of exported PROGRAM document in IDA output window.
        """
        fileline = '\nFile: %s' % self.filename
        summary  = "\n--------------------------------------"
        total = 0
        tags = self.tags[:]
        for tag in tags:
            count = self.counters[self.elements[tag]]
            summary += "\n%-22s %8d" % (tag, count)
            total += count
        summary += ("\n%-22s %8d" % ("Total XML Elements:",total))
        idaapi.msg(summary)
        idaapi.msg("\n\nDatabase exported to: %s" % self.filename)
        idaapi.msg("\n--------------------------------------------" +
                   "---------------")
        if self.autorun == False:
            frmt  = "TITLE XML Export Successful!\n"
            frmt += "ICON INFO\n"
            frmt += "AUTOHIDE NONE\n"
            frmt += "HIDECANCEL\n"
            details = '\nSee output window for details...'
            idaapi.info("%s" % (frmt + fileline + details))


    def display_xml_exporter_version(self):
        """
        Displays XML Exporter plugin version info in IDA output window.
        """
        plugin = idaapi.idadir(idaapi.PLG_SUBDIR) + '/xmlexp.py'
        plugintime = time.localtime(os.path.getmtime(plugin))
        # ts = time.strftime('%Y-%m-%d %H:%M:%S', plugintime)
        ts = time.strftime('%b %d %Y %H:%M:%S', plugintime)
        version = "\nXML Exporter Version " + XML_EXPORTER_VERSION
        version += " : SDK " + str(IDA_SDK_VERSION)
        version += " : Python : "+ ts + '\n'
        idaapi.msg(version)
    

    def end_element(self, tag, newline=True):
        """
        Writes the element end tag to the XML file.
        
        Args:
            tag: String containing the element name.
            newline: Boolean indicating if end tag should go on new line.
        """
        self.indent_level -= 1
        if newline:
            start = '\n' + ("    " * self.indent_level)
        else:
            start = ''
        self.write_to_xmlfile(start  + "</" + tag + ">")


    '''
    # BIT_MASK not currently supported for ENUM
    def export_bitmask(self, eid, mask):
        """
        Exports an enum bitmask member as BIT_MASK element.
        
        Args:
            eid: Integer representing the IDA enum id
            mask: Integer representing the IDA enum mask value
        """
        name = idaapi.get_bmask_name(eid, mask)
        if name == None:
            return
        self.start_element(BIT_MASK)
        self.write_attribute(NAME, name)
        self.write_numeric_attribute(VALUE, mask)
        regcmt = idaapi.get_bmask_cmt(eid, mask, False)
        rptcmt = idaapi.get_bmask_cmt(eid, mask, True)
        has_comment =  regcmt != None or rptcmt != None
        self.close_tag(has_comment)
        if regcmt != None and len(regcmt) > 0:
            self.export_regular_cmt(regcmt)
        if rptcmt != None and len(rptcmt) > 0:
            self.export_repeatable_cmt(rptcmt)
        if (has_comment):
            self.end_element(BIT_MASK)
    '''


    def export_bookmarks(self):
        """
        Exports marked location descriptions as BOOKMARK elements.
        """
        found = False
        timer = time.clock()
        if IDA_SDK_VERSION > 690:
            import ida_moves
            import ida_pro
        for slot in range(1,1025):
            if IDA_SDK_VERSION <= 690:
                address = idc.GetMarkedPos(slot)
                description = idc.GetMarkComment(slot)
            else:
                curloc = ida_moves.curloc()
                intp = ida_pro.int_pointer()
                intp.assign(slot)
                address = curloc.markedpos(intp)
                description = curloc.markdesc(slot)
            if address == BADADDR:
                continue
            if description == None:
                continue
            if found == False:
                found = True
                self.update_status(BOOKMARKS)
                self.start_element(BOOKMARKS, True)
            self.start_element(BOOKMARK)
            self.write_address_attribute(ADDRESS, address)
            self.write_attribute(DESCRIPTION, description)
            self.close_tag()
        if found:
            self.end_element(BOOKMARKS)
            self.display_cpu_time(timer)


    def export_c_comments(self):
        """
        Exports block and end-of-line comments entered in the decompiler
        interface.
        """
        if IDA_SDK_VERSION < 660 or self.hexrays == False:
            return
        functions = idautils.Functions()
        if functions == None:
            return
        for addr in functions:
            try:
                if idaapi.is_spec_ea(addr):
                    continue
                ccmts = idaapi.restore_user_cmts(addr)
                if ccmts == None:
                    continue
                p = idaapi.user_cmts_begin(ccmts)
                while p != idaapi.user_cmts_end(ccmts):
                    cmk = idaapi.user_cmts_first(p)
                    cmv = idaapi.user_cmts_second(p)
                    if cmk.itp < (idaapi.ITP_COLON+1):
                        self.export_comment(cmk.ea, "end-of-line", cmv.c_str())
                    else:
                        self.export_comment(cmk.ea, "pre", cmv.c_str())
                    p=idaapi.user_cmts_next(p)
                idaapi.user_cmts_free(ccmts)
            except:
                continue


    def export_code(self):
        """
        Exports the address ranges of code sequences as CODE_BLOCK(s)
        with START and END address attributes.
        """
        addr = self.min_ea
        if idaapi.isCode(idaapi.getFlags(addr)) == False:
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.isCode)
        if (addr == BADADDR):
            return
        self.update_status(CODE)
        timer = time.clock()
        data = idaapi.nextthat(addr, self.inf.maxEA, isData)
        unknown = idaapi.next_unknown(addr, self.inf.maxEA)
        self.start_element(CODE, True)
        while (addr != BADADDR):
            start = addr
            end = min(data, unknown)
            if (end == BADADDR):
                if (idaapi.getseg(start).endEA < self.inf.maxEA):
                    codeend = idaapi.getseg(start).endEA - 1
                    addr = idaapi.getseg(idaapi.nextaddr(codeend)).startEA
                    if idaapi.isCode(idaapi.getFlags(addr)) == False:
                        addr = idaapi.nextthat(addr, self.max_ea,
                                               idaapi.isCode)
                else:
                    codeend = self.max_ea - 1
                    addr = BADADDR
            else:
                if (idaapi.getseg(start).endEA < end):
                    codeend = idaapi.getseg(start).endEA - 1
                    addr = idaapi.getseg(idaapi.nextaddr(codeend)).startEA
                    if idaapi.isCode(idaapi.getFlags(addr)) == False:
                        addr = idaapi.nextthat(addr, self.max_ea,
                                               idaapi.isCode)
                else:
                    codeend = idaapi.get_item_end(idaapi.prevthat(end,
                                                start, idaapi.isCode)) - 1
                    addr = idaapi.nextthat(end, self.max_ea,
                                           idaapi.isCode)
                if (data < addr):
                    data = idaapi.nextthat(addr, self.max_ea,
                                           idaapi.isData)
                if (unknown < addr):
                    unknown = idaapi.next_unknown(addr, self.max_ea)
            self.start_element(CODE_BLOCK)
            self.write_address_attribute(START, start)
            self.write_address_attribute(END, codeend)
            self.close_tag()
        self.end_element(CODE)
        self.display_cpu_time(timer)


    def export_comment(self, addr, cmt_type, cmt):
        """
        Exports a <COMMENT> element with ADDRESS and TYPE attributes.
        The comment is exported as the element text (parsed character data).
        
        Args:
            addr: Integers representing address of comment.
            cmt_type: String indicating the comment type.
            cmt: String containing the comment.
        """
        self.start_element(COMMENT)
        self.write_address_attribute(ADDRESS, addr)
        self.write_attribute(TYPE, cmt_type)
        self.close_tag(True)
        # tag_remove seems to be losing last character
        # work around is to add a space
        cmt_text = idaapi.tag_remove(cmt + ' ')
        self.write_text(cmt_text)
        self.end_element(COMMENT, False)


    def export_comments(self):
        """
        Exports all comments in the IDA database as <COMMENT> elements.
        """
        addr = self.min_ea
        if idaapi.has_cmt(idaapi.getFlags(addr)) == False:
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.has_cmt)
        if (addr == BADADDR):
            return
        self.update_status(COMMENTS)
        timer = time.clock()
        self.start_element(COMMENTS, True)
        while (addr != BADADDR):
            cmt = idaapi.get_cmt(addr, False)
            if (cmt != None):
                self.export_comment(addr, "end-of-line", cmt)
            cmt = idaapi.get_cmt(addr, True)
            if (cmt != None):
                self.export_comment(addr, "repeatable", cmt)
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.has_cmt)
        addr = self.min_ea
        if idaapi.hasExtra(idaapi.getFlags(addr)) == False:
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.hasExtra)
        while (addr != BADADDR):
            if IDA_SDK_VERSION < 640:
                extra = idaapi.ExtraGet(addr, idaapi.E_PREV)
            else:
                extra = idaapi.get_extra_cmt(addr, idaapi.E_PREV)
            if (extra != None):
                self.export_extra_comment(addr, "pre", idaapi.E_PREV)
            if IDA_SDK_VERSION < 640:
                extra = idaapi.ExtraGet(addr, idaapi.E_NEXT)
            else:
                extra = idaapi.get_extra_cmt(addr, idaapi.E_NEXT)
            if (extra != None):
                self.export_extra_comment(addr, "post", idaapi.E_NEXT)
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.hasExtra)
        self.export_c_comments()
        self.end_element(COMMENTS)
        self.display_cpu_time(timer)


    def export_data(self):
        """
        Exports the data items in the database as <DEFINED_DATA> elements.
        """
        addr = self.min_ea
        if idaapi.isData(idaapi.getFlags(addr)) == False:
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.isData)
        if (addr == BADADDR):
            return
        timer = time.clock()
        self.update_status(DATA)
        self.start_element(DATA, True)
        while (addr != BADADDR):
            f = idaapi.getFlags(addr)
            if idaapi.isAlign(f) == True:
                addr = idaapi.nextthat(addr, self.max_ea, idaapi.isData)
                continue
            dtype = self.get_datatype(addr)
            size = idaapi.get_item_size(addr)
            ti = idaapi.opinfo_t()
            if IDA_SDK_VERSION < 640:
                msize = idaapi.get_data_elsize(addr, f, ti)
            else:
                msize = idaapi.get_data_type_size(f, ti)
            if idaapi.isStruct(f) == True:
                s = idaapi.get_struc_id(dtype)
                msize = idaapi.get_struc_size(s)
                if msize == 0:
                    msize = 1
            if idaapi.isASCII(f) == False and size != msize:
                dtype = "%s[%d]" % (dtype, size/msize)
            self.start_element(DEFINED_DATA)
            self.write_address_attribute(ADDRESS, addr)
            self.write_attribute(DATATYPE, dtype)
            self.write_numeric_attribute(SIZE, size*self.cbsize)
            #TODO consider using GetTrueNameEx and Demangle
            demangled = idaapi.get_demangled_name(BADADDR, addr,
                            DEMANGLED_TYPEINFO, self.inf.demnames, True)
            outbuf = ''
            if IDA_SDK_VERSION < 660:
                outbuf = idaapi.print_type(addr, False)
            else:
                outbuf = idaapi.print_type(addr, outbuf)
            if demangled == "'string'":
                demangled == None
            has_typeinfo = demangled != None or (outbuf != None and
                                                len(outbuf) > 0)
            #TODO export_data: add DISPLAY_SETTINGS
            self.close_tag(has_typeinfo)
            if has_typeinfo == True:
                if demangled != None:
                    self.export_typeinfo_cmt(demangled)
                else:
                    #TODO export_data - check this
                    self.export_typeinfo_cmt(outbuf[:-1])
                self.end_element(DEFINED_DATA)
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.isData)
        self.end_element(DATA)
        self.display_cpu_time(timer)
        

    def export_datatypes(self):
        """
        Exports the structures and enums in IDA database.
        """
        # skip if no structures/unions to export
        if idaapi.get_struc_qty() == 0: return
        self.update_status(DATATYPES)
        timer = time.clock()
        self.start_element(DATATYPES, True)
        self.export_structures()
        self.export_enums()
        self.end_element(DATATYPES)
        self.display_cpu_time(timer)


    def export_enum_member(self, cid, bf, mask, radix, signness):
        """
        Exports a member of an enum.

        This function can only be called by IDA versions newer than 6.3 
        
        Args:
            cid: Integer representing id of enum member
            bf: Boolean indicates if a bitfield
            mask: Integer representing bitmask if bitfield
            radix: Integer representing numeric display format
            signness: Boolean indicating if signed value 
        """
        if IDA_SDK_VERSION < 640:
            return
        cname = idaapi.get_const_name(cid)
        if cname == None or len(cname) == 0:
            return
        regcmt = idaapi.get_const_cmt(cid, False)
        rptcmt = idaapi.get_const_cmt(cid, True)
        has_comment =  regcmt != None or rptcmt != None
        self.start_element(ENUM_ENTRY)
        self.write_attribute(NAME, cname)
        value = idaapi.get_const_value(cid)
        self.write_numeric_attribute(VALUE, value, radix, signness)
        # BIT_MASK attribute not currently supported for ENUM_ENTRY
        #if bf == True:
        #    self.write_numeric_attribute(BIT_MASK, mask)
        self.close_tag(has_comment)
        if regcmt != None and len(regcmt) > 0:
            self.export_regular_cmt(regcmt)
        if rptcmt != None and len(rptcmt) > 0:
            self.export_repeatable_cmt(rptcmt)
        if (has_comment):
            self.end_element(ENUM_ENTRY)


    def export_enum_member_legacy(self, cid, bf, mask, radix, signness):
        """
        Exports a member of an enum.
        
        This function can only be called by IDA versions older than 6.3 

        Args:
            cid: Integer representing id of enum member
            bf: Boolean indicates if a bitfield
            mask: Integer representing bitmask if bitfield
            radix: Integer representing numeric display format
            signness: Boolean indicating if signed value 
        """
        if IDA_SDK_VERSION > 630:
            return
        cname = idaapi.get_enum_member_name(cid)
        if cname == None or len(cname) == 0:
            return
        regcmt = idaapi.get_enum_member_cmt(cid, False)
        rptcmt = idaapi.get_enum_member_cmt(cid, True)
        has_comment =  regcmt != None or rptcmt != None
        self.start_element(ENUM_ENTRY)
        self.write_attribute(NAME, cname)
        value = idaapi.get_enum_member_value(cid)
        self.write_numeric_attribute(VALUE, value, radix, signness)
        # BIT_MASK attribute not currently supported for ENUM_ENTRY
        #if bf == True:
        #    self.write_numeric_attribute(BIT_MASK, mask)
        self.close_tag(has_comment)
        if regcmt != None and len(regcmt) > 0:
            self.export_regular_cmt(regcmt)
        if rptcmt != None and len(rptcmt) > 0:
            self.export_repeatable_cmt(rptcmt)
        if (has_comment):
            self.end_element(ENUM_ENTRY)


    def export_enum_members(self, eid, bf, eflags):
        """
        Exports the members of an enum.

        This function can only be called by IDA versions newer than 6.3 
        
        Args:
            eid: Integer representing id of enum
            bf: Boolean indicates if a bitfield
            eflags: Integer representing the enum flags
        """
        if IDA_SDK_VERSION < 640:
            return
        mask=0xFFFFFFFF
        if bf == True:
            mask = idaapi.get_first_bmask(eid)
        first = True
        for n in range(idaapi.get_enum_size(eid)):
            if (first == True):
                value = idaapi.get_first_const(eid, mask)
                first = False
            else:
                value = idaapi.get_next_const(eid, value, mask)
            (cid, serial) = idaapi.get_first_serial_const(eid, value, mask)
            main_cid = cid
            while cid != idaapi.BADNODE:
                self.export_enum_member(cid, bf, mask,
                                   idaapi.getRadix(eflags, 0),
                                   self.is_signed_data(eflags))
                last_value = idaapi.get_last_const(eid, mask)
                if value == last_value:
                    # BIT_MASK not currently supported for ENUM
                    #self.export_bitmask(eid, mask)
                    mask = idaapi.get_next_bmask(eid, mask)
                    first = True
                (cid, serial) = idaapi.get_next_serial_const(main_cid)


    def export_enum_members_legacy(self, eid, bf, eflags):
        """
        Exports the members of an enum.
        
        This function can only be called by IDA versions older than 6.3 
        
        Args:
            eid: Integer representing id of enum
            bf: Boolean indicates if a bitfield
            eflags: Integer representing the enum flags
        """
        if IDA_SDK_VERSION > 630:
            return
        mask=0xFFFFFFFF
        if bf == True:
            mask = idaapi.get_first_bmask(eid)
        first = True
        for n in range(idaapi.get_enum_size(eid)):
            if (first == True):
                value = idaapi.get_first_enum_member(eid, mask)
                first = False
            else:
                value = idaapi.get_next_enum_member(eid, value, mask)
            (cid, serial) = idaapi.get_first_serial_enum_member(eid, value, mask)
            main_cid = cid
            while cid != idaapi.BADNODE:
                self.export_enum_member_legacy(cid, bf, mask,
                                   idaapi.getRadix(eflags, 0),
                                   self.is_signed_data(eflags))
                last_value = idaapi.get_last_enum_member(eid, mask)
                if value == last_value:
                    # BIT_MASK not currently supported for ENUM
                    #self.export_bitmask(eid, mask)
                    mask = idaapi.get_next_bmask(eid, mask)
                    first = True
                (cid, serial) = idaapi.get_next_serial_enum_member(main_cid)


    def export_enum_reference(self, addr, op):
        """
        Exports the enum reference for an operand at an address.
        
        Args:
            addr: Integer representing the instruction address.
            op: Integer representing the operand index (0-based)
        """
        (eid, serial) = idaapi.get_enum_id(addr, op)
        idaapi.decode_insn(addr)
        value = idaapi.cmd.Operands[op].value
        cid = idaapi.BADNODE
        last = idaapi.get_last_bmask(eid)
        if idaapi.is_bf(eid) == True:
            last = idaapi.get_last_bmask(eid)
            mask = idaapi.get_first_bmask(eid)
            while  (cid == idaapi.BADNODE):
                if IDA_SDK_VERSION < 640:
                    cid = idaapi.get_enum_member(eid, (value & mask), 0, mask)
                else:
                    cid = idaapi.get_const(eid, (value & mask), 0, mask)
                if cid != idaapi.BADNODE or mask == last:
                    break
                mask = idaapi.get_next_bmask(eid, mask)
        else:
            if IDA_SDK_VERSION < 640:
                cid = idaapi.get_enum_member(eid, value, 0, last)
            else:
               cid = idaapi.get_const(eid, value, 0, last)
        if (cid == idaapi.BADNODE):
            return
        self.start_element(EQUATE_REFERENCE)
        self.write_address_attribute(ADDRESS, addr)
        self.write_numeric_attribute(OPERAND_INDEX, op, 10)
        if IDA_SDK_VERSION < 640:
            self.write_numeric_attribute(VALUE, idaapi.get_enum_member_value(cid))
            cname = idaapi.get_enum_member_name(cid)
        else:
            self.write_numeric_attribute(VALUE, idaapi.get_const_value(cid))
            cname = idaapi.get_const_name(cid)
        if cname != None and len(cname) > 0:
            self.write_attribute(NAME, cname)
        # BIT_MASK feature not currently supported
        #if idaapi.is_bf(eid) == True:
        #    self.write_numeric_attribute("BIT_MASK", mask);
        self.close_tag()
        

    def export_enum_references(self, addr):
        """
        Finds and exports enum references at an address.
        
        Args:
            addr: Integer representing the instruction address.
        """
        f = idaapi.getFlags(addr)
        for op in range(2):
            if idaapi.isEnum(f, op) == True:
                self.export_enum_reference(addr, op)
                

    def export_enums(self):
        """
        Exports enumerations.
        """
        num_enums = idaapi.get_enum_qty()
        if (num_enums == 0):
            return
        for i in range(num_enums):
            self.start_element(ENUM)
            eid = idaapi.getn_enum(i)
            ename = idaapi.get_enum_name(eid)
            if (ename == None or len(ename) == 0):
                continue
            self.write_attribute(NAME, ename)
            ewidth = idaapi.get_enum_width(eid)
            if ewidth != 0 and ewidth <= 7:
                self.write_numeric_attribute(SIZE, 1 << (ewidth-1), 10)
            eflags = idaapi.get_enum_flag(eid)
            bf = idaapi.is_bf(eid)
            # BIT_FIELD attribute not supported for ENUM
            #if bf == True:
            #    self.write_attribute(BIT_FIELD, "yes")
            regcmt = idaapi.get_enum_cmt(eid, False)
            rptcmt = idaapi.get_enum_cmt(eid, True)
            has_children = ((idaapi.get_enum_size(eid) > 0) or
                            (regcmt != None) or (rptcmt != None) or
                            (idaapi.getRadix(eflags, 0) != 16) or
                            (self.is_signed_data(eflags) == True))
            self.close_tag(has_children)
            if (idaapi.getRadix(eflags, 0) != 16 or
                self.is_signed_data(eflags) == True):
                self.start_element(DISPLAY_SETTINGS)
                if idaapi.getRadix(eflags, 0) != 16:
                    self.write_attribute(FORMAT, self.get_format(eflags))
                if self.is_signed_data(eflags) == True:
                    self.write_attribute(SIGNED, "yes")
                self.close_tag()
            if regcmt != None:
                self.export_regular_cmt(regcmt)
            if rptcmt != None:
                self.export_repeatable_cmt(rptcmt)
            if IDA_SDK_VERSION < 640:
                self.export_enum_members_legacy(eid, bf, eflags)
            else:
                self.export_enum_members(eid, bf, eflags)
            if (has_children):
                self.end_element(ENUM)


    def export_extra_comment(self, addr, cmt_type, extra):
        """
        Exports pre- and post- comments for an address.
        
        Args:
            addr: Integer representing the instruction address.
            cmt_type: String indicating comment type
            extra: Integer representing extra comment index
        """
        cmt = ''
        if IDA_SDK_VERSION < 640:
            nextline = idaapi.ExtraGet(addr, extra)
        else:
            nextline = idaapi.get_extra_cmt(addr, extra)
        while (nextline != None):
            # workaround for tag_remove bug is to add space
            cmt += idaapi.tag_remove(nextline + ' ')
            extra += 1
            if IDA_SDK_VERSION < 640:
                nextline = idaapi.ExtraGet(addr, extra)
            else:
                nextline = idaapi.get_extra_cmt(addr, extra)
            if (nextline != None):
                cmt += '\n' 
        self.export_comment(addr, cmt_type, cmt)


    def export_functions(self):
        """
        Exports information about all functions. 
        """
        functions = idautils.Functions()
        if functions == None:
            return
        self.update_status(FUNCTIONS)
        timer = time.clock()
        self.start_element(FUNCTIONS, True)
        for addr in functions:
            function = idaapi.get_func(addr)
            if idaapi.is_spec_ea(function.startEA) == True:
                continue
            self.start_element(FUNCTION)
            self.write_address_attribute(ENTRY_POINT, function.startEA)
            if idaapi.has_user_name(idaapi.getFlags(addr)) == True:
                name = self.get_symbol_name(addr)
                if name != None and len(name) > 0:
                    self.write_attribute(NAME, name)
            if function.flags & idaapi.FUNC_LIB != 0:
                self.write_attribute(LIBRARY_FUNCTION, "y")
            self.close_tag(True)
            fchunks = idautils.Chunks(addr)
            for (startEA, endEA) in fchunks:
                self.start_element(ADDRESS_RANGE)
                self.write_address_attribute(START, startEA)
                self.write_address_attribute(END, endEA-1)
                self.close_tag()
            regcmt = idaapi.get_func_cmt(function, False)
            if regcmt != None:
                self.export_regular_cmt(regcmt)
            rptcmt = idaapi.get_func_cmt(function, True)
            if rptcmt != None:
                self.export_repeatable_cmt(rptcmt)
            demangled = idaapi.get_demangled_name(BADADDR, addr,
                                            DEMANGLED_TYPEINFO,
                                            self.inf.demnames, True)
            if demangled != None and demangled == "'string'":
                demangled = None
            outbuf = ''
            if IDA_SDK_VERSION < 660:
                outbuf = idaapi.print_type(addr, False)
            else:
                outbuf = idaapi.print_type(addr, outbuf)
            has_typeinfo = (demangled != None or (outbuf != None and
                            len(outbuf) > 0))
            if demangled != None:
                self.export_typeinfo_cmt(demangled)
            elif has_typeinfo == True:
                self.export_typeinfo_cmt(outbuf[:-1])
            self.export_stack_frame(function)
            self.end_element(FUNCTION)
        self.end_element(FUNCTIONS)
        self.display_cpu_time(timer)


    def export_manual_instruction(self, addr):
        """
        Exports user-entered "manual instruction" at an address.
        
        Args:
            addr: Integer representing instruction address.
        """
        text = idaapi.get_manual_insn(addr)
        if text == None or len(text) == 0:
            return
        self.start_element(MANUAL_INSTRUCTION)
        self.write_address_attribute(ADDRESS, addr)
        self.close_tag(True)
        self.write_text(text)
        self.end_element(MANUAL_INSTRUCTION, False)
        

    def export_manual_operand(self, addr):
        """
        Exports user-entered "manual operands" at an address.
        
        Args:
            addr: Integer representing instruction address.
        """
        for op in range(idaapi.UA_MAXOP):
            if idaapi.is_forced_operand(addr, op) == True:
                text = idaapi.get_forced_operand(addr, op)
                if text != None and len(text) > 0:
                    self.start_element(MANUAL_OPERAND)
                    self.write_address_attribute(ADDRESS, addr)
                    self.write_numeric_attribute(OPERAND_INDEX, op, 10)
                    self.close_tag(True)
                    self.write_text(text)
                    self.end_element(MANUAL_OPERAND, False)


    def export_markup(self):
        """
        Exports markup for instructions and data items including references
        and manual instructions and operands.
        """
        self.update_status(MARKUP)
        timer = time.clock()
        self.start_element(MARKUP, True)
        addr = self.min_ea
        while addr != BADADDR:
            f = idaapi.getFlags(addr)
            if self.options.MemoryReferences.checked == True:
                if idaapi.hasRef(f) == True:
                    self.export_user_memory_reference(addr)
                if idaapi.isOff(f, idaapi.OPND_ALL) == True:
                    self.export_memory_references(addr)
            if (self.options.Functions.checked == True and
               self.options.StackReferences.checked == True and
               idaapi.isStkvar(f, idaapi.OPND_ALL) == True):
               self.export_stack_reference(addr)
            if (self.options.DataTypes.checked == True and
                    idaapi.isEnum(f, idaapi.OPND_ALL) == True):
                self.export_enum_references(addr)
            if self.options.Manual.checked == True:
                if idaapi.isFop(f, idaapi.OPND_ALL)   == True:
                    self.export_manual_operand(addr)
                if idaapi.is_manual_insn(addr) == True:
                    self.export_manual_instruction(addr)
            addr = idaapi.next_head(addr, self.max_ea)
        self.end_element(MARKUP)
        self.display_cpu_time(timer)


    def export_members(self, s):
        """
        Exports the members of a structure or union.
        
        Args:
            s: IDA structure/union instance
        """
        nmembers = s.memqty
        for n in range(nmembers):
            m = s.get_member(n)
            offset = m.soff
            if s.is_union() == True:
                offset = 0
            self.start_element(MEMBER)
            self.write_numeric_attribute(OFFSET, offset)
            mname = idaapi.get_member_name(m.id)
            if len(mname) > 0:
                self.write_attribute(NAME, mname)
            dtype = self.get_member_type(m)
            if idaapi.is_varmember(m) == True:
                msize = 0
                size  = 0
            else:
                mtibuf = idaapi.opinfo_t()
                mti = idaapi.retrieve_member_info(m, mtibuf)
                if IDA_SDK_VERSION < 640:
                    msize = idaapi.get_type_size0(None, dtype)
                    if msize == None or msize == 0:
                        msize = idaapi.get_member_size(m)
                else:
                    msize = idaapi.get_data_type_size(m.flag, mtibuf)
                size = idaapi.get_member_size(m)
                osize = size
                if size < msize: size = msize
            if (size != msize):
                arraytype = self.get_member_type(m)
                dtype = "%s[%d]" % (arraytype, size/msize)
            self.write_attribute(DATATYPE, dtype)
            self.write_numeric_attribute(SIZE, size*self.cbsize)
            regcmt = idaapi.get_member_cmt(m.id, False)
            rptcmt = idaapi.get_member_cmt(m.id, True)
            hascmt = regcmt != None or rptcmt != None
            self.close_tag(hascmt)
            if (hascmt):
                if regcmt != None:
                    self.export_regular_cmt(regcmt)
                if rptcmt != None:
                    self.export_repeatable_cmt(rptcmt)
                self.end_element(MEMBER)


    def export_memory_contents(self, binfilename, binfile, start, end):
        """
        Exports the binary memory contents in the database.
        
        A MEMORY_CONTENTS element is generated for each contiguous address
        range where each address in the range contains a value.
        The binary values are store in a separate file (not the XML file),
        and the MEMORY_CONTENTS element identifies the file and the
        offset in the file where the address range is located.

        Args:
            binfilename: String containing the absolute filepath
            binfile: IDA file instance for binary file
            start: Integer representing the starting address
            end: Integer representing the ending address
        """
        length = 0
        startaddr = start
        for addr in range(start, end):
            # reset start address when length == 0
            if (length == 0):
                startaddr = addr
            has_val = idaapi.hasValue(idaapi.getFlags(addr))
            if has_val == True:
                length += self.cbsize
            next_address = idaapi.nextaddr(addr)
            if ((has_val == False) or (next_address != addr+1) or
                    (next_address == end)):
                if length > 0:
                    offset = binfile.tell()
                    idaapi.base2file(binfile.get_fp(), offset, startaddr,
                                     startaddr+length)
                    self.start_element(MEMORY_CONTENTS)
                    self.write_address_attribute(START_ADDR, startaddr)
                    self.write_attribute(FILE_NAME, binfilename)
                    self.write_numeric_attribute(FILE_OFFSET, offset)
                    self.write_numeric_attribute(LENGTH, length)
                    self.close_tag(False)
                    length=0


    def export_memory_map(self):
        """
        Exports information about all memory blocks in the database.
        
        A MEMORY_SECTION is generated for each block (segment). If the
        memory block is initialized (has values), the contents are exported
        using the MEMORY_CONTENTS element.
        """
        nsegs = idaapi.get_segm_qty()
        if (nsegs == 0):
            return
        self.update_status(MEMORY_MAP)
        timer = time.clock();
        binfilename = ''
        if (self.options.MemoryContent.checked == True):
            (binfilename, ext) = os.path.splitext(self.filename)
            binfilename += ".bytes"
            self.binfile = idaapi.qfile_t()
            self.binfile.open(binfilename,'wb');
        self.start_element(MEMORY_MAP, True)
        for i in range(nsegs):
            self.export_memory_section(idaapi.getnseg(i), binfilename)
        self.end_element(MEMORY_MAP)
        if (self.options.MemoryContent.checked == True):
            self.close_binfile()
        self.display_cpu_time(timer)


    def export_memory_reference(self, addr, op):
        """
        Exports the memory reference for operand at the address.
        
        Args:
            addr: Integer representing the instruction address.
            op: Integer representing the operand index (0-based)
        """
        f = idaapi.getFlags(addr)
        ri = idaapi.refinfo_t()
        if idaapi.get_refinfo(addr, op, ri) == 1: 
            if ri.target != BADADDR:
                target = ri.target
            elif idaapi.isCode(f) == True:
                idaapi.decode_insn(addr)
                target = idaapi.cmd.Operands[op].value - ri.tdelta + ri.base
            elif idaapi.isData(f) == True:
                target = self.get_data_value(addr) - ri.tdelta + ri.base;
            else:
                return
        else:
            return
        if idaapi.isEnabled(target) == False:
            return
        self.start_element(MEMORY_REFERENCE)
        self.write_address_attribute(ADDRESS, addr)
        self.write_numeric_attribute(OPERAND_INDEX, op, 10)
        self.write_address_attribute(TO_ADDRESS, target)
        self.write_attribute(PRIMARY, "y")
        self.close_tag()
        

    def export_memory_references(self, addr):
        """
        Exports the memory references for any operands at the address.
        
        Args:
            addr: Integer representing the instruction address.
        """
        f = idaapi.getFlags(addr)
        for op in range(idaapi.UA_MAXOP):
            if idaapi.isOff(f, op) == True and (idaapi.isData(f) == True or
                    (idaapi.isCode(f) == True and
                    self.is_imm_op(addr, op) == True)):
                self.export_memory_reference(addr, op)
    

    def export_memory_section(self, seg, binfilename):
        """
        Exports segment information as a MEMORY_SECTIONS element.
        
        Args:
            seg: IDA segment instance
            binfilename: String containing absolute filepath for binary file.
        """
        segname = idaapi.get_segm_name(seg)
        self.start_element(MEMORY_SECTION)
        self.write_attribute(NAME, segname)
        self.write_address_attribute(START_ADDR, seg.startEA)
        length = (seg.endEA - seg.startEA)*self.cbsize
        self.write_numeric_attribute(LENGTH, length)
        perms = ""
        if (seg.perm != 0):
            if (seg.perm & idaapi.SEGPERM_READ  != 0):
                perms += 'r'
            if (seg.perm & idaapi.SEGPERM_WRITE != 0):
                perms += 'w' 
            if (seg.perm & idaapi.SEGPERM_EXEC  != 0):
                perms += 'x'
            if (len(perms) > 0):
                self.write_attribute(PERMISSIONS, perms)
        has_contents = (self.options.MemoryContent.checked == True and
                       self.check_if_seg_contents(seg) == True)
        self.close_tag(has_contents)
        if (has_contents == True):
            self.export_memory_contents(os.path.basename(binfilename),
                                      self.binfile, seg.startEA, seg.endEA)
            self.end_element(MEMORY_SECTION)


    def export_program(self):
        """
        Exports basic information about the program as the PROGRAM,
        INFO_SOURCE, PROCESSOR, and COMPILER elements.
        """
        # output the PROGRAM element
        self.update_status(PROGRAM);
        timer = time.clock()
        self.start_element(PROGRAM)
        self.write_attribute(NAME, idaapi.get_root_filename())
        self.write_attribute(EXE_PATH, idaapi.get_input_file_path())
        etype = idaapi.get_file_type_name()
        if (len(etype) > 0):
            self.write_attribute(EXE_FORMAT, etype)
        # check for presence of INPUT_MD5 netnode
        md5 = idaapi.netnode(INPUT_MD5)
        if md5 == idaapi.BADNODE:
            input_md5 = idc.GetInputMD5()
        else:
            input_md5 = md5.supval(idaapi.RIDX_MD5)
        if input_md5 != None:
            self.write_attribute(INPUT_MD5,input_md5)
        self.close_tag(True)
    
        # output the INFO_SOURCE element
        self.start_element(INFO_SOURCE)
        if IDA_SDK_VERSION < 670:
            tool  = 'IDA-Pro XML plugin (Python) SDK '
            tool +=  str(IDA_SDK_VERSION)
        else:
            tool  = 'IDA-Pro ' + idaapi.get_kernel_version()
            tool += ' XML plugin (Python) SDK ' + str(IDA_SDK_VERSION)
        self.write_attribute(TOOL, tool)
        user = os.getenv("USERNAME", "UNKNOWN")
        if (user == "UNKNOWN"):
            user = os.getenv("USER", "UNKNOWN")
        self.write_attribute(USER, user)
        self.write_attribute(FILE, idaapi.cvar.database_idb)
        ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
        self.write_attribute(TIMESTAMP, ts)
        self.close_tag()
    
        # output the PROCESSOR element
        self.start_element(PROCESSOR)
        self.write_attribute(NAME, self.inf.procName)
        if (self.inf.mf):
            byte_order ="big"
        else:
            byte_order ="little"
        self.write_attribute(ENDIAN, byte_order)
        self.seg_addr = False
        bitness = 1
        model_warning = False
        nsegs = idaapi.get_segm_qty()
        if (nsegs > 0):
            bitness = idaapi.getnseg(0).bitness
            for i in range(1,nsegs):
                seg = idaapi.getnseg(i)
                if (seg.bitness != bitness):
                    model_warning = True
                if (seg.bitness > bitness):
                    bitness = seg.bitness
        addr_model = "32-bit"
        if (bitness == 0):
            addr_model = "16-bit"
        elif (bitness == 2):
            addr_model = "64-bit"
        self.write_attribute(ADDRESS_MODEL, addr_model)
        self.close_tag()
        if (model_warning):
            idaapi.msg("WARNING: Segments do not have same " +
                       "addressing model!\n")
        if (idaapi.ph.id == idaapi.PLFM_386 and bitness == 0):
            self.seg_addr = True
        # find any overlayed memory before processing addressable items
        self.find_overlay_memory()
    
        # output compiler info
        self.start_element(COMPILER)
        self.write_attribute(NAME, idaapi.get_compiler_name(self.inf.cc.id))
        self.close_tag()
        self.display_cpu_time(timer)


    def export_program_entry_points(self):
        """
        Exports entry points for the program.
        """
        nepts = idaapi.get_entry_qty()
        if (nepts  == 0):
            return
        self.update_status(PROGRAM_ENTRY_POINTS)
        timer = time.clock()
        self.start_element(PROGRAM_ENTRY_POINTS, True)
        for i in range(nepts):
            self.start_element(PROGRAM_ENTRY_POINT)
            addr = idaapi.get_entry(idaapi.get_entry_ordinal(i))
            self.write_address_attribute(ADDRESS, addr)
            self.close_tag()
        self.end_element(PROGRAM_ENTRY_POINTS)
        self.display_cpu_time(timer)


    def export_register_values(self):
        """
        Exports segment register value ranges.
        """
        # should use legacy function if IDA > 660
        if IDA_SDK_VERSION < 670:
            self.export_register_values_legacy()
            return
        first = idaapi.ph_get_regFirstSreg()
        last  = idaapi.ph_get_regLastSreg()+1
        has_segregareas = False
        for j in range(first, last):
            nsegregareas = idaapi.get_srareas_qty2(j)
            if nsegregareas != 0:
                has_segregareas = True
                break;
        if has_segregareas == False:
            return
        self.update_status(REGISTER_VALUES)
        timer = time.clock();
        self.start_element(REGISTER_VALUES, True)
        sr = idaapi.segreg_area_t()
        for j in range(first, last):
            nsegregareas = idaapi.get_srareas_qty2(j)
            if nsegregareas == 0:
                continue
            for i in range(nsegregareas):
                success = idaapi.getn_srarea2(sr, j, i)
                if success == False:
                    continue
                value = sr.val
                if value == idaapi.BADSEL:
                    continue
                regname = idaapi.ph.regnames[j]
                if regname == None:
                    continue
                if regname.lower() == "cs":
                    continue
                if (idaapi.ph.id == idaapi.PLFM_TMS and
                    regname.lower() == "ds"):
                    continue
                self.start_element(REGISTER_VALUE_RANGE)
                self.write_attribute(REGISTER, idaapi.ph.regnames[j])
                self.write_numeric_attribute(VALUE, value)
                self.write_address_attribute(START_ADDRESS, sr.startEA)
                length = (sr.endEA - sr.startEA) * self.cbsize
                self.write_numeric_attribute(LENGTH, length)
                self.close_tag()
        self.end_element(REGISTER_VALUES)
        self.display_cpu_time(timer)


    def export_register_values_legacy(self):
        """
        Exports segment register value ranges.
        """
        # should use export_register_values() if IDA > 660
        if IDA_SDK_VERSION > 660:
            return
        if IDA_SDK_VERSION < 650:
            n = 0
            srarea = idaapi.getnSRarea(n)
            while type(srarea) == idaapi.segreg_t:
                n += 1
                srarea = idaapi.getnSRarea(n)
            nsegregareas = n
        else:
            nsegregareas = idaapi.get_srareas_qty()
        if (nsegregareas == 0):
            return
        self.update_status(REGISTER_VALUES)
        timer = time.clock();
        self.start_element(REGISTER_VALUES, True)
        for i in range(nsegregareas):
            if IDA_SDK_VERSION < 650:
                sr = idaapi.getnSRarea(i)
            else:
                sr = idaapi.getn_srarea(i)
            first = idaapi.ph_get_regFirstSreg()
            last  = idaapi.ph_get_regLastSreg()+1
            for j in range(first, last):
                value = idaapi.getSR(sr.startEA, j)
                if value == idaapi.BADSEL:
                    continue
                regname = idaapi.ph.regnames[j]
                if regname == None:
                    continue
                if regname.lower() == "cs":
                    continue
                if (idaapi.ph.id == idaapi.PLFM_TMS and
                    regname.lower() == "ds"):
                    continue
                self.start_element(REGISTER_VALUE_RANGE)
                self.write_attribute(REGISTER, idaapi.ph.regnames[j])
                self.write_numeric_attribute(VALUE, value)
                self.write_address_attribute(START_ADDRESS, sr.startEA)
                length = (sr.endEA - sr.startEA) * self.cbsize
                self.write_numeric_attribute(LENGTH, length)
                self.close_tag()
        self.end_element(REGISTER_VALUES)
        self.display_cpu_time(timer)


    def export_regular_cmt(self, cmt):
        """
        Exports the regular comment for an item.
        
        Args:
            cmt: String containing the regular comment.
        """
        self.write_comment_element(REGULAR_CMT, cmt)
    

    def export_repeatable_cmt(self, cmt):
        """
        Exports the repeatable comment for an item.
        
        Args:
            cmt: String containing the repeatable comment.
        """
        self.write_comment_element(REPEATABLE_CMT, cmt)
    

    def export_stack_frame(self, function):
        """
        Export information about a function stack frame including
        variables allocated on the stack.
        
        Args:
            function: IDA function instance
        """
        sframe = idaapi.get_struc(function.frame)
        if sframe == None or sframe.memqty <= 0:
            return
        self.start_element(STACK_FRAME)
        self.write_numeric_attribute(LOCAL_VAR_SIZE, function.frsize)
        self.write_numeric_attribute(REGISTER_SAVE_SIZE, function.frregs)
        retsize = idaapi.get_frame_retsize(function)
        self.write_numeric_attribute(RETURN_ADDR_SIZE, retsize)
        self.write_numeric_attribute(BYTES_PURGED, function.argsize)
        has_stack_vars = self.check_stack_frame(sframe)
        self.close_tag(has_stack_vars)
        if has_stack_vars == True:
            self.export_stack_vars(function, sframe)
            self.end_element(STACK_FRAME)

    def export_stack_reference(self, addr):
        """
        Exports references to stack variables at the address.
        
        Args:
            addr: Integer containing instruction address.
        """
        f = idaapi.getFlags(addr)
        for op in range(idaapi.UA_MAXOP):
            if idaapi.isCode(f) == True and idaapi.isStkvar(f, op) == True:
                idaapi.decode_insn(addr)
                opnd = idaapi.cmd.Operands[op]
                optype = idaapi.op_t_get_type(opnd)
                if optype == idaapi.o_void:
                    continue
                SV = idaapi.get_stkvar(opnd, idaapi.op_t_get_addr(opnd))
                if SV == None:
                    continue
                (sv, actval) = SV
                function = idaapi.get_func(addr)
                self.start_element(STACK_REFERENCE)
                self.write_address_attribute(ADDRESS, addr)
                self.write_numeric_attribute(OPERAND_INDEX, op, 10)
                offset = opnd.addr
                spoff = offset - function.frregs
                if offset > 0x7FFFFFFF:
                    offset -= 0x100000000
                if spoff > 0x7FFFFFFF:
                    spoff  -= 0x100000000
                self.write_numeric_attribute(STACK_PTR_OFFSET,
                                             spoff,
                                             16, True)
                if (function.flags & FUNC_FRAME) != 0:
                    self.write_numeric_attribute(FRAME_PTR_OFFSET,
                                                offset,
                                                16, True)
                self.close_tag()

    def export_stack_vars(self, function, sframe):
        """
        Exports the stack variables (parameters and locals) in a stack frame.
        
        Args:
            function: IDA function instance.
            sframe: IDA stack frame instance.
        """
        for i in range(sframe.memqty):
            member = sframe.get_member(i)
            if member == None:
                continue
            mname = idaapi.get_member_name(member.id)
            if mname == None or len(mname) < 0:
                continue
            if mname == " s" or mname == " r":
                continue
            spoff = member.soff - function.frsize - function.frregs
            froff = member.soff - function.frsize
            self.start_element(STACK_VAR)
            self.write_numeric_attribute(STACK_PTR_OFFSET, spoff, 16, True)
            if function.flags & idaapi.FUNC_FRAME != 0:
                self.write_numeric_attribute(FRAME_PTR_OFFSET, froff, 16, True)
            pre = mname[0:4]
            if pre != "var_" and pre != "arg_":
                self.write_attribute(NAME, mname)
            f = member.flag
            size = idaapi.get_member_size(member)
            mtype = self.get_member_type(member)
            msize = size
            if idaapi.isStruct(f) == True:
                msize = idaapi.get_struc_size(idaapi.get_struc_id(mtype))
            elif idaapi.isASCII(f) == False:
                mtibuf = idaapi.opinfo_t()
                mti = idaapi.retrieve_member_info(member, mtibuf)
                if IDA_SDK_VERSION < 640:
                    msize = idaapi.get_type_size0(None, mtype)
                    if msize == None or msize == 0:
                        msize = idaapi.get_member_size(member)
                else:
                    msize = idaapi.get_data_type_size(f, mtibuf)
            if size < msize: size = msize
            if (idaapi.isASCII(f) == False and idaapi.isAlign(f) == False
                and size != msize):
                mtype = "%s[%d]" % (mtype, size/msize)
            self.write_attribute(DATATYPE, mtype)
            self.write_numeric_attribute(SIZE, size*self.cbsize)
            regcmt = idaapi.get_member_cmt(member.id, False)
            rptcmt = idaapi.get_member_cmt(member.id, True)
            if regcmt != None:
                regcmt  = idaapi.tag_remove(regcmt + " ", 0)
            if rptcmt != None:
                rptrcmt = idaapi.tag_remove(rptcmt + " ", 0)
            has_regcmt = regcmt != None and len(regcmt) > 0
            has_rptcmt = rptcmt != None and len(rptcmt) > 0
            has_content = has_regcmt or has_rptcmt
            self.close_tag(has_content)
            if has_content == True:
                if has_regcmt == True:
                    self.export_regular_cmt(regcmt)
                if has_rptcmt == True:
                    self.export_repeatable_cmt(rptcmt)
                self.end_element(STACK_VAR)


    def export_structures(self):
        """
        Exports information about all structures and unions.
        """
        structs = idautils.Structs()
        for struct in structs:
            (sid, idx, sname) = struct
            s = idaapi.get_struc(idx)
            stype = STRUCTURE
            if s.is_union() == True:
                stype = UNION
            self.start_element(stype)
            self.write_attribute(NAME, sname)
            size = idaapi.get_struc_size(idx)*self.cbsize
            self.write_numeric_attribute(SIZE, size)
            if s.is_varstr() == True:
                self.write_attribute(VARIABLE_LENGTH, "y")
            regcmt = idaapi.get_struc_cmt(idx, False)
            rptcmt = idaapi.get_struc_cmt(idx, True)
            has_contents = regcmt != None or rptcmt != None or s.memqty > 0
            self.close_tag(has_contents)
            if (has_contents):
                if regcmt != None:
                    self.export_regular_cmt(regcmt)
                if rptcmt != None:
                    self.export_repeatable_cmt(rptcmt)
                if s.memqty > 0:
                    self.export_members(s)
                self.end_element(stype)
    

    def export_symbol(self, addr, name, stype=""):
        """
        Exports name for an address as a SYMBOL element. If the name is a
        demangled name, add the mangled name as the MANGLED attribute.
        
        Args:
            addr: Integer representing the symbol address.
            name: String containing the symbol name.
            stype: String indicating symbol type (global or local)
        """
        self.start_element(SYMBOL)
        self.write_address_attribute(ADDRESS, addr)
        self.write_attribute(NAME, name)
        self.write_attribute(TYPE, stype)
        mangled = idaapi.get_name(BADADDR, addr)
        if name != None and mangled != name:
            self.write_attribute("MANGLED", mangled)
        self.close_tag()
        

    def export_symbol_table(self):
        """
        Exports user-defined and non-default names as SYMBOL elements.
        """
        addr = self.min_ea
        if idaapi.has_any_name(idaapi.getFlags(addr)) == False:
            addr = idaapi.nextthat(addr, self.max_ea, idaapi.has_any_name)
        if addr == BADADDR:
            return
        self.update_status(SYMBOL_TABLE)
        self.start_element(SYMBOL_TABLE, True)
        timer = time.clock()
        while addr != BADADDR:
            # only export meaningful names (user and auto)
            f = idaapi.getFlags(addr)
            if (idaapi.has_user_name(f) == True or
                idaapi.has_auto_name(f) == True):
                # check for global name
                name = self.get_symbol_name(addr)
                if name != None and len(name) > 0:
                    self.export_symbol(addr, name)
                # check for local name
                if ((IDA_SDK_VERSION > 620 and
                    idaapi.has_lname(addr)) or
                    idaapi.get_aflags(addr) & idaapi.AFL_LNAME):
                    if IDA_SDK_VERSION < 680:
                        name = idaapi.get_name(addr, addr)
                    else:
                        name = idaapi.get_ea_name(addr, idaapi.GN_LOCAL)
                    if name != None and len(name) > 0:
                        self.export_symbol(addr, name, 'local')
            # get next address with any name
            addr = idaapi.nextthat(addr, self.max_ea,
                                   idaapi.has_any_name)
        self.end_element(SYMBOL_TABLE)
        self.display_cpu_time(timer)
        

    def export_typeinfo_cmt(self, cmt):
        """
        Exports comment containing type information for data and functions.
        
        Args:
            cmt: String containing type info.
        """
        # older versions of IDAPython returned a '\n' at end of cmt
        if cmt[-1] == '\n':
            cmt = cmt[0:-1]
        self.write_comment_element(TYPEINFO_CMT, cmt)
        

    def export_user_memory_reference(self, addr):
        """
        Exports a user-specified memory reference at the address.
        
        Args:
            addr: Integer representing the instruction address.
        """
        for xref in idautils.XrefsTo(addr, idaapi.XREF_FAR):
            if xref.user == 1:
                self.start_element(MEMORY_REFERENCE)
                self.write_address_attribute(ADDRESS, xref.frm)
                self.write_address_attribute(TO_ADDRESS, xref.to)
                self.write_attribute(USER_DEFINED, "y")
                self.close_tag()


    def find_overlay_memory(self):
        """
        Determines if any memory blocks (segments) are overlays.
        
        A segment is an overlay if it translates to the same logical
        address as another segment. This is rare, but may occur, for
        example when a processor has a small logical address space
        (i.e. a 16-bit address is limited to 64K) and multiple physical
        segments are mapped into the same logical segment.
        """
        self.overlay = dict()
        self.has_overlays = False;
        nsegs = idaapi.get_segm_qty()
        if nsegs == 0:
            return
        s = idaapi.getnseg(0)
        start = self.translate_address(s.startEA)
        self.overlay[start] = False
        for i in range(1, nsegs):
            s = idaapi.getnseg(i)
            space = self.get_space_name(s.startEA)
            saddr = self.translate_address(s.startEA)
            eaddr = self.translate_address(s.endEA-1)
            is_overlay = False
            for j in range(i):
                s2 = idaapi.getnseg(j)
                space2 = self.get_space_name(s2.startEA)
                if space == space2:
                    start = self.translate_address(s2.startEA)
                    end   = self.translate_address(s2.endEA-1)
                    if ((saddr >= start and saddr <= end) or 
                        (eaddr >= start and eaddr <= end)):
                        is_overlay = True
                        self.has_overlays = True
                        break
            self.overlay[saddr] = is_overlay


    def get_address_string(self, addr):
        """
        Returns a string representing the address.
        
        The representation is typically a hex string of the address,
        but may include a segment or space name prefixe based on the
        processor or architecture.
        
        Args:
            addr: Integer representing a program address.
        """
        temp = "0x%X" % (addr - idaapi.get_segm_base(idaapi.getseg(addr)))
        space = self.get_space_name(addr)
        if space != None:
            temp = "%s:%04X" % (space,
                            addr - idaapi.get_segm_base(idaapi.getseg(addr)))
        else:
            if (idaapi.ph_get_id() == idaapi.PLFM_386 and
                idaapi.getseg(addr).bitness == 0):
                base = idaapi.get_segm_para(idaapi.getseg(addr))
                temp = "%04X:%04X" % (base, addr - (base << 4))
        if idaapi.ph_get_id() == idaapi.PLFM_C166:
            temp = "0x%X" % addr
        if self.has_overlays == True and self.is_overlay(addr) == True:
            oname = idaapi.get_segm_name(idaapi.getseg(addr))
            if len(oname) > 0:
                temp = oname + "::" + temp
        return temp


    def get_data_value(self, addr):
        """
        Returns the data item value at an address based on its size.
        
        Args:
            addr: Integer representing a program address.
        """
        size = idaapi.get_item_size(addr)*self.cbsize
        if size == 1:   return idaapi.get_byte(addr)
        if size == 2:   return idaapi.get_16bit(addr)
        if size == 4:   return idaapi.get_32bit(addr)
        if size == 8:   return idaapi.get_64bit(addr)
        return 0
    

    def get_datatype(self, addr):
        """
        Returns the datatype at an address.
        
        The type could be a basic type (byte, word, dword, etc.),
        a structure, an array, a pointer, or a string type.
        
        Args:
            addr: Integer representing a program address.
        """
        f = idaapi.getFlags(addr)
        t = self.get_type(f)
        if idaapi.isStruct(f) == True:
            opndbuf = idaapi.opinfo_t()
            opnd = idaapi.get_opinfo(addr, 0, f, opndbuf)
            return idaapi.get_struc_name(opnd.tid)
        if idaapi.isASCII(f) == True:
            str_type = idc.GetStringType(addr)
            if str_type == idaapi.ASCSTR_TERMCHR:   return "string"
            if str_type == idaapi.ASCSTR_PASCAL:    return "string1"
            if str_type == idaapi.ASCSTR_LEN2:      return "string2"
            if str_type == idaapi.ASCSTR_LEN4:      return "string4"
            if str_type == idaapi.ASCSTR_UNICODE:   return "unicode"
            if str_type == idaapi.ASCSTR_ULEN2:     return "unicode2"
            if str_type == idaapi.ASCSTR_ULEN4:     return "unicode4"
            return "string"
        if idaapi.isOff0(f) == True: return "pointer"
        return t


    def get_format(self, flags):
        """
        Returns the display format of a data item based on its flags.
        
        Args:
            flags: Integer representing IDA item flags
            
        Returns:
            String representing IDA display format.
        """
        if idaapi.isChar0(flags): return "char"
        radix = idaapi.getRadix(flags, 0)
        if radix == 2:  return "binary"
        if radix == 8:  return "octal"
        if radix == 10: return "decimal"
        return "hex" # default
    

    def get_member_type(self, m):
        """
        Returns the datatype of a structure member.
        
        Args:
            m: IDA member instance.
            
        Returns:
            String representing member datatype.
        """
        f = m.flag
        t = self.get_type(f)
        if idaapi.isOff0(f) == True:
            t = "pointer"
        if idaapi.isStruct(f) == False:
            return t
        s = idaapi.get_sptr(m)
        if (s == None):
            return t
        sname = idaapi.get_struc_name(s.id)
        if (sname == None):
            return t
        return sname


    def get_options(self):
        """
        Displays the options menu and retrieves the option settings. 
        """
        fmt =  "HELP\n"
        fmt += "XML plugin (Python)\n"
        fmt += "IDA SDK: "+ str(IDA_SDK_VERSION) + "\n"
        fmt += "\n"
        fmt += "The XML interface provides a dump of the IDA-Pro database as "
        fmt += "a XML \"PROGRAM\" document. The XML PROGRAM document contains "
        fmt += "information from the idb file in a readable text format, and "
        fmt += "can be viewed with a text editor or web browser.\n\n"
        fmt += "ENDHELP\n"
        fmt += "Export as XML PROGRAM document...."
        fmt += "\n <##Options##Memory Sections:{MemorySections}>"
        fmt += "\n <Memory Content:{MemoryContent}>"
        fmt += "\n <Segment Register Value Ranges:{RegisterValues}>"
        fmt += "\n <Data Types:{DataTypes}>"
        fmt += "\n <Code Blocks:{CodeBlocks}>"
        fmt += "\n <Data Definitions:{DataDefinitions}>"
        fmt += "\n <Comments:{Comments}>"
        fmt += "\n <Entry Points:{EntryPoints}>"
        fmt += "\n <Symbols:{Symbols}>"
        fmt += "\n <Functions:{Functions}>"
        fmt += "\n <Memory References:{MemoryReferences}>"
        fmt += "\n <Stack References:{StackReferences}>"
        fmt += "\n <Manual Instructions/Operands:{Manual}>{cGroup1}>"
        fmt += "\n\n"
        
        Opts = { 'cGroup1': idaapi.Form.ChkGroupControl
            (
                (
                "MemorySections",
                "MemoryContent",
                "RegisterValues",
                "DataTypes",
                "CodeBlocks",
                "DataDefinitions",
                "Comments",
                "EntryPoints",
                "Symbols",
                "Functions",
                "MemoryReferences",
                "StackReferences",
                "Manual"
                )
            )
        }
        
        self.options = idaapi.Form(fmt, Opts)
        self.options.Compile()
        
        self.options.MemorySections.checked   = True
        self.options.MemoryContent.checked    = True
        self.options.DataTypes.checked        = True
        self.options.RegisterValues.checked   = True
        self.options.CodeBlocks.checked       = True
        self.options.DataDefinitions.checked  = True
        self.options.Symbols.checked          = True
        self.options.EntryPoints.checked      = True
        self.options.Functions.checked        = True
        self.options.Comments.checked         = True
        self.options.MemoryReferences.checked = True
        self.options.StackReferences.checked  = False
        self.options.Manual.checked           = True
    
        if (self.autorun == False):
            ok = self.options.Execute()
            if (ok == 0):
                raise Cancelled


    def get_space_name(self, addr):
        """
        Returns the memory space name associated with an address.
        
        Args:
            addr: Integer representing a program address.
            
        Returns:
            String containg the memory space name.
            None if single address space architecture.

        Used for Harvard architectures (Intel 8051 and TMS, add others
        as needed). 
        """
        pid = idaapi.ph_get_id()
        stype = idaapi.segtype(addr)
        if pid == idaapi.PLFM_8051:
            if stype == idaapi.SEG_CODE:
                return "CODE"
            else:
                if stype == idaapi.SEG_IMEM:
                    iaddr = addr - idaapi.get_segm_base(idaapi.getseg(addr))
                    if iaddr < 0x80:
                        return "INTMEM"
                    else:
                        return "SFR"
                else:
                    return "EXTMEM"
        if pid == idaapi.PLFM_TMS:
            if stype == idaapi.SEG_CODE:
                return "CODE"
            else:
                return "DATA"
        return None


    def get_symbol_name(self, ea):
        """
        Returns the symbol name for the address.
        
        Args:
            ea: Integer representing the symbol address.
            
        Returns:
            String containing the symbol name.
        
        The demangled name will be returned if it exists, otherwise the
        displayed name is returned. Spaces (' ') will be replaced with '_'.
        """
        flags = idaapi.getFlags(ea)
        name = idaapi.get_demangled_name(BADADDR, ea, DEMANGLED_FORM,
                                         self.inf.demnames, False)
        if name == None or len(name) == 0 or name == "`string'":
            name = idaapi.get_name(BADADDR, ea)
        if name != None:
            name = name.replace(" ","_")
        return name


    def get_type(self, flags):
        """
        Returns a datatype string based on the item flags.
        
        Args:
            flags: IDA item flags.
            
        Returns:
            String representing item datatype.
        """
        if (self.cbsize == 2):
            if idaapi.isByte(flags) == True: return "word"
            if idaapi.isWord(flags) == True: return "dword"
        if idaapi.isByte(flags)     == True: return "byte"
        if idaapi.isWord(flags)     == True: return "word"
        if idaapi.isDwrd(flags)     == True: return "dword"
        if idaapi.isQwrd(flags)     == True: return "qword"
        if idaapi.isOwrd(flags)     == True: return "oword"
        if idaapi.isTbyt(flags)     == True: return "tbyte"
        if idaapi.isFloat(flags)    == True: return "float"
        if idaapi.isDouble(flags)   == True: return "double"
        if idaapi.isPackReal(flags) == True: return "packed"
        if idaapi.isASCII(flags)    == True: return "ascii"
        if idaapi.isStruct(flags)   == True: return "structure"
        if idaapi.isAlign(flags)    == True: return "align"
        return "unknown"


    def is_imm_op(self, addr, op):
        """
        Returns true if instruction operand at address is an immediate value.
        
        Args:
            addr: Integer representing instruction address.
            op: Integer representing operand index (0-based).
            
        Returns:
            True if instruction operand at address is an immediate value.
            False otherwise.
        """
        idaapi.decode_insn(addr)
        if (idaapi.cmd.Operands[op].type == idaapi.o_imm):
            return True
        return False
        

    def is_overlay(self, addr):
        """
        Checks if memory block (segment) is an overlay.
        
        Args:
            addr: Integer representing a program address.
            
        Returns:
            True if memory block (segment) is an overlay.
        """
        if idaapi.ph_get_id() == idaapi.PLFM_C166:
            return False
        s = idaapi.getseg(addr)
        if s.startEA in self.overlay:
            return self.overlay[s.startEA]
        return False
    
    def is_signed_data(self, flags):
        return (flags & idaapi.FF_SIGN) != 0


    def open_file(self, filename, mode):
        """
        Opens filename to specified mode.
        
        Args:
            filename: String representing absolute filepath.
            mode: String representing mode for open.
            
        Returns
            File handle.
        
        Exceptions:
            Displays a warning and raises FileError exception
            if open fails.
        """
        try:
            f = open(filename, mode)
            return f
        except:
            fmt = "TITLE ERROR!\n"
            fmt += "ICON ERROR\n"
            fmt += "AUTOHIDE NONE\n"
            fmt += "HIDECANCEL\n"
            fmt += "Error opening file" + filename + "!\n"
            idaapi.warning(fmt)
            raise FileError


    def start_element(self, tag, close=False):
        """
        Outputs the start of a new element on a new indented line.
        
        Args:
            tag: String representing the element tag
            close: Boolean indicating if tag is should be closed.
        """
        if idaapi.wasBreak() == True:
            raise Cancelled
        self.write_to_xmlfile("\n" + ("    " * self.indent_level) + "<" + tag)
        if (close):
            self.close_tag(True)
        # Add a counter for this tag; increment counter if it already exists.
        if (tag in self.elements) == False:
            self.elements[tag] = len(self.elements)
            self.counters.append(1)
            self.tags.append(tag)
        else:
            self.counters[self.elements[tag]] += 1
        

    def translate_address(self, addr):
        """
        Returns the translated logical address.

        The logical address is adjusted for the segment base address.
        For 16-bit segmented memory, return the 20-bit address.
        
        Args:
            addr: Integer representing a program address.
            
        Returns:
            Integer representing the logical address.
        """
        if self.seg_addr == False:
            return addr - idaapi.get_segm_base(idaapi.getseg(addr))
        base = idaapi.get_segm_para(idaapi.getseg(addr))
        return (base << 16) + (addr - (base << 4))
    

    def update_status(self, tag):
        """
        Displays the processing status in the IDA window.
        
        Args:
            tag: String representing XML element tag
        """
        status = "Exporting <" + tag + ">"
        idaapi.msg('\n%-35s' % status)
        idaapi.hide_wait_box()
        idaapi.show_wait_box(status)


    def write_address_attribute(self, name, addr):
        """
        Outputs an address attribute for an element.
        
        Args:
            name: String representing attribute name.
            addr: Integer representing a program address.
        """
        self.write_attribute(name, self.get_address_string(addr))
    

    def write_attribute(self, name, value):
        """
        Outputs an attribute (name and value) for an element.
        
        Args:
            name: String representing attribute name.
            value: String representing attribute value.
        """
        if name == None or value == None:
            return
        if (len(name) == 0) or (len(value) == 0):
            return
        attr = " " + name + '="' + self.check_for_entities(value) + '"'
        self.write_to_xmlfile(attr)
        

    def write_comment_element(self, name, cmt):
        """
        Outputs the tag and text for a comment element.
        Comment elements can be REGULAR_CMT, REPEATABLE_CMT, or TYPEINFO_CMT.
        
        Args:
            name: String representing the comment element name.
            cmt: String containing the comment.
        """
        self.start_element(name, True)
        self.write_text(cmt)
        self.end_element(name, False)
        

    def write_numeric_attribute(self, name, value, base=16, signedhex=False):
        """
        Outputs a numeric value attribute (name and value) for an element.
        
        Args:
            name: String representing the attribute name.
            value: Integer representing the attribute value.
            base: Integer representing numeric base to use for value.
            signedhex: Boolean indicating if hex representation of
                value is signed.
        """
        if base == 10:
            temp = "%d" % value
        else:
            if signedhex == True and value < 0:
                temp = "-0x%X" % abs(value)
            else:
                temp = "0x%X" % value
        self.write_attribute(name, temp)


    def write_text(self, text):
        """
        Outputs the parsed character text for an element.
        The text is checked for special characters.
        
        Args:
            text: String representing the element text.
        """
        self.write_to_xmlfile(self.check_for_entities(text))
    

    def write_to_xmlfile(self, buf):
        """
        Writes the buffer to the XML file.
        
        Args:
            buf: String containg data to write to XML file.
        """
        self.xmlfile.write(buf)
        self.dbg(buf)
    

    def write_xml_declaration(self):
        """
        Writes the XML Declarations at the start of the XML file.
        """
        self.dbg("\n")
        xml_declaration  = "<?xml version=\"1.0\" standalone=\"yes\"?>"
        xml_declaration += "\n<?program_dtd version=\"1\"?>\n"
        self.write_to_xmlfile(xml_declaration)

# Global constants
# mangled name inhibit flags are not exposed in idaapi
# inhibit flags for symbol names
# DEMANGLE_FORM (MNG_SHORT_FORM | MNG_NOBASEDT | MNG_NOCALLC | MNG_NOCSVOL)
DEMANGLED_FORM = 0x0ea3ffe7
# inhibit flags for typeinfo cmts
# DEMANGLED_TYPEINFO (MNG_LONG_FORM)
DEMANGLED_TYPEINFO = 0x06400007

# Global XML string constants for elements and attributes
ADDRESS = 'ADDRESS'
ADDRESS_MODEL = 'ADDRESS_MODEL'
ADDRESS_RANGE = 'ADDRESS_RANGE'
BASE_ADDRESS = 'BASE_ADDRESS'
BIT_FIELD = 'BIT_FIELD'
BIT_MAPPED = 'BIT_MAPPED'
BIT_MASK = 'BIT_MASK'
BOOKMARK = 'BOOKMARK'
BOOKMARKS = 'BOOKMARKS'
BYTES = 'BYTES'
BYTES_PURGED = 'BYTES_PURGED'
CATEGORY = 'CATEGORY'
CODE = 'CODE'
CODE_BLOCK = 'CODE_BLOCK'
COMMENT = 'COMMENT'
COMMENTS = 'COMMENTS'
COMPILER = 'COMPILER'
DATA = 'DATA'
DATATYPE = 'DATATYPE'
DATATYPES = 'DATATYPES'
DATATYPE_NAMESPACE = 'DATATYPE_NAMESPACE'
DEFINED_DATA = 'DEFINED_DATA'
DESCRIPTION = 'DESCRIPTION'
DISPLAY_SETTINGS = 'DISPLAY_SETTINGS'
END = 'END'
ENDIAN = 'ENDIAN'
ENTRY_POINT = 'ENTRY_POINT'
ENUM = 'ENUM'
ENUM_ENTRY = 'ENUM_ENTRY'
EQUATE = 'EQUATE'
EQUATES = 'EQUATES'
EQUATE_GROUP = 'EQUATE_GROUP'
EQUATE_REFERENCE = 'EQUATE_REFERENCE'
EXE_FORMAT = 'EXE_FORMAT'
EXE_PATH = 'EXE_PATH'
EXT_LIBRARY = 'EXT_LIBRARY'
EXT_LIBRARY_REFERENCE = 'EXT_LIBRARY_REFERENCE'
EXT_LIBRARY_TABLE = 'EXT_LIBRARY_TABLE'
FAMILY = 'FAMILY'
FILE = 'FILE'
FILE_NAME = 'FILE_NAME'
FILE_OFFSET = 'FILE_OFFSET'
FOLDER = 'FOLDER'
FORMAT = 'FORMAT'
FRAGMENT = 'FRAGMENT'
FRAME_PTR_OFFSET = 'FRAME_PTR_OFFSET'
FUNCTION = 'FUNCTION'
FUNCTIONS = 'FUNCTIONS'
FUNCTION_DEF = 'FUNCTION_DEF'
IMAGE_BASE = 'IMAGE_BASE'
INPUT_MD5 = 'INPUT_MD5'
INFO_SOURCE = 'INFO_SOURCE'
LANGUAGE_PROVIDER = 'LANGUAGE_PROVIDER'
LENGTH = 'LENGTH'
LIB_ADDR = 'LIB_ADDR'
LIB_LABEL = 'LIB_LABEL'
LIB_ORDINAL = 'LIB_ORDINAL'
LIB_PROG_NAME = 'LIB_PROG_NAME'
LIBRARY_FUNCTION = 'LIBRARY_FUNCTION'
LOCAL_VAR_SIZE = 'LOCAL_VAR_SIZE'
MANGLED = 'MANGLED'
MANUAL_INSTRUCTION = 'MANUAL_INSTRUCTION'
MANUAL_OPERAND = 'MANUAL_OPERAND'
MARKUP = 'MARKUP'
MEMBER = 'MEMBER'
MEMORY_CONTENTS = 'MEMORY_CONTENTS'
MEMORY_MAP = 'MEMORY_MAP'
MEMORY_REFERENCE = 'MEMORY_REFERENCE'
MEMORY_SECTION = 'MEMORY_SECTION'
NAME = 'NAME'
NAMESPACE = 'NAMESPACE'
OFFSET = 'OFFSET'
OPERAND_INDEX = 'OPERAND_INDEX'
PARAM_OFFSET = 'PARAM_OFFSET'
PATH = 'PATH'
PERMISSIONS = 'PERMISSIONS'
PRIMARY = 'PRIMARY'
PROCESSOR = 'PROCESSOR'
PROGRAM = 'PROGRAM'
PROGRAM_ENTRY_POINT = 'PROGRAM_ENTRY_POINT'
PROGRAM_ENTRY_POINTS = 'PROGRAM_ENTRY_POINTS'
PROGRAM_TREES = 'PROGRAM_TREES'
PROPERTIES = 'PROPERTIES'
PROPERTY = 'PROPERTY'
REGISTER = 'REGISTER'
REGISTER_SAVE_SIZE = 'REGISTER_SAVE_SIZE'
REGISTER_VALUES = 'REGISTER_VALUES'
REGISTER_VALUE_RANGE = 'REGISTER_VALUE_RANGE'
REGISTER_VAR = 'REGISTER_VAR'
REGULAR_CMT = 'REGULAR_CMT'
RELOCATION = 'RELOCATION'
RELOCATION_TABLE = 'RELOCATION_TABLE'
REPEATABLE_CMT = 'REPEATABLE_CMT'
RETURN_ADDR_SIZE = 'RETURN_ADDR_SIZE'
RETURN_TYPE = 'RETURN_TYPE'
SHOW_TERMINATOR = 'SHOW_TERMINATOR'
SIGNED = 'SIGNED'
SIZE = 'SIZE'
SOURCE_ADDRESS = 'SOURCE_ADDRESS'
SOURCE_TYPE = 'SOURCE_TYPE'
STACK_FRAME = 'STACK_FRAME'
STACK_PTR_OFFSET = 'STACK_PTR_OFFSET'
STACK_REFERENCE = 'STACK_REFERENCE'
STACK_VAR = 'STACK_VAR'
START = 'START'
START_ADDR = 'START_ADDR'
START_ADDRESS = 'START_ADDRESS'
STRUCTURE = 'STRUCTURE'
SYMBOL = 'SYMBOL'
SYMBOL_TABLE = 'SYMBOL_TABLE'
TIMESTAMP = 'TIMESTAMP'
TOOL = 'TOOL'
TO_ADDRESS = 'TO_ADDRESS'
TREE = 'TREE'
TYPE = 'TYPE'
TYPEINFO_CMT = 'TYPEINFO_CMT'
TYPE_DEF = 'TYPE_DEF'
UNION = 'UNION'
USER = 'USER'
USER_DEFINED = 'USER_DEFINED'
VALUE = 'VALUE'
VARIABLE_LENGTH = 'VARIABLE_LENGTH'
ZERO_PAD = 'ZERO_PAD'


