# plugins

repository: FFT-Hackers-old/IDA-Tools
file: plugins/export-xref.py
description: The IDA sync plugin
wanted_name: Export-Xref
comment: Export Xref into a C-friendly format
created_at: 2018-07-06 21:36:24+00:00
pushed_at: 2019-01-16 05:14:52+00:00
url:
https://github.com/FFT-Hackers-old/IDA-Tools/blob/a8ba253612f05fcf6ea6f79846138bfe9a9d9482/plugins/export-xref.py

content:
from __future__ import print_function

import os
import json

import ida_bytes
import ida_enum
import ida_kernwin
import ida_nalt
import ida_name
import ida_offset
import ida_struct
import ida_typeinf
import ida_ua

import idautils
import idc

from ida_idaapi import BADADDR

class export_handler_t(ida_kernwin.action_handler_t):
    def activate(self, ctx):
        addr = ida_kernwin.askaddr(0, "Target address")
        if addr == BADADDR:
            print('[Export-Xref] Bad address given')
            return 1
        filename = ida_kernwin.ask_file(True, '*', 'Export Xrefs to...')
        if filename is None:
            return 1
        print('[Export-Xref] Exporting %s...' % filename)
        with open(filename, 'w') as f:
            for x in XrefsTo(addr, 0):
                print("0x%08x," % x.frm, file=f)
        print('[Export-Xref] Done')
        return 1

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

class export_xref_t(ida_idaapi.plugin_t):
    flags = 0
    comment = "Export Xref into a C-friendly format"
    help = "Export Xref"
    wanted_name = "Export-Xref"
    wanted_hotkey = ''

    def init(self):
        export_action = ida_kernwin.action_desc_t(
            'export-xref:export',
            'Export Xref...',
            export_handler_t())
        ida_kernwin.register_action(export_action)
        ida_kernwin.attach_action_to_menu(
            'File/Produce file/',
            'export-xref:export',
            ida_kernwin.SETMENU_APP
        )
        print("[Export-Xref] Loaded")
        return ida_idaapi.PLUGIN_OK

    def run(self, arg):
        pass

    def term(self):
        pass

def PLUGIN_ENTRY():
    return export_xref_t()


repository: tmr232/Sark
file: plugins/autoenum.py
description: IDAPython Made Easy
url: https://github.com/tmr232/Sark/blob/950e2c17e520ab637a3cdcd0da312d224b951f13/plugins/autoenum.py
wanted_name: AutoEnum
comment: Automatic Enum Generation
created_at: 2015-01-19 09:13:48+00:00
pushed_at: 2025-02-22 10:17:16+00:00
content:
from collections import defaultdict
import idaapi
import sark
import sark.exceptions


def apply_enum_by_name(enum, member_name):
    member_value = enum.members[member_name].value
    for line in sark.lines(*sark.get_selection()):
        for operand in line.insn.operands:
            if operand.type.is_imm:
                if operand.imm == member_value:
                    idaapi.op_enum(line.ea, operand.n, enum.eid, enum.members[member_name].serial)

            elif operand.type.is_displ or operand.type.is_phrase:
                if operand.addr == member_value:
                    idaapi.op_enum(line.ea, operand.n, enum.eid, enum.members[member_name].serial)


def get_common_value(desired=None):
    values = defaultdict(int)
    for line in sark.lines(*sark.get_selection()):
        for operand in line.insn.operands:
            if operand.type.is_imm:
                if desired is not None:
                    if desired == operand.imm:
                        return desired
                values[operand.imm] += 1

            elif operand.type.is_displ or operand.type.is_phrase:
                if desired is not None:
                    if desired == operand.addr:
                        return desired
                values[operand.addr] += 1

    # Ignore 0 as it is usually not interesting
    values[0] = 0
    # Get the most common value
    common_value = max(values.items(), key=lambda x: x[1])[0]
    return common_value


def const_name(enum, value):
    return "{}_{:X}h".format(enum.name.upper(), value)


def rename_immediate():
    highlighted = sark.get_highlighted_identifier()
    try:
        desired = int(highlighted, 0)
    except (ValueError, TypeError):
        desired = None
    value = idaapi.ask_long(get_common_value(desired), "Const Value")
    if value is None:
        return

    name = idaapi.ask_str("", 0, "Constant Name")
    if name is None:
        return

    try:
        enum = sark.Enum('GlobalConstants')
    except sark.exceptions.EnumNotFound:
        enum = sark.add_enum('GlobalConstants')

    enum.members.add(name, value)
    apply_enum_by_name(enum, name)


class RenameImmediateHandler(idaapi.action_handler_t):
    def activate(self, ctx):
        try:
            rename_immediate()
        except:
            import traceback
            traceback.print_exc()

        return 1

    def update(self, ctx):
        if ctx.widget_type == idaapi.BWN_DISASM:
            return idaapi.AST_ENABLE_FOR_WIDGET
        return idaapi.AST_DISABLE_FOR_WIDGET


class AutoEnumHandler(idaapi.action_handler_t):
    def activate(self, ctx):
        try:
            rename_immediate()
        except:
            import traceback
            traceback.print_exc()

        return 1

    def update(self, ctx):
        if ctx.widget_type == idaapi.BWN_DISASM:
            return idaapi.AST_ENABLE_FOR_WIDGET
        return idaapi.AST_DISABLE_FOR_WIDGET


class AutoEnum(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = "Automatic Enum Generation"
    help = "Automatic Enum Generation"
    wanted_name = "AutoEnum"
    wanted_hotkey = ""

    def init(self):
        self._last_enum = ""

        self.rename_action_desc = idaapi.action_desc_t('AutoEnum:RenameImmediate',
                                                       'Rename immediate value',
                                                       RenameImmediateHandler(),
                                                       'Ctrl+Shift+M',
                                                       'Rename immediate value',
                                                       -1)
        idaapi.register_action(self.rename_action_desc)

        self.autoenum_action_desc = idaapi.action_desc_t('AutoEnum:AutoEnum',
                                                         'Automatically create enum',
                                                         AutoEnumHandler(),
                                                         'Shift+M',
                                                         'Automatically create enum',
                                                         -1)
        idaapi.register_action(self.autoenum_action_desc)

        return idaapi.PLUGIN_KEEP

    def term(self):
        idaapi.unregister_action(self.autoenum_action_desc.name)
        idaapi.unregister_action(self.rename_action_desc.name)

    def run(self, arg):
        pass

    def autoenum(self):
        common_value = get_common_value()

        enum_name = idaapi.ask_str(self._last_enum, 0, "Enum Name")
        if enum_name is None:
            return

        if not enum_name:
            enum_name = None

        self._last_enum = enum_name

        # Can't ask with negative numbers.
        if common_value >> ((8 * sark.core.get_native_size()) - 1):
            common_value = 0

        const_value = idaapi.ask_long(common_value, "Const Value")
        if const_value is None:
            return

        modify = True

        try:
            enum = sark.add_enum(enum_name)

        except sark.exceptions.EnumAlreadyExists:
            enum = sark.Enum(enum_name)
            yes_no_cancel = idaapi.ask_yn(idaapi.ASKBTN_NO,
                                      "Enum already exists. Modify?\n")
            if yes_no_cancel == idaapi.ASKBTN_CANCEL:
                return

            elif yes_no_cancel == idaapi.ASKBTN_YES:
                modify = True

            else:  # yes_no_cancel == idaapi.ASKBTN_NO:
                modify = False

        member_name = const_name(enum, const_value)

        if modify:

            try:
                enum.members.add(member_name, const_value)
            except sark.exceptions.SarkErrorAddEnumMemeberFailed as ex:
                idaapi.msg("[AutoEnum] Adding enum member failed: {}.".format(ex.message))


        else:
            for member in enum.members:
                if member.value == const_value:
                    member_name = member.name
                    break
                else:
                    return

        # Apply the enum
        apply_enum_by_name(enum, member_name)


def PLUGIN_ENTRY():
    return AutoEnum()


repository: alexander-hanel/RenameLocalVars
file: src/rename_local_vars.py
description: RenameLocalVars is an IDA plugin that renames local variables to something easier to read.
wanted_name: Rename Local Variables
comment: Rename local variables
created_at: 2023-07-07 22:52:36+00:00
pushed_at: 2023-07-09 01:34:25+00:00
url:
https://github.com/alexander-hanel/RenameLocalVars/blob/67826c85afcf719b8b7344d51461c061bdef9671/src/rename_local_vars.py

content:
"""
Author: Alexander Hanel
Version: 1.0
Purpose: Renames local variables to something easier to read
Updates:
    * Version 1.0 Release
"""


import ida_kernwin
import ida_idaapi
import idaapi
import idc
import base64

RENAME_ICON = b"iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAMAAADDpiTIAAAC8VBMVEX///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
              b"AAAAAAAAAAAAAAAAAAAAAAAAAAAACckYSCAAAA+nRSTlMAoNjAuPnccP78/QMBDQIE+/L69/QrCwwX4RXxUgk94oBvMudIIr/oHc2DE" \
              b"2x7Zkec9iAUJN3W63jZ6pc07hEadQatCO3jdNchUTMjQuDbZA+uXgfzuWnp9cx6Eh/JO3KQLFspGxlG5DbwQA6UKBzF5WejJYyOBVlL" \
              b"3tDLZUTD1S8tamLUsyrRT5OJgauNvrFVPHa87MaWMKlYbRCEijVuvWCPusRQFviG70mbr855VoWonYcKHjm2Jk21VFqCTjEYsIunwd9" \
              b"3u5FBJ9pTqn4/c2HTLsc6PjdciF2SoZ9xV1/mY6I40qaemLSVyM+ZRUOkTMKyyqxrSpp9Yh3iIgAADLhJREFUeF7szDEOwVAAANC/N6" \
              b"1Nmg4N0kN06GapzQHEQNLJSCydxcoonMAZ3MHkAF0kZgdwBxPJewd44e8AAAAAAAAAAAAAAAAAAIzn9+vqMjieuu2+OYyWs6yfhN8Vx" \
              b"Wm9qx7FZNEOy9v69Tznva8zppv3h11764nqCsMA/MWkOLNnxmFkKkxnFJWTDkdDkSAMEkC0gBToiMcCCpYWD4hQFLQRsIRDQCYRLdqE" \
              b"NhgbNeIh2ibYC7jQBFOaNu2F2iaevWjUmKYXTb6rmpAQA7L2XnsWndnJ9/yBvZJv7Xe9yVrzcbrkQ931MX+3mMB/RC91lI31tttxus8" \
              b"yD6tbKal8hkzZQ0WfL+loNoHvJA54Mnb/9aUbmZ7vAn6kwoaKSE2pxXU+SPuR1v0BqMwJ/gwgpS7k0NcWmx8M/5f3hs9FWpFDDffaSB" \
              b"Ly0jelPpz1KJj3VcmTAuR2A/iQXFSnsSsXZo35k4Q+VEXaClxIF6qWdG0EZoHh1ctkVO0ycCGN6I2CK82Ck7/65Eb0RgLwIGnorRTnu" \
              b"yCIcVFnGHrJBjxIKQrw7RcggLn4CAoQAhxIsA5FODQ+D7yzMGY9ihAFXMhPKMacow2gXuGWVShGP3AhP6Moa+4vAHVC7tlRlFbgQi6h" \
              b"OH2LC4HfttQgFIezkZJRGwpU5QBO5pxNKFCKEfiQFguK9M/3wOPTFyhS6DbgRS7oUSRrmQGUMnVKKFJYHfAjMSjW0AAo46hCoZLVFQD" \
              b"S3BOFIkl/mkDesl4UKmlwD6hEzAf719qSrRIKEpoGcjxWFERv2XirZst28B7JSq98//c7t//9sbg2o7PtNzeqFXQXmIxOHaqVvTyzZ+" \
              b"7hHY7wXwa2R+wsbIBZQ6Jzx399vREsyK+e1QVNvSoHf3rYF49USXR+7A0X8tmQDjOpW4l8qrrLftgFPkWMFUcTVvIEd8BMpTz+PCo3/" \
              b"+Pyy8vAT5D0C85jgaiQ5Sy8TYaEClmKckqzwM+QrR+eUbgHdI9hujFUxnr1A38dPkmMO65HBaQDMNU5VGJFt+c6+DPyXUmkhLICp54C" \
              b"sSgv6OKpBeD/SISCi9x34uFN91GWy5kIGkFGu+yyYV4Nk6AW5TzKMYGGkK9lH3O5n05+4A8dshXEfQQaQxbG7EMmVx5MCNcjU/sDA2g" \
              b"QMZWzJ7t84rdezc4K9xLNjp/kbkaW3fCa4SayXNwL2kUM31iQwQMAV5AhtAO0jVQW4czseRDPKIC6wWDQPFLGaAJD6xgFwD78Hzt3rw" \
              b"tdGIZhlHwhfsbMl0nIFBITIqIgGhIJkiEqOolWoRDEWWg5AYWg0YlKolNItKJSKBUaUYpKL3v27N0+75p1BneuYhfvPMYL4TPn2EjO8" \
              b"79FT7mi2Dj+Kf+7OzNcGEMPpQN4rZotkMp3yQBmbRZL9atUADsWi2ZwtUQATXvF07ovHMCltSJa+1cwgLeKsUJqFDv1NL1lqqA2i7z7" \
              b"3Z80VFjNAgGMmimuaufPgCcrRXZU73RSyrvf2E7c9E3bYf7NiVMLRfecF8B/x1zCq7y76Z22i5wnYLvmia/V/vT7R/QByP8f6IFxUtB" \
              b"o90h03jZpuGoTwHXas/gMfDFNGlayT0qNWSYV2dfgbg2Tiu7MAM4Nk4rtzACWDJOK8awA6jXDpKKW9RU4YZd0ZD0LeTRLOnqyTgj/HQ" \
              b"ABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAvhll" \
              b"w4JAABAGIBxQQDaPBH99RUFMHcIlmGE6bUfoHAAYzPs18EJgDAURMHAR6KeJEXYf3052YCg4iXkz6thDrvRP6b6BGDQAoC7ABAAAkAA" \
              b"CAAB8DMAjtwAANhzAwBgzQ0AgC0xAABsAADaBADkBgoAASAABMAUAXC+BnCxS8ckAAAgFETlwAY/mf2TOFlABBcnX4IbTgxe7Adwjqm" \
              b"JKHbt4AQAEIiBoOBp//X6sgXBV46ZGpa88r4ANcJ5BZcAvggAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgADyCQABIAAEgA" \
              b"AQAAJAAAgAASAABIAAEAACQAAIAAEgAASAADoQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAAB5BMAAkAACAABIAAEgAAQA" \
              b"AJAAAgAASAABIAAEAACQAAIAAF0IAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAsgnAASAABAAAkAACAABIAAEgAAQAAJA" \
              b"AAgAASAABIAAEAAC6EAACAABIAAEgAAQAAJAAAgAASAABIAAEAACQAAIAAEgAASQTwAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAg" \
              b"AASAABNCAABAAAkAACAABIAAEgAAQAAJAAAgAAeQTwGWXjmkAAEAYgJERHEwZ/pXswgAh4eGiGtoFIAYv9wECx1js17EJACEURMGDQ0" \
              b"Qj+y/VyA4URBC+w2thgt1dAFqX1wDudwyAABAAAkAACAABMA+A9jYAAOrDAAAAAIASCIBsAHkBAkAACAABIABCBcBgrw4JAABAIIjxg" \
              b"gC0uUT01ygyvEOwEkOm134BpQNY9u6fpcowDuP4BVqcyPwDwYkiD5whBw0RXFrUA5YuDtEQYUoIgU5FW4P0Z2koGgSF3oAOgTSloIuF" \
              b"U9Cma+/jmnoD9/2c54znd38/r+E7XnApIFbBIAAQAAgABAACAAGAAEAAIAAQAAgABAACAAGAAEAAIAAQAAgABAACAAGAAEAAIAAQAAg" \
              b"ABAACAAGAAEAAIAAQAAgABAACAAGAAEAAIAAQAAgABAACAAHkEcC5gkP1ceRrlQKTThhTKbDtlH0VAjtOmVYh8NsphyoEHjjli8qAZS" \
              b"dNDKsIeOS0dRUBu077pRJgtuG01ogKgBfO+ab40LzjnDXFhynnXSg6PHnrvCNFhwVXmVZsuL3lKu+uKzTsudpHRYbxa662OK+4sPrQ3" \
              b"XwdVlg4c3f3FRWmXMPoT8WEzQPXsbKviPBh0fV8mlM8OJ5wXWs3FA2et1zfwF3FgpdX3YujjiLB+xX35tUtxYGTm+5Vq60gMPS54d7N" \
              b"/FMIaD523qDzfgyp/2F8yXlvOs+cd+9YfQ5z3xvO+9PU7JjzDv521M9w+tQVrrQlbbjK0qX6Fv6zV68tUbVRHIcXgrG3TmPNNJMzNub" \
              b"ojI2apqNZaqFYnsoDD2WeErRzRqaBhyxHSLNIKMzCSuMpMkLqTR4iqKznMZSMgqAIyqAorNdBQetVYBCGsmfuvW91Btb1BX5v/uu+rf" \
              b"koyTwZeI9ShOgS8EjEvz8GJZ2FSfp4lJSYvhA8DtmcpEVpjgH4zZaD0taHG8GjkOCLIjpRdOBPIC4EnSh8Nwgeg2RfR6daIqYEdOfRm" \
              b"UXjreAJSPnDDHRuwW6Yal0AOiUcMu8C90Z0bRnoijQ7/C0wBl0g9PybDO6K6ILi0TUFY9MCFm90ieC47IYbIPZLZU3oqpN2mO7uCnSR" \
              b"4OiujwV3QfQVa/9bgQxS9TCTyjBkkPvpeLAfzC+ypfXa1wYRmQhtKpiZaRjZFD45nWVTw5wjpoOrqvsnLlQJyKzwvkSgWEBmmtVXv7z" \
              b"4UGH3g1lD1Hrj9p2b2vfceJBnvvK8tABlM3SClKHFKJdo+HY4fGXzpaHG151jtgiTChQjqn3hrxLSNmqRl1AjSNM9Q06EwjUGr6KyYJ" \
              b"CLBEaHIFe9Uc7PckO3BrlqGqkEOUi9Bvm6+BhcUX4B+brZDuzIUy1yZagHF6m2BSBXa6zAinzeijyJNXqGePJ35CqjC9gQ/17kKWwTs" \
              b"Blajjx1+AETsgM58nrpC6z0kYnIUTYwIdHITWmWGuQw/TiC3KQDE7IaOcl/CrL51jUgJxPAggwiF+KdR6CMJRS58AIW5BRyoE3aDMq9" \
              b"+UdA5XyWAgOSjEotGj0WAXzYU44qHyPbAEgLKiGORsUCT5nnFG7gNjAhNSib2BFlBP4yzxWhfHXAhFhQHp+r1UaYLdaRMJQnhvEHIKa" \
              b"9yGx/fpClBGbXrmX/92iRWSowInFVyECI78vTqWFu1O4zD+cgiwQ7sCL3PjIcfhfMtd0MT8GrWGBHxkJQkmZBR9/Pt4E2FcwXv7jGvO" \
              b"7DjjQflHRiAOQgtpQzGpzOO3f0VuSO9ko1uItaa3Zz2/gTg4jTLQkt/tWeHeIgCAYAGKU7RjU4BxfAC1BsMoMmOsHNDaJn8Ag/G5sXI" \
              b"BDIWq1WjuAxvIPJzfeu8MVvEX2LuAxTe0uf/eFebtbFdbdsfnmsbo+nfX1+Das5z8bHJbyr7k/qAwAAAAAAAAAAAAAAAAAfO8Ta8Itx" \
              b"h4YAAAAASUVORK5CYII="

RENAME_ICON_32 = ida_kernwin.load_custom_icon(data=base64.b64decode(RENAME_ICON), format="png")


class RENAMER(ida_kernwin.action_handler_t):
    def __init__(self):
        ida_kernwin.action_handler_t.__init__(self)
        return

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

    def activate(self, ctx):
        word_list = ['abacus', 'aeon', 'alpha', 'arc', 'atlas', 'baryon', 'beta', 'carat', 'ceres', 'chaos', 'chi',
                     'dean', 'delta', 'epsilon', 'eta', 'fermat', 'gamma', 'gaudi', 'gnomen', 'ides', 'iota', 'iris',
                     'julia', 'kappa', 'kite', 'lambda', 'lemma', 'locus', 'lune', 'mars', 'mu', 'nocebo', 'nu',
                     'occam', 'ogive', 'omega', 'omicron', 'pareto', 'pascal', 'phi', 'pi', 'psi', 'rho', 'sabot',
                     'secant', 'sigma', 'simson', 'surd', 'tare', 'tau', 'theta', 'umbra', 'upsilon', 'venus', 'xenon',
                     'xi', 'zeta', 'zipf']

        func = idaapi.get_func(idc.here())
        frame = idaapi.get_frame(func)

        for mid in range(frame.memqty):
            var_name = idc.get_member_name(frame.id, frame.get_member(mid).soff)
            if not var_name.startswith("var_"):
                continue
            if len(word_list) < mid:
                print("exceeded variable names.. exiting ")
                break
            idc.set_member_name(frame.id, frame.get_member(mid).soff, "_" + word_list[mid])
        idaapi.request_refresh(0xFFFFFFFF)
        return

    def term(self):
        pass


class RenameLocalVarsPlugin(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_KEEP
    comment = "Rename local variables"
    help = "Renames local variables to something more friendly"
    wanted_name = "Rename Local Variables"
    wanted_hotkey = ""

    def init(self):
        self.actions = [
            ida_kernwin.action_desc_t(
                "rename_vars",
                "Rename Local Variables",
                RENAMER(),
                "Shift-Alt-r",
                "Rename local variables",
                RENAME_ICON_32
            )
        ]
        for action_desc in self.actions:
            ida_kernwin.register_action(action_desc)

        self.menus = Menus()
        self.menus.hook()

        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        return

    def term(self):
        if self.actions:
            for action_desc in self.actions:
                ida_kernwin.unregister_action(action_desc.name)


class Menus(ida_kernwin.UI_Hooks):
    def finish_populating_widget_popup(self, form, popup):
        if ida_kernwin.get_widget_type(form) == ida_kernwin.BWN_DISASM:
            ida_kernwin.attach_action_to_popup(form, popup, "rename_vars", "Rename Local Variables")


def PLUGIN_ENTRY():
    return RenameLocalVarsPlugin()


repository: tmr232/Sark
file: plugins/highlight_calls.py
description: IDAPython Made Easy
wanted_name: Highlight Calls
comment: Highlight Call Instructions
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/highlight_calls.py

content:
from contextlib import suppress
import idaapi
import sark
from ida_settings import IDASettings

HIGHLIGHT_COLOR = 0x303060


def highlight_calls_in_function(ea):
    highlighted_lines = set()
    for line in sark.Function(ea).lines:
        if not line.insn.is_call:
            continue

        # Refrain from painting over someone else...
        if line.color is None:
            line.color = HIGHLIGHT_COLOR
            highlighted_lines.add(line.ea)
    return highlighted_lines


class UiHooks(idaapi.UI_Hooks):
    def __init__(self, lines):
        super(UiHooks, self).__init__()
        self.lines = lines

    def updating_actions(self, ctx):
        if ctx.form_type == idaapi.BWN_DISASM:
            with suppress(sark.exceptions.SarkNoFunction):
                self.lines.update(highlight_calls_in_function(ctx.cur_ea))

        return super(UiHooks, self).updating_actions(ctx)


class ToggleHandler(idaapi.action_handler_t):
    def __init__(self, state=False):
        idaapi.action_handler_t.__init__(self)

        self._state = state

    def on_enable(self, ctx):
        raise NotImplementedError()

    def on_disable(self, ctx):
        raise NotImplementedError()

    def activate(self, ctx):
        if self._state:
            self.on_disable(ctx)
        else:
            self.on_enable(ctx)

        self._state = not self._state


class ToggleHighlightHandler(ToggleHandler):
    def __init__(self, enable, disable):
        ToggleHandler.__init__(self)

        self.enable = enable
        self.disable = disable

    def on_enable(self, ctx):
        self.enable()

    def on_disable(self, ctx):
        self.disable()

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


class SelectColorHandler(idaapi.action_handler_t):
    def __init__(self, set_color):
        idaapi.action_handler_t.__init__(self)
        self._set_color = set_color

    def activate(self, ctx):
        color = sark.ui.ask_color(initial=HIGHLIGHT_COLOR)
        if color is not None:
            self._set_color(color)

        return 1

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


class HighlightCalls(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = 'Highlight Call Instructions'
    help = 'Highlight all call instructions'
    wanted_name = 'Highlight Calls'
    wanted_hotkey = ''

    def init(self):
        self.lines = set()
        self.settings = IDASettings('HighlightCalls')
        try:
            self.set_color(self.settings['color'])
        except KeyError:
            self.settings.user['color'] = HIGHLIGHT_COLOR
            self.set_color(HIGHLIGHT_COLOR)
        self.ui_hooks = UiHooks(self.lines)

        self.toggle_action_desc = idaapi.action_desc_t('HighlightCalls:Toggle',
                                                       'Toggle call highlighting',
                                                       ToggleHighlightHandler(self.enable_highlights,
                                                                              self.disable_highlights),
                                                       '',
                                                       'Toggle call highlighting',
                                                       -1)
        idaapi.register_action(self.toggle_action_desc)

        self.color_selector = idaapi.action_desc_t('HighlightCalls:SelectColor',
                                                   'Select highlight color',
                                                   SelectColorHandler(set_color=self.set_color),
                                                   '',
                                                   'Select highlight color',
                                                   -1)
        idaapi.register_action(self.color_selector)

        idaapi.attach_action_to_menu('View/', self.toggle_action_desc.name, idaapi.SETMENU_APP)
        idaapi.attach_action_to_menu('View/', self.color_selector.name, idaapi.SETMENU_APP)

        return idaapi.PLUGIN_KEEP

    def set_color(self, color):
        self.color = color
        self.settings.user['color'] = color
        global HIGHLIGHT_COLOR
        HIGHLIGHT_COLOR = color
        self.clear_all_highlights()

    def enable_highlights(self):
        self.ui_hooks.hook()

    def term(self):
        self.disable_highlights()
        idaapi.unregister_action(self.toggle_action_desc.name)
        idaapi.unregister_action(self.color_selector.name)

    def disable_highlights(self):
        self.ui_hooks.unhook()
        self.clear_all_highlights()

    def clear_all_highlights(self):
        for ea in self.lines:
            sark.Line(ea=ea).color = None
        self.lines.clear()

    def run(self, arg):
        pass


def PLUGIN_ENTRY():
    return HighlightCalls()


repository: thalium/idatag
file: test/tagtest.py
description: IDA plugin to explore and browse tags
url: https://github.com/thalium/idatag/blob/bdc681ac7deb06549218894c8a3b8aaee27a389a/test/tagtest.py
wanted_name: TagTest
comment: IDATag client - Test script
created_at: 2019-06-04 15:56:02+00:00
pushed_at: 2019-07-19 15:41:07+00:00
content:
import idaapi
import idc
import idautils
import json
import string
import random

PLUGIN_VERSION = "0.0.1"
AUTHORS = "Arnaud Gatignol @ Thalium"
DATE = "2019"


class tagstring_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "IDATag client - Test script"
    help = ""
    wanted_name = "TagTest"
    wanted_hotkey = ""

    def __init__(self):
        super(tagstring_t, self).__init__()
        self.tags = []
        self.path = idautils.GetIdbDir() + "tags\\"
        self.base = idaapi.get_imagebase()

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        print("=" * 80)
        print(
            "TagTest v{0} - {1} - {2}".format(PLUGIN_VERSION, AUTHORS, DATE))
        print("=" * 80)

        self.get_testtags()
        self.export_tags()

    def get_testtags(self):
        export_tag = {}
        functions = idautils.Functions()
        for function in functions:
            for i in range(10):
                feeder = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(4))
                tag = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
                export_tag = {'offset': function, 'tag': tag, 'feeder': feeder}
                self.tags.append(export_tag)
        for address, _ in idautils.Names():
            for i in range(10):
                feeder = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(4))
                tag = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
                export_tag = {'offset': address, 'tag': tag, 'feeder': feeder}
                self.tags.append(export_tag)

    def export_tags(self):
        out_dir = self.path + idaapi.get_root_filename() + "_TagTest" + ".json"
        with open(out_dir, 'wb') as f:
            json.dump(self.tags, f)
        print("[TagTest] Tags created : " + str(len(self.tags)))
        print("[TagTest] Tags exported : " + out_dir)

    def term(self):
        pass


def PLUGIN_ENTRY():
    return tagstring_t()


repository: grantmagdanz/virtualization-sdk-prototype
file: tools/src/test/python/dlpx/virtualization/_internal/conftest.py
created_at: 2019-08-09 19:00:25+00:00
pushed_at: 2020-01-22 00:22:30+00:00
url:
https://github.com/grantmagdanz/virtualization-sdk-prototype/blob/0b803bb82321924dae3bc3e8e1d55c01ce933243/tools/src/test/python/dlpx/virtualization/_internal/conftest.py

content:
#
# Copyright (c) 2019 by Delphix. All rights reserved.
#

import configparser
import copy
import json
import os

import pytest
import yaml
from dlpx.virtualization._internal import cli, click_util, package_util, util_classes

#
# conftest.py is used to share fixtures among multiple tests files. pytest will
# automatically get discovered in the test class if the figure name is used
# as the input variable. The idea of fixtures is to define certain object
# configs and allow them to get used in different tests but also being allowed
# to set certain parts definated in other fixtures. Read more at:
# https://docs.pytest.org/en/latest/fixture.html
#


@pytest.fixture
def plugin_config_file(tmpdir, plugin_config_filename, plugin_config_content):
    """
    This fixture creates a tempdir and writes the plugin_config_content to the
    file plugin_config_filename. Then it returns the full path of the file. If
    plugin_config_content is a dict we also want to create the full directory
    structure which includes the schema.json file and the src folder. Also
    it will do a yaml dump. If it's not a dict assume it's just a string and
     write that directly.
    """
    if isinstance(plugin_config_content, dict):
        plugin_config_content = yaml.dump(
            plugin_config_content, default_flow_style=False
        )

    f = tmpdir.join(plugin_config_filename)
    f.write(plugin_config_content)
    return f.strpath


@pytest.fixture
def plugin_config_filename():
    return "plugin_config.yml"


@pytest.fixture
def fake_staged_plugin_config():
    return os.path.join(
        os.path.dirname(__file__), "fake_plugin/staged/plugin_config.yml"
    )


@pytest.fixture
def fake_direct_plugin_config():
    return os.path.join(
        os.path.dirname(__file__), "fake_plugin/direct/plugin_config.yml"
    )


@pytest.fixture
def src_dir(tmpdir, src_dirname):
    """
    This fixture creates a tempdir and makes a directory in it called
    src_dirname.
    """
    path = tmpdir.join(src_dirname).strpath
    os.mkdir(path)
    return path


@pytest.fixture
def src_dirname():
    return "src"


@pytest.fixture
def schema_file(tmpdir, schema_filename, schema_content):
    """
    This fixture creates a tempdir and writes the schema.json file
    with the schema_content passed in via the fixture. Then it returns the
    path of the file. If artifact_content is a dict it will do a json dump,
    otherwise if it is just a string we'll write that directly.
    """
    if isinstance(schema_content, dict):
        schema_content = json.dumps(schema_content, indent=4)

    f = tmpdir.join(schema_filename)
    f.write(schema_content)
    return f.strpath


@pytest.fixture
def schema_filename():
    return "schema.json"


@pytest.fixture
def dvp_config_file(tmpdir, dvp_config_properties):
    dvp_dir = tmpdir.join(click_util.CONFIG_DIR_NAME).strpath
    os.mkdir(dvp_dir)
    dvp_config_filepath = os.path.join(dvp_dir, click_util.CONFIG_FILE_NAME)
    parser = configparser.ConfigParser()
    parser["default"] = dvp_config_properties
    with open(dvp_config_filepath, "wb") as config_file:
        parser.write(config_file)

    #
    # Add temp_dir to list of config files the ConfigFileProcessor will
    # check to ensure the fixture is cleaned up after the test completes.
    #
    click_util.ConfigFileProcessor.config_files = []
    click_util.ConfigFileProcessor.config_files.append(dvp_config_filepath)

    #
    # Context settings are initialized before the pytest fixture object
    # is created, so read the config file before the command is invoked
    #
    cli.CONTEXT_SETTINGS["obj"] = {}
    cli.CONTEXT_SETTINGS["obj"] = click_util.ConfigFileProcessor.read_config()

    reload(cli)


@pytest.fixture
def dvp_config_properties():
    return {"engine": "engine.delphix.com", "user": "user", "password": "password"}


@pytest.fixture
def artifact_file(tmpdir, artifact_content, artifact_filename, artifact_file_created):
    """
    This fixture creates a tempdir and writes the artifact_filename file
    with the artifact_content passed in via the fixture. Then it returns the
    path of the file. If artifact_content is a dict it will do a json dump,
    otherwise if it is just a string we'll write that directly.
    """
    f = tmpdir.join(artifact_filename)
    if artifact_file_created:
        # Only write the artifact if we want to actually create it.
        if isinstance(artifact_content, dict):
            artifact_content = json.dumps(artifact_content, indent=4)
        f.write(artifact_content)
    return f.strpath


@pytest.fixture
def artifact_filename():
    return "artifact.json"


@pytest.fixture
def artifact_file_created():
    return True


@pytest.fixture
def plugin_config_content(
    plugin_id,
    plugin_name,
    src_dir,
    schema_file,
    language,
    manual_discovery,
    plugin_type,
):
    """
    This fixutre creates the dict expected in the properties yaml file the
    customer must provide for the build and compile commands.
    """
    config = {
        "version": "2.0.0",
        "hostTypes": ["UNIX"],
        "entryPoint": "python_vfiles:vfiles",
        "defaultLocale": "en-us",
        "rootSquashEnabled": True,
    }
    if id:
        config["id"] = plugin_id

    if plugin_name:
        config["name"] = plugin_name

    if plugin_type:
        config["pluginType"] = plugin_type

    if src_dir:
        config["srcDir"] = src_dir

    if schema_file:
        config["schemaFile"] = schema_file

    if language:
        config["language"] = language

    # Here we do is not None check because we will be passing in
    # booleans as a parameter in tests.
    if manual_discovery is not None:
        config["manualDiscovery"] = manual_discovery

    return config


@pytest.fixture
def plugin_entry_point_name():
    return "vfiles"


@pytest.fixture
def plugin_module_content(plugin_entry_point_name):
    class Object(object):
        pass

    discovery = Object()
    discovery.repository_impl = True
    discovery.source_config_impl = True

    linked = Object()
    linked.pre_snapshot_impl = True
    linked.post_snapshot_impl = True
    linked.start_staging_impl = True
    linked.stop_staging_impl = False
    linked.status_impl = True
    linked.worker_impl = False
    linked.mount_specification_impl = True

    virtual = Object()
    virtual.configure_impl = True
    virtual.unconfigure_impl = False
    virtual.reconfigure_impl = True
    virtual.start_impl = True
    virtual.stop_impl = False
    virtual.pre_snapshot_impl = True
    virtual.post_snapshot_impl = True
    virtual.mount_specification_impl = True
    virtual.status_impl = False
    virtual.initialize_impl = False

    plugin_object = Object()
    plugin_object.discovery = discovery
    plugin_object.linked = linked
    plugin_object.virtual = virtual

    plugin_module = Object()
    setattr(plugin_module, plugin_entry_point_name, plugin_object)

    return plugin_module


@pytest.fixture
def plugin_manifest():
    manifest = {
        "type": "PluginManifest",
        "hasRepositoryDiscovery": True,
        "hasSourceConfigDiscovery": True,
        "hasLinkedPreSnapshot": True,
        "hasLinkedPostSnapshot": True,
        "hasLinkedStartStaging": True,
        "hasLinkedStopStaging": False,
        "hasLinkedStatus": True,
        "hasLinkedWorker": False,
        "hasLinkedMountSpecification": True,
        "hasVirtualConfigure": True,
        "hasVirtualUnconfigure": False,
        "hasVirtualReconfigure": True,
        "hasVirtualStart": True,
        "hasVirtualStop": False,
        "hasVirtualPreSnapshot": True,
        "hasVirtualPostSnapshot": True,
        "hasVirtualMountSpecification": True,
        "hasVirtualStatus": False,
        "hasInitialize": False,
    }
    return manifest


@pytest.fixture
def plugin_id():
    return "16bef554-9470-11e9-b2e3-8c8590d4a42c"


@pytest.fixture
def plugin_name():
    return "python_vfiles"


@pytest.fixture
def language():
    return "PYTHON27"


@pytest.fixture
def manual_discovery():
    return None


@pytest.fixture
def artifact_manual_discovery():
    return True


@pytest.fixture
def plugin_type():
    return util_classes.DIRECT_TYPE


@pytest.fixture
def schema_content(
    repository_definition,
    source_config_definition,
    virtual_source_definition,
    linked_source_definition,
    snapshot_definition,
    additional_definition,
):

    schema = {}

    if repository_definition:
        schema["repositoryDefinition"] = repository_definition

    if source_config_definition:
        schema["sourceConfigDefinition"] = source_config_definition

    if virtual_source_definition:
        schema["virtualSourceDefinition"] = virtual_source_definition

    if linked_source_definition:
        schema["linkedSourceDefinition"] = linked_source_definition

    if snapshot_definition:
        schema["snapshotDefinition"] = snapshot_definition

    if additional_definition:
        schema["additionalDefinition"] = additional_definition

    return schema


@pytest.fixture
def swagger_schema_content(schema_content, snapshot_parameters_definition):

    schema = schema_content
    if snapshot_parameters_definition:
        schema["snapshotParametersDefinition"] = snapshot_parameters_definition

    return schema


@pytest.fixture
def repository_definition():
    return {
        "type": "object",
        "properties": {"name": {"type": "string"}},
        "nameField": "name",
        "identityFields": ["name"],
    }


@pytest.fixture
def source_config_definition():
    return {
        "type": "object",
        "required": ["name", "path"],
        "additionalProperties": False,
        "properties": {"name": {"type": "string"}, "path": {"type": "string"}},
        "nameField": "name",
        "identityFields": ["path"],
    }


@pytest.fixture
def virtual_source_definition():
    return {
        "type": "object",
        "additionalProperties": False,
        "properties": {"path": {"type": "string"}},
    }


@pytest.fixture
def linked_source_definition():
    return {"type": "object", "additionalProperties": False, "properties": {}}


@pytest.fixture
def snapshot_definition():
    return {
        "type": "object",
        "additionalProperties": False,
        "properties": {"snapshot_name": {"type": "string"}},
    }


@pytest.fixture
def snapshot_parameters_definition():
    return {
        "type": "object",
        "additionalProperties": False,
        "properties": {"resync": {"type": "boolean"}},
    }


@pytest.fixture
def additional_definition():
    return None


@pytest.fixture
def basic_artifact_content(
    engine_api,
    virtual_source_definition,
    linked_source_definition,
    discovery_definition,
    snapshot_definition,
):
    artifact = {
        "type": "Plugin",
        "name": "16bef554-9470-11e9-b2e3-8c8590d4a42c",
        "prettyName": "python_vfiles",
        "version": "2.0.0",
        "defaultLocale": "en-us",
        "language": "PYTHON27",
        "hostTypes": ["UNIX"],
        "entryPoint": "python_vfiles:vfiles",
        "buildApi": package_util.get_build_api_version(),
        "engineApi": engine_api,
        "rootSquashEnabled": True,
        "sourceCode": "UEsFBgAAAAAAAAAAAAAAAAAAAAAAAA==",
        "manifest": {},
    }
    if virtual_source_definition:
        artifact["virtualSourceDefinition"] = {
            "type": "PluginVirtualSourceDefinition",
            "parameters": virtual_source_definition,
        }

    if linked_source_definition:
        artifact["linkedSourceDefinition"] = {
            "type": "PluginLinkedDirectSourceDefinition",
            "parameters": linked_source_definition,
        }

    if discovery_definition:
        artifact["discoveryDefinition"] = discovery_definition

    if snapshot_definition:
        artifact["snapshotSchema"] = snapshot_definition

    return artifact


@pytest.fixture
def artifact_content(
    engine_api,
    virtual_source_definition,
    linked_source_definition,
    discovery_definition,
    snapshot_definition,
):
    """
    This fixture creates base artifact that was generated from build and
    used in upload. If any fields besides engine_api needs to be changed,
    add fixtures below and add the function name to be part of the input to
    this function.
    """
    artifact = {
        "type": "Plugin",
        "name": "16bef554-9470-11e9-b2e3-8c8590d4a42c",
        "prettyName": "python_vfiles",
        "version": "2.0.0",
        "defaultLocale": "en-us",
        "language": "PYTHON27",
        "hostTypes": ["UNIX"],
        "entryPoint": "python_vfiles:vfiles",
        "buildApi": package_util.get_build_api_version(),
        "sourceCode": "UEsFBgAAAAAAAAAAAAAAAAAAAAAAAA==",
        "rootSquashEnabled": True,
        "manifest": {},
    }

    if engine_api:
        artifact["engineApi"] = engine_api

    if virtual_source_definition:
        artifact["virtualSourceDefinition"] = {
            "type": "PluginVirtualSourceDefinition",
            "parameters": virtual_source_definition,
        }

    if linked_source_definition:
        artifact["linkedSourceDefinition"] = {
            "type": "PluginLinkedDirectSourceDefinition",
            "parameters": linked_source_definition,
        }

    if discovery_definition:
        artifact["discoveryDefinition"] = discovery_definition

    if snapshot_definition:
        artifact["snapshotSchema"] = snapshot_definition

    return artifact


@pytest.fixture
def engine_api():
    return {"type": "APIVersion", "major": 1, "minor": 10, "micro": 5}


@pytest.fixture
def discovery_definition(
    repository_definition, source_config_definition, artifact_manual_discovery
):
    discovery_definition = {"type": "PluginDiscoveryDefinition"}

    if artifact_manual_discovery:
        discovery_definition["manualSourceConfigDiscovery"] = artifact_manual_discovery

    if repository_definition:
        old_repository_def = copy.deepcopy(repository_definition)
        repo_id_fields = old_repository_def.pop("identityFields", None)
        repo_name_field = old_repository_def.pop("nameField", None)

        if repo_id_fields:
            discovery_definition["repositoryIdentityFields"] = repo_id_fields
        if repo_name_field:
            discovery_definition["repositoryNameField"] = repo_name_field
        discovery_definition["repositorySchema"] = old_repository_def

    if source_config_definition:
        old_source_config_def = copy.deepcopy(source_config_definition)
        scf_id_fields = old_source_config_def.pop("identityFields", None)
        scf_name_field = old_source_config_def.pop("nameField", None)

        if scf_id_fields:
            discovery_definition["sourceConfigIdentityFields"] = scf_id_fields
        if scf_name_field:
            discovery_definition["sourceConfigNameField"] = scf_name_field
        discovery_definition["sourceConfigSchema"] = old_source_config_def

    return discovery_definition


@pytest.fixture
def linked_source_def_type(plugin_type):
    if plugin_type == "DIRECT":
        return "PluginLinkedDirectSourceDefinition"
    else:
        return "PluginLinkedStagedSourceDefinition"


@pytest.fixture
def codegen_gen_py_inputs(
    plugin_config_file, plugin_name, src_dir, tmpdir, schema_content
):
    class CodegenInput:
        def __init__(self, name, source_dir, plugin_content_dir, schema_dict):
            self.name = name
            self.source_dir = source_dir
            self.plugin_content_dir = plugin_content_dir
            self.schema_dict = schema_dict

    return CodegenInput(plugin_name, src_dir, tmpdir.strpath, schema_content)


repository: KernelAnalysisPlatform/KlareDbg
file: ida/python/qira.py
description: Kernel debugger using Timeless Debugging method.
wanted_name: QIRA Plugin
comment: QEMU Interactive Runtime Analyser plugin
created_at: 2016-06-20 12:58:40+00:00
pushed_at: 2017-02-16 04:54:55+00:00
url:
https://github.com/KernelAnalysisPlatform/KlareDbg/blob/9c80885928bef104019a27bff86689a819635d64/ida/python/qira.py

content:
import idaapi
import threading
import time

wsserver = None
qira_address = None

# this handles all receiving
msg_queue = []  # python array is threadsafe

def handle_message_queue():
  global msg_queue
  while len(msg_queue) > 0:
    dat = msg_queue[0].split(" ")
    msg_queue = msg_queue[1:]

    if dat[0] == "setaddress" and dat[1] != "undefined":
      try:
        a = idaapi.toEA(0, int(str(dat[1][2:]),16))
        jump_to(a)
      except e:
        idaapi.msg("[QIRA Plugin] Error processing the address\n")

def start_server():
  global wsserver
  wsserver = SimpleWebSocketServer('', 3003, QiraServer)
  if wsserver is not None:
    idaapi.msg("[QIRA Plugin] Starting WS Server\n")
    wsserver.serveforever()

def set_qira_address(la):
  global qira_address
  ea = 0
  if qira_address is not None and qira_address != BADADDR:
    ea = idaapi.toEA(0, qira_address)
    idaapi.del_bpt(ea)

  qira_address = la
  idaapi.add_bpt(qira_address, 0, BPT_SOFT)
  EnableBpt(qira_address, False)

def jump_to(a):
  global qira_address
  if a is not None:
    if (a != qira_address) and (a != BADADDR):
      set_qira_address(a)
      idaapi.jumpto(qira_address, -1, 0)
    else:
      idaapi.jumpto(qira_address, -1, 0)

def ws_send(msg):
  global wsserver
  if (wsserver is not None) and (msg is not None):
    for conn in wsserver.connections.itervalues():
      conn.sendMessage(msg)

def update_address(addr_type, addr):
  if (addr_type is not None) and (addr is not None):
    cmd = "set%s 0x%x" % (addr_type, addr)
    ws_send(cmd)

def update_comment(addr, rpt):
  cmt = idaapi.get_cmt(addr, rpt)
  if cmt is not None:
    ws_send("setcmt 0x%x %s" % (addr, cmt))

class MyIDAViewWrapper(idaapi.IDAViewWrapper):
  def __init__(self, viewName):
    idaapi.IDAViewWrapper.__init__(self, viewName)
    self.old_addr = None
    self.addr = None

  def OnViewCurpos(self):
    self.addr = idaapi.get_screen_ea()
    if (self.old_addr != self.addr):
      if (idaapi.isCode(idaapi.getFlags(self.addr))):
        # don't update the address if it's already the qira address or None
        if (self.addr is not None) and (self.addr != qira_address):
          #idaapi.msg("[QIRA Plugin] Qira Address %x \n" % (self.addr))
          # Instruction Address
          set_qira_address(self.addr)
          update_address("iaddr", self.addr)
      else:
        # Data Address
        update_address("daddr", self.addr)
    self.old_addr = self.addr

class idbhook(idaapi.IDB_Hooks):
  def cmt_changed(self, a, b):
    update_comment(a, b)
    return 0

class idphook(idaapi.IDP_Hooks):
  def renamed(self, ea, new_name, local_name):
    #print ea, new_name
    ws_send("setname 0x%x %s" % (ea, new_name))
    return 0
  
class uihook(idaapi.UI_Hooks):
  def __init__(self):
    idaapi.UI_Hooks.__init__(self)
    self.binds = []
  def preprocess(self, arg):
    #print "preprocess", arg
    return 0
  def current_tform_changed(self, a1, a2):
    #print "tform", idaapi.get_tform_title(a1)
    tm = MyIDAViewWrapper(idaapi.get_tform_title(a1))
    if tm.Bind():
      self.binds.append(tm)
    return 0


class qiraplugin_t(idaapi.plugin_t):
  flags = 0
  comment = "QEMU Interactive Runtime Analyser plugin"
  help = "Visit qira.me for more infos"
  wanted_name = "QIRA Plugin"
  wanted_hotkey = "z"

  def init(self):
    threading.Thread(target=start_server).start()
    idaapi.msg("[QIRA Plugin] Ready to go!\n")
    
    self.w1 = None

    #threading.Thread(target=poll_address).start()

    self.idbhook = idbhook()
    self.idbhook.hook()
    self.idphook = idphook()
    self.idphook.hook()
    self.uihook = uihook()
    self.uihook.hook()

    return idaapi.PLUGIN_KEEP


  def run(self, arg):
    global qira_address
    idaapi.msg("[QIRA Plugin] Syncing with Qira\n")


    # sync names
    for i in range(idaapi.get_nlist_size()):
      ws_send("setname 0x%x %s" % (idaapi.get_nlist_ea(i), idaapi.get_nlist_name(i)))

    # sync comment
    addr = idaapi.get_segm_base(idaapi.get_first_seg())
    while addr != idaapi.BADADDR:
      for rpt in [True, False]:
        update_comment(addr, rpt)
      addr = idaapi.nextaddr(addr)

  def term(self):
    global wsserver
    if wsserver is not None:
      wsserver.close()
    idaapi.msg("[QIRA Plugin] Plugin uninstalled!\n")


def PLUGIN_ENTRY():
  return qiraplugin_t()



###########################################################
#                                                         #
#    SimpleWebSocketServer                                #
#    https://github.com/opiate/SimpleWebSocketServer.git  #
#                                                         #
###########################################################

'''
The MIT License (MIT)

Copyright (c) 2013 Dave P.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''

import SocketServer
import hashlib
import base64
import socket
import struct
import ssl
import time
import sys
import errno
import logging
from BaseHTTPServer import BaseHTTPRequestHandler
from StringIO import StringIO
from select import select


class HTTPRequest(BaseHTTPRequestHandler):
  def __init__(self, request_text):
    self.rfile = StringIO(request_text)
    self.raw_requestline = self.rfile.readline()
    self.error_code = self.error_message = None
    self.parse_request()


class WebSocket(object):

  handshakeStr = (
    "HTTP/1.1 101 Switching Protocols\r\n"
    "Upgrade: WebSocket\r\n"
    "Connection: Upgrade\r\n"
    "Sec-WebSocket-Protocol: qira\r\n"
    "Sec-WebSocket-Accept: %(acceptstr)s\r\n\r\n"
  )

  hixiehandshakedStr = (
    "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
    "Upgrade: WebSocket\r\n"
    "Connection: Upgrade\r\n"
    "Sec-WebSocket-Origin: %(origin)s\r\n"
    "Sec-WebSocket-Protocol: qira\r\n"
    "Sec-WebSocket-Location: %(type)s://%(host)s%(location)s\r\n\r\n"
  )

  GUIDStr = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

  STREAM = 0x0
  TEXT = 0x1
  BINARY = 0x2
  CLOSE = 0x8
  PING = 0x9
  PONG = 0xA

  HEADERB1 = 1
  HEADERB2 = 3
  LENGTHSHORT = 4
  LENGTHLONG = 5
  MASK = 6
  PAYLOAD = 7

  def __init__(self, server, sock, address):
    self.server = server
    self.client = sock
    self.address = address

    self.handshaked = False
    self.headerbuffer = ''
    self.readdraftkey = False
    self.draftkey = ''
    self.headertoread = 2048
    self.hixie76 = False

    self.fin = 0
    self.data = None
    self.opcode = 0
    self.hasmask = 0
    self.maskarray = None
    self.length = 0
    self.lengtharray = None
    self.index = 0
    self.request = None
    self.usingssl = False

    self.state = self.HEADERB1

    # restrict the size of header and payload for security reasons
    self.maxheader = 65536
    self.maxpayload = 4194304

  def close(self):
    self.client.close()
    self.state = self.HEADERB1
    self.hasmask = False
    self.handshaked = False
    self.readdraftkey = False
    self.hixie76 = False
    self.headertoread = 2048
    self.headerbuffer = ''
    self.data = ''


  def handleMessage(self):
    pass

  def handleConnected(self):
    pass

  def handleClose(self):
    pass

  def handlePacket(self):
    # close
    if self.opcode == self.CLOSE:
      self.sendClose()
      raise Exception("received client close")
    # ping
    elif self.opcode == self.PING:
      pass

    # pong
    elif self.opcode == self.PONG:
      pass

    # data
    elif self.opcode == self.STREAM or self.opcode == self.TEXT or self.opcode == self.BINARY:
      self.handleMessage()


  def handleData(self):

    # do the HTTP header and handshake
    if self.handshaked is False:

      data = self.client.recv(self.headertoread)

      if data:
        # accumulate
        self.headerbuffer += data

        if len(self.headerbuffer) >= self.maxheader:
          raise Exception('header exceeded allowable size')

        # we need to read the entire 8 bytes of after the HTTP header, ensure we do
        if self.readdraftkey is True:
          self.draftkey += self.headerbuffer
          read = self.headertoread - len(self.headerbuffer)

          if read != 0:
            self.headertoread = read
          else:
            # complete hixie76 handshake
            self.handshake_hixie76()

        # indicates end of HTTP header
        elif '\r\n\r\n' in self.headerbuffer:
          self.request = HTTPRequest(self.headerbuffer)
          # hixie handshake
          if self.request.headers.has_key('Sec-WebSocket-Key1'.lower()) and self.request.headers.has_key('Sec-WebSocket-Key2'.lower()):
            # check if we have the key in our buffer
            index = self.headerbuffer.find('\r\n\r\n') + 4
            # determine how much of the 8 byte key we have
            read = len(self.headerbuffer) - index
            # do we have all the 8 bytes we need?
            if read < 8:
              self.headertoread = 8 - read
              self.readdraftkey = True
              if read > 0:
                self.draftkey += self.headerbuffer[index:index+read]

            else:
              # get the key
              self.draftkey += self.headerbuffer[index:index+8]
              # complete hixie handshake
              self.handshake_hixie76()

          # handshake rfc 6455
          elif self.request.headers.has_key('Sec-WebSocket-Key'.lower()):
            key = self.request.headers['Sec-WebSocket-Key'.lower()]
            hStr = self.handshakeStr % { 'acceptstr' :  base64.b64encode(hashlib.sha1(key + self.GUIDStr).digest()) }
            self.sendBuffer(hStr)
            self.handshaked = True
            self.headerbuffer = ''

            try:
              self.handleConnected()
            except:
              pass
          else:
            raise Exception('Sec-WebSocket-Key does not exist')

      # remote connection has been closed
      else:
        raise Exception("remote socket closed")

    # else do normal data
    else:
      data = self.client.recv(2048)
      if data:
        for val in data:
          if self.hixie76 is False:
            self.parseMessage(ord(val))
          else:
            self.parseMessage_hixie76(ord(val))
      else:
        raise Exception("remote socket closed")



  def handshake_hixie76(self):

    k1 = self.request.headers['Sec-WebSocket-Key1'.lower()]
    k2 = self.request.headers['Sec-WebSocket-Key2'.lower()]

    spaces1 = k1.count(" ")
    spaces2 = k2.count(" ")
    num1 = int("".join([c for c in k1 if c.isdigit()])) / spaces1
    num2 = int("".join([c for c in k2 if c.isdigit()])) / spaces2

    key = ''
    key += struct.pack('>I', num1)
    key += struct.pack('>I', num2)
    key += self.draftkey

    typestr = 'ws'
    if self.usingssl is True:
      typestr = 'wss'

    response = self.hixiehandshakedStr % { 'type' : typestr, 'origin' : self.request.headers['Origin'.lower()], 'host' : self.request.headers['Host'.lower()], 'location' : self.request.path }

    self.sendBuffer(response)
    self.sendBuffer(hashlib.md5(key).digest())

    self.handshaked = True
    self.hixie76 = True
    self.headerbuffer = ''

    try:
      self.handleConnected()
    except:
      pass


  def sendClose(self):

    msg = bytearray()
    if self.hixie76 is False:
      msg.append(0x88)
      msg.append(0x00)
      self.sendBuffer(msg)
    else:
      pass

  def sendBuffer(self, buff):
    size = len(buff)
    tosend = size
    index = 0

    while tosend > 0:
      try:
        # i should be able to send a bytearray
        sent = self.client.send(str(buff[index:size]))
        if sent == 0:
          raise RuntimeError("socket connection broken")

        index += sent
        tosend -= sent

      except socket.error as e:
        # if we have full buffers then wait for them to drain and try again
        if e.errno == errno.EAGAIN:
          time.sleep(0.001)
        else:
          raise e


  #if s is a string then websocket TEXT is sent else BINARY
  def sendMessage(self, s):

    if self.hixie76 is False:

      header = bytearray()
      isString = isinstance(s, str)

      if isString is True:
        header.append(0x81)
      else:
        header.append(0x82)

      b2 = 0
      length = len(s)

      if length <= 125:
        b2 |= length
        header.append(b2)

      elif length >= 126 and length <= 65535:
        b2 |= 126
        header.append(b2)
        header.extend(struct.pack("!H", length))

      else:
        b2 |= 127
        header.append(b2)
        header.extend(struct.pack("!Q", length))

      if length > 0:
        self.sendBuffer(header + s)
      else:
        self.sendBuffer(header)
      header = None

    else:
      msg = bytearray()
      msg.append(0)
      if len(s) > 0:
        msg.extend(str(s).encode("UTF8"))
      msg.append(0xFF)

      self.sendBuffer(msg)
      msg = None


  def parseMessage_hixie76(self, byte):

    if self.state == self.HEADERB1:
      if byte == 0:
        self.state = self.PAYLOAD
        self.data = bytearray()

    elif self.state == self.PAYLOAD:
      if byte == 0xFF:
        self.opcode = 1
        self.length = len(self.data)
        try:
          self.handlePacket()
        finally:
          self.data = None
          self.state = self.HEADERB1
      else :
        self.data.append(byte)
        # if length exceeds allowable size then we except and remove the connection
        if len(self.data) >= self.maxpayload:
          raise Exception('payload exceeded allowable size')


  def parseMessage(self, byte):
    # read in the header
    if self.state == self.HEADERB1:
      # fin
      self.fin = (byte & 0x80)
      # get opcode
      self.opcode = (byte & 0x0F)

      self.state = self.HEADERB2

    elif self.state == self.HEADERB2:
      mask = byte & 0x80
      length = byte & 0x7F

      if mask == 128:
        self.hasmask = True
      else:
        self.hasmask = False

      if length <= 125:
        self.length = length

        # if we have a mask we must read it
        if self.hasmask is True:
          self.maskarray = bytearray()
          self.state = self.MASK
        else:
          # if there is no mask and no payload we are done
          if self.length <= 0:
            try:
              self.handlePacket()
            finally:
              self.state = self.HEADERB1
              self.data = None

          # we have no mask and some payload
          else:
            self.index = 0
            self.data = bytearray()
            self.state = self.PAYLOAD

      elif length == 126:
        self.lengtharray = bytearray()
        self.state = self.LENGTHSHORT

      elif length == 127:
        self.lengtharray = bytearray()
        self.state = self.LENGTHLONG


    elif self.state == self.LENGTHSHORT:
      self.lengtharray.append(byte)

      if len(self.lengtharray) > 2:
        raise Exception('short length exceeded allowable size')

      if len(self.lengtharray) == 2:
        self.length = struct.unpack_from('!H', str(self.lengtharray))[0]

        if self.hasmask is True:
          self.maskarray = bytearray()
          self.state = self.MASK
        else:
          # if there is no mask and no payload we are done
          if self.length <= 0:
            try:
              self.handlePacket()
            finally:
              self.state = self.HEADERB1
              self.data = None

          # we have no mask and some payload
          else:
            self.index = 0
            self.data = bytearray()
            self.state = self.PAYLOAD

    elif self.state == self.LENGTHLONG:

      self.lengtharray.append(byte)

      if len(self.lengtharray) > 8:
        raise Exception('long length exceeded allowable size')

      if len(self.lengtharray) == 8:
        self.length = struct.unpack_from('!Q', str(self.lengtharray))[0]

        if self.hasmask is True:
          self.maskarray = bytearray()
          self.state = self.MASK
        else:
          # if there is no mask and no payload we are done
          if self.length <= 0:
            try:
              self.handlePacket()
            finally:
              self.state = self.HEADERB1
              self.data = None

          # we have no mask and some payload
          else:
            self.index = 0
            self.data = bytearray()
            self.state = self.PAYLOAD

    # MASK STATE
    elif self.state == self.MASK:
      self.maskarray.append(byte)

      if len(self.maskarray) > 4:
        raise Exception('mask exceeded allowable size')

      if len(self.maskarray) == 4:
        # if there is no mask and no payload we are done
        if self.length <= 0:
          try:
            self.handlePacket()
          finally:
            self.state = self.HEADERB1
            self.data = None

        # we have no mask and some payload
        else:
          self.index = 0
          self.data = bytearray()
          self.state = self.PAYLOAD

    # PAYLOAD STATE
    elif self.state == self.PAYLOAD:
      if self.hasmask is True:
        self.data.append( byte ^ self.maskarray[self.index % 4] )
      else:
        self.data.append( byte )

      # if length exceeds allowable size then we except and remove the connection
      if len(self.data) >= self.maxpayload:
        raise Exception('payload exceeded allowable size')

      # check if we have processed length bytes; if so we are done
      if (self.index+1) == self.length:
        try:
          self.handlePacket()
        finally:
          self.state = self.HEADERB1
          self.data = None
      else:
        self.index += 1


class SimpleWebSocketServer(object):
  def __init__(self, host, port, websocketclass):
    self.websocketclass = websocketclass
    self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    self.serversocket.bind((host, port))
    self.serversocket.listen(5)
    self.connections = {}
    self.listeners = [self.serversocket]
    self.forceclose = False


  def decorateSocket(self, sock):
    return sock

  def constructWebSocket(self, sock, address):
    return self.websocketclass(self, sock, address)

  def close(self):
    self.serversocket.close()

    for conn in self.connections.itervalues():
      try:
        conn.handleClose()
      except:
        pass

      conn.close()

    self.forceclose = True


  def serveforever(self):
    while True:
      rList, wList, xList = select(self.listeners, [], self.listeners, 1)

      if self.forceclose:
        break

      for ready in rList:
        if ready == self.serversocket:
          try:
            sock, address = self.serversocket.accept()
            newsock = self.decorateSocket(sock)
            newsock.setblocking(0)
            fileno = newsock.fileno()
            self.listeners.append(fileno)
            self.connections[fileno] = self.constructWebSocket(newsock, address)

          except Exception as n:

            logging.debug(str(address) + ' ' + str(n))

            if sock is not None:
              sock.close()
        else:
          client = self.connections[ready]

          try:
            client.handleData()

          except Exception as n:

            logging.debug(str(client.address) + ' ' + str(n))

            try:
              client.handleClose()
            except:
              pass

            client.close()

            del self.connections[ready]
            self.listeners.remove(ready)

      for failed in xList:
        if failed == self.serversocket:
          self.close()
          raise Exception("server socket failed")
        else:
          client = self.connections[failed]

          try:
            client.handleClose()
          except:
            pass

          client.close()

          del self.connections[failed]
          self.listeners.remove(failed)


class SimpleSSLWebSocketServer(SimpleWebSocketServer):

  def __init__(self, host, port, websocketclass, certfile, keyfile, version = ssl.PROTOCOL_TLSv1):

    SimpleWebSocketServer.__init__(self, host, port, websocketclass)

    self.cerfile = certfile
    self.keyfile = keyfile
    self.version = version

  def close(self):
    super(SimpleSSLWebSocketServer, self).close()

  def decorateSocket(self, sock):
    sslsock = ssl.wrap_socket(sock,
                  server_side=True,
                  certfile=self.cerfile,
                  keyfile=self.keyfile,
                  ssl_version=self.version)
    return sslsock

  def constructWebSocket(self, sock, address):
    ws = self.websocketclass(self, sock, address)
    ws.usingssl = True
    return ws

  def serveforever(self):
    super(SimpleSSLWebSocketServer, self).serveforever()



###################
#                 #
#    QIRA CODE    #
#                 #
###################


class QiraServer(WebSocket):
  def handleMessage(self):
    msg_queue.append(self.data)
    idaapi.execute_sync(handle_message_queue, idaapi.MFF_WRITE)

  def handleConnected(self):
    idaapi.msg("[QIRA Plugin] Client connected\n")

  def handleClose(self):
    idaapi.msg("[QIRA Plugin] WebSocket closed\n")


repository: HACK-THE-WORLD/IDAPluginList
file: symless/plugin/symless_plugin.py
description: IDA插件集合，含项目名称及项目地址，每日定时Clone项目。
wanted_name: Symless
comment: Symless interactive plugin
created_at: 2022-03-21 02:05:27+00:00
pushed_at: 2024-09-21 02:08:36+00:00
url:
https://github.com/HACK-THE-WORLD/IDAPluginList/blob/a6367e2126fca45260d1052b0c54bd332490fd60/symless/plugin/symless_plugin.py

content:
import base64
import collections
import importlib
import inspect
import os
import pkgutil
import sys
from typing import Collection

import idaapi

import symless
import symless.plugins as plugins
import symless.utils.utils as utils


class fixedBtn(idaapi.Form.ButtonInput):
    def __init__(self, plugin: "SymlessPlugin"):
        super().__init__(self.reload, "0")
        self.plugin = plugin

    def reload(self, code):
        idaapi.show_wait_box("Reloading Symless..")

        try:
            # terminate all extensions
            self.plugin.term()

            # reload symless code
            reload_plugin()

            # rebind all extensions
            self.plugin.find_extensions()

        except Exception as e:
            import traceback

            utils.g_logger.critical(repr(e) + "\n" + traceback.format_exc())
        finally:
            idaapi.hide_wait_box()

    def get_tag(self):
        return "<Reload:%s%d:%s%s:%s:%s>" % (
            self.tp,
            self.id,
            "+" if self.is_relative_offset else "",
            self.width,
            self.swidth,
            ":" if self.hlp is None else self.hlp,
        )


class SymlessInfoForm(idaapi.Form):
    def __init__(self, plugin: "SymlessPlugin"):
        icon_path = os.path.join(os.path.abspath(symless.__path__[0]), "resources", "bigger_champi.png")
        with open(icon_path, "rb") as file:
            icon_b64 = base64.b64encode(file.read()).decode()

        img_html = "<img src='data:image/png;base64,%s'>" % icon_b64
        info_html = """
            <div style='text-align: center; margin: 6px; font-size: 16px;'>
                <pre style='font-size: 72px;'>Symless</pre>
                <pre style='font-size: 16px; text-align: left;'>%s<br><br>Version: <b>%.1f</b></pre>
            </div>
        """ % (
            symless.PLUGIN_DESC,
            symless.PLUGIN_VERSION,
        )

        super().__init__(
            "BUTTON YES NONE\nBUTTON CANCEL NONE\nSymless plugin\n{img}<|>{info}\n{reload}",
            {
                "img": idaapi.Form.StringLabel(img_html, tp=idaapi.Form.FT_HTML_LABEL, size=None),
                "info": idaapi.Form.StringLabel(info_html, tp=idaapi.Form.FT_HTML_LABEL, size=None),
                "reload": fixedBtn(plugin),
            },
        )


# Symless plugin
class SymlessPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_MOD | idaapi.PLUGIN_PROC  # | idaapi.PLUGIN_HIDE
    comment = "Symless interactive plugin"
    wanted_name = "Symless"
    help = ""  # not used, IDA < 7.6 compatibility
    wanted_hotkey = ""  # not used, IDA < 7.6 compatibility

    # find & initialize all extensions
    def init(self) -> idaapi.plugmod_t:
        self.ext: Collection[plugins.plugin_t] = collections.deque()
        self.find_extensions()
        return idaapi.PLUGIN_KEEP

    # find and load extensions from symless plugins folder
    def find_extensions(self):
        for mod_info in pkgutil.walk_packages(plugins.__path__, prefix="symless.plugins."):
            if mod_info.ispkg:
                continue

            spec = mod_info.module_finder.find_spec(mod_info.name)
            module = importlib.util.module_from_spec(spec)

            # module is already loaded
            if module.__name__ in sys.modules:
                module = sys.modules[module.__name__]

            # load the module
            else:
                sys.modules[module.__name__] = module
                try:
                    spec.loader.exec_module(module)
                except BaseException as e:
                    sys.modules.pop(module.__name__)
                    print(f"Error while loading extension {mod_info.name}: {e}")
                    continue

            # module defines an extension
            if not hasattr(module, "get_plugin"):
                continue

            ext: plugins.plugin_t = module.get_plugin()
            self.ext.append(ext)

    # debug - reload plugin action
    def run(self, args):
        info = SymlessInfoForm(self)
        info.Compile()
        return info.Execute()

    # term all extensions
    def term(self):
        while len(self.ext) > 0:
            ext = self.ext.pop()
            ext.term()


def PLUGIN_ENTRY() -> idaapi.plugin_t:
    return SymlessPlugin()


# reload one module, by first reloading all imports from that module
# to_reload contains all modules to reload
def reload_module(module, to_reload: set):
    if module not in to_reload:
        return

    # remove from set first, avoid infinite recursion if recursive imports
    to_reload.remove(module)

    # reload all imports first
    for _, dep in inspect.getmembers(module, lambda k: inspect.ismodule(k)):
        reload_module(dep, to_reload)

    # reload the module
    utils.g_logger.info(f"Reloading {module.__name__} ..")
    importlib.reload(module)


# reload all symless code
def reload_plugin():
    # list all modules to reload, unordered
    to_reload = set()
    for k, mod in sys.modules.items():
        if k.startswith("symless"):
            to_reload.add(mod)

    for mod in list(to_reload):  # copy to alter
        reload_module(mod, to_reload)


repository: Introspelliam/ida-python
file: Scripts/AsmViewer.py
wanted_name: ASM file viewer
comment: ASM viewer
created_at: 2019-03-17 16:34:44+00:00
pushed_at: 2019-03-17 17:41:26+00:00
url:
https://github.com/Introspelliam/ida-python/blob/e3f6e6a763f442add7c2422cd410b2b03fd3e1fb/Scripts/AsmViewer.py

content:
from __future__ import print_function
# -----------------------------------------------------------------------
# This is an example illustrating how to use customview in Python
# The sample will allow you to open an assembly file and display it in color
# (c) Hex-Rays
#

import os

import ida_idaapi
import ida_kernwin
import ida_lines
import idautils

# ----------------------------------------------------------------------
class asm_colorizer_t(object):
    def is_id(self, ch):
        return ch == '_' or ch.isalpha() or '0' <= ch <= '9'

    def get_identifier(self, line, x, e):
        i = x
        is_digit = line[i].isdigit()
        while i < e:
            ch = line[i]
            if not self.is_id(ch):
                if ch != '.' or not is_digit:
                    break
            i += 1
        return (i, line[x:i])

    def get_quoted_string(self, line, x, e):
        quote = line[x]
        i = x + 1
        while i < e:
            ch = line[i]
            if ch == '\\' and line[i+1] == quote:
                i += 1
            elif ch == quote:
                i += 1 # also take the quote
                break
            i += 1
        return (i, line[x:i])

    def colorize(self, lines):
        for line in lines:
            line = line.rstrip()
            if not line:
                self.add_line()
                continue
            x = 0
            e = len(line)
            s = ""
            while x < e:
                ch = line[x]
                # String?
                if ch == '"' or ch == "'":
                    x, w = self.get_quoted_string(line, x, e)
                    s += self.as_string(w)
                # Tab?
                elif ch == '\t':
                    s += ' ' * 4
                    x += 1
                # Comment?
                elif ch == ';':
                    s += self.as_comment(line[x:])
                    # Done with this line
                    break
                elif ch == '.' and x + 1 < e:
                    x, w = self.get_identifier(line, x + 1, e)
                    s += self.as_directive(ch + w)
                # Identifiers?
                elif self.is_id(ch):
                    x, w = self.get_identifier(line, x, e)
                    # Number?
                    if ch.isdigit():
                        s += self.as_num(w)
                    # Other identifier
                    else:
                        s += self.as_id(w)
                # Output as is
                else:
                    s += ch
                    x += 1
            self.add_line(s)


class base_asmview_ah_t(ida_kernwin.action_handler_t):
    def __init__(self, obj):
        ida_kernwin.action_handler_t.__init__(self)
        self.obj = obj

    def update(self, ctx):
        if self.obj.view and self.obj.view.GetWidget() == ctx.widget:
            return ida_kernwin.AST_ENABLE_FOR_WIDGET
        else:
            return ida_kernwin.AST_DISABLE_FOR_WIDGET


class refresh_ah_t(base_asmview_ah_t):
    def activate(self, ctx):
        self.obj.view.reload_file()
        print("Reloaded")


class close_ah_t(base_asmview_ah_t):
    def activate(self, ctx):
        self.obj.view.Close()
        print("Closed")


# -----------------------------------------------------------------------
class asmview_t(ida_kernwin.simplecustviewer_t, asm_colorizer_t):
    def Create(self, fn):
        # Create the customview
        if not ida_kernwin.simplecustviewer_t.Create(
                self,
                "Viewing file - %s" % os.path.basename(fn)):
            return False

        self.instruction_list = idautils.GetInstructionList()
        self.instruction_list.extend(["ret"])
        self.register_list    = idautils.GetRegisterList()
        self.register_list.extend(["eax", "ebx", "ecx", "edx", "edi", "esi", "ebp", "esp"])

        self.fn = fn
        if not self.reload_file():
            return False

        return True

    def reload_file(self):
        if not self.colorize_file(self.fn):
            self.Close()
            return False
        return True

    def colorize_file(self, fn):
        try:
            f = open(fn, "r")
            lines = f.readlines()
            f.close()
            self.ClearLines()
            self.colorize(lines)
            return True
        except:
            return False

    def add_line(self, s=None):
        if not s:
            s = ""
        self.AddLine(s)

    def as_comment(self, s):
        return ida_lines.COLSTR(s, ida_lines.SCOLOR_RPTCMT)

    def as_id(self, s):
        t = s.lower()
        if t in self.register_list:
            return ida_lines.COLSTR(s, ida_lines.SCOLOR_REG)
        elif t in self.instruction_list:
            return ida_lines.COLSTR(s, ida_lines.SCOLOR_INSN)
        else:
            return s

    def as_string(self, s):
        return ida_lines.COLSTR(s, ida_lines.SCOLOR_STRING)

    def as_num(self, s):
        return ida_lines.COLSTR(s, ida_lines.SCOLOR_NUMBER)

    def as_directive(self, s):
        return ida_lines.COLSTR(s, ida_lines.SCOLOR_KEYWORD)

    def OnKeydown(self, vkey, shift):
        """
        User pressed a key
        @param vkey: Virtual key code
        @param shift: Shift flag
        @return Boolean. True if you handled the event
        """
        # ESCAPE
        if vkey == 27:
            self.Close()
        elif vkey == ord('H'):
            lineno = self.GetLineNo()
            if lineno is not None:
                line, fg, bg = self.GetLine(lineno)
                if line and line[0] != ida_lines.SCOLOR_INV:
                    s = ida_lines.SCOLOR_INV + line + ida_lines.SCOLOR_INV
                    self.EditLine(lineno, s, fg, bg)
                    self.Refresh()
        elif vkey == ord('C'):
            self.ClearLines()
            self.Refresh()
        elif vkey == ord('S'):
            print("Selection (x1, y1, x2, y2) = ", self.GetSelection())
        elif vkey == ord('I'):
            print("Position (line, x, y) = ", self.GetPos(mouse = 0))
        else:
            return False
        return True

# -----------------------------------------------------------------------
ACTNAME_REFRESH = "asmview_t::refresh"
ACTNAME_CLOSE = "asmview_t::close"

class asmviewplg(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_KEEP
    comment = "ASM viewer"
    help = "This is help"
    wanted_name = "ASM file viewer"
    wanted_hotkey = "Alt-F8"
    def __init__(self):
        self.view = None

    def init(self):
        # Register actions
        ida_kernwin.register_action(
            ida_kernwin.action_desc_t(
                ACTNAME_REFRESH, "Refresh", refresh_ah_t(self)))
        ida_kernwin.register_action(
            ida_kernwin.action_desc_t(
                ACTNAME_CLOSE, "Close", close_ah_t(self)))
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        if self.view:
            self.Close()
        fn = ida_kernwin.ask_file(0, "*.asm", "Select ASM file to view")
        if not fn:
            return
        self.view = asmview_t()
        if not self.view.Create(fn):
            return
        self.view.Show()
        widget = self.view.GetWidget()

        # Attach actions to this widget's popup menu
        ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_REFRESH)
        ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLOSE)

    def term(self):
        if self.view:
            self.view.Close()

def PLUGIN_ENTRY():
    return asmviewplg()


repository: vAlerainTech/vAlerain-Ark
file: cmake-build-debug/IDA_Pro_7.7/python/examples/hexrays/vds5.py
wanted_name: Hex-Rays show C graph (IDAPython)
comment: Sample plugin5 for Hex-Rays decompiler
created_at: 2024-03-25 14:26:36+00:00
pushed_at: 2024-07-27 05:07:46+00:00
url:
https://github.com/vAlerainTech/vAlerain-Ark/blob/df8bcafeda091d30ceade7bb2c7c73b5ae351a38/cmake-build-debug/IDA_Pro_7.7/python/examples/hexrays/vds5.py

content:
"""
summary: show ctree graph

description:
  Registers an action that can be used to show the graph of the ctree.
  The current item will be highlighted in the graph.

  The command shortcut is `Ctrl+Shift+G`, and is also added
  to the context menu.

  To display the graph, we produce a .gdl file, and
  request that ida displays that using `ida_gdl.display_gdl`.
"""

from __future__ import print_function

import ida_pro
import ida_hexrays
import ida_kernwin
import ida_gdl
import ida_lines
import ida_idaapi

ACTION_NAME = "vds5.py:displaygraph"
ACTION_SHORTCUT = "Ctrl+Shift+G"

CL_WHITE            = ((255)+  (255<<8)+  (255<<16)) #   0
CL_BLUE             = ((0  )+  (0  <<8)+  (255<<16)) #   1
CL_RED              = ((255)+  (0  <<8)+  (0  <<16)) #   2
CL_GREEN            = ((0  )+  (255<<8)+  (0  <<16)) #   3
CL_YELLOW           = ((255)+  (255<<8)+  (0  <<16)) #   4
CL_MAGENTA          = ((255)+  (0  <<8)+  (255<<16)) #   5
CL_CYAN             = ((0  )+  (255<<8)+  (255<<16)) #   6
CL_DARKGREY         = ((85 )+  (85 <<8)+  (85 <<16)) #   7
CL_DARKBLUE         = ((0  )+  (0  <<8)+  (128<<16)) #   8
CL_DARKRED          = ((128)+  (0  <<8)+  (0  <<16)) #   9
CL_DARKGREEN        = ((0  )+  (128<<8)+  (0  <<16)) #  10
CL_DARKYELLOW       = ((128)+  (128<<8)+  (0  <<16)) #  11
CL_DARKMAGENTA      = ((128)+  (0  <<8)+  (128<<16)) #  12
CL_DARKCYAN         = ((0  )+  (128<<8)+  (128<<16)) #  13
CL_GOLD             = ((255)+  (215<<8)+  (0  <<16)) #  14
CL_LIGHTGREY        = ((170)+  (170<<8)+  (170<<16)) #  15
CL_LIGHTBLUE        = ((128)+  (128<<8)+  (255<<16)) #  16
CL_LIGHTRED         = ((255)+  (128<<8)+  (128<<16)) #  17
CL_LIGHTGREEN       = ((128)+  (255<<8)+  (128<<16)) #  18
CL_LIGHTYELLOW      = ((255)+  (255<<8)+  (128<<16)) #  19
CL_LIGHTMAGENTA     = ((255)+  (128<<8)+  (255<<16)) #  20
CL_LIGHTCYAN        = ((128)+  (255<<8)+  (255<<16)) #  21
CL_LILAC            = ((238)+  (130<<8)+  (238<<16)) #  22
CL_TURQUOISE        = ((64 )+  (224<<8)+  (208<<16)) #  23
CL_AQUAMARINE       = ((127)+  (255<<8)+  (212<<16)) #  24
CL_KHAKI            = ((240)+  (230<<8)+  (140<<16)) #  25
CL_PURPLE           = ((160)+  (32 <<8)+  (240<<16)) #  26
CL_YELLOWGREEN      = ((154)+  (205<<8)+  (50 <<16)) #  27
CL_PINK             = ((255)+  (192<<8)+  (203<<16)) #  28
CL_ORANGE           = ((255)+  (165<<8)+  (0  <<16)) #  29
CL_ORCHID           = ((218)+  (112<<8)+  (214<<16)) #  30
CL_BLACK            = ((0  )+  (0  <<8)+  (0  <<16)) #  31

COLORS_LUT = {
    CL_WHITE             : "white",
    CL_BLUE              : "blue",
    CL_RED               : "red",
    CL_GREEN             : "green",
    CL_YELLOW            : "yellow",
    CL_MAGENTA           : "magenta",
    CL_CYAN              : "cyan",
    CL_DARKGREY          : "darkgrey",
    CL_DARKBLUE          : "darkblue",
    CL_DARKRED           : "darkred",
    CL_DARKGREEN         : "darkgreen",
    CL_DARKYELLOW        : "darkyellow",
    CL_DARKMAGENTA       : "darkmagenta",
    CL_DARKCYAN          : "darkcyan",
    CL_GOLD              : "gold",
    CL_LIGHTGREY         : "lightgrey",
    CL_LIGHTBLUE         : "lightblue",
    CL_LIGHTRED          : "lightred",
    CL_LIGHTGREEN        : "lightgreen",
    CL_LIGHTYELLOW       : "lightyellow",
    CL_LIGHTMAGENTA      : "lightmagenta",
    CL_LIGHTCYAN         : "lightcyan",
    CL_LILAC             : "lilac",
    CL_TURQUOISE         : "turquoise",
    CL_AQUAMARINE        : "aquamarine",
    CL_KHAKI             : "khaki",
    CL_PURPLE            : "purple",
    CL_YELLOWGREEN       : "yellowgreen",
    CL_PINK              : "pink",
    CL_ORANGE            : "orange",
    CL_ORCHID            : "orchid",
    CL_BLACK             : "black",
}

def get_color_name(c):
    return COLORS_LUT[c] if c in COLORS_LUT.keys() else "?"

class cfunc_graph_t: # alas we can't inherit gdl_graph_t
    def __init__(self, highlight):
        self.items = [] # list of citem_t
        self.highlight = highlight
        self.succs = [] # list of lists of next nodes
        self.preds = [] # list of lists of previous nodes

    def nsucc(self, n):
        return len(self.succs[n]) if self.size() else 0

    def npred(self, n):
        return len(self.preds[n]) if self.size() else 0

    def succ(self, n, i):
        return self.succs[n][i]

    def pred(self, n, i):
        return self.preds[n][i]

    def size(self):
        return len(self.preds)

    def add_node(self):
        n = self.size()

        def resize(array, new_size):
            if new_size > len(array):
                while len(array) < new_size:
                    array.append([])
            else:
                array = array[:new_size]
            return array

        self.preds = resize(self.preds, n+1)
        self.succs = resize(self.succs, n+1)
        return n

    def add_edge(self, x, y):
        self.preds[y].append(x)
        self.succs[x].append(y)

    def get_expr_name(self, expr):
        name = expr.print1(None)
        name = ida_lines.tag_remove(name)
        name = ida_pro.str2user(name)
        return name

    def get_node_label(self, n):
        item = self.items[n]
        op = item.op
        insn = item.cinsn
        expr = item.cexpr
        parts = [ida_hexrays.get_ctype_name(op)]
        if op == ida_hexrays.cot_ptr:
            parts.append(".%d" % expr.ptrsize)
        elif op == ida_hexrays.cot_memptr:
            parts.append(".%d (m=%d)" % (expr.ptrsize, expr.m))
        elif op == ida_hexrays.cot_memref:
            parts.append(" (m=%d)" % (expr.m,))
        elif op in [
                ida_hexrays.cot_obj,
                ida_hexrays.cot_var]:
            name = self.get_expr_name(expr)
            parts.append(".%d %s" % (expr.refwidth, name))
        elif op in [
                ida_hexrays.cot_num,
                ida_hexrays.cot_helper,
                ida_hexrays.cot_str]:
            name = self.get_expr_name(expr)
            parts.append(" %s" % (name,))
        elif op == ida_hexrays.cit_goto:
            parts.append(" LABEL_%d" % insn.cgoto.label_num)
        elif op == ida_hexrays.cit_asm:
            parts.append("<asm statements; unsupported ATM>")
            # parts.append(" %a.%d" % ())
        parts.append("\\n")
        parts.append("ea: %08X" % item.ea)
        if item.is_expr() and not expr.type.empty():
            parts.append("\\n")
            tstr = expr.type._print()
            parts.append(tstr if tstr else "?")
        return "".join(parts)

    def get_node_color(self, n):
        item = self.items[n]
        if self.highlight is not None and item.obj_id == self.highlight.obj_id:
            return CL_GREEN
        return None

    def gen_gdl(self, fname):
        with open(fname, "wb") as out:
            def write_out(s):
                out.write(s.encode("UTF-8"))
            write_out("graph: {\n")

            write_out("// *** nodes\n")
            for n in range(len(self.items)):
                item = self.items[n]
                node_label = self.get_node_label(n)
                node_props = [""]
                if n == 0:
                    node_props.append("vertical_order: 0")
                color = self.get_node_color(n)
                if color is not None:
                    node_props.append("color: %s" % get_color_name(color))
                write_out("""node: { title: "%d" label: "%d: %s" %s}\n""" % (
                    n,
                    n,
                    node_label,
                    " ".join(node_props)))

            write_out("// *** edges\n")
            for n in range(len(self.items)):
                item = self.items[n]
                write_out("// edges %d -> ?\n" % n)
                for i in range(self.nsucc(n)):
                    t = self.succ(n, i)
                    label = ""
                    if item.is_expr():
                        target = self.items[t]
                        if item.x is not None and item.x == target:
                            label = "x"
                        elif item.y is not None and item.y == target:
                            label = "y"
                        elif item.z is not None and item.z == target:
                            label = "z"
                        if label:
                            label = """ label: "%s" """ % label
                    write_out("""edge: { sourcename: "%s" targetname: "%s"%s}\n""" % (
                        str(n), str(t), label))

            write_out("}\n")

    def dump(self):
        print("%d items:" % len(self.items))
        for i in self.items:
            print("\t%s (%08x)" % (i, i.ea))

        print("succs:")
        for s in self.succs:
            print("\t%s" % s)

        print("preds:")
        for p in self.preds:
            print("\t%s" % p)


class graph_builder_t(ida_hexrays.ctree_parentee_t):

    def __init__(self, cg):
        ida_hexrays.ctree_parentee_t.__init__(self)
        self.cg = cg
        self.reverse = [] # (citem_t, node#) tuples

    def add_node(self, i):
        for k, _ in self.reverse:
            if i.obj_id == k.obj_id:
                ida_kernwin.warning("bad ctree - duplicate nodes! (i.ea=%x)" % i.ea)
                self.cg.dump()
                return -1

        n = self.cg.add_node()
        if n <= len(self.cg.items):
            self.cg.items.append(i)
        self.cg.items[n] = i
        self.reverse.append((i, n))
        return n

    def process(self, i):
        n = self.add_node(i)
        if n < 0:
            return n
        if len(self.parents) > 1:
            lp = self.parents.back().obj_id
            for k, v in self.reverse:
                if k.obj_id == lp:
                    p = v
                    break
            self.cg.add_edge(p, n)
        return 0

    def visit_insn(self, i):
        return self.process(i)

    def visit_expr(self, e):
        return self.process(e)


class display_graph_ah_t(ida_kernwin.action_handler_t):
    def __init__(self):
        ida_kernwin.action_handler_t.__init__(self)

    def activate(self, ctx):
        vu = ida_hexrays.get_widget_vdui(ctx.widget)
        vu.get_current_item(ida_hexrays.USE_KEYBOARD)
        highlight = vu.item.e if vu.item.is_citem() else None

        cg = cfunc_graph_t(highlight)
        gb = graph_builder_t(cg)
        gb.apply_to(vu.cfunc.body, None)

        import tempfile
        fname = tempfile.mktemp(suffix=".gdl")
        cg.gen_gdl(fname)
        ida_gdl.display_gdl(fname)
        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


class vds5_hooks_t(ida_hexrays.Hexrays_Hooks):
    def populating_popup(self, widget, handle, vu):
        ida_kernwin.attach_action_to_popup(vu.ct, None, ACTION_NAME)
        return 0

# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "Hex-Rays show C graph (IDAPython)"
    wanted_hotkey = ""
    comment = "Sample plugin5 for Hex-Rays decompiler"
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            ida_kernwin.register_action(
                ida_kernwin.action_desc_t(
                    ACTION_NAME,
                    "Hex-Rays show C graph (IDAPython)",
                    display_graph_ah_t(),
                    ACTION_SHORTCUT))
            self.vds5_hooks = vds5_hooks_t()
            self.vds5_hooks.hook()
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        self.vds5_hooks.unhook()
    def run(self, arg):
        pass

def PLUGIN_ENTRY():
    return my_plugin_t()


repository: BraveCattle/ctf
file: tools/ida_pro/IDA_Pro_7.7/python/examples/hexrays/vds5.py
wanted_name: Hex-Rays show C graph (IDAPython)
comment: Sample plugin5 for Hex-Rays decompiler
created_at: 2024-01-23 08:17:51+00:00
pushed_at: 2024-10-23 09:31:30+00:00
url:
https://github.com/BraveCattle/ctf/blob/5858d0da5c048eb8468ff8be1d63fbc50d61b601/tools/ida_pro/IDA_Pro_7.7/python/examples/hexrays/vds5.py

content:
"""
summary: show ctree graph

description:
  Registers an action that can be used to show the graph of the ctree.
  The current item will be highlighted in the graph.

  The command shortcut is `Ctrl+Shift+G`, and is also added
  to the context menu.

  To display the graph, we produce a .gdl file, and
  request that ida displays that using `ida_gdl.display_gdl`.
"""

from __future__ import print_function

import ida_pro
import ida_hexrays
import ida_kernwin
import ida_gdl
import ida_lines
import ida_idaapi

ACTION_NAME = "vds5.py:displaygraph"
ACTION_SHORTCUT = "Ctrl+Shift+G"

CL_WHITE            = ((255)+  (255<<8)+  (255<<16)) #   0
CL_BLUE             = ((0  )+  (0  <<8)+  (255<<16)) #   1
CL_RED              = ((255)+  (0  <<8)+  (0  <<16)) #   2
CL_GREEN            = ((0  )+  (255<<8)+  (0  <<16)) #   3
CL_YELLOW           = ((255)+  (255<<8)+  (0  <<16)) #   4
CL_MAGENTA          = ((255)+  (0  <<8)+  (255<<16)) #   5
CL_CYAN             = ((0  )+  (255<<8)+  (255<<16)) #   6
CL_DARKGREY         = ((85 )+  (85 <<8)+  (85 <<16)) #   7
CL_DARKBLUE         = ((0  )+  (0  <<8)+  (128<<16)) #   8
CL_DARKRED          = ((128)+  (0  <<8)+  (0  <<16)) #   9
CL_DARKGREEN        = ((0  )+  (128<<8)+  (0  <<16)) #  10
CL_DARKYELLOW       = ((128)+  (128<<8)+  (0  <<16)) #  11
CL_DARKMAGENTA      = ((128)+  (0  <<8)+  (128<<16)) #  12
CL_DARKCYAN         = ((0  )+  (128<<8)+  (128<<16)) #  13
CL_GOLD             = ((255)+  (215<<8)+  (0  <<16)) #  14
CL_LIGHTGREY        = ((170)+  (170<<8)+  (170<<16)) #  15
CL_LIGHTBLUE        = ((128)+  (128<<8)+  (255<<16)) #  16
CL_LIGHTRED         = ((255)+  (128<<8)+  (128<<16)) #  17
CL_LIGHTGREEN       = ((128)+  (255<<8)+  (128<<16)) #  18
CL_LIGHTYELLOW      = ((255)+  (255<<8)+  (128<<16)) #  19
CL_LIGHTMAGENTA     = ((255)+  (128<<8)+  (255<<16)) #  20
CL_LIGHTCYAN        = ((128)+  (255<<8)+  (255<<16)) #  21
CL_LILAC            = ((238)+  (130<<8)+  (238<<16)) #  22
CL_TURQUOISE        = ((64 )+  (224<<8)+  (208<<16)) #  23
CL_AQUAMARINE       = ((127)+  (255<<8)+  (212<<16)) #  24
CL_KHAKI            = ((240)+  (230<<8)+  (140<<16)) #  25
CL_PURPLE           = ((160)+  (32 <<8)+  (240<<16)) #  26
CL_YELLOWGREEN      = ((154)+  (205<<8)+  (50 <<16)) #  27
CL_PINK             = ((255)+  (192<<8)+  (203<<16)) #  28
CL_ORANGE           = ((255)+  (165<<8)+  (0  <<16)) #  29
CL_ORCHID           = ((218)+  (112<<8)+  (214<<16)) #  30
CL_BLACK            = ((0  )+  (0  <<8)+  (0  <<16)) #  31

COLORS_LUT = {
    CL_WHITE             : "white",
    CL_BLUE              : "blue",
    CL_RED               : "red",
    CL_GREEN             : "green",
    CL_YELLOW            : "yellow",
    CL_MAGENTA           : "magenta",
    CL_CYAN              : "cyan",
    CL_DARKGREY          : "darkgrey",
    CL_DARKBLUE          : "darkblue",
    CL_DARKRED           : "darkred",
    CL_DARKGREEN         : "darkgreen",
    CL_DARKYELLOW        : "darkyellow",
    CL_DARKMAGENTA       : "darkmagenta",
    CL_DARKCYAN          : "darkcyan",
    CL_GOLD              : "gold",
    CL_LIGHTGREY         : "lightgrey",
    CL_LIGHTBLUE         : "lightblue",
    CL_LIGHTRED          : "lightred",
    CL_LIGHTGREEN        : "lightgreen",
    CL_LIGHTYELLOW       : "lightyellow",
    CL_LIGHTMAGENTA      : "lightmagenta",
    CL_LIGHTCYAN         : "lightcyan",
    CL_LILAC             : "lilac",
    CL_TURQUOISE         : "turquoise",
    CL_AQUAMARINE        : "aquamarine",
    CL_KHAKI             : "khaki",
    CL_PURPLE            : "purple",
    CL_YELLOWGREEN       : "yellowgreen",
    CL_PINK              : "pink",
    CL_ORANGE            : "orange",
    CL_ORCHID            : "orchid",
    CL_BLACK             : "black",
}

def get_color_name(c):
    return COLORS_LUT[c] if c in COLORS_LUT.keys() else "?"

class cfunc_graph_t: # alas we can't inherit gdl_graph_t
    def __init__(self, highlight):
        self.items = [] # list of citem_t
        self.highlight = highlight
        self.succs = [] # list of lists of next nodes
        self.preds = [] # list of lists of previous nodes

    def nsucc(self, n):
        return len(self.succs[n]) if self.size() else 0

    def npred(self, n):
        return len(self.preds[n]) if self.size() else 0

    def succ(self, n, i):
        return self.succs[n][i]

    def pred(self, n, i):
        return self.preds[n][i]

    def size(self):
        return len(self.preds)

    def add_node(self):
        n = self.size()

        def resize(array, new_size):
            if new_size > len(array):
                while len(array) < new_size:
                    array.append([])
            else:
                array = array[:new_size]
            return array

        self.preds = resize(self.preds, n+1)
        self.succs = resize(self.succs, n+1)
        return n

    def add_edge(self, x, y):
        self.preds[y].append(x)
        self.succs[x].append(y)

    def get_expr_name(self, expr):
        name = expr.print1(None)
        name = ida_lines.tag_remove(name)
        name = ida_pro.str2user(name)
        return name

    def get_node_label(self, n):
        item = self.items[n]
        op = item.op
        insn = item.cinsn
        expr = item.cexpr
        parts = [ida_hexrays.get_ctype_name(op)]
        if op == ida_hexrays.cot_ptr:
            parts.append(".%d" % expr.ptrsize)
        elif op == ida_hexrays.cot_memptr:
            parts.append(".%d (m=%d)" % (expr.ptrsize, expr.m))
        elif op == ida_hexrays.cot_memref:
            parts.append(" (m=%d)" % (expr.m,))
        elif op in [
                ida_hexrays.cot_obj,
                ida_hexrays.cot_var]:
            name = self.get_expr_name(expr)
            parts.append(".%d %s" % (expr.refwidth, name))
        elif op in [
                ida_hexrays.cot_num,
                ida_hexrays.cot_helper,
                ida_hexrays.cot_str]:
            name = self.get_expr_name(expr)
            parts.append(" %s" % (name,))
        elif op == ida_hexrays.cit_goto:
            parts.append(" LABEL_%d" % insn.cgoto.label_num)
        elif op == ida_hexrays.cit_asm:
            parts.append("<asm statements; unsupported ATM>")
            # parts.append(" %a.%d" % ())
        parts.append("\\n")
        parts.append("ea: %08X" % item.ea)
        if item.is_expr() and not expr.type.empty():
            parts.append("\\n")
            tstr = expr.type._print()
            parts.append(tstr if tstr else "?")
        return "".join(parts)

    def get_node_color(self, n):
        item = self.items[n]
        if self.highlight is not None and item.obj_id == self.highlight.obj_id:
            return CL_GREEN
        return None

    def gen_gdl(self, fname):
        with open(fname, "wb") as out:
            def write_out(s):
                out.write(s.encode("UTF-8"))
            write_out("graph: {\n")

            write_out("// *** nodes\n")
            for n in range(len(self.items)):
                item = self.items[n]
                node_label = self.get_node_label(n)
                node_props = [""]
                if n == 0:
                    node_props.append("vertical_order: 0")
                color = self.get_node_color(n)
                if color is not None:
                    node_props.append("color: %s" % get_color_name(color))
                write_out("""node: { title: "%d" label: "%d: %s" %s}\n""" % (
                    n,
                    n,
                    node_label,
                    " ".join(node_props)))

            write_out("// *** edges\n")
            for n in range(len(self.items)):
                item = self.items[n]
                write_out("// edges %d -> ?\n" % n)
                for i in range(self.nsucc(n)):
                    t = self.succ(n, i)
                    label = ""
                    if item.is_expr():
                        target = self.items[t]
                        if item.x is not None and item.x == target:
                            label = "x"
                        elif item.y is not None and item.y == target:
                            label = "y"
                        elif item.z is not None and item.z == target:
                            label = "z"
                        if label:
                            label = """ label: "%s" """ % label
                    write_out("""edge: { sourcename: "%s" targetname: "%s"%s}\n""" % (
                        str(n), str(t), label))

            write_out("}\n")

    def dump(self):
        print("%d items:" % len(self.items))
        for i in self.items:
            print("\t%s (%08x)" % (i, i.ea))

        print("succs:")
        for s in self.succs:
            print("\t%s" % s)

        print("preds:")
        for p in self.preds:
            print("\t%s" % p)


class graph_builder_t(ida_hexrays.ctree_parentee_t):

    def __init__(self, cg):
        ida_hexrays.ctree_parentee_t.__init__(self)
        self.cg = cg
        self.reverse = [] # (citem_t, node#) tuples

    def add_node(self, i):
        for k, _ in self.reverse:
            if i.obj_id == k.obj_id:
                ida_kernwin.warning("bad ctree - duplicate nodes! (i.ea=%x)" % i.ea)
                self.cg.dump()
                return -1

        n = self.cg.add_node()
        if n <= len(self.cg.items):
            self.cg.items.append(i)
        self.cg.items[n] = i
        self.reverse.append((i, n))
        return n

    def process(self, i):
        n = self.add_node(i)
        if n < 0:
            return n
        if len(self.parents) > 1:
            lp = self.parents.back().obj_id
            for k, v in self.reverse:
                if k.obj_id == lp:
                    p = v
                    break
            self.cg.add_edge(p, n)
        return 0

    def visit_insn(self, i):
        return self.process(i)

    def visit_expr(self, e):
        return self.process(e)


class display_graph_ah_t(ida_kernwin.action_handler_t):
    def __init__(self):
        ida_kernwin.action_handler_t.__init__(self)

    def activate(self, ctx):
        vu = ida_hexrays.get_widget_vdui(ctx.widget)
        vu.get_current_item(ida_hexrays.USE_KEYBOARD)
        highlight = vu.item.e if vu.item.is_citem() else None

        cg = cfunc_graph_t(highlight)
        gb = graph_builder_t(cg)
        gb.apply_to(vu.cfunc.body, None)

        import tempfile
        fname = tempfile.mktemp(suffix=".gdl")
        cg.gen_gdl(fname)
        ida_gdl.display_gdl(fname)
        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


class vds5_hooks_t(ida_hexrays.Hexrays_Hooks):
    def populating_popup(self, widget, handle, vu):
        ida_kernwin.attach_action_to_popup(vu.ct, None, ACTION_NAME)
        return 0

# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "Hex-Rays show C graph (IDAPython)"
    wanted_hotkey = ""
    comment = "Sample plugin5 for Hex-Rays decompiler"
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            ida_kernwin.register_action(
                ida_kernwin.action_desc_t(
                    ACTION_NAME,
                    "Hex-Rays show C graph (IDAPython)",
                    display_graph_ah_t(),
                    ACTION_SHORTCUT))
            self.vds5_hooks = vds5_hooks_t()
            self.vds5_hooks.hook()
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        self.vds5_hooks.unhook()
    def run(self, arg):
        pass

def PLUGIN_ENTRY():
    return my_plugin_t()


repository: carsond135/malwareanalysis
file: tools/IDA_Pro_7.7/IDA_Pro_7.7/python/examples/hexrays/vds5.py
description: NKU恶意代码分析
wanted_name: Hex-Rays show C graph (IDAPython)
comment: Sample plugin5 for Hex-Rays decompiler
created_at: 2023-06-25 19:16:29+00:00
pushed_at: 2022-12-18 09:34:14+00:00
url:
https://github.com/carsond135/malwareanalysis/blob/45c8bf862a54239ff1e7c91313eccf9cf7d4a35b/tools/IDA_Pro_7.7/IDA_Pro_7.7/python/examples/hexrays/vds5.py


repository: readloud/PoC
file: VMware/patchgap-blog/rizzo/rizzo_string.py
description: Proof of concept (PoC) - misc PoC - Internet of (In)Security Things
wanted_name: Rizzo
comment: Function signature
created_at: 2022-10-14 16:13:04+00:00
pushed_at: 2022-10-16 16:05:19+00:00
url:
https://github.com/readloud/PoC/blob/a3f756f89c51b431a2b208cf97f3dd072d92165e/VMware/patchgap-blog/rizzo/rizzo_string.py

content:
##########################################################################
# An IDAPython plugin that generates "fuzzy" function signatures that can
# be shared and applied amongst different IDBs.
#
# There are multiple sets of signatures that are generated:
#
#   o "Formal" signatures, where functions must match exactly
#   o "Fuzzy" signatures, where functions must only resemble each other
#     in terms of data/call references.
#   o String-based signatures, where functions are identified based on
#     unique string references.
#   o Immediate-based signatures, where functions are identified based
#     on immediate value references.
#
# These signatures are applied based on accuracy, that is, formal
# signatures are applied first, then string and immediate based
# signatures, and finally fuzzy signatures.
#
# Further, functions are identified based on call references. Consider,
# for example, two functions, one named 'foo', the other named 'bar'.
# The 'foo' function is fairly unique and a reliable signature is easily
# generated for it, but the 'bar' function is more difficult to reliably
# identify. However, 'foo' calls 'bar', and thus once 'foo' is identified,
# 'bar' can also be identified by association.
#
# Craig Heffner
# @devttys0
##########################################################################
import idc
import idaapi
import idautils

import os
import time
import pickle       # http://natashenka.ca/pickle/
import collections

import ida_shims

class RizzoSignatures(object):
    """
    Simple wrapper class for storing signature info.
    """
    SHOW = []

    def __init__(self):
        self.fuzzy = {}
        self.formal = {}
        self.strings = {}
        self.functions = {}
        self.immediates = {}

        self.fuzzydups = set()
        self.formaldups = set()
        self.stringdups = set()
        self.immediatedups = set()

    def show(self):
        if not self.SHOW:
            return

        print "\n\nGENERATED FORMAL SIGNATURES FOR:"
        for (key, ea) in self.formal.iteritems():
            func = RizzoFunctionDescriptor(self.formal, self.functions, key)
            if func.name in self.SHOW:
                print func.name

        print "\n\nGENERATRED FUZZY SIGNATURES FOR:"
        for (key, ea) in self.fuzzy.iteritems():
            func = RizzoFunctionDescriptor(self.fuzzy, self.functions, key)
            if func.name in self.SHOW:
                print func.name


class RizzoStringDescriptor(object):
    """
    Wrapper class for easily accessing necessary string information.
    """

    def __init__(self, string):
        self.ea = string.ea
        self.value = str(string)
        self.xrefs = [x.frm for x in idautils.XrefsTo(self.ea)]


class RizzoBlockDescriptor(object):
    """
    Code block info is stored in tuples, which minimize pickle storage space.
    This class provides more Pythonic (and sane) access to values of interest
    for a given block.
    """

    def __init__(self, block):
        self.formal = block[0]
        self.fuzzy = block[1]
        self.immediates = block[2]
        self.functions = block[3]

    def match(self, nblock, fuzzy=False):
        # TODO: Fuzzy matching at the block level gets close, but produces a
        #  higher number of false positives; for example, it confuses
        #  hmac_md5 with hmac_sha1.
        return (self.formal == nblock.formal and
                len(self.immediates) == len(nblock.immediates) and
                len(self.functions) == len(nblock.functions))


class RizzoFunctionDescriptor(object):
    """
    Function signature info is stored in dicts and tuples, which minimize pickle
    storage space. This class provides more Pythonic (and sane) access to
    values of interest for a given function.
    """

    def __init__(self, signatures, functions, key):
        self.ea = signatures[key]
        self.name = functions[self.ea][0]
        self.blocks = functions[self.ea][1]


class Rizzo(object):
    """
    Workhorse class which performs the primary logic and functionality.
    """

    DEFAULT_SIGNATURE_FILE = "rizzo.sig"

    def __init__(self, sigfile=None):
        if sigfile:
            self.sigfile = sigfile
        else:
            self.sigfile = self.DEFAULT_SIGNATURE_FILE

        # Useful for identifying string xrefs from individual instructions
        self.strings = {}
        for string in idautils.Strings():
            self.strings[string.ea] = RizzoStringDescriptor(string)

        start = time.time()
        self.signatures = self.generate()
        end = time.time()

        print "Generated %d formal signatures and %d fuzzy signatures for %d " \
              "functions in %.2f seconds." % (len(self.signatures.formal),
                                              len(self.signatures.fuzzy),
                                              len(self.signatures.functions),
                                              (end-start))

    def save(self):
        print ("Saving signatures to %s..." % self.sigfile),
        fp = open(self.sigfile, "wb")
        pickle.dump(self.signatures, fp)
        fp.close()
        print "done."

    def load(self):
        print ("Loading signatures from %s..." % self.sigfile),
        #fp = open(self.sigfile, "rb")
        fp=open(self.sigfile,"rb").readlines()
        fp=map(lambda x:x.replace(b"\r\n","\n"),fp)
        with open(self.sigfile,"wb") as j:
            for i in fp:
                j.write(i)
                
        sigs = pickle.load(open(self.sigfile,"rb"))
        #fp.close()
        print ("done.")
        return sigs
		
    def sighash(self, value):
        return hash(str(value)) & 0xFFFFFFFF

    def block(self, block):
        """
        Returns a tuple:
        ([formal, block, signatures], [fuzzy, block, signatures],
        set([unique, immediate, values]), [called, function, names])
        """
        formal = []
        fuzzy = []
        functions = []
        immediates = []

        ea = ida_shims.start_ea(block)
        while ea < ida_shims.end_ea(block):
            insn = ida_shims.decode_insn(ea)

            # Get a list of all data/code refs from the current instruction
            drefs = [x for x in idautils.DataRefsFrom(ea)]
            crefs = [x for x in idautils.CodeRefsFrom(ea, False)]

            # Add all instruction mnemonics to the formal block hash
            formal.append(ida_shims.print_insn_mnem(ea))

            # If this is a call instruction, be sure to note the name of the
            # function being called. This is used to apply call-based
            # signatures to functions.
            #
            # For fuzzy signatures, we can't use the actual name or EA of the
            # function, but rather just want to note that a function call was
            # made.
            #
            # Formal signatures already have the call instruction mnemonic,
            # which is more specific than just saying that a call was made.
            if idaapi.is_call_insn(ea):
                for cref in crefs:
                    func_name = ida_shims.get_name(cref)
                    if func_name:
                        functions.append(func_name)
                        fuzzy.append("funcref")
            # If there are data references from the instruction, check to see
            # if any of them are strings. These are looked up in the
            # pre-generated strings dictionary.
            #
            # String values are easily identifiable, and are used as part of
            # both the fuzzy and the formal signatures.
            #
            # It is more difficult to determine if non-string values are
            # constants or not; for both fuzzy and formal signatures, just use
            # "data" to indicate that some data was referenced.
            elif drefs:
                for dref in drefs:
                    if self.strings.has_key(dref):
                        formal.append(self.strings[dref].value)
                        fuzzy.append(self.strings[dref].value)
                    else:
                        formal.append("dataref")
                        fuzzy.append("dataref")
            # If there are no data or code references from the instruction, use
            # every operand as part of the formal signature.
            #
            # Fuzzy signatures are only concerned with interesting immediate
            # values, that is, values that are greater than 65,535, are not
            # memory addresses, and are not displayed as negative values.
            elif not drefs and not crefs:
                ops = ida_shims.get_operands(insn)
                for n in range(0, len(ops)):
                    opnd_text = ida_shims.print_operand(ea, n)
                    formal.append(opnd_text)
                    if ops[n].type == idaapi.o_imm and \
                            not opnd_text.startswith('-'):
                        if ops[n].value >= 0xFFFF:
                            if ida_shims.get_full_flags(ops[n].value) == 0:
                                fuzzy.append(str(ops[n].value))
                                immediates.append(ops[n].value)

            ea = ida_shims.next_head(ea)

        return (self.sighash(''.join(formal)),
                self.sighash(''.join(fuzzy)),
                immediates, functions)

    def function(self, func):
        """
        Returns a list of blocks.
        """
        blocks = []

        for block in idaapi.FlowChart(func):
            blocks.append(self.block(block))

        return blocks

    def generate(self):
        signatures = RizzoSignatures()

        # Generate unique string-based function signatures
        for (ea, string) in self.strings.iteritems():
            # Only generate signatures on reasonably long strings with one xref
            if len(string.value) >= 8 and len(string.xrefs) == 1:
                func = idaapi.get_func(string.xrefs[0])
                if func:
                    str_hash = self.sighash(string.value)

                    # Check for and remove string duplicate signatures (the same
                    # string can appear more than once in an IDB).
                    # If no duplicates, add this to the string signature dict.
                    if str_hash in signatures.strings:
                        del signatures.strings[str_hash]
                        signatures.stringdups.add(str_hash)
                    elif str_hash not in signatures.stringdups:
                        signatures.strings[str_hash] = ida_shims.start_ea(func)

        # Generate formal, fuzzy, and immediate-based function signatures
        for ea in idautils.Functions():
            func = idaapi.get_func(ea)
            if func:
                start_ea = ida_shims.start_ea(func)
                signatures.functions[start_ea] = (ida_shims.get_name(start_ea), [])

        # These need not be maintained across function calls,
        # and only add to the size of the saved signature file.
        signatures.fuzzydups = set()
        signatures.formaldups = set()
        signatures.stringdups = set()
        signatures.immediatedups = set()

        # DEBUG
        signatures.show()

        return signatures

    def match(self, extsigs):
        fuzzy = {}
        formal = {}
        strings = {}
        immediates = {}

        # Match string based function signatures
        start = time.time()
        for (extsig, ext_func_ea) in extsigs.strings.iteritems():
            if extsig in self.signatures.strings:
                curr_fun = RizzoFunctionDescriptor(
                    self.signatures.strings, self.signatures.functions, extsig)
                new_fun = RizzoFunctionDescriptor(
                    extsigs.strings, extsigs.functions, extsig)
                strings[curr_fun] = new_fun
        end = time.time()
        print "Found %d string matches in %.2f seconds." % (len(strings),
                                                            (end-start))

        # Return signature matches in the order we want them applied
        # The second tuple of each match is set to True if it is a fuzzy match,
        # e.g.:   ((match, fuzzy), (match, fuzzy), ...)
        return ((formal, False),
                (strings, False),
                (immediates, False),
                (fuzzy, True))

    def rename(self, ea, name):
        # Don't rely on the name in curfunc, it could have already been renamed
        curname = ida_shims.get_name(ea)
        # Don't rename if the name is a special identifier, or if the ea has
        # already been named
        if curname.startswith('sub_') and \
                name.split('_')[0] not in \
                ['sub', 'loc', 'unk', 'dword', 'word', 'byte']:
            # Don't rename if the name already exists in the IDB
            if ida_shims.get_name_ea_simple(name) == idc.BADADDR:
                if ida_shims.set_name(ea, name):
                    ida_shims.set_func_flags(
                        ea, (idc.get_func_flags(ea) | idc.FUNC_LIB))
                    return 1
        return 0

    def apply(self, extsigs):
        count = 0

        start = time.time()

        # This applies formal matches first, then fuzzy matches
        for (match, fuzzy) in self.match(extsigs):
            # Keeps track of all function names that we've identified candidate
            # functions for
            rename = {}

            for (curfunc, newfunc) in match.iteritems():
                if newfunc not in rename:
                    rename[newfunc.name] = []

                # Attempt to rename this function
                rename[newfunc.name].append(curfunc.ea)

                bm = {}
                duplicates = set()

                # Search for unique matching code blocks inside this function
                for nblock in newfunc.blocks:
                    nblock = RizzoBlockDescriptor(nblock)
                    for cblock in curfunc.blocks:
                        cblock = RizzoBlockDescriptor(cblock)

                        if cblock.match(nblock, fuzzy):
                            if bm.has_key(cblock):
                                del bm[cblock]
                                duplicates.add(cblock)
                            elif cblock not in duplicates:
                                bm[cblock] = nblock

                # Rename known function calls from each unique code block
                for (cblock, nblock) in bm.iteritems():
                    for n in range(0, len(cblock.functions)):
                        ea = idc.get_name_ea_simple(cblock.functions[n])
                        if ea != idc.BADADDR:
                            if rename.has_key(nblock.functions[n]):
                                rename[nblock.functions[n]].append(ea)
                            else:
                                rename[nblock.functions[n]] = [ea]

                # Rename the identified functions
                for (name, candidates) in rename.iteritems():
                    if candidates:
                        winner = \
                            collections.Counter(candidates).most_common(1)[0][0]
                        count += self.rename(winner, name)

        end = time.time()
        print "Renamed %d functions in %.2f seconds." % (count, (end-start))


def RizzoBuild(sigfile=None):
    print "Building Rizzo signatures, this may take a few minutes..."
    start = time.time()
    r = Rizzo(sigfile)
    r.save()
    end = time.time()
    print "Built signatures in %.2f seconds" % (end-start)


def RizzoApply(sigfile=None):
    print "Applying Rizzo signatures, this may take a few minutes..."
    start = time.time()
    r = Rizzo(sigfile)
    s = r.load()
    r.apply(s)
    end = time.time()
    print "Signatures applied in %.2f seconds" % (end-start)


def rizzo_produce(arg=None):
    fname = ida_shims.ask_file(1, "*.riz", "Save signature file as")
    if fname:
        if '.' not in fname:
            fname += ".riz"
        RizzoBuild(fname)


def rizzo_load(arg=None):
    fname = ida_shims.ask_file(0, "*.riz", "Load signature file")
    if fname:
        RizzoApply(fname)

try:
    class ProduceRizzoAction(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)

        def activate(self, ctx):
            rizzo_produce()
            return 1

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


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

        def activate(self, ctx):
            rizzo_load()
            return 1

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


class RizzoPlugin(idaapi.plugin_t):
    flags = 0
    comment = "Function signature"
    help = ""
    wanted_name = "Rizzo"
    wanted_hotkey = ""
    produce_action_name = 'producerizzo:action'
    load_action_name = 'loadrizzo:action'
    menu_name = "Rizzo signature file..."
    produce_tooltip = "Produce rizzo signature file."
    load_tooltip = "Load rizzo signature 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,
                                                ProduceRizzoAction(),
                                                self.wanted_hotkey,
                                                self.produce_tooltip,
                                                199)

            load_desc = idaapi.action_desc_t(self.load_action_name,
                                             self.menu_name,
                                             LoadRizzoAction(),
                                             self.wanted_hotkey,
                                             self.load_tooltip,
                                             199)

            idaapi.register_action(produce_desc)
            idaapi.register_action(load_desc)

            idaapi.attach_action_to_menu('File/Produce file/',
                self.produce_action_name,
                idaapi.SETMENU_APP)
            idaapi.attach_action_to_menu('File/Load file/',
                self.load_action_name,
                idaapi.SETMENU_APP)
        else:
            self.menu_context.append(
                idaapi.add_menu_item(
                    os.path.join(self.menu_tab, 'Load file/'),
                    "Rizzo signature file...", "", 0, rizzo_load, (None,)))

            self.menu_context.append(
                idaapi.add_menu_item(
                    os.path.join(self.menu_tab, 'Produce file/'),
                    "Rizzo signature file...", "", 0, rizzo_produce, (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 rizzo_script(self):
        idaapi.IDAPython_ExecScript(self.script, globals())


def PLUGIN_ENTRY():
    return RizzoPlugin()

rizzo_plugin = PLUGIN_ENTRY()
rizzo_plugin.init()


repository: 0xjacklove/XMicro
file: python/examples/core/install_user_defined_prefix.py
description: Ida microcode dislodge ollvm
wanted_name: user defined prefix
comment: This is a user defined prefix sample plugin
created_at: 2020-10-09 10:25:17+00:00
pushed_at: 2020-10-09 10:34:58+00:00
url:
https://github.com/0xjacklove/XMicro/blob/bcabc18d72c0b08942d99bdae4e247e4ca31283a/python/examples/core/install_user_defined_prefix.py

content:
from __future__ import print_function
import idaapi

PREFIX = idaapi.SCOLOR_INV + ' ' + idaapi.SCOLOR_INV

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


    def user_prefix(self, ea, lnnum, indent, line, bufsize):
        #print("ea=%x lnnum=%d indent=%d line=%s bufsize=%d" % (ea, lnnum, indent, line, bufsize))

        if (ea % 2 == 0) and indent == -1:
            return PREFIX
        else:
            return ""


    def init(self):
        self.prefix_installed = idaapi.set_user_defined_prefix(8, self.user_prefix)
        if self.prefix_installed:
            print("prefix installed")

        return idaapi.PLUGIN_KEEP


    def run(self, arg):
        pass


    def term(self):
        if self.prefix_installed:
            idaapi.set_user_defined_prefix(0, None)
            print("prefix uninstalled!")


def PLUGIN_ENTRY():
    return prefix_plugin_t()


repository: Ph0en1x-XMU/Ph0en1x-Team
file: Reverse/ida/ida-plugin/findcrypt/findcrypt3.py
wanted_name: Findcrypt
comment: Findcrypt plugin for IDA Pro (using yara framework)
created_at: 2022-06-02 11:06:16+00:00
pushed_at: 2022-06-02 11:11:40+00:00
url:
https://github.com/Ph0en1x-XMU/Ph0en1x-Team/blob/a8d80aaea8227e418f5826bf05ef69a1bc8d84f1/Reverse/ida/ida-plugin/findcrypt/findcrypt3.py

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

import idaapi
import idautils
import ida_bytes
import ida_diskio
import idc
import operator
import yara
import os
import glob

VERSION = "0.2"
YARARULES_CFGFILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "findcrypt3.rules")

try:
    class Kp_Menu_Context(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)

        @classmethod
        def get_name(self):
            return self.__name__

        @classmethod
        def get_label(self):
            return self.label

        @classmethod
        def register(self, plugin, label):
            self.plugin = plugin
            self.label = label
            instance = self()
            return idaapi.register_action(idaapi.action_desc_t(
                self.get_name(),  # Name. Acts as an ID. Must be unique.
                instance.get_label(),  # Label. That's what users see.
                instance  # Handler. Called when activated, and for updating
            ))

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

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

        @classmethod
        def update(self, ctx):
            if ctx.form_type == idaapi.BWN_DISASM:
                return idaapi.AST_ENABLE_FOR_WIDGET
            return idaapi.AST_DISABLE_FOR_WIDGET

    class Searcher(Kp_Menu_Context):
        def activate(self, ctx):
            self.plugin.search()
            return 1

except:
    pass


p_initialized = False


class YaraSearchResultChooser(idaapi.Choose):
    def __init__(self, title, items, flags=0, width=None, height=None, embedded=False, modal=False):
        idaapi.Choose.__init__(
            self,
            title,
            [
                ["Address", idaapi.Choose.CHCOL_HEX|10],
                ["Rules file", idaapi.Choose.CHCOL_PLAIN|12],
                ["Name", idaapi.Choose.CHCOL_PLAIN|25],
                ["String", idaapi.Choose.CHCOL_PLAIN|25],
                ["Value", idaapi.Choose.CHCOL_PLAIN|40],
            ],
            flags=flags,
            width=width,
            height=height,
            embedded=embedded)
        self.items = items
        self.selcount = 0
        self.n = len(items)

    def OnClose(self):
        return

    def OnSelectLine(self, n):
        self.selcount += 1
        idc.jumpto(self.items[n][0])

    def OnGetLine(self, n):
        res = self.items[n]
        res = [idc.atoa(res[0]), res[1], res[2], res[3], res[4]]
        return res

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

    def show(self):
        return self.Show() >= 0

#--------------------------------------------------------------------------
# Plugin
#--------------------------------------------------------------------------
class Findcrypt_Plugin_t(idaapi.plugin_t):
    comment = "Findcrypt plugin for IDA Pro (using yara framework)"
    help = "todo"
    wanted_name = "Findcrypt"
    wanted_hotkey = "Ctrl-Alt-F"
    flags = idaapi.PLUGIN_KEEP

    def init(self):
        global p_initialized

        # register popup menu handlers
        try:
            Searcher.register(self, "Findcrypt")
        except:
            pass

        if p_initialized is False:
            p_initialized = True
            self.user_directory = self.get_user_directory()
            idaapi.register_action(idaapi.action_desc_t(
                "Findcrypt",
                "Find crypto constants",
                Searcher(),
                None,
                None,
                0))
            idaapi.attach_action_to_menu("Search", "Findcrypt", idaapi.SETMENU_APP)
            print("=" * 80)
            print("Findcrypt v{0} by David BERARD, 2017".format(VERSION))
            print("Findcrypt search shortcut key is Ctrl-Alt-F")
            print("Global rules in %s" % YARARULES_CFGFILE)
            print("User-defined rules in %s/*.rules" % self.user_directory)
            print("=" * 80)

        return idaapi.PLUGIN_KEEP

    def term(self):
        pass


    def toVirtualAddress(self, offset, segments):
        va_offset = 0
        for seg in segments:
            if seg[1] <= offset < seg[2]:
                va_offset = seg[0] + (offset - seg[1])
        return va_offset


    def get_user_directory(self):
        user_dir = ida_diskio.get_user_idadir()
        plug_dir = os.path.join(user_dir, "plugins")
        res_dir = os.path.join(plug_dir, "findcrypt-yara")
        if not os.path.exists(res_dir):
            os.makedirs(res_dir, 0o755)
        return res_dir


    def get_rules_files(self):
        rules_filepaths = {"global":YARARULES_CFGFILE}
        for fpath in glob.glob(os.path.join(self.user_directory, "*.rules")):
            name = os.path.basename(fpath)
            rules_filepaths.update({name:fpath})
        return rules_filepaths


    def search(self):
        memory, offsets = self._get_memory()
        rules = yara.compile(filepaths=self.get_rules_files())
        values = self.yarasearch(memory, offsets, rules)
        c = YaraSearchResultChooser("Findcrypt results", values)
        r = c.show()

    def yarasearch(self, memory, offsets, rules):
        print(">>> start yara search")
        values = list()
        matches = rules.match(data=memory)
        for match in matches:
            for string in match.strings:
                name = match.rule
                if name.endswith("_API"):
                    try:
                        name = name + "_" + idc.GetString(self.toVirtualAddress(string[0], offsets))
                    except:
                        pass
                value = [
                    self.toVirtualAddress(string[0], offsets),
                    match.namespace,
                    name + "_" + hex(self.toVirtualAddress(string[0], offsets)).lstrip("0x").rstrip("L").upper(),
                    string[1],
                    repr(string[2]),
                ]
                idaapi.set_name(value[0], name
                             + "_"
                             + hex(self.toVirtualAddress(string[0], offsets)).lstrip("0x").rstrip("L").upper()
                             , 0)
                values.append(value)
        print("<<< end yara search")
        return values

    def _get_memory(self):
        result = bytearray()
        segment_starts = [ea for ea in idautils.Segments()]
        offsets = []
        start_len = 0
        for start in segment_starts:
            end = idc.get_segm_attr(start, idc.SEGATTR_END)
            result += ida_bytes.get_bytes(start, end - start)
            offsets.append((start, start_len, len(result)))
            start_len = len(result)
        return bytes(result), offsets

    def run(self, arg):
        self.search()


# register IDA plugin
def PLUGIN_ENTRY():
    return Findcrypt_Plugin_t()


repository: grumpycoders/ghidra
file: GhidraBuild/IDAPro/Python/6xx/plugins/xmlexp.py
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2020-05-05 20:28:01+00:00
pushed_at: 2020-05-07 19:43:38+00:00
url:
https://github.com/grumpycoders/ghidra/blob/bcb825fb029232175625bc85653ec0e810b1252e/GhidraBuild/IDAPro/Python/6xx/plugins/xmlexp.py

content:
#---------------------------------------------------------------------
# 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'


repository: r3dlobst3r-org/Ghidra_Linux
file: Extensions/IDAPro/Python/6xx/plugins/xmlexp.py
description: Ghidra from source, for Linux
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2019-12-06 23:01:45+00:00
pushed_at: 2019-12-09 20:07:17+00:00
url:
https://github.com/r3dlobst3r-org/Ghidra_Linux/blob/3ca7ebdf807f45820f8e42776d5240abf8be8fe7/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py

content:
#---------------------------------------------------------------------
# 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'


repository: Maliha1994/DUDE
file: DUDE/static/ghidra_9.1_PUBLIC/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2023-05-30 04:47:46+00:00
pushed_at: 2023-05-30 04:58:17+00:00
url:
https://github.com/Maliha1994/DUDE/blob/b26fc9e9cea954713ef00e65e048568e003c201f/DUDE/static/ghidra_9.1_PUBLIC/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py


repository: syst3m2/pen_testing
file: ghidra_9.1.2_PUBLIC_20200212/ghidra_9.1.2_PUBLIC/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py
description: Repository for Capture the Flag events
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2023-10-08 13:04:40+00:00
pushed_at: 2023-10-08 13:11:01+00:00
url:
https://github.com/syst3m2/pen_testing/blob/84770516757335c826f5b60b015f875128d45cfd/ghidra_9.1.2_PUBLIC_20200212/ghidra_9.1.2_PUBLIC/Extensions/IDAPro/Python/6xx/plugins/xmlexp.py


repository: yashchaurasia667/Binary-Exploitation
file: Tools/ghidra_11.3.1_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.py
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2023-11-11 13:49:18+00:00
pushed_at: 2025-04-26 07:50:25+00:00
url:
https://github.com/yashchaurasia667/Binary-Exploitation/blob/bf9e64a8a9d630289d3fae68cd2f7cd6f88648eb/Tools/ghidra_11.3.1_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.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.
This file must be placed in the IDA plugins directory.
The file idaxml.py must be placed in the IDA python directory.
"""

from __future__ import print_function
import ida_auto
import ida_idaapi
import ida_kernwin
import idaxml
import idc
import sys

if sys.version_info.major >= 3:
    from idaxml import _exc_info
    sys.exc_value = lambda: _exc_info()[1]
    sys.exc_type = lambda: _exc_info()[0]

class XmlExporterPlugin(ida_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 idaxml.is_ida_version_supported():
            return ida_idaapi.PLUGIN_OK 
        else:
            return ida_idaapi.PLUGIN_SKIP


    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.
        """
        st = idc.set_ida_state(idc.IDA_STATUS_WORK)
        xml = idaxml.XmlExporter(arg)
        try:
            try:
                xml.export_xml()
            except idaxml.Cancelled:
                ida_kernwin.hide_wait_box()
                msg = "XML Export cancelled!"
                print("\n" + msg)
                idc.warning(msg)
            except:
                ida_kernwin.hide_wait_box()
                msg = "***** Exception occurred: XML Exporter failed! *****"
                print("\n" + msg + "\n", sys.exc_type, sys.exc_value)
                idc.warning(msg)
        finally:
            xml.cleanup()
            ida_auto.set_ida_state(st)


    def term(self):
        pass


def PLUGIN_ENTRY():
    return XmlExporterPlugin()


repository: MassinissaDjellouli/CTFStuff
file: tools/ghidra_11.2.1_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.py
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2024-01-16 04:53:55+00:00
pushed_at: 2025-05-13 04:26:11+00:00
url:
https://github.com/MassinissaDjellouli/CTFStuff/blob/8be397cdd7b2b33bde74f33314fdcaf7244d36cb/tools/ghidra_11.2.1_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.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.
This file must be placed in the IDA plugins directory.
The file idaxml.py must be placed in the IDA python directory.
"""

from __future__ import print_function
import ida_auto
import ida_idaapi
import ida_kernwin
import idaxml
import idc
import sys

if sys.version_info.major >= 3:
    from idaxml import _exc_info
    sys.exc_value = lambda: _exc_info()[1]
    sys.exc_type = lambda: _exc_info()[0]

class XmlExporterPlugin(ida_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 idaxml.is_ida_version_supported():
            return ida_idaapi.PLUGIN_OK 
        else:
            return ida_idaapi.PLUGIN_SKIP


    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.
        """
        st = idc.set_ida_state(idc.IDA_STATUS_WORK)
        xml = idaxml.XmlExporter(arg)
        try:
            try:
                xml.export_xml()
            except idaxml.Cancelled:
                ida_kernwin.hide_wait_box()
                msg = "XML Export cancelled!"
                print("\n" + msg)
                idc.warning(msg)
            except:
                ida_kernwin.hide_wait_box()
                msg = "***** Exception occurred: XML Exporter failed! *****"
                print("\n" + msg + "\n", sys.exc_type, sys.exc_value)
                idc.warning(msg)
        finally:
            xml.cleanup()
            ida_auto.set_ida_state(st)


    def term(self):
        pass


def PLUGIN_ENTRY():
    return XmlExporterPlugin()


repository: IoMT-Lab/re_toolbox
file: tools/ghidra/ghidra_11.2.1_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.py
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2025-02-20 20:32:04+00:00
pushed_at: 2025-02-28 15:22:13+00:00
url:
https://github.com/IoMT-Lab/re_toolbox/blob/b840e3b98e3e3d32d20c3dcc87669734456e9866/tools/ghidra/ghidra_11.2.1_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.py


repository: k3rn3Lp4n1cK/ctf-live-build-config
description: Custom Kali Live Builder for CTF's & OSCP
wanted_name: XML Exporter
comment: Export database as XML file
created_at: 2023-01-28 21:06:18+00:00
pushed_at: 2023-05-18 00:05:00+00:00
file:
kali-config/common/includes.chroot/opt/ghidra_10.2.2_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.py

url:
https://github.com/k3rn3Lp4n1cK/ctf-live-build-config/blob/6ca05257d5cdecfa6dc05ab1a8258506864f6edd/kali-config/common/includes.chroot/opt/ghidra_10.2.2_PUBLIC/Extensions/IDAPro/Python/7xx/plugins/xml_exporter.py


repository: techbliss/Python_editor
file: 7.5/Add to plugins folder/Code editor/template/Plugin_temp.py
description: Better CodeEditor for Ida Pro.
wanted_name: Python Editor
comment: Run me
created_at: 2015-07-04 21:14:02+00:00
pushed_at: 2020-12-25 13:54:19+00:00
url:
https://github.com/techbliss/Python_editor/blob/4c2b67bd0b1a2b9b9403f5ae31cf8f13c247c981/7.5/Add%20to%20plugins%20folder/Code%20editor/template/Plugin_temp.py

content:
# Created by: Storm Shadow http://www.techbliss.org
import os
import ida_idaapi, ida_kernwin
import idc
from idc import *
from idaapi import *
import sys
sys.path.insert(0 , idaapi.idadir("plugins\\Code editor\\icons"))
import ico
from ico import *


PLUGIN_VERSION = "1.4"
IDAVERISONS    = "IDA PRO 7.0+"
AUTHORS        = "Storm Shadow"
DATE           = "2017"
TWITTER        = "Twitter @zadow28"

def banner():
    banner_options = (PLUGIN_VERSION, AUTHORS, DATE, TWITTER, IDAVERISONS)
    banner_titles = "Python Editor v%s - (c) %s - %s - %s - %s" % banner_options

# print plugin banner
    print("---[" + banner_titles + "]---\n")

banner()

# 1) Create the handler class
class MyEditorHandler(idaapi.action_handler_t):
    def __init__(self):
        idaapi.action_handler_t.__init__(self)

    # Run editor when invoked.
    def activate(self, ctx):
        g = globals()
        idahome = idaapi.idadir("plugins\\Code editor")
        IDAPython_ExecScript(idahome + "\\pyeditor.py", g)

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

class ripeye(idaapi.plugin_t):
    flags = idaapi.PLUGIN_FIX
    comment = "Run me"
    help = "Python Editor"
    wanted_name = "Python Editor"
    wanted_hotkey = "" #the tooltip horkey goes away when setting it here DONT DO it! and only is shown in File/Plugins menu


    def editor_menuaction(self):
        action_desc = idaapi.action_desc_t(
            'my:editoraction',  # The action name. This acts like an ID and must be unique
            'Python Editor!',  # The action text.
            MyEditorHandler(),  # The action handler.
            'Ctrl+H',  # Optional: the action shortcut DO IT  HERE!
            'Script editor',  # Optional: the action tooltip (available in menus/toolbar)
            idaapi.load_custom_icon(":/ico/python.png")  # hackish load action icon , if no custom icon use number from 1-150 from internal ida
        )

        # 3) Register the action
        idaapi.register_action(action_desc)

        idaapi.attach_action_to_menu(
            'File/Editor...',  # The relative path of where to add the action
            'my:editoraction',  # The action ID (see above)
            idaapi.SETMENU_APP)  # We want to append the action after the 'Manual instruction...

        form = idaapi.get_current_tform()
        idaapi.attach_action_to_popup(form, None, "my:editoraction", None)

    def init(self):
        """
        This is called by IDA when it is loading the plugin.
        """
        #self._icon_id_file = idaapi.BADADDR
        # attempt plugin initialization
        try:
            self._install_plugin()

        # failed to initialize or integrate the plugin, log and skip loading
        except Exception as e:
            form = idaapi.get_current_tform()
            pass

        return PLUGIN_KEEP


    def _install_plugin(self):
        """
        Initialize & integrate the plugin into IDA.
        """
        self.editor_menuaction()
        self._init()

    def term(self):
        pass

    def run(self, arg = 0):
        #we need the calls again if we wanna load it via File/Plugins/editor
        idaapi.msg("Python Editor Loaded to menu \n use Alt+E hot key to quick load ")
        hackish = MyEditorHandler()
        hackish.activate(self)

def PLUGIN_ENTRY():
    return ripeye()


repository: AshishGiri1806/langflowhack
file: myenv/Lib/site-packages/qianfan/common/client/plugin.py
created_at: 2024-07-15 17:05:14+00:00
pushed_at: 2024-07-22 22:58:15+00:00
url:
https://github.com/AshishGiri1806/langflowhack/blob/3cda246d4e0df8d360c57891eeb80f694d6e6f48/myenv/Lib/site-packages/qianfan/common/client/plugin.py

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


import json
import os
from typing import Any, Dict, List, Optional, Tuple
from urllib.parse import unquote, urlparse

import typer
from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.document import Document
from prompt_toolkit.validation import ValidationError
from rich import print as rprint
from rich.console import Group, RenderableType
from rich.live import Live
from rich.markdown import Markdown
from rich.spinner import Spinner
from rich.text import Text

import qianfan
from qianfan import QfRole
from qianfan.common.client.utils import (
    BosPathValidator,
    InputEmptyValidator,
    credential_required,
    render_response_debug_info,
    replace_logger_handler,
)
from qianfan.resources.typing import QfMessages
from qianfan.utils.bos_uploader import BosHelper, parse_bos_path


class PluginInputValidator(InputEmptyValidator):
    """
    Validator for input in plugin
    """

    def validate(self, document: Document) -> None:
        """
        validate input:
        - input must not be empty
        - if input is /image, file path must be provided
        """
        super().validate(document)
        text = document.text.strip()
        if text.startswith(PluginClient.IMAGE_PROMPT):
            path = text[len(PluginClient.IMAGE_PROMPT) :].strip()
            if len(path) == 0:
                raise ValidationError(
                    message="Image file path must be provided (e.g. /image car.jpg)."
                )


class PluginClient(object):
    """
    Client object for the chat command
    """

    END_PROMPT = "/exit"
    RESET_PROMPT = "/reset"
    IMAGE_PROMPT = "/image"
    FILE_PROMPT = "/file"
    HELP_PROMPT = "/help"

    HELP_MESSAGES = {
        END_PROMPT: "End the conversation",
        RESET_PROMPT: "Reset the conversation",
        IMAGE_PROMPT: "Attach a local image to the conversation (e.g. /image car.jpg)",
        FILE_PROMPT: "Attach a local file to the conversation (e.g. /file xx.docx)",
        HELP_PROMPT: "Print help message",
    }

    def __init__(
        self,
        model: str,
        endpoint: Optional[str],
        multi_line: bool,
        debug: bool,
        plugins: List[str],
        bos_path: Optional[str],
        **kwargs: Any,
    ) -> None:
        """
        Init the chat client
        """
        if endpoint is None:
            self.client = qianfan.Plugin(model=model)
        else:
            self.client = qianfan.Plugin(endpoint=endpoint)
        self.msg_history = QfMessages()
        self.multi_line = multi_line
        self.console = replace_logger_handler()
        self.inference_args = kwargs
        self.bos_path = bos_path
        self.plugins = plugins
        self.debug = debug
        if self._is_qianfan_plugin():
            self.HELP_MESSAGES.pop(self.FILE_PROMPT)
        self.input_completer = WordCompleter(
            list(self.HELP_MESSAGES.keys()), sentence=True, meta_dict=self.HELP_MESSAGES
        )

    def print_hint_msg(self) -> None:
        """
        Print hint message when startup
        """
        if self.multi_line:
            rprint(
                "[bold]Hint[/bold]: [green bold]Press Esc before Enter[/] to submit"
                f" your message, or use '{self.HELP_PROMPT}' to acquire more commands."
            )
        else:
            rprint(
                "[bold]Hint[/bold]: Press enter to submit your message, or use"
                f" '{self.HELP_PROMPT}' to acquire more commands.."
            )
            rprint(
                "[bold]Hint[/bold]: If you want to submit multiple lines, use the"
                " '--multi-line' option."
            )
        if self._is_qianfan_plugin():
            rprint(f"[dim]Using qianfan plugin with {self.plugins}...[/]")
        else:
            rprint("[dim]Using ERNIE Bot plugin...[/]")

    def get_bos_path(self) -> str:
        """
        Get bos path. If bos_path is not provided, prompt user to input
        """
        if self.bos_path is None:
            rprint("Please input bos bucket path [dim](bos:/<bucket>/<path>)[/]: ")
            self.bos_path = prompt("bos:/", validator=BosPathValidator())
            self.bos_path = "bos:/" + self.bos_path
        if not self.bos_path.endswith("/"):
            self.bos_path = self.bos_path + "/"
        return self.bos_path

    def upload_file_to_bos(self, filepath: str) -> Tuple[str, str]:
        """
        Upload file to bos and get bos_url and http_url
        """
        bos_helper = BosHelper()
        bucket, bos_path = parse_bos_path(self.get_bos_path())

        bos_path = bos_path + os.path.basename(filepath)
        with self.console.status("Uploading file to bos..."):
            bos_helper.upload_file_to_bos(filepath, bos_path, bucket)
        url = bos_helper.get_bos_file_shared_url(bos_path, bucket)
        bos_url = f"bos:/{bucket}{bos_path}"
        return bos_url, url

    def print_help_message(self) -> None:
        """
        Print command introduction
        """
        for k, v in self.HELP_MESSAGES.items():
            rprint(f"[bold green]{k}[/]: {v}")

    def _is_qianfan_plugin(self) -> bool:
        """
        Check if the plugin is qianfan plugin
        """
        return self.client._endpoint is not None

    def chat_in_terminal(self) -> None:
        """
        Chat in terminal
        """

        self.print_hint_msg()
        # loop the conversation
        extra_field: Dict[str, Any] = {}
        while True:
            rprint("\n[yellow bold]Enter your message[/yellow bold]:")
            while True:
                message = prompt(
                    multiline=self.multi_line,
                    validator=PluginInputValidator(),
                    completer=self.input_completer,
                ).strip()
                if message.startswith(self.IMAGE_PROMPT):
                    path = message[len(self.IMAGE_PROMPT) :].strip()
                    if not path.startswith("http"):
                        bos_path, http_path = self.upload_file_to_bos(path)
                        filename = os.path.basename(path)
                        rprint(f"Image has been uploaded to: {bos_path}")
                        rprint(f"Image share url: {http_path}\n")
                        path = http_path
                    else:
                        filename = unquote(os.path.basename(urlparse(path).path))
                    if self._is_qianfan_plugin():
                        rprint(
                            "[yellow bold]Please continue to input your prompt[/yellow"
                            " bold]:"
                        )
                        extra_field["fileurl"] = path
                        continue
                    else:
                        message = f"<img>{filename}</img><url>{path}</url>"
                if not self._is_qianfan_plugin() and message.startswith(
                    self.FILE_PROMPT
                ):
                    path = message[len(self.FILE_PROMPT) :].strip()
                    if not path.startswith("http"):
                        bos_path, http_path = self.upload_file_to_bos(path)
                        filename = os.path.basename(path)
                        rprint(f"File has been uploaded to: {bos_path}")
                        rprint(f"File share url: {http_path}\n")
                        path = http_path
                    else:
                        filename = unquote(os.path.basename(urlparse(path).path))
                    message = f"<file>{filename}</file><url>{path}</url>"

                break

            rprint("\n[blue][bold]Model response[/bold][/blue]:")

            if message == self.END_PROMPT:
                rprint("Bye!")
                raise typer.Exit()
            elif message == self.RESET_PROMPT:
                self.msg_history = QfMessages()
                extra_field = {}
                rprint("Chat history has been cleared.")
                continue
            elif message == self.HELP_PROMPT:
                self.print_help_message()
                continue
            else:
                self.msg_history.append(message)

            with Live(
                Spinner("dots", text="Thinking...", style="status.spinner"),
                auto_refresh=True,
                refresh_per_second=24,
                console=self.console,
            ) as live:
                if self._is_qianfan_plugin():
                    response = self.client.do(
                        message,
                        plugins=self.plugins,
                        llm=self.inference_args,
                        stream=True,
                        history=self.msg_history._to_list()[:-1],
                        **self.inference_args,
                        **extra_field,
                    )
                else:
                    response = self.client.do(
                        self.msg_history,
                        plugins=self.plugins,
                        stream=True,
                        **self.inference_args,
                    )

                m = ""
                plugin_metainfo = {}
                plugin_render_content = None
                for r in response:
                    render_list: List[RenderableType] = []
                    if self._is_qianfan_plugin():
                        m += r["result"]
                        render_list.append(Markdown(m))
                    else:
                        event = r["_event"]
                        if event == "pluginMeta":
                            for meta in r["plugin_metas"]:
                                plugin_metainfo[meta["pluginId"]] = meta
                        elif event == "plugin":
                            plugin_render_list: List[RenderableType] = []
                            for plugin in r["plugin_info"]:
                                plugin_name = plugin["plugin_id"]
                                if plugin_name in plugin_metainfo:
                                    plugin_name = (
                                        f"{plugin_metainfo[plugin_name]['pluginNameForHuman']}"
                                        f"({plugin_metainfo[plugin_name]['pluginNameForModel']})"
                                    )

                                plugin_resp = plugin["plugin_resp"].strip().split("\n")
                                status = None
                                for resp in reversed(plugin_resp):
                                    if len(resp) == 0:
                                        continue
                                    plugin_response = json.loads(resp)
                                    if "actionName" not in plugin_response:
                                        continue
                                    status = plugin_response["actionContent"]
                                    break
                                if status is not None:
                                    if plugin["status"] == "1":
                                        plugin_info_obj = Text.from_markup(
                                            text=f"• {plugin_name}\t{status}",
                                            style="dim",
                                        )
                                    else:
                                        plugin_info_obj = Text(
                                            f"• {plugin_name}", style="dim"
                                        )
                                    plugin_render_list.append(plugin_info_obj)
                            plugin_render_list.append(Text(""))
                            plugin_render_content = Group(*plugin_render_list)

                        elif event == "chat":
                            m += r["result"]
                        if plugin_render_content is not None:
                            render_list.append(plugin_render_content)
                        render_list.append(Markdown(m))

                    if "is_end" not in r or not r["is_end"]:
                        render_list.append(
                            Spinner(
                                "dots", text="Generating...", style="status.spinner"
                            )
                        )
                    stat = r.statistic
                    render_list.append(
                        Text.from_markup(
                            "\n[dim]First token latentcy:"
                            f" {stat['first_token_latency']:.2f}s, Total latency:"
                            f" {stat['total_latency']:.2f}s.[/]"
                        )
                    )
                    if "is_end" in r and r["is_end"]:
                        if "usage" in r:
                            token_usage = r["usage"]
                            render_list.append(
                                Text.from_markup(
                                    f"[dim]Input token: {token_usage['prompt_tokens']},"
                                    " Output token:"
                                    f" {token_usage['completion_tokens']}, Total token:"
                                    f" {token_usage['total_tokens']}.[/]"
                                )
                            )
                        if self.debug:
                            render_list.append(render_response_debug_info(response=r))

                    live.update(Group(*render_list))

            self.msg_history.append(m, role=QfRole.Assistant)


MODEL_ARGUMENTS_PANEL = (
    "Model Arguments (Some arguments are not supported by every model)"
)


@credential_required
def plugin_entry(
    endpoint: Optional[str] = typer.Option(
        None,
        help="Endpoint of the plugin. ERNIE-Bot plugin will be used if not specified.",
    ),
    # tui: bool = typer.Option(False, help="Using Terminal UI"),
    multi_line: bool = typer.Option(
        False,
        "--multi-line",
        help="Multi-line mode which needs to press Esc before enter to submit message.",
    ),
    debug: bool = typer.Option(
        False,
        "--debug",
        help="Enable debug mode. The request infomation will be printed.",
    ),
    plugins: str = typer.Option(
        ...,
        help=(
            "Plugins enabled. Use comma(,) to split. (e.g."
            " uuid-zhishiku,uuid-chatocr,uuid-weatherforecast)"
        ),
    ),
    bos_path: Optional[str] = typer.Option(None, help="Bos path used for upload file."),
    temperature: Optional[float] = typer.Option(
        None,
        help=(
            "Controls the randomness of the generated text. A higher temperature makes"
            " the model more creative and produces more diverse, but potentially less"
            " coherent."
        ),
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    top_p: Optional[float] = typer.Option(
        None,
        help=(
            "Lower top_p value allows the model to focus on a narrowed set of likely"
            " next tokens, making the response more conherent but less random."
        ),
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    penalty_score: Optional[float] = typer.Option(
        None,
        help="Penalty scores can be applied to discourage repetition.",
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    system: Optional[str] = typer.Option(
        None,
        help="Persona setting for the model.",
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    stop: Optional[str] = typer.Option(
        None,
        help="Stop words. Use comma to split multiple stop words.",
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
) -> None:
    """
    Chat with the LLM with plugins in the terminal.
    """
    model = "EBPluginV2"

    extra_args = {}

    def add_if_not_none(key: str, value: Any) -> None:
        if value is not None:
            extra_args[key] = value

    add_if_not_none("temperature", temperature)
    add_if_not_none("top_p", top_p)
    add_if_not_none("penalty_score", penalty_score)
    add_if_not_none("system", system)

    if stop is not None:
        extra_args["stop"] = stop.split(",")

    client = PluginClient(
        model,
        endpoint,
        multi_line,
        debug=debug,
        plugins=plugins.split(","),
        bos_path=bos_path,
        **extra_args,
    )
    client.chat_in_terminal()


repository: KrishayNair/RAG_Chatbot
file: myenv/Lib/site-packages/qianfan/common/client/plugin.py
created_at: 2024-07-15 17:32:49+00:00
pushed_at: 2024-07-15 17:48:00+00:00
url:
https://github.com/KrishayNair/RAG_Chatbot/blob/91d7aae1303b29f105e2a7bdac848e2aa4010aa0/myenv/Lib/site-packages/qianfan/common/client/plugin.py

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


import json
import os
from typing import Any, Dict, List, Optional, Tuple
from urllib.parse import unquote, urlparse

import typer
from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.document import Document
from prompt_toolkit.validation import ValidationError
from rich import print as rprint
from rich.console import Group, RenderableType
from rich.live import Live
from rich.markdown import Markdown
from rich.spinner import Spinner
from rich.text import Text

import qianfan
from qianfan import QfRole
from qianfan.common.client.utils import (
    BosPathValidator,
    InputEmptyValidator,
    credential_required,
    render_response_debug_info,
    replace_logger_handler,
)
from qianfan.resources.typing import QfMessages
from qianfan.utils.bos_uploader import BosHelper, parse_bos_path


class PluginInputValidator(InputEmptyValidator):
    """
    Validator for input in plugin
    """

    def validate(self, document: Document) -> None:
        """
        validate input:
        - input must not be empty
        - if input is /image, file path must be provided
        """
        super().validate(document)
        text = document.text.strip()
        if text.startswith(PluginClient.IMAGE_PROMPT):
            path = text[len(PluginClient.IMAGE_PROMPT) :].strip()
            if len(path) == 0:
                raise ValidationError(
                    message="Image file path must be provided (e.g. /image car.jpg)."
                )


class PluginClient(object):
    """
    Client object for the chat command
    """

    END_PROMPT = "/exit"
    RESET_PROMPT = "/reset"
    IMAGE_PROMPT = "/image"
    FILE_PROMPT = "/file"
    HELP_PROMPT = "/help"

    HELP_MESSAGES = {
        END_PROMPT: "End the conversation",
        RESET_PROMPT: "Reset the conversation",
        IMAGE_PROMPT: "Attach a local image to the conversation (e.g. /image car.jpg)",
        FILE_PROMPT: "Attach a local file to the conversation (e.g. /file xx.docx)",
        HELP_PROMPT: "Print help message",
    }

    def __init__(
        self,
        model: str,
        endpoint: Optional[str],
        multi_line: bool,
        debug: bool,
        plugins: List[str],
        bos_path: Optional[str],
        **kwargs: Any,
    ) -> None:
        """
        Init the chat client
        """
        if endpoint is None:
            self.client = qianfan.Plugin(model=model)
        else:
            self.client = qianfan.Plugin(endpoint=endpoint)
        self.msg_history = QfMessages()
        self.multi_line = multi_line
        self.console = replace_logger_handler()
        self.inference_args = kwargs
        self.bos_path = bos_path
        self.plugins = plugins
        self.debug = debug
        if self._is_qianfan_plugin():
            self.HELP_MESSAGES.pop(self.FILE_PROMPT)
        self.input_completer = WordCompleter(
            list(self.HELP_MESSAGES.keys()), sentence=True, meta_dict=self.HELP_MESSAGES
        )

    def print_hint_msg(self) -> None:
        """
        Print hint message when startup
        """
        if self.multi_line:
            rprint(
                "[bold]Hint[/bold]: [green bold]Press Esc before Enter[/] to submit"
                f" your message, or use '{self.HELP_PROMPT}' to acquire more commands."
            )
        else:
            rprint(
                "[bold]Hint[/bold]: Press enter to submit your message, or use"
                f" '{self.HELP_PROMPT}' to acquire more commands.."
            )
            rprint(
                "[bold]Hint[/bold]: If you want to submit multiple lines, use the"
                " '--multi-line' option."
            )
        if self._is_qianfan_plugin():
            rprint(f"[dim]Using qianfan plugin with {self.plugins}...[/]")
        else:
            rprint("[dim]Using ERNIE Bot plugin...[/]")

    def get_bos_path(self) -> str:
        """
        Get bos path. If bos_path is not provided, prompt user to input
        """
        if self.bos_path is None:
            rprint("Please input bos bucket path [dim](bos:/<bucket>/<path>)[/]: ")
            self.bos_path = prompt("bos:/", validator=BosPathValidator())
            self.bos_path = "bos:/" + self.bos_path
        if not self.bos_path.endswith("/"):
            self.bos_path = self.bos_path + "/"
        return self.bos_path

    def upload_file_to_bos(self, filepath: str) -> Tuple[str, str]:
        """
        Upload file to bos and get bos_url and http_url
        """
        bos_helper = BosHelper()
        bucket, bos_path = parse_bos_path(self.get_bos_path())

        bos_path = bos_path + os.path.basename(filepath)
        with self.console.status("Uploading file to bos..."):
            bos_helper.upload_file_to_bos(filepath, bos_path, bucket)
        url = bos_helper.get_bos_file_shared_url(bos_path, bucket)
        bos_url = f"bos:/{bucket}{bos_path}"
        return bos_url, url

    def print_help_message(self) -> None:
        """
        Print command introduction
        """
        for k, v in self.HELP_MESSAGES.items():
            rprint(f"[bold green]{k}[/]: {v}")

    def _is_qianfan_plugin(self) -> bool:
        """
        Check if the plugin is qianfan plugin
        """
        return self.client._endpoint is not None

    def chat_in_terminal(self) -> None:
        """
        Chat in terminal
        """

        self.print_hint_msg()
        # loop the conversation
        extra_field: Dict[str, Any] = {}
        while True:
            rprint("\n[yellow bold]Enter your message[/yellow bold]:")
            while True:
                message = prompt(
                    multiline=self.multi_line,
                    validator=PluginInputValidator(),
                    completer=self.input_completer,
                ).strip()
                if message.startswith(self.IMAGE_PROMPT):
                    path = message[len(self.IMAGE_PROMPT) :].strip()
                    if not path.startswith("http"):
                        bos_path, http_path = self.upload_file_to_bos(path)
                        filename = os.path.basename(path)
                        rprint(f"Image has been uploaded to: {bos_path}")
                        rprint(f"Image share url: {http_path}\n")
                        path = http_path
                    else:
                        filename = unquote(os.path.basename(urlparse(path).path))
                    if self._is_qianfan_plugin():
                        rprint(
                            "[yellow bold]Please continue to input your prompt[/yellow"
                            " bold]:"
                        )
                        extra_field["fileurl"] = path
                        continue
                    else:
                        message = f"<img>{filename}</img><url>{path}</url>"
                if not self._is_qianfan_plugin() and message.startswith(
                    self.FILE_PROMPT
                ):
                    path = message[len(self.FILE_PROMPT) :].strip()
                    if not path.startswith("http"):
                        bos_path, http_path = self.upload_file_to_bos(path)
                        filename = os.path.basename(path)
                        rprint(f"File has been uploaded to: {bos_path}")
                        rprint(f"File share url: {http_path}\n")
                        path = http_path
                    else:
                        filename = unquote(os.path.basename(urlparse(path).path))
                    message = f"<file>{filename}</file><url>{path}</url>"

                break

            rprint("\n[blue][bold]Model response[/bold][/blue]:")

            if message == self.END_PROMPT:
                rprint("Bye!")
                raise typer.Exit()
            elif message == self.RESET_PROMPT:
                self.msg_history = QfMessages()
                extra_field = {}
                rprint("Chat history has been cleared.")
                continue
            elif message == self.HELP_PROMPT:
                self.print_help_message()
                continue
            else:
                self.msg_history.append(message)

            with Live(
                Spinner("dots", text="Thinking...", style="status.spinner"),
                auto_refresh=True,
                refresh_per_second=24,
                console=self.console,
            ) as live:
                if self._is_qianfan_plugin():
                    response = self.client.do(
                        message,
                        plugins=self.plugins,
                        llm=self.inference_args,
                        stream=True,
                        history=self.msg_history._to_list()[:-1],
                        **self.inference_args,
                        **extra_field,
                    )
                else:
                    response = self.client.do(
                        self.msg_history,
                        plugins=self.plugins,
                        stream=True,
                        **self.inference_args,
                    )

                m = ""
                plugin_metainfo = {}
                plugin_render_content = None
                for r in response:
                    render_list: List[RenderableType] = []
                    if self._is_qianfan_plugin():
                        m += r["result"]
                        render_list.append(Markdown(m))
                    else:
                        event = r["_event"]
                        if event == "pluginMeta":
                            for meta in r["plugin_metas"]:
                                plugin_metainfo[meta["pluginId"]] = meta
                        elif event == "plugin":
                            plugin_render_list: List[RenderableType] = []
                            for plugin in r["plugin_info"]:
                                plugin_name = plugin["plugin_id"]
                                if plugin_name in plugin_metainfo:
                                    plugin_name = (
                                        f"{plugin_metainfo[plugin_name]['pluginNameForHuman']}"
                                        f"({plugin_metainfo[plugin_name]['pluginNameForModel']})"
                                    )

                                plugin_resp = plugin["plugin_resp"].strip().split("\n")
                                status = None
                                for resp in reversed(plugin_resp):
                                    if len(resp) == 0:
                                        continue
                                    plugin_response = json.loads(resp)
                                    if "actionName" not in plugin_response:
                                        continue
                                    status = plugin_response["actionContent"]
                                    break
                                if status is not None:
                                    if plugin["status"] == "1":
                                        plugin_info_obj = Text.from_markup(
                                            text=f"• {plugin_name}\t{status}",
                                            style="dim",
                                        )
                                    else:
                                        plugin_info_obj = Text(
                                            f"• {plugin_name}", style="dim"
                                        )
                                    plugin_render_list.append(plugin_info_obj)
                            plugin_render_list.append(Text(""))
                            plugin_render_content = Group(*plugin_render_list)

                        elif event == "chat":
                            m += r["result"]
                        if plugin_render_content is not None:
                            render_list.append(plugin_render_content)
                        render_list.append(Markdown(m))

                    if "is_end" not in r or not r["is_end"]:
                        render_list.append(
                            Spinner(
                                "dots", text="Generating...", style="status.spinner"
                            )
                        )
                    stat = r.statistic
                    render_list.append(
                        Text.from_markup(
                            "\n[dim]First token latentcy:"
                            f" {stat['first_token_latency']:.2f}s, Total latency:"
                            f" {stat['total_latency']:.2f}s.[/]"
                        )
                    )
                    if "is_end" in r and r["is_end"]:
                        if "usage" in r:
                            token_usage = r["usage"]
                            render_list.append(
                                Text.from_markup(
                                    f"[dim]Input token: {token_usage['prompt_tokens']},"
                                    " Output token:"
                                    f" {token_usage['completion_tokens']}, Total token:"
                                    f" {token_usage['total_tokens']}.[/]"
                                )
                            )
                        if self.debug:
                            render_list.append(render_response_debug_info(response=r))

                    live.update(Group(*render_list))

            self.msg_history.append(m, role=QfRole.Assistant)


MODEL_ARGUMENTS_PANEL = (
    "Model Arguments (Some arguments are not supported by every model)"
)


@credential_required
def plugin_entry(
    endpoint: Optional[str] = typer.Option(
        None,
        help="Endpoint of the plugin. ERNIE-Bot plugin will be used if not specified.",
    ),
    # tui: bool = typer.Option(False, help="Using Terminal UI"),
    multi_line: bool = typer.Option(
        False,
        "--multi-line",
        help="Multi-line mode which needs to press Esc before enter to submit message.",
    ),
    debug: bool = typer.Option(
        False,
        "--debug",
        help="Enable debug mode. The request infomation will be printed.",
    ),
    plugins: str = typer.Option(
        ...,
        help=(
            "Plugins enabled. Use comma(,) to split. (e.g."
            " uuid-zhishiku,uuid-chatocr,uuid-weatherforecast)"
        ),
    ),
    bos_path: Optional[str] = typer.Option(None, help="Bos path used for upload file."),
    temperature: Optional[float] = typer.Option(
        None,
        help=(
            "Controls the randomness of the generated text. A higher temperature makes"
            " the model more creative and produces more diverse, but potentially less"
            " coherent."
        ),
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    top_p: Optional[float] = typer.Option(
        None,
        help=(
            "Lower top_p value allows the model to focus on a narrowed set of likely"
            " next tokens, making the response more conherent but less random."
        ),
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    penalty_score: Optional[float] = typer.Option(
        None,
        help="Penalty scores can be applied to discourage repetition.",
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    system: Optional[str] = typer.Option(
        None,
        help="Persona setting for the model.",
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
    stop: Optional[str] = typer.Option(
        None,
        help="Stop words. Use comma to split multiple stop words.",
        rich_help_panel=MODEL_ARGUMENTS_PANEL,
    ),
) -> None:
    """
    Chat with the LLM with plugins in the terminal.
    """
    model = "EBPluginV2"

    extra_args = {}

    def add_if_not_none(key: str, value: Any) -> None:
        if value is not None:
            extra_args[key] = value

    add_if_not_none("temperature", temperature)
    add_if_not_none("top_p", top_p)
    add_if_not_none("penalty_score", penalty_score)
    add_if_not_none("system", system)

    if stop is not None:
        extra_args["stop"] = stop.split(",")

    client = PluginClient(
        model,
        endpoint,
        multi_line,
        debug=debug,
        plugins=plugins.split(","),
        bos_path=bos_path,
        **extra_args,
    )
    client.chat_in_terminal()


repository: Prithiviraj-23/Drdo_documentqa
file: venv/Lib/site-packages/qianfan/common/client/plugin.py
created_at: 2024-09-02 15:22:06+00:00
pushed_at: 2024-09-04 14:36:14+00:00
url:
https://github.com/Prithiviraj-23/Drdo_documentqa/blob/776a63014818a865857e23b5c183803fa07b44d5/venv/Lib/site-packages/qianfan/common/client/plugin.py


repository: Rabbonos/langhack
file: lang/hackathon/Lib/site-packages/qianfan/common/client/plugin.py
created_at: 2024-07-15 18:58:18+00:00
pushed_at: 2024-07-15 19:13:13+00:00
url:
https://github.com/Rabbonos/langhack/blob/a4339a127e87401e9ec56a9661dfb51f98598b38/lang/hackathon/Lib/site-packages/qianfan/common/client/plugin.py


repository: nccgroup/DriverBuddy
file: DriverBuddy.py
wanted_name: Driver Buddy
created_at: 2016-09-15 16:33:56+00:00
pushed_at: 2020-01-08 19:28:54+00:00
description:
DriverBuddy is an IDA Python script to assist with the reverse engineering of Windows kernel drivers.

url:
https://github.com/nccgroup/DriverBuddy/blob/e1e107212805cfe8995d92e58deda9abacc793fa/DriverBuddy.py

content:
from idaapi import *
from idautils import *
from idc import *
from DriverBuddy import data
from DriverBuddy import ioctl
'''#######################################################################################

DriverBuddy.py: Entry point for IDA python plugin used in Windows driver
                vulnerability research.

Written by Braden Hollembaek and Adam Pond of NCC Group
#######################################################################################'''

class DriverBuddyPlugin(plugin_t):
    flags = PLUGIN_UNL
    comment = ('Plugin to aid in Windows driver vulnerability research. ' +
               'Automatically tries to find IOCTL handlers, decode IOCTLS, '+
               'flag dangerous C/C++ functions, find Windows imports for privesc, '+
               'and identify the type of Windows driver.')
    help = ''
    wanted_name = 'Driver Buddy'
    wanted_hotkey = 'Ctrl-Alt-D'

    def init(self):
        self.hotkeys = []
        self.hotkeys.append(add_hotkey("Ctrl+Alt+I", self.decode))
        return PLUGIN_KEEP

    def run(self, args):
        print "[+] Welcome to Driver Buddy"
        autoWait() # Wait for IDA autoanalysis to complete
        driver_entry = data.is_driver()
	if driver_entry == "":
            print "[-] No DriverEntry stub found"
            print "[-] Exiting..."
            return
        print "[+] DriverEntry found"
        if data.populate_data_structures() == False:
            print "[-] Unable to load functions"
            print "[-] Exiting..."
            return
	driver_type = data.get_driver_id(driver_entry)
        if driver_type == "":
            print "[-] Unable to determine driver type assuming wdm"
        else:
            print "[+] Driver type detected: " + driver_type
        if ioctl.find_ioctls() == False:
            print "[-] Unable to automatically find any IOCTLs"
        return

    def decode(self, _=0):
        if idc.GetOpType(idc.ScreenEA(), 1) != 5:   # Immediate
            return
        value = idc.GetOperandValue(idc.ScreenEA(), 1) & 0xffffffff
        ioctl.get_ioctl_code(value)

    def term(self):
        pass

def PLUGIN_ENTRY():
    return DriverBuddyPlugin()


repository: gaasedelen/patching
file: plugins/patching.py
description: An Interactive Binary Patching Plugin for IDA Pro
wanted_name: Patching
comment: A plugin to enable binary patching in IDA
created_at: 2022-02-10 05:44:39+00:00
pushed_at: 2024-11-24 19:12:32+00:00
url:
https://github.com/gaasedelen/patching/blob/f7902033f9c2be9ea71017ce9eb13691906cc858/plugins/patching.py

content:
#------------------------------------------------------------------------------
# Plugin Preflight
#------------------------------------------------------------------------------
#
#    the purpose of this 'preflight' is to test if the plugin is compatible
#    with the environment it is being loaded in. specifically, these preflight
#    checks are designed to be compatible with IDA 7.0+ and Python 2/3
#
#    if the environment does not meet the specifications required by the
#    plugin, this file will gracefully decline to load the plugin without
#    throwing noisy errors (besides a simple print to the IDA console)
#
#    this makes it easy to install the plugin on machines with numerous
#    versions of IDA / Python / virtualenvs which employ a shared plugin
#    directory such as the 'preferred' IDAUSR plugin directory...
#

import sys

# 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)
if not SUPPORTED_ENVIRONMENT:
    print("Patching plugin is not compatible with this IDA/Python version")

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

if SUPPORTED_ENVIRONMENT:
    import patching
    from patching.util.python import reload_package

def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython plugins.
    """
    return PatchingPlugin()

class PatchingPlugin(ida_idaapi.plugin_t):
    """
    The IDA Patching 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 binary patching in IDA"
    help = ""
    wanted_name = "Patching"
    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

        # load the plugin core
        self.core = patching.PatchingCore(defer_load=True)

        # inject a reference to the plugin context into the IDA console scope
        IDA_GLOBAL_SCOPE.patching = self

        # 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.
        """
        try:
            self.core.unload()
        except Exception as e:
            pass
        self.core = None

    #--------------------------------------------------------------------------
    # Development Helpers
    #--------------------------------------------------------------------------

    def reload(self):
        """
        Hot-reload the plugin.
        """
        if self.core:
            self.core.unload()
        reload_package(patching)
        self.core = patching.PatchingCore()


repository: AcademySoftwareFoundation/OpenTimelineIO
file: src/py-opentimelineio/opentimelineio/plugins/manifest.py
description: Open Source API and interchange format for editorial timeline information.
created_at: 2016-08-23 21:22:18+00:00
pushed_at: 2025-05-12 01:31:44+00:00
url:
https://github.com/AcademySoftwareFoundation/OpenTimelineIO/blob/c125d9c093871ec6b487b55db2392a9b9b00653a/src/py-opentimelineio/opentimelineio/plugins/manifest.py

content:
# SPDX-License-Identifier: Apache-2.0
# Copyright Contributors to the OpenTimelineIO project

"""OTIO Python Plugin Manifest system: locates plugins to OTIO."""

from importlib import resources
import inspect
import logging
import os
from pathlib import Path

try:
    from importlib import metadata
except ImportError:
    # For python 3.7
    import importlib_metadata as metadata

from .. import (
    core,
    exceptions,
)


# for tracking what kinds of plugins the manifest system supports
OTIO_PLUGIN_TYPES = [
    'adapters',
    'media_linkers',
    'schemadefs',
    'hook_scripts',
    'hooks',
    'version_manifests',
]


def plugin_entry_points():
    """Returns the list of entry points for all available OpenTimelineIO
    plugins.
    """
    try:
        entry_points = metadata.entry_points(group='opentimelineio.plugins')
    except TypeError:
        # For python <= 3.9
        entry_points = metadata.entry_points().get(
            'opentimelineio.plugins', []
        )

    return entry_points


def manifest_from_file(filepath):
    """Read the .json file at filepath into a :py:class:`Manifest` object."""

    result = core.deserialize_json_from_file(filepath)
    absfilepath = os.path.abspath(filepath)
    result.source_files.append(absfilepath)
    result._update_plugin_source(absfilepath)
    return result


def manifest_from_string(input_string):
    """Deserialize the json string into a manifest object."""

    result = core.deserialize_json_from_string(input_string)

    # try and get the caller's name
    name = "unknown"
    stack = inspect.stack()
    if len(stack) > 1 and len(stack[1]) > 3:
        #                     filename     function name
        name = f"{stack[1][1]}:{stack[1][3]}"

    # set the value in the manifest
    src_string = f"call to manifest_from_string() in {name}"
    result.source_files.append(src_string)
    result._update_plugin_source(src_string)

    return result


@core.register_type
class Manifest(core.SerializableObject):
    """Defines an OTIO plugin Manifest.

    This is considered an internal OTIO implementation detail.

    A manifest tracks a collection of plugins and enables finding them by name
    or other features (in the case of adapters, what file suffixes they
    advertise support for).

    For more information, consult the documentation.
    """
    _serializable_label = "PluginManifest.1"

    def __init__(self):
        core.SerializableObject.__init__(self)
        self.adapters = []
        self.schemadefs = []
        self.media_linkers = []
        self.source_files = []

        # hook system stuff
        self.hooks = {}
        self.hook_scripts = []

        self.version_manifests = {}

    adapters = core.serializable_field(
        "adapters",
        type([]),
        "Adapters this manifest describes."
    )
    schemadefs = core.serializable_field(
        "schemadefs",
        type([]),
        "Schemadefs this manifest describes."
    )
    media_linkers = core.serializable_field(
        "media_linkers",
        type([]),
        "Media Linkers this manifest describes."
    )
    hooks = core.serializable_field(
        "hooks",
        type({}),
        "Hooks that hooks scripts can be attached to."
    )
    hook_scripts = core.serializable_field(
        "hook_scripts",
        type([]),
        "Scripts that can be attached to hooks."
    )
    version_manifests = core.serializable_field(
        "version_manifests",
        type({}),
        "Sets of versions to downgrade schemas to."
    )

    def extend(self, another_manifest):
        """
        Aggregate another manifest's plugins into this one.

        During startup, OTIO will deserialize the individual manifest JSON files
        and use this function to concatenate them together.
        """
        if not another_manifest:
            return

        self.adapters.extend(another_manifest.adapters)
        self.schemadefs.extend(another_manifest.schemadefs)
        self.media_linkers.extend(another_manifest.media_linkers)
        self.hook_scripts.extend(another_manifest.hook_scripts)

        for family, label_map in another_manifest.version_manifests.items():
            # because self.version_manifests is an AnyDictionary instead of a
            # vanilla python dictionary, it does not support the .set_default()
            # method.
            if family not in self.version_manifests:
                self.version_manifests[family] = {}
            self.version_manifests[family].update(label_map)

        for trigger_name, hooks in another_manifest.hooks.items():
            # because self.hooks is an AnyDictionary instead of a vanilla
            # python dictionary, it does not support the .set_default() method.
            if trigger_name not in self.hooks:
                self.hooks[trigger_name] = []
            self.hooks[trigger_name].extend(hooks)

        self.source_files.extend(another_manifest.source_files)

    def _update_plugin_source(self, path):
        """Set the source file path for the manifest."""

        for thing in (
            self.adapters
            + self.schemadefs
            + self.media_linkers
            + self.hook_scripts
        ):
            thing._json_path = path

    def from_filepath(self, suffix):
        """Return the adapter object associated with a given file suffix."""

        for adapter in self.adapters:
            if suffix.lower() in adapter.suffixes:
                return adapter
        raise exceptions.NoKnownAdapterForExtensionError(suffix)

    def adapter_module_from_suffix(self, suffix):
        """Return the adapter module associated with a given file suffix."""

        adp = self.from_filepath(suffix)
        return adp.module()

    # @TODO: (breaking change) this should search all plugins by default instead
    #        of just adapters
    def from_name(self, name, kind_list="adapters"):
        """Return the plugin object associated with a given plugin name."""

        for thing in getattr(self, kind_list):
            if name == thing.name:
                return thing

        raise exceptions.NotSupportedError(
            "Could not find plugin: '{}' in kind_list: '{}'."
            " options: {}".format(
                name,
                kind_list,
                getattr(self, kind_list)
            )
        )

    def adapter_module_from_name(self, name):
        """Return the adapter module associated with a given adapter name."""

        adp = self.from_name(name)
        return adp.module()

    def schemadef_module_from_name(self, name):
        """Return the schemadef module associated with a given schemadef name."""

        adp = self.from_name(name, kind_list="schemadefs")
        return adp.module()


_MANIFEST = None


def load_manifest():
    """Walk the plugin manifest discovery systems and accumulate manifests.

    The order of loading (and precedence) is:

       1. Manifests specified via the :term:`OTIO_PLUGIN_MANIFEST_PATH` variable
       2. Entrypoint based plugin manifests
       3. Builtin plugin manifest
    """

    result = Manifest()

    # Read plugin manifests defined on the $OTIO_PLUGIN_MANIFEST_PATH
    # environment variable.  This variable is an os.pathsep separated list of
    # file paths to manifest json files. Can be set to "" to indicate no
    # local custom manifest path.
    _local_manifest_path = os.environ.get("OTIO_PLUGIN_MANIFEST_PATH", None)
    if _local_manifest_path:
        for src_json_path in _local_manifest_path.split(os.pathsep):
            json_path = os.path.abspath(src_json_path)
            if (
                not os.path.exists(json_path)
                # the manifest has already been loaded
                or json_path in result.source_files
            ):
                # XXX: In case error reporting is requested
                # print(
                #     "Warning: OpenTimelineIO cannot access path '{}' from "
                #     "$OTIO_PLUGIN_MANIFEST_PATH".format(json_path)
                # )
                continue

            result.extend(manifest_from_file(json_path))

    if not os.environ.get("OTIO_DISABLE_ENTRYPOINTS_PLUGINS"):
        entry_points = plugin_entry_points()

        for plugin in entry_points:
            plugin_name = plugin.name
            try:
                plugin_entry_point = plugin.load()
                try:
                    plugin_manifest = plugin_entry_point.plugin_manifest()

                    # this ignores what the plugin_manifest() function might
                    # put into source_files in favor of using the path to the
                    # python package as the unique identifier

                    manifest_path = os.path.abspath(
                        plugin_entry_point.__file__
                    )

                    if manifest_path in result.source_files:
                        continue

                    plugin_manifest.source_files = [manifest_path]
                    plugin_manifest._update_plugin_source(manifest_path)

                except AttributeError:
                    name = plugin_entry_point.__name__

                    try:
                        filepath = resources.files(name) / "plugin_manifest.json"
                    except AttributeError:
                        # For python <= 3.7
                        with resources.path(name, "plugin_manifest.json") as p:
                            filepath = Path(p)

                    if filepath.as_posix() in result.source_files:
                        continue

                    plugin_manifest = core.deserialize_json_from_string(
                        filepath.read_text()
                    )
                    plugin_manifest._update_plugin_source(filepath.as_posix())
                    plugin_manifest.source_files.append(filepath.as_posix())

            except Exception as e:
                logging.exception(
                    f"could not load plugin: {plugin_name}.  Exception is: {e}"
                )
                continue

            result.extend(plugin_manifest)
    else:
        logging.debug(
            "OTIO_DISABLE_ENTRYPOINTS_PLUGINS is set. "
            "Entry points plugings have been disabled."
        )

    # the builtin plugin manifest
    try:
        builtin_manifest_path = (
            resources.files("opentimelineio.adapters")
            / "builtin_adapters.plugin_manifest.json"
        ).as_posix()
    except AttributeError:
        # For python <= 3.7
        with resources.path(
            "opentimelineio.adapters",
            "builtin_adapters.plugin_manifest.json"
        ) as p:
            builtin_manifest_path = p.as_posix()

    if os.path.abspath(builtin_manifest_path) not in result.source_files:
        plugin_manifest = manifest_from_file(builtin_manifest_path)
        result.extend(plugin_manifest)

    # force the schemadefs to load and add to schemadef module namespace
    for s in result.schemadefs:
        s.module()

    return result


def ActiveManifest(force_reload=False):
    """Return the fully resolved plugin manifest."""

    global _MANIFEST
    if not _MANIFEST or force_reload:
        _MANIFEST = load_manifest()

    return _MANIFEST


repository: allthingsida/qscripts
file: snippet_manager/snippetmanager.py
description: Programming productivity plugin for IDAPython and C++ development
wanted_name: Snippet manager
comment: Snippet manager plugin
created_at: 2019-04-10 18:06:19+00:00
pushed_at: 2025-03-25 18:39:59+00:00
url:
https://github.com/allthingsida/qscripts/blob/a1c9f4fc592455946dd8ff53cec3a4ad5e164c55/snippet_manager/snippetmanager.py

content:
"""
Snippet loader/saver for IDA Pro

by Elias Bachaalany / @allthingsida
"""

# TODO:
# - snippetmanager: run snippets in order (optional prefix RE; comes with stock REs); save_combined()
# - - save combined()
# - save_clean() --> delete previous snippets and save
# - stock regular expressions: \d+

import os
from typing import Union
import idaapi, idc

LANG_EXT_MAP = {
    "Python": "py",
    "IDC": "idc",
}

R_LANG_EXT_MAP = {v: k for k, v in LANG_EXT_MAP.items()}

# --------------------------------------------------------------
class snippet_t:
    def __init__(self, 
                 lang: str, 
                 name: str, 
                 body: str, /, 
                 netnode_idx: int = idaapi.BADNODE, 
                 index: int =-1):
        self.lang : str = lang
        self.name : str = name
        self.body : str = body
        self.netnode_idx : int = netnode_idx
        """Underlying netnode index"""
        self.index : int = index
        """Position in the index table"""

    def __repr__(self) -> str:
        return f"snippet_t({self.lang}, {self.name}, {self.body}, {self.netnode_idx}, {self.index})"

    def __str__(self) -> str:
        return f"lang: {self.lang}; title: {self.name}; index: {self.index}"

    def save(self, index: int) -> None:
        """Saves a node and updates the netnode index that was used to save it"""
        node = idaapi.netnode()
        node.create()

        node.supset(1, self.lang)
        node.supset(0, self.name)
        node.setblob(self.body.encode('utf-8'), 0, 'X')

        self.index = index
        self.netnode_idx = node.index()

    @staticmethod
    def from_file(file_name: str) -> Union[None, 'snippet_t']:
        """Constructs a snippet from a file.
        Its slot index and netnode index are not known yet.
        Call save() to save it to the database and get the netnode index
        """
        if not os.path.exists(file_name):
            return None

        basename = os.path.basename(file_name)
        ext = os.path.splitext(basename)[1].lstrip('.').lower()
        if not (lang := R_LANG_EXT_MAP.get(ext)):
            return None

        with open(file_name, 'r') as f:
            body = f.read()

        name = os.path.splitext(basename)[0]
        return snippet_t(lang, name, body)

    @staticmethod
    def from_netnode(netnode_idx: int, slot_idx: int, fast: bool = False):
        node = idaapi.netnode(netnode_idx)
        body = None if fast else node.getblob(0, 'X')
        return snippet_t(node.supstr(1),
                         node.supstr(0),
                         body.decode('utf-8').rstrip('\x00') if body else "",
                         netnode_idx=netnode_idx,
                         index=slot_idx)

# --------------------------------------------------------------
class snippet_manager_t:
    def __init__(self):
        self.index = idaapi.netnode()
        self.index.create("$ scriptsnippets")

    def delete(self, snippet: snippet_t) -> bool:
        if snippet.netnode_idx == idaapi.BADNODE:
            return False

        self.index.altdel(snippet.index)
        snippet.netnode_idx = idaapi.BADNODE
        idaapi.netnode(snippet.netnode_idx).kill()
        return True

    def delete_all(self) -> None:
        snippets = self.retrieve_snippets(fast=True)
        for snip in snippets:
            self.delete(snip)

        self.index.altdel_all()

    def load_from_folder(self, folder : str = '') -> bool:
        """Imports snippets from a folder."""
        # If no input directory is specified, use the default one
        if not folder:
            folder = os.path.join(os.path.dirname(idc.get_idb_path()), '.snippets')
            # For the default directory, create it if it doesn't exist
            if not os.path.exists(folder):
                os.makedirs(folder)

        # For a custom directory, check if it exists
        if not os.path.exists(folder):
            return False

        # Delete previous snippets (and reset index)
        self.delete_all()

        # Enumerate files
        snippets = []
        for file in os.listdir(folder):
            file = os.path.join(folder, file)
            if snippet := snippet_t.from_file(file):
                snippets.append(snippet)

        # Sort by snippet name
        snippets.sort(key=lambda x: x.name)

        for isnippet, snippet in enumerate(snippets):
            snippet.save(isnippet)
            self.index.altset(isnippet, snippet.netnode_idx + 1)

        return True

    def save_to_folder(self, folder: str = '') -> tuple[bool, str]:
        if not folder:
            folder = os.path.join(os.path.dirname(idc.get_idb_path()), '.snippets')

        if not os.path.exists(folder):
            os.makedirs(folder)

        try:
            snippets = self.retrieve_snippets()
            for snip in snippets:
                outfile_name = os.path.join(folder, "%s.%s" % (snip.name, LANG_EXT_MAP[snip.lang]))
                with open(outfile_name, 'w') as outfile:
                    outfile.write(snip.body)
        except Exception as e:
            return (False, f'Failed to save: {e!s}')
        return (True, f'Saved {len(snippets)} snippets to {folder}')


    def retrieve_snippets(self, fast: bool = False) -> list[snippet_t]:
        """Load all snippets from the database"""
        snip_idx = self.index.altfirst()
        if snip_idx == idaapi.BADNODE:
            return []

        snippets = []
        while snip_idx != idaapi.BADNODE:
            snip_node_idx = self.index.altval(snip_idx) - 1
            snippet = snippet_t.from_netnode(snip_node_idx, snip_idx, fast=fast)
            snippets.append(snippet)

            snip_idx = self.index.altnext(snip_idx)

        return snippets


# --------------------------------------------------------------
# Register some functions into the extension part of the IDA API
ext = getattr(idaapi, 'ext', None)
if not ext:
    ext = idaapi.ext = idaapi.object_t()

_sm = snippet_manager_t()

def save_snippets(output_folder: str =''):
    """
    Save snippets to a specified output folder. If no folder is specified, 
    the function uses the current directory by default.

    Args:
    output_folder (str): The folder where the snippets will be saved. 
                         Defaults to current directory if not specified.

    Returns: 
    Result of the operation performed by the save_to_folder method.
    """
    return _sm.save_to_folder(output_folder)

def load_snippets(input_folder: str =''):
    """
    Load snippets from a specified input folder. If no folder is specified, 
    the function uses the current directory by default.

    Args:
    input_folder (str): The folder from where the snippets will be loaded. 
                        Defaults to current directory if not specified.

    Returns: 
    Result of the operation performed by the load_from_folder method.
    """
    return _sm.load_from_folder(input_folder)

def delete_snippets():
    """
    Delete all existing snippets.

    Returns: 
    Result of the operation performed by the delete_all method.
    """
    return _sm.delete_all()

ext.snippets = idaapi.object_t(
    save=save_snippets,
    load=load_snippets,
    delete=delete_snippets,
    man=_sm,
)

# --------------------------------------------------------------
def _test_load(with_body=False):
    idaapi.msg_clear()
    global sm
    sm = _sm
    snippets = sm.retrieve_snippets()
    for isnip, snip in enumerate(snippets):
        print(f"#{isnip:03d} {snip!s}")
        if with_body:
            print("<body>\n%s" % snip.body)
            print("</body>\n------")

# --------------------------------------------------------------
class snippetman_plugmod_t(idaapi.plugmod_t):
    def run(self, _):
        print("""Please use the following methods to:
    - save snippets: idaapi.ext.snippets.save()
    - load snippets: idaapi.ext.snippets.load()
    - delete snippets: idaapi.ext.snippets.delete()
    - access the snippet manager object: idaapi.ext.snippets.man
""")
        return 0

class snippetman_plugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_MULTI
    comment = "Snippet manager plugin"
    help = "Run the plugin to get the full help message"
    wanted_name = "Snippet manager"
    wanted_hotkey = ""

    def init(self):
        return snippetman_plugmod_t()

def PLUGIN_ENTRY() -> idaapi.plugin_t:
    return snippetman_plugin_t()

# --------------------------------------------------------------
if __name__ == "__main__":
    _test_load(False)


repository: williballenthin/idawilli
file: plugins/dynamic_hints/dynamic_hints.py
description: IDA Pro resources, scripts, and configurations
wanted_name: DynHints2
comment: Display dynamically-generated hints (2).
created_at: 2016-12-14 02:23:46+00:00
pushed_at: 2025-05-12 10:45:42+00:00
url:
https://github.com/williballenthin/idawilli/blob/063417207eebd061fbd58f6441e6b5e9ffc43f8e/plugins/dynamic_hints/dynamic_hints.py

content:
'''
example of:
  - providing custom UI hints with dynamic data from Python

in this silly example, we display UI hints with the current timestamp.
a more useful plugin might inspect the hovered line, and display some documentation.

Author: Willi Ballenthin <william.ballenthin@fireeye.com>
Licence: Apache 2.0
'''
import sys
import datetime

import idc
import idaapi
import idautils


class HintsHooks(idaapi.UI_Hooks):
    def get_custom_viewer_hint(self, view, place):
        curline = idaapi.get_custom_viewer_curline(view, True)
        _, x, y = idaapi.get_custom_viewer_place(view, True)
        ea = place.toea()

        return ('0x%08X: %s' % (place.toea(), datetime.datetime.now().isoformat(' ')), 1)

    def get_ea_hint(self, ea):
        return datetime.datetime.now().isoformat(' ')


class DynHints2Plugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = "Display dynamically-generated hints (2)."

    help = "Display dynamically-generated hints (2)."
    wanted_name = "DynHints2"
    wanted_hotkey = "Ctrl-["


    def init(self):
        self.hooks = HintsHooks()
        return idaapi.PLUGIN_OK

    def run(self, arg):
        print('hints2: run')
        self.hooks.hook()

    def term(self):
        print('hints2: term')
        self.hooks.unhook()


def PLUGIN_ENTRY():
    return DynHints2Plugin()


repository: osirislab/ugo
file: src/hotloader.py
description: IDA Hexrays plugin for Go binaries
url: https://github.com/osirislab/ugo/blob/a4d02afa0a061a68fe693d728830cb37c018ce67/src/hotloader.py
wanted_name: Hotloader
created_at: 2017-12-13 22:17:52+00:00
pushed_at: 2017-12-21 04:34:00+00:00
content:
from idaapi import *
from idc import *
from idautils import *

class Hotloader(plugin_t):
    flags = PLUGIN_KEEP
    comment = ""

    help = "Unloads and loads plugins"
    wanted_name = "Hotloader"
    wanted_hotkey = "Ctrl-R"

    plugins = ['test']

    def init(self):
        msg("init() called!\n")
        return PLUGIN_OK

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

        for plugin in self.plugins:
            idaapi.require(plugin) # set your plugin to unload when run with -1

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

def PLUGIN_ENTRY():
    return Hotloader()


repository: danielplohmann/gui-plugin-template
file: template_plugin/PluginIda.py
description: A template for cross-compatible GUI plugins (IDA, Ghidra, Binary Ninja, Cutter)
wanted_name: TODO_PLUGIN_NAME
comment: TODO_PLUGIN_DESCRIPTION
created_at: 2024-02-26 15:14:34+00:00
pushed_at: 2024-03-07 16:03:50+00:00
url:
https://github.com/danielplohmann/gui-plugin-template/blob/94033763cf4bb9713304a240156ae10753425dfe/template_plugin/PluginIda.py

content:
from plugin.apis.IdaApi import IdaApi
from plugin.gui.PluginGui import PluginGui

import idaapi
import ida_nalt
import ida_kernwin


class Plugin_action_handler_t(idaapi.action_handler_t):
    def __init__(self, dummy_arg):
        idaapi.action_handler_t.__init__(self)
        return

    def activate(self, ctx):
        # some action
        return 1

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


class Hooks(ida_kernwin.UI_Hooks):
    def __init__(self):
        ida_kernwin.UI_Hooks.__init__(self)

    def finish_populating_widget_popup(self, widget, popup):
        ida_kernwin.attach_action_to_popup(widget, popup, "TODO_IDA_ACTION_NAME", None)


class PluginWidget(ida_kernwin.PluginForm):

    def OnCreate(self, form):
        self.parent = self.FormToPyQtWidget(form)
        self.api_proxy = IdaApi()
        self.plugin_gui = PluginGui(self.api_proxy)
        self.parent.setLayout(self.plugin_gui.layout)

        # context menu extensions / actions need to be registered here
        idaapi.register_action(
            ida_kernwin.action_desc_t(
                "TODO_IDA_ACTION_NAME",
                "TODO_ACTION_DESCRIPTION",
                Plugin_action_handler_t("DummyArg"),
                "Ctrl+Shift+S",
                "TODO_ACTION_DESCRIPTION",
            )
        ),

    def OnClose(self, form):
        idaapi.unregister_action("TODO_IDA_ACTION_NAME")
        hooks.unhook()

    def Show(self):
        if ida_nalt.retrieve_input_file_md5() is not None:
            return ida_kernwin.PluginForm.Show(self, "TODO_PLUGIN_NAME", options=(ida_kernwin.PluginForm.WCLS_CLOSE_LATER | ida_kernwin.PluginForm.WOPN_RESTORE | ida_kernwin.PluginForm.WCLS_SAVE))
        return None


class TemplatePlugin(idaapi.plugin_t):
    # https://www.hex-rays.com/products/ida/support/sdkdoc/group___p_l_u_g_i_n__.html
    flags = idaapi.PLUGIN_UNL
    comment = "TODO_PLUGIN_DESCRIPTION"
    help = "help"
    wanted_name = "TODO_PLUGIN_NAME"
    wanted_hotkey = "Ctrl+Shift+Y"

    def init(self):
        global hooks
        hooks = Hooks()
        hooks.hook()
        return idaapi.PLUGIN_OK

    def run(self, arg):
        plg = PluginWidget()
        plg.Show("TODO_PLUGIN_NAME")

        try:
            widget_a = ida_kernwin.find_widget("IDA View-A")
            widget_template_plugin = ida_kernwin.find_widget("TODO_PLUGIN_NAME")
            if widget_template_plugin and widget_a:
                ida_kernwin.set_dock_pos("TODO_PLUGIN_NAME", "IDA View-A", ida_kernwin.DP_RIGHT)
        except:
            print("find_widget option is available version 7.0 or later")

    def term(self):
        pass


def PLUGIN_ENTRY():
    return TemplatePlugin()


def main():
    global TEMPLATE_WIDGET
    try:
        # TODO figure out why we can't reopen our form when it was closed once
        TEMPLATE_WIDGET = PluginWidget()
    except Exception:
        import traceback
        traceback.print_exc()
        TEMPLATE_WIDGET = PluginWidget()

    TEMPLATE_WIDGET.Show()


if __name__ == "__main__":
    main()


repository: grayhatacademy/ida
file: plugins/funcprofiler/funcprofiler.py
wanted_name: Function Profiler
comment: Function xref profiler
created_at: 2013-10-16 18:52:21+00:00
pushed_at: 2023-10-23 10:38:19+00:00
url:
https://github.com/grayhatacademy/ida/blob/faf13a13c4f5070fe8f0666c1508f928ffe40e23/plugins/funcprofiler/funcprofiler.py

content:
import re
import idc
import idaapi
import idautils
from shims import ida_shims
from operator import attrgetter

# Legacy IDA fix-up for the Choose class.
try:
    import ida_kernwin
    choose = ida_kernwin.Choose
except ImportError:
    choose = idaapi.Choose2

IDA_FUNCTION_PROFILES = None


class IDAProfilerXref(object):
    def __init__(self, **kwargs):
        for (k, v) in kwargs.items():
            setattr(self, k, v)


class IDAFunctionProfiler(object):
    def __init__(self):
        self.functions = {}
        self._build_string_xrefs()
        self._build_function_xrefs()
        self._sort_functions()

    def _build_string_xrefs(self):
        for string in idautils.Strings():
            key_string = str(string)

            for xref in idautils.XrefsTo(string.ea):
                func = idaapi.get_func(xref.frm)
                if func:
                    start_ea = ida_shims.start_ea(func)
                    if start_ea not in self.functions:
                        self.functions[start_ea] = list()

                    xref = IDAProfilerXref(ea=string.ea, string=key_string,
                                           xref=xref.frm, type=str)
                    self.functions[start_ea].append(xref)

    def _build_function_xrefs(self):
        for function in idautils.Functions():
            for xref in idautils.XrefsTo(function):
                func = idaapi.get_func(xref.frm)
                if func:
                    start_ea = ida_shims.start_ea(func)
                    if start_ea not in self.functions:
                        self.functions[start_ea] = list()

                    self.functions[start_ea].append(IDAProfilerXref(
                        ea=function, string=ida_shims.get_name(function),
                        xref=xref.frm, type=callable))

    def _sort_functions(self):
        for function in list(self.functions.keys()):
            self.functions[function] = sorted(self.functions[function],
                                              key=attrgetter('xref'))


class IDAFunctionProfilerChooser(choose):
    DELIM_COL_1 = '-' * 125
    DELIM_COL_2 = '-' * 175

    def __init__(self):
        global IDA_FUNCTION_PROFILES
        choose.__init__(self,
                        "Strings Profile",
                        [["Function", 50 | choose.CHCOL_PLAIN],
                         ["Xrefs", 75 | choose.CHCOL_PLAIN], ])

        self.icon = 41

        if not IDA_FUNCTION_PROFILES:
            IDA_FUNCTION_PROFILES = IDAFunctionProfiler()

        self.items = []
        self.rename_function_cmd = None
        self.rename_regex_test_cmd = None
        self.rename_regex_cmd = None
        self.rename_regex_fuzzy_cmd = None

        self.string_filters = set()
        self.function_filters = set()

        self.profile = IDA_FUNCTION_PROFILES
        self.populate_items()

    def OnSelectLine(self, n):
        ida_shims.jumpto(self.items[n][2])

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

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

    def OnClose(self):
        pass

    def OnCommand(self, n, cmd):
        if cmd == self.rename_function_cmd and \
                self.items[n][0] != self.DELIM_COL_1:
            self.rename_function(n)
        elif cmd == self.rename_regex_cmd:
            self.rename_regex(n)
        elif cmd == self.rename_regex_test_cmd:
            self.rename_regex(n, dryrun=True)
        elif cmd == self.rename_regex_fuzzy_cmd:
            self.rename_fuzzy(n)

        self.populate_items()
        return 0

    def set_internal_filter(self, functions=set(), strings=set()):
        self.string_filters = strings
        self.function_filters = functions
        self.populate_items()

    def populate_items(self):
        self.items = []

        for (func_ea, xrefs) in self.profile.functions.items():
            if not self.function_filters or func_ea in self.function_filters:
                orig_items_len = len(self.items)

                for xref in xrefs:
                    if not self.string_filters or \
                            xref.string in self.string_filters:
                        if xref.type == callable:
                            display_string = xref.string + "()"
                        elif xref.type == str:
                            display_string = '"%s"' % xref.string
                        else:
                            display_string = xref.string

                        self.items.append([ida_shims.get_name(func_ea),
                                           display_string,
                                           xref.xref, func_ea])

                if len(self.items) != orig_items_len:
                    self.items.append([self.DELIM_COL_1, self.DELIM_COL_2,
                                       idc.BADADDR, idc.BADADDR])

        # Remove the last delimiter column
        if self.items and self.items[-1][-1] == idc.BADADDR:
            self.items.pop(-1)

    def show(self):
        if self.Show(modal=False) < 0:
            return False

        self.rename_function_cmd = self.AddCommand("Rename this function")
        self.rename_regex_test_cmd = self.AddCommand("Test a regex renaming rule")
        self.rename_regex_cmd = self.AddCommand("Apply a regex renaming rule")
        self.rename_regex_fuzzy_cmd = self.AddCommand("Apply generic fuzzy naming rules")

        return True

    def rename_function(self, n):
        new_name = ida_shims.ask_ident(self.items[n][0], "New function name")
        if new_name:
            ida_shims.set_name(self.items[n][3], new_name)

    def rename_fuzzy(self, n):
        if idc.AskYN(0, "Really rename functions based on fuzzy string "
                        "matching? (Save your database first!)") == 1:
            self.rename_regex(n, regex_str="\[(.*?)\]")
            self.rename_regex(n, regex_str="(\w*)\(\)")
            self.rename_regex(n, regex_str="^In.(\w*)(?i)")
            self.rename_regex(n, regex_str="(^\w*):")
            self.rename_regex(n, regex_str="(\w*)\.c(?i)")
            self.rename_regex(n, regex_str="(\w*):")
            self.rename_regex(n, regex_str="(^\w*)")

    def rename_regex(self, n, regex_str="", dryrun=False):
        count = 0
        if not regex_str:
            regex_str = idc.AskStr("", "Regex rename rule")

        if regex_str:
            if dryrun:
                print("Testing regex rename rule: '%s'" % regex_str)

            regex = re.compile(regex_str)

            # Look at all the profiled functions
            for (function, xrefs) in self.profile.functions.items():
                new_function_name = ""

                # Don't rename functions that have already been renamed
                if not idc.Name(function).startswith("sub_"):
                    continue

                # Look through all the strings referenced by this function
                for string in [x.string for x in xrefs if x.type == str]:

                    # Does the string match the given regex?
                    m = regex.search(string)
                    if m:
                        # Take the last group from the regex match
                        potential_function_name = m.groups()[-1].split(" ")[0]

                        # Replace common bad chars with underscores
                        for c in ['-', '>']:
                            potential_function_name = \
                                potential_function_name.replace(c, '_')

                        if idaapi.isident(potential_function_name) and \
                                '%' not in potential_function_name:
                            if len(potential_function_name) > len(new_function_name):
                                new_function_name = potential_function_name

                if new_function_name:
                    # Append _n to the function name, if it already exists
                    n = 1
                    orig_new_function_name = new_function_name
                    while idc.LocByName(new_function_name) != idc.BADADDR:
                        new_function_name = "%s_%d" % (orig_new_function_name,
                                                       n)
                        n += 1

                    if dryrun:
                        print("%s => %s" % (idc.Name(function),
                                            new_function_name))
                        count += 1
                    else:
                        if ida_shims.set_name(function, new_function_name):
                            count += 1

            print("Renamed %d functions" % count)


def from_function_profiler(arg=None):
    try:
        chooser = IDAFunctionProfilerChooser()
        cur_loc = ida_shims.get_screen_ea()
        func = idaapi.get_func(cur_loc)
        if func:
            start_ea = ida_shims.start_ea(func)
            chooser.set_internal_filter(functions=set([start_ea]))
        else:
            raise Exception("Can't limit profile to just this function, "
                            "because 0x%X is not inside a function!" % cur_loc)
        chooser.show()
    except Exception as e:
        print("IDAFunctionProfiler ERROR: %s" % str(e))


def all_functions_profiler(arg=None):
     try:
        chooser = IDAFunctionProfilerChooser()
        chooser.show()
     except Exception as e:
        print("IDAFunctionProfiler ERROR: %s" % str(e))


try:
    class FunctionProfilerFromFunctionActionHandler(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)

        def activate(self, ctx):
            from_function_profiler()
            return 1

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


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

        def activate(self, ctx):
            all_functions_profiler()
            return 1

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


class IDAFunctionProfilerPlugin(idaapi.plugin_t):
    flags = 0
    comment = "Function xref profiler"
    help = ""
    wanted_name = "Function Profiler"
    wanted_hotkey = ""
    xref_current_func_action_name = 'xrefcurrentfunction:action'
    all_xref_action_name = 'allxrefs:action'
    xref_current_func_menu_name = 'Xrefs from the current function'
    all_xref_menu_name = 'All function xrefs'
    menu_tab = 'View/Open subviews/'
    menu_context = []

    def init(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            xref_current_func_desc = idaapi.action_desc_t(
                self.xref_current_func_action_name,
                self.xref_current_func_menu_name,
                FunctionProfilerFromFunctionActionHandler(),
                self.wanted_hotkey, 'Xrefs from the current function.', 199)

            all_xref_desc = idaapi.action_desc_t(
                self.all_xref_action_name, self.all_xref_menu_name,
                FunctionProfilerAllFunctionsActionHandler(), self.wanted_hotkey,
                'All functions xref.', 199)

            idaapi.register_action(xref_current_func_desc)
            idaapi.register_action(all_xref_desc)

            idaapi.attach_action_to_menu(
                self.menu_tab, self.xref_current_func_action_name,
                idaapi.SETMENU_APP)

            idaapi.attach_action_to_menu(
                self.menu_tab, self.all_xref_action_name, idaapi.SETMENU_APP)
        else:
            self.menu_context.append(
                idaapi.add_menu_item(self.menu_tab,
                                     self.xref_current_func_menu_name,
                                     "",
                                     0,
                                     from_function_profiler,
                                     (True,)))

            self.menu_context.append(
                idaapi.add_menu_item(self.menu_tab,
                                     self.all_xref_menu_name,
                                     "",
                                     0,
                                     all_functions_profiler,
                                     (False,)))
        return idaapi.PLUGIN_KEEP

    def term(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            idaapi.detach_action_from_menu(self.menu_tab,
                                           self.xref_current_func_action_name)
            idaapi.detach_action_from_menu(self.menu_tab,
                                           self.all_xref_action_name)
        else:
            for context in self.menu_context:
                idaapi.del_menu_item(context)
        return None

    def run(self):
        pass


def IDAFunctionProfilerRefresh():
    global IDA_FUNCTION_PROFILES
    IDA_FUNCTION_PROFILES = IDAFunctionProfiler()


def PLUGIN_ENTRY():
    return IDAFunctionProfilerPlugin()


