# plugins

repository: Shenmarukai/IdaClass
file: src/OOAnalyzer.py
wanted_name: OOAnalyzer
comment: IDA OOAnalyzer plugin
created_at: 2025-03-05 21:50:12+00:00
pushed_at: 2025-03-05 21:54:25+00:00
url:
https://github.com/Shenmarukai/IdaClass/blob/ebf1a7fb939218a0cd150f23699c0ea8d5557d8e/src/OOAnalyzer.py

content:
'''Created on Jan 8, 2014

@version: 2.0
@organization: CERT Malicious Code Team
@author: jsg

This is the IDA plugin to apply OOAnalyzer output into IDA
Pro. Version 2.0 is the first version that works with OOAnalyzer.

'''
try:
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    import six
    import logging
    import zlib
    import traceback
    import re
    import copy
    import json
    from abc import ABCMeta, abstractmethod
    import sys
    import codecs
    import ida_bytes
    import ida_idp
    import ida_kernwin
    import ida_name
    import ida_typeinf
    import ida_ua
    import ida_xref
    import idaapi
    import idaapi
    import idautils
    import idc
except ImportError:
    print("Could not import IDA Python modules")
    sys.exit(-1)

# In lieu of using "six", let's make out own Python2/3 changes ourselves.
python_version_2 = (sys.version_info[0] == 2)

PLUGIN_VERSION = 2.0

EXPECTED_JSON_VERSION = "2.2.0"

BLOB_SIZE = 1024
OUR_NETNODE = "$ com.williballenthin"
INT_KEYS_TAG = 'M'
STR_KEYS_TAG = 'N'
STR_TO_INT_MAP_TAG = 'O'
INT_TO_INT_MAP_TAG = 'P'
OOA_JSON = "OOA_JSON"
APPLIED_CLASS_NAMES = "OOA_APPLIED"

logger = logging.getLogger(__name__)

# get the IDA version number
ida_major, ida_minor = list(map(int, idaapi.get_kernel_version().split(".")))
using_ida7api = (ida_major > 6)

def ida_hexify(value):
    '''
    convert hex string (0xNNNN...) to IDA-like hex value (NNNh)
    '''
    val = value

    # convert integers to strings and make them "IDA-like"
    if not isinstance(value, str):
        val = hex(value)

    if val.find("0x") != -1:
        ret_val = val[val.find("0x") + 2:]
        if ret_val.find('L') != -1:  # in newer versions of IDA an 'L' is appended to immediates
            ret_val = ret_val[:-1]
        return ret_val
    else:
        return val


def sanitize_name(name):
    if name != None:
        # strip off non-alphanumeric characters because IDA kind of sucks
        return re.sub('[^0-9a-zA-Z]+', '_', name)
    return None


def generate_vftable_name(classes, vft, cls, off):
    '''
    Generate vftable names of the form X_Y_... where Y is the parent of X. This captures the
    lineage of overwritten vftptrs
    '''

    lineage = []
    c = cls

    while c is not None:

        if c in lineage:
            print("WARNING: Class %s is in it's own lineage. This shouldn't happen" % c)
            break

        lineage.append(c)

        # add the current parent
        if off in c.parents:

            # go to the next parent at this offset. This will capture over-written vftptrs
            c = next(
                (pc for pc in classes if c.parents[off].name == pc.name), None)

            if c is None:
                print("WARNING: Unable to find correct parent")
                assert False

        else:
            c = None

    vft_parts = []
    for x in lineage:
        if x.ida_name != "":
            vft_parts.append(x.ida_name)
        else:
            vft_parts.append(x.name)

    vft_name = "_".join(str(y) for y in vft_parts)

    print("Vritual function table name %s_vftable" % vft_name)
    return "%s_vftable" % vft_name

# The code below was taken from:
#
# https://github.com/williballenthin/ida-netnode/tree/master
#
# To manage storage in an IDA IDB.

class NetnodeCorruptError(RuntimeError):
    pass

class Netnode(object):
    """
    A netnode is a way to persistently store data in an IDB database.
    The underlying interface is a bit weird, so you should read the IDA
      documentation on the subject. Some places to start:

      - https://www.hex-rays.com/products/ida/support/sdkdoc/netnode_8hpp.html
      - The IDA Pro Book, version 2

    Conceptually, this netnode class represents is a key-value store
      uniquely identified by a namespace.

    This class abstracts over some of the peculiarities of the low-level
      netnode API. Notably, it supports indexing data by strings or
      numbers, and allows values to be larger than 1024 bytes in length.

    This class supports keys that are numbers or strings.
    Values must be JSON-encodable. They can not be None.

    Implementation:
     (You don't have to worry about this section if you just want to
        use the library. Its here for potential contributors.)

      The major limitation of the underlying netnode API is the fixed
        maximum length of a value. Values must not be larger than 1024
        bytes. Otherwise, you must use the `blob` API. We do that for you.

      The first enhancement is transparently zlib-encoding all values.

      To support arbitrarily sized values with keys of either int or str types,
        we store the values in different places:

        - integer keys with small values: stored in default supval table
        - integer keys with large values: the data is stored in the blob
           table named 'M' using an internal key. The link from the given key
           to the internal key is stored in the supval table named 'P'.
        - string keys with small values: stored in default hashval table
        - string keys with large values: the data is stored in the blob
           table named 'N' using an integer key. The link from string key
           to int key is stored in the supval table named 'O'.
    """

    def __init__(self, netnode_name):
        self._netnode_name = netnode_name
        self._n = idaapi.netnode(netnode_name, 0, True)

    @staticmethod
    def _decompress(data):
        '''
        args:
          data (bytes): the data to decompress

        returns:
          bytes: the decompressed data.
        '''
        return zlib.decompress(data)

    @staticmethod
    def _compress(data):
        '''
        args:
          data (bytes): the data to compress

        returns:
          bytes: the compressed data.
        '''
        return zlib.compress(data)

    @staticmethod
    def _encode(data):
        '''
        args:
          data (object): the data to serialize to json.

        returns:
          bytes: the ascii-encoded serialized data buffer.
        '''
        return json.dumps(data).encode("ascii")

    @staticmethod
    def _decode(data):
        '''
        args:
          data (bytes): the ascii-encoded json serialized data buffer.

        returns:
          object: the deserialized object.
        '''
        return json.loads(data.decode("ascii"))

    def _intdel(self, key):
        assert isinstance(key, six.integer_types)

        did_del = False
        storekey = self._n.supval(key, INT_TO_INT_MAP_TAG)
        if storekey is not None:
            storekey = int(storekey)
            self._n.delblob(storekey, INT_KEYS_TAG)
            self._n.supdel(key, INT_TO_INT_MAP_TAG)
            did_del = True
        if self._n.supval(key) is not None:
            self._n.supdel(key)
            did_del = True

        if not did_del:
            raise KeyError("'{}' not found".format(key))

    def _get_next_slot(self, tag):
        '''
        get the first unused supval table key, or 0 if the
         table is empty.
        useful for filling the supval table sequentially.
        '''
        slot = self._n.suplast(tag)
        if slot is None or slot == idaapi.BADNODE:
            return 0
        else:
            return slot + 1

    def _intset(self, key, value):
        assert isinstance(key, six.integer_types)
        assert value is not None

        try:
            self._intdel(key)
        except KeyError:
            pass

        if len(value) > BLOB_SIZE:
            storekey = self._get_next_slot(INT_KEYS_TAG)
            self._n.setblob(value, storekey, INT_KEYS_TAG)
            self._n.supset(key, str(storekey).encode(
                'utf-8'), INT_TO_INT_MAP_TAG)
        else:
            self._n.supset(key, value)

    def _intget(self, key):
        assert isinstance(key, six.integer_types)

        storekey = self._n.supval(key, INT_TO_INT_MAP_TAG)
        if storekey is not None:
            storekey = int(storekey.decode('utf-8'))
            v = self._n.getblob(storekey, INT_KEYS_TAG)
            if v is None:
                raise NetnodeCorruptError()
            return v

        v = self._n.supval(key)
        if v is not None:
            return v

        raise KeyError("'{}' not found".format(key))

    def _strdel(self, key):
        assert isinstance(key, (str))

        did_del = False
        storekey = self._n.hashval(key, STR_TO_INT_MAP_TAG)
        if storekey is not None:
            storekey = int(storekey.decode('utf-8'))
            self._n.delblob(storekey, STR_KEYS_TAG)
            self._n.hashdel(key, STR_TO_INT_MAP_TAG)
            did_del = True
        if self._n.hashval(key):
            self._n.hashdel(key)
            did_del = True

        if not did_del:
            raise KeyError("'{}' not found".format(key))

    def _strset(self, key, value):
        assert isinstance(key, (str))
        assert value is not None

        try:
            self._strdel(key)
        except KeyError:
            pass

        if len(value) > BLOB_SIZE:
            storekey = self._get_next_slot(STR_KEYS_TAG)
            self._n.setblob(value, storekey, STR_KEYS_TAG)
            self._n.hashset(key, str(storekey).encode(
                'utf-8'), STR_TO_INT_MAP_TAG)
        else:
            self._n.hashset(key, bytes(value))

    def _strget(self, key):
        assert isinstance(key, (str))

        storekey = self._n.hashval(key, STR_TO_INT_MAP_TAG)
        if storekey is not None:
            storekey = int(storekey.decode('utf-8'))
            v = self._n.getblob(storekey, STR_KEYS_TAG)
            if v is None:
                raise NetnodeCorruptError()
            return v

        v = self._n.hashval(key)
        if v is not None:
            return v

        raise KeyError("'{}' not found".format(key))

    def __getitem__(self, key):
        if isinstance(key, str):
            v = self._strget(key)
        elif isinstance(key, six.integer_types):
            v = self._intget(key)
        else:
            raise TypeError("cannot use {} as key".format(type(key)))

        data = self._decompress(v)
        return self._decode(data)

    def __setitem__(self, key, value):
        '''
        does not support setting a value to None.
        value must be json-serializable.
        key must be a string or integer.
        '''
        assert value is not None

        v = self._compress(self._encode(value))
        if isinstance(key, str):
            self._strset(key, v)
        elif isinstance(key, six.integer_types):
            self._intset(key, v)
        else:
            raise TypeError("cannot use {} as key".format(type(key)))

    def __delitem__(self, key):
        if isinstance(key, str):
            self._strdel(key)
        elif isinstance(key, six.integer_types):
            self._intdel(key)
        else:
            raise TypeError("cannot use {} as key".format(type(key)))

    def get(self, key, default=None):
        try:
            return self[key]
        except (KeyError, zlib.error):
            return default

    def __contains__(self, key):
        try:
            if self[key] is not None:
                return True
            return False
        except (KeyError, zlib.error):
            return False

    def _iter_int_keys_small(self):
        # integer keys for all small values
        i = None
        if using_ida7api:
            i = self._n.supfirst()
        else:
            i = self._n.sup1st()
        while i != idaapi.BADNODE:
            yield i
            if using_ida7api:
                i = self._n.supnext(i)
            else:
                i = self._n.supnxt(i)

    def _iter_int_keys_large(self):
        # integer keys for all big values
        if using_ida7api:
            i = self._n.supfirst(INT_TO_INT_MAP_TAG)
        else:
            i = self._n.sup1st(INT_TO_INT_MAP_TAG)
        while i != idaapi.BADNODE:
            yield i
            if using_ida7api:
                i = self._n.supnext(i, INT_TO_INT_MAP_TAG)
            else:
                i = self._n.supnxt(i, INT_TO_INT_MAP_TAG)

    def _iter_str_keys_small(self):
        # string keys for all small values
        if using_ida7api:
            i = self._n.hashfirst()
        else:
            i = self._n.hash1st()
        while i != idaapi.BADNODE and i is not None:
            yield i
            if using_ida7api:
                i = self._n.hashnext(i)
            else:
                i = self._n.hashnxt(i)

    def _iter_str_keys_large(self):
        # string keys for all big values
        if using_ida7api:
            i = self._n.hashfirst(STR_TO_INT_MAP_TAG)
        else:
            i = self._n.hash1st(STR_TO_INT_MAP_TAG)
        while i != idaapi.BADNODE and i is not None:
            yield i
            if using_ida7api:
                i = self._n.hashnext(i, STR_TO_INT_MAP_TAG)
            else:
                i = self._n.hashnxt(i, STR_TO_INT_MAP_TAG)

    def iterkeys(self):
        for key in self._iter_int_keys_small():
            yield key

        for key in self._iter_int_keys_large():
            yield key

        for key in self._iter_str_keys_small():
            yield key

        for key in self._iter_str_keys_large():
            yield key

    def keys(self):
        return [k for k in list(self.iterkeys())]

    def itervalues(self):
        for k in list(self.keys()):
            yield self[k]

    def values(self):
        return [v for v in list(self.itervalues())]

    def iteritems(self):
        for k in list(self.keys()):
            yield k, self[k]

    def items(self):
        return [(k, v) for k, v in list(self.iteritems())]

    def kill(self):
        self._n.kill()
        self._n = idaapi.netnode(self._netnode_name, 0, True)


class PyOOAnalyzer(object):
    '''
    Responsible for parsing and applying JSON class specification to an IDB.
    '''

    def __init__(self):
        '''
        Constructor for the PyOOAnalyzer Plugin
        '''
        self.__json_filename = None
        self.__json_data = None
        self.__classes = []
        self.__vfcalls = []

        self.__member_usages_found = False
        self.__vcall_usages_found = False
        self.__classes_found = False

        self.__is_parsed = False

        # this is the results of parsing
        self.__parse_results = {"NumUsages": 0}

        self.__applied_classes = {}

        return

    def set_classes(self, other_classes):
        self.__classes = copy.deepcopy(other_classes)

    def __str__(self):
        '''
        Convert a class to a string
        '''
        s = ""
        for c in self.__classes:
            s += "%s\n" % c
        return s

    def set_json_file(self, jsn_file):
        '''
        Set the JSON file
        '''

        self.__json_filename = jsn_file
        result = True
        if jsn_file != None:

            try:
                self.__json_data = open(jsn_file).read()
            except:
                result = False

        # the file was opened, it is eligible for parsing
        if result == True:
            self.__is_parsed = False

        return result

    def is_parsed(self):
        '''
        Return flag indicate if the JSON file is parsed
        '''
        return self.__is_parsed

    def get_classes(self):
        return self.__classes

    def find_name(self, ea):
        for cls in self.__classes:
            for method in cls.methods:
                if method.start_ea == ea:
                    return method.method_name
        return None

    def get_json_filename(self):
        return self.__json_filename

    def get_json_data(self):
        return self.__json_data

    def set_json_data(self, data):
        self.__json_data = data

    def get_parse_results(self):
        '''
        Return results of parsing
        '''
        if len(self.__parse_results) == 0:
            return None

        return self.__parse_results

    def parse(self):
        """
        Parse the JSON object into a Python dictionary. Parse methods are responsible for properly
        setting the type of data. addresses are always base 16 and offsets are always in base 10
        """

        def _byteify(data):
            # if this is a unicode string, return its string representation
            if python_version_2:
                if isinstance(data, basestring):
                    return str(data.encode('utf-8').decode())
            else:
                if isinstance(data, str):
                    return str(data.encode('utf-8').decode())

            # if this is a list of values, return list of byteified values
            if isinstance(data, list):
                return [_byteify(item) for item in data]

            # if this is a dictionary, return dictionary of byteified keys and values
            # but only if we haven't already byteified it
            if isinstance(data, dict):
                if python_version_2:
                    return {_byteify(key): _byteify(value) for key, value in data.iteritems()}
                else:
                    return {_byteify(key): _byteify(value) for key, value in data.items()}

            # if it's anything else, return it in its original form
            return data

        if self.__json_data == None:
            return [False, "File not specified"]

        data = None
        try:
            data = json.loads(self.__json_data, object_hook=_byteify)
        except Exception as e:
            print("JSON parsing error: %s" % e)
            return [False, "JSON parsing error: %s" % e]

        file_version = data["version"] if "version" in data else "unknown"
        if file_version != EXPECTED_JSON_VERSION:
            return [False, "Expected JSON version '%s' but found '%s' instead. Aborting." % (EXPECTED_JSON_VERSION, file_version)]

        if "filemd5" in data:
            jsonMd5 = data["filemd5"]
            # Some IDA give a binary encoded version, and some give it in ascii already!
            idaMd5 = idautils.GetInputFileMD5()
            idaMd52 = codecs.encode(idaMd5, 'hex').decode("ascii")
            if jsonMd5.lower() != idaMd5.lower() and jsonMd5.lower() != idaMd52.lower():
                print(jsonMd5, idaMd5)
                if ida_kernwin.ask_yn(ida_kernwin.ASKBTN_YES, "There was a hash mismatch.  This JSON may be for a different file '%s'.  Do you want to import anyway?" % data["filename"]) == ida_kernwin.ASKBTN_NO:
                    return [False, "User aborted after hash mismatch"]

        print("Parsing JSON structures ...")
        self.__parse_structs(data["structures"])
        print("Completed Parsing JSON structures file: %s classes found" %
              len(self.__classes))

        self.__parse_vf_calls(data['vcalls'])
        print("Completed Parsing JSON class usages")

        self.__is_parsed = True
        return [True, "OK"]

    def apply(self):
        '''
        Apply classes, then usages
        '''

        print("Applying class structures")

        self.__apply_structs()

        print("Applying class virtual function calls")

        self.__apply_vfcalls()

        return True

    def apply_struct(self, cls_to_apply):
        '''
        Apply classes, then usages
        '''
        result = False
        for c in self.__classes:
            if c.ida_name == cls_to_apply.ida_name:
                if not c.applied:

                    print("Applying class %s" % c.ida_name)

                    self.apply_class(c)

                    c.applied = True
                    result = True
                    break
                else:
                    print("Class %s already applied" % c.name)
                    result = False
                    break

        return result

    def __parse_structs(self, structs):
        '''
        Parse the structures defined in the JSON file. The order here matters:

           * vftables must be parsed before methods because vftables contain additional methods
        '''
        self.__parse_results["NumClasses"] = len(structs)

        for s in structs.values():
            # use the ID of the current struct, if it exists
            c = PyClassStructure(s)

            #  The class must be created for members to work
            self.__classes.append(c)

            print("Found class %s" % c.ida_name)

        for s in structs.values():
            # Now fill out the structures
            for c in self.__classes:
                if c.name == s['name']:

                    cid = ida_typeinf.get_named_type_tid(c.ida_name)
                    if cid != idaapi.BADADDR:
                        c.id = cid

                    self.__parse_methods(c, s['methods'])
                    self.__parse_vftables(c, s['vftables'])
                    self.__parse_members(c, s['members'])

        # this point each class structure is loaded
        self.__classes_found = True

        return

    def __parse_members(self, cls, members):
        '''
        Add each member specified
        '''

        print("Parsing %d members for class %s ..." %
              (len(members), cls.ida_name))

        for m in members.values():
            offset = int(m['offset'], 16)
            if offset > 0x7FFFFFFF:
                offset -= 0x100000000

            if offset < 0:
                print("WARNING: Negative member offset (%d) found - discarding" % offset)
                continue

            # valid member - create it
            mem = PyClassMember(cls, m['type'], m['size'])
            mem.cls = cls
            mem.base = m['base']
            mem.member_name = m['name']

            self.__parse_member_usages(cls, m['usages'])

            if mem.member_type == ida_bytes.FF_STRUCT:

                mem.class_member_name = m['struc']  # This could be mangled

                # Search for the parsed class information
                cls_mem = None
                for c in self.__classes:
                    if mem.class_member_name == c.name:
                        cls_mem = c
                        break

                assert cls_mem is not None, "Unable to find class member struct %s" % mem.class_member_name

                mem.class_member = cls_mem

                if m['parent']:
                    # this is a parent
                    mem.is_parent = True
                    cls.add_parent(cls_mem, offset)
                    print("   - Found class parent, name: '%s', type: %s" %
                          (mem.member_name, mem.class_member.ida_name))
                else:
                    print("   - Found class member, name: '%s', type: %s" %
                          (mem.member_name, mem.class_member.ida_name))

            else:
                mem.class_member = None
                print("   - Found member '%s' @ offset 0x%x" %
                      (mem.member_name, offset))

            # offset of this member
            mem.offset = offset

            # add the new member if it is not from the base class
            if not mem.base:
                cls.add_member(mem, offset)

            if cls.id != idaapi.BADADDR:
                mid = idc.get_member_id(cls.id, offset)
                if mid != -1:
                    mem.id = mid

        print("Members parsed")

        return

    def __parse_methods(self, cls, methods):
        '''
        Parse the methods defined in the JSON file
        '''

        print("Parsing %d methods for class %s ..." %
              (len(methods), cls.ida_name))

        for m in methods.values():

            meth = PyClassMethod()

            meth.cls = cls
            meth.is_virtual = False  # no traditional methods are virtual

            meth.is_ctor = False
            meth.is_dtor = False
            if m['type'] == "ctor":
                meth.is_ctor = True
            elif m['type'] == "dtor" or m['type'] == "deldtor":
                meth.is_dtor = True

            #  this method is imported
            meth.is_import = False
            if m['import']:
                meth.is_import = True

            meth.start_ea = int(m['ea'], 16)

            meth.method_name = m['name']
            meth.demangled_name = m['demangled_name'] if m['demangled_name'] != "" else None
            meth.userdef_name = False

            flags = ida_bytes.get_flags(meth.start_ea)

            # In vs2010/Lite/oo, there is a function tail at 0x403ed0.  Unfortunately another
            # function tail calls that chunk, and get_func(0x403ed0) refers to the larger one.
            # This results in IDA helpfully saying that the function at 0x403ed0 is named
            # sub_402509.  Thanks IDA!
            idafunc = idaapi.get_func(meth.start_ea)
            is_func = idafunc is not None and idafunc.start_ea == meth.start_ea

            has_name = is_func \
                and ida_bytes.has_name(flags) \
                and ida_bytes.has_user_name(flags) \
                and idc.get_func_name(meth.start_ea) != ""
            # Does IDA have a name for this function?
            if has_name:
                meth.method_name = idc.get_func_name(meth.start_ea)
                meth.demangled_name = idc.demangle_name(meth.method_name,
                                                        idc.get_inf_attr(idc.INF_SHORT_DN))
                meth.demangled_name = meth.demangled_name if meth.demangled_name != "" else None
                meth.userdef_name = True

            print("   - Method %s parsed" % meth.method_name)

            cls.add_method(meth)

        print("All methods parsed")

        return

    def __parse_vftables(self, cls, vftables):
        '''
        Parse the vftables defined in the JSON file
        '''

        print("Parsing %s vftables for class %s ..." %
              (len(vftables), cls.ida_name))

        for v in vftables.values():

            vftptr_off = int(v['vftptr'], 16)
            if vftptr_off < 0:
                print(
                    "   WARNING: Negative virtual function pointer offset found (%d) - discarding" % vftptr_off)
                continue

            vft = PyClassVftable()
            vft.cls = cls
            vft.length = int(v['length'])
            vft.start_ea = int(v['ea'], 16)

            for entry in v['entries'].values():
                start_ea = int(entry['ea'], 16)

                # Look up method instead of duplicating code
                meth = next(
                    method for method in cls.methods if method.start_ea == start_ea)
                assert meth is not None

                vft.add_virtual_function(meth, int(entry['offset']))

            print("   - Adding vtable %x at offset %d" %
                  (vft.start_ea, vftptr_off))
            print("")

            vft_name = generate_vftable_name(
                self.__classes, vft, cls, vftptr_off)
            vftid = ida_typeinf.get_named_type_tid(vft_name)
            if vftid != idaapi.BADADDR:
                vft.id = vftid
            cls.add_vftable(vft, vftptr_off)

        print("Vftables parsed")

        return

    def __parse_member_usages(self, cls, mem_usages):
        '''
        Parse member usages and enqueue them for processing.
        '''
        self.__parse_results["NumUsages"] += len(mem_usages)
        for ea in mem_usages:
            mu = PyMemberUsage()
            mu.ida_name = cls.ida_name
            mu.ea = int(ea, 16)
            mu.cid = idaapi.BADADDR
            cls.add_usage(mu)
        return

    def __parse_vf_calls(self, vcalls):
        '''
        Parse virtual function calls
        '''
        print("Parsing %s virtual function calls ..." % (len(vcalls)))
        self.__parse_results["NumVcalls"] = len(vcalls)

        for call_ea, targets in vcalls.items():
            vc = PyVirtualFunctionCallUsage()
            vc.call_ea = int(call_ea, 16)

            for t in targets['targets']:
                vc.add_target_ea(int(t, 16))

            print("Parsed virtual function call: %s" % vc)

            self.__vfcalls.append(vc)

        return

    # The following methods concern applying the parsed JSON file to an IDB

    def apply_class(self, cls):
        '''
        Apply the class
        '''

        print("================================================")
        print("Applying class '%s'" % cls.ida_name)

        if cls.ida_name in self.__applied_classes:
            print("class '%s' is already applied" % cls.ida_name)
            return cls.id

        cls.id = ida_typeinf.tinfo_t.create_udt(0, cls.ida_name, 0)
        cls.ptr = ida_typeinf.tinfo_t(tid=cls.id)
        # Save the ID of this struct
        self.__applied_classes[cls.ida_name] = cls

        if cls.demangled_name != "":
            ida_typeinf.tinfo_t(tid=
                cls.id).set_type_cmt("Original mangled name: %s" % cls.name, 1)

        if len(cls.members) > 0:
            print("Applying %d members ..." % len(cls.members))
            for moff, mem in cls.members.items():
                self.__apply_member(cls, mem, moff)
            print("Members applied")
        else:
            print("Class with no members not allowed by IDA Pro, creating dummy member")

            idc.tinfo_t.create_udt_member(
                cls.id, "EMPTY", 0, ida_bytes.FF_BYTE | ida_bytes.FF_DATA, 0xffffffff, 1)
        if len(cls.parents) > 0:
            print("Applying %d parents ..." % len(cls.parents))
            for poff, par in cls.parents.items():
                self.__apply_parent(cls, par, poff)
            print("Parents applied")

        if len(cls.vftables) > 0:
            print("Applying %d vftables ..." % len(cls.vftables))
            for voff, vft in cls.vftables.items():
                self.__apply_vftable(cls, vft, voff)
            print("Vftables applied")

        if len(cls.methods) > 0:
            print("Applying %d methods ..." % len(cls.methods))
            for n in cls.methods:
                self.__apply_method(n)
            print("Methods applied")

        if len(cls.usages) > 0:
            print("Applying %d usages ..." % len(cls.usages))
            for u in cls.usages:
                u.cid = cls.id
                u.apply()
            print("Methods applied")

        print("Applied class '%s'" % cls.ida_name)
        print("================================================\n")

        cls.applied = True

        return cls.id

    def __apply_member(self, cls, mem, off):
        '''
        Apply members of this class to the IDB
        '''

        if mem.is_parent:
            return

        nbytes = idaapi.BADADDR
        if mem.member_type == ida_bytes.FF_STRUCT:
            if mem.class_member.ida_name not in self.__applied_classes:
                self.apply_class(mem.class_member)
                print("Applying class member: %s" %
                      str(mem.class_member.ida_name))

            nbytes = ida_typeinf.tinfo_t(tid=mem.class_member.id).get_size()
            mem_type = ida_bytes.FF_STRUCT | ida_bytes.FF_DATA

            idc.tinfo_t.create_udt_member(cls.id, mem.member_name,
                                 off, mem_type, mem.class_member.id, nbytes)

        else:
            # non-struct member
            nbytes = mem.size
            idc.tinfo_t.create_udt_member(cls.id, mem.member_name,
                                 off, mem.member_type, 0xffffffff, nbytes)

        # save the member ID
        mem.id = idc.get_member_id(cls.id, off)
        if mem.id == -1:
            print("WARNING: Adding member at offset %d to %s failed" %
                  (off, cls.ida_name))

        return

    def apply_method(self, method):
        self.__apply_method(method)

    def __apply_method(self, method):
        '''
        Apply methods of this class to the IDB
        '''

        method_name = method.demangled_name if method.demangled_name is not None else method.method_name

        cmt = cmt = "%s::%s" % (method.cls.ida_name, method_name)

        if method.is_virtual == True:
            cmt = "virtual %s" % cmt

        if method.is_ctor == True:
            cmt += " (constructor)"

        if method.is_dtor == True:
            cmt += " (destructor)"

        idafunc = idaapi.get_func(method.start_ea)
        is_func = idafunc is not None and idafunc.start_ea == method.start_ea
        if is_func:
            # Check for existing name
            if idc.get_name_ea_simple(method_name) != idaapi.BADADDR:
                method_name += "_%x" % method.start_ea

            print("Renaming %s to %s" %
                  (idc.get_func_name(method.start_ea), method_name))

            idc.set_name(method.start_ea, method_name,
                         ida_name.SN_NOCHECK | ida_name.SN_FORCE)
        else:
            print("Not renaming the function at %#x %s because it is not a function in IDA!" % (
                method.start_ea, method_name))

        idc.set_func_cmt(method.start_ea, cmt, 1)

        return

    def __apply_vftable(self, cls, vft, off):
        '''
        Apply the parsed vftable(s) for this class
        '''

        vft_name = generate_vftable_name(self.__classes, vft, cls, off)

        print("Applying vftable: %s" % (vft_name))

        idc.set_name(vft.start_ea, "%s_%x" %
                     (vft_name, vft.start_ea), ida_name.SN_CHECK)

        vftid = ida_typeinf.tinfo_t.create_udt(0, vft_name, 0)

        # this will likely occur if the structure name is already present in the IDB.
        if vftid == idaapi.BADADDR:
            print("Vtable already exists!")
            n = 1  # simply create an indexed struct
            while vftid == idaapi.BADADDR:
                vft_name = "%s_%d" % (vft_name, n)

                vftid = ida_typeinf.tinfo_t.create_udt(0, vft_name, 0)
                n += 1

        vft.id = vftid
        vft.ptr = ida_typeinf.tinfo_t(tid=vft.id)

        ida_typeinf.tinfo_t(tid=vftid).set_type_cmt(vft.cls.ida_name + " virtual function table", 1)

        print("VFtable length = %d" % vft.length)

        for vt_index in range(0, vft.length):
            vt_off = vt_index * 4

            # Where the entry points to
            ea = idc.get_wide_dword(vft.start_ea + vt_off)

            # Look up the name in the virtual function table entries.  This only contains
            # functions believed to be defined on the current class, and will probably be
            # removed.
            if vt_index in vft.virtual_functions:
                name = vft.virtual_functions[vt_index].method_name
            else:
                name = self.find_name(ea)
                if name is None:
                    # Last resort: Use whatever name IDA has
                    name = idc.get_func_name(ea)

            # print("Virtual function entry index %d using name %s" % (vt_index, name))

            # Mark the entry as a dword
            ida_bytes.create_data(vft.start_ea + vt_off,
                                  ida_bytes.FF_DWORD, 4, idaapi.BADADDR)

            res = idc.tinfo_t.create_udt_member(vft.id,
                                       str(name),
                                       vt_off,
                                       ida_bytes.FF_DWORD,
                                       0xffffffff,
                                       4)
            if res == ida_typeinf.STRUC_ERROR_MEMBER_NAME:
                name = "%s_%d" % (name, vt_index)
                res = idc.tinfo_t.create_udt_member(vft.id,
                                           str(name),
                                           vt_off,
                                           ida_bytes.FF_DWORD,
                                           0xffffffff,
                                           4)

            # Add comment with the original value
            idc.set_member_cmt(vftid, vt_off, ida_hexify(ea), 1)

        # add the vftptr :or rename existing member to vftptr
        vptr_name = "vftptr_0x%s" % ida_hexify(off)

        if idc.get_member_name(cls.id, off) == None:
            # there is no member at this offset
            idc.tinfo_t.create_udt_member(cls.id, vptr_name, off,
                                 ida_bytes.FF_DWORD, 0xffffffff, 4)
            idc.set_member_cmt(cls.id, off, vft_name, 1)
        else:
            # There is a member at this offset

            if (idc.get_member_flag(cls.id, off) & ida_bytes.DT_TYPE) != ida_bytes.FF_STRUCT:
                # there is already a member defined at this offset, change it to a vftptr
                # ida_typeinf.set_member_name(cls.ptr, off, vptr_name)
                idc.set_member_type(
                    cls.id, off, ida_bytes.FF_DWORD, 0xFFFFFFFF, 4)

        # set the vtable name on the global class list
        for c in self.__classes:
            if c.ida_name == cls.ida_name:
                c.vftables[off].vtable_name = vft_name
                break

        return

    def __apply_parent(self, cls, parent, off):
        '''
        Apply the parent classes to the IDB. This may (should) overwrite a virtual function pointer
        '''
        print("Applying parent: %s" % parent.ida_name)

        if parent.id == idaapi.BADADDR:
            self.apply_class(parent)

            # add the variable for the parent
        nbytes = ida_typeinf.tinfo_t(tid=parent.id).get_size()
        name = "parent_%s" % ida_hexify(off)

        print("Parent ID: %s size: %d" % (parent.id, nbytes))

        # apply the parent, deleting any empty members :or vftptrs
        result = idc.tinfo_t.create_udt_member(
            cls.id, name, off, ida_bytes.FF_STRUCT | ida_bytes.FF_DATA, parent.id, nbytes)
        if result == ida_typeinf.STRUC_ERROR_MEMBER_OFFSET:
            idc.del_struc_member(cls.id, off)
            idc.tinfo_t.create_udt_member(
                cls.id, name, off, ida_bytes.FF_STRUCT | ida_bytes.FF_DATA, parent.id, nbytes)

        return

    def get_applied_classes(self):
        '''
        Return a list of classes that have been applied
        '''
        return [cls for name, cls in self.__applied_classes.items()]

    def apply_all_structs(self):
        '''
        Apply the parsed classes. The basic algorithm is to iteratively apply
        structs for classes without parents. Eventually all structs will have
        their parents applied
        '''

        print("OOAnalyzer has %d classes to apply:" % len(self.__classes))

        for c in self.__classes:
            if c.demangled_name != "":
                print("   * %s (Demangled name: %s)" %
                      (c.name, c.demangled_name))
            else:
                print("   * %s" % (c.name))

        worklist_count = len(self.__classes)
        # while all classes are not complete

        while worklist_count > 0:

            # idaapi.show_wait_box("Applying class %s ..." % c.demangled_name)

            for c in self.__classes:

                # Has this class been applied
                if c.applied == True:
                    continue

                do_apply = True
                missing_member = ""

                # if this class has members, check for sub classes
                if c.members:
                    for o, m in c.members.items():

                        if m == None:
                            continue
                        if m.member_type == ida_bytes.FF_STRUCT:

                            if m.class_member == None:
                                continue

                            if m.class_member.ida_name not in self.__applied_classes:
                                # don't apply members with structs that are not yet applied
                                do_apply = False
                                missing_member = m.class_member.ida_name
                                break

                if do_apply:
                    print("Applying class '%s'" % c.ida_name)
                    self.apply_class(c)
                    c.applied = True
                    worklist_count -= 1

                else:
                    print("Delaying application of class '%s' because cannot find member class '%s'" % (
                        c.ida_name, missing_member))

            # idaapi.hide_wait_box()

        return

    def apply_vfcalls(self):
        '''
        Apply the different usages for each class
        '''

        for u in self.__vfcalls:
            # apply the virtual function call
            u.apply()

        return

# =============================================================================
# End class


class PyClassUsage(object):

    OP_REG = ida_ua.o_reg     # 1
    OP_MEM_REF = ida_ua.o_mem     # 2
    OP_REG_INDEX = ida_ua.o_phrase  # 3
    OP_REG_INDEX_DISP = ida_ua.o_displ   # 4
    OP_IMMEDIATE = ida_ua.o_imm     # 5

    '''
   This interface represents the usages for classes.
   '''

    def apply(self, usage):
        raise NotImplementedError("Must extend")

# =============================================================================
# End class


class PyVirtualFunctionCallUsage(PyClassUsage):
    '''
    This class represents a virtual function call
    '''

    def __init__(self):
        '''
        Constructor
        '''
        self.__call_ea = None
        self.__targets = []

        return

    def __str__(self):

        tstr = ""
        for t in self.targets:
            tstr += ("%x " % t)

        return "Call %x -> Target(s): %s" % (self.__call_ea, tstr)

    def get_call_ea(self):
        return self.__call_ea

    def get_target_ea(self):
        return self.__targets

    def set_call_ea(self, value):
        self.__call_ea = value

    def add_target_ea(self, t):
        self.__targets.append(t)

    def apply(self):
        '''
        Apply virtual function call by making an xref to the right place in the IDB
        '''
        print("================================================")
        for t in self.__targets:
            print("Applying virtual function call %x -> %x" %
                  (self.__call_ea, t))

            ida_xref.add_cref(self.__call_ea, t,
                              ida_xref.XREF_USER | ida_xref.fl_F)

            cmt = idc.GetCommentEx(self.__call_ea, 0)
            if cmt != None:
                cmt = cmt + ", %s" % ida_hexify(t)
                print("Multiple virtual function calls detected at %x (%s)" %
                      (self.__call_ea, cmt))

            else:
                cmt = "%s" % ida_hexify(t)

            idc.set_cmt(self.__call_ea, cmt, 0)

        print("================================================")

        return True

    # properties of virtual function calls

    call_ea = property(get_call_ea, set_call_ea, None, None)
    targets = property(get_target_ea, None, None, None)


# =============================================================================
# End class

class PyMemberUsage(PyClassUsage):

    def __init__(self, name=None):
        self.__ea = None
        self.__ida_name = name
        self.__cid = idaapi.BADADDR

        return

    def apply(self):
        '''
        Apply a usage
        '''

        if self.__ida_name is None:
            return False

        if self.__cid == idaapi.BADADDR:
            print("*** Cannot apply because bad ID")
            return False

        print("================================================")
        print("Applying class %s to %x" % (self.__ida_name, self.__ea))
        print("================================================")

        # there must be two and only two operands, assume 1 and check for 2
        n = 0
        if idc.get_operand_type(self.__ea, 1) in [self.OP_REG_INDEX, self.OP_REG_INDEX_DISP]:
            n = 1

        idc.op_stroff(self.__ea, n, self.__cid, 0)

        return True

    def get_cid(self):
        return self.__cid

    def get_ida_name(self):
        return self.__ida_name

    def get_ea(self):
        return self.__ea

    def set_cid(self, value):
        self.__cid = value

    def set_ea(self, value):
        self.__ea = value

    def set_ida_name(self, value):
        self.__ida_name = value

    ida_name = property(get_ida_name, set_ida_name, None, None)
    cid = property(get_cid, set_cid, None, None)
    ea = property(get_ea, set_ea, None, None)

# =============================================================================
# End class


class PyVtableUsage(PyClassUsage):

    def __init__(self, cls):
        self.__call_ea = None
        self.__cls = cls
        self.__vf_offset = None

        return

    def get_call_ea(self):
        return self.__call_ea

    def get_cls(self):
        return self.__cls

    def get_vf_offset(self):
        return self.__vf_offset

    def set_call_ea(self, value):
        self.__call_ea = value

    def set_cls(self, value):
        self.__cls = value

    def set_vf_offset(self, value):
        self.__vf_offset = value

    def del_call_ea(self):
        del self.__call_ea

    def del_cls(self):
        del self.__cls

    def del_vf_offset(self):
        del self.__vf_offset

    def apply(self):
        pass

    call_ea = property(get_call_ea, set_call_ea,
                       del_call_ea, "call_ea's docstring")
    cls = property(get_cls, set_cls, del_cls, "class_name's docstring")
    vf_offset = property(get_vf_offset, set_vf_offset,
                         del_vf_offset, "vf_offset's docstring")

# =============================================================================
# End class


# =============================================================================
# End class

class PyClassMethod(object):
    '''
    This class represents a class method
    '''

    def __init__(self, meth_name=None, cls=None, ea=None, ctor=False, dtor=False, virt=False, imp=False, udn=False):

        # Set the formatter for this object
        self.__start_ea = None
        if ea != None:
            self.__start_ea = int(ea)

        self.__method_name = None
        if meth_name != None:
            self.__method_name = meth_name

        self.__cls = None
        if cls != None:
            self.__cls = cls

        self.__userdef_name = udn
        self.__is_ctor = ctor
        self.__is_dtor = dtor
        self.__is_virtual = virt
        self.__is_import = imp

        return

    def get_best_method_name(self):
        if self.demangled_name is not None:
            s = self.demangled_name
            return re.sub(r'^.*?::', '', s)
        else:
            return self.method_name

    def __str__(self):
        return "%s @ %x" % (self.__method_name, self.__start_ea)

    def get_cls(self):
        return self.__cls

    def get_is_virtual(self):
        return self.__is_virtual

    def get_is_import(self):
        return self.__is_import

    def get_is_ctor(self):
        return self.__is_ctor

    def get_is_dtor(self):
        return self.__is_dtor

    def get_method_name(self):
        return self.__method_name

    def get_start_ea(self):
        return self.__start_ea

    def get_userdef_name(self):
        return self.__userdef_name

    def set_is_virtual(self, value):
        self.__is_virtual = value

    def set_is_import(self, value):
        self.__is_import = value

    def set_cls(self, value):
        self.__cls = value

    def set_is_ctor(self, value):
        self.__is_ctor = value

    def set_is_dtor(self, value):
        self.__is_dtor = value

    def set_method_name(self, value):
        self.__method_name = value

    def set_start_ea(self, value):
        self.__start_ea = value

    def set_userdef_name(self, value):
        self.__userdef_name = value

    # properties for class methods
    userdef_name = property(get_userdef_name, set_userdef_name, None, None)
    method_name = property(get_method_name, set_method_name, None, None)
    is_virtual = property(get_is_virtual, set_is_virtual, None, None)
    is_import = property(get_is_import, set_is_import, None, None)
    is_ctor = property(get_is_ctor, set_is_ctor, None, None)
    is_dtor = property(get_is_dtor, set_is_dtor, None, None)
    cls = property(get_cls, set_cls, None, None)
    start_ea = property(get_start_ea, set_start_ea, None, None)

# =============================================================================
# End class


class PyClassVftable(object):

    def __init__(self, vft_ea=None, cls=None):
        '''
        Represents a VFtable
        '''

        self.__vftable_ea = None
        if vft_ea != None:
            self.__vftable_ea = vft_ea

        self.__cls = None
        if cls != None:
            self.__cls = cls

        self.__vfuncs = {}
        self.__vtable_name = None

        self.__id = idaapi.BADADDR

        return

    def __str__(self):
        t = "Vftable  %s @ %x\n" % (self.__vtable_name, self.__vftable_ea)
        for k, v in self.__vfuncs.items():
            t += "\tvirtual %x @ %x\n" % (v.start_ea, k)
        return t

    def has_vfuncs(self):
        return not self.__vfuncs

    def get_vftable_ea(self):
        return self.__vftable_ea

    def get_vftable_name(self):
        return self.__vtable_name

    def get_cls(self):
        return self.__cls

    def set_vftable_ea(self, value):
        self.__vftable_ea = value

    def set_vftable_name(self, value):
        self.__vtable_name = value

    def set_cls(self, value):
        self.__cls = value

    def add_virtual_function(self, value, offset):
        self.__vfuncs[offset] = value

    def get_virtual_functions(self):
        return self.__vfuncs

    def set_id(self, value):
        self.__id = value
        return

    def get_id(self):
        return self.__id

    # Properties
    start_ea = property(get_vftable_ea, set_vftable_ea, None, None)
    vtable_name = property(get_vftable_name, set_vftable_name, None, None)
    cls = property(get_cls, set_cls, None, None)
    virtual_functions = property(get_virtual_functions, None, None, None)
    id = property(get_id, set_id, None, None)

# =============================================================================
# End class


class PyClassMember(object):
    '''
    Represents a class member
    '''

    def __init__(self, cls, mem_type, size):
        '''
        initialize class member
        '''

        self.__cls = cls
        self.__type_size = size
        self.set_type(mem_type, size)

        self.__member_name = None
        self.__class_mem = None

        self.__id = idaapi.BADADDR

        self.__is_parent = False

        self.__is_vftptr = False

        self.__offset = -1

        return

    def __str__(self):
        '''
        Return member as string
        '''
        return "%s (%x)" % (self.__member_name, self.__id)

    # property getters/setters

    def get_member_name(self):
        return self.__member_name

    def get_type(self):
        return self.__type

    def get_cls(self):
        return self.__cls

    def get_class_member(self):
        return self.__class_mem

    def set_class_member(self, value):
        self.__class_mem = value
        if self.__type_size == None and self.__class_mem != None:
            # ida_typeinf.get_struc_size(idc.GetStrucIdByName(self.value))
            self.__type_size = self.__class_mem.size()

        return

    def set_member_name(self, value):
        self.__member_name = value
        return

    def get_type_size(self):
        return self.__type_size

    def set_type(self, value, size):
        '''
        the ID for the struct member_type must be supplied
        the member_type can either by primitive :or a structure. If it is a struct :or
        string, then size is required
        '''

        if value == 'vftptr':
            self.__is_vftptr = True
            self.__type = ida_bytes.FF_DWORD

        elif value == 'struc':
            self.__type = ida_bytes.FF_STRUCT

        elif value == '':
            d = {1: ida_bytes.FF_BYTE,
                 2: ida_bytes.FF_WORD,
                 4: ida_bytes.FF_DWORD}
            self.__type = d.get(size, None)
            if self.__type is None:
                # XXX: We should really have a marker here so that in apply_member we create a
                # dummy type of the appropriate size.
                print("WARNING: Unknown type for size %d" % size)
                self.__type = ida_bytes.FF_BYTE
        else:
            print("WARNING: Unknown type %s" % value)
            assert (False)

        return

    def set_cls(self, value):
        self.__cls = value

    def set_id(self, value):
        self.__id = value
        return

    def set_isvftptr(self, value):
        self.__is_vftptr = value
        return

    def get_isvftptr(self):
        return self.__is_vftptr

    def get_id(self):
        return self.__id

    def get_size(self):
        return self.__size

    def set_size(self, value):
        self.__size = value
        return

    def get_isparent(self):
        return self.__is_parent

    def set_isparent(self, value):
        self.__is_parent = value

    def get_member_offset(self):
        return self.__offset

    def set_member_offset(self, value):
        self.__offset = value

    # Properties for class members

    member_name = property(get_member_name, set_member_name, None, None)
    is_parent = property(get_isparent, set_isparent, None, None)
    is_vftptr = property(get_isvftptr, set_isvftptr, None, None)
    member_type = property(get_type, set_type, None, None)
    class_member = property(get_class_member, set_class_member, None, None)
    cls = property(get_cls, set_cls, None, None)
    offset = property(get_member_offset, set_member_offset, None, None)
    size = property(get_type_size, None, None, None)
    id = property(get_id, set_id, None, None)

# =============================================================================
# End class


class PyClassStructure(object):
    '''
    Represents C++ class/structure
    '''

    def __init__(self, struc_def):
        '''
        Builds PyClassStructure
        '''
        self.__methods = []
        self.__vftables = {}

        # members and parents take up space
        self.__members = {}
        self.__parents = {}

        self.__vfcalls = []
        self.__vfcalls = []

        self.__id = idaapi.BADADDR   # This is set when applied

        self.__applied = False

        self.__size = struc_def['size']

        self.__demangled_name = struc_def['demangled_name']

        self.__name = struc_def['name']

        # set the IDA name
        self.__ida_name = sanitize_name(self.__name)

        if self.__demangled_name != "":
            self.__ida_name = sanitize_name(self.__demangled_name)

        return

    def __eq__(self, other):
        if other == None:
            return False
        return self.__id == other.__id and self.__name == other.__name

    def __str__(self):
        '''
        String representation of C++ class
        '''
        folks = ""
        for o, p in self.__parents.items():
            folks += "%s" % (p.ida_name)

        s = "class %s" % self.__ida_name
        if len(folks) > 0:
            s += " : %s" % folks

        s += " { \n"
        for m in self.__members:
            s += "\t%s @ %s\n" % (self.__members[m], m)

        for t in self.__methods:
            s += "\t%s\n" % t

        for v in self.__vftables:
            s += "\t%s\n" % str(v)

        return "%s}" % s

    # property getters/setters

    def get_best_name(self):
        dn = self.get_demangled_name()
        if dn is not None and dn != "":
            return dn
        else:
            return self.get_name()

    def get_ida_name(self):
        return self.__ida_name

    def set_ida_name(self, value):
        self.__ida_name = value
        return

    def get_demangled_name(self):
        return self.__demangled_name

    def set_demangled_name(self, value):
        self.__demangled_name = value
        return

    def get_name(self):
        return self.__name

    def set_name(self, value):
        self.__name = value
        return

    def add_member(self, member, off):
        '''
        Add the member, ensuring that the required parameters are specified
        '''
        member.cls = self
        self.__members[off] = member

        return

    def add_method(self, method):

        method.cls = self
        self.__methods.append(method)

        return

    def add_vftable(self, vft, vftptr):
        self.__vftables[vftptr] = vft
        return

    def add_parent(self, parent, off):
        self.__parents[off] = parent
        return

    def add_usage(self, usage):
        self.__vfcalls.append(usage)
        return

    def has_members(self):
        return not self.__members

    def has_methods(self):
        return not self.__methods

    def has_vftable(self):
        return not self.__vftables

    def get_members(self):
        return self.__members

    def get_methods(self):
        return self.__methods

    def get_vftables(self):
        return self.__vftables

    def set_vftables(self, value):
        self.__vftables = value

    def get_parents(self):
        return self.__parents

    def set_id(self, value):
        self.__id = value
        return

    def get_id(self):
        return self.__id

    def set_applied(self, value):
        self.__applied = value
        return

    def get_applied(self):
        return self.__applied

    def get_size(self):
        return self.__size

    def set_size(self, value):
        self.__size = value
        return

    def get_usages(self):
        return self.__vfcalls

    def set_usages(self, value):
        self.__vfcalls = value

    def get_vfcalls(self):
        return self.__vfcalls

    def set_vfcalls(self, value):
        self.__vfcalls = value

    # Properties of class structures

    demangled_name = property(
        get_demangled_name, set_demangled_name, None, None)
    ida_name = property(get_ida_name, set_ida_name, None, None)
    name = property(get_name, set_name, None, None)
    members = property(get_members, None, None, None)
    methods = property(get_methods, None, None, None)
    vftables = property(get_vftables, set_vftables, None, None)
    parents = property(get_parents, None, None, None)
    id = property(get_id, set_id, None, None)
    size = property(get_size, set_size, None, None)
    applied = property(get_applied, set_applied, None, None)
    usages = property(get_usages, set_usages, None, None)
    vfcalls = property(get_vfcalls, set_vfcalls, None, None)

# =============================================================================
# End class


class PyOOAnalyzerMethodTreeItem(QTreeWidgetItem):
    '''
    This is a method element in the class viewer tree
    '''

    def __init__(self, parent, meth):

        super(PyOOAnalyzerMethodTreeItem, self).__init__(parent)
        self.method = meth
        self.setText(0, self.method.get_best_method_name())


class PyOOAnalyzerMemberTreeItem(QTreeWidgetItem):
    '''
    This is a member element in the class viewer tree
    '''

    def __init__(self, parent, mem=None):

        super(PyOOAnalyzerMemberTreeItem, self).__init__(parent)
        self.member = mem
        self.setText(0, self.member.member_name)


class PyOOAnalyzerStructTreeItem(QTreeWidgetItem):
    '''
    This is a class element in the class viewer tree
    '''

    def __init__(self, parent, cls=None):

        super(PyOOAnalyzerStructTreeItem, self).__init__(parent)
        self.class_struct = cls
        self.setText(0, self.class_struct.get_best_name())


class PyOOAnalyzerExpForm(idaapi.PluginForm):
    '''
    This is the main class viewer form.
    '''

    def __init__(self):

        super(PyOOAnalyzerExpForm, self).__init__()

        self.__ooanalyzer = None

        self.cls_tree = None
        self.idbhook = None
        self.idphook = None
        self.__node = Netnode("$ cert.ooanalyzer")

        # the name of the IDA netnode that contains class information
        self.__NODE_NAME = "$OBJD"

        self.__visible = False

        return

    def start_ida_hooks(self):

        if self.idbhook == None:
            self.idbhook = PyOOAnalyzerIDBHooks(self.__ooanalyzer, self)
            self.idbhook.hook()

        if self.idphook == None:
            self.idphook = PyOOAnalyzerIDPHooks(self.__ooanalyzer, self)
            self.idphook.hook()

    def remove_ida_hooks(self):

        if self.idbhook != None:
            self.idbhook.unhook()
            self.idbhook = None

        if self.idphook != None:
            self.idphook.unhook()
            self.idphook = None

    def populate_class_list(self):
        '''
        Initialize the class viewer
        '''

        cls_list = self.__ooanalyzer.get_classes()
        for cls in cls_list:
            cls_entry = PyOOAnalyzerStructTreeItem(self.cls_tree, cls)

            cls_methods = QTreeWidgetItem(cls_entry)
            cls_methods.setText(0, "Methods")

            for m in cls.methods:
                PyOOAnalyzerMethodTreeItem(cls_methods, m)

            cls_members = QTreeWidgetItem(cls_entry)
            cls_members.setText(0, "Members")

            for off, mbr in cls.members.items():
                if mbr.is_parent == False:
                    PyOOAnalyzerMemberTreeItem(cls_members, mbr)

            if len(cls.parents) > 0:
                cls_parents = QTreeWidgetItem(cls_entry)
                cls_parents.setText(0, "Parents")
                for o, p in cls.parents.items():
                    # Parents are really special members that are class types
                    PyOOAnalyzerStructTreeItem(cls_parents, p)

        return

    def navigate(self, event):
        '''
        Enable 1-click navigation to class methods
        '''

        self.cls_tree.blockSignals(True)

        item = self.cls_tree.currentItem()
        if type(item) is PyOOAnalyzerMethodTreeItem:
            idaapi.jumpto(item.method.start_ea)

        self.cls_tree.blockSignals(False)

        return

    def update_class_method(self, cid, old_name, new_name):
        '''
        Update the class viewer in response to a method name change elsewhere in
        the IDB
        '''

        self.cls_tree.blockSignals(True)

        iterator = QTreeWidgetItemIterator(self.cls_tree,
                                           QTreeWidgetItemIterator.NotHidden)

        item = iterator.value()

        terminate = False
        while item and not terminate:
            if type(item) is PyOOAnalyzerStructTreeItem:
                if item.class_struct.id == cid:

                    # Have to check for because parents are stuct items,
                    # thus we may be in a parent
                    if item.childCount() > 0:
                        meth_group = item.child(0)  # should be methods
                        if meth_group != None:
                            if meth_group.text(0) == "Methods":

                                i = 0
                                while i < meth_group.childCount():
                                    meth_item = meth_group.child(i)
                                    if meth_item:

                                        if type(meth_item) is PyOOAnalyzerMethodTreeItem:
                                            if meth_item.text(0) == old_name:
                                                meth_item.setText(0, new_name)
                                                terminate = True
                                                break
                                    i += 1

            iterator += 1
            item = iterator.value()

        self.cls_tree.blockSignals(False)

    def update_class_member(self, cid, old_name, new_name):
        '''
        Update the class viewer in response to a member name change elsewhere in
        the IDB
        '''

        self.cls_tree.blockSignals(True)

        iterator = QTreeWidgetItemIterator(self.cls_tree,
                                           QTreeWidgetItemIterator.NotHidden)

        item = iterator.value()
        terminate = False
        while item and not terminate:
            if type(item) is PyOOAnalyzerStructTreeItem:
                if item.class_struct.id == cid:

                    # Have to check for because parents are stuct items,
                    # thus we may be in a parent
                    if item.childCount() > 0:
                        mbr_group = item.child(1)  # should be members
                        if mbr_group != None:

                            if mbr_group.text(0) == "Members":
                                i = 0
                                while i < mbr_group.childCount():
                                    mem_item = mbr_group.child(i)
                                    if mem_item:
                                        if type(mem_item) is PyOOAnalyzerMemberTreeItem:
                                            if mem_item.text(0) == old_name:
                                                mem_item.setText(0, new_name)
                                                terminate = True
                                                break
                                    i += 1

            iterator += 1
            item = iterator.value()

        self.cls_tree.blockSignals(False)

    def update_class(self, old_name, new_name):
        '''
        Update the class viewer in response to a class name change elsewhere in
        the IDB
        '''

        self.cls_tree.blockSignals(True)

        iterator = QTreeWidgetItemIterator(self.cls_tree,
                                           QTreeWidgetItemIterator.NotHidden)

        item = iterator.value()
        while item:
            if item.text(0) == old_name:
                item.setText(0, new_name)
            else:
                # rename parents of this item
                if item.childCount() == 3:
                    par_group = item.child(3)  # should be members
                    if par_group != None:
                        if par_group.text(0) == "Parents":
                            i = 0
                            while i < par_group.childCount():
                                par_item = mbr_group.child(i)
                                if par_item:
                                    if par_item.text(0) == old_name:
                                        par_item.setText(0, new_name)
            iterator += 1
            item = iterator.value()

        self.cls_tree.blockSignals(False)

        return

    def __edit_member_from_class_viewer(self, item):
        '''
        Edit the class member from the Class Viewer
        '''
        member = item.member
        old_member_name = member.member_name
        new_member_name = ida_kernwin.ask_str(
            old_member_name, 0, "Enter new member name")

        if new_member_name == None:
            self.cls_tree.blockSignals(False)
            return

        # cid = ida_typeinf.get_named_type_tid(member.member_name
        cls_members = idautils.StructMembers(member.cls.id)

        for [off, n, s] in cls_members:
            if n == member.member_name:

                if idc.set_member_name(member.cls.id, off, str(new_member_name)) != 0:
                    item.setText(0, str(new_member_name))

                    cls_list = self.__ooanalyzer.get_classes()
                    for c in cls_list:
                        for moff, mem in c.members.items():
                            if mem.member_name == old_member_name:
                                mem.member_name = new_member_name
                else:
                    ida_kernwin.warning(
                        "Cannot rename member to %s, the name already exists or is malformed!" % new_member_name)

        return

    def __edit_method_from_class_viewer(self, item):
        '''
        Handle method change request initiated from Class Viewer
        '''
        method = item.method
        old_method_name = method.method_name
        new_method_name = ida_kernwin.ask_str(
            old_method_name, 0, "Enter new method name")

        if new_method_name == None:
            self.cls_tree.blockSignals(False)
            return

        cls = item.parent().parent().class_struct

        m = next(m for m in cls.methods if m.start_ea == method.start_ea)
        m.method_name = new_method_name
        m.userdef_name = True
        # Rename the function
        self.__ooanalyzer.apply_method(m)
        item.setText(0, m.get_best_method_name())

        # XXX: Rename all vftable structures.
        # # the method is a virtual function
        # if m.is_virtual == True:
        #     cmt = "virtual %s" % cmt

        #     # rename virtual function in vftable structure
        #     for off, vt in c.vftables.items():
        #         for vfoff, vf in vt.virtual_functions.items():
        #             if vf.start_ea == m.start_ea:

        #                 global ignore_renamed
        #                 ignore_renamed = True
        #                 idc.set_member_name(
        #                     vt.id, 4*vfoff, new_method_name)
        #                 ignore_renamed = False

        #                 vf.method_name = new_method_name
        #                 break

        return

    def __edit_class_from_class_viewer(self, item):
        '''
        Handle class name change request initiated from Class Viewer
        '''

        old_name = item.class_struct.ida_name
        cid = idaapi.get_named_type_tid(str(old_name))

        if (cid is not None) and (cid != 0) and (cid != idaapi.BADADDR):

            new_name = ida_kernwin.ask_str(
                old_name, 0, "Enter new class name:")

            if new_name == None:
                self.cls_tree.blockSignals(False)
                return

            if ida_typeinf.tinfo_t(tid=cid).rename_type(new_name) != 0:
                item.setText(0, str(new_name))

                cls_list = self.__ooanalyzer.get_classes()
                for c in cls_list:
                    if c.ida_name == old_name:
                        c.ida_name = new_name

            else:
                ida_kernwin.warning(
                    "Cannot rename class to %s, the name already exists!" % new_name)
        else:
            ida_kernwin.warning("Cannot rename class before it is applied")

        return

    def edit_class_item(self, event):

        self.cls_tree.blockSignals(True)

        item = self.cls_tree.currentItem()

        if type(item) is PyOOAnalyzerMemberTreeItem:
            self.__edit_member_from_class_viewer(item)

        elif type(item) is PyOOAnalyzerMethodTreeItem:
            self.__edit_method_from_class_viewer(item)

        elif type(item) is PyOOAnalyzerStructTreeItem:
            self.__edit_class_from_class_viewer(item)

        self.cls_tree.blockSignals(False)

    def OnCreate(self, form):

        # Get parent widget

        self.parent = self.FormToPyQtWidget(form)

        # Create cls_tree control
        self.cls_tree = QTreeWidget()
        headerItem = QTreeWidgetItem()
        headerItem.setText(0, "Class")
        headerItem.setText(1, "State")

        self.cls_tree.setHeaderItem(headerItem)
        self.cls_tree.setWindowTitle("OOAnalzyer Results")
        self.cls_tree.setColumnWidth(0, 200)

        self.cls_tree.itemSelectionChanged.connect(
            lambda: self.navigate(self.cls_tree.currentItem()))

        # install the context menu
        self.cls_tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.cls_tree.customContextMenuRequested.connect(self.open_menu)

        # Create layout
        layout = QVBoxLayout()
        layout.addWidget(self.cls_tree)

        self.populate_class_list()
        # Populate PluginForm
        self.parent.setLayout(layout)

        applied_cls = self.__ooanalyzer.get_applied_classes()
        for c in applied_cls:
            self.__mark_applied(c)

        return

    def open_menu(self, position):

        menu = QMenu()

        # can only apply classes
        applyAction = None
        gotoDefAction = None
        renameAction = None

        item = self.cls_tree.currentItem()
        if type(item) is PyOOAnalyzerStructTreeItem:
            cls = item.class_struct
            if cls.applied:
                gotoDefAction = menu.addAction("Open Definition")
                renameAction = menu.addAction("Rename")
            else:
                applyAction = menu.addAction("Apply")
        elif type(item) is PyOOAnalyzerMethodTreeItem:
            if item.method.cls.applied:
                renameAction = menu.addAction("Rename")
        elif type(item) is PyOOAnalyzerMemberTreeItem:
            if item.member.cls.applied:
                renameAction = menu.addAction("Rename")

        action = menu.exec_(QCursor.pos())

        if applyAction is not None and action == applyAction:
            self.apply_action()
        elif gotoDefAction is not None and action == gotoDefAction:
            self.goto_def_action()
        elif renameAction is not None and action == renameAction:
            self.rename_action()

        return

    def apply_class_item(self, item):
        '''
        Apply a class
        '''
        if type(item) is PyOOAnalyzerStructTreeItem:

            applied_classes = self.__ooanalyzer.get_applied_classes()

            if self.__ooanalyzer.apply_struct(item.class_struct):

                new_applied_classes = self.__ooanalyzer.get_applied_classes()

                # mark all the newly applied classes
                delta = [
                    x for x in new_applied_classes if x not in applied_classes]

                for c in delta:
                    self.__mark_applied(c)
        else:
            print("Not applying because not struct")

        return

    def __mark_applied(self, applied_class):
        '''
        Mark the classes that have been applied
        '''
        root = self.cls_tree.invisibleRootItem()
        child_count = root.childCount()

        for i in range(child_count):
            cur_item = root.child(i)
            if type(cur_item) is PyOOAnalyzerStructTreeItem:
                if applied_class.ida_name == cur_item.class_struct.ida_name:
                    cur_item.setBackground(
                        0, QBrush(Qt.green, Qt.Dense7Pattern))
                    font = cur_item.font(0)
                    font.setBold(True)
                    cur_item.setFont(0, font)
                    cur_item.setText(1, "Applied")

    def apply_action(self):
        '''
        Execute the apply action
        '''
        item = self.cls_tree.currentItem()
        self.apply_class_item(item)

    def goto_def_action(self):
        '''
        Execute the goto definition action
        '''
        item = self.cls_tree.currentItem()
        cls = item.class_struct
        if cls.applied == True:
            ida_kernwin.open_structs_window(item.class_struct.id)
        else:
            ida_kernwin.warning(
                "You must apply class '%s' before you can open its definition" % item.class_struct.ida_name)

    def rename_action(self):
        '''
        Execute the rename action
        '''
        item = self.cls_tree.currentItem()
        self.edit_class_item(item)

    def __save_to_idb(self):
        '''
        Save the JSON to a netnode
        '''

        self.__node[OOA_JSON] = self.__ooanalyzer.get_json_data()
        self.__node[APPLIED_CLASS_NAMES] = [
            cls.ida_name for cls in self.__ooanalyzer.get_applied_classes()]

        return

    def __load_from_idb(self):
        '''
        Load previously applied JSON
        '''

        if OOA_JSON in self.__node and APPLIED_CLASS_NAMES in self.__node:
            return [self.__node[OOA_JSON], self.__node[APPLIED_CLASS_NAMES]]
        elif OOA_JSON in self.__node:
            return [self.__node[OOA_JSON], None]
        elif APPLIED_CLASS_NAMES in self.__node:
            return [None, self.__node[APPLIED_CLASS_NAMES]]

        return [None, None]

    def __load_from_json_file(self, json_file):
        '''
        Parse the JSON file
        '''
        if json_file != None:

            if self.__ooanalyzer.set_json_file(json_file):
                print("Opened JSON file %s" % json_file)
            else:
                ida_kernwin.warning("Could not open %s" % json_file)
                return None, 0, 0, 0

        if self.__ooanalyzer.is_parsed() == False:  # not parsed yet
            result, msg = self.__ooanalyzer.parse()
            print("Parsed %s %s" % (result, msg))
            if result == False:
                ida_kernwin.warning("Could not parse JSON: %s" % msg)
                return None, 0, 0, 0

        parse_results = self.__ooanalyzer.get_parse_results()
        if parse_results == None:
            return None, 0, 0, 0

        num_classes = 0
        num_vcalls = 0
        num_usages = 0

        if "NumClasses" in parse_results:
            num_classes = parse_results["NumClasses"]
        if "NumVcalls" in parse_results:
            num_vcalls = parse_results["NumVcalls"]
        if "NumUsages" in parse_results:
            num_usages = parse_results["NumUsages"]

        res_str = """
Successfully parsed JSON file: "%s".

The following C++ constructs are ready to apply:

  * %s class structures
  * %s object usages
  * %s virtual function calls

Press \"Yes\" to apply these items to the IDB. Press no to apply them manually (Note that virtual function calls will be resolved automatically)
""" % (self.__ooanalyzer.get_json_filename(), num_classes, num_usages, num_vcalls)

        return res_str, num_classes, num_vcalls, num_usages

    def OnClose(self, form):
        """
        Called when the plugin form is closed
        """

        global clsExpForm

        self.__save_to_idb()

        clsExpForm.remove_ida_hooks()

        self.__visible = False

        clsExpForm = None

        return

    def __setup_form(self):
        '''
        set up form data
        '''

        [json, applied_classes] = self.__load_from_idb()
        if json:
            print("OOAnalyzer class information found in IDB, opening Class Viewer")

            # mark previously applied classes
            self.__ooanalyzer = PyOOAnalyzer()
            self.__ooanalyzer.set_json_data(json)
            self.__ooanalyzer.parse()
            if applied_classes:

                # Find the intersection
                intersection = [
                    c for c in self.__ooanalyzer.get_classes() if c.ida_name in applied_classes]

                for c in intersection:
                    self.__ooanalyzer.apply_class(c)

        else:

            # load the class spec from a JSON file

            json_file = ida_kernwin.ask_file(
                0, "*.json", "Open JSON C++ specification file")
            if not json_file:
                return False

            self.__ooanalyzer = PyOOAnalyzer()

            # if everything applies OK, then show the form
            parse_results, nc, nvc, nu = self.__load_from_json_file(json_file)

            if parse_results != None:
                form_msg = """CERT OOAnalyzer v%.1f
%s
""" % (PLUGIN_VERSION, parse_results)

                do_apply = ida_kernwin.ask_yn(ida_kernwin.ASKBTN_YES, form_msg)

                if do_apply == ida_kernwin.ASKBTN_YES:  # yes
                    print("Applying all results")

                    self.__ooanalyzer.apply_all_structs()

                # apply virtual function calls for yes or no
                if do_apply == ida_kernwin.ASKBTN_YES or do_apply == ida_kernwin.ASKBTN_NO:
                    print("Applying virtual function calls")
                    self.__ooanalyzer.apply_vfcalls()
                    print("Applied virtual function calls")

                else:  # cancel
                    print("User cancelled.  Not applying anything")
                    return False

            else:
                # ida_kernwin.warning(
                #     "No C++ data structures found in '%s'" % json_file)
                return False

        return True

    def Show(self):

        if not self.__visible:
            setup_form = False
            try:
                setup_form = self.__setup_form
            except Exception as e:
                ida_kernwin.warning("An exception occurred: %s" % e)
            if self.__setup_form():

                if self.idbhook == None and self.idphook == None:
                    self.start_ida_hooks()

                self.__visible = True
                return idaapi.PluginForm.Show(self, "OOAnalyzer Class Viewer", options=idaapi.PluginForm.WOPN_PERSIST)
            else:
                return -1

    def is_visible(self):
        return self.__visible


class PyOOAnalyzerIDPHooks(ida_idp.IDP_Hooks):

    def __init__(self, objd, frm):

        ida_idp.IDP_Hooks.__init__(self)

        self.__ooanalyzer = objd
        self.__form = frm

        return

    def renamed(self, *args):
        '''
        Something in the database has been renamed
        '''
        ea = args[0]
        new_name = args[1]
        local_name = args[2]
        global ignore_renamed
        if ignore_renamed:
            return ida_idp.IDP_Hooks.renamed(self, ea, new_name, local_name)

        # a function has been renamed
        if idc.get_func_name(ea) != "":
            cls_model = self.__ooanalyzer.get_classes()
            for c in cls_model:
                for m in c.methods:
                    if m.start_ea == ea:

                        self.__form.update_class_method(
                            c.id, m.method_name, new_name)
                        m.method_name = new_name

                        if m.is_virtual:

                            # rename function name in vftable structure
                            for off, vt in c.vftables.items():
                                for vfoff, vf in vt.virtual_functions.items():

                                    if vf.start_ea == m.start_ea:

                                        ignore_renamed = True
                                        idc.set_member_name(
                                            vt.id, 4*vfoff, new_name)

                                        ignore_renamed = False

                                        vf.method_name = new_name
                                        break

                        return ida_idp.IDP_Hooks.renamed(self, ea, new_name, local_name)

        return ida_idp.IDP_Hooks.renamed(self, ea, new_name, local_name)


class PyOOAnalyzerIDBHooks(idaapi.IDB_Hooks):

    def __init__(self, objd, frm):

        self.form = frm
        self.__ooanalyzer = objd

        idaapi.IDB_Hooks.__init__(self)

    def struc_member_renamed(self, *args):
        '''
        A structure member has been renamed
        '''
        global ignore_renamed
        if ignore_renamed:
            return 0

        struc = args[0]
        mbr = args[1]

        class_model = self.__ooanalyzer.get_classes()
        for c in class_model:
            if c.id == struc.id:

                changed_name = idc.get_member_name(c.id, int(mbr.soff))

                for off, m in c.members.items():
                    if off == mbr.soff:
                        if m:
                            self.form.update_class_member(
                                c.id, m.member_name, changed_name)
                            m.member_name = changed_name
                            return 0

        # Update virtual functions if they are changed
        for c in class_model:
            for off, vt in c.vftables.items():
                if struc.id == vt.id:

                    changed_name = idc.get_member_name(vt.id, int(mbr.soff))

                    for vfoff, vf in vt.virtual_functions.items():

                        # this has to be scaled
                        if (vfoff*4) == mbr.soff:
                            if vt:
                                self.form.update_class_method(
                                    c.id, vf.method_name, changed_name)
                                vf.method_name = changed_name

                                ignore_renamed = True
                                idc.set_name(
                                    vf.start_ea, changed_name, ida_name.SN_CHECK)
                                ignore_renamed = False

                                return 0
        return 0

    def struc_renamed(self, *args):
        '''
        A structure was renamed in the IDB
        '''
        renamed_struc = args[0]

        class_model = self.__ooanalyzer.get_classes()
        for c in class_model:
            if c.id == renamed_struc.id:

                new_name = ida_typeinf.get_tid_name(renamed_struc.id)
                if new_name != None:

                    self.form.update_class(c.ida_name, new_name)

                    c.ida_name = new_name
                    for off, m in c.members.items():
                        m.ida_name = new_name

                    for mtd in c.methods:
                        cmt = "%s::%s" % (c.ida_name, mtd.method_name)
                        if mtd.is_virtual == True:
                            cmt = "virtual %s" % cmt
                        if mtd.is_ctor == True:
                            cmt += " (constructor)"
                        if mtd.is_dtor == True:
                            cmt += " (destructor)"
                        idc.set_func_cmt(mtd.start_ea, cmt, 1)

                    for off, v in c.vftables.items():
                        v.cls.ida_name = new_name

                        new_vft_name = generate_vftable_name(
                            self.__ooanalyzer.get_classes(), v, c, off)

                        idc.set_name(v.start_ea, "%s_%x" % (
                            new_vft_name, v.start_ea), ida_name.SN_CHECK)
                        ida_typeinf.tinfo_t(tid=v.id).rename_type(new_vft_name)
                        ida_typeinf.tinfo_t(tid=v.id).set_type_cmt(v.cls.ida_name + " virtual function table", 1)

                    break

        return 0

# =============================================================================
# This is the entry point for the plugin


class PyOOAnalyzer_plugin(idaapi.plugin_t):
    '''
    Main plugin class
    '''

    flags = idaapi.PLUGIN_UNL

    comment = "IDA OOAnalyzer plugin"
    help = "Email jsg@cert.org"
    wanted_name = "OOAnalyzer"
    wanted_hotkey = "F3"

    def init(self):
        '''
        Create the Plugin
        '''

        return idaapi.PLUGIN_OK

    def run(self, arg):
        '''
        Run the plugin
        '''

        try:
            # is the Class Viewer already open?
            class_viewer = ida_kernwin.find_widget("OOAnalyzer Class Viewer")
            if class_viewer != None:
                ida_kernwin.warning(
                    "OOAnalyzer class information has already been imported")
                return

            global clsExpForm
            global ignore_renamed

            ignore_renamed = False
            clsExpForm = None

            print("\n\nOOAnalyzer Plugin version %s loaded" % PLUGIN_VERSION)

            if clsExpForm is None:
                clsExpForm = PyOOAnalyzerExpForm()
                clsExpForm.Show()

            idaapi.set_dock_pos('OOAnalyzer Class Viewer',
                                'Functions window', idaapi.DP_BOTTOM)
        except Exception as e:
            traceback.print_exc()
            print(e)

        return

    def term(self):
        print("OOAnalyzer Plugin version %s done importing" % PLUGIN_VERSION)
        return


def PLUGIN_ENTRY():
    try:
        return PyOOAnalyzer_plugin()
    except Exception as e:
        traceback.print_exc()
        print(e)
    return None

# Local Variables:
# mode: python
# fill-column: 95
# comment-column: 0
# python-indent-offset: 4
# End:


repository: clovme/WTools
file: software/Reverse Cracking/IDA Professional 9.0/python/examples/hexrays/vds11.py
wanted_name: Optimize goto chains (IDAPython)
comment: Sample plugin11 for Hex-Rays decompiler
created_at: 2025-03-27 00:14:54+00:00
pushed_at: 2025-05-11 10:18:21+00:00
url:
https://github.com/clovme/WTools/blob/f12f01c833a6a6241799a9d01e9117cc28a65dbc/software/Reverse%20Cracking/IDA%20Professional%209.0/python/examples/hexrays/vds11.py

content:
"""
summary: a custom microcode block optimization rule (resolve `goto` chains)

description:
  Installs a custom microcode block optimization rule,
  to transform:

        goto L1
        ...
      L1:
        goto L2

  into

        goto L2

  In other words we fix a goto target if it points to a chain of gotos.
  This improves the decompiler output in some cases.
"""

import ida_bytes
import ida_range
import ida_kernwin
import ida_hexrays
import ida_typeinf
import ida_idaapi

class goto_optimizer_t(ida_hexrays.optblock_t):
    def func(self, blk):
        if self.handle_goto_chain(blk):
            return 1
        return 0

    def handle_goto_chain(self, blk):
        mgoto = blk.tail
        if not mgoto or mgoto.opcode != ida_hexrays.m_goto:
            return False

        visited = []
        t0 = mgoto.l.b
        i = t0
        mba = blk.mba

        # follow the goto chain
        while True:
            if i in visited:
                return False
            visited.append(i)
            b = mba.get_mblock(i)
            m2 = ida_hexrays.getf_reginsn(b.head)
            if not m2 or m2.opcode != ida_hexrays.m_goto:
                break
            i = m2.l.b

        if i == t0:
            return False # not a chain

        # all ok, found a goto chain
        mgoto.l.b = i # jump directly to the end of the chain

        # fix the successor/predecessor lists
        blk.succset[0] = i
        mba.get_mblock(i).predset.add(blk.serial)
        mba.get_mblock(t0).predset._del(blk.serial)

        # since we changed the control flow graph, invalidate the use/def chains.
        # stricly speaking it is not really necessary in our plugin because
        # we did not move around any microcode operands.
        mba.mark_chains_dirty()

        # it is a good idea to verify microcode after each change
        # however, it may be time consuming, so comment it out eventually
        mba.verify(True);
        return True

# --------------------------------------------------------------------------
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "Optimize goto chains (IDAPython)"
    wanted_hotkey = ""
    comment = "Sample plugin11 for Hex-Rays decompiler"
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            self.optimizer = goto_optimizer_t()
            self.optimizer.install()
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        self.optimizer.remove()
    def run(self, arg):
        if arg == 1:
            return self.optimizer.remove()
        elif arg == 2:
            return self.optimizer.install()

def PLUGIN_ENTRY():
    return my_plugin_t()



repository: poohzaza166/pooh-plugin-assistant
file: test/example copy/main.py
description: This is a showcase of pytohn plugin system for creating voice assistant
created_at: 2025-03-01 22:01:41+00:00
pushed_at: 2025-03-23 16:19:46+00:00
url:
https://github.com/poohzaza166/pooh-plugin-assistant/blob/21decaa3651a0191e1006927a31c097dd3c8bde3/test/example%20copy/main.py

content:
import asyncio
import threading
import time
from datetime import datetime

from .pooh_lib import (PluginBase, loop_method, provide_data, push_event,
                       register_command, subscribe)


def plugin_entry(message_bus, parser):
    print("loading plugin")
    print("checking parser object")
    print(parser)
    plugin = ExamplePlugin(message_bus, parser)
    return  plugin

class ExamplePlugin(PluginBase):
    def __init__(self, message_bus,parser):
        print(parser)
        super().__init__(message_bus, parser)

    @subscribe("example_event")
    def handle_example_event(self, message):
        print(f"Example plugin received: {message}")

    @provide_data("example_data")
    def provide_example_data(self, param):
        return f"Example data with param: {param}"

    @loop_method(delay=1)
    async def async_task(self):
        print(f"Async task running... (Thread: {threading.current_thread().name})")
        await asyncio.sleep(0.5)

    @loop_method(delay=2)
    def sync_task(self):
        print(f"Sync task running... (Thread: {threading.current_thread().name})")
        time.sleep(1)

    @subscribe("text_input")
    def say_hi(self, message):
        self.parser.execute_command(message)

    async def run(self):
        print("Example plugin is running")
        while True:
            await asyncio.sleep(2)
            # print("Example plugin still active")

    @register_command(example_phrase = ["whats the current time"])
    def get_current_time(self):
        print(datetime.now().time())

    @register_command(
        patterns=[r"hello (bot|assistant)"],
        example_phrase={"greeting": "Hello assistant"}
    )
    def hello_command(self, target: str = "bot"):
        """Respond to a greeting with a friendly message"""
        return f"Hello! I'm your {target}. How can I help you today?"
    


repository: manicmaniac/headlessvim
file: tests/test_headlessvim.py
description: programmable Vim, no need of +clientserver!
created_at: 2015-08-27 22:20:20+00:00
pushed_at: 2015-09-06 14:53:19+00:00
url:
https://github.com/manicmaniac/headlessvim/blob/3e4657f95d981ddf21fd285b7e1b9da2154f9cb9/tests/test_headlessvim.py

content:
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import os

import mock
import pytest

from headlessvim import Vim, open


@pytest.fixture
def env(request):
    return dict(os.environ, LANG='C')


@pytest.fixture
def unterminated_vim(request, env):
    return open(env=env)


@pytest.yield_fixture
def vim(request, unterminated_vim):
    vim = unterminated_vim
    yield vim
    vim.close()


@pytest.fixture
def fixtures(request):
    here = os.path.dirname(__file__)
    return os.path.join(here, 'fixtures')


@pytest.fixture
def plugin_dir(request, fixtures):
    return os.path.join(fixtures, 'spam')


@pytest.fixture
def plugin_entry_script(request):
    return 'plugin/spam.vim'


@pytest.fixture
def zombie_vim(request, unterminated_vim):
    vim = unterminated_vim
    vim.close()
    vim._process = mock.MagicMock()
    vim._process.is_alive.return_value = True
    return vim


def test_open(vim):
    assert isinstance(vim, Vim)


def test_close_zombie(zombie_vim):
    vim = zombie_vim
    vim.close()
    vim._process.terminate.assert_called_once_with()
    vim._process.kill.assert_called_once_with()


def test_del(zombie_vim):
    vim = zombie_vim
    vim.__del__()
    vim._process.terminate.assert_called_once_with()
    vim._process.kill.assert_called_once_with()


def test_context_manager():
    with open() as vim:
        assert isinstance(vim, Vim)
        assert vim.is_alive()
    assert not vim.is_alive()


def test_is_alive(unterminated_vim):
    vim = unterminated_vim
    assert vim.is_alive()
    vim.close()
    assert not vim.is_alive()


def test_display(vim):
    display = vim.display()
    assert 'VIM - Vi IMproved' in display
    assert 'by Bram Moolenaar et al.' in display
    assert 'type  :q<Enter>' in display
    assert 'type  :help<Enter>' in display


def test_display_lines(vim):
    lines = vim.display_lines()
    assert all(len(line) == 80 for line in lines)
    assert any('VIM - Vi IMproved' in line for line in lines)
    assert lines[-1].strip() == ''


def test_send_keys(vim):
    vim.send_keys('ispam\033')
    assert 'spam' in vim.display_lines()[0]


def test_install_plugin(vim, plugin_dir, plugin_entry_script):
    vim.install_plugin(plugin_dir, plugin_entry_script)
    assert plugin_dir in vim.runtimepath
    assert vim.command('Spam') == 'spam'


def test_command(vim):
    message = 'spam'
    assert vim.command('echo "{0}"'.format(message)) == message


def test_echo(vim):
    message = 'spam'
    assert vim.echo('"{0}"'.format(message)) == message


def test_set_mode(vim):
    vim.set_mode('insert')
    vim.send_keys('spam')
    vim.set_mode('normal')
    assert vim.display_lines()[0].strip() == 'spam'
    vim.set_mode('visual')
    vim.send_keys('0yP')
    assert vim.display_lines()[0].strip() == 'spamspam'
    vim.send_keys('yyp')
    vim.set_mode('visual-block')
    vim.send_keys('kIspam')
    assert vim.display_lines()[0].strip() == 'spamspamspam'


def test_set_mode_invalid(vim):
    with pytest.raises(ValueError):
        vim.set_mode('invalid-mode')


def test_set_mode_setter(vim):
    vim.mode = 'insert'
    vim.send_keys('spam')
    vim.mode = 'normal'
    assert vim.display_lines()[0].strip() == 'spam'


def test_executable(vim):
    assert 'vim' in vim.executable
    assert os.path.isabs(vim.executable)


def test_args(vim):
    assert '-u' in vim.args


def test_encoding(vim):
    assert vim.encoding.lower() == 'utf-8'


def test_screen_size(vim):
    assert vim.screen_size == (80, 24)


def test_screen_size_setter(vim):
    screen_size = (120, 32)
    vim.screen_size = screen_size
    assert vim.screen_size == screen_size
    assert all(len(line) == screen_size[0] for line in vim.display_lines())


def test_timeout(vim):
    assert 0 < vim.timeout < 1


def test_timeout_setter(vim):
    vim.timeout = 10
    assert vim.timeout == 10


repository: z1r00/dia2ol
file: diaphora/extras/diaphora_local.py
description: online diff
wanted_name: Diaphora: Diff Local Function
comment: Locally diff functions
created_at: 2024-12-11 02:37:26+00:00
pushed_at: 2024-12-31 02:47:04+00:00
url:
https://github.com/z1r00/dia2ol/blob/45e3fd5345e13ce389b51c2d6dcf21f30cb16920/diaphora/extras/diaphora_local.py

content:
#!/usr/bin/python3

"""
Diaphora, a binary diffing tool
Copyright (c) 2015-2024, Joxean Koret

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import re
import os
import sys
import time
import difflib

import idc
import idaapi
import idautils

from PyQt5 import QtWidgets
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import NasmLexer, CppLexer, DiffLexer

#-------------------------------------------------------------------------------
DIFF_COLOR_ADDED      = "#aaffaa"
DIFF_COLOR_CHANGED    = "#ffff77"
DIFF_COLOR_SUBTRACTED = "#ffaaaa"
DIFF_COLOR_LINE_NO    = "#e0e0e0"

#-------------------------------------------------------------------------------
def log(msg):
  idaapi.msg(f"[{time.asctime()}] {msg}")

#-------------------------------------------------------------------------------
def do_decompile(f):
  return idaapi.decompile(f, flags=idaapi.DECOMP_NO_WAIT)

#-------------------------------------------------------------------------------
class CHtmlDiff:
  """A replacement for difflib.HtmlDiff that tries to enforce a max width

  The main challenge is to do this given QTextBrowser's limitations. In
  particular, QTextBrowser only implements a minimum of CSS.
  """

  _html_template = """
  <html>
  <head>
  <style>%(style)s</style>
  </head>
  <body>
  <table class="diff_tab" cellspacing=0>
  %(rows)s
  </table>
  </body>
  </html>
  """

  _style = (
    """
  table.diff_tab {
  font-family: Courier, monospace;
  table-layout: fixed;
  width: 100%;
  }

  .diff_add {
  background-color: """
    + DIFF_COLOR_ADDED
    + """;
  }
  .diff_chg {
  background-color: """
    + DIFF_COLOR_CHANGED
    + """;
  }
  .diff_sub {
  background-color: """
    + DIFF_COLOR_SUBTRACTED
    + """;
  }
  .diff_lineno {
  text-align: right;
  background-color: """
    + DIFF_COLOR_LINE_NO
    + """;
  }
  """
  )

  _row_template = """
  <tr>
    <td class="diff_lineno" width="auto">%s</td>
    <td class="diff_play" nowrap width="45%%">%s</td>
    <td class="diff_lineno" width="auto">%s</td>
    <td class="diff_play" nowrap width="45%%">%s</td>
  </tr>
  """

  _rexp_too_much_space = re.compile("^\t[.\\w]+ {8}")

  def make_file(self, lhs, rhs, fmt, lex):
    rows = []
    for left, right, changed in difflib._mdiff(lhs, rhs):
      lno, ltxt = left
      rno, rtxt = right

      if not changed:
        ltxt = highlight(ltxt, lex, fmt)
        rtxt = highlight(rtxt, lex, fmt)
      else:
        ltxt = self._stop_wasting_space(ltxt)
        rtxt = self._stop_wasting_space(rtxt)

        ltxt = ltxt.replace(" ", "&nbsp;")
        rtxt = rtxt.replace(" ", "&nbsp;")
        ltxt = ltxt.replace("<", "&lt;")
        ltxt = ltxt.replace(">", "&gt;")
        rtxt = rtxt.replace("<", "&lt;")
        rtxt = rtxt.replace(">", "&gt;")

      row = self._row_template % (str(lno), ltxt, str(rno), rtxt)
      rows.append(row)

    all_the_rows = "\n".join(rows)
    all_the_rows = (
      all_the_rows.replace("\x00+", '<span class="diff_add">')
      .replace("\x00-", '<span class="diff_sub">')
      .replace("\x00^", '<span class="diff_chg">')
      .replace("\x01", "</span>")
      .replace("\t", 4 * "&nbsp;")
    )

    res = self._html_template % {"style": self._style, "rows": all_the_rows}
    return res

  def _stop_wasting_space(self, s):
    """I never understood why you'd want to have 13 spaces between instruction and args'"""
    m = self._rexp_too_much_space.search(s)
    if m:
      mlen = len(m.group(0))
      return s[: mlen - 4] + s[mlen:]
    else:
      return s

#-------------------------------------------------------------------------------
class CHtmlViewer(idaapi.PluginForm):
  """
  Class used to graphically show the differences.
  """

  def OnCreate(self, form):
    self.parent = self.FormToPyQtWidget(form)
    self.PopulateForm()

    self.browser = None
    self.layout = None
    return 1

  def PopulateForm(self):
    self.layout = QtWidgets.QVBoxLayout()
    self.browser = QtWidgets.QTextBrowser()
    self.browser.setLineWrapMode(QtWidgets.QTextEdit.FixedColumnWidth)
    self.browser.setLineWrapColumnOrWidth(150)
    self.browser.setHtml(self.text)
    self.browser.setReadOnly(True)
    self.layout.addWidget(self.browser)
    self.parent.setLayout(self.layout)

  def Show(self, text, title):
    self.text = text
    return idaapi.PluginForm.Show(self, title)

#-------------------------------------------------------------------------------
def decompile_and_get(ea):
  decompiler_plugin = os.getenv("DIAPHORA_DECOMPILER_PLUGIN")
  if decompiler_plugin is None:
    decompiler_plugin = "hexrays"
  if not idaapi.init_hexrays_plugin() and not (
    load_plugin(decompiler_plugin) and idaapi.init_hexrays_plugin()
  ):
    return False

  f = idaapi.get_func(ea)
  if f is None:
    return False

  cfunc = do_decompile(f)
  if cfunc is None:
    # Failed to decompile
    return False

  sv = cfunc.get_pseudocode()
  lines = []
  first_line = None
  for sline in sv:
    line = idaapi.tag_remove(sline.line)
    if line.startswith("//"):
      continue

    if first_line is None:
      first_line = line
    else:
      lines.append(line)

  return first_line, "\n".join(lines)

#-------------------------------------------------------------------------------
def get_disasm(ea):
  mnem = idc.print_insn_mnem(ea)
  op1 = idc.print_operand(ea, 0)
  op2 = idc.print_operand(ea, 1)
  line = f"{mnem.ljust(8)} {op1}"
  if op2 != "":
    line += f", {op2}"
  return line

#-------------------------------------------------------------------------------
def get_assembly(ea):
  f = int(ea)
  func = idaapi.get_func(f)
  if not func:
    log("Cannot get a function object for 0x%x" % f)
    return False

  lines = []
  flow = idaapi.FlowChart(func)
  for block in flow:
    if block.end_ea == 0 or block.end_ea == idaapi.BADADDR:
      log("0x%08x: Skipping bad basic block" % f)
      continue

    if block.start_ea != func.start_ea:
      lines.append("loc_%08x:" % (block.start_ea))
    for head in idautils.Heads(block.start_ea, block.end_ea):
      lines.append("     %s" % (get_disasm(head)))

  return "\n".join(lines)

#-------------------------------------------------------------------------------
class CLocalDiffer:
  def __init__(self):
    pass

  def get_pseudo_diff_data(self, ea1, ea2):
    html_diff = CHtmlDiff()
    tmp = decompile_and_get(int(ea1))
    if not tmp:
      log("[i] Cannot get the pseudo-code for the current function")
      return False
    proto1, tmp1 = tmp
    buf1 = proto1 + "\n" + tmp1

    tmp = decompile_and_get(int(ea2))
    if not tmp:
      log("Cannot get the pseudo-code for the second function")
      return False
    proto2, tmp2 = tmp
    buf2 = proto2 + "\n" + tmp2

    if buf1 == buf2:
      warning("Both pseudo-codes are equal.")

    fmt = HtmlFormatter()
    fmt.noclasses = True
    fmt.linenos = False
    fmt.nobackground = True
    src = html_diff.make_file(
      buf1.split("\n"), buf2.split("\n"), fmt, CppLexer()
    )

    name1 = idaapi.get_func_name(int(ea1))
    name2 = idaapi.get_func_name(int(ea2))
    title = f'Diff pseudo-code {name1} - {name2}'
    res = (src, title)
    return res

  def get_asm_diff_data(self, ea1, ea2):
    html_diff = CHtmlDiff()
    asm1 = get_assembly(ea1)
    asm2 = get_assembly(ea2)
    name1 = idaapi.get_func_name(int(ea1))
    name2 = idaapi.get_func_name(int(ea2))
    buf1 = f'{name1} proc near\n{asm1}\n{name1} endp'
    buf2 = f'{name2} proc near\n{asm2}\n{name2} endp'

    fmt = HtmlFormatter()
    fmt.noclasses = True
    fmt.linenos = False
    fmt.nobackground = True
    src = html_diff.make_file(
      buf1.split("\n"), buf2.split("\n"), fmt, NasmLexer()
    )

    title = f"Diff assembly {name1} - {name2}"
    res = (src, title)
    return res

  def diff_pseudo(self, main_ea, diff_ea):
    res = self.get_pseudo_diff_data(main_ea, diff_ea)
    self.show_res(res)

  def diff_assembly(self, main_ea, diff_ea):
    res = self.get_asm_diff_data(main_ea, diff_ea)
    self.show_res(res)

  def show_res(self, res):
    if res:
      (src, title) = res
      cdiffer = CHtmlViewer()
      cdiffer.Show(src, title)

  def diff(self, main_ea, diff_ea):
    self.diff_assembly(main_ea, diff_ea)
    self.diff_pseudo(main_ea, diff_ea)

#-------------------------------------------------------------------------------
class myplugin_t(idaapi.plugin_t):
  flags = idaapi.PLUGIN_UNL
  comment = "Locally diff functions"
  help = "Tool to diff functions inside this database"
  wanted_name = "Diaphora: Diff Local Function"
  wanted_hotkey = "Ctrl+Shift+D"

  def init(self):
    return idaapi.PLUGIN_OK

  def run(self, arg):
    main()

  def term(self):
    pass

def PLUGIN_ENTRY():
  return myplugin_t()

#-------------------------------------------------------------------------------
def main():
  ea = idc.get_screen_ea()
  func = idaapi.get_func(ea)
  if func is None:
    warning("Please place the cursor over a function before calling this plugin.")
    return

  func_name = idaapi.get_func_name(ea)
  line = f"Select the function to diff {func_name} against"
  diff_ea = idc.choose_func(line)
  if diff_ea == idaapi.BADADDR:
    return

  log("Selected function address 0x%08x\n" % diff_ea)
  differ = CLocalDiffer()
  differ.diff(ea, diff_ea)

if __name__ == "__main__":
  main()


repository: malefs/security-smell-detector-python-gist
file: gist-src/b5e7a80691ed5e44e7fea1964bae18dc.py
wanted_name: DynHints
comment: Display dynamically-generated hints.
created_at: 2019-11-23 19:14:08+00:00
pushed_at: 2020-07-26 03:38:04+00:00
url:
https://github.com/malefs/security-smell-detector-python-gist/blob/e90764deb06ae4d3c45e702db7ad00351520348f/gist-src/b5e7a80691ed5e44e7fea1964bae18dc.py

content:
'''
example of:
  - using ctypes with the IDA SDK
  - 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 ctypes

import idc
import idaapi
import idautils


def get_ida_ctypes():
    '''
    get the ida sdk dll.
    
    Args: None

    Returns:
      ctypes.CDLL: the IDA SDK DLL

    via: http://www.hexblog.com/?p=695
    '''
    idaname = 'ida64' if __EA64__ else 'ida'
    if sys.platform == 'win32':
        return ctypes.windll[idaname + '.wll']
    elif sys.platform == 'linux2':
        return ctypes.cdll['lib' + idaname + '.so']
    elif sys.platform == 'darwin':
        return ctypes.cdll['lib' + idaname + '.dylib']
    else:
        raise RuntimeError('unknown platform: ' + sys.platform)


c_long_p = ctypes.POINTER(ctypes.c_long)
c_int_p = ctypes.POINTER(ctypes.c_int)
c_char_pp = ctypes.POINTER(ctypes.c_char_p)
    

# ctypes declaration for the `hook_type_t` type.
#
# the idaapi calling convention is stdcall, so we use WINFUNCTYPE;
#  it is *not* cdecl (ctypes.CFUNCTYPE).
#
# relevant idasdk documentation: 
#
#    typedef int idaapi hook_cb_t(
#      void *user_data,
#      int notification_code,
#      va_list va);
HookCb = ctypes.WINFUNCTYPE(
    # return type
    ctypes.c_int,      # int idaapi

    # argument types
    ctypes.c_void_p,   # void       *user_data
    ctypes.c_int,      # int         notification_code
    ctypes.c_void_p,   # va_list     va 
)


class HOOK_TYPES:
    '''
    types of events that be hooked to with hook_to_notification_point().
    this corresponds to `hook_type_t`.
    '''
    # Hook to the processor module.
    HT_IDP = 0

    # Hook to the user interface.
    HT_UI = 1

    # Hook to the debugger.
    HT_DBG = 2

    # Hook to the database events.
    HT_IDB = 3

    # Internal debugger events.
    HT_DEV = 4

    # Custom/IDA views notifications.
    HT_VIEW = 5

    # Output window notifications.
    HT_OUTPUT = 6

    HT_LAST = 7


def do_hook_to_notification_point(dll, hook_type, cb, user_data):
    '''
    register a callback for a class of events in IDA.
    
    Args:
      dll (ctypes.CDLL): the IDA SDK DLL
      hook_type (int): hook type from HOOK_TYPES enum
      cb (HookCb): the callback to register
      user_data (ctypes.c_void_p): context provided to callback when invoked
    
    Returns: None

    relevant idasdk documentation: 

        idaman bool ida_export 	
        hook_to_notification_point (
            hook_type_t hook_type,
            hook_cb_t *cb,
            void *user_data)
   '''
    
    hook_to_notification_point = dll.hook_to_notification_point
    hook_to_notification_point.argtypes = [
        ctypes.c_int,      # hook_type_t hook_type
        HookCb,            # hook_cb_t  *cb
        ctypes.c_void_p,   # void       *user_data
    ]

    hook_to_notification_point(hook_type, cb, user_data)


def do_unhook_from_notification_point(dll, hook_type, cb, user_data):
    '''
    unregister a callback.
    
    Args:
      dll (ctypes.CDLL): the IDA SDK DLL
      hook_type (int): hook type from HOOK_TYPES enum
      cb (HookCb): the callback to register
      user_data (ctypes.c_void_p): context provided to callback when invoked
    
    Returns: None

    relevant idasdk documentation: 

        idaman bool ida_export 	
        unhook_from_notification_point (
            hook_type_t hook_type,
            hook_cb_t *cb,
            void *user_data = NULL)
   '''
    
    unhook_from_notification_point = dll.unhook_from_notification_point
    unhook_from_notification_point.argtypes = [
        ctypes.c_int,      # hook_type_t hook_type
        HookCb,            # hook_cb_t  *cb
        ctypes.c_void_p,   # void       *user_data
    ]

    unhook_from_notification_point(hook_type, cb, user_data)


class UI_NOTIFICATIONS:

    # get single line hint for the given address.
    # this is *not* used for the disasm listing hover tooltips.
    # this is used for nav bar hover tooltips.
    # 
    # relevant idasdk documentation: 
    #
    #     cb: ui wants to display a simple hint for an address.
    #     Use this event to generate a custom hint
    #     See also more generic ::ui_get_item_hint
    #     \param ea       (::ea_t)
    #     \param buf      (char *)
    #     \param bufsize  (size_t)
    #     \return true if generated a hint
    UI_GET_EA_HINT = 79

    # i'm not sure where this is called.
    #
    # relevant idasdk documentation: 
    #
    #     cb: ui wants to display multiline hint for an item.
    #     See also more generic ::ui_get_custom_viewer_hint
    #     \param ea (ea_t, or item id like a structure or enum member)
    #     \param max_lines             (int) maximal number of lines
    #     \param[out] important_lines  (int *) number of important lines. if zero, output is ignored
    #     \param[out] hint             (::qstring *) the output string
    #     \return true if generated a hint
    UI_GET_ITEM_HINT = 80

    # get multiple line hint for the given address
    # this is invoked for at least the following views:
    #   - disasm
    #   - enums
    #   - structures
    # 
    # relevant idasdk documentation: 
    #
    #     cb: ui wants to display a hint for a viewer (idaview or custom).
    #     \param viewer (TCustomControl*) viewer
    #     \param place                 (::place_t *) current position in the viewer
    #     \param[out] important_lines  (int *) number of important lines.
    #                                         if zero, the result is ignored
    #     \param[out] hint             (::qstring *) the output string
    #     \return true if generated a hint
    UI_GET_CUSTOM_VIEWER_HINT = 91


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

    help = "Display dynamically-generated hints."
    wanted_name = "DynHints"
    wanted_hotkey = "Ctrl-]"

    def init(self):
        import datetime
        self.dll = get_ida_ctypes()

        # can't use a bound method as a callback (since `self` doesn't get provided)
        #  so we'll create a closure that has access to `self`.
        # 
        # via: http://stackoverflow.com/a/7261524/87207
        def cb(user_data, notification_code, va_list):
            '''
            example hook_cb_t function that handles custom viewer hints.

            Args:
              user_data (ctypes.c_void_p): context supplied to callback registration
              notification_code (int): one of the UI_NOTIFICATIONS enum values
              va_list (ctypes.c_void_p): varargs that must be manually parsed

            Returns:
              int: see notifiication code documentation for interpretation

            Notes:
              - This is a closure that expects to have 
            '''

            # this function is called *a lot*, so don't do any heavy lifting
            #  until you know its the event you want.

            # ctypes doesn't support varargs in callback functions.
            # so, we need to parse the remaining arguments ourselves.
            #
            # on windows, varargs are sequential stack locations.
            # so, lets access the members like an array of ints/pointers.
            #
            # WARNING: the following section that manually parses varargs is
            #  probably architecture and platfrom dependent!
            va_list = ctypes.cast(va_list, c_long_p)

            if notification_code == UI_NOTIFICATIONS.UI_GET_EA_HINT:
                # ea is just a number: 
                #
                #     typedef uint32 	ea_t
                #
                # via: https://www.hex-rays.com/products/ida/support/sdkdoc/pro_8h.html#a7b0aeaed04e477c02cf8ea3452002d1a
                ea = va_list[0]
                buf = ctypes.cast(va_list[1], ctypes.c_char_p)
                bufsize = va_list[2]

                print('ui_get_ea_hint:')
                print('>.. notification code: %s' % (notification_code))
                print('>.. ea: %s' % (hex(ea)))
                print('>.. buf: %s' % (buf))
                print('>.. bufsize: %s' % (hex(bufsize)))

                the_hint = datetime.datetime.now().isoformat(' ')

                self.dll.qstrncpy(buf, ctypes.c_char_p(the_hint), bufsize)
                print('<.. buf: %s' % (buf))

                return 1

            elif notification_code == UI_NOTIFICATIONS.UI_GET_CUSTOM_VIEWER_HINT:
                viewer = ctypes.cast(va_list[0], c_long_p)
                place = ctypes.cast(va_list[1], c_long_p)
                important_lines = ctypes.cast(va_list[2], c_long_p)
                hint = ctypes.cast(va_list[3], c_char_pp)

                if not place:
                    print('ui_get_custom_viewer_hint: invalid place')
                    return 0

                print('ui_get_custom_viewer_hint:')
                print('>.. notification code: %s' % (notification_code))
                print('>.. important lines: %s %s' % (important_lines, important_lines.contents))
                print('>.. hint: %s %s' % (hint, hint.contents))

                # so, we'd like to fetch the EA of the current view.
                # ideally, we'd do:
                #
                #     ea = place.toea()
                #
                # but `place` is a raw c++ object pointer, and ctypes isn't that smart.
                # next best would be to do something like:
                #
                #     place = self.dll.get_custom_viewer_place(viewer);
                #
                # however, this doesn't work because `get_custom_viewer_place` is not an exported routine.
                # it seems to be part of the IDA SDK static lib to which plugins link.
                #
                # next best would be to use `idaapi.get_custom_viewer_place`:
                #
                #     place = idaapi.get_custom_viewer_place(viewer);
                #
                # but, this doesn't work because we're mixing a ctypes pointer with a swig function.
                # so, we'll fall back to querying the current viewer, and fetching the place from there.

                # let's only display for disassembly listings
                #
                # i only know how to test the view/form type using the `get_tform_type` function.
                # therefore, we'll first query the current tform, and subsequently the current custom_viewer.
                tform = idaapi.get_current_tform()
                if idaapi.get_tform_type(tform) != idaapi.BWN_DISASM:
                    return 0

                viewer = idaapi.get_current_viewer()

                # `place` is a tuple (though techincally, a list), with elements:
                #  - place_t proxy
                #  - x position in characters
                #  - y position in characters from top of screen/form (-1 in graph view)
                place, x, y = idaapi.get_custom_viewer_place(viewer, True)

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

                important_lines[0] = ctypes.c_long(1)
                # we don't have access to the qstring c++ class methods,
                #  so we'll use a dummy routine to correctly set our qstring contents.
                # `replace_tabs` assigns from a char * to a qstring *.
                #
                # relevant idasdk documentation: 
                #
                #    idaman THREAD_SAFE bool ida_export
                #    replace_tabs (
                #        qstring *out,
                #        const char *str,
                #        int tabsize)
                self.dll.replace_tabs(hint, ctypes.c_char_p(the_hint), 4)

                print('<.. important lines: %s %s' % (important_lines, important_lines.contents))
                print('<.. hint: %s %s' % (hint, hint.contents))

                return 1

            return 0


        # need to keep a ref around, or the function gets garbage collected
        self.cb = HookCb(cb)

        # need to keep a ref around, or the param gets garbage collected
        self.ctx = ctypes.c_long(69)

        return idaapi.PLUGIN_OK

    def run(self, arg):
        print('hints: run')
        do_hook_to_notification_point(self.dll, HOOK_TYPES.HT_UI, self.cb, ctypes.byref(self.ctx))

    def term(self):
        print('hints: term')
        do_unhook_from_notification_point(self.dll, HOOK_TYPES.HT_UI, self.cb, ctypes.byref(self.ctx))


def PLUGIN_ENTRY():
    return DynHintsPlugin()


repository: malefs/security-smell-detector-python-gist
file: gist-hash/b5e7a80691ed5e44e7fea1964bae18dc/snippet.py
wanted_name: DynHints
comment: Display dynamically-generated hints.
created_at: 2019-11-23 19:14:08+00:00
pushed_at: 2020-07-26 03:38:04+00:00
url:
https://github.com/malefs/security-smell-detector-python-gist/blob/e90764deb06ae4d3c45e702db7ad00351520348f/gist-hash/b5e7a80691ed5e44e7fea1964bae18dc/snippet.py

content:
'''
example of:
  - using ctypes with the IDA SDK
  - 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 ctypes

import idc
import idaapi
import idautils


def get_ida_ctypes():
    '''
    get the ida sdk dll.
    
    Args: None

    Returns:
      ctypes.CDLL: the IDA SDK DLL

    via: http://www.hexblog.com/?p=695
    '''
    idaname = 'ida64' if __EA64__ else 'ida'
    if sys.platform == 'win32':
        return ctypes.windll[idaname + '.wll']
    elif sys.platform == 'linux2':
        return ctypes.cdll['lib' + idaname + '.so']
    elif sys.platform == 'darwin':
        return ctypes.cdll['lib' + idaname + '.dylib']
    else:
        raise RuntimeError('unknown platform: ' + sys.platform)


c_long_p = ctypes.POINTER(ctypes.c_long)
c_int_p = ctypes.POINTER(ctypes.c_int)
c_char_pp = ctypes.POINTER(ctypes.c_char_p)
    

# ctypes declaration for the `hook_type_t` type.
#
# the idaapi calling convention is stdcall, so we use WINFUNCTYPE;
#  it is *not* cdecl (ctypes.CFUNCTYPE).
#
# relevant idasdk documentation: 
#
#    typedef int idaapi hook_cb_t(
#      void *user_data,
#      int notification_code,
#      va_list va);
HookCb = ctypes.WINFUNCTYPE(
    # return type
    ctypes.c_int,      # int idaapi

    # argument types
    ctypes.c_void_p,   # void       *user_data
    ctypes.c_int,      # int         notification_code
    ctypes.c_void_p,   # va_list     va 
)


class HOOK_TYPES:
    '''
    types of events that be hooked to with hook_to_notification_point().
    this corresponds to `hook_type_t`.
    '''
    # Hook to the processor module.
    HT_IDP = 0

    # Hook to the user interface.
    HT_UI = 1

    # Hook to the debugger.
    HT_DBG = 2

    # Hook to the database events.
    HT_IDB = 3

    # Internal debugger events.
    HT_DEV = 4

    # Custom/IDA views notifications.
    HT_VIEW = 5

    # Output window notifications.
    HT_OUTPUT = 6

    HT_LAST = 7


def do_hook_to_notification_point(dll, hook_type, cb, user_data):
    '''
    register a callback for a class of events in IDA.
    
    Args:
      dll (ctypes.CDLL): the IDA SDK DLL
      hook_type (int): hook type from HOOK_TYPES enum
      cb (HookCb): the callback to register
      user_data (ctypes.c_void_p): context provided to callback when invoked
    
    Returns: None

    relevant idasdk documentation: 

        idaman bool ida_export 	
        hook_to_notification_point (
            hook_type_t hook_type,
            hook_cb_t *cb,
            void *user_data)
   '''
    
    hook_to_notification_point = dll.hook_to_notification_point
    hook_to_notification_point.argtypes = [
        ctypes.c_int,      # hook_type_t hook_type
        HookCb,            # hook_cb_t  *cb
        ctypes.c_void_p,   # void       *user_data
    ]

    hook_to_notification_point(hook_type, cb, user_data)


def do_unhook_from_notification_point(dll, hook_type, cb, user_data):
    '''
    unregister a callback.
    
    Args:
      dll (ctypes.CDLL): the IDA SDK DLL
      hook_type (int): hook type from HOOK_TYPES enum
      cb (HookCb): the callback to register
      user_data (ctypes.c_void_p): context provided to callback when invoked
    
    Returns: None

    relevant idasdk documentation: 

        idaman bool ida_export 	
        unhook_from_notification_point (
            hook_type_t hook_type,
            hook_cb_t *cb,
            void *user_data = NULL)
   '''
    
    unhook_from_notification_point = dll.unhook_from_notification_point
    unhook_from_notification_point.argtypes = [
        ctypes.c_int,      # hook_type_t hook_type
        HookCb,            # hook_cb_t  *cb
        ctypes.c_void_p,   # void       *user_data
    ]

    unhook_from_notification_point(hook_type, cb, user_data)


class UI_NOTIFICATIONS:

    # get single line hint for the given address.
    # this is *not* used for the disasm listing hover tooltips.
    # this is used for nav bar hover tooltips.
    # 
    # relevant idasdk documentation: 
    #
    #     cb: ui wants to display a simple hint for an address.
    #     Use this event to generate a custom hint
    #     See also more generic ::ui_get_item_hint
    #     \param ea       (::ea_t)
    #     \param buf      (char *)
    #     \param bufsize  (size_t)
    #     \return true if generated a hint
    UI_GET_EA_HINT = 79

    # i'm not sure where this is called.
    #
    # relevant idasdk documentation: 
    #
    #     cb: ui wants to display multiline hint for an item.
    #     See also more generic ::ui_get_custom_viewer_hint
    #     \param ea (ea_t, or item id like a structure or enum member)
    #     \param max_lines             (int) maximal number of lines
    #     \param[out] important_lines  (int *) number of important lines. if zero, output is ignored
    #     \param[out] hint             (::qstring *) the output string
    #     \return true if generated a hint
    UI_GET_ITEM_HINT = 80

    # get multiple line hint for the given address
    # this is invoked for at least the following views:
    #   - disasm
    #   - enums
    #   - structures
    # 
    # relevant idasdk documentation: 
    #
    #     cb: ui wants to display a hint for a viewer (idaview or custom).
    #     \param viewer (TCustomControl*) viewer
    #     \param place                 (::place_t *) current position in the viewer
    #     \param[out] important_lines  (int *) number of important lines.
    #                                         if zero, the result is ignored
    #     \param[out] hint             (::qstring *) the output string
    #     \return true if generated a hint
    UI_GET_CUSTOM_VIEWER_HINT = 91


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

    help = "Display dynamically-generated hints."
    wanted_name = "DynHints"
    wanted_hotkey = "Ctrl-]"

    def init(self):
        import datetime
        self.dll = get_ida_ctypes()

        # can't use a bound method as a callback (since `self` doesn't get provided)
        #  so we'll create a closure that has access to `self`.
        # 
        # via: http://stackoverflow.com/a/7261524/87207
        def cb(user_data, notification_code, va_list):
            '''
            example hook_cb_t function that handles custom viewer hints.

            Args:
              user_data (ctypes.c_void_p): context supplied to callback registration
              notification_code (int): one of the UI_NOTIFICATIONS enum values
              va_list (ctypes.c_void_p): varargs that must be manually parsed

            Returns:
              int: see notifiication code documentation for interpretation

            Notes:
              - This is a closure that expects to have 
            '''

            # this function is called *a lot*, so don't do any heavy lifting
            #  until you know its the event you want.

            # ctypes doesn't support varargs in callback functions.
            # so, we need to parse the remaining arguments ourselves.
            #
            # on windows, varargs are sequential stack locations.
            # so, lets access the members like an array of ints/pointers.
            #
            # WARNING: the following section that manually parses varargs is
            #  probably architecture and platfrom dependent!
            va_list = ctypes.cast(va_list, c_long_p)

            if notification_code == UI_NOTIFICATIONS.UI_GET_EA_HINT:
                # ea is just a number: 
                #
                #     typedef uint32 	ea_t
                #
                # via: https://www.hex-rays.com/products/ida/support/sdkdoc/pro_8h.html#a7b0aeaed04e477c02cf8ea3452002d1a
                ea = va_list[0]
                buf = ctypes.cast(va_list[1], ctypes.c_char_p)
                bufsize = va_list[2]

                print('ui_get_ea_hint:')
                print('>.. notification code: %s' % (notification_code))
                print('>.. ea: %s' % (hex(ea)))
                print('>.. buf: %s' % (buf))
                print('>.. bufsize: %s' % (hex(bufsize)))

                the_hint = datetime.datetime.now().isoformat(' ')

                self.dll.qstrncpy(buf, ctypes.c_char_p(the_hint), bufsize)
                print('<.. buf: %s' % (buf))

                return 1

            elif notification_code == UI_NOTIFICATIONS.UI_GET_CUSTOM_VIEWER_HINT:
                viewer = ctypes.cast(va_list[0], c_long_p)
                place = ctypes.cast(va_list[1], c_long_p)
                important_lines = ctypes.cast(va_list[2], c_long_p)
                hint = ctypes.cast(va_list[3], c_char_pp)

                if not place:
                    print('ui_get_custom_viewer_hint: invalid place')
                    return 0

                print('ui_get_custom_viewer_hint:')
                print('>.. notification code: %s' % (notification_code))
                print('>.. important lines: %s %s' % (important_lines, important_lines.contents))
                print('>.. hint: %s %s' % (hint, hint.contents))

                # so, we'd like to fetch the EA of the current view.
                # ideally, we'd do:
                #
                #     ea = place.toea()
                #
                # but `place` is a raw c++ object pointer, and ctypes isn't that smart.
                # next best would be to do something like:
                #
                #     place = self.dll.get_custom_viewer_place(viewer);
                #
                # however, this doesn't work because `get_custom_viewer_place` is not an exported routine.
                # it seems to be part of the IDA SDK static lib to which plugins link.
                #
                # next best would be to use `idaapi.get_custom_viewer_place`:
                #
                #     place = idaapi.get_custom_viewer_place(viewer);
                #
                # but, this doesn't work because we're mixing a ctypes pointer with a swig function.
                # so, we'll fall back to querying the current viewer, and fetching the place from there.

                # let's only display for disassembly listings
                #
                # i only know how to test the view/form type using the `get_tform_type` function.
                # therefore, we'll first query the current tform, and subsequently the current custom_viewer.
                tform = idaapi.get_current_tform()
                if idaapi.get_tform_type(tform) != idaapi.BWN_DISASM:
                    return 0

                viewer = idaapi.get_current_viewer()

                # `place` is a tuple (though techincally, a list), with elements:
                #  - place_t proxy
                #  - x position in characters
                #  - y position in characters from top of screen/form (-1 in graph view)
                place, x, y = idaapi.get_custom_viewer_place(viewer, True)

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

                important_lines[0] = ctypes.c_long(1)
                # we don't have access to the qstring c++ class methods,
                #  so we'll use a dummy routine to correctly set our qstring contents.
                # `replace_tabs` assigns from a char * to a qstring *.
                #
                # relevant idasdk documentation: 
                #
                #    idaman THREAD_SAFE bool ida_export
                #    replace_tabs (
                #        qstring *out,
                #        const char *str,
                #        int tabsize)
                self.dll.replace_tabs(hint, ctypes.c_char_p(the_hint), 4)

                print('<.. important lines: %s %s' % (important_lines, important_lines.contents))
                print('<.. hint: %s %s' % (hint, hint.contents))

                return 1

            return 0


        # need to keep a ref around, or the function gets garbage collected
        self.cb = HookCb(cb)

        # need to keep a ref around, or the param gets garbage collected
        self.ctx = ctypes.c_long(69)

        return idaapi.PLUGIN_OK

    def run(self, arg):
        print('hints: run')
        do_hook_to_notification_point(self.dll, HOOK_TYPES.HT_UI, self.cb, ctypes.byref(self.ctx))

    def term(self):
        print('hints: term')
        do_unhook_from_notification_point(self.dll, HOOK_TYPES.HT_UI, self.cb, ctypes.byref(self.ctx))


def PLUGIN_ENTRY():
    return DynHintsPlugin()


repository: PyEGo/exp-gist
file: hard-gists-me/b5e7a80691ed5e44e7fea1964bae18dc/snippet.py
description: Experiment results on hard-gists dataset
wanted_name: DynHints
comment: Display dynamically-generated hints.
created_at: 2021-04-18 11:16:06+00:00
pushed_at: 2022-11-18 17:22:11+00:00
url:
https://github.com/PyEGo/exp-gist/blob/bb8212e435ab525bb8ebdee2f620f850133a9b99/hard-gists-me/b5e7a80691ed5e44e7fea1964bae18dc/snippet.py


repository: PyEGo/exp-gist
file: hard-gists-ego/b5e7a80691ed5e44e7fea1964bae18dc/snippet.py
description: Experiment results on hard-gists dataset
wanted_name: DynHints
comment: Display dynamically-generated hints.
created_at: 2021-04-18 11:16:06+00:00
pushed_at: 2022-11-18 17:22:11+00:00
url:
https://github.com/PyEGo/exp-gist/blob/bb8212e435ab525bb8ebdee2f620f850133a9b99/hard-gists-ego/b5e7a80691ed5e44e7fea1964bae18dc/snippet.py


repository: PyEGo/exp-gist
file: hard-gists-me/8bfa8e25f36e6f619247203d1bc0291b/snippet.py
description: Experiment results on hard-gists dataset
wanted_name: AddSegment
comment: Add a segment to an IDA .idb from a file.
created_at: 2021-04-18 11:16:06+00:00
pushed_at: 2022-11-18 17:22:11+00:00
url:
https://github.com/PyEGo/exp-gist/blob/bb8212e435ab525bb8ebdee2f620f850133a9b99/hard-gists-me/8bfa8e25f36e6f619247203d1bc0291b/snippet.py

content:
'''
IDAPython plugin that adds the contents of a file as a new segment in an existing idb.
Prompts the user for:
  - file path
  - segment name
  - segment starting offset
  
Useful for reversing engineering packed software and shellcode.

Author: Willi Ballenthin <william.ballenthin@fireeye.com>
Licence: Apache 2.0
'''
import logging
from collections import namedtuple

import idc
import idaapi
import idautils


logger = logging.getLogger(__name__)


class BadInputError(Exception):
    pass


Segment = namedtuple('SegmentBuffer', ['path', 'name', 'addr'])


def prompt_for_segment():
    ''' :returns: a Segment instance, or raises BadInputError '''
    class MyForm(idaapi.Form):
        def __init__(self):
            idaapi.Form.__init__(self, """STARTITEM 0
add segment by buffer

<##buffer path:{path}>
<##segment name:{name}>
<##segment start address:{addr}>
""",
{
    'path': idaapi.Form.FileInput(open=True),
    'name': idaapi.Form.StringInput(),
    'addr': idaapi.Form.NumericInput(tp=Form.FT_ADDR),
})

        def OnFormChange(self, fid):
            return 1

    f = MyForm()
    f.Compile()
    f.path.value = ""
    f.name.value = ""
    f.addr.value = 0x0
    ok = f.Execute()
    if ok != 1:
        raise BadInputError('user cancelled')

    path = f.path.value
    if path == "" or path is None:
        raise BadInputError('bad path provided')

    if not os.path.exists(path):
        raise BadInputError('file doesn\'t exist')

    name = f.name.value
    if name == "" or name is None:
        raise BadInputError('bad name provided')

    addr = f.addr.value
    f.Free()
    return Segment(path, name, addr)


def main(argv=None):
    if argv is None:
        argv = sys.argv[:]

    try:
        seg = prompt_for_segment()
    except BadInputError:
        logger.error('bad input, exiting...')
        return -1

    with open(seg.path, 'rb') as f:
        buf = f.read()

    seglen = len(buf)
    if seglen % 0x1000 != 0:
        seglen = seglen + (0x1000 - (seglen % 0x1000))

    if not idc.AddSeg(seg.addr, seg.addr + seglen, 0, 1, 0, idaapi.scPub):
        logger.error('failed to add segment: 0x%x', seg.addr)
        return -1

    if not idc.RenameSeg(seg.addr, seg.name):
        logger.warning('failed to rename segment: %s', seg.name)

    if not idc.SetSegClass(seg.addr, 'CODE'):
        logger.warning('failed to set segment class CODE: %s', seg.name)

    if not idc.SegAlign(seg.addr, idc.saRelPara):
        logger.warning('failed to align segment: %s', seg.name)

    idaapi.patch_many_bytes(seg.addr, buf)
    

class AddSegmentPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = "Add a segment to an IDA .idb from a file."

    help = "Add a segment to an IDA .idb from a file."
    wanted_name = "AddSegment"
    wanted_hotkey = "Alt-F8"

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        main()

    def term(self):
        pass


def PLUGIN_ENTRY():
    return AddSegmentPlugin()


#if __name__ == '__main__':
#    logging.basicConfig(level=logging.DEBUG)
#    main()


repository: PyEGo/exp-gist
file: hard-gists-ego/8bfa8e25f36e6f619247203d1bc0291b/snippet.py
description: Experiment results on hard-gists dataset
wanted_name: AddSegment
comment: Add a segment to an IDA .idb from a file.
created_at: 2021-04-18 11:16:06+00:00
pushed_at: 2022-11-18 17:22:11+00:00
url:
https://github.com/PyEGo/exp-gist/blob/bb8212e435ab525bb8ebdee2f620f850133a9b99/hard-gists-ego/8bfa8e25f36e6f619247203d1bc0291b/snippet.py

content:
'''
IDAPython plugin that adds the contents of a file as a new segment in an existing idb.
Prompts the user for:
  - file path
  - segment name
  - segment starting offset
  
Useful for reversing engineering packed software and shellcode.

Author: Willi Ballenthin <william.ballenthin@fireeye.com>
Licence: Apache 2.0
'''
import logging
from collections import namedtuple

import idc
import idaapi
import idautils


logger = logging.getLogger(__name__)


class BadInputError(Exception):
    pass


Segment = namedtuple('SegmentBuffer', ['path', 'name', 'addr'])


def prompt_for_segment():
    ''' :returns: a Segment instance, or raises BadInputError '''
    class MyForm(idaapi.Form):
        def __init__(self):
            idaapi.Form.__init__(self, """STARTITEM 0
add segment by buffer

<##buffer path:{path}>
<##segment name:{name}>
<##segment start address:{addr}>
""",
{
    'path': idaapi.Form.FileInput(open=True),
    'name': idaapi.Form.StringInput(),
    'addr': idaapi.Form.NumericInput(tp=Form.FT_ADDR),
})

        def OnFormChange(self, fid):
            return 1

    f = MyForm()
    f.Compile()
    f.path.value = ""
    f.name.value = ""
    f.addr.value = 0x0
    ok = f.Execute()
    if ok != 1:
        raise BadInputError('user cancelled')

    path = f.path.value
    if path == "" or path is None:
        raise BadInputError('bad path provided')

    if not os.path.exists(path):
        raise BadInputError('file doesn\'t exist')

    name = f.name.value
    if name == "" or name is None:
        raise BadInputError('bad name provided')

    addr = f.addr.value
    f.Free()
    return Segment(path, name, addr)


def main(argv=None):
    if argv is None:
        argv = sys.argv[:]

    try:
        seg = prompt_for_segment()
    except BadInputError:
        logger.error('bad input, exiting...')
        return -1

    with open(seg.path, 'rb') as f:
        buf = f.read()

    seglen = len(buf)
    if seglen % 0x1000 != 0:
        seglen = seglen + (0x1000 - (seglen % 0x1000))

    if not idc.AddSeg(seg.addr, seg.addr + seglen, 0, 1, 0, idaapi.scPub):
        logger.error('failed to add segment: 0x%x', seg.addr)
        return -1

    if not idc.RenameSeg(seg.addr, seg.name):
        logger.warning('failed to rename segment: %s', seg.name)

    if not idc.SetSegClass(seg.addr, 'CODE'):
        logger.warning('failed to set segment class CODE: %s', seg.name)

    if not idc.SegAlign(seg.addr, idc.saRelPara):
        logger.warning('failed to align segment: %s', seg.name)

    idaapi.patch_many_bytes(seg.addr, buf)
    

class AddSegmentPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_KEEP
    comment = "Add a segment to an IDA .idb from a file."

    help = "Add a segment to an IDA .idb from a file."
    wanted_name = "AddSegment"
    wanted_hotkey = "Alt-F8"

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        main()

    def term(self):
        pass


def PLUGIN_ENTRY():
    return AddSegmentPlugin()


#if __name__ == '__main__':
#    logging.basicConfig(level=logging.DEBUG)
#    main()


repository: PyEGo/exp-gist
file: hard-gists-reqs/8bfa8e25f36e6f619247203d1bc0291b/snippet.py
description: Experiment results on hard-gists dataset
wanted_name: AddSegment
comment: Add a segment to an IDA .idb from a file.
created_at: 2021-04-18 11:16:06+00:00
pushed_at: 2022-11-18 17:22:11+00:00
url:
https://github.com/PyEGo/exp-gist/blob/bb8212e435ab525bb8ebdee2f620f850133a9b99/hard-gists-reqs/8bfa8e25f36e6f619247203d1bc0291b/snippet.py


repository: PyEGo/exp-gist
file: hard-gists-ego-s2/8bfa8e25f36e6f619247203d1bc0291b/snippet.py
description: Experiment results on hard-gists dataset
wanted_name: AddSegment
comment: Add a segment to an IDA .idb from a file.
created_at: 2021-04-18 11:16:06+00:00
pushed_at: 2022-11-18 17:22:11+00:00
url:
https://github.com/PyEGo/exp-gist/blob/bb8212e435ab525bb8ebdee2f620f850133a9b99/hard-gists-ego-s2/8bfa8e25f36e6f619247203d1bc0291b/snippet.py


repository: OraclePi/IDA_plugins
file: mipsrop/mipsrop.py
description: personal use
wanted_name: MIPS ROP Finder
comment: MIPS ROP Finder
created_at: 2023-07-14 13:53:34+00:00
pushed_at: 2023-07-14 13:53:39+00:00
url:
https://github.com/OraclePi/IDA_plugins/blob/ef918e02be7fd556b6d36730d8c50d64b86a253a/mipsrop/mipsrop.py

content:
# IDA plugin for identifying ROP gadgets in Linux MIPS binaries.
#
# Return Oriented Programming in Linux MIPS is more like Jump Oriented
# Programming; the idea is to control enough of the stack/registers in order to
# control various jumps. Since all instructions in MIPS must be 4-byte aligned,
# You cannot "create" new instructions by returning into the middle
# of existing instructions, as is possible with some other architectures.
#
# In any given MIPS function, various registers are saved onto the stack by
# necessity:
#
#    o $s0 - $s7
#    o $fp
#    o $ra
#
# These values are restored from the stack before the function returns, thus,
# during a stack overflow one can control some or all of these register values.
# The subroutine registers ($s*) are of particular interest, as they are
# commonly used by the compiler to store function pointers. By convention, gcc
# will move function pointers into the $t9 register, then call the function
# using jalr:
#
#     move $t9, $s0  <-- If we control $s0, we control where the jump is taken
#    jalr $t9
#
# While there are other jumps that are of use, and which this plugin searches
# for, the premise is the same: control the stack/registers, and you control
# various jumps allowing you to chain various blocks of code together.
#
# With a list of controllable jumps such as these, we then just need to search
# the surrounding instructions to see if they perform some operation which may
# be useful. For example, let's say we need to load the value 1 into the $a0
# register; in this case, we would want to look for a controllable jump such as
# this:
#
#    move $t9, $s1
#    jalr $t9
#    li $a0, 1    <-- Remember MIPS has jump delay slots, so this instruction
#    is executed with the jump
#
# If we return to this piece of code (and if we control $s1), we can pre-load
# $s1 with the address of the next ROP gadget; thus, $a0 will be loaded with
# the value 1 and we can chain this block of code with other gadgets in order
# to perform more complex operations.
#
# This plugin finds all potentially controllable jumps, and then allows you to
# search for desired instructions surrounding these controllable jumps. Example:
#
#    Python> mipsrop.find("li $a0, 1")
#    -------------------------------------------------
#    |  Address     |  Action      |  Control Jump   |
#    -------------------------------------------------
#    |  0x0002F0F8  |  li $a0,1    |  jalr  $s4      |
#    |  0x00057E50  |  li $a0,1    |  jalr  $s1      |
#    -------------------------------------------------
#
# The output shows the offset of each ROP gadget, the instruction within the
# gadget that your search matched, and the effective register that is jumped to
# after that instruction is executed.
#
# The specified instruction can be a full instruction, such as the example
# above, or a partial instruction. Regex is supported for any of the
# instruction mnemonics or operands; for convenience, the dollar signs in front
# of register names are automatically escaped.
#
# If .set_base(int) is called with a non-zero value then the output will
# include base, offset and address.
# Example
# -------------------------------------------------------------------------
# |  Base       + Offset     =  Address     |  Action  |  Control Jump    |
# -------------------------------------------------------------------------
#
# Craig Heffner
# Tactical Network Solutions

import re
import idc
import sys
import idaapi
import idautils

from shims import ida_shims

# Global instance of MIPSROPFinder
mipsrop = None


def add_to_namespace(namespace, name, variable):
    '''
    Add a variable to a different namespace, likely __main__.
    '''
    import importlib

    importer_module = sys.modules[namespace]
    if name in list(sys.modules.keys()):
        if not (sys.version_info.major == 3 and sys.version_info.minor >= 4):
            import imp
            imp.reload(sys.modules[name])
        else:
            importlib.reload(sys.modules[name])
    else:
        m = importlib.import_module(name, None)
        sys.modules[name] = m

    setattr(importer_module, name, variable)


class MIPSInstruction(object):
    '''
    Class for storing info about a specific instruction.
    '''

    def __init__(self, mnem, opnd0=None, opnd1=None, opnd2=None,
                 ea=idc.BADADDR):
        self.mnem = mnem
        self.operands = [opnd0, opnd1, opnd2]
        self.opnd0 = opnd0
        self.opnd1 = opnd1
        self.opnd2 = opnd2
        self.ea = ea

    def __str__(self):
        string = self.mnem + " "

        for op in self.operands:
            if op:
                string += "%s," % op
            else:
                break

        return string[:-1]


class ROPGadget(object):
    '''
    Class for storing information about a specific ROP gadget.
    '''
    def __init__(self, control, jump, operation=None, description="ROP gaget",
                 base=0):
        self.control = control
        self.exit = jump
        self.operation = operation
        self.description = description
        self.base = base
        self.h = '-' * 112
        if self.base != 0:
            self.h += '-' * 27

        if self.control.opnd1:
            self.control.register = self.control.opnd1
        else:
            self.control.register = self.control.opnd0

        if self.exit.opnd1:
            self.exit.register = self.exit.opnd1
        else:
            self.exit.register = self.exit.opnd0

        if self.operation:
            if self.operation.ea < self.control.ea:
                self.entry = self.operation
            else:
                self.entry = self.control
        else:
            self.operation = self.control
            self.entry = self.control


    def header(self):
        if self.base != 0:
            return self.h + "\n|  Base       + Offset     =  Address       " \
                            "|  Action                                     " \
                            "         |  Control Jump                      " \
                            "    |\n" + self.h
        else:
            return self.h + "\n|  Address     |  Action                     " \
                            "                         |  Control Jump       " \
                            "                   |\n" + self.h

    def footer(self):
        return self.h

    def __str__(self):
        if self.base != 0:
            return "|  0x%.8X + 0x%.8X = 0x%.8X     |  %-50s  | " \
                   " %-5s %-30s  |" % \
                   (self.base, self.entry.ea, self.entry.ea + self.base,
                    str(self.operation), self.exit.mnem, self.control.register)
        else:
            return "|  0x%.8X  |  %-50s  |  %-5s %-30s  |" % \
                   (self.entry.ea, str(self.operation), self.exit.mnem,
                    self.control.register)


class BowcasterBuilder(object):
    '''
    Class to generate bowcaster code from a list of selected ROP gadgets. WIP.
    '''
    INSIZE = 4
    SEARCH_DEPTH = 25

    def __init__(self, gadgets):
        self.code = []
        self.gadgets = gadgets

    def build_code(self):
        keys = list(self.gadgets.keys())
        keys.sort()

        for key in keys[::-1]:
            last_instruction = False
            ea = self.gadgets[key]
            end_ea = ea + self.SEARCH_DEPTH

            while ea <= end_ea:
                mnem = idc.print_insn_mnem(ea)
                if mnem in ['jr', 'jalr']:
                    last_instruction = True
                ea += self.INSIZE

    def print_code(self):
        for line in self.code:
            print(line)


class MIPSROPFinder(object):
    '''
    Primary ROP finder class.
    '''
    CODE = 2
    DATA = 3
    INSIZE = 4
    SEARCH_DEPTH = 25

    def __init__(self):
        self.base = 0

        self._initial_find()

        if self.controllable_jumps or self.system_calls:
            print("MIPS ROP Finder activated, found %d controllable jumps " \
                  "between 0x%.8X and 0x%.8X" % (len(self.controllable_jumps),
                                                 self.start, self.end))
        else:
            print("No ROP gadgets found!")

    def _initial_find(self):
        self.start = idc.BADADDR
        self.end = idc.BADADDR
        self.system_calls = []
        self.double_jumps = []
        self.controllable_jumps = []
        start = 0
        end = 0

        for (start, end) in self._get_segments(self.CODE):
            self.controllable_jumps += self._find_controllable_jumps(start, end)
            self.system_calls += self._find_system_calls(start, end)
            self.double_jumps += self._find_double_jumps(start, end)
            if self.start == idc.BADADDR:
                self.start = start
        self.end = end

    def _get_segments(self, attr):
        segments = []
        seg = ida_shims.get_first_seg()

        while seg != idc.BADADDR:
            if ida_shims.get_segm_attr(seg, idc.SEGATTR_TYPE) == attr:
                start = ida_shims.get_segm_start(seg)
                end = ida_shims.get_segm_end(seg)
                segments.append((start, end))
            seg = ida_shims.get_next_seg(seg)

        return segments

    def _get_instruction(self, ea):
        return MIPSInstruction(ida_shims.print_insn_mnem(ea),
                               ida_shims.print_operand(ea, 0),
                               ida_shims.print_operand(ea, 1),
                               ida_shims.print_operand(ea, 2), ea)

    def _does_instruction_match(self, ea, instruction, regex=False):
        i = 0
        op_cnt = 0
        op_ok_cnt = 0
        match = False
        insn = ida_shims.decode_insn(ea)
        mnem = ida_shims.print_insn_mnem(ea)
        if (not instruction.mnem) or (instruction.mnem == mnem) or \
                (regex and re.match(instruction.mnem, mnem)):
            for operand in instruction.operands:
                if operand:
                    op_cnt += 1
                    op = ida_shims.print_operand(ea, i)

                    if regex:
                        if re.match(operand, op):
                            op_ok_cnt += 1
                    elif operand == op:
                        op_ok_cnt += 1
                i += 1

            if op_cnt == op_ok_cnt:
                match = True
        return match

    def _is_bad_instruction(self, ea, bad_instructions=['j', 'b'],
                            no_clobber=[]):
        bad = False
        mnem = ida_shims.print_insn_mnem(ea)

        if mnem and mnem[0] in bad_instructions:
            bad = True
        else:
            insn = ida_shims.decode_insn(ea)
            feature = ida_shims.get_canon_feature(insn)
            for register in no_clobber:
                if (feature & idaapi.CF_CHG1) == idaapi.CF_CHG1:
                    if ida_shims.print_operand(ea, 0) == register:
                        bad = True

        return bad

    def _contains_bad_instruction(self, start_ea, end_ea,
                                  bad_instructions=['j', 'b'], no_clobber=[]):
        ea = start_ea

        while ea <= end_ea:
            if self._is_bad_instruction(ea, bad_instructions, no_clobber):
                return True
            else:
                ea += self.INSIZE

        return False

    def _find_prev_instruction_ea(self, start_ea, instruction, end_ea=0,
                                  no_baddies=True, regex=False,
                                  dont_overwrite=[]):
        instruction_ea = idc.BADADDR
        ea = start_ea

        while ea >= end_ea:
            if self._does_instruction_match(ea, instruction, regex):
                instruction_ea = ea
                break
            elif no_baddies and self._is_bad_instruction(
                    ea, no_clobber=dont_overwrite):
                break

            ea -= self.INSIZE

        return instruction_ea

    def _find_next_instruction_ea(self, start_ea, instruction,
                                  end_ea=idc.BADADDR, no_baddies=False,
                                  regex=False, dont_overwrite=[]):
        instruction_ea = idc.BADADDR
        ea = start_ea

        while ea <= end_ea:
            if self._does_instruction_match(ea, instruction, regex):
                instruction_ea = ea
                break
            elif no_baddies and self._is_bad_instruction(
                    ea, no_clobber=dont_overwrite):
                break

            ea += self.INSIZE

        return instruction_ea

    def _find_controllable_jumps(self, start_ea, end_ea):
        controllable_jumps = []
        t9_controls = [
            MIPSInstruction("move", "\$t9"),
            MIPSInstruction("addiu", "\$t9", "^\$"),
        ]
        t9_jumps = [
            MIPSInstruction("jr", "\$t9"),
        ]
        ra_controls = [
            MIPSInstruction("lw", "\$ra"),
        ]
        ra_jumps = [
            # TODO: Search for jumps to registers other than $ra.
            MIPSInstruction("jr", "\$ra"),
        ]
        t9_musnt_clobber = ["$t9"]
        ra_musnt_clobber = ["$ra"]

        if idaapi.IDA_SDK_VERSION >= 740:
            t9_jumps.append(MIPSInstruction("jalr", "\$ra"))
        else:
            t9_jumps.append(MIPSInstruction("jalr", "\$t9"))

        for possible_control_instruction in t9_controls+ra_controls:
            ea = start_ea

            if possible_control_instruction in t9_controls:
                jumps = t9_jumps
                musnt_clobber = t9_musnt_clobber
            else:
                jumps = ra_jumps
                musnt_clobber = ra_musnt_clobber

            while ea <= end_ea:
                ea = self._find_next_instruction_ea(
                    ea, possible_control_instruction, end_ea, regex=True)
                if ea != idc.BADADDR:
                    insn = ida_shims.decode_insn(ea)

                    control_instruction = self._get_instruction(ea)
                    control_register = control_instruction.operands[1]

                    if control_register:
                        for jump in jumps:
                            jump_ea = self._find_next_instruction_ea(
                                ea+insn.size, jump, end_ea, no_baddies=True,
                                regex=True, dont_overwrite=musnt_clobber)

                            if jump_ea != idc.BADADDR:
                                jump_instruction = self._get_instruction(
                                    jump_ea)
                                controllable_jumps.append(
                                    ROPGadget(
                                        control_instruction, jump_instruction,
                                        description="Controllable Jump",
                                        base=self.base))
                                ea = jump_ea

                    ea += insn.size

        return controllable_jumps

    def _find_system_calls(self, start_ea, end_ea):
        system_calls = []
        system_load = MIPSInstruction("la", "$t9", "system")
        stack_arg_zero = MIPSInstruction("addiu", "$a0", "$sp")

        for xref in idautils.XrefsTo(ida_shims.get_name_ea_simple('system')):
            ea = xref.frm
            mnem = ida_shims.print_insn_mnem(ea)
            if mnem and ea >= start_ea and \
                    ea <= end_ea and mnem[0] in ['j', 'b']:
                a0_ea = self._find_next_instruction_ea(
                    ea+self.INSIZE, stack_arg_zero, ea+self.INSIZE)

                if a0_ea == idc.BADADDR:
                    a0_ea = self._find_prev_instruction_ea(
                        ea, stack_arg_zero, ea-(self.SEARCH_DEPTH*self.INSIZE))

                if a0_ea != idc.BADADDR:
                    control_ea = self._find_prev_instruction_ea(
                        ea-self.INSIZE, system_load,
                        ea-(self.SEARCH_DEPTH*self.INSIZE))

                    if control_ea != idc.BADADDR:
                        system_calls.append(
                            ROPGadget(
                                self._get_instruction(control_ea),
                                self._get_instruction(ea),
                                self._get_instruction(a0_ea),
                                description="System call", base=self.base))

                ea += self.INSIZE
            else:
                break

        return system_calls

    def _find_double_jumps(self, start_ea, end_ea):
        double_jumps = []

        for i in range(0, len(self.controllable_jumps)):
            g1 = self.controllable_jumps[i]
            if g1.exit.mnem != 'jalr':
                continue

            for j in range(i+1, len(self.controllable_jumps)):
                g2 = self.controllable_jumps[j]
                distance = (g2.entry.ea - g1.exit.ea)

                if 0 < distance <= (self.SEARCH_DEPTH * self.INSIZE):
                    if g1.control.register != g2.control.register:
                        if not self._contains_bad_instruction(
                                g1.exit.ea+self.INSIZE,
                                g2.control.ea-self.INSIZE,
                                no_clobber=[g2.control.register]):
                            double_jumps.append(g1)
                            break

        return double_jumps

    def _find_rop_gadgets(self, gadget):
        gadget_list = []

        for controllable_jump in self.controllable_jumps:
            gadget_ea = idc.BADADDR

            ea = self._find_next_instruction_ea(
                controllable_jump.entry.ea, gadget,
                controllable_jump.exit.ea+self.INSIZE, regex=True)

            if ea != idc.BADADDR:
                gadget_ea = ea
            else:
                ea = self._find_prev_instruction_ea(
                    controllable_jump.entry.ea, gadget,
                    controllable_jump.entry.ea-(self.SEARCH_DEPTH*self.INSIZE),
                    no_baddies=True, regex=True,
                    dont_overwrite=[controllable_jump.entry.opnd1])

                if ea != idc.BADADDR:
                    gadget_ea = ea

            if gadget_ea != idc.BADADDR:
                gadget_list.append(
                    ROPGadget(
                        controllable_jump.entry, controllable_jump.exit,
                        self._get_instruction(gadget_ea), base=self.base))

        return gadget_list

    def _print_gadgets(self, gadgets):
        if gadgets:
            print(gadgets[0].header())

        for gadget in gadgets:
            print(str(gadget))

        if gadgets:
            print(gadgets[0].footer())

        print("Found %d matching gadgets" % (len(gadgets)))

    def _get_marked_gadgets(self):
        rop_gadgets = {}

        for i in range(0 if idaapi.IDA_SDK_VERSION >= 690 else 1, 1024):
            marked_pos = ida_shims.get_bookmark(i)
            if marked_pos != idc.BADADDR:
                marked_comment = ida_shims.get_bookmark_desc(i)
                if marked_comment and marked_comment.lower().startswith("rop"):
                    rop_gadgets[marked_comment] = marked_pos
            else:
                break

        return rop_gadgets

    def double(self):
        self.doubles()

    def doubles(self):
        '''
        Prints a list of all "double jump" gadgets (useful for function calls).
        '''
        self._print_gadgets(self.double_jumps)

    def stackfinder(self):
        self.stackfinders()

    def stackfinders(self):
        '''
        Prints a list of all gadgets that put a stack address into a register.
        '''
        self.find("addiu .*, $sp")

    def lia0(self):
        '''
        Prints a list of all gadgets that load an immediate value number into
        $a0 (useful for setting up the argument to sleep).
        '''
        self.find("li $a0")

    def tail(self):
        return self.tails()

    def tails(self):
        '''
        Prints a lits of all tail call gadgets (useful for function calls).
        '''
        return self.iret()

    def iret(self):
        '''
        Prints a lits of all tail gadgets (useful for function calls).
        '''
        tail_gadgets = []

        for gadget in self._find_rop_gadgets(MIPSInstruction("move", "\$t9")):
            if gadget.exit.mnem == 'jr' and gadget.exit.register == '$t9':
                tail_gadgets.append(gadget)

        self._print_gadgets(tail_gadgets)

    def system(self):
        '''
        Prints a list of gadgets that may be used to call system().
        '''
        sys_gadgets = self.system_calls + self._find_rop_gadgets(
            MIPSInstruction("addiu", "\$a0", "\$sp"))
        self._print_gadgets(sys_gadgets)

    def find(self, *args):
        '''
        Locates all potential ROP gadgets that contain the specified
        instruction.

        @instruction_string - The instruction you need executed. This can be
        either a:

                    o Full instruction    - "li $a0, 1"
                    o Partial instruction - "li $a0"
                    o Regex instruction   - "li $a0, .*"
        '''
        count = 0
        good_gadgets = {}
        final_gadgets = []
        registers = ['$v', '$s', '$a', '$t', '$k', '$pc', '$fp', '$ra', '$gp',
                     '$at', '$zero']

        for instruction_string in args:
            comma_split = instruction_string.split(',')
            instruction_parts = comma_split[0].split()
            if len(comma_split) > 1:
                instruction_parts += comma_split[1:]

            for i in range(0, 4):
                if i > len(instruction_parts) - 1:
                    instruction_parts.append(None)
                else:
                    instruction_parts[i] = instruction_parts[i].strip().\
                        strip(',').strip()
                    for reg in registers:
                        instruction_parts[i] = instruction_parts[i].replace(
                            reg, "\\%s" % reg)

            instruction = MIPSInstruction(
                instruction_parts[0], instruction_parts[1],
                instruction_parts[2], instruction_parts[3])

            gadgets = self._find_rop_gadgets(instruction)
            for gadget in gadgets:
                if count == 0:
                    good_gadgets[gadget.control.ea] = {'gadget': gadget,
                                                       'count': 1}
                elif gadget.control.ea in good_gadgets:
                    good_gadgets[gadget.control.ea]['count'] += 1
                    break
            count += 1

        if good_gadgets:
            for (ea, info) in good_gadgets.items():
                if info['count'] == count:
                    final_gadgets.append(info['gadget'])
                else:
                    print("Skipping gadget at 0x%X" % ea)
        if final_gadgets:
            self._print_gadgets(gadgets)
        else:
            print("No ROP gadgets found!")

    def summary(self):
        '''
        Prints a summary of your currently marked ROP gadgets, in alphabetical
        order by the marked name. To mark a location as a ROP gadget, simply
        mark the position in IDA (Alt+M) with any name that starts with "ROP".
        '''
        rop_gadgets = self._get_marked_gadgets()
        summaries = []
        delim_char = "-"
        headings = {
            'name': "Gadget Name",
            'offset': "Gadget Offset",
            'summary': "Gadget Summary"
        }
        if self.base != 0:
            headings['offset'] = "Gadget Base + Offset = Address       "
        lengths = {
            'name': len(headings['name']),
            'offset': len(headings['offset']),
            'summary': len(headings['summary']),
        }
        total_length = (3 * len(headings)) + 1

        if rop_gadgets:
            gadget_keys = list(rop_gadgets.keys())
            gadget_keys.sort()

            for marked_comment in gadget_keys:
                if len(marked_comment) > lengths['name']:
                    lengths['name'] = len(marked_comment)

                summary = []
                ea = rop_gadgets[marked_comment]
                end_ea = ea + (self.SEARCH_DEPTH * self.INSIZE)

                while ea <= end_ea:
                    summary.append(idc.GetDisasm(ea))
                    mnem = ida_shims.print_insn_mnem(ea)
                    if len(mnem) > 0 and mnem[0].lower() in ['j', 'b']:
                        summary.append(idc.GetDisasm(ea+self.INSIZE))
                        break

                    ea += self.INSIZE

                if len(summary) == 0:
                    summary.append('')

                for line in summary:
                    if len(line) > lengths['summary']:
                        lengths['summary'] = len(line)

                summaries.append(summary)

            for (heading, size) in lengths.items():
                total_length += size

            delim = delim_char * total_length
            line_fmt = "| %%-%ds | %%-%ds | %%-%ds |" % \
                       (lengths['name'], lengths['offset'], lengths['summary'])

            print(delim)
            print(line_fmt % \
                  (headings['name'], headings['offset'], headings['summary']))
            print(delim)

            for i in range(0, len(gadget_keys)):
                line_count = 0
                marked_comment = gadget_keys[i]
                if self.base != 0:
                    offset = "0x%.8X + 0x%.8X = 0x%.8X" % \
                             (self.base, rop_gadgets[marked_comment],
                              self.base + rop_gadgets[marked_comment])
                else:
                    offset = "0x%.8X" % rop_gadgets[marked_comment]
                summary = summaries[i]

                for line in summary:
                    if line_count == 0:
                        print(line_fmt % (marked_comment, offset, line))
                    else:
                        print(line_fmt % ('', '', line))

                    line_count += 1

                print(delim)

    def build(self):
        '''
        WIP.
        '''
        gadgets = self._get_marked_gadgets()
        bc = BowcasterBuilder(gadgets)
        bc.build_code()

    def set_base(self, base=0):
        '''
        Set base address used for display
        '''
        self.base = base
        self._initial_find()

    def help(self):
        '''
        Show help info.
        '''
        delim = "-" * 140

        print("")
        print("mipsrop.find(instruction_string)")
        print(delim)
        print(self.find.__doc__)

        print("")
        print("mipsrop.system()")
        print(delim)
        print(self.system.__doc__)

        print("")
        print("mipsrop.doubles()")
        print(delim)
        print(self.doubles.__doc__)

        print("")
        print("mipsrop.stackfinders()")
        print(delim)
        print(self.stackfinders.__doc__)

        print("")
        print("mipsrop.tails()")
        print(delim)
        print(self.tails.__doc__)

        print("")
        print("mipsrop.set_base()")
        print(delim)
        print(self.set_base.__doc__)

        print("")
        print("mipsrop.summary()")
        print(delim)
        print(self.summary.__doc__)


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

        def activate(self, ctx):
            global mipsrop
            mipsrop = MIPSROPFinder()
            add_to_namespace('__main__', 'mipsrop', mipsrop)
            return 1

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


class mipsropfinder_t(idaapi.plugin_t):
    flags = 0
    comment = 'MIPS ROP Finder'
    help = ''
    action_name = 'mipsrop:action'
    wanted_name = "MIPS ROP Finder"
    wanted_hotkey = ""
    wanted_tooltip = "MIPS ROP Gadget Finder"
    root_tab = 'Search/'
    menu_name = 'mips rop gadgets'
    menu_context = None

    def init(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            # 199 is a default icon.
            action_desc = idaapi.action_desc_t(self.action_name,
                                               self.menu_name,
                                               MipsRopHandler(),
                                               self.wanted_hotkey,
                                               self.wanted_tooltip,
                                               199)

            idaapi.register_action(action_desc)
            idaapi.attach_action_to_menu(
                self.root_tab, self.action_name, idaapi.SETMENU_APP)
        else:
            self.menu_context = idaapi.add_menu_item(
                "Search/", self.menu_name, "", 0, self.run, (None,))
        return idaapi.PLUGIN_KEEP

    def term(self):
        if idaapi.IDA_SDK_VERSION >= 700:
            idaapi.detach_action_from_menu(self.root_tab, self.action_name)
        else:
            if self.menu_context is not None:
                idaapi.del_menu_item(self.menu_context)
        return None

    def run(self, arg):
        global mipsrop
        mipsrop = MIPSROPFinder()


def PLUGIN_ENTRY():
        return mipsropfinder_t()


repository: Teijen/Personal-Microsoft-Malware-Classification
file: ida-plugin/classifier.py
wanted_name: 2015 Microsoft Malware Classifier
created_at: 2024-09-16 15:07:49+00:00
pushed_at: 2025-02-27 07:46:57+00:00
description:
A personal repo to get around Fork file size upload limitations. See https://github.com/Teijen/Microsoft-Malware-Classification for original fork and history.

url:
https://github.com/Teijen/Personal-Microsoft-Malware-Classification/blob/12e5c09719e0ac19a1a86b2b968dafda272a985b/ida-plugin/classifier.py

content:
"""
-- INTRODUCTION --
An IDA Pro plug-in to classify a malware into nine families defined by the 2015 Microsoft Malware Classification Challenge.

-- USAGE --
This script must be located in the same folder as `generator.py`.

-- HELP --
See: https://github.com/czs108/Microsoft-Malware-Classification
"""

from pathlib import Path
import zlib
import joblib
import re

import pandas as pd
import numpy as np

import idaapi
import ida_loader
import ida_diskio

from generator import Generator


class Reader:
    def __init__(self, file: str) -> None:
        self._file: str = file

    def bytes(self) -> list[str]:
        with Path(self._file + ".bytes").open() as file:
            data = file.read()
        items = data.split()
        byte_list = []
        for item in items:
            if len(item) == 2 and item != "??":
                byte_list.append(item)
        return byte_list

    def asm(self) -> str:
        with Path(self._file + ".asm").open(encoding="utf-8", errors="ignore") as file:
            return file.read()

    def asm_lines(self) -> list[str]:
        with Path(self._file + ".asm").open(encoding="utf-8", errors="ignore") as file:
            return file.readlines()


class Section:
    _sctn_rgx: re.Pattern = re.compile(r"([A-Za-z]+):[\dA-F]{8}\s+;\s+Virtual\s+size\s+:\s+[\dA-F]{8}\s+\(\s*(\d+)\.[\s\S]+?Section\s+size\s+in\s+file\s+:\s+[\dA-F]{8}\s+\(\s*(\d+)\.[\s\S]+?;\s+Flags\s+[\dA-F]{8}:[\s\S]+?\s+((?:Executable|Readable|Writable|\s)+)", flags=re.IGNORECASE)

    def __init__(self) -> None:
        self.name: str = ""
        self.virtual_size: int = 0
        self.raw_size: int = 0
        self.executable: bool = False
        self.writable: bool = False

    @classmethod
    def load(cls, asm: str) -> list[Section]:
        sctns = []
        for attr in cls._sctn_rgx.findall(asm):
            sctn = Section()
            sctn.name, sctn.virtual_size, sctn.raw_size, access = \
                attr[0].lower(), int(attr[1]), int(attr[2]), set(attr[3].split())
            if "Executable" in access:
                sctn.executable = True
            if "Writable" in access:
                sctn.writable = True
            sctns.append(sctn)
        return sctns


class Extractor:
    _sctn_cols: tuple[str] = ("idata-Raw", "text-Raw", "data-Virtual", "data-Ratio", "text-Virtual",
                                "data-Raw", "idata-Virtual", "text-Ratio", "idata-Ratio", "bss-Virtual",
                                "bss-Ratio", "rdata-Ratio", "rdata-Virtual", "rdata-Raw", "bss-Raw",
                                "code-Raw", "code-Ratio", "tls-Raw", "code-Virtual", "tls-Ratio",
                                "tls-Virtual", "rata-Raw", "rata-Virtual", "rata-Ratio", "zenc-Ratio")

    def __init__(self, file: str) -> None:
        self._file: str = file
        self._id: str = Path(file).name

    def extract(self) -> pd.DataFrame:
        size, rwe = self._section_attributions()
        selected_size = pd.DataFrame(index=size.index, columns=self._sctn_cols)
        for col in size.columns:
            if col in selected_size.columns:
                selected_size.at[self._id, col] = size.at[self._id, col]
        return pd.concat([selected_size.fillna(0), rwe, self._content_complexity()], axis="columns")

    def _content_complexity(self) -> pd.DataFrame:
        df = pd.DataFrame(
            columns=["ID", "Asm-Len", "Zip-Asm-Len", "Asm-Zip-Ratio", "Byte-Len", "Zip-Byte-Len", "Byte-Zip-Ratio"],
            dtype=float).set_index("ID")

        reader = Reader(self._file)
        asm = reader.asm().encode("utf-8")
        bytes = " ".join([str(byte) for byte in reader.bytes()]).encode("utf-8")
        df.at[self._id, "Asm-Len"] = len(asm)
        df.at[self._id, "Zip-Asm-Len"] = len(zlib.compress(asm))
        df.at[self._id, "Byte-Len"] = len(bytes)
        df.at[self._id, "Zip-Byte-Len"] = len(zlib.compress(bytes))

        df[["Asm-Len", "Zip-Asm-Len", "Byte-Len", "Zip-Byte-Len"]] = \
            df[["Asm-Len", "Zip-Asm-Len", "Byte-Len", "Zip-Byte-Len"]].astype(int)
        df["Asm-Zip-Ratio"] = (df["Asm-Len"] / df["Zip-Asm-Len"]).round(5)
        df["Byte-Zip-Ratio"] = (df["Byte-Len"] / df["Zip-Byte-Len"]).round(5)
        return df

    def _section_attributions(self) -> tuple[pd.DataFrame, pd.DataFrame]:
        rwe_cols = [f"{i}-{j}" for i in ["Executable", "Writable", "Readable"] for j in ["Virtual", "Raw", "Ratio"]]
        rwe = pd.DataFrame(columns=["ID"] + rwe_cols, dtype=float).set_index("ID")
        sctn = pd.DataFrame(columns=["ID"], dtype=float).set_index("ID")

        sctn_int_cols = set()

        def save_by_sections(attr: Section) -> None:
            sctn.at[self._id, f"{attr.name}-Virtual"] = attr.virtual_size
            sctn.at[self._id, f"{attr.name}-Raw",] = attr.raw_size
            sctn.at[self._id, f"{attr.name}-Ratio",] = round(attr.raw_size / attr.virtual_size, 5)
            sctn_int_cols.update([f"{attr.name}-Virtual", f"{attr.name}-Raw"])

        def save_by_access(attr: Section) -> None:
            rwe.at[self._id, "Readable-Virtual"] += attr.virtual_size
            rwe.at[self._id, "Readable-Raw"] += attr.raw_size
            if attr.writable:
                rwe.at[self._id, "Writable-Virtual"] += attr.virtual_size
                rwe.at[self._id, "Writable-Raw"] += attr.raw_size
            if attr.executable:
                rwe.at[self._id, "Executable-Virtual"] += attr.virtual_size
                rwe.at[self._id, "Executable-Raw"] += attr.raw_size

        rwe.loc[self._id, :] = 0
        sctn.loc[self._id, :] = 0
        for attr in Section.load(Reader(self._file).asm()):
            save_by_sections(attr)
            save_by_access(attr)

        sctn.fillna(0, inplace=True)
        sctn_int_cols = list(sctn_int_cols)
        sctn[sctn_int_cols] = sctn[sctn_int_cols].astype(int)

        rwe.fillna(0, inplace=True)
        rwe_int_cols = [i for i in rwe.columns if "-Virtual" in i or "-Raw" in i]
        rwe[rwe_int_cols] = rwe[rwe_int_cols].astype(int)

        rwe["Readable-Ratio"] = (rwe["Readable-Raw"] / rwe["Readable-Virtual"]).round(5)
        rwe["Writable-Ratio"] = (rwe["Writable-Raw"] / rwe["Writable-Virtual"]).round(5)
        rwe["Executable-Ratio"] = (rwe["Executable-Raw"] / rwe["Executable-Virtual"]).round(5)
        return sctn, rwe.fillna(0)


class MalwareClassifier(idaapi.plugin_t):
    flags: int = idaapi.PLUGIN_UNL
    comment: str = "This plug-in can calculate the probability that it belongs to each malware family defined by the 2015 Microsoft Malware Classification Challenge."
    help: str = "See: https://github.com/czs108/Microsoft-Malware-Classification"
    wanted_name: str = "2015 Microsoft Malware Classifier"
    wanted_hotkey: str = "Alt-F8"

    def init(self) -> int:
        self._file: str = ida_loader.get_path(ida_loader.PATH_TYPE_IDB)[:-len(".idb")]
        self._classes: tuple[str] = \
            ("Ramnit", "Lollipop", "Kelihos_ver3", "Vundo", "Simda", "Tracur", "Kelihos_ver1", "Obfuscator.ACY", "Gatak")
        return idaapi.PLUGIN_OK

    def run(self, arg: int) -> None:
        self._gene_data()
        plugin_folder = Path(ida_diskio.idadir("plugins"))
        X = Extractor(self._file).extract()
        classifier = joblib.load(plugin_folder.joinpath("tpot_exported_ida_clf.joblib"))
        proba = classifier.predict_proba(X)[0]
        for i in np.argsort(proba)[::-1]:
            idaapi.msg("{:04.2f} -> {}\n".format(proba[i], self._classes[i]))

    def term(self) -> None:
        pass

    def _gene_data(self) -> None:
        generator = Generator(self._file)
        generator.asm()
        generator.bytes()


def PLUGIN_ENTRY() -> MalwareClassifier:
    return MalwareClassifier()

comment:
This plug-in can calculate the probability that it belongs to each malware family defined by the 2015 Microsoft Malware Classification Challenge.


repository: leonail1/Microsoft-Malware-Classification
file: ida-plugin/classifier.py
wanted_name: 2015 Microsoft Malware Classifier
created_at: 2024-12-05 08:25:25+00:00
pushed_at: 2024-12-05 08:25:36+00:00
url:
https://github.com/leonail1/Microsoft-Malware-Classification/blob/34b3cbb316f9925120bd9cc6d1be12615bf95452/ida-plugin/classifier.py

content:
"""
-- INTRODUCTION --
An IDA Pro plug-in to classify a malware into nine families defined by the 2015 Microsoft Malware Classification Challenge.

-- USAGE --
This script must be located in the same folder as `generator.py`.

-- HELP --
See: https://github.com/czs108/Microsoft-Malware-Classification
"""

from pathlib import Path
import zlib
import joblib
import re

import pandas as pd
import numpy as np

import idaapi
import ida_loader
import ida_diskio

from generator import Generator


class Reader:
    def __init__(self, file: str) -> None:
        self._file: str = file

    def bytes(self) -> list[str]:
        with Path(self._file + ".bytes").open() as file:
            data = file.read()
        items = data.split()
        byte_list = []
        for item in items:
            if len(item) == 2 and item != "??":
                byte_list.append(item)
        return byte_list

    def asm(self) -> str:
        with Path(self._file + ".asm").open(encoding="utf-8", errors="ignore") as file:
            return file.read()

    def asm_lines(self) -> list[str]:
        with Path(self._file + ".asm").open(encoding="utf-8", errors="ignore") as file:
            return file.readlines()


class Section:
    _sctn_rgx: re.Pattern = re.compile(r"([A-Za-z]+):[\dA-F]{8}\s+;\s+Virtual\s+size\s+:\s+[\dA-F]{8}\s+\(\s*(\d+)\.[\s\S]+?Section\s+size\s+in\s+file\s+:\s+[\dA-F]{8}\s+\(\s*(\d+)\.[\s\S]+?;\s+Flags\s+[\dA-F]{8}:[\s\S]+?\s+((?:Executable|Readable|Writable|\s)+)", flags=re.IGNORECASE)

    def __init__(self) -> None:
        self.name: str = ""
        self.virtual_size: int = 0
        self.raw_size: int = 0
        self.executable: bool = False
        self.writable: bool = False

    @classmethod
    def load(cls, asm: str) -> list[Section]:
        sctns = []
        for attr in cls._sctn_rgx.findall(asm):
            sctn = Section()
            sctn.name, sctn.virtual_size, sctn.raw_size, access = \
                attr[0].lower(), int(attr[1]), int(attr[2]), set(attr[3].split())
            if "Executable" in access:
                sctn.executable = True
            if "Writable" in access:
                sctn.writable = True
            sctns.append(sctn)
        return sctns


class Extractor:
    _sctn_cols: tuple[str] = ("idata-Raw", "text-Raw", "data-Virtual", "data-Ratio", "text-Virtual",
                                "data-Raw", "idata-Virtual", "text-Ratio", "idata-Ratio", "bss-Virtual",
                                "bss-Ratio", "rdata-Ratio", "rdata-Virtual", "rdata-Raw", "bss-Raw",
                                "code-Raw", "code-Ratio", "tls-Raw", "code-Virtual", "tls-Ratio",
                                "tls-Virtual", "rata-Raw", "rata-Virtual", "rata-Ratio", "zenc-Ratio")

    def __init__(self, file: str) -> None:
        self._file: str = file
        self._id: str = Path(file).name

    def extract(self) -> pd.DataFrame:
        size, rwe = self._section_attributions()
        selected_size = pd.DataFrame(index=size.index, columns=self._sctn_cols)
        for col in size.columns:
            if col in selected_size.columns:
                selected_size.at[self._id, col] = size.at[self._id, col]
        return pd.concat([selected_size.fillna(0), rwe, self._content_complexity()], axis="columns")

    def _content_complexity(self) -> pd.DataFrame:
        df = pd.DataFrame(
            columns=["ID", "Asm-Len", "Zip-Asm-Len", "Asm-Zip-Ratio", "Byte-Len", "Zip-Byte-Len", "Byte-Zip-Ratio"],
            dtype=float).set_index("ID")

        reader = Reader(self._file)
        asm = reader.asm().encode("utf-8")
        bytes = " ".join([str(byte) for byte in reader.bytes()]).encode("utf-8")
        df.at[self._id, "Asm-Len"] = len(asm)
        df.at[self._id, "Zip-Asm-Len"] = len(zlib.compress(asm))
        df.at[self._id, "Byte-Len"] = len(bytes)
        df.at[self._id, "Zip-Byte-Len"] = len(zlib.compress(bytes))

        df[["Asm-Len", "Zip-Asm-Len", "Byte-Len", "Zip-Byte-Len"]] = \
            df[["Asm-Len", "Zip-Asm-Len", "Byte-Len", "Zip-Byte-Len"]].astype(int)
        df["Asm-Zip-Ratio"] = (df["Asm-Len"] / df["Zip-Asm-Len"]).round(5)
        df["Byte-Zip-Ratio"] = (df["Byte-Len"] / df["Zip-Byte-Len"]).round(5)
        return df

    def _section_attributions(self) -> tuple[pd.DataFrame, pd.DataFrame]:
        rwe_cols = [f"{i}-{j}" for i in ["Executable", "Writable", "Readable"] for j in ["Virtual", "Raw", "Ratio"]]
        rwe = pd.DataFrame(columns=["ID"] + rwe_cols, dtype=float).set_index("ID")
        sctn = pd.DataFrame(columns=["ID"], dtype=float).set_index("ID")

        sctn_int_cols = set()

        def save_by_sections(attr: Section) -> None:
            sctn.at[self._id, f"{attr.name}-Virtual"] = attr.virtual_size
            sctn.at[self._id, f"{attr.name}-Raw",] = attr.raw_size
            sctn.at[self._id, f"{attr.name}-Ratio",] = round(attr.raw_size / attr.virtual_size, 5)
            sctn_int_cols.update([f"{attr.name}-Virtual", f"{attr.name}-Raw"])

        def save_by_access(attr: Section) -> None:
            rwe.at[self._id, "Readable-Virtual"] += attr.virtual_size
            rwe.at[self._id, "Readable-Raw"] += attr.raw_size
            if attr.writable:
                rwe.at[self._id, "Writable-Virtual"] += attr.virtual_size
                rwe.at[self._id, "Writable-Raw"] += attr.raw_size
            if attr.executable:
                rwe.at[self._id, "Executable-Virtual"] += attr.virtual_size
                rwe.at[self._id, "Executable-Raw"] += attr.raw_size

        rwe.loc[self._id, :] = 0
        sctn.loc[self._id, :] = 0
        for attr in Section.load(Reader(self._file).asm()):
            save_by_sections(attr)
            save_by_access(attr)

        sctn.fillna(0, inplace=True)
        sctn_int_cols = list(sctn_int_cols)
        sctn[sctn_int_cols] = sctn[sctn_int_cols].astype(int)

        rwe.fillna(0, inplace=True)
        rwe_int_cols = [i for i in rwe.columns if "-Virtual" in i or "-Raw" in i]
        rwe[rwe_int_cols] = rwe[rwe_int_cols].astype(int)

        rwe["Readable-Ratio"] = (rwe["Readable-Raw"] / rwe["Readable-Virtual"]).round(5)
        rwe["Writable-Ratio"] = (rwe["Writable-Raw"] / rwe["Writable-Virtual"]).round(5)
        rwe["Executable-Ratio"] = (rwe["Executable-Raw"] / rwe["Executable-Virtual"]).round(5)
        return sctn, rwe.fillna(0)


class MalwareClassifier(idaapi.plugin_t):
    flags: int = idaapi.PLUGIN_UNL
    comment: str = "This plug-in can calculate the probability that it belongs to each malware family defined by the 2015 Microsoft Malware Classification Challenge."
    help: str = "See: https://github.com/czs108/Microsoft-Malware-Classification"
    wanted_name: str = "2015 Microsoft Malware Classifier"
    wanted_hotkey: str = "Alt-F8"

    def init(self) -> int:
        self._file: str = ida_loader.get_path(ida_loader.PATH_TYPE_IDB)[:-len(".idb")]
        self._classes: tuple[str] = \
            ("Ramnit", "Lollipop", "Kelihos_ver3", "Vundo", "Simda", "Tracur", "Kelihos_ver1", "Obfuscator.ACY", "Gatak")
        return idaapi.PLUGIN_OK

    def run(self, arg: int) -> None:
        self._gene_data()
        plugin_folder = Path(ida_diskio.idadir("plugins"))
        X = Extractor(self._file).extract()
        classifier = joblib.load(plugin_folder.joinpath("tpot_exported_ida_clf.joblib"))
        proba = classifier.predict_proba(X)[0]
        for i in np.argsort(proba)[::-1]:
            idaapi.msg("{:04.2f} -> {}\n".format(proba[i], self._classes[i]))

    def term(self) -> None:
        pass

    def _gene_data(self) -> None:
        generator = Generator(self._file)
        generator.asm()
        generator.bytes()


def PLUGIN_ENTRY() -> MalwareClassifier:
    return MalwareClassifier()

comment:
This plug-in can calculate the probability that it belongs to each malware family defined by the 2015 Microsoft Malware Classification Challenge.


repository: merydwin/idascrtipt
file: Scripts/msdnapihelp.py
wanted_name: MSDN API Help
comment: Online MSDN API Help
created_at: 2015-06-25 13:32:18+00:00
pushed_at: 2015-06-25 13:33:48+00:00
url:
https://github.com/merydwin/idascrtipt/blob/431e04847e55adbb1d263aa2aadc2d489d068f50/Scripts/msdnapihelp.py

content:
"""
User contributed script: MSDN API HELP plugin

This script fetches the API reference (from MSDN) of a given highlighted identifier
and returns the results in a new web browser page.

This script depends on the feedparser package: http://code.google.com/p/feedparser/

10/05/2010
- initial version


"""

import idaapi

# -----------------------------------------------------------------------
class msdnapihelp_plugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "Online MSDN API Help"
    help = "Help me"
    wanted_name = "MSDN API Help"
    wanted_hotkey = "F3"

    def init(self):
        return idaapi.PLUGIN_OK


    @staticmethod
    def sanitize_name(name):
        t = idaapi.FUNC_IMPORT_PREFIX
        if name.startswith(t):
            return name[len(t):]
        return name


    def run(self, arg):
        # Get the highlighted identifier
        id = idaapi.get_highlighted_identifier()
        if not id:
            print "No identifier was highlighted"
            return

        import webbrowser

        try:
            import feedparser
        except:
            idaapi.warning('Feedparser package not installed')
            return

        id = self.sanitize_name(id)
        print "Looking up '%s' in MSDN online" % id
        d = feedparser.parse("http://social.msdn.microsoft.com/Search/Feed.aspx?locale=en-us&format=RSS&Query=%s" % id)
        if len(d['entries']) > 0:
            url = d['entries'][0].link
            webbrowser.open_new_tab(url)
        else:
            print "API documentation not found for: %s" % id


    def term(self):
        pass


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


repository: mxmssh/idapython
file: Scripts/msdnapihelp.py
description: Automatically exported from code.google.com/p/idapython
wanted_name: MSDN API Help
comment: Online MSDN API Help
created_at: 2015-04-10 06:53:38+00:00
pushed_at: 2015-04-10 06:56:13+00:00
url:
https://github.com/mxmssh/idapython/blob/081b988a03b88867786ad4131269db6930637a5b/Scripts/msdnapihelp.py

content:
"""
User contributed script: MSDN API HELP plugin

This script fetches the API reference (from MSDN) of a given highlighted identifier
and returns the results in a new web browser page.

This script depends on the feedparser package: http://code.google.com/p/feedparser/

10/05/2010
- initial version


"""

import idaapi

# -----------------------------------------------------------------------
class msdnapihelp_plugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "Online MSDN API Help"
    help = "Help me"
    wanted_name = "MSDN API Help"
    wanted_hotkey = "F3"

    def init(self):
        return idaapi.PLUGIN_OK


    @staticmethod
    def sanitize_name(name):
        t = idaapi.FUNC_IMPORT_PREFIX
        if name.startswith(t):
            return name[len(t):]
        return name


    def run(self, arg):
        # Get the highlighted identifier
        id = idaapi.get_highlighted_identifier()
        if not id:
            print "No identifier was highlighted"
            return

        import webbrowser

        try:
            import feedparser
        except:
            idaapi.warning('Feedparser package not installed')
            return

        id = self.sanitize_name(id)
        print "Looking up '%s' in MSDN online" % id
        d = feedparser.parse("http://social.msdn.microsoft.com/Search/Feed.aspx?locale=en-us&format=RSS&Query=%s" % id)
        if len(d['entries']) > 0:
            url = d['entries'][0].link
            webbrowser.open_new_tab(url)
        else:
            print "API documentation not found for: %s" % id


    def term(self):
        pass


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


repository: hoyori/idapython
file: Scripts/msdnapihelp.py
description: Automatically exported from code.google.com/p/idapython
wanted_name: MSDN API Help
comment: Online MSDN API Help
created_at: 2015-04-04 07:26:57+00:00
pushed_at: 2015-04-04 07:30:32+00:00
url:
https://github.com/hoyori/idapython/blob/081b988a03b88867786ad4131269db6930637a5b/Scripts/msdnapihelp.py


repository: Hehouhua/idapython
file: Scripts/msdnapihelp.py
description: Automatically exported from code.google.com/p/idapython
wanted_name: MSDN API Help
comment: Online MSDN API Help
created_at: 2016-04-08 08:07:27+00:00
pushed_at: 2016-04-08 08:10:01+00:00
url:
https://github.com/Hehouhua/idapython/blob/518dc295c8a9747834ddbe19566c2d2924d8ad01/Scripts/msdnapihelp.py


repository: BraveCattle/ctf
file: tools/ida_pro/IDA_Pro_7.7/plugins/auto_re.py
wanted_name: Auto RE
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/plugins/auto_re.py

content:
# -*- coding: utf-8 -*
__author__ = 'Trafimchuk Aliaksandr'
__version__ = '1.8'

from collections import defaultdict
import idaapi
from idautils import FuncItems, CodeRefsTo
from idaapi import o_reg, o_imm, o_far, o_near, o_mem, o_displ
import os
import re
import sys
import traceback


HAS_PYSIDE = idaapi.IDA_SDK_VERSION < 690
if HAS_PYSIDE:
    from PySide import QtGui, QtCore
    from PySide.QtGui import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QAction, QTabWidget
else:
    from PyQt5 import QtGui, QtCore
    from PyQt5.QtWidgets import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QAction, QTabWidget


try:
    # Python 2.
    xrange
except NameError:
    # Python 3.
    xrange = range


# enable to allow PyCharm remote debug
RDEBUG = False
# adjust this value to be a full path to a debug egg
RDEBUG_EGG = r'c:\Program Files\JetBrains\PyCharm 2017.1.4\debug-eggs\pycharm-debug.egg'
RDEBUG_HOST = 'localhost'
RDEBUG_PORT = 12321


TAGS_IGNORE_LIST = {
    'OpenProcessToken',
    'DisconnectNamedPipe'
}

IGNORE_CALL_LIST = {
    'RtlNtStatusToDosError',
    'GetLastError',
    'SetLastError'
}

TAGS = {
    'net': ['WSAStartup', 'socket', 'recv', 'recvfrom', 'send', 'sendto', 'acccept', 'bind', 'listen', 'select',
            'setsockopt', 'ioctlsocket', 'closesocket', 'WSAAccept', 'WSARecv', 'WSARecvFrom', 'WSASend', 'WSASendTo',
            'WSASocket', 'WSAConnect', 'ConnectEx', 'TransmitFile', 'HTTPOpenRequest', 'HTTPSendRequest',
            'URLDownloadToFile', 'InternetCrackUrl', 'InternetOpen', 'InternetOpen', 'InternetConnect',
            'InternetOpenUrl', 'InternetQueryOption', 'InternetSetOption', 'InternetReadFile', 'InternetWriteFile',
            'InternetGetConnectedState', 'InternetSetStatusCallback', 'DnsQuery', 'getaddrinfo', 'GetAddrInfo',
            'GetAdaptersInfo', 'GetAdaptersAddresses', 'HttpQueryInfo', 'ObtainUserAgentString', 'WNetGetProviderName',
            'GetBestInterfaceEx', 'gethostbyname', 'getsockname', 'connect', 'WinHttpOpen', 'WinHttpSetTimeouts',
            'WinHttpSendRequest', 'WinHttpConnect', 'WinHttpCrackUrl', 'WinHttpReadData', 'WinHttpOpenRequest',
            'WinHttpReceiveResponse', 'WinHttpQueryHeaders', 'HttpSendRequestW', 'HttpSendRequestA', 'HttpAddRequestHeadersW', 'HttpAddRequestHeadersA', 'HttpOpenRequestW', 'HttpOpenRequestA', 'NetServerGetInfo', 'NetApiBufferFree', 'NetWkstaGetInfo'],
    'spawn': ['CreateProcess', 'ShellExecute', 'ShellExecuteEx', 'system', 'CreateProcessInternal', 'NtCreateProcess',
              'ZwCreateProcess', 'NtCreateProcessEx', 'ZwCreateProcessEx', 'NtCreateUserProcess', 'ZwCreateUserProcess',
              'RtlCreateUserProcess', 'NtCreateSection', 'ZwCreateSection', 'NtOpenSection', 'ZwOpenSection',
              'NtAllocateVirtualMemory', 'ZwAllocateVirtualMemory', 'NtWriteVirtualMemory', 'ZwWriteVirtualMemory',
              'NtMapViewOfSection', 'ZwMapViewOfSection', 'OpenSCManager', 'CreateService', 'OpenService',
              'StartService', 'ControlService', 'ShellExecuteExA', 'ShellExecuteExW'],
    'inject': ['OpenProcess-disabled', 'ZwOpenProcess', 'NtOpenProcess', 'WriteProcessMemory', 'NtWriteVirtualMemory',
               'ZwWriteVirtualMemory', 'CreateRemoteThread', 'QueueUserAPC', 'ZwUnmapViewOfSection', 'NtUnmapViewOfSection'],
    'com': ['CoCreateInstance', 'CoInitializeSecurity', 'CoGetClassObject', 'OleConvertOLESTREAMToIStorage', 'CreateBindCtx', 'CoSetProxyBlanket', 'VariantClear'],
    'crypto': ['CryptAcquireContext', 'CryptProtectData', 'CryptUnprotectData', 'CryptProtectMemory',
               'CryptUnprotectMemory', 'CryptDecrypt', 'CryptEncrypt', 'CryptHashData', 'CryptDecodeMessage',
               'CryptDecryptMessage', 'CryptEncryptMessage', 'CryptHashMessage', 'CryptExportKey', 'CryptGenKey',
               'CryptCreateHash', 'CryptDecodeObjectEx', 'EncryptMessage', 'DecryptMessage'],
    'kbd': ['SendInput', 'VkKeyScanA', 'VkKeyScanW'],
    'file': ['_open64', 'open64', 'open', 'open64', 'fopen', 'fread', 'fclose', 'fwrite', 'flock', 'read', 'write',
             'fstat', 'lstat', 'stat', 'chmod', 'chown', 'lchown', 'link', 'symlink', 'readdir', 'readdir64'],
    'reg': ['RegOpenKeyExW', 'RegQueryValueExW', 'RegSetValueExW', 'RegCreateKeyExW', 'RegDeleteValueW', 'RegEnumKeyW', 'RegCloseKey', 'RegQueryInfoKeyW', 'RegOpenKeyExA', 'RegQueryValueExA', 'RegSetValueExA', 'RegCreateKeyExA', 'RegDeleteValueA', 'RegEnumKeyA',  'RegQueryInfoKeyA'],
    'dev': ['DeviceIoControl'],
    'wow': ['Wow64DisableWow64FsRedirection', 'Wow64RevertWow64FsRedirection']
}

STRICT_TAG_NAME_CHECKING = {'file'}

blacklist = {'@__security_check_cookie@4', '__SEH_prolog4', '__SEH_epilog4'}
replacements = [
    ('??3@YAXPAX@Z', 'alloc'),
    ('?', '')
]


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


def decode_insn(ea):
    if idaapi.IDA_SDK_VERSION >= 700 and sys.maxsize > 2**32:
        insn = idaapi.insn_t()
        if idaapi.decode_insn(insn, ea) > 0:
            return insn
    else:
        if idaapi.decode_insn(ea):
            return idaapi.cmd.copy()


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


class AutoReIDPHooks(idaapi.IDP_Hooks):
    """
    Hooks to keep view updated if some function is updated
    """
    def __init__(self, view, *args):
        super(AutoReIDPHooks, self).__init__(*args)
        self._view = view

    def __on_rename(self, ea, new_name):
        if not self._view:
            return
        items = self._view._model.findItems(('%0' + get_addr_width() + 'X') % ea, QtCore.Qt.MatchRecursive)
        if len(items) != 1:
            return

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

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

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

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

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


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

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

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

    def Show(self):
        return idaapi.PluginForm.Show(self, 'AutoRE', options=self.OPT_FORM_PERSIST)

    def _get_parent_widget(self, form):
        if HAS_PYSIDE:
            return self.FormToPySideWidget(form)
        return self.FormToPyQtWidget(form)

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

        self._idp_hooks = AutoReIDPHooks(self)
        if not self._idp_hooks.hook():
            print('IDP_Hooks.hook() failed')

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

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

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

        self.parent.setLayout(root_layout)

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

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

        self.tv.expandAll()

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

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

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

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

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

        menu = QMenu()
        rename_action = menu.addAction('Rename `%s`...' % old_name)
        rename_action.setShortcut('n')
        action = menu.exec_(self.tv.mapToGlobal(pos))
        if action == rename_action:
            return self._rename_ea_requested(addr, name_idx)

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

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

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

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

        return self._rename_ea_requested(addr, name_idx)

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

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

        if new_name is None:
            return

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

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

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

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

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

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

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

    def _tv_make_ref_item(self, tag, ref):
        ea_item = QtGui.QStandardItem(('%0' + get_addr_width() + 'X') % ref['ea'])
        ea_item.setEditable(False)
        ea_item.setData(ref['ea'], self.ADDR_ROLE)

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

        apis = ', '.join(ref['tags'][tag])
        api_name = QtGui.QStandardItem(apis)
        api_name.setEditable(False)
        api_name.setData(ref['ea'], self.ADDR_ROLE)
        api_name.setToolTip(apis)

        return [ea_item, name_item, api_name]

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

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

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

            root_node.appendRow(item_tag_list)

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

                item_tag.appendRow(ref_item_list)

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

    # def on_filter_text_changed(self, text):
    #     print('on_text_changed: %s' % text)


class auto_re_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = ""

    help = ""
    wanted_name = "Auto RE"
    wanted_hotkey = "Ctrl+Shift+M"

    _PREFIX_NAME = 'au_re_'
    _MIN_MAX_MATH_OPS_TO_ALLOW_RENAME = 10

    _CALLEE_NODE_NAMES = {
        idaapi.PLFM_MIPS: '$ mips',
        idaapi.PLFM_ARM: '$ arm'
    }
    _DEFAULT_CALLEE_NODE_NAME = '$ vmm functions'

    _JMP_TYPES = {idaapi.NN_jmp, idaapi.NN_jmpni, idaapi.NN_jmpfi, idaapi.NN_jmpshort}

    def __init__(self):
        super(auto_re_t, self).__init__()
        self._data = None
        self.view = None

    def init(self):
        # self._cfg = None
        self.view = None
        # self._load_config()

        return idaapi.PLUGIN_OK

    # def _load_config(self):
    #     self._cfg = {'auto_rename': False}

    # def _store_config(self, cfg):
    #     pass

    def _handle_tags(self, fn, fn_an, known_refs):
        if known_refs:
            known_refs = dict(known_refs)
            for k, names in known_refs.items():
                existing = set(fn_an['tags'][k])
                new = set(names) - existing
                if new:
                    fn_an['tags'][k] += list(new)

        tags = dict(fn_an['tags'])
        if not tags:
            return
        print('fn: %#08x tags: %s' % (self.start_ea_of(fn), tags))
        cmt = idaapi.get_func_cmt(fn, True)
        if cmt:
            cmt += '\n'
        s = str(tags.keys())
        name = idaapi.get_ea_name(self.start_ea_of(fn))
        item = {'ea': self.start_ea_of(fn), 'name': name, 'tags': tags}
        if not cmt or s not in cmt:
            idaapi.set_func_cmt(fn, '%sTAGS: %s' % (cmt or '', s), True)
        # self.mark_position(self.start_ea_of(fn), 'TAGS: %s' % s)
        for tag in tags:
            if tag not in self._data:
                self._data[tag] = list()
            self._data[tag].append(item)

    def _handle_calls(self, fn, fn_an):
        num_calls = len(fn_an['calls'])
        if num_calls != 1:
            return

        dis = fn_an['calls'][0]
        if dis.Op1.type not in (o_imm, o_far, o_near, o_mem):
            return

        ea = dis.Op1.value
        if not ea and dis.Op1.addr:
            ea = dis.Op1.addr

        if idaapi.has_dummy_name(self.get_flags_at(ea)):
            return

        # TODO: check is there jmp, push+retn then don't rename the func
        if fn_an['strange_flow']:
            return

        possible_name = idaapi.get_ea_name(ea)
        if not possible_name or possible_name in blacklist:
            return

        normalized = self.normalize_name(possible_name)

        # if self._cfg.get('auto_rename'):
        if len(fn_an['math']) < self._MIN_MAX_MATH_OPS_TO_ALLOW_RENAME:
            force_name(self.start_ea_of(fn), normalized)
        # TODO: add an API to the view
        print('fn: %#08x: %d calls, %d math%s possible name: %s, normalized: %s' % (
            self.start_ea_of(fn), len(fn_an['calls']), len(fn_an['math']), 'has bads' if fn_an['has_bads'] else '',
            possible_name, normalized))

    # noinspection PyMethodMayBeStatic
    def _check_is_jmp_wrapper(self, dis):
        # checks instructions like `jmp API`
        if dis.itype not in self._JMP_TYPES:
            return

        # handle call wrappers like jmp GetProcAddress
        if dis.Op1.type == idaapi.o_mem and dis.Op1.addr:
            # TODO: check is there better way to determine is the function a wrapper
            v = dis.Op1.addr
            flags = self.get_flags_at(v)
            if v and dis.itype == idaapi.NN_jmpni and self.is_data(flags) and self.__is_ptr_val(flags):
                v = self.__get_ptr_val(v)
            return v

    # noinspection PyMethodMayBeStatic
    def _check_is_push_retn_wrapper(self, dis0, dis1):
        """
        Checks for sequence of push IMM32/retn
        :param dis0: the first insn
        :param dis1: the second insn
        :return: value of IMM32
        """
        if dis0.itype != idaapi.NN_push or dis0.Op1.type != idaapi.o_imm or not dis0.Op1.value:
            return

        if dis1.itype not in (idaapi.NN_retn,):
            return

        return dis0.Op1.value

    def _preprocess_api_wrappers(self, fnqty):
        rv = defaultdict(dict)

        for i in xrange(fnqty):
            fn = idaapi.getn_func(i)
            items = list(FuncItems(self.start_ea_of(fn)))
            if len(items) not in (1, 2):
                continue

            dis0 = decode_insn(items[0])
            if dis0 is None:
                continue
            addr = self._check_is_jmp_wrapper(dis0)

            if not addr and len(items) > 1:
                dis1 = decode_insn(items[1])
                if dis1 is not None:
                    addr = self._check_is_push_retn_wrapper(dis0, dis1)

            if not addr:
                continue

            name = idaapi.get_ea_name(addr)
            name = name.replace(idaapi.FUNC_IMPORT_PREFIX, '')
            if not name:
                continue

            imp_stripped_name = name.lstrip('_')

            for tag, names in TAGS.items():
                for tag_api in names:
                    if tag in STRICT_TAG_NAME_CHECKING:
                        match = tag_api in (name, imp_stripped_name)
                    else:
                        match = tag_api in name
                    if not match:
                        continue

                    refs = list(CodeRefsTo(self.start_ea_of(fn), 1))

                    for ref in refs:
                        ref_fn = idaapi.get_func(ref)
                        if not ref_fn:
                            # idaapi.msg('AutoRE: there is no func for ref: %08x for api: %s' % (ref, name))
                            continue
                        if tag not in rv[self.start_ea_of(ref_fn)]:
                            rv[self.start_ea_of(ref_fn)][tag] = list()
                        if name not in rv[self.start_ea_of(ref_fn)][tag]:
                            rv[self.start_ea_of(ref_fn)][tag].append(name)
        return dict(rv)

    def run(self, arg):
        if RDEBUG and RDEBUG_EGG:
            if not os.path.isfile(RDEBUG_EGG):
                idaapi.msg('AutoRE: Remote debug is enabled, but I cannot find the debug egg: %s' % RDEBUG_EGG)
            else:
                import sys

                if RDEBUG_EGG not in sys.path:
                    sys.path.append(RDEBUG_EGG)

                import pydevd
                pydevd.settrace(RDEBUG_HOST, port=RDEBUG_PORT, stdoutToServer=True, stderrToServer=True)

        try:
            self._data = dict()
            count = idaapi.get_func_qty()

            # pre-process of api wrapper functions
            known_refs_tags = self._preprocess_api_wrappers(count)

            for i in xrange(count):
                fn = idaapi.getn_func(i)
                fn_an = self.analyze_func(fn)

                # if fn_an['math']:
                # 	print('fn: %#08x has math' % self.start_ea_of(fn))

                if idaapi.has_dummy_name(self.get_flags_at(self.start_ea_of(fn))):
                    self._handle_calls(fn, fn_an)

                known_refs = known_refs_tags.get(self.start_ea_of(fn))
                self._handle_tags(fn, fn_an, known_refs)

            if self.view:
                self.view.Close(AutoREView.OPT_FORM_NO_CONTEXT)
            self.view = AutoREView(self._data)
            self.view.Show()
        except:
            idaapi.msg('AutoRE: error: %s\n' % traceback.format_exc())

    def term(self):
        self._data = None

    @classmethod
    def disasm_func(cls, fn):
        rv = list()
        items = list(FuncItems(cls.start_ea_of(fn)))
        for item_ea in items:
            obj = {'ea': item_ea, 'fn_ea': cls.start_ea_of(fn), 'dis': None}
            insn = decode_insn(item_ea)
            if insn is not None:
                obj['dis'] = insn
            rv.append(obj)
        return rv

    @classmethod
    def get_callee_netnode(cls):
        node_name = cls._CALLEE_NODE_NAMES.get(idaapi.ph.id, cls._DEFAULT_CALLEE_NODE_NAME)
        n = idaapi.netnode(node_name)
        return n

    @classmethod
    def get_callee(cls, ea):
        n = cls.get_callee_netnode()
        v = n.altval(ea)
        v -= 1
        if v == idaapi.BADNODE:
            return
        return v

    @classmethod
    def _analysis_handle_call_insn(cls, dis, rv):
        rv['calls'].append(dis)
        if dis.Op1.type != o_mem or not dis.Op1.addr:
            callee = cls.get_callee(dis.ip)
            if not callee:
                return
        else:
            callee = dis.Op1.addr

        cls._apply_tag_on_callee(callee, rv, is_call=True)

    @classmethod
    def _apply_tag_on_callee(cls, callee_ea, rv, is_call=False):
        name = idaapi.get_ea_name(callee_ea)
        name = name.replace(idaapi.FUNC_IMPORT_PREFIX, '')

        if '@' in name:
            name = name.split('@')[0]

        if not name:
            return

        if name in IGNORE_CALL_LIST:
            if is_call:
                rv['calls'].pop()
            return

        if name in TAGS_IGNORE_LIST:
            return

        for tag, names in TAGS.items():
            for tag_api in names:
                if tag in STRICT_TAG_NAME_CHECKING:
                    match = tag_api in (name, name.lstrip('_'))
                else:
                    match = tag_api in name
                if not match or name in rv['tags'][tag]:
                    continue

                # print('%#08x: %s, tag: %s' % (dis.ea, name, tag))
                rv['tags'][tag].append(name)
                break

    @classmethod
    def __is_ptr_val(cls, flags):
        if idaapi.IDA_SDK_VERSION >= 700:
            return (idaapi.is_qword if idaapi.cvar.inf.is_64bit() else idaapi.is_dword)(flags)
        return (idaapi.isQwrd if idaapi.cvar.inf.is_64bit() else idaapi.isDwrd)(flags)

    @classmethod
    def __get_ptr_val(cls, ea):
        if idaapi.cvar.inf.is_64bit():
            return idaapi.get_qword(ea)

        return (idaapi.get_dword if idaapi.IDA_SDK_VERSION >= 700 else idaapi.get_long)(ea)
        
    @classmethod
    def start_ea_of(cls, o):
        return getattr(o, 'start_ea' if idaapi.IDA_SDK_VERSION >= 700 else 'startEA')

    @classmethod
    def end_ea_of(cls, o):
        return getattr(o, 'end_ea' if idaapi.IDA_SDK_VERSION >= 700 else 'endEA')

    @classmethod
    def get_flags_at(cls, ea):
        return getattr(idaapi, 'get_flags' if idaapi.IDA_SDK_VERSION >= 700 else 'getFlags')(ea)

    @classmethod
    def is_data(cls, flags):
        return getattr(idaapi, 'is_data' if idaapi.IDA_SDK_VERSION >= 700 else 'isData')(flags)

    @classmethod
    def analyze_func(cls, fn):
        rv = {
            'fn': fn,
            'calls': [],
            'math': [],
            'has_bads': False,
            'strange_flow': False,
            'tags': defaultdict(list)
        }
        items = cls.disasm_func(fn)
        items_set = set(map(lambda x: x['ea'], items))

        for item in items:
            dis = item['dis']
            if dis is None:
                rv['has_bads'] = True
                continue

            if dis.itype in (idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni):
                cls._analysis_handle_call_insn(dis, rv)
            elif dis.itype == idaapi.NN_xor:
                if dis.Op1.type == o_reg and dis.Op2.type == o_reg and dis.Op1.reg == dis.Op2.reg:
                    continue
                rv['math'].append(dis)
            elif dis.itype in (idaapi.NN_shr, idaapi.NN_shl, idaapi.NN_sal, idaapi.NN_sar, idaapi.NN_ror,
                               idaapi.NN_rol, idaapi.NN_rcl, idaapi.NN_rcl):
                # TODO
                rv['math'].append(dis)
            elif dis.itype in cls._JMP_TYPES:
                if dis.Op1.type not in (o_far, o_near, o_mem, o_displ):
                    continue

                if dis.Op1.type == o_displ:
                    rv['strange_flow'] = True
                    continue

                ea = dis.Op1.value
                if not ea and dis.Op1.addr:
                    ea = dis.Op1.addr
                if ea not in items_set:
                    rv['strange_flow'] = True

                # flags = self.get_flags_at(ea)
                # if dis.itype == idaapi.NN_jmpni and dis.Op1.type == o_mem and ea and self.is_data(flags):
                #     if cls.__is_ptr_val(flags):
                #         val = cls.__get_ptr_val(ea)
                #         if val:
                #             cls._apply_tag_on_callee(val, rv, is_call=False)

        return rv

    @classmethod
    def normalize_name(cls, n):
        for repl in replacements:
            n = n.replace(*repl)
        if '@' in n:
            n = n.split('@')[0]
        if len(n) < 3:
            return ''
        if not n.startswith(cls._PREFIX_NAME):
            n = cls._PREFIX_NAME + n
        return n

    # @classmethod
    # def mark_position(cls, ea, name, slot=[0]):
    #     curloc = idaapi.curloc()
    #     curloc.ea = ea
    #     curloc.lnnum = 0
    #     curloc.x = 0
    #     curloc.y = 0
    #     slot[0] += 1
    #     curloc.mark(slot[0], name, name)


# noinspection PyPep8Naming
def PLUGIN_ENTRY():
    return auto_re_t()


repository: carsond135/malwareanalysis
file: tools/IDA_Pro_7.7/IDA_Pro_7.7/plugins/auto_re.py
description: NKU恶意代码分析
wanted_name: Auto RE
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/plugins/auto_re.py

content:
# -*- coding: utf-8 -*
__author__ = 'Trafimchuk Aliaksandr'
__version__ = '1.8'

from collections import defaultdict
import idaapi
from idautils import FuncItems, CodeRefsTo
from idaapi import o_reg, o_imm, o_far, o_near, o_mem, o_displ
import os
import re
import sys
import traceback


HAS_PYSIDE = idaapi.IDA_SDK_VERSION < 690
if HAS_PYSIDE:
    from PySide import QtGui, QtCore
    from PySide.QtGui import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QAction, QTabWidget
else:
    from PyQt5 import QtGui, QtCore
    from PyQt5.QtWidgets import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QAction, QTabWidget


try:
    # Python 2.
    xrange
except NameError:
    # Python 3.
    xrange = range


# enable to allow PyCharm remote debug
RDEBUG = False
# adjust this value to be a full path to a debug egg
RDEBUG_EGG = r'c:\Program Files\JetBrains\PyCharm 2017.1.4\debug-eggs\pycharm-debug.egg'
RDEBUG_HOST = 'localhost'
RDEBUG_PORT = 12321


TAGS_IGNORE_LIST = {
    'OpenProcessToken',
    'DisconnectNamedPipe'
}

IGNORE_CALL_LIST = {
    'RtlNtStatusToDosError',
    'GetLastError',
    'SetLastError'
}

TAGS = {
    'net': ['WSAStartup', 'socket', 'recv', 'recvfrom', 'send', 'sendto', 'acccept', 'bind', 'listen', 'select',
            'setsockopt', 'ioctlsocket', 'closesocket', 'WSAAccept', 'WSARecv', 'WSARecvFrom', 'WSASend', 'WSASendTo',
            'WSASocket', 'WSAConnect', 'ConnectEx', 'TransmitFile', 'HTTPOpenRequest', 'HTTPSendRequest',
            'URLDownloadToFile', 'InternetCrackUrl', 'InternetOpen', 'InternetOpen', 'InternetConnect',
            'InternetOpenUrl', 'InternetQueryOption', 'InternetSetOption', 'InternetReadFile', 'InternetWriteFile',
            'InternetGetConnectedState', 'InternetSetStatusCallback', 'DnsQuery', 'getaddrinfo', 'GetAddrInfo',
            'GetAdaptersInfo', 'GetAdaptersAddresses', 'HttpQueryInfo', 'ObtainUserAgentString', 'WNetGetProviderName',
            'GetBestInterfaceEx', 'gethostbyname', 'getsockname', 'connect', 'WinHttpOpen', 'WinHttpSetTimeouts',
            'WinHttpSendRequest', 'WinHttpConnect', 'WinHttpCrackUrl', 'WinHttpReadData', 'WinHttpOpenRequest',
            'WinHttpReceiveResponse', 'WinHttpQueryHeaders', 'HttpSendRequestW', 'HttpSendRequestA', 'HttpAddRequestHeadersW', 'HttpAddRequestHeadersA', 'HttpOpenRequestW', 'HttpOpenRequestA', 'NetServerGetInfo', 'NetApiBufferFree', 'NetWkstaGetInfo'],
    'spawn': ['CreateProcess', 'ShellExecute', 'ShellExecuteEx', 'system', 'CreateProcessInternal', 'NtCreateProcess',
              'ZwCreateProcess', 'NtCreateProcessEx', 'ZwCreateProcessEx', 'NtCreateUserProcess', 'ZwCreateUserProcess',
              'RtlCreateUserProcess', 'NtCreateSection', 'ZwCreateSection', 'NtOpenSection', 'ZwOpenSection',
              'NtAllocateVirtualMemory', 'ZwAllocateVirtualMemory', 'NtWriteVirtualMemory', 'ZwWriteVirtualMemory',
              'NtMapViewOfSection', 'ZwMapViewOfSection', 'OpenSCManager', 'CreateService', 'OpenService',
              'StartService', 'ControlService', 'ShellExecuteExA', 'ShellExecuteExW'],
    'inject': ['OpenProcess-disabled', 'ZwOpenProcess', 'NtOpenProcess', 'WriteProcessMemory', 'NtWriteVirtualMemory',
               'ZwWriteVirtualMemory', 'CreateRemoteThread', 'QueueUserAPC', 'ZwUnmapViewOfSection', 'NtUnmapViewOfSection'],
    'com': ['CoCreateInstance', 'CoInitializeSecurity', 'CoGetClassObject', 'OleConvertOLESTREAMToIStorage', 'CreateBindCtx', 'CoSetProxyBlanket', 'VariantClear'],
    'crypto': ['CryptAcquireContext', 'CryptProtectData', 'CryptUnprotectData', 'CryptProtectMemory',
               'CryptUnprotectMemory', 'CryptDecrypt', 'CryptEncrypt', 'CryptHashData', 'CryptDecodeMessage',
               'CryptDecryptMessage', 'CryptEncryptMessage', 'CryptHashMessage', 'CryptExportKey', 'CryptGenKey',
               'CryptCreateHash', 'CryptDecodeObjectEx', 'EncryptMessage', 'DecryptMessage'],
    'kbd': ['SendInput', 'VkKeyScanA', 'VkKeyScanW'],
    'file': ['_open64', 'open64', 'open', 'open64', 'fopen', 'fread', 'fclose', 'fwrite', 'flock', 'read', 'write',
             'fstat', 'lstat', 'stat', 'chmod', 'chown', 'lchown', 'link', 'symlink', 'readdir', 'readdir64'],
    'reg': ['RegOpenKeyExW', 'RegQueryValueExW', 'RegSetValueExW', 'RegCreateKeyExW', 'RegDeleteValueW', 'RegEnumKeyW', 'RegCloseKey', 'RegQueryInfoKeyW', 'RegOpenKeyExA', 'RegQueryValueExA', 'RegSetValueExA', 'RegCreateKeyExA', 'RegDeleteValueA', 'RegEnumKeyA',  'RegQueryInfoKeyA'],
    'dev': ['DeviceIoControl'],
    'wow': ['Wow64DisableWow64FsRedirection', 'Wow64RevertWow64FsRedirection']
}

STRICT_TAG_NAME_CHECKING = {'file'}

blacklist = {'@__security_check_cookie@4', '__SEH_prolog4', '__SEH_epilog4'}
replacements = [
    ('??3@YAXPAX@Z', 'alloc'),
    ('?', '')
]


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


def decode_insn(ea):
    if idaapi.IDA_SDK_VERSION >= 700 and sys.maxsize > 2**32:
        insn = idaapi.insn_t()
        if idaapi.decode_insn(insn, ea) > 0:
            return insn
    else:
        if idaapi.decode_insn(ea):
            return idaapi.cmd.copy()


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


class AutoReIDPHooks(idaapi.IDP_Hooks):
    """
    Hooks to keep view updated if some function is updated
    """
    def __init__(self, view, *args):
        super(AutoReIDPHooks, self).__init__(*args)
        self._view = view

    def __on_rename(self, ea, new_name):
        if not self._view:
            return
        items = self._view._model.findItems(('%0' + get_addr_width() + 'X') % ea, QtCore.Qt.MatchRecursive)
        if len(items) != 1:
            return

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

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

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

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

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


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

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

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

    def Show(self):
        return idaapi.PluginForm.Show(self, 'AutoRE', options=self.OPT_FORM_PERSIST)

    def _get_parent_widget(self, form):
        if HAS_PYSIDE:
            return self.FormToPySideWidget(form)
        return self.FormToPyQtWidget(form)

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

        self._idp_hooks = AutoReIDPHooks(self)
        if not self._idp_hooks.hook():
            print('IDP_Hooks.hook() failed')

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

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

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

        self.parent.setLayout(root_layout)

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

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

        self.tv.expandAll()

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

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

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

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

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

        menu = QMenu()
        rename_action = menu.addAction('Rename `%s`...' % old_name)
        rename_action.setShortcut('n')
        action = menu.exec_(self.tv.mapToGlobal(pos))
        if action == rename_action:
            return self._rename_ea_requested(addr, name_idx)

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

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

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

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

        return self._rename_ea_requested(addr, name_idx)

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

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

        if new_name is None:
            return

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

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

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

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

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

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

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

    def _tv_make_ref_item(self, tag, ref):
        ea_item = QtGui.QStandardItem(('%0' + get_addr_width() + 'X') % ref['ea'])
        ea_item.setEditable(False)
        ea_item.setData(ref['ea'], self.ADDR_ROLE)

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

        apis = ', '.join(ref['tags'][tag])
        api_name = QtGui.QStandardItem(apis)
        api_name.setEditable(False)
        api_name.setData(ref['ea'], self.ADDR_ROLE)
        api_name.setToolTip(apis)

        return [ea_item, name_item, api_name]

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

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

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

            root_node.appendRow(item_tag_list)

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

                item_tag.appendRow(ref_item_list)

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

    # def on_filter_text_changed(self, text):
    #     print('on_text_changed: %s' % text)


class auto_re_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = ""

    help = ""
    wanted_name = "Auto RE"
    wanted_hotkey = "Ctrl+Shift+M"

    _PREFIX_NAME = 'au_re_'
    _MIN_MAX_MATH_OPS_TO_ALLOW_RENAME = 10

    _CALLEE_NODE_NAMES = {
        idaapi.PLFM_MIPS: '$ mips',
        idaapi.PLFM_ARM: '$ arm'
    }
    _DEFAULT_CALLEE_NODE_NAME = '$ vmm functions'

    _JMP_TYPES = {idaapi.NN_jmp, idaapi.NN_jmpni, idaapi.NN_jmpfi, idaapi.NN_jmpshort}

    def __init__(self):
        super(auto_re_t, self).__init__()
        self._data = None
        self.view = None

    def init(self):
        # self._cfg = None
        self.view = None
        # self._load_config()

        return idaapi.PLUGIN_OK

    # def _load_config(self):
    #     self._cfg = {'auto_rename': False}

    # def _store_config(self, cfg):
    #     pass

    def _handle_tags(self, fn, fn_an, known_refs):
        if known_refs:
            known_refs = dict(known_refs)
            for k, names in known_refs.items():
                existing = set(fn_an['tags'][k])
                new = set(names) - existing
                if new:
                    fn_an['tags'][k] += list(new)

        tags = dict(fn_an['tags'])
        if not tags:
            return
        print('fn: %#08x tags: %s' % (self.start_ea_of(fn), tags))
        cmt = idaapi.get_func_cmt(fn, True)
        if cmt:
            cmt += '\n'
        s = str(tags.keys())
        name = idaapi.get_ea_name(self.start_ea_of(fn))
        item = {'ea': self.start_ea_of(fn), 'name': name, 'tags': tags}
        if not cmt or s not in cmt:
            idaapi.set_func_cmt(fn, '%sTAGS: %s' % (cmt or '', s), True)
        # self.mark_position(self.start_ea_of(fn), 'TAGS: %s' % s)
        for tag in tags:
            if tag not in self._data:
                self._data[tag] = list()
            self._data[tag].append(item)

    def _handle_calls(self, fn, fn_an):
        num_calls = len(fn_an['calls'])
        if num_calls != 1:
            return

        dis = fn_an['calls'][0]
        if dis.Op1.type not in (o_imm, o_far, o_near, o_mem):
            return

        ea = dis.Op1.value
        if not ea and dis.Op1.addr:
            ea = dis.Op1.addr

        if idaapi.has_dummy_name(self.get_flags_at(ea)):
            return

        # TODO: check is there jmp, push+retn then don't rename the func
        if fn_an['strange_flow']:
            return

        possible_name = idaapi.get_ea_name(ea)
        if not possible_name or possible_name in blacklist:
            return

        normalized = self.normalize_name(possible_name)

        # if self._cfg.get('auto_rename'):
        if len(fn_an['math']) < self._MIN_MAX_MATH_OPS_TO_ALLOW_RENAME:
            force_name(self.start_ea_of(fn), normalized)
        # TODO: add an API to the view
        print('fn: %#08x: %d calls, %d math%s possible name: %s, normalized: %s' % (
            self.start_ea_of(fn), len(fn_an['calls']), len(fn_an['math']), 'has bads' if fn_an['has_bads'] else '',
            possible_name, normalized))

    # noinspection PyMethodMayBeStatic
    def _check_is_jmp_wrapper(self, dis):
        # checks instructions like `jmp API`
        if dis.itype not in self._JMP_TYPES:
            return

        # handle call wrappers like jmp GetProcAddress
        if dis.Op1.type == idaapi.o_mem and dis.Op1.addr:
            # TODO: check is there better way to determine is the function a wrapper
            v = dis.Op1.addr
            flags = self.get_flags_at(v)
            if v and dis.itype == idaapi.NN_jmpni and self.is_data(flags) and self.__is_ptr_val(flags):
                v = self.__get_ptr_val(v)
            return v

    # noinspection PyMethodMayBeStatic
    def _check_is_push_retn_wrapper(self, dis0, dis1):
        """
        Checks for sequence of push IMM32/retn
        :param dis0: the first insn
        :param dis1: the second insn
        :return: value of IMM32
        """
        if dis0.itype != idaapi.NN_push or dis0.Op1.type != idaapi.o_imm or not dis0.Op1.value:
            return

        if dis1.itype not in (idaapi.NN_retn,):
            return

        return dis0.Op1.value

    def _preprocess_api_wrappers(self, fnqty):
        rv = defaultdict(dict)

        for i in xrange(fnqty):
            fn = idaapi.getn_func(i)
            items = list(FuncItems(self.start_ea_of(fn)))
            if len(items) not in (1, 2):
                continue

            dis0 = decode_insn(items[0])
            if dis0 is None:
                continue
            addr = self._check_is_jmp_wrapper(dis0)

            if not addr and len(items) > 1:
                dis1 = decode_insn(items[1])
                if dis1 is not None:
                    addr = self._check_is_push_retn_wrapper(dis0, dis1)

            if not addr:
                continue

            name = idaapi.get_ea_name(addr)
            name = name.replace(idaapi.FUNC_IMPORT_PREFIX, '')
            if not name:
                continue

            imp_stripped_name = name.lstrip('_')

            for tag, names in TAGS.items():
                for tag_api in names:
                    if tag in STRICT_TAG_NAME_CHECKING:
                        match = tag_api in (name, imp_stripped_name)
                    else:
                        match = tag_api in name
                    if not match:
                        continue

                    refs = list(CodeRefsTo(self.start_ea_of(fn), 1))

                    for ref in refs:
                        ref_fn = idaapi.get_func(ref)
                        if not ref_fn:
                            # idaapi.msg('AutoRE: there is no func for ref: %08x for api: %s' % (ref, name))
                            continue
                        if tag not in rv[self.start_ea_of(ref_fn)]:
                            rv[self.start_ea_of(ref_fn)][tag] = list()
                        if name not in rv[self.start_ea_of(ref_fn)][tag]:
                            rv[self.start_ea_of(ref_fn)][tag].append(name)
        return dict(rv)

    def run(self, arg):
        if RDEBUG and RDEBUG_EGG:
            if not os.path.isfile(RDEBUG_EGG):
                idaapi.msg('AutoRE: Remote debug is enabled, but I cannot find the debug egg: %s' % RDEBUG_EGG)
            else:
                import sys

                if RDEBUG_EGG not in sys.path:
                    sys.path.append(RDEBUG_EGG)

                import pydevd
                pydevd.settrace(RDEBUG_HOST, port=RDEBUG_PORT, stdoutToServer=True, stderrToServer=True)

        try:
            self._data = dict()
            count = idaapi.get_func_qty()

            # pre-process of api wrapper functions
            known_refs_tags = self._preprocess_api_wrappers(count)

            for i in xrange(count):
                fn = idaapi.getn_func(i)
                fn_an = self.analyze_func(fn)

                # if fn_an['math']:
                # 	print('fn: %#08x has math' % self.start_ea_of(fn))

                if idaapi.has_dummy_name(self.get_flags_at(self.start_ea_of(fn))):
                    self._handle_calls(fn, fn_an)

                known_refs = known_refs_tags.get(self.start_ea_of(fn))
                self._handle_tags(fn, fn_an, known_refs)

            if self.view:
                self.view.Close(AutoREView.OPT_FORM_NO_CONTEXT)
            self.view = AutoREView(self._data)
            self.view.Show()
        except:
            idaapi.msg('AutoRE: error: %s\n' % traceback.format_exc())

    def term(self):
        self._data = None

    @classmethod
    def disasm_func(cls, fn):
        rv = list()
        items = list(FuncItems(cls.start_ea_of(fn)))
        for item_ea in items:
            obj = {'ea': item_ea, 'fn_ea': cls.start_ea_of(fn), 'dis': None}
            insn = decode_insn(item_ea)
            if insn is not None:
                obj['dis'] = insn
            rv.append(obj)
        return rv

    @classmethod
    def get_callee_netnode(cls):
        node_name = cls._CALLEE_NODE_NAMES.get(idaapi.ph.id, cls._DEFAULT_CALLEE_NODE_NAME)
        n = idaapi.netnode(node_name)
        return n

    @classmethod
    def get_callee(cls, ea):
        n = cls.get_callee_netnode()
        v = n.altval(ea)
        v -= 1
        if v == idaapi.BADNODE:
            return
        return v

    @classmethod
    def _analysis_handle_call_insn(cls, dis, rv):
        rv['calls'].append(dis)
        if dis.Op1.type != o_mem or not dis.Op1.addr:
            callee = cls.get_callee(dis.ip)
            if not callee:
                return
        else:
            callee = dis.Op1.addr

        cls._apply_tag_on_callee(callee, rv, is_call=True)

    @classmethod
    def _apply_tag_on_callee(cls, callee_ea, rv, is_call=False):
        name = idaapi.get_ea_name(callee_ea)
        name = name.replace(idaapi.FUNC_IMPORT_PREFIX, '')

        if '@' in name:
            name = name.split('@')[0]

        if not name:
            return

        if name in IGNORE_CALL_LIST:
            if is_call:
                rv['calls'].pop()
            return

        if name in TAGS_IGNORE_LIST:
            return

        for tag, names in TAGS.items():
            for tag_api in names:
                if tag in STRICT_TAG_NAME_CHECKING:
                    match = tag_api in (name, name.lstrip('_'))
                else:
                    match = tag_api in name
                if not match or name in rv['tags'][tag]:
                    continue

                # print('%#08x: %s, tag: %s' % (dis.ea, name, tag))
                rv['tags'][tag].append(name)
                break

    @classmethod
    def __is_ptr_val(cls, flags):
        if idaapi.IDA_SDK_VERSION >= 700:
            return (idaapi.is_qword if idaapi.cvar.inf.is_64bit() else idaapi.is_dword)(flags)
        return (idaapi.isQwrd if idaapi.cvar.inf.is_64bit() else idaapi.isDwrd)(flags)

    @classmethod
    def __get_ptr_val(cls, ea):
        if idaapi.cvar.inf.is_64bit():
            return idaapi.get_qword(ea)

        return (idaapi.get_dword if idaapi.IDA_SDK_VERSION >= 700 else idaapi.get_long)(ea)
        
    @classmethod
    def start_ea_of(cls, o):
        return getattr(o, 'start_ea' if idaapi.IDA_SDK_VERSION >= 700 else 'startEA')

    @classmethod
    def end_ea_of(cls, o):
        return getattr(o, 'end_ea' if idaapi.IDA_SDK_VERSION >= 700 else 'endEA')

    @classmethod
    def get_flags_at(cls, ea):
        return getattr(idaapi, 'get_flags' if idaapi.IDA_SDK_VERSION >= 700 else 'getFlags')(ea)

    @classmethod
    def is_data(cls, flags):
        return getattr(idaapi, 'is_data' if idaapi.IDA_SDK_VERSION >= 700 else 'isData')(flags)

    @classmethod
    def analyze_func(cls, fn):
        rv = {
            'fn': fn,
            'calls': [],
            'math': [],
            'has_bads': False,
            'strange_flow': False,
            'tags': defaultdict(list)
        }
        items = cls.disasm_func(fn)
        items_set = set(map(lambda x: x['ea'], items))

        for item in items:
            dis = item['dis']
            if dis is None:
                rv['has_bads'] = True
                continue

            if dis.itype in (idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni):
                cls._analysis_handle_call_insn(dis, rv)
            elif dis.itype == idaapi.NN_xor:
                if dis.Op1.type == o_reg and dis.Op2.type == o_reg and dis.Op1.reg == dis.Op2.reg:
                    continue
                rv['math'].append(dis)
            elif dis.itype in (idaapi.NN_shr, idaapi.NN_shl, idaapi.NN_sal, idaapi.NN_sar, idaapi.NN_ror,
                               idaapi.NN_rol, idaapi.NN_rcl, idaapi.NN_rcl):
                # TODO
                rv['math'].append(dis)
            elif dis.itype in cls._JMP_TYPES:
                if dis.Op1.type not in (o_far, o_near, o_mem, o_displ):
                    continue

                if dis.Op1.type == o_displ:
                    rv['strange_flow'] = True
                    continue

                ea = dis.Op1.value
                if not ea and dis.Op1.addr:
                    ea = dis.Op1.addr
                if ea not in items_set:
                    rv['strange_flow'] = True

                # flags = self.get_flags_at(ea)
                # if dis.itype == idaapi.NN_jmpni and dis.Op1.type == o_mem and ea and self.is_data(flags):
                #     if cls.__is_ptr_val(flags):
                #         val = cls.__get_ptr_val(ea)
                #         if val:
                #             cls._apply_tag_on_callee(val, rv, is_call=False)

        return rv

    @classmethod
    def normalize_name(cls, n):
        for repl in replacements:
            n = n.replace(*repl)
        if '@' in n:
            n = n.split('@')[0]
        if len(n) < 3:
            return ''
        if not n.startswith(cls._PREFIX_NAME):
            n = cls._PREFIX_NAME + n
        return n

    # @classmethod
    # def mark_position(cls, ea, name, slot=[0]):
    #     curloc = idaapi.curloc()
    #     curloc.ea = ea
    #     curloc.lnnum = 0
    #     curloc.x = 0
    #     curloc.y = 0
    #     slot[0] += 1
    #     curloc.mark(slot[0], name, name)


# noinspection PyPep8Naming
def PLUGIN_ENTRY():
    return auto_re_t()


repository: Ph0en1x-XMU/Ph0en1x-Team
file: Reverse/ida/IDA Pro 7.5.201028 (x86,x64,ARM,ARM64,PPC,PPC64,MIPS)/python/examples/hexrays/vds3.py
wanted_name: Hex-Rays if-inverter (IDAPython)
comment: Sample plugin3 for Hex-Rays decompiler
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%20Pro%207.5.201028%20(x86,x64,ARM,ARM64,PPC,PPC64,MIPS)/python/examples/hexrays/vds3.py

content:
""" Invert the then and else blocks of a cif_t.

Author: EiNSTeiN_ <einstein@g3nius.org>

This is a rewrite in Python of the vds3 example that comes with hexrays sdk.
"""
from __future__ import print_function

import idautils

import ida_kernwin
import ida_hexrays
import ida_netnode
import ida_idaapi

import traceback

NETNODE_NAME = '$ hexrays-inverted-if'

inverter_actname = "vds3:invert"

class invert_action_handler_t(ida_kernwin.action_handler_t):
    def __init__(self, inverter):
        ida_kernwin.action_handler_t.__init__(self)
        self.inverter = inverter

    def activate(self, ctx):
        vdui = ida_hexrays.get_widget_vdui(ctx.widget)
        self.inverter.invert_if_event(vdui)
        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 hexrays_callback_info(object):

    def __init__(self):
        self.vu = None

        self.node = ida_netnode.netnode()
        if not self.node.create(NETNODE_NAME):
            # node exists
            self.load()
        else:
            self.stored = []

        return

    def load(self):

        self.stored = []

        try:
            data = self.node.getblob(0, 'I')
            if data:
                self.stored = eval(data)
                print('Invert-if: Loaded %s' % (repr(self.stored), ))
        except:
            print('Failed to load invert-if locations')
            traceback.print_exc()
            return

        return

    def save(self):

        try:
            self.node.setblob(repr(self.stored), 0, 'I')
        except:
            print('Failed to save invert-if locations')
            traceback.print_exc()
            return

        return

    def invert_if(self, cfunc, insn):

        if insn.opname != 'if':
            return False

        cif = insn.details

        if not cif.ithen or not cif.ielse:
            return False

        ida_hexrays.qswap(cif.ithen, cif.ielse)
        # Make a copy of 'cif.expr': 'lnot' might destroy its toplevel
        # cexpr_t and return a pointer to its direct child (but we'll want to
        # 'swap' it later, the 'cif.expr' cexpr_t object must remain valid.)
        cond = ida_hexrays.cexpr_t(cif.expr)
        notcond = ida_hexrays.lnot(cond)

        cif.expr.swap(notcond)

        return True

    def add_location(self, ea):
        if ea in self.stored:
            self.stored.remove(ea)
        else:
            self.stored.append(ea)
        self.save()
        return

    def find_if_statement(self, vu):

        vu.get_current_item(ida_hexrays.USE_KEYBOARD)
        item = vu.item

        if item.is_citem() and item.it.op == ida_hexrays.cit_if and item.it.to_specific_type.cif.ielse is not None:
            return item.it.to_specific_type

        if vu.tail.citype == ida_hexrays.VDI_TAIL and vu.tail.loc.itp == ida_hexrays.ITP_ELSE:
            # for tail marks, we know only the corresponding ea,
            # not the pointer to if-statement
            # find it by walking the whole ctree
            class if_finder_t(ida_hexrays.ctree_visitor_t):
                def __init__(self, ea):
                    ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST | ida_hexrays.CV_INSNS)

                    self.ea = ea
                    self.found = None
                    return

                def visit_insn(self, i):
                    if i.op == ida_hexrays.cit_if and i.ea == self.ea:
                        self.found = i
                        return 1 # stop enumeration
                    return 0

            iff = if_finder_t(vu.tail.loc.ea)
            if iff.apply_to(vu.cfunc.body, None):
                return iff.found

        return

    def invert_if_event(self, vu):

        cfunc = vu.cfunc.__deref__()
        i = self.find_if_statement(vu)
        if not i:
            return False

        if self.invert_if(cfunc, i):
            vu.refresh_ctext()
            self.add_location(i.ea)

        return True

    def restore(self, cfunc):

        class visitor(ida_hexrays.ctree_visitor_t):

            def __init__(self, inverter, cfunc):
                ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST | ida_hexrays.CV_INSNS)
                self.inverter = inverter
                self.cfunc = cfunc
                return

            def visit_insn(self, i):
                try:
                    if i.op == ida_hexrays.cit_if and i.ea in self.inverter.stored:
                        self.inverter.invert_if(self.cfunc, i)
                except:
                    traceback.print_exc()
                return 0 # continue enumeration

        visitor(self, cfunc).apply_to(cfunc.body, None)

        return


class vds3_hooks_t(ida_hexrays.Hexrays_Hooks):
    def __init__(self, i):
        ida_hexrays.Hexrays_Hooks.__init__(self)
        self.i = i

    def populating_popup(self, widget, phandle, vu):
        ida_kernwin.attach_action_to_popup(vu.ct, None, inverter_actname)
        return 0

    def maturity(self, cfunc, maturity):
        if maturity == ida_hexrays.CMAT_FINAL:
            self.i.restore(cfunc)
        return 0

# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_HIDE
    wanted_name = "Hex-Rays if-inverter (IDAPython)"
    wanted_hotkey = ""
    comment = "Sample plugin3 for Hex-Rays decompiler"
    help = ""
    def init(self):
        if ida_hexrays.init_hexrays_plugin():
            i = hexrays_callback_info()
            ida_kernwin.register_action(
                ida_kernwin.action_desc_t(
                    inverter_actname,
                    "Invert then/else",
                    invert_action_handler_t(i),
                    "I"))
            self.vds3_hooks = vds3_hooks_t(i)
            self.vds3_hooks.hook()
            return ida_idaapi.PLUGIN_KEEP # keep us in the memory
    def term(self):
        self.vds3_hooks.unhook()
    def run(self, arg):
        pass

def PLUGIN_ENTRY():
    return my_plugin_t()


repository: BinSigma/BinSourcerer
file: IDA Pro Plugin/IdaProTextExtractor_HRC.py
description: BinSourcerer
wanted_name: BinSourcerer Features Extractor
comment: Extract all the available features
created_at: 2014-02-26 21:59:45+00:00
pushed_at: 2022-03-01 15:52:39+00:00
url:
https://github.com/BinSigma/BinSourcerer/blob/5958abc1d0091d60638291ebc1118990b4fa97dc/IDA%20Pro%20Plugin/IdaProTextExtractor_HRC.py

content:
#!/usr/bin/env python
# -*-coding: utf8 -*-
#
################################################################################
#
#  Copyright 2014 Concordia University
# 
#  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.
#
################################################################################
# Description:
#   This plugin extracts certain featuers from IDA Pro disassembly and
#   generates a feature file, which is used in an online code search process.
#   This file is part of the BinSourcerer framework.
#
# Status: Debug
#
################################################################################




DIRECTION_FORW = 0x03

import idautils
import os

from idaapi import *
import hashlib
import idc

#Offline Analyzer (OA), Windows function names and a brief description.
WinMalFuncInSight = {
    'accept':'The program may listen for incoming connections on a socket.',
    'AdjustTokenPrivileges':'The program may modify specific access privileges.',
    'AttachThreadInput':'The program may contain keylogging or spyware functionality.',
    'bind':'The program may listen for incoming connections.',
    'BitBlt':'The program may capture screenshots.',
    'CallNextHookEx':'The code calls the next hook in the chain set by SetWindowsHookEx.',
    'CertOpenSystemStore':'The program may access the certificates stored on the local system.',
    'CheckRemoteDebuggerPresent':'The code may contain an anti-debugging technique.',
    'CoCreateInstance':'The code creates a COM object.',
    'connect':'The program may connect to a remote command-and-control server.',
    'ConnectNamedPipe':'The code may create a server pipe for interprocess communication',
    'ControlService':'The code may start, stop, modify, or send a signal to a running service.',
    'CreateFile':'The program may create a new file or open an existing file.',
    'CreateFileMapping':'The code may read and modify PE files by creating handles to file mappings.',
    'CreateMutex':'Only one instance of the program runs on a system.',
    'CreateProcess':'The code may create and launch a new process.',
    'CreateRemoteThread':'The program may inject code into a different process.',
    'CreateService':'The program may create an autorun service or load kernel drivers.',
    'CreateToolhelp32Snapshot':'The code may create a snapshot of processes, heaps, threads, and modules.',
    'CryptAcquireContext':'The code may initialize the use of Windows encryption.',
    'DeviceIoControl':'The code may send control messages from user space to kernel space.',
    'DllCanUnloadNow':'The program implements a COM server. (Exported function)',
    'DllGetClassObject':'The program implements a COM server. (Exported function)',
    'DllInstall':'The program implements a COM server. (Exported function)',
    'DllRegisterServer':'The program implements a COM server. (Exported function)',
    'DllUnregisterServer':'The program implements a COM server. (Exported function)',
    'EnableExecuteProtectionSupport':'The code may modify the Data Execution Protection (DEP) settings of the host.',
    'EnumProcesses':'The code may enumerate through processes to find a process to inject into.',
    'EnumProcessModules':'The code may enumerate the loaded modules for injection.',
    'FindFirstFile':'The program may search through a directory and enumerate the filesystem.',
    'FindNextFile':'The program may search through a directory and enumerate the filesystem.',
    'FindResource':'The code may search for a resource in an executable or loaded DLL',
    'FindWindow':'The code searches for an open window on the desktop. (It may also contain anti-debugging)',
    'FtpPutFile':'The program may upload files to a remote FTP server.',
    'GetAdaptersInfo':'The code may obtain information about the MAC, network adapters on the system.',
    'GetAsyncKeyState':'The code may contain keylogger functionality.',
    'GetDC':'The code returns a handle to a device context for a window or the whole screen. (Screen Capture)',
    'GetForegroundWindow':'The code returns a handle to the window currently in the foreground of the desktop. (Keylogger)',
    'gethostbyname':'The code may perform a DNS lookup on a particular hostname. (C&C)',
    'gethostname':'The code may retrieve the hostname of the computer. (Backdoor)',
    'GetKeyState':'The code may obtain the status of a particular key on the keyboard. (Keylogger)',
    'GetModuleFilename':'The code may return the filename of a module that is loaded in the current process.',
    'GetModuleHandle':'The code may obtain a handle to an already loaded module. (Inject Code)',
    'GetProcAddress':'The code may retrieve the address of a function in a DLL loaded into memory.',
    'GetStartupInfo':'The code may get details about how the current process was configured to run',
    'GetSystemDefaultLangId':'The code may return the default language settings for the system.',
    'GetTempPath':'The code may return the temporary file path.',
    'GetThreadContext':'The code may read the context of a thread. (register values and current state)',
    'GetTickCount':'The code may retrieve the number of milliseconds since bootup. (Anti-debugging)',
    'GetVersionEx':'The code may return information about which version of Windows is currently running.',
    'GetWindowsDirectory':'The code may get the file path to the Windows directory.',
    'inet_addr':'The code may process IP addresses for network connectivity.',
    'InternetOpen':'The code may initialize the high-level Internet access functions from WinINet',
    'InternetOpenUrl':'The program may open a specific URL for a connection using FTP, HTTP, or HTTPS.',
    'InternetReadFile':'The program may read data from a previously opened URL.',
    'InternetWriteFile':'The program may write data to a previously opened URL.',
    'IsDebuggerPresent':'The code may check to see if the current process is being debugged. (Anti-debugging)',
    'IsNTAdmin':'The code checks if the user has administrator privileges.',
    'IsWoW64Process':'The code may check if it is running on a 64-bit operating system.',
    'LdrLoadDll':'The code uses a low-level function to load a DLL into a process and it may attempt to be stealthy.',
    'LoadLibrary':'The code loads a DLL into a process that may not have been loaded when the program started.',
    'LoadResource':'The code may load a resource from a PE file into memory.',
    'LsaEnumerateLogonSessions':'The code may enumerate through logon sessions on the current system.',
    'MapViewOfFile':'The code maps a file into memory and makes the contents of the file accessible via memory addresses.',
    'MapVirtualKey':'The code may translate a virtual-key code into a character value. (Keylogger)',
    'MmGetSystemRoutineAddress':'The code may retrieve the address of a function from another module. (Kernel code)',
    'Module32First':'The code may enumerate through modules loaded into a process. (Injector)',
    'Module32Next':'The code mya enumerate through modules loaded into a process. (Injector)',
    'NetScheduleJobAdd':'The code may submit a request for a program to be run at a specified date and time in the future.',
    'NetShareEnum':'The code may enumerate network shares.',
    'NtQueryDirectoryFile':'The code may get information about files in a directory. (Rootkit)',
    'NtQueryInformationProcess':'The code may return various information about a specified process. (Anti-debugging)',
    'NtSetInformationProcess':'The code may change the privilege level of a program or to bypass Data Execution Prevention (DEP).',
    'OleInitialize':'The code initialize the COM library.',
    'OpenMutex':'The code may open a handle to a mutual exclusion object to ensure that only a single instance is running.',
    'OpenProcess':'The code may open a handle to another process to read/write or to inject code.',
    'OpenSCManager':'The code may open a handle to the service control manager.',
    'OutputDebugString':'The code may output a string to a debugger if one is attached. (Anti-debugging)',
    'PeekNamedPipe':'The code may copy data from a named pipe without removing data from the pipe. (Reverse shell)',
    'Process32First':'The code may enumerate through processes to find a process to inject into.',
    'Process32Next':'The code may enumerate through processes to find a process to inject into.',
    'QueryPerformanceCounter':'The code may retrieve the value of the hardware-based performance counter. (Anti-debugging)',
    'QueueUserAPC':'The code may execute code for a different thread and inject code into another process.',
    'ReadProcessMemory':'The code may read the memory of a remote process.',
    'recv':'The code may receive data from a remote command-and-control server.',
    'RegisterHotKey':'The code may register a handler to be notified anytime a user enters a particular key combination.',
    'RegOpenKey':'The code may open a handle to a registry key for reading and editing.',
    'ResumeThread':'The code may resume a previously suspended thread for injection.',
    'RtlCreateRegistryKey':'The code may create a registry from kernel-mode code.',
    'RtlWriteRegistryValue':'The code may write a value to the registry from kernel-mode code.',
    'SamIConnect':'The code may connect to the Security Account Manager in order to make future calls that access credential information. (Hash-dumping)',
    'SamIGetPrivateData':'The code may query the private information about a specific user from the Security Account Manager (SAM) database. (Hash-dumping)',
    'SamQueryInformationUse':'The code may query information about a specific user in the Security Account Manager (SAM) database. (Hash-dumping)',
    'send':'The code may send data to a remote command-and-control server.',
    'SetFileTime':'The code may modify the creation/access/last modified time of a file.',
    'SetThreadContext':'The code may modify the context of a given thread for code injection.',
    'SetWindowsHookEx':'The code may set a hook function to be called whenever a certain event is called. (Keylogger)',
    'SfcTerminateWatcherThread':'The code may disable Windows file protection and modify the otherwise protected files.',
    'ShellExecute':'The code may execute another program and create a new process.',
    'StartServiceCtrlDispatcher':'The code may run as a service.',
    'SuspendThread':'The code may suspend a thread to modify it by performing code injection.',
    'system':'The code may run another program provided by some C runtime libraries. (wrapper function to CreateProcess.',
    'Thread32First':'The code may iterate through the threads of a process to find an appropriate thread to inject into.',
    'Thread32Next':'The code may iterate through the threads of a process to find an appropriate thread to inject into.',
    'Toolhelp32ReadProcessMemory':'The code may read the memory of a remote process.',
    'URLDownloadToFile':'The code may download a file from a web server and save it to disk.',
    'VirtualAllocEx':'The code may allocate memory in a remote process as part of process injection.',
    'VirtualProtectEx':'The code may change the protection on a region of memory from read-only to executable.',
    'WideCharToMultiByte':'The code may convert a Unicode string into an ASCII string.',
    'WinExec':'The code may execute another program by creating a new process.',
    'WlxLoggedOnSAS':'The code may perform Graphical Identification and Authentication (GINA) replacement.',
    'Wow64DisableWow64FsRedirection':'The code may disable file redirection that occurs in 32-bit files loaded on a 64-bit system.',
    'WriteProcessMemory':'The code may write data to a remote process as part of process injection.',
    'WSAStartup':'The code may initialize low-level network functionality.'
     }

# These lists are for renaming functions if they contain certain API names. Based on the script by Alexander Hanel.

# resource manipulation
launcher = ['LCH','FindResource','LoadResource','SizeofResource']
# injection attacks
process_injection = ['PSJ','VirtualAllocEx','WriteProcessMemory']
DLL_injection = ['DLJ','CreateToolhelp32Snapshot','Process32First','Process32Next', 'OpenProcess', 'CreateRemoteThread', 'WriteProcessMemory']
direct_injection = ['DRJ', 'VirtualAllocEx','WriteProcessMemory','CreateRemoteThread']
process_replacement = ['PRP', 'ZwUnmapViewOfSection','VirtualAllocEx','WriteProcessMemory','SetThreadContext','ResumeThread']
hook_injection = ['HKJ', 'SetWindowsHookEx','CallNextHookEx','UnhookWindowsHookEx']
APC_injection = ['ACJ', 'CreateRemoteThread','WaitForSingleObjectEx','WaitForMultipleObjectsEx','Sleep']
APC_userspace = ['AUJ', 'QueueUserAPC','WaitForSingleObjectEx','CreateToolhelp32Snapshot','Process32First','Process32Next','Thread32Next',\
                 'Thread32First','NtQuerySystemInformation','ZwQuerySystemInformation']
APC_kernelspace = ['AKJ', 'KeInitializeApc','KeInsertQueueApc']
# networking code
windows_networking = ['WNT','WSAStartup','getaddrinfo','socket','connect','send','recv','WSAGetLastError','InternetOpen','InternetConnect',\
                      'InternetOpenURL','InternetReadFile','InternetWriteFile','HTTPOpenRequest','HTTPQueryInfo','HTTPSendRequest',\
                      'URLDownloadToFile','CoInitialize','CoCreateInstance','Navigate','gethostbyname']
anti_debugging = ['ADB','IsDebuggerPresent','CheckRemoteDebuggerPresent','NtQueryInfromationProcess','OutputDebugString','FindWindow',\
                  'QueryPerformanceCounter','GetTickCount']
# anti-vm x86 instructions -> e.g. GetMnem(i) == "cpuid"
anti_vmx86 = ['AVM','sidt','sgdt','sldt','smsw','str','in','cpuid']

reg = [ 'REG', 'RegCloseKey' ,'RegConnectRegistryA' ,'RegConnectRegistryW' ,'RegCreateKeyA' ,'RegCreateKeyExA' ,'RegCreateKeyExW',\
        'RegCreateKeyW' ,'RegDeleteKeyA' ,'RegDeleteKeyW' ,'RegDeleteValueA' ,'RegDeleteValueW' ,'RegDisablePredefinedCache' ,\
        'RegDisablePredefinedCacheEx' ,'RegEnumKeyA' ,'RegEnumKeyExA' ,'RegEnumKeyExW' ,'RegEnumKeyW' ,'RegEnumValueA' ,\
        'RegEnumValueW' ,'RegFlushKey' ,'RegGetKeySecurity' ,'RegLoadKeyA' ,'RegLoadKeyW' ,'RegNotifyChangeKeyValue' ,\
        'RegOpenCurrentUser' ,'RegOpenKeyA' ,'RegOpenKeyExA' ,'RegOpenKeyExW' ,'RegOpenKeyW' ,'RegOpenUserClassesRoot' ,\
        'RegOverridePredefKey' ,'RegQueryInfoKeyA' ,'RegQueryInfoKeyW' ,'RegQueryMultipleValuesA' ,'RegQueryMultipleValuesW' ,\
        'RegQueryValueA' ,'RegQueryValueExA' ,'RegQueryValueExW' ,'RegQueryValueW' ,'RegReplaceKeyA' ,'RegReplaceKeyW' ,\
        'RegRestoreKeyA' ,'RegRestoreKeyW' ,'RegSaveKeyA' ,'RegSaveKeyExA' ,'RegSaveKeyExW' ,'RegSaveKeyW' ,'RegSetKeySecurity' ,\
        'RegSetValueA' ,'RegSetValueExA' ,'RegSetValueExW' ,'RegSetValueW' ,'RegUnLoadKeyA' ,'RegUnLoadKeyW', 'SHDeleteEmptyKeyA' ,\
        'SHDeleteEmptyKeyW' ,'SHDeleteKeyA' ,'SHDeleteKeyW' ,'SHOpenRegStream2A' ,'SHOpenRegStream2W' ,'SHOpenRegStreamA' ,\
        'SHOpenRegStreamW' ,'SHQueryInfoKeyA' ,'SHQueryInfoKeyW' ,'SHQueryValueExA' ,'SHQueryValueExW' ,'SHRegCloseUSKey' ,\
        'SHRegCreateUSKeyA' ,'SHRegCreateUSKeyW' ,'SHRegDeleteEmptyUSKeyA' ,'SHRegDeleteEmptyUSKeyW' ,'SHRegDeleteUSValueA' ,\
        'SHRegDeleteUSValueW' ,'SHRegDuplicateHKey' ,'SHRegEnumUSKeyA' ,'SHRegEnumUSKeyW' ,'SHRegEnumUSValueA' ,'SHRegEnumUSValueW'\
        ,'SHRegGetBoolUSValueA' ,'SHRegGetBoolUSValueW' ,'SHRegGetPathA' ,'SHRegGetPathW' ,'SHRegGetUSValueA' ,'SHRegGetUSValueW' ,\
        'SHRegGetValueA' ,'SHRegGetValueW' ,'SHRegOpenUSKeyA' ,'SHRegOpenUSKeyW' ,'SHRegQueryInfoUSKeyA' ,'SHRegQueryInfoUSKeyW' ,\
        'SHRegQueryUSValueA' ,'SHRegQueryUSValueW' ,'SHRegSetPathA' ,'SHRegSetPathW' ,'SHRegSetUSValueA' ,'SHRegSetUSValueW' ,\
        'SHRegWriteUSValueA' ,'SHRegWriteUSValueW' ,'SHDeleteOrphanKeyA' ,'SHDeleteOrphanKeyW' ,'SHDeleteValueA' ,'SHDeleteValueW' ,\
        'SHEnumKeyExA' ,'SHEnumKeyExW' ,'SHEnumValueA' ,'SHEnumValueW' ,'SHGetValueA' ,'SHGetValueW' ,'SHOpenRegStream2A' ,\
        'SHOpenRegStream2W' ,'SHOpenRegStreamA' ,'SHOpenRegStreamW' ,'SHQueryInfoKeyA' ,'SHQueryInfoKeyW' ,'SHQueryValueExA' ,\
        'SHQueryValueExW' ,'SHRegCloseUSKey' ,'SHRegCreateUSKeyA' ,'SHRegCreateUSKeyW' ,'SHRegDeleteEmptyUSKeyA' ,\
        'SHRegDeleteEmptyUSKeyW' ,'SHRegDeleteUSValueA' ,'SHRegDeleteUSValueW' ,'SHRegDuplicateHKey' ,'SHRegEnumUSKeyA' ,\
        'SHRegEnumUSKeyW' ,'SHRegEnumUSValueA' ,'SHRegEnumUSValueW' ,'SHRegGetBoolUSValueA' ,'SHRegGetBoolUSValueW' ,'SHRegGetPathA' ,\
        'SHRegGetPathW' ,'SHRegGetUSValueA' ,'SHRegGetUSValueW' ,'SHRegGetValueA' ,'SHRegGetValueW' ,'SHRegOpenUSKeyA' ,'SHRegOpenUSKeyW' ,\
        'SHRegQueryInfoUSKeyA' ,'SHRegQueryInfoUSKeyW' ,'SHRegQueryUSValueA' ,'SHRegQueryUSValueW' ,'SHRegSetPathA' ,'SHRegSetPathW' ,\
        'SHRegSetUSValueA' ,'SHRegSetUSValueW' ,'SHRegWriteUSValueA' ,'SHRegWriteUSValueW']
winsock = [ 'NET', 'FreeAddrInfoW', 'GetAddrInfoW', 'GetNameInfoW', 'WEP', 'WPUCompleteOverlappedRequest', 'WSAAccept', \
            'WSAAddressToStringA', 'WSAAddressToStringW', 'WSAAsyncGetHostByAddr', 'WSAAsyncGetHostByName', 'WSAAsyncGetProtoByName',\
            'WSAAsyncGetProtoByNumber', 'WSAAsyncGetServByName', 'WSAAsyncGetServByPort', 'WSAAsyncSelect', 'WSACancelAsyncRequest',\
            'WSACancelBlockingCall', 'WSACleanup', 'WSACloseEvent', 'WSAConnect', 'WSACreateEvent', 'WSADuplicateSocketA',\
            'WSADuplicateSocketW', 'WSAEnumNameSpaceProvidersA', 'WSAEnumNameSpaceProvidersW', 'WSAEnumNetworkEvents', 'WSAEnumProtocolsA',\
            'WSAEnumProtocolsW', 'WSAEventSelect', 'WSAGetLastError', 'WSAGetOverlappedResult', 'WSAGetQOSByName', \
            'WSAGetServiceClassInfoA', 'WSAGetServiceClassInfoW', 'WSAGetServiceClassNameByClassIdA', 'WSAGetServiceClassNameByClassIdW',\
            'WSAHtonl', 'WSAHtons', 'WSAInstallServiceClassA', 'WSAInstallServiceClassW', 'WSAIoctl', 'WSAIsBlocking', 'WSAJoinLeaf', \
            'WSALookupServiceBeginA', 'WSALookupServiceBeginW', 'WSALookupServiceEnd', 'WSALookupServiceNextA', 'WSALookupServiceNextW', \
            'WSANSPIoctl', 'WSANtohl', 'WSANtohs', 'WSAProviderConfigChange', 'WSARecv', 'WSARecvDisconnect', 'WSARecvFrom', \
            'WSARemoveServiceClass', 'WSAResetEvent', 'WSASend', 'WSASendDisconnect', 'WSASendTo', 'WSASetBlockingHook', 'WSASetEvent',\
            'WSASetLastError', 'WSASetServiceA', 'WSASetServiceW', 'WSASocketA', 'WSASocketW', 'WSAStartup', 'WSAStringToAddressA', \
            'WSAStringToAddressW', 'WSAUnhookBlockingHook', 'WSAWaitForMultipleEvents', 'WSApSetPostRoutine', 'WSCDeinstallProvider', \
            'WSCEnableNSProvider', 'WSCEnumProtocols', 'WSCGetProviderPath', 'WSCInstallNameSpace', 'WSCInstallProvider', 'WSCUnInstallNameSpace',\
            'WSCUpdateProvider', 'WSCWriteNameSpaceOrder', 'WSCWriteProviderOrder', '__WSAFDIsSet', 'accept', 'bind', 'closesocket', 'connect', \
            'freeaddrinfo', 'getaddrinfo', 'gethostbyaddr', 'gethostbyname', 'gethostname', 'getnameinfo', 'getpeername', 'getprotobyname', \
            'getprotobynumber', 'getservbyname', 'getservbyport', 'getsockname', 'getsockopt', 'htonl', 'htons', 'inet_addr', 'inet_ntoa', \
            'ioctlsocket', 'listen', 'ntohl', 'ntohs', 'recv', 'recvfrom', 'select', 'send', 'sendto', 'setsockopt', 'shutdown', 'socket']
WinINet = [ 'NET', 'CreateMD5SSOHash', 'DetectAutoProxyUrl', 'DllInstall', 'ForceNexusLookup', 'ForceNexusLookupExW', 'InternetAlgIdToStringA',\
            'InternetAlgIdToStringW', 'InternetAttemptConnect', 'InternetAutodial', 'InternetAutodialCallback', 'InternetAutodialHangup',\
            'InternetCanonicalizeUrlA', 'InternetCanonicalizeUrlW', 'InternetCheckConnectionA', 'InternetCheckConnectionW', \
            'InternetClearAllPerSiteCookieDecisions', 'InternetCloseHandle', 'InternetCombineUrlA', 'InternetCombineUrlW', \
            'InternetConfirmZoneCrossing', 'InternetConfirmZoneCrossingA', 'InternetConfirmZoneCrossingW', 'InternetConnectA',\
            'InternetConnectW', 'InternetCrackUrlA', 'InternetCrackUrlW', 'InternetCreateUrlA', 'InternetCreateUrlW', 'InternetDial',\
            'InternetDialA', 'InternetDialW', 'InternetEnumPerSiteCookieDecisionA', 'InternetEnumPerSiteCookieDecisionW', 'InternetErrorDlg',\
            'InternetFindNextFileA', 'InternetFindNextFileW', 'InternetFortezzaCommand', 'InternetGetCertByURL', 'InternetGetCertByURLA',\
            'InternetGetConnectedState', 'InternetGetConnectedStateEx', 'InternetGetConnectedStateExA', 'InternetGetConnectedStateExW',\
            'InternetGetCookieA', 'InternetGetCookieExA', 'InternetGetCookieExW', 'InternetGetCookieW', 'InternetGetLastResponseInfoA', \
            'InternetGetLastResponseInfoW', 'InternetGetPerSiteCookieDecisionA', 'InternetGetPerSiteCookieDecisionW', 'InternetGoOnline',\
            'InternetGoOnlineA', 'InternetGoOnlineW', 'InternetHangUp', 'InternetInitializeAutoProxyDll', 'InternetLockRequestFile',\
            'InternetOpenA', 'InternetOpenUrlA', 'InternetOpenUrlW', 'InternetOpenW', 'InternetQueryDataAvailable', 'InternetQueryFortezzaStatus',\
            'InternetQueryOptionA', 'InternetQueryOptionW', 'InternetReadFile', 'InternetReadFileExA', 'InternetReadFileExW', \
            'InternetSecurityProtocolToStringA', 'InternetSecurityProtocolToStringW', 'InternetSetCookieA', 'InternetSetCookieExA', \
            'InternetSetCookieExW', 'InternetSetCookieW', 'InternetSetDialState', 'InternetSetDialStateA', 'InternetSetDialStateW',\
            'InternetSetFilePointer', 'InternetSetOptionA', 'InternetSetOptionExA', 'InternetSetOptionExW', 'InternetSetOptionW', \
            'InternetSetPerSiteCookieDecisionA', 'InternetSetPerSiteCookieDecisionW', 'InternetSetStatusCallback', 'InternetSetStatusCallbackA',\
            'InternetSetStatusCallbackW', 'InternetShowSecurityInfoByURL', 'InternetShowSecurityInfoByURLA', 'InternetShowSecurityInfoByURLW', \
            'InternetTimeFromSystemTime', 'InternetTimeFromSystemTimeA', 'InternetTimeFromSystemTimeW', 'InternetTimeToSystemTime',\
            'InternetTimeToSystemTimeA', 'InternetTimeToSystemTimeW', 'InternetUnlockRequestFile', 'InternetWriteFile', 'InternetWriteFileExA',\
            'InternetWriteFileExW', 'IsHostInProxyBypassList', 'ParseX509EncodedCertificateForListBoxEntry', 'PrivacyGetZonePreferenceW', \
            'PrivacySetZonePreferenceW', 'ResumeSuspendedDownload', 'ShowCertificate', 'ShowClientAuthCerts', 'ShowSecurityInfo', \
            'ShowX509EncodedCertificate','UrlZonesDetach', '_GetFileExtensionFromUrl'] 
cache = [ 'NET','CommitUrlCacheEntryA', 'CommitUrlCacheEntryW', 'CreateUrlCacheContainerA', 'CreateUrlCacheContainerW', 'CreateUrlCacheEntryA',\
          'CreateUrlCacheEntryW', 'CreateUrlCacheGroup', 'DeleteIE3Cache', 'DeleteUrlCacheContainerA', 'DeleteUrlCacheContainerW', \
          'DeleteUrlCacheEntry', 'DeleteUrlCacheEntryA', 'DeleteUrlCacheEntryW', 'DeleteUrlCacheGroup', 'FindCloseUrlCache', 'FindFirstUrlCacheContainerA',\
          'FindFirstUrlCacheContainerW', 'FindFirstUrlCacheEntryA', 'FindFirstUrlCacheEntryExA', 'FindFirstUrlCacheEntryExW', 'FindFirstUrlCacheEntryW', \
          'FindFirstUrlCacheGroup', 'FindNextUrlCacheContainerA', 'FindNextUrlCacheContainerW', 'FindNextUrlCacheEntryA', 'FindNextUrlCacheEntryExA',\
          'FindNextUrlCacheEntryExW', 'FindNextUrlCacheEntryW', 'FindNextUrlCacheGroup', 'FreeUrlCacheSpaceA', 'FreeUrlCacheSpaceW', 'GetUrlCacheConfigInfoA', \
          'GetUrlCacheConfigInfoW', 'GetUrlCacheEntryInfoA', 'GetUrlCacheEntryInfoExA', 'GetUrlCacheEntryInfoExW', 'GetUrlCacheEntryInfoW', \
          'GetUrlCacheGroupAttributeA', 'GetUrlCacheGroupAttributeW', 'GetUrlCacheHeaderData', 'IncrementUrlCacheHeaderData', 'IsUrlCacheEntryExpiredA',\
          'IsUrlCacheEntryExpiredW', 'LoadUrlCacheContent', 'ReadUrlCacheEntryStream', 'RegisterUrlCacheNotification', 'RetrieveUrlCacheEntryFileA', \
          'RetrieveUrlCacheEntryFileW', 'RetrieveUrlCacheEntryStreamA', 'RetrieveUrlCacheEntryStreamW', 'RunOnceUrlCache', 'SetUrlCacheConfigInfoA',\
          'SetUrlCacheConfigInfoW', 'SetUrlCacheEntryGroup', 'SetUrlCacheEntryGroupA', 'SetUrlCacheEntryGroupW', 'SetUrlCacheEntryInfoA', 'SetUrlCacheEntryInfoW',\
          'SetUrlCacheGroupAttributeA', 'SetUrlCacheGroupAttributeW', 'SetUrlCacheHeaderData', 'UnlockUrlCacheEntryFile', 'UnlockUrlCacheEntryFileA', \
          'UnlockUrlCacheEntryFileW', 'UnlockUrlCacheEntryStream', 'UpdateUrlCacheContentPath']
ftp = [ 'FTP','FtpCommandA' ,'FtpCommandW' ,'FtpCreateDirectoryA' ,'FtpCreateDirectoryW' ,'FtpDeleteFileA' ,'FtpDeleteFileW' ,'FtpFindFirstFileA' ,\
        'FtpFindFirstFileW' ,'FtpGetCurrentDirectoryA' ,'FtpGetCurrentDirectoryW' ,'FtpGetFileA' ,'FtpGetFileEx' ,'FtpGetFileSize' ,'FtpGetFileW' ,\
        'FtpOpenFileA' ,'FtpOpenFileW' ,'FtpPutFileA' ,'FtpPutFileEx' ,'FtpPutFileW' ,'FtpRemoveDirectoryA' ,'FtpRemoveDirectoryW' ,'FtpRenameFileA' ,\
        'FtpRenameFileW' ,'FtpSetCurrentDirectoryA' ,'FtpSetCurrentDirectoryW']
gopher = [ 'GPR', 'GopherCreateLocatorA', 'GopherCreateLocatorW', 'GopherFindFirstFileA', 'GopherFindFirstFileW', 'GopherGetAttributeA', \
           'GopherGetAttributeW', 'GopherGetLocatorTypeA', 'GopherGetLocatorTypeW', 'GopherOpenFileA', 'GopherOpenFileW'] 
url = ['URL', 'UrlApplySchemeA' ,'UrlApplySchemeW' ,'UrlCanonicalizeA' ,'UrlCanonicalizeW' ,'UrlCombineA' ,'UrlCombineW' ,'UrlCompareA' ,\
       'UrlCompareW' ,'UrlCreateFromPathA' ,'UrlCreateFromPathW' ,'UrlEscapeA' ,'UrlEscapeW' ,'UrlGetLocationA' ,'UrlGetLocationW' ,'UrlGetPartA'\
       ,'UrlGetPartW' ,'UrlHashA' ,'UrlHashW' ,'UrlIsA' ,'UrlIsNoHistoryA' ,'UrlIsNoHistoryW' ,'UrlIsOpaqueA' ,'UrlIsOpaqueW' ,'UrlIsW' ,'UrlUnescapeA' ,'UrlUnescapeW']
dir = ['DIR','CreateDirectoryA', 'CreateDirectoryExA', 'CreateDirectoryExW', 'CreateDirectoryW', 'GetCurrentDirectoryA', 'GetCurrentDirectoryW',\
       'GetDllDirectoryA', 'GetDllDirectoryW', 'GetSystemDirectoryA', 'GetSystemDirectoryW', 'GetSystemWindowsDirectoryA', 'GetSystemWindowsDirectoryW',\
       'GetSystemWow64DirectoryA', 'GetSystemWow64DirectoryW', 'GetVDMCurrentDirectories', 'GetWindowsDirectoryA', 'GetWindowsDirectoryW', \
       'ReadDirectoryChangesW', 'RemoveDirectoryA', 'RemoveDirectoryW', 'SetCurrentDirectoryA', 'SetCurrentDirectoryW', 'SetDllDirectoryA',\
       'SetDllDirectoryW', 'SetVDMCurrentDirectories', 'SHCreateDirectory', 'SHCreateDirectoryExA', 'SHCreateDirectoryExW']
mutex = ['MTX','CreateMutexA', 'CreateMutexW', 'OpenMutexA', 'OpenMutexW', 'ReleaseMutex']
pipe = [ 'PIP', 'CallNamedPipeA', 'CallNamedPipeW', 'ConnectNamedPipe', 'CreateNamedPipeA', 'CreateNamedPipeW', 'CreatePipe', 'DisconnectNamedPipe',\
         'GetNamedPipeHandleStateA', 'GetNamedPipeHandleStateW', 'GetNamedPipeInfo', 'PeekNamedPipe', 'SetNamedPipeHandleState', 'TransactNamedPipe',\
         'WaitNamedPipeA', 'WaitNamedPipeW']
http = [ 'HTP', 'HttpAddRequestHeadersA', 'HttpAddRequestHeadersW', 'HttpCheckDavCompliance', 'HttpEndRequestA', 'HttpEndRequestW',\
         'HttpOpenRequestA', 'HttpOpenRequestW', 'HttpQueryInfoA', 'HttpQueryInfoW', 'HttpSendRequestA', 'HttpSendRequestExA', \
         'HttpSendRequestExW', 'HttpSendRequestW' ] 
enum = [ 'ENP', 'CreateToolhelp32Snapshot', 'Process32First' ,'Process32FirstW' ,'Process32Next' ,'Process32NextW']
hash = ['HAS', 'CryptCreateHash' ,'CryptDestroyHash' ,'CryptDuplicateHash' ,'CryptGetHashParam' ,'CryptHashData' ,'CryptHashSessionKey' ,\
        'CryptSetHashParam' ,'CryptSignHashA' ,'CryptSignHashW', 'FreeEncryptionCertificateHashList']
crypt = ['CRY', 'CryptAcquireContextA' ,'CryptAcquireContextW' ,'CryptContextAddRef' ,'CryptDecrypt' ,'CryptDeriveKey' ,'CryptDestroyKey' ,\
         'CryptDuplicateKey' ,'CryptEncrypt' ,'CryptEnumProviderTypesA' ,'CryptEnumProviderTypesW' ,'CryptEnumProvidersA' ,'CryptEnumProvidersW'\
         ,'CryptExportKey' ,'CryptGenKey' ,'CryptGenRandom' ,'CryptGetDefaultProviderA' ,'CryptGetDefaultProviderW' ,'CryptGetKeyParam' ,\
         'CryptGetProvParam' ,'CryptGetUserKey' ,'CryptImportKey' ,'CryptReleaseContext' ,'CryptSetKeyParam' ,'CryptSetProvParam' ,\
         'CryptSetProviderA' ,'CryptSetProviderExA' ,'CryptSetProviderExW' ,'CryptSetProviderW' ,'CryptVerifySignatureA' ,'CryptVerifySignatureW' ,\
         'DecryptFileA' ,'DecryptFileW', 'EncryptFileA' ,'EncryptFileW' ,'EncryptedFileKeyInfo' ,'EncryptionDisable', 'WriteEncryptedFileRaw', \
         'OpenEncryptedFileRawA' ,'OpenEncryptedFileRawW', 'DuplicateEncryptionInfoFile', 'SetUserFileEncryptionKey', 'ReadEncryptedFileRaw', \
         'RemoveUsersFromEncryptedFile', 'FileEncryptionStatusA', 'FileEncryptionStatusW', 'FreeEncryptedFileKeyInfo', 'CloseEncryptedFileRaw', \
         'AddUsersToEncryptedFile', 'QueryRecoveryAgentsOnEncryptedFile', 'QueryUsersOnEncryptedFile', 'ChainWlxLogoffEvent' ,'CryptAcquireContextU' ,\
         'CryptBinaryToStringA' ,'CryptBinaryToStringW' ,'CryptCloseAsyncHandle' ,'CryptCreateAsyncHandle' ,'CryptDecodeMessage' ,'CryptDecodeObject' ,\
         'CryptDecodeObjectEx' ,'CryptDecryptAndVerifyMessageSignature' ,'CryptDecryptMessage' ,'CryptEncodeObject' ,'CryptEncodeObjectEx' ,\
         'CryptEncryptMessage' ,'CryptEnumKeyIdentifierProperties' ,'CryptEnumOIDFunction' ,'CryptEnumOIDInfo' ,'CryptEnumProvidersU' ,'CryptExportPKCS8' ,\
         'CryptExportPublicKeyInfo' ,'CryptExportPublicKeyInfoEx' ,'CryptFindLocalizedName' ,'CryptFindOIDInfo' ,'CryptFormatObject' ,\
         'CryptFreeOIDFunctionAddress' ,'CryptGetAsyncParam' ,'CryptGetDefaultOIDDllList' ,'CryptGetDefaultOIDFunctionAddress' ,\
         'CryptGetKeyIdentifierProperty' ,'CryptGetMessageCertificates' ,'CryptGetMessageSignerCount' ,'CryptGetOIDFunctionAddress' ,\
         'CryptGetOIDFunctionValue' ,'CryptHashCertificate' ,'CryptHashMessage' ,'CryptHashPublicKeyInfo' ,'CryptHashToBeSigned' ,\
         'CryptImportPKCS8' ,'CryptImportPublicKeyInfo' ,'CryptImportPublicKeyInfoEx' ,'CryptInitOIDFunctionSet' ,'CryptInstallDefaultContext' ,\
         'CryptInstallOIDFunctionAddress' ,'CryptLoadSip' ,'CryptMemAlloc' ,'CryptMemFree' ,'CryptMemRealloc' ,'CryptMsgCalculateEncodedLength' ,\
         'CryptMsgClose' ,'CryptMsgControl' ,'CryptMsgCountersign' ,'CryptMsgCountersignEncoded' ,'CryptMsgDuplicate' ,'CryptMsgEncodeAndSignCTL' ,\
         'CryptMsgGetAndVerifySigner' ,'CryptMsgGetParam' ,'CryptMsgOpenToDecode' ,'CryptMsgOpenToEncode' ,'CryptMsgSignCTL' ,'CryptMsgUpdate' ,\
         'CryptMsgVerifyCountersignatureEncoded' ,'CryptMsgVerifyCountersignatureEncodedEx' ,'CryptProtectData' ,'CryptQueryObject' ,\
         'CryptRegisterDefaultOIDFunction' ,'CryptRegisterOIDFunction' ,'CryptRegisterOIDInfo' ,'CryptSIPAddProvider' ,\
         'CryptSIPCreateIndirectData' ,'CryptSIPGetSignedDataMsg' ,'CryptSIPLoad' ,'CryptSIPPutSignedDataMsg' ,'CryptSIPRemoveProvider' ,\
         'CryptSIPRemoveSignedDataMsg' ,'CryptSIPRetrieveSubjectGuid' ,'CryptSIPRetrieveSubjectGuidForCatalogFile' ,'CryptSIPVerifyIndirectData' ,\
         'CryptSetAsyncParam' ,'CryptSetKeyIdentifierProperty' ,'CryptSetOIDFunctionValue' ,'CryptSetProviderU' ,'CryptSignAndEncodeCertificate' ,\
         'CryptSignAndEncryptMessage' ,'CryptSignCertificate' ,'CryptSignHashU' ,'CryptSignMessage' ,'CryptSignMessageWithKey' ,\
         'CryptStringToBinaryA' ,'CryptStringToBinaryW' ,'CryptUninstallDefaultContext' ,'CryptUnprotectData' ,'CryptUnregisterDefaultOIDFunction' ,\
         'CryptUnregisterOIDFunction' ,'CryptUnregisterOIDInfo' ,'CryptVerifyCertificateSignature' ,'CryptVerifyCertificateSignatureEx' ,\
         'CryptVerifyDetachedMessageHash' ,'CryptVerifyDetachedMessageSignature' ,'CryptVerifyMessageHash' ,'CryptVerifyMessageSignature' ,\
         'CryptVerifyMessageSignatureWithKey' ,'CryptVerifySignatureU' ,'I_CertProtectFunction' ,'I_CertSrvProtectFunction' ,'I_CertSyncStore' ,\
         'I_CertUpdateStore' ,'I_CryptAddRefLruEntry' ,'I_CryptAddSmartCardCertToStore' ,'I_CryptAllocTls' ,'I_CryptCreateLruCache' ,\
         'I_CryptCreateLruEntry' ,'I_CryptDetachTls' ,'I_CryptDisableLruOfEntries' ,'I_CryptEnableLruOfEntries' ,'I_CryptEnumMatchingLruEntries' ,\
         'I_CryptFindLruEntry' ,'I_CryptFindLruEntryData' ,'I_CryptFindSmartCardCertInStore' ,'I_CryptFlushLruCache' ,'I_CryptFreeLruCache' ,\
         'I_CryptFreeTls' ,'I_CryptGetAsn1Decoder' ,'I_CryptGetAsn1Encoder' ,'I_CryptGetDefaultCryptProv' ,'I_CryptGetDefaultCryptProvForEncrypt' ,\
         'I_CryptGetFileVersion' ,'I_CryptGetLruEntryData' ,'I_CryptGetLruEntryIdentifier' ,'I_CryptGetOssGlobal' ,'I_CryptGetTls' ,'I_CryptInsertLruEntry' ,\
         'I_CryptInstallAsn1Module' ,'I_CryptInstallOssGlobal' ,'I_CryptReadTrustedPublisherDWORDValueFromRegistry' ,'I_CryptRegisterSmartCardStore' ,\
         'I_CryptReleaseLruEntry' ,'I_CryptRemoveLruEntry' ,'I_CryptSetTls' ,'I_CryptTouchLruEntry' ,'I_CryptUninstallAsn1Module' ,\
         'I_CryptUninstallOssGlobal' ,'I_CryptUnregisterSmartCardStore' ,'I_CryptWalkAllLruCacheEntries']
service = ['SRV', 'ChangeServiceConfig2A' ,'ChangeServiceConfig2W' ,'ChangeServiceConfigA' ,'ChangeServiceConfigW' ,'CloseServiceHandle' ,\
           'ControlService' ,'CreateServiceA' ,'CreateServiceW' ,'DeleteService' ,'EnumDependentServicesA' ,'EnumDependentServicesW' ,\
           'EnumServiceGroupW' ,'EnumServicesStatusA' ,'EnumServicesStatusExA' ,'EnumServicesStatusExW' ,'EnumServicesStatusW' ,\
           'GetServiceDisplayNameA' ,'GetServiceDisplayNameW' ,'GetServiceKeyNameA' ,'GetServiceKeyNameW' ,'I_ScPnPGetServiceName' ,\
           'I_ScSetServiceBitsA' ,'I_ScSetServiceBitsW' ,'LockServiceDatabase' ,'OpenServiceA' ,'OpenServiceW' ,'PrivilegedServiceAuditAlarmA' ,\
           'PrivilegedServiceAuditAlarmW' ,'QueryServiceConfig2A' ,'QueryServiceConfig2W' ,'QueryServiceConfigA' ,'QueryServiceConfigW' ,\
           'QueryServiceLockStatusA' ,'QueryServiceLockStatusW' ,'QueryServiceObjectSecurity' ,'QueryServiceStatus' ,'QueryServiceStatusEx' ,\
           'RegisterServiceCtrlHandlerA' ,'RegisterServiceCtrlHandlerExA' ,'RegisterServiceCtrlHandlerExW' ,'RegisterServiceCtrlHandlerW' ,\
           'SetServiceBits' ,'SetServiceObjectSecurity' ,'SetServiceStatus' ,'StartServiceA' ,'StartServiceCtrlDispatcherA' ,'StartServiceCtrlDispatcherW' ,\
           'StartServiceW' ,'UnlockServiceDatabase' ,'WdmWmiServiceMain']
file = ['FIL', 'CompareFileTime' ,'CopyFileA' ,'CopyFileExA' ,'CopyFileExW' ,'CopyFileW' ,'CopyLZFile' ,'CreateFileA' ,'CreateFileMappingA' ,\
        'CreateFileMappingW' ,'CreateFileW' ,'DeleteFileA' ,'DeleteFileW' ,'DosDateTimeToFileTime' ,'FileTimeToDosDateTime' ,\
        'FileTimeToLocalFileTime' ,'FileTimeToLocalFileTime' ,'FileTimeToSystemTime' ,'FlushFileBuffers' ,'FlushViewOfFile' ,\
        'GetCPFileNameFromRegistry' ,'GetCompressedFileSizeA' ,'GetCompressedFileSizeW' ,'GetFileAttributesA' ,'GetFileAttributesExA' ,\
        'GetFileAttributesExW' ,'GetFileAttributesW' ,'GetFileInformationByHandle' ,'GetFileSize' ,'GetFileSizeEx' ,'GetFileTime' ,\
        'GetFileType' ,'GetSystemTimeAsFileTime' ,'GetTempFileNameA' ,'GetTempFileNameW' ,'LZCloseFile' ,'LZCreateFileW' ,'LZOpenFileA',\
        'LZOpenFileW' ,'LocalFileTimeToFileTime' ,'LocalFileTimeToFileTime' ,'LockFile' ,'LockFileEx' ,'MapViewOfFile' ,'MapViewOfFileEx' ,\
        'MoveFileA' ,'MoveFileExA' ,'MoveFileExW' ,'MoveFileW' ,'MoveFileWithProgressA' ,'MoveFileWithProgressW' ,'OpenDataFile' ,'OpenFile' ,\
        'OpenFileMappingA' ,'OpenFileMappingW' ,'OpenProfileUserMapping' ,'PrivCopyFileExW' ,'PrivMoveFileIdentityW' ,'ReadFile' ,'ReadFileEx' ,\
        'ReplaceFile' ,'ReplaceFileA' ,'ReplaceFileW' ,'SetEndOfFile' ,'SetFileAttributesA' ,'SetFileAttributesW' ,'SetFilePointer' ,\
        'SetFilePointerEx' ,'SetFileShortNameA' ,'SetFileShortNameW' ,'SetFileTime' ,'SetFileValidData' ,'SystemTimeToFileTime' ,\
        'UnlockFile' ,'UnlockFileEx' ,'UnmapViewOfFile' ,'WriteFile' ,'WriteFileEx' ,'WriteFileGather' ,'GetFileSecurityA' ,\
        'GetFileSecurityW' ,'SetFileSecurityA' ,'SetFileSecurityW', 'CreateFileU']
os_info = [ 'OSI', 'GetComputerNameA' ,'GetComputerNameExA' ,'GetComputerNameExW' ,'GetComputerNameW' ,'GetDiskFreeSpaceA' ,\
            'GetDiskFreeSpaceExA' ,'GetDiskFreeSpaceExW' ,'GetDiskFreeSpaceW' ,'GetDriveTypeA' ,'GetDriveTypeW', 'GetVersion' ,\
            'GetVersionExA' ,'GetVersionExW', 'GetSystemInfo', 'GetSystemMetrics', 'CheckTokenMembership']
cert = ['CER','CertAddCRLContextToStore' ,'CertAddCRLLinkToStore' ,'CertAddCTLContextToStore' ,'CertAddCTLLinkToStore' ,\
        'CertAddCertificateContextToStore' ,'CertAddCertificateLinkToStore' ,'CertAddEncodedCRLToStore' ,'CertAddEncodedCertificateToStore' ,\
        'CertAddEncodedCertificateToSystemStoreA' ,'CertAddEncodedCertificateToSystemStoreW' ,'CertAddEnhancedKeyUsageIdentifier' ,\
        'CertAddSerializedElementToStore' ,'CertAddStoreToCollection' ,'CertAlgIdToOID' ,'CertCloseStore' ,'CertCompareCertificate' ,\
        'CertCompareCertificateName' ,'CertCompareIntegerBlob' ,'CertComparePublicKeyInfo' ,'CertControlStore' ,'CertCreateCTLContext' ,\
        'CertCreateCTLEntryFromCertificateContextProperties' ,'CertCreateCertificateChainEngine' ,'CertCreateCertificateContext' ,'CertCreateContext',\
        'CertCreateSelfSignCertificate' ,'CertDeleteCTLFromStore' ,'CertDeleteCertificateFromStore' ,'CertDuplicateCTLContext' ,\
        'CertDuplicateCertificateChain' ,'CertDuplicateCertificateContext' ,'CertDuplicateStore' ,'CertEnumCRLContextProperties' ,\
        'CertEnumCRLsInStore' ,'CertEnumCTLContextProperties' ,'CertEnumCTLsInStore' ,'CertEnumCertificateContextProperties' ,\
        'CertEnumCertificatesInStore' ,'CertEnumPhysicalStore' ,'CertEnumSubjectInSortedCTL' ,'CertEnumSystemStore' ,\
        'CertEnumSystemStoreLocation' ,'CertFindAttribute' ,'CertFindCRLInStore' ,'CertFindCertificateInCRL' ,'CertFindCertificateInStore',\
        'CertFindChainInStore' ,'CertFindExtension' ,'CertFindRDNAttr' ,'CertFindSubjectInCTL' ,'CertFindSubjectInSortedCTL' ,\
        'CertFreeCRLContext' ,'CertFreeCertificateChain' ,'CertFreeCertificateChainEngine' ,'CertFreeCertificateContext' ,'CertGetCRLContextProperty' ,\
        'CertGetCRLFromStore' ,'CertGetCTLContextProperty' ,'CertGetCertificateChain' ,'CertGetCertificateContextProperty' ,'CertGetEnhancedKeyUsage' ,\
        'CertGetIssuerCertificateFromStore' ,'CertGetNameStringA' ,'CertGetNameStringW' ,'CertGetPublicKeyLength' ,'CertGetStoreProperty' ,\
        'CertGetSubjectCertificateFromStore' ,'CertGetValidUsages' ,'CertIsRDNAttrsInCertificateName' ,'CertIsValidCRLForCertificate' ,\
        'CertNameToStrA' ,'CertNameToStrW' ,'CertOIDToAlgId' ,'CertOpenStore' ,'CertOpenSystemStoreA' ,'CertOpenSystemStoreW' ,'CertRDNValueToStrA',\
        'CertRDNValueToStrW' ,'CertRegisterPhysicalStore' ,'CertRegisterSystemStore' ,'CertRemoveEnhancedKeyUsageIdentifier' ,\
        'CertRemoveStoreFromCollection' ,'CertResyncCertificateChainEngine' ,'CertSaveStore' ,'CertSerializeCRLStoreElement' ,\
        'CertSerializeCertificateStoreElement' ,'CertSetCRLContextProperty' ,'CertSetCertificateContextPropertiesFromCTLEntry' ,\
        'CertSetCertificateContextProperty' ,'CertSetEnhancedKeyUsage' ,'CertSetStoreProperty' ,'CertStrToNameA' ,'CertStrToNameW' ,\
        'CertUnregisterPhysicalStore' ,'CertUnregisterSystemStore' ,'CertVerifyCRLRevocation' ,'CertVerifyCRLTimeValidity' ,\
        'CertVerifyCTLUsage' ,'CertVerifyCertificateChainPolicy' ,'CertVerifyCertificateChainPolicy' ,'CertVerifyRevocation' ,\
        'CertVerifySubjectCertificateContext','CertVerifyTimeValidity' ,'CertVerifyValidityNesting' ,'CloseCertPerformanceData' ,\
        'CollectCertPerformanceData' ,'CryptAcquireCertificatePrivateKey' ,'CryptFindCertificateKeyProvInfo' ,'CryptGetMessageCertificates' ,\
        'CryptHashCertificate' ,'CryptSignAndEncodeCertificate' ,'CryptSignCertificate' ,'CryptVerifyCertificateSignature' ,\
        'CryptVerifyCertificateSignatureEx' ,'I_CertProtectFunction' ,'I_CertSrvProtectFunction' ,'I_CertSyncStore' ,'I_CertUpdateStore' ,\
        'I_CryptAddSmartCardCertToStore' ,'I_CryptFindSmartCardCertInStore' ,'OpenCertPerformanceData' ,'PFXExportCertStore' ,'PFXExportCertStoreEx' ,'PFXImportCertStore'] 
file_s = ['SRC', 'FindFirstFileW', 'FindNextFileW', 'FindClose']
modify = ['MOD', 'WriteProcessMemory', 'ReadProcessMemory'] 
virtual = ['VIR', 'VirtualAlloc' ,'VirtualAllocEx' ,'VirtualBufferExceptionHandler' ,'VirtualFree' ,'VirtualFreeEx' ,'VirtualLock' ,
           'VirtualProtect' ,'VirtualProtectEx' ,'VirtualQuery' ,'VirtualQueryEx' ,'VirtualUnlock']
critical_section = [ 'CRT', 'DeleteCriticalSection' ,'EnterCriticalSection' ,'InitializeCriticalSection' ,
                     'InitializeCriticalSectionAndSpinCount' ,'LeaveCriticalSection' ,'SetCriticalSectionSpinCount' ,'TryEnterCriticalSection']

api_matrix = [ reg, winsock, WinINet, cache, ftp, gopher, dir, mutex, pipe, http, enum, hash, crypt, service, file, cert, os_info, file_s, modify, virtual, critical_section,\
               launcher, process_injection, DLL_injection, direct_injection, process_replacement, hook_injection, APC_injection, APC_injection, APC_userspace, APC_kernelspace,\
               windows_networking, anti_debugging]

def getAllStrings():
    #We want to use a specific string configuration
    stringList = idautils.Strings(default_setup = False)
    # Lets use C et UNICODE string
    stringList.setup(strtypes=Strings.STR_C | Strings.STR_UNICODE, ignore_instructions = True, display_only_existing_strings = True)

    parsedStringList = []
    #Get all strings
    for string in stringList:                
        if string:
            parsedStringList.append((str(string), string.ea))

    return parsedStringList
#-----------------------------------------------------------------------
# Configuration agent
# The two next function are needed so the configuration manager will be
# able to provide configuration for this module.
# configurationNeed is called first. If no config for this 
# utility exists, user is prompted for utility configuration. If
# configuration exists, previous configs will be used. In all cases,
# configuration step is ended when configurationProvision
# is called with a configuration list as arg.
#-----------------------------------------------------------------------
'''
IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = ""
def configurationNeed():
    return ["IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH", "IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE"]
            
def configurationProvision(utilityConfig=[]):
    global IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH
    global IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE
    IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = utilityConfig[0]
    IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE = utilityConfig[1]
'''

def sanitize_name(name):
    name = name.strip()
    if name.endswith("A"):
        name = name[:-1]
    elif name.endswith("W"):
        name = name[:-1]
    while name.startswith("_"):
        name = name[1:]
    return name

def getFuncDeclaration(func):
    if func != idc.BADADDR:
        ft = idc.GetType(func)
        if ft:
            return ft
        else:
            #print "-Type info not available!"
            return ""
    else:
        return ""    
  
def prefix_lines(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [RESULT_PREFIX+line for line in lines.splitlines()]
    return "\n".join(result)
    
def prefix_lines_oa(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [RESULT_PREFIX_OA+line for line in lines.splitlines()]
    return "\n".join(result)
    
def remove_prefixed_lines(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [line for line in lines.splitlines() \
              if not line.startswith(RESULT_PREFIX)]
    return "\n".join(result)
    
def remove_prefixed_lines_oa(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [line for line in lines.splitlines() \
              if not line.startswith(RESULT_PREFIX_OA)]
    return "\n".join(result)

  
def get_all_function_eas():
    result = []
    for fnum in xrange(idaapi.get_func_qty()):
        func = idaapi.getn_func(fnum)
        f_ea = func.startEA
        if f_ea != idaapi.BADADDR:
            result.append(f_ea)
    return result
    
def get_import_segments():
    result = []
    for seg_ea in idautils.Segments():
        seg = idaapi.getseg(seg_ea)
        if seg.type == idaapi.SEG_XTRN:
            result.append( (seg.startEA, seg.endEA) )
    return result
    
def u2signed(num):
    if num < 0x80000000:
        return num
    else:
        return num - 0x100000000
    
class FunctionX(object):
    def __init__(self, start_ea):
        self.start_ea = start_ea
        
    def get_end_address(self):
        return "0x%x" % idc.GetFunctionAttr(self.start_ea, idc.FUNCATTR_END)
    def get_number_of_instructions(self):
        icount = 0
        for i in idautils.FuncItems(self.start_ea): icount = icount + 1
        return icount
    def get_size_of_argument_bytes(self):
        try:
            fframe = idc.GetFrame(self.start_ea)
            if fframe is not None:
                fret = idc.GetMemberOffset(fframe, " r")
            if fret is not -1:
                firstArg = fret + 4
            args = idc.GetStrucSize(fframe) - firstArg
            return args
        except:
            pass     
            return
    def get_number_of_arguments(self):
        try:
            args = self.get_size_of_argument_bytes()
            out = args/4
        except:
            out = 0
            pass
        return out
    def get_size_of_local_variables(self):
        try:
            locals = idc.GetFunctionAttr(self.start_ea, idc.FUNCATTR_FRSIZE)
            return locals
        except:
            pass
            return
    def get_function_flags(self):
        try:
            return idc.GetFunctionFlags(self.start_ea)
        except:
            pass
            return
    def get_name(self):
        return idaapi.get_func_name(self.start_ea)
    def get_demangled_name(self):
        mangled = idaapi.get_func_name(self.start_ea)
        demangled = idc.Demangle(mangled, idc.GetLongPrm(idc.INF_SHORT_DN))
        if  demangled == None:
             return "" #mangled
        else:
            #print " --Demangling..."
            return demangled
    def get_instruction_eas(self):
        result = []
        for chunk in self.__get_chunks():
            ins_ea = idc.FindCode(chunk.startEA-1, DIRECTION_FORW)
            while ins_ea <= chunk.endEA and ins_ea != idc.BADADDR:
                result.append(ins_ea)
                ins_ea = idc.FindCode(ins_ea, DIRECTION_FORW)
        return result    
    def get_instructions(self):
        return [Instruction(iea, ) for iea in self.get_instruction_eas()]
    def get_code_refs_from(self):
        result = []
        for iea in self.get_instruction_eas():
            inst = Instruction(iea, )
            result += inst.get_code_refs_from()
        return list(set(result))
    def get_data_refs_from(self):
        result = []
        for iea in self.get_instruction_eas():
            inst = Instruction(iea, )
            result += inst.get_data_refs_from()
        return list(set(result))
    def set_comment(self, comment, repeatable = False):
        func = self.__get_func_t()
        idaapi.set_func_cmt(func, comment, repeatable)
    def get_comment(self, repeatable = False):
        func = self.__get_func_t()
        return idaapi.get_func_cmt(func, repeatable)
    def __get_chunks(self):
        func = self.__get_func_t()
        result = [func]
        ft_iter = idaapi.func_tail_iterator_t(func, self.start_ea)
        if ft_iter.first():
            result.append(ft_iter.chunk())
        while ft_iter.next():
            result.append(ft_iter.chunk())
        return result
    def __get_func_t(self):
        result = idaapi.get_func(self.start_ea)
        if not result:
            raise RuntimeError, \
                  "Cannot retrieve function information @ address %s" % \
                  self.start_ea
        return result
    def __repr__(self):
        return self.get_name()

class Instruction(object):
    def __init__(self, inst_ea):
        self.iea = inst_ea
        
    def get_code_refs_from(self):
        return idautils.CodeRefsFrom(self.iea, False)
    def get_data_refs_from(self):
        return idautils.DataRefsFrom(self.iea, False)
    def get_operands(self):
        result = []
        ins = self.__get_insn_t()
        if not ins:
            print >> sys.stderr, \
                  "Cannot retrieve operand information @ address %s" % \
                  hex(self.iea)
            return result
        for i in xrange(6):
            oper = ins[i]
            if oper.type != idaapi.o_void:
                result.append( Operand(i, self.iea, ) )
        return result
    def __get_insn_t(self):
        ins = idautils.DecodeInstruction(self.iea)
        if ins:
	    return ins
        return None
    def __repr__(self):        
        return idaapi.tag_remove( idaapi.generate_disasm_line(self.iea) )
        
class Operand(object):
    def __init__(self, oper_num, inst_ea):
        self.onum = oper_num
        self.iea = inst_ea
        
        
    def get_immediate(self):
        oper = self.__get_op_t()
        if not oper or oper.type != idaapi.o_imm:
            return None        
        value = oper.value
        if value in idautils.DataRefsFrom(self.iea):
            return None
        return value
    def get_string(self):
        mem = self.get_memory_address()
        if not mem:
            return None
        flags = idaapi.getFlags(mem)
        if not idaapi.isASCII(flags):
            return None
        tinfo = idaapi.opinfo_t()
        idaapi.get_opinfo(mem, 0, flags, tinfo)
        slen = idaapi.get_max_ascii_length(mem, tinfo.strtype)
        return idaapi.get_ascii_contents2(mem, slen, tinfo.strtype)
        
    def get_symbolicName(self):
        #Check if constant:
        value = self.get_immediate()
        if value is None:
            return None
        
        #Now lets try to see if it uses symbolic names
        operandCounter = 0
        operandText = GetOpnd(self.iea, self.onum)
        if (operandText != ""):
                if operandText[-1] == "h":
                    operandTextTemp = operandText[:-1]
                try:
                    result = int(operandTextTemp, 16)
                    return None
                except:
                    return operandText
        #End of symbolic names chases...
        
    def get_memory_address(self):
        oper = self.__get_op_t()
        if oper.type == idaapi.o_mem:
            return oper.addr
        elif oper.type == idaapi.o_imm and self.iea != idc.BADADDR:
            ref = oper.value
            if ref in idautils.DataRefsFrom(self.iea):
                return ref
        elif (oper.type == idaapi.o_displ or oper.type == idaapi.o_phrase) \
                 and not self.is_stackref():
            return oper.addr
        return None
    def is_stackref(self):
        oper = self.__get_op_t()
        if not oper.type in [idaapi.o_displ, idaapi.o_phrase]:
            return False
        offset = u2signed(oper.addr)
        return ( idaapi.get_stkvar(oper, offset) != None )
    def __get_op_t(self):
        ins = idautils.DecodeInstruction(self.iea)
        if not ins:
            return None
        else:
            return ins.Operands[self.onum]
             

 
#-----------------------------------------------------------------------
# IdaProExtractor 
# This class implements IDA Pro extraction capabilities. It also uses 
# a UI part (IdaProExtractorUI) in order to get 
# configuration selection from the User
#-----------------------------------------------------------------------
class IdaProTextExtractorScript():
    
    def __init__(self, sList=[], flg=0):
        self._sList = sList #func, eas for selected functions
        self._flg = flg
    #
    # This method is in charge of features extraction coordination from an IdaPro Input source
    # it returns an array containing all source features built with the following format:
    # [(functionName, [("searchTerm", "type") ..]), ..]
    # returns None if it encounters an error
    # Input file singleFile should be built like:
    # (fileName, [func_ea, ..])
    #
    def pluginExtract(self, _flg, _sList):
        if _flg:
            return self.getFunctionFeatures(self.getSelectedFunctionNamesAndEas(_sList))
        else:
            return self.getFunctionFeatures(self.getAllFunctionNamesAndEas())

    #
    # This method is used so plugin user could have the chance
    # to select functions to be extracted in a list of all the functions
    # that are inside a specific 
    #
   
    def getAllFunctionNamesAndEas(self):
        result = []
        allFuncEAS = get_all_function_eas()
        for ea in allFuncEAS:
            result.append((idaapi.get_func_name(ea), ea))
        return result


    def getSelectedFunctionNamesAndEas(self, _sList):
        result = []
        allFuncEAS = _sList
        for ea in allFuncEAS:
            result.append((idaapi.get_func_name(ea), ea))
        return result
 
    #
    # This method aims at returning all extracted features for a 
    # specific range of function. Its returns a features list.
    #
    def getFunctionFeatures(self, funcEaList=None):
        result = []
        if funcEaList:
            for cidx, currFunc in enumerate(funcEaList):
			
                tempFunc = FunctionX(currFunc[1])
                currFuncFeatures = self.extractFeatures(tempFunc)
                result.append(((tempFunc.get_name(),currFunc[1]), currFuncFeatures))
        #Check for strings!
        idbStringList = getAllStrings()
        
        for singleResult in result:
            for searchTerm in singleResult[1]:
                for singleString in idbStringList:
                    if searchTerm[1] in singleString[0]:
                        idbStringList.remove(singleString)
        
        alreadyInList = [] #This is used as a buffer to prevent duplicate strings
        for string in idbStringList:
            #Prevent duplicate strings
            if string[0] not in alreadyInList:
                #hash = hashlib.md5(string).hexdigest()
                result.append((("Orphan String " + str(string[0]), string[1]),[('s', str(string[0]))]))
                alreadyInList.append(string[0])
        #print len(idbStringList)
        #print result[0]        
        return result

    
    def extractFeatures(self, func):
        #print " --Extracting..."
        IDATA_SEGMENTS1 = get_import_segments()
        joint=[]
        name=[]
        demangle=[]
        constants=[]
        strlist=[]
        implist=[]
        impset = set()
        plist=[]
        rtype=[]
        arglist=[]
        numinst=[]
        numargs=[]
        argbytes=[]
        varbytes=[]
        fnflags=[]
        tagmal=[]
        tagapi=[]
        outcall=[]
        calllist=[]
        fname = func.get_name()
        dename = func.get_demangled_name()
        name.append(('n',fname))
        if dename:
            demangle.append(('ne',dename)) #ne: demangled C++ names
        for inst in func.get_instructions():
            for oper in inst.get_operands():
                val = oper.get_immediate()
                if val:
                    constants.append(('c', "%x" % val))
                    #Symbolic name can't be used in const we have to put it in strings
                    symbolicName = oper.get_symbolicName()
                    if symbolicName is not None and len(symbolicName) > 0:
                        try:
                            transition = int(symbolicName)
                        except:
                            strlist.append(('s',"%s" % symbolicName))
                    
                soper = oper.get_string()
                if soper:
                    if len(soper)>0:
                        #soper = replace_all(soper,BlackDict) 
                        strlist.append(('s',"%s" % soper))
                coderefs = inst.get_code_refs_from()
                for ref in coderefs:
                    if len([start for (start, end) in IDATA_SEGMENTS1 \
                            if start <= ref <= end]) > 0:
                        iname = idaapi.get_name(inst.iea, ref)
                        if iname:
                            impset.add( sanitize_name(iname) )
        for item in impset:
            implist.append( ( 'i', sanitize_name(item) ) )
        addr = func.start_ea 
        fend = idc.GetFunctionAttr(addr, idc.FUNCATTR_END)
        tt = getFuncDeclaration(addr)
        plist.append( ( 'p', tt ) ) #p: prototype
        params =  tt[tt.find("(")+1:tt.find(")")]
        lparams = params.split(',')
        sl = [(par.strip()) for par in lparams]
        for arg in sl:
            if arg:
                arglist.append( ( 'a', arg ) ) #a: arguments
        rt = tt[:tt.find(' ')]
        if rt:
            rtype.append( ( 'r', rt ) ) #r: return type    
        numinst.append( ( 'm', str(func.get_number_of_instructions()) ) ) #m: number of instructions
        g=func.get_number_of_arguments()
        if g:
            numargs.append( ( 'g', str(g) ) ) #g: number of arguments
        bbb=func.get_size_of_argument_bytes()
        if bbb:
            argbytes.append( ( 'b', str(bbb) ) ) #b: size of argument bytes
        varbytes.append( ( 'l', str(func.get_size_of_local_variables()) ) ) #l: size of local vars bytes
        fnflags.append( ( 'f', str(func.get_function_flags()) ) ) #f: function flags
        for elmntz in implist:
            if elmntz[1] in WinMalFuncInSight:
                tagmal.append(('d','MAL'))
        ad=0
        for api_row in api_matrix:
            l = api_row[0]
            apis = api_row[1:]
            for api in apis:
                ref_addrs = idautils.CodeRefsTo(idc.LocByName(api),0)
                for ref in ref_addrs:
                    func_name = idc.GetFunctionName(ref)
                    func_addr = idc.LocByName(func_name)
                    ad=addr 
                    if func_addr == ad:
                        tagapi.append(('t',l))
                    
                    
        bb=func.get_code_refs_from()
        outcall.append(('o', str( len(bb) ) )) # o: number of code refs from this function
        items = idautils.FuncItems(func.start_ea)
        for i in items:
            for xref in idautils.XrefsFrom(i, 0):
                if xref.type == idc.fl_CN or xref.type == idc.fl_CF:
                    dstf = idc.Name(xref.to)
                    calllist.append(('k', dstf )) # o: number of code refs from this function
                    demangled = idc.Demangle(dstf, idc.GetLongPrm(idc.INF_SHORT_DN))
                    if  demangled != None:
                        calllist.append(('ke', demangled )) #ke: demangled C++ call
        joint = name+demangle+constants+strlist+implist+rtype+arglist+numinst+numargs+argbytes+varbytes+fnflags+tagmal+tagapi+outcall+calllist
        z=fend - ad
        if z:
            joint.append(('z', str(z))) #function size (bytes)
        joint.append(('cx',str(len(constants))))
        joint.append(('sx',str(len(strlist))))
        joint.append(('mx',str(numinst[0][1])))
        if g:
            joint.append(('gx',str(numargs[0][1])))
        joint.append(('ix',str(len(implist))))
        joint.append(('dx',str(len(tagmal))))
        joint.append(('tx',str(len(tagapi))))
        joint.append(('ox',str(len(outcall))))
        joint.append(('kx',str(len(calllist))))
        return joint

class AsmFunctionList(Choose2):
    def __init__(self, title, flags=0):
        Choose2.__init__(self,
                         title,
                         [ ["Address", 10 | Choose2.CHCOL_HEX],
                           ["Function Name", 15 | Choose2.CHCOL_PLAIN] ],
                         embedded=True, width=25, height=5, flags=flags)
        self.n = 0
        self.selcount = 0
        self.icon = 41
        self.PopulateItems()
    def PopulateItems(self):
        global funcListG
        funcListG = [ [hex(x), GetFunctionName(x), x] for x in idautils.Functions() ] #x=ea
        self.items = funcListG
    def OnClose(self):
        pass
    def OnRefresh(self, n):
        self.Refresh()
        return self.n
    def OnRefreshed(self):
        return self.n
    def OnSelectLine(self, n):
        self.selcount += 1
    def OnGetLine(self, n):
        return self.items[n]
    def OnGetSize(self):
        n = len(self.items)
        return n
		
class ReSrcForm(Form):
    def __init__(self):
        global funcListG
        self.EChooser = AsmFunctionList("asm", flags=Choose2.CH_MULTI)
        Form.__init__(self,
r"""BUTTON YES* NONE
BUTTON CANCEL Exit
BinSourcerer
{FormChangeCb}
Function Selection:
<Please use [ctrl+click] or [shift+click] for multiple selections.:{cEChooser}>
Search Configuration:
<#Selecting this option will disable the list.#Select all functions:{rSearchAll}>
<Use offline analyzer for malware:{rOfflineMal}>{cSearchCfgGroup}>
Feature Extraction:
<Path:{iPath}>
<File:{iFSave}>
<Feature Extraction:{iBatchM}><API Tagging:{iAPI}>

""", {
            'cSearchCfgGroup': Form.ChkGroupControl(("rSearchAll", "rOfflineMal")),
            'iBatchM': Form.ButtonInput(self.OnFormChange),
            'iPath': Form.DirInput(swidth=30),
	    'iFSave': Form.FileInput(save=True, swidth=30),
            'iAPI': Form.ButtonInput(self.OnFormChange),
            'FormChangeCb': Form.FormChangeCb(self.OnFormChange),
            'cEChooser' : Form.EmbeddedChooserControl(self.EChooser)
        })
        
    def validPath(self, inputFPath):
        if os.access(os.path.dirname(inputFPath), os.W_OK):
            return True
        else:
            return False
        
    def OnFormChange(self, fid):
        if fid == self.iBatchM.id:
            p = self.GetControlValue(self.iPath)
	    f = self.GetControlValue(self.iFSave)
	    if p and f and self.validPath(p+"\\"+f):
                print "Status: BinSourcerer is ready for magic..."
                IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = p + "\\"
                IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE = f
                print "Status: Extracting features. This could take a few minutes..."

                stt = self.GetControlValue(self.rSearchAll)
                if stt==1:
                    x = IdaProTextExtractorScript()
                    results = x.pluginExtract(0, [])
                else:
                    global funcListG
                    selFuncEAS = []
                    selectedFuncsList = self.GetControlValue(self.cEChooser)
                    if len(selectedFuncsList) > 0:
                            for funcIndex in selectedFuncsList:
                                    selFuncEAS.append( funcListG[funcIndex][2] )
                            x = IdaProTextExtractorScript(selFuncEAS, 1)
                            results = x.pluginExtract(1, selFuncEAS)
                    else:
                            idc.Warning("Please select multiple functions from the list.")
                print "Status: Features extraction completed. Saving results..."
                try:
                    f = open(IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH + IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE, "w")
                    if results:
                        for func in results:
                            f.write(str(func) + "\n")
			outputPath = IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH + IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE
                    
                        print "Status: Features file saved in " + os.path.normpath(outputPath)
                except:
                    print "I/O Error! Could not write to the output file!"
                finally:
                    f.close()
                    
            else:
                idc.Warning("Please select a valid path and file name!\n")
       
        elif fid == self.rSearchAll.id:
            stt = self.GetControlValue(self.rSearchAll)
            if stt==1:
                self.EnableField(self.cEChooser, False)
            else:
                self.EnableField(self.cEChooser, True)

	elif fid == self.iAPI.id:
            potentialTags=APITags(0)
            choice=AskYN(1,"Approximately "+ str(potentialTags)+" tags are to be assigned to functions.\nProceed with name changes?")
            if choice==1:
                APITags(1)
                self.EChooser.PopulateItems()
                self.RefreshField(self.cEChooser)
        return 1

def APITags(flag):
    apilist=[]
    if flag==1:
        save=True
    else:
        save=False
    count=0
    for idx, api_row in enumerate(api_matrix):
        l = api_row[0]
        apis = api_row[1:]
        for api in apis:
            ref_addrs = CodeRefsTo(LocByName(api),0)
            for ref in ref_addrs:
                func_addr = LocByName(GetFunctionName(ref))
                fnsAddr = GetFunctionAttr( func_addr, FUNCATTR_START )
                func_name = GetFunctionName(ref)
                if l not in func_name:
                    count +=1 #counter
                    apilist.append( ( 't', l ) ) #t: api tags
                    ss = l + '_' + func_name
                    if save:
                        MakeNameEx(func_addr , ss  , SN_NOWARN)		
    return count		
#-------------------------------------------------------
# The following vars have to be configured to matching
# values from the BinSourcerer configuration file
#-------------------------------------------------------
IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = 'C:\\Users\\UserName\\Desktop\\Features\\'
IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE = 'Features.xtrak'
funcListG = []

class TextExtract_t(idaapi.plugin_t):
    flags = 0
    comment = "Extract all the available features"
    help = "Features will be saved in the output file: [extractedFeatures.extract]"
    wanted_name = "BinSourcerer Features Extractor"
    wanted_hotkey = "Ctrl-Shift-J"
   
    def init(self):
        idaapi.msg("Status: BinSourcerer Features Extractor plug-in loaded.\n")
        return idaapi.PLUGIN_KEEP
    def run(self, arg):
	global fm
	fm = ReSrcForm()
	fm.Compile()
        fm.rSearchAll.checked = False
	fm.rOfflineMal.checked = True
	cwdPath = os.getcwd() + "\\"
	IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = cwdPath	
	fm.iPath.value = cwdPath
	fm.iFSave.value = "Features.xtrak"
	ok = fm.Execute()
	fm.Free()
    def term(self):
        pass

def PLUGIN_ENTRY():
    return TextExtract_t()



    


repository: binsrc/BinSourcerer
file: BinSourcerer/IdaProTextExtractor_HRC.py
description: Assembly to Source Code Matching Framework
wanted_name: BinSourcerer Features Extractor
comment: Extract all the available features
created_at: 2015-02-04 16:42:49+00:00
pushed_at: 2015-02-04 16:46:13+00:00
url:
https://github.com/binsrc/BinSourcerer/blob/2eab5d88d721909f440427042a896dfa591053c6/BinSourcerer/IdaProTextExtractor_HRC.py

content:
#!/usr/bin/env python
# -*-coding: utf8 -*-
#
################################################################################
#
#  Copyright 2014 Concordia University
# 
#  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.
#
################################################################################
# Description:
#   This plugin extracts certain featuers from IDA Pro disassembly and
#   generates a feature file, which is used in an online code search process.
#   This file is part of the BinSourcerer framework.
#
# Status: Debug
#
################################################################################




DIRECTION_FORW = 0x03

import idautils
import os

from idaapi import *
import hashlib
import idc

#Offline Analyzer (OA), Windows function names and a brief description.
WinMalFuncInSight = {
    'accept':'The program may listen for incoming connections on a socket.',
    'AdjustTokenPrivileges':'The program may modify specific access privileges.',
    'AttachThreadInput':'The program may contain keylogging or spyware functionality.',
    'bind':'The program may listen for incoming connections.',
    'BitBlt':'The program may capture screenshots.',
    'CallNextHookEx':'The code calls the next hook in the chain set by SetWindowsHookEx.',
    'CertOpenSystemStore':'The program may access the certificates stored on the local system.',
    'CheckRemoteDebuggerPresent':'The code may contain an anti-debugging technique.',
    'CoCreateInstance':'The code creates a COM object.',
    'connect':'The program may connect to a remote command-and-control server.',
    'ConnectNamedPipe':'The code may create a server pipe for interprocess communication',
    'ControlService':'The code may start, stop, modify, or send a signal to a running service.',
    'CreateFile':'The program may create a new file or open an existing file.',
    'CreateFileMapping':'The code may read and modify PE files by creating handles to file mappings.',
    'CreateMutex':'Only one instance of the program runs on a system.',
    'CreateProcess':'The code may create and launch a new process.',
    'CreateRemoteThread':'The program may inject code into a different process.',
    'CreateService':'The program may create an autorun service or load kernel drivers.',
    'CreateToolhelp32Snapshot':'The code may create a snapshot of processes, heaps, threads, and modules.',
    'CryptAcquireContext':'The code may initialize the use of Windows encryption.',
    'DeviceIoControl':'The code may send control messages from user space to kernel space.',
    'DllCanUnloadNow':'The program implements a COM server. (Exported function)',
    'DllGetClassObject':'The program implements a COM server. (Exported function)',
    'DllInstall':'The program implements a COM server. (Exported function)',
    'DllRegisterServer':'The program implements a COM server. (Exported function)',
    'DllUnregisterServer':'The program implements a COM server. (Exported function)',
    'EnableExecuteProtectionSupport':'The code may modify the Data Execution Protection (DEP) settings of the host.',
    'EnumProcesses':'The code may enumerate through processes to find a process to inject into.',
    'EnumProcessModules':'The code may enumerate the loaded modules for injection.',
    'FindFirstFile':'The program may search through a directory and enumerate the filesystem.',
    'FindNextFile':'The program may search through a directory and enumerate the filesystem.',
    'FindResource':'The code may search for a resource in an executable or loaded DLL',
    'FindWindow':'The code searches for an open window on the desktop. (It may also contain anti-debugging)',
    'FtpPutFile':'The program may upload files to a remote FTP server.',
    'GetAdaptersInfo':'The code may obtain information about the MAC, network adapters on the system.',
    'GetAsyncKeyState':'The code may contain keylogger functionality.',
    'GetDC':'The code returns a handle to a device context for a window or the whole screen. (Screen Capture)',
    'GetForegroundWindow':'The code returns a handle to the window currently in the foreground of the desktop. (Keylogger)',
    'gethostbyname':'The code may perform a DNS lookup on a particular hostname. (C&C)',
    'gethostname':'The code may retrieve the hostname of the computer. (Backdoor)',
    'GetKeyState':'The code may obtain the status of a particular key on the keyboard. (Keylogger)',
    'GetModuleFilename':'The code may return the filename of a module that is loaded in the current process.',
    'GetModuleHandle':'The code may obtain a handle to an already loaded module. (Inject Code)',
    'GetProcAddress':'The code may retrieve the address of a function in a DLL loaded into memory.',
    'GetStartupInfo':'The code may get details about how the current process was configured to run',
    'GetSystemDefaultLangId':'The code may return the default language settings for the system.',
    'GetTempPath':'The code may return the temporary file path.',
    'GetThreadContext':'The code may read the context of a thread. (register values and current state)',
    'GetTickCount':'The code may retrieve the number of milliseconds since bootup. (Anti-debugging)',
    'GetVersionEx':'The code may return information about which version of Windows is currently running.',
    'GetWindowsDirectory':'The code may get the file path to the Windows directory.',
    'inet_addr':'The code may process IP addresses for network connectivity.',
    'InternetOpen':'The code may initialize the high-level Internet access functions from WinINet',
    'InternetOpenUrl':'The program may open a specific URL for a connection using FTP, HTTP, or HTTPS.',
    'InternetReadFile':'The program may read data from a previously opened URL.',
    'InternetWriteFile':'The program may write data to a previously opened URL.',
    'IsDebuggerPresent':'The code may check to see if the current process is being debugged. (Anti-debugging)',
    'IsNTAdmin':'The code checks if the user has administrator privileges.',
    'IsWoW64Process':'The code may check if it is running on a 64-bit operating system.',
    'LdrLoadDll':'The code uses a low-level function to load a DLL into a process and it may attempt to be stealthy.',
    'LoadLibrary':'The code loads a DLL into a process that may not have been loaded when the program started.',
    'LoadResource':'The code may load a resource from a PE file into memory.',
    'LsaEnumerateLogonSessions':'The code may enumerate through logon sessions on the current system.',
    'MapViewOfFile':'The code maps a file into memory and makes the contents of the file accessible via memory addresses.',
    'MapVirtualKey':'The code may translate a virtual-key code into a character value. (Keylogger)',
    'MmGetSystemRoutineAddress':'The code may retrieve the address of a function from another module. (Kernel code)',
    'Module32First':'The code may enumerate through modules loaded into a process. (Injector)',
    'Module32Next':'The code mya enumerate through modules loaded into a process. (Injector)',
    'NetScheduleJobAdd':'The code may submit a request for a program to be run at a specified date and time in the future.',
    'NetShareEnum':'The code may enumerate network shares.',
    'NtQueryDirectoryFile':'The code may get information about files in a directory. (Rootkit)',
    'NtQueryInformationProcess':'The code may return various information about a specified process. (Anti-debugging)',
    'NtSetInformationProcess':'The code may change the privilege level of a program or to bypass Data Execution Prevention (DEP).',
    'OleInitialize':'The code initialize the COM library.',
    'OpenMutex':'The code may open a handle to a mutual exclusion object to ensure that only a single instance is running.',
    'OpenProcess':'The code may open a handle to another process to read/write or to inject code.',
    'OpenSCManager':'The code may open a handle to the service control manager.',
    'OutputDebugString':'The code may output a string to a debugger if one is attached. (Anti-debugging)',
    'PeekNamedPipe':'The code may copy data from a named pipe without removing data from the pipe. (Reverse shell)',
    'Process32First':'The code may enumerate through processes to find a process to inject into.',
    'Process32Next':'The code may enumerate through processes to find a process to inject into.',
    'QueryPerformanceCounter':'The code may retrieve the value of the hardware-based performance counter. (Anti-debugging)',
    'QueueUserAPC':'The code may execute code for a different thread and inject code into another process.',
    'ReadProcessMemory':'The code may read the memory of a remote process.',
    'recv':'The code may receive data from a remote command-and-control server.',
    'RegisterHotKey':'The code may register a handler to be notified anytime a user enters a particular key combination.',
    'RegOpenKey':'The code may open a handle to a registry key for reading and editing.',
    'ResumeThread':'The code may resume a previously suspended thread for injection.',
    'RtlCreateRegistryKey':'The code may create a registry from kernel-mode code.',
    'RtlWriteRegistryValue':'The code may write a value to the registry from kernel-mode code.',
    'SamIConnect':'The code may connect to the Security Account Manager in order to make future calls that access credential information. (Hash-dumping)',
    'SamIGetPrivateData':'The code may query the private information about a specific user from the Security Account Manager (SAM) database. (Hash-dumping)',
    'SamQueryInformationUse':'The code may query information about a specific user in the Security Account Manager (SAM) database. (Hash-dumping)',
    'send':'The code may send data to a remote command-and-control server.',
    'SetFileTime':'The code may modify the creation/access/last modified time of a file.',
    'SetThreadContext':'The code may modify the context of a given thread for code injection.',
    'SetWindowsHookEx':'The code may set a hook function to be called whenever a certain event is called. (Keylogger)',
    'SfcTerminateWatcherThread':'The code may disable Windows file protection and modify the otherwise protected files.',
    'ShellExecute':'The code may execute another program and create a new process.',
    'StartServiceCtrlDispatcher':'The code may run as a service.',
    'SuspendThread':'The code may suspend a thread to modify it by performing code injection.',
    'system':'The code may run another program provided by some C runtime libraries. (wrapper function to CreateProcess.',
    'Thread32First':'The code may iterate through the threads of a process to find an appropriate thread to inject into.',
    'Thread32Next':'The code may iterate through the threads of a process to find an appropriate thread to inject into.',
    'Toolhelp32ReadProcessMemory':'The code may read the memory of a remote process.',
    'URLDownloadToFile':'The code may download a file from a web server and save it to disk.',
    'VirtualAllocEx':'The code may allocate memory in a remote process as part of process injection.',
    'VirtualProtectEx':'The code may change the protection on a region of memory from read-only to executable.',
    'WideCharToMultiByte':'The code may convert a Unicode string into an ASCII string.',
    'WinExec':'The code may execute another program by creating a new process.',
    'WlxLoggedOnSAS':'The code may perform Graphical Identification and Authentication (GINA) replacement.',
    'Wow64DisableWow64FsRedirection':'The code may disable file redirection that occurs in 32-bit files loaded on a 64-bit system.',
    'WriteProcessMemory':'The code may write data to a remote process as part of process injection.',
    'WSAStartup':'The code may initialize low-level network functionality.'
     }

# These lists are for renaming functions if they contain certain API names. Based on the script by Alexander Hanel.

# resource manipulation
launcher = ['LCH','FindResource','LoadResource','SizeofResource']
# injection attacks
process_injection = ['PSJ','VirtualAllocEx','WriteProcessMemory']
DLL_injection = ['DLJ','CreateToolhelp32Snapshot','Process32First','Process32Next', 'OpenProcess', 'CreateRemoteThread', 'WriteProcessMemory']
direct_injection = ['DRJ', 'VirtualAllocEx','WriteProcessMemory','CreateRemoteThread']
process_replacement = ['PRP', 'ZwUnmapViewOfSection','VirtualAllocEx','WriteProcessMemory','SetThreadContext','ResumeThread']
hook_injection = ['HKJ', 'SetWindowsHookEx','CallNextHookEx','UnhookWindowsHookEx']
APC_injection = ['ACJ', 'CreateRemoteThread','WaitForSingleObjectEx','WaitForMultipleObjectsEx','Sleep']
APC_userspace = ['AUJ', 'QueueUserAPC','WaitForSingleObjectEx','CreateToolhelp32Snapshot','Process32First','Process32Next','Thread32Next',\
                 'Thread32First','NtQuerySystemInformation','ZwQuerySystemInformation']
APC_kernelspace = ['AKJ', 'KeInitializeApc','KeInsertQueueApc']
# networking code
windows_networking = ['WNT','WSAStartup','getaddrinfo','socket','connect','send','recv','WSAGetLastError','InternetOpen','InternetConnect',\
                      'InternetOpenURL','InternetReadFile','InternetWriteFile','HTTPOpenRequest','HTTPQueryInfo','HTTPSendRequest',\
                      'URLDownloadToFile','CoInitialize','CoCreateInstance','Navigate','gethostbyname']
anti_debugging = ['ADB','IsDebuggerPresent','CheckRemoteDebuggerPresent','NtQueryInfromationProcess','OutputDebugString','FindWindow',\
                  'QueryPerformanceCounter','GetTickCount']
# anti-vm x86 instructions -> e.g. GetMnem(i) == "cpuid"
anti_vmx86 = ['AVM','sidt','sgdt','sldt','smsw','str','in','cpuid']

reg = [ 'REG', 'RegCloseKey' ,'RegConnectRegistryA' ,'RegConnectRegistryW' ,'RegCreateKeyA' ,'RegCreateKeyExA' ,'RegCreateKeyExW',\
        'RegCreateKeyW' ,'RegDeleteKeyA' ,'RegDeleteKeyW' ,'RegDeleteValueA' ,'RegDeleteValueW' ,'RegDisablePredefinedCache' ,\
        'RegDisablePredefinedCacheEx' ,'RegEnumKeyA' ,'RegEnumKeyExA' ,'RegEnumKeyExW' ,'RegEnumKeyW' ,'RegEnumValueA' ,\
        'RegEnumValueW' ,'RegFlushKey' ,'RegGetKeySecurity' ,'RegLoadKeyA' ,'RegLoadKeyW' ,'RegNotifyChangeKeyValue' ,\
        'RegOpenCurrentUser' ,'RegOpenKeyA' ,'RegOpenKeyExA' ,'RegOpenKeyExW' ,'RegOpenKeyW' ,'RegOpenUserClassesRoot' ,\
        'RegOverridePredefKey' ,'RegQueryInfoKeyA' ,'RegQueryInfoKeyW' ,'RegQueryMultipleValuesA' ,'RegQueryMultipleValuesW' ,\
        'RegQueryValueA' ,'RegQueryValueExA' ,'RegQueryValueExW' ,'RegQueryValueW' ,'RegReplaceKeyA' ,'RegReplaceKeyW' ,\
        'RegRestoreKeyA' ,'RegRestoreKeyW' ,'RegSaveKeyA' ,'RegSaveKeyExA' ,'RegSaveKeyExW' ,'RegSaveKeyW' ,'RegSetKeySecurity' ,\
        'RegSetValueA' ,'RegSetValueExA' ,'RegSetValueExW' ,'RegSetValueW' ,'RegUnLoadKeyA' ,'RegUnLoadKeyW', 'SHDeleteEmptyKeyA' ,\
        'SHDeleteEmptyKeyW' ,'SHDeleteKeyA' ,'SHDeleteKeyW' ,'SHOpenRegStream2A' ,'SHOpenRegStream2W' ,'SHOpenRegStreamA' ,\
        'SHOpenRegStreamW' ,'SHQueryInfoKeyA' ,'SHQueryInfoKeyW' ,'SHQueryValueExA' ,'SHQueryValueExW' ,'SHRegCloseUSKey' ,\
        'SHRegCreateUSKeyA' ,'SHRegCreateUSKeyW' ,'SHRegDeleteEmptyUSKeyA' ,'SHRegDeleteEmptyUSKeyW' ,'SHRegDeleteUSValueA' ,\
        'SHRegDeleteUSValueW' ,'SHRegDuplicateHKey' ,'SHRegEnumUSKeyA' ,'SHRegEnumUSKeyW' ,'SHRegEnumUSValueA' ,'SHRegEnumUSValueW'\
        ,'SHRegGetBoolUSValueA' ,'SHRegGetBoolUSValueW' ,'SHRegGetPathA' ,'SHRegGetPathW' ,'SHRegGetUSValueA' ,'SHRegGetUSValueW' ,\
        'SHRegGetValueA' ,'SHRegGetValueW' ,'SHRegOpenUSKeyA' ,'SHRegOpenUSKeyW' ,'SHRegQueryInfoUSKeyA' ,'SHRegQueryInfoUSKeyW' ,\
        'SHRegQueryUSValueA' ,'SHRegQueryUSValueW' ,'SHRegSetPathA' ,'SHRegSetPathW' ,'SHRegSetUSValueA' ,'SHRegSetUSValueW' ,\
        'SHRegWriteUSValueA' ,'SHRegWriteUSValueW' ,'SHDeleteOrphanKeyA' ,'SHDeleteOrphanKeyW' ,'SHDeleteValueA' ,'SHDeleteValueW' ,\
        'SHEnumKeyExA' ,'SHEnumKeyExW' ,'SHEnumValueA' ,'SHEnumValueW' ,'SHGetValueA' ,'SHGetValueW' ,'SHOpenRegStream2A' ,\
        'SHOpenRegStream2W' ,'SHOpenRegStreamA' ,'SHOpenRegStreamW' ,'SHQueryInfoKeyA' ,'SHQueryInfoKeyW' ,'SHQueryValueExA' ,\
        'SHQueryValueExW' ,'SHRegCloseUSKey' ,'SHRegCreateUSKeyA' ,'SHRegCreateUSKeyW' ,'SHRegDeleteEmptyUSKeyA' ,\
        'SHRegDeleteEmptyUSKeyW' ,'SHRegDeleteUSValueA' ,'SHRegDeleteUSValueW' ,'SHRegDuplicateHKey' ,'SHRegEnumUSKeyA' ,\
        'SHRegEnumUSKeyW' ,'SHRegEnumUSValueA' ,'SHRegEnumUSValueW' ,'SHRegGetBoolUSValueA' ,'SHRegGetBoolUSValueW' ,'SHRegGetPathA' ,\
        'SHRegGetPathW' ,'SHRegGetUSValueA' ,'SHRegGetUSValueW' ,'SHRegGetValueA' ,'SHRegGetValueW' ,'SHRegOpenUSKeyA' ,'SHRegOpenUSKeyW' ,\
        'SHRegQueryInfoUSKeyA' ,'SHRegQueryInfoUSKeyW' ,'SHRegQueryUSValueA' ,'SHRegQueryUSValueW' ,'SHRegSetPathA' ,'SHRegSetPathW' ,\
        'SHRegSetUSValueA' ,'SHRegSetUSValueW' ,'SHRegWriteUSValueA' ,'SHRegWriteUSValueW']
winsock = [ 'NET', 'FreeAddrInfoW', 'GetAddrInfoW', 'GetNameInfoW', 'WEP', 'WPUCompleteOverlappedRequest', 'WSAAccept', \
            'WSAAddressToStringA', 'WSAAddressToStringW', 'WSAAsyncGetHostByAddr', 'WSAAsyncGetHostByName', 'WSAAsyncGetProtoByName',\
            'WSAAsyncGetProtoByNumber', 'WSAAsyncGetServByName', 'WSAAsyncGetServByPort', 'WSAAsyncSelect', 'WSACancelAsyncRequest',\
            'WSACancelBlockingCall', 'WSACleanup', 'WSACloseEvent', 'WSAConnect', 'WSACreateEvent', 'WSADuplicateSocketA',\
            'WSADuplicateSocketW', 'WSAEnumNameSpaceProvidersA', 'WSAEnumNameSpaceProvidersW', 'WSAEnumNetworkEvents', 'WSAEnumProtocolsA',\
            'WSAEnumProtocolsW', 'WSAEventSelect', 'WSAGetLastError', 'WSAGetOverlappedResult', 'WSAGetQOSByName', \
            'WSAGetServiceClassInfoA', 'WSAGetServiceClassInfoW', 'WSAGetServiceClassNameByClassIdA', 'WSAGetServiceClassNameByClassIdW',\
            'WSAHtonl', 'WSAHtons', 'WSAInstallServiceClassA', 'WSAInstallServiceClassW', 'WSAIoctl', 'WSAIsBlocking', 'WSAJoinLeaf', \
            'WSALookupServiceBeginA', 'WSALookupServiceBeginW', 'WSALookupServiceEnd', 'WSALookupServiceNextA', 'WSALookupServiceNextW', \
            'WSANSPIoctl', 'WSANtohl', 'WSANtohs', 'WSAProviderConfigChange', 'WSARecv', 'WSARecvDisconnect', 'WSARecvFrom', \
            'WSARemoveServiceClass', 'WSAResetEvent', 'WSASend', 'WSASendDisconnect', 'WSASendTo', 'WSASetBlockingHook', 'WSASetEvent',\
            'WSASetLastError', 'WSASetServiceA', 'WSASetServiceW', 'WSASocketA', 'WSASocketW', 'WSAStartup', 'WSAStringToAddressA', \
            'WSAStringToAddressW', 'WSAUnhookBlockingHook', 'WSAWaitForMultipleEvents', 'WSApSetPostRoutine', 'WSCDeinstallProvider', \
            'WSCEnableNSProvider', 'WSCEnumProtocols', 'WSCGetProviderPath', 'WSCInstallNameSpace', 'WSCInstallProvider', 'WSCUnInstallNameSpace',\
            'WSCUpdateProvider', 'WSCWriteNameSpaceOrder', 'WSCWriteProviderOrder', '__WSAFDIsSet', 'accept', 'bind', 'closesocket', 'connect', \
            'freeaddrinfo', 'getaddrinfo', 'gethostbyaddr', 'gethostbyname', 'gethostname', 'getnameinfo', 'getpeername', 'getprotobyname', \
            'getprotobynumber', 'getservbyname', 'getservbyport', 'getsockname', 'getsockopt', 'htonl', 'htons', 'inet_addr', 'inet_ntoa', \
            'ioctlsocket', 'listen', 'ntohl', 'ntohs', 'recv', 'recvfrom', 'select', 'send', 'sendto', 'setsockopt', 'shutdown', 'socket']
WinINet = [ 'NET', 'CreateMD5SSOHash', 'DetectAutoProxyUrl', 'DllInstall', 'ForceNexusLookup', 'ForceNexusLookupExW', 'InternetAlgIdToStringA',\
            'InternetAlgIdToStringW', 'InternetAttemptConnect', 'InternetAutodial', 'InternetAutodialCallback', 'InternetAutodialHangup',\
            'InternetCanonicalizeUrlA', 'InternetCanonicalizeUrlW', 'InternetCheckConnectionA', 'InternetCheckConnectionW', \
            'InternetClearAllPerSiteCookieDecisions', 'InternetCloseHandle', 'InternetCombineUrlA', 'InternetCombineUrlW', \
            'InternetConfirmZoneCrossing', 'InternetConfirmZoneCrossingA', 'InternetConfirmZoneCrossingW', 'InternetConnectA',\
            'InternetConnectW', 'InternetCrackUrlA', 'InternetCrackUrlW', 'InternetCreateUrlA', 'InternetCreateUrlW', 'InternetDial',\
            'InternetDialA', 'InternetDialW', 'InternetEnumPerSiteCookieDecisionA', 'InternetEnumPerSiteCookieDecisionW', 'InternetErrorDlg',\
            'InternetFindNextFileA', 'InternetFindNextFileW', 'InternetFortezzaCommand', 'InternetGetCertByURL', 'InternetGetCertByURLA',\
            'InternetGetConnectedState', 'InternetGetConnectedStateEx', 'InternetGetConnectedStateExA', 'InternetGetConnectedStateExW',\
            'InternetGetCookieA', 'InternetGetCookieExA', 'InternetGetCookieExW', 'InternetGetCookieW', 'InternetGetLastResponseInfoA', \
            'InternetGetLastResponseInfoW', 'InternetGetPerSiteCookieDecisionA', 'InternetGetPerSiteCookieDecisionW', 'InternetGoOnline',\
            'InternetGoOnlineA', 'InternetGoOnlineW', 'InternetHangUp', 'InternetInitializeAutoProxyDll', 'InternetLockRequestFile',\
            'InternetOpenA', 'InternetOpenUrlA', 'InternetOpenUrlW', 'InternetOpenW', 'InternetQueryDataAvailable', 'InternetQueryFortezzaStatus',\
            'InternetQueryOptionA', 'InternetQueryOptionW', 'InternetReadFile', 'InternetReadFileExA', 'InternetReadFileExW', \
            'InternetSecurityProtocolToStringA', 'InternetSecurityProtocolToStringW', 'InternetSetCookieA', 'InternetSetCookieExA', \
            'InternetSetCookieExW', 'InternetSetCookieW', 'InternetSetDialState', 'InternetSetDialStateA', 'InternetSetDialStateW',\
            'InternetSetFilePointer', 'InternetSetOptionA', 'InternetSetOptionExA', 'InternetSetOptionExW', 'InternetSetOptionW', \
            'InternetSetPerSiteCookieDecisionA', 'InternetSetPerSiteCookieDecisionW', 'InternetSetStatusCallback', 'InternetSetStatusCallbackA',\
            'InternetSetStatusCallbackW', 'InternetShowSecurityInfoByURL', 'InternetShowSecurityInfoByURLA', 'InternetShowSecurityInfoByURLW', \
            'InternetTimeFromSystemTime', 'InternetTimeFromSystemTimeA', 'InternetTimeFromSystemTimeW', 'InternetTimeToSystemTime',\
            'InternetTimeToSystemTimeA', 'InternetTimeToSystemTimeW', 'InternetUnlockRequestFile', 'InternetWriteFile', 'InternetWriteFileExA',\
            'InternetWriteFileExW', 'IsHostInProxyBypassList', 'ParseX509EncodedCertificateForListBoxEntry', 'PrivacyGetZonePreferenceW', \
            'PrivacySetZonePreferenceW', 'ResumeSuspendedDownload', 'ShowCertificate', 'ShowClientAuthCerts', 'ShowSecurityInfo', \
            'ShowX509EncodedCertificate','UrlZonesDetach', '_GetFileExtensionFromUrl'] 
cache = [ 'NET','CommitUrlCacheEntryA', 'CommitUrlCacheEntryW', 'CreateUrlCacheContainerA', 'CreateUrlCacheContainerW', 'CreateUrlCacheEntryA',\
          'CreateUrlCacheEntryW', 'CreateUrlCacheGroup', 'DeleteIE3Cache', 'DeleteUrlCacheContainerA', 'DeleteUrlCacheContainerW', \
          'DeleteUrlCacheEntry', 'DeleteUrlCacheEntryA', 'DeleteUrlCacheEntryW', 'DeleteUrlCacheGroup', 'FindCloseUrlCache', 'FindFirstUrlCacheContainerA',\
          'FindFirstUrlCacheContainerW', 'FindFirstUrlCacheEntryA', 'FindFirstUrlCacheEntryExA', 'FindFirstUrlCacheEntryExW', 'FindFirstUrlCacheEntryW', \
          'FindFirstUrlCacheGroup', 'FindNextUrlCacheContainerA', 'FindNextUrlCacheContainerW', 'FindNextUrlCacheEntryA', 'FindNextUrlCacheEntryExA',\
          'FindNextUrlCacheEntryExW', 'FindNextUrlCacheEntryW', 'FindNextUrlCacheGroup', 'FreeUrlCacheSpaceA', 'FreeUrlCacheSpaceW', 'GetUrlCacheConfigInfoA', \
          'GetUrlCacheConfigInfoW', 'GetUrlCacheEntryInfoA', 'GetUrlCacheEntryInfoExA', 'GetUrlCacheEntryInfoExW', 'GetUrlCacheEntryInfoW', \
          'GetUrlCacheGroupAttributeA', 'GetUrlCacheGroupAttributeW', 'GetUrlCacheHeaderData', 'IncrementUrlCacheHeaderData', 'IsUrlCacheEntryExpiredA',\
          'IsUrlCacheEntryExpiredW', 'LoadUrlCacheContent', 'ReadUrlCacheEntryStream', 'RegisterUrlCacheNotification', 'RetrieveUrlCacheEntryFileA', \
          'RetrieveUrlCacheEntryFileW', 'RetrieveUrlCacheEntryStreamA', 'RetrieveUrlCacheEntryStreamW', 'RunOnceUrlCache', 'SetUrlCacheConfigInfoA',\
          'SetUrlCacheConfigInfoW', 'SetUrlCacheEntryGroup', 'SetUrlCacheEntryGroupA', 'SetUrlCacheEntryGroupW', 'SetUrlCacheEntryInfoA', 'SetUrlCacheEntryInfoW',\
          'SetUrlCacheGroupAttributeA', 'SetUrlCacheGroupAttributeW', 'SetUrlCacheHeaderData', 'UnlockUrlCacheEntryFile', 'UnlockUrlCacheEntryFileA', \
          'UnlockUrlCacheEntryFileW', 'UnlockUrlCacheEntryStream', 'UpdateUrlCacheContentPath']
ftp = [ 'FTP','FtpCommandA' ,'FtpCommandW' ,'FtpCreateDirectoryA' ,'FtpCreateDirectoryW' ,'FtpDeleteFileA' ,'FtpDeleteFileW' ,'FtpFindFirstFileA' ,\
        'FtpFindFirstFileW' ,'FtpGetCurrentDirectoryA' ,'FtpGetCurrentDirectoryW' ,'FtpGetFileA' ,'FtpGetFileEx' ,'FtpGetFileSize' ,'FtpGetFileW' ,\
        'FtpOpenFileA' ,'FtpOpenFileW' ,'FtpPutFileA' ,'FtpPutFileEx' ,'FtpPutFileW' ,'FtpRemoveDirectoryA' ,'FtpRemoveDirectoryW' ,'FtpRenameFileA' ,\
        'FtpRenameFileW' ,'FtpSetCurrentDirectoryA' ,'FtpSetCurrentDirectoryW']
gopher = [ 'GPR', 'GopherCreateLocatorA', 'GopherCreateLocatorW', 'GopherFindFirstFileA', 'GopherFindFirstFileW', 'GopherGetAttributeA', \
           'GopherGetAttributeW', 'GopherGetLocatorTypeA', 'GopherGetLocatorTypeW', 'GopherOpenFileA', 'GopherOpenFileW'] 
url = ['URL', 'UrlApplySchemeA' ,'UrlApplySchemeW' ,'UrlCanonicalizeA' ,'UrlCanonicalizeW' ,'UrlCombineA' ,'UrlCombineW' ,'UrlCompareA' ,\
       'UrlCompareW' ,'UrlCreateFromPathA' ,'UrlCreateFromPathW' ,'UrlEscapeA' ,'UrlEscapeW' ,'UrlGetLocationA' ,'UrlGetLocationW' ,'UrlGetPartA'\
       ,'UrlGetPartW' ,'UrlHashA' ,'UrlHashW' ,'UrlIsA' ,'UrlIsNoHistoryA' ,'UrlIsNoHistoryW' ,'UrlIsOpaqueA' ,'UrlIsOpaqueW' ,'UrlIsW' ,'UrlUnescapeA' ,'UrlUnescapeW']
dir = ['DIR','CreateDirectoryA', 'CreateDirectoryExA', 'CreateDirectoryExW', 'CreateDirectoryW', 'GetCurrentDirectoryA', 'GetCurrentDirectoryW',\
       'GetDllDirectoryA', 'GetDllDirectoryW', 'GetSystemDirectoryA', 'GetSystemDirectoryW', 'GetSystemWindowsDirectoryA', 'GetSystemWindowsDirectoryW',\
       'GetSystemWow64DirectoryA', 'GetSystemWow64DirectoryW', 'GetVDMCurrentDirectories', 'GetWindowsDirectoryA', 'GetWindowsDirectoryW', \
       'ReadDirectoryChangesW', 'RemoveDirectoryA', 'RemoveDirectoryW', 'SetCurrentDirectoryA', 'SetCurrentDirectoryW', 'SetDllDirectoryA',\
       'SetDllDirectoryW', 'SetVDMCurrentDirectories', 'SHCreateDirectory', 'SHCreateDirectoryExA', 'SHCreateDirectoryExW']
mutex = ['MTX','CreateMutexA', 'CreateMutexW', 'OpenMutexA', 'OpenMutexW', 'ReleaseMutex']
pipe = [ 'PIP', 'CallNamedPipeA', 'CallNamedPipeW', 'ConnectNamedPipe', 'CreateNamedPipeA', 'CreateNamedPipeW', 'CreatePipe', 'DisconnectNamedPipe',\
         'GetNamedPipeHandleStateA', 'GetNamedPipeHandleStateW', 'GetNamedPipeInfo', 'PeekNamedPipe', 'SetNamedPipeHandleState', 'TransactNamedPipe',\
         'WaitNamedPipeA', 'WaitNamedPipeW']
http = [ 'HTP', 'HttpAddRequestHeadersA', 'HttpAddRequestHeadersW', 'HttpCheckDavCompliance', 'HttpEndRequestA', 'HttpEndRequestW',\
         'HttpOpenRequestA', 'HttpOpenRequestW', 'HttpQueryInfoA', 'HttpQueryInfoW', 'HttpSendRequestA', 'HttpSendRequestExA', \
         'HttpSendRequestExW', 'HttpSendRequestW' ] 
enum = [ 'ENP', 'CreateToolhelp32Snapshot', 'Process32First' ,'Process32FirstW' ,'Process32Next' ,'Process32NextW']
hash = ['HAS', 'CryptCreateHash' ,'CryptDestroyHash' ,'CryptDuplicateHash' ,'CryptGetHashParam' ,'CryptHashData' ,'CryptHashSessionKey' ,\
        'CryptSetHashParam' ,'CryptSignHashA' ,'CryptSignHashW', 'FreeEncryptionCertificateHashList']
crypt = ['CRY', 'CryptAcquireContextA' ,'CryptAcquireContextW' ,'CryptContextAddRef' ,'CryptDecrypt' ,'CryptDeriveKey' ,'CryptDestroyKey' ,\
         'CryptDuplicateKey' ,'CryptEncrypt' ,'CryptEnumProviderTypesA' ,'CryptEnumProviderTypesW' ,'CryptEnumProvidersA' ,'CryptEnumProvidersW'\
         ,'CryptExportKey' ,'CryptGenKey' ,'CryptGenRandom' ,'CryptGetDefaultProviderA' ,'CryptGetDefaultProviderW' ,'CryptGetKeyParam' ,\
         'CryptGetProvParam' ,'CryptGetUserKey' ,'CryptImportKey' ,'CryptReleaseContext' ,'CryptSetKeyParam' ,'CryptSetProvParam' ,\
         'CryptSetProviderA' ,'CryptSetProviderExA' ,'CryptSetProviderExW' ,'CryptSetProviderW' ,'CryptVerifySignatureA' ,'CryptVerifySignatureW' ,\
         'DecryptFileA' ,'DecryptFileW', 'EncryptFileA' ,'EncryptFileW' ,'EncryptedFileKeyInfo' ,'EncryptionDisable', 'WriteEncryptedFileRaw', \
         'OpenEncryptedFileRawA' ,'OpenEncryptedFileRawW', 'DuplicateEncryptionInfoFile', 'SetUserFileEncryptionKey', 'ReadEncryptedFileRaw', \
         'RemoveUsersFromEncryptedFile', 'FileEncryptionStatusA', 'FileEncryptionStatusW', 'FreeEncryptedFileKeyInfo', 'CloseEncryptedFileRaw', \
         'AddUsersToEncryptedFile', 'QueryRecoveryAgentsOnEncryptedFile', 'QueryUsersOnEncryptedFile', 'ChainWlxLogoffEvent' ,'CryptAcquireContextU' ,\
         'CryptBinaryToStringA' ,'CryptBinaryToStringW' ,'CryptCloseAsyncHandle' ,'CryptCreateAsyncHandle' ,'CryptDecodeMessage' ,'CryptDecodeObject' ,\
         'CryptDecodeObjectEx' ,'CryptDecryptAndVerifyMessageSignature' ,'CryptDecryptMessage' ,'CryptEncodeObject' ,'CryptEncodeObjectEx' ,\
         'CryptEncryptMessage' ,'CryptEnumKeyIdentifierProperties' ,'CryptEnumOIDFunction' ,'CryptEnumOIDInfo' ,'CryptEnumProvidersU' ,'CryptExportPKCS8' ,\
         'CryptExportPublicKeyInfo' ,'CryptExportPublicKeyInfoEx' ,'CryptFindLocalizedName' ,'CryptFindOIDInfo' ,'CryptFormatObject' ,\
         'CryptFreeOIDFunctionAddress' ,'CryptGetAsyncParam' ,'CryptGetDefaultOIDDllList' ,'CryptGetDefaultOIDFunctionAddress' ,\
         'CryptGetKeyIdentifierProperty' ,'CryptGetMessageCertificates' ,'CryptGetMessageSignerCount' ,'CryptGetOIDFunctionAddress' ,\
         'CryptGetOIDFunctionValue' ,'CryptHashCertificate' ,'CryptHashMessage' ,'CryptHashPublicKeyInfo' ,'CryptHashToBeSigned' ,\
         'CryptImportPKCS8' ,'CryptImportPublicKeyInfo' ,'CryptImportPublicKeyInfoEx' ,'CryptInitOIDFunctionSet' ,'CryptInstallDefaultContext' ,\
         'CryptInstallOIDFunctionAddress' ,'CryptLoadSip' ,'CryptMemAlloc' ,'CryptMemFree' ,'CryptMemRealloc' ,'CryptMsgCalculateEncodedLength' ,\
         'CryptMsgClose' ,'CryptMsgControl' ,'CryptMsgCountersign' ,'CryptMsgCountersignEncoded' ,'CryptMsgDuplicate' ,'CryptMsgEncodeAndSignCTL' ,\
         'CryptMsgGetAndVerifySigner' ,'CryptMsgGetParam' ,'CryptMsgOpenToDecode' ,'CryptMsgOpenToEncode' ,'CryptMsgSignCTL' ,'CryptMsgUpdate' ,\
         'CryptMsgVerifyCountersignatureEncoded' ,'CryptMsgVerifyCountersignatureEncodedEx' ,'CryptProtectData' ,'CryptQueryObject' ,\
         'CryptRegisterDefaultOIDFunction' ,'CryptRegisterOIDFunction' ,'CryptRegisterOIDInfo' ,'CryptSIPAddProvider' ,\
         'CryptSIPCreateIndirectData' ,'CryptSIPGetSignedDataMsg' ,'CryptSIPLoad' ,'CryptSIPPutSignedDataMsg' ,'CryptSIPRemoveProvider' ,\
         'CryptSIPRemoveSignedDataMsg' ,'CryptSIPRetrieveSubjectGuid' ,'CryptSIPRetrieveSubjectGuidForCatalogFile' ,'CryptSIPVerifyIndirectData' ,\
         'CryptSetAsyncParam' ,'CryptSetKeyIdentifierProperty' ,'CryptSetOIDFunctionValue' ,'CryptSetProviderU' ,'CryptSignAndEncodeCertificate' ,\
         'CryptSignAndEncryptMessage' ,'CryptSignCertificate' ,'CryptSignHashU' ,'CryptSignMessage' ,'CryptSignMessageWithKey' ,\
         'CryptStringToBinaryA' ,'CryptStringToBinaryW' ,'CryptUninstallDefaultContext' ,'CryptUnprotectData' ,'CryptUnregisterDefaultOIDFunction' ,\
         'CryptUnregisterOIDFunction' ,'CryptUnregisterOIDInfo' ,'CryptVerifyCertificateSignature' ,'CryptVerifyCertificateSignatureEx' ,\
         'CryptVerifyDetachedMessageHash' ,'CryptVerifyDetachedMessageSignature' ,'CryptVerifyMessageHash' ,'CryptVerifyMessageSignature' ,\
         'CryptVerifyMessageSignatureWithKey' ,'CryptVerifySignatureU' ,'I_CertProtectFunction' ,'I_CertSrvProtectFunction' ,'I_CertSyncStore' ,\
         'I_CertUpdateStore' ,'I_CryptAddRefLruEntry' ,'I_CryptAddSmartCardCertToStore' ,'I_CryptAllocTls' ,'I_CryptCreateLruCache' ,\
         'I_CryptCreateLruEntry' ,'I_CryptDetachTls' ,'I_CryptDisableLruOfEntries' ,'I_CryptEnableLruOfEntries' ,'I_CryptEnumMatchingLruEntries' ,\
         'I_CryptFindLruEntry' ,'I_CryptFindLruEntryData' ,'I_CryptFindSmartCardCertInStore' ,'I_CryptFlushLruCache' ,'I_CryptFreeLruCache' ,\
         'I_CryptFreeTls' ,'I_CryptGetAsn1Decoder' ,'I_CryptGetAsn1Encoder' ,'I_CryptGetDefaultCryptProv' ,'I_CryptGetDefaultCryptProvForEncrypt' ,\
         'I_CryptGetFileVersion' ,'I_CryptGetLruEntryData' ,'I_CryptGetLruEntryIdentifier' ,'I_CryptGetOssGlobal' ,'I_CryptGetTls' ,'I_CryptInsertLruEntry' ,\
         'I_CryptInstallAsn1Module' ,'I_CryptInstallOssGlobal' ,'I_CryptReadTrustedPublisherDWORDValueFromRegistry' ,'I_CryptRegisterSmartCardStore' ,\
         'I_CryptReleaseLruEntry' ,'I_CryptRemoveLruEntry' ,'I_CryptSetTls' ,'I_CryptTouchLruEntry' ,'I_CryptUninstallAsn1Module' ,\
         'I_CryptUninstallOssGlobal' ,'I_CryptUnregisterSmartCardStore' ,'I_CryptWalkAllLruCacheEntries']
service = ['SRV', 'ChangeServiceConfig2A' ,'ChangeServiceConfig2W' ,'ChangeServiceConfigA' ,'ChangeServiceConfigW' ,'CloseServiceHandle' ,\
           'ControlService' ,'CreateServiceA' ,'CreateServiceW' ,'DeleteService' ,'EnumDependentServicesA' ,'EnumDependentServicesW' ,\
           'EnumServiceGroupW' ,'EnumServicesStatusA' ,'EnumServicesStatusExA' ,'EnumServicesStatusExW' ,'EnumServicesStatusW' ,\
           'GetServiceDisplayNameA' ,'GetServiceDisplayNameW' ,'GetServiceKeyNameA' ,'GetServiceKeyNameW' ,'I_ScPnPGetServiceName' ,\
           'I_ScSetServiceBitsA' ,'I_ScSetServiceBitsW' ,'LockServiceDatabase' ,'OpenServiceA' ,'OpenServiceW' ,'PrivilegedServiceAuditAlarmA' ,\
           'PrivilegedServiceAuditAlarmW' ,'QueryServiceConfig2A' ,'QueryServiceConfig2W' ,'QueryServiceConfigA' ,'QueryServiceConfigW' ,\
           'QueryServiceLockStatusA' ,'QueryServiceLockStatusW' ,'QueryServiceObjectSecurity' ,'QueryServiceStatus' ,'QueryServiceStatusEx' ,\
           'RegisterServiceCtrlHandlerA' ,'RegisterServiceCtrlHandlerExA' ,'RegisterServiceCtrlHandlerExW' ,'RegisterServiceCtrlHandlerW' ,\
           'SetServiceBits' ,'SetServiceObjectSecurity' ,'SetServiceStatus' ,'StartServiceA' ,'StartServiceCtrlDispatcherA' ,'StartServiceCtrlDispatcherW' ,\
           'StartServiceW' ,'UnlockServiceDatabase' ,'WdmWmiServiceMain']
file = ['FIL', 'CompareFileTime' ,'CopyFileA' ,'CopyFileExA' ,'CopyFileExW' ,'CopyFileW' ,'CopyLZFile' ,'CreateFileA' ,'CreateFileMappingA' ,\
        'CreateFileMappingW' ,'CreateFileW' ,'DeleteFileA' ,'DeleteFileW' ,'DosDateTimeToFileTime' ,'FileTimeToDosDateTime' ,\
        'FileTimeToLocalFileTime' ,'FileTimeToLocalFileTime' ,'FileTimeToSystemTime' ,'FlushFileBuffers' ,'FlushViewOfFile' ,\
        'GetCPFileNameFromRegistry' ,'GetCompressedFileSizeA' ,'GetCompressedFileSizeW' ,'GetFileAttributesA' ,'GetFileAttributesExA' ,\
        'GetFileAttributesExW' ,'GetFileAttributesW' ,'GetFileInformationByHandle' ,'GetFileSize' ,'GetFileSizeEx' ,'GetFileTime' ,\
        'GetFileType' ,'GetSystemTimeAsFileTime' ,'GetTempFileNameA' ,'GetTempFileNameW' ,'LZCloseFile' ,'LZCreateFileW' ,'LZOpenFileA',\
        'LZOpenFileW' ,'LocalFileTimeToFileTime' ,'LocalFileTimeToFileTime' ,'LockFile' ,'LockFileEx' ,'MapViewOfFile' ,'MapViewOfFileEx' ,\
        'MoveFileA' ,'MoveFileExA' ,'MoveFileExW' ,'MoveFileW' ,'MoveFileWithProgressA' ,'MoveFileWithProgressW' ,'OpenDataFile' ,'OpenFile' ,\
        'OpenFileMappingA' ,'OpenFileMappingW' ,'OpenProfileUserMapping' ,'PrivCopyFileExW' ,'PrivMoveFileIdentityW' ,'ReadFile' ,'ReadFileEx' ,\
        'ReplaceFile' ,'ReplaceFileA' ,'ReplaceFileW' ,'SetEndOfFile' ,'SetFileAttributesA' ,'SetFileAttributesW' ,'SetFilePointer' ,\
        'SetFilePointerEx' ,'SetFileShortNameA' ,'SetFileShortNameW' ,'SetFileTime' ,'SetFileValidData' ,'SystemTimeToFileTime' ,\
        'UnlockFile' ,'UnlockFileEx' ,'UnmapViewOfFile' ,'WriteFile' ,'WriteFileEx' ,'WriteFileGather' ,'GetFileSecurityA' ,\
        'GetFileSecurityW' ,'SetFileSecurityA' ,'SetFileSecurityW', 'CreateFileU']
os_info = [ 'OSI', 'GetComputerNameA' ,'GetComputerNameExA' ,'GetComputerNameExW' ,'GetComputerNameW' ,'GetDiskFreeSpaceA' ,\
            'GetDiskFreeSpaceExA' ,'GetDiskFreeSpaceExW' ,'GetDiskFreeSpaceW' ,'GetDriveTypeA' ,'GetDriveTypeW', 'GetVersion' ,\
            'GetVersionExA' ,'GetVersionExW', 'GetSystemInfo', 'GetSystemMetrics', 'CheckTokenMembership']
cert = ['CER','CertAddCRLContextToStore' ,'CertAddCRLLinkToStore' ,'CertAddCTLContextToStore' ,'CertAddCTLLinkToStore' ,\
        'CertAddCertificateContextToStore' ,'CertAddCertificateLinkToStore' ,'CertAddEncodedCRLToStore' ,'CertAddEncodedCertificateToStore' ,\
        'CertAddEncodedCertificateToSystemStoreA' ,'CertAddEncodedCertificateToSystemStoreW' ,'CertAddEnhancedKeyUsageIdentifier' ,\
        'CertAddSerializedElementToStore' ,'CertAddStoreToCollection' ,'CertAlgIdToOID' ,'CertCloseStore' ,'CertCompareCertificate' ,\
        'CertCompareCertificateName' ,'CertCompareIntegerBlob' ,'CertComparePublicKeyInfo' ,'CertControlStore' ,'CertCreateCTLContext' ,\
        'CertCreateCTLEntryFromCertificateContextProperties' ,'CertCreateCertificateChainEngine' ,'CertCreateCertificateContext' ,'CertCreateContext',\
        'CertCreateSelfSignCertificate' ,'CertDeleteCTLFromStore' ,'CertDeleteCertificateFromStore' ,'CertDuplicateCTLContext' ,\
        'CertDuplicateCertificateChain' ,'CertDuplicateCertificateContext' ,'CertDuplicateStore' ,'CertEnumCRLContextProperties' ,\
        'CertEnumCRLsInStore' ,'CertEnumCTLContextProperties' ,'CertEnumCTLsInStore' ,'CertEnumCertificateContextProperties' ,\
        'CertEnumCertificatesInStore' ,'CertEnumPhysicalStore' ,'CertEnumSubjectInSortedCTL' ,'CertEnumSystemStore' ,\
        'CertEnumSystemStoreLocation' ,'CertFindAttribute' ,'CertFindCRLInStore' ,'CertFindCertificateInCRL' ,'CertFindCertificateInStore',\
        'CertFindChainInStore' ,'CertFindExtension' ,'CertFindRDNAttr' ,'CertFindSubjectInCTL' ,'CertFindSubjectInSortedCTL' ,\
        'CertFreeCRLContext' ,'CertFreeCertificateChain' ,'CertFreeCertificateChainEngine' ,'CertFreeCertificateContext' ,'CertGetCRLContextProperty' ,\
        'CertGetCRLFromStore' ,'CertGetCTLContextProperty' ,'CertGetCertificateChain' ,'CertGetCertificateContextProperty' ,'CertGetEnhancedKeyUsage' ,\
        'CertGetIssuerCertificateFromStore' ,'CertGetNameStringA' ,'CertGetNameStringW' ,'CertGetPublicKeyLength' ,'CertGetStoreProperty' ,\
        'CertGetSubjectCertificateFromStore' ,'CertGetValidUsages' ,'CertIsRDNAttrsInCertificateName' ,'CertIsValidCRLForCertificate' ,\
        'CertNameToStrA' ,'CertNameToStrW' ,'CertOIDToAlgId' ,'CertOpenStore' ,'CertOpenSystemStoreA' ,'CertOpenSystemStoreW' ,'CertRDNValueToStrA',\
        'CertRDNValueToStrW' ,'CertRegisterPhysicalStore' ,'CertRegisterSystemStore' ,'CertRemoveEnhancedKeyUsageIdentifier' ,\
        'CertRemoveStoreFromCollection' ,'CertResyncCertificateChainEngine' ,'CertSaveStore' ,'CertSerializeCRLStoreElement' ,\
        'CertSerializeCertificateStoreElement' ,'CertSetCRLContextProperty' ,'CertSetCertificateContextPropertiesFromCTLEntry' ,\
        'CertSetCertificateContextProperty' ,'CertSetEnhancedKeyUsage' ,'CertSetStoreProperty' ,'CertStrToNameA' ,'CertStrToNameW' ,\
        'CertUnregisterPhysicalStore' ,'CertUnregisterSystemStore' ,'CertVerifyCRLRevocation' ,'CertVerifyCRLTimeValidity' ,\
        'CertVerifyCTLUsage' ,'CertVerifyCertificateChainPolicy' ,'CertVerifyCertificateChainPolicy' ,'CertVerifyRevocation' ,\
        'CertVerifySubjectCertificateContext','CertVerifyTimeValidity' ,'CertVerifyValidityNesting' ,'CloseCertPerformanceData' ,\
        'CollectCertPerformanceData' ,'CryptAcquireCertificatePrivateKey' ,'CryptFindCertificateKeyProvInfo' ,'CryptGetMessageCertificates' ,\
        'CryptHashCertificate' ,'CryptSignAndEncodeCertificate' ,'CryptSignCertificate' ,'CryptVerifyCertificateSignature' ,\
        'CryptVerifyCertificateSignatureEx' ,'I_CertProtectFunction' ,'I_CertSrvProtectFunction' ,'I_CertSyncStore' ,'I_CertUpdateStore' ,\
        'I_CryptAddSmartCardCertToStore' ,'I_CryptFindSmartCardCertInStore' ,'OpenCertPerformanceData' ,'PFXExportCertStore' ,'PFXExportCertStoreEx' ,'PFXImportCertStore'] 
file_s = ['SRC', 'FindFirstFileW', 'FindNextFileW', 'FindClose']
modify = ['MOD', 'WriteProcessMemory', 'ReadProcessMemory'] 
virtual = ['VIR', 'VirtualAlloc' ,'VirtualAllocEx' ,'VirtualBufferExceptionHandler' ,'VirtualFree' ,'VirtualFreeEx' ,'VirtualLock' ,
           'VirtualProtect' ,'VirtualProtectEx' ,'VirtualQuery' ,'VirtualQueryEx' ,'VirtualUnlock']
critical_section = [ 'CRT', 'DeleteCriticalSection' ,'EnterCriticalSection' ,'InitializeCriticalSection' ,
                     'InitializeCriticalSectionAndSpinCount' ,'LeaveCriticalSection' ,'SetCriticalSectionSpinCount' ,'TryEnterCriticalSection']

api_matrix = [ reg, winsock, WinINet, cache, ftp, gopher, dir, mutex, pipe, http, enum, hash, crypt, service, file, cert, os_info, file_s, modify, virtual, critical_section,\
               launcher, process_injection, DLL_injection, direct_injection, process_replacement, hook_injection, APC_injection, APC_injection, APC_userspace, APC_kernelspace,\
               windows_networking, anti_debugging]

def getAllStrings():
    #We want to use a specific string configuration
    stringList = idautils.Strings(default_setup = False)
    # Lets use C et UNICODE string
    stringList.setup(strtypes=Strings.STR_C | Strings.STR_UNICODE, ignore_instructions = True, display_only_existing_strings = True)

    parsedStringList = []
    #Get all strings
    for string in stringList:                
        if string:
            parsedStringList.append((str(string), string.ea))

    return parsedStringList
#-----------------------------------------------------------------------
# Configuration agent
# The two next function are needed so the configuration manager will be
# able to provide configuration for this module.
# configurationNeed is called first. If no config for this 
# utility exists, user is prompted for utility configuration. If
# configuration exists, previous configs will be used. In all cases,
# configuration step is ended when configurationProvision
# is called with a configuration list as arg.
#-----------------------------------------------------------------------
'''
IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = ""
def configurationNeed():
    return ["IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH", "IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE"]
            
def configurationProvision(utilityConfig=[]):
    global IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH
    global IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE
    IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = utilityConfig[0]
    IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE = utilityConfig[1]
'''

def sanitize_name(name):
    name = name.strip()
    if name.endswith("A"):
        name = name[:-1]
    elif name.endswith("W"):
        name = name[:-1]
    while name.startswith("_"):
        name = name[1:]
    return name

def getFuncDeclaration(func):
    if func != idc.BADADDR:
        ft = idc.GetType(func)
        if ft:
            return ft
        else:
            #print "-Type info not available!"
            return ""
    else:
        return ""    
  
def prefix_lines(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [RESULT_PREFIX+line for line in lines.splitlines()]
    return "\n".join(result)
    
def prefix_lines_oa(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [RESULT_PREFIX_OA+line for line in lines.splitlines()]
    return "\n".join(result)
    
def remove_prefixed_lines(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [line for line in lines.splitlines() \
              if not line.startswith(RESULT_PREFIX)]
    return "\n".join(result)
    
def remove_prefixed_lines_oa(lines):
    if not lines or len(lines) == 0:
        return lines
    result = [line for line in lines.splitlines() \
              if not line.startswith(RESULT_PREFIX_OA)]
    return "\n".join(result)

  
def get_all_function_eas():
    result = []
    for fnum in xrange(idaapi.get_func_qty()):
        func = idaapi.getn_func(fnum)
        f_ea = func.startEA
        if f_ea != idaapi.BADADDR:
            result.append(f_ea)
    return result
    
def get_import_segments():
    result = []
    for seg_ea in idautils.Segments():
        seg = idaapi.getseg(seg_ea)
        if seg.type == idaapi.SEG_XTRN:
            result.append( (seg.startEA, seg.endEA) )
    return result
    
def u2signed(num):
    if num < 0x80000000:
        return num
    else:
        return num - 0x100000000
    
class FunctionX(object):
    def __init__(self, start_ea):
        self.start_ea = start_ea
        
    def get_end_address(self):
        return "0x%x" % idc.GetFunctionAttr(self.start_ea, idc.FUNCATTR_END)
    def get_number_of_instructions(self):
        icount = 0
        for i in idautils.FuncItems(self.start_ea): icount = icount + 1
        return icount
    def get_size_of_argument_bytes(self):
        try:
            fframe = idc.GetFrame(self.start_ea)
            if fframe is not None:
                fret = idc.GetMemberOffset(fframe, " r")
            if fret is not -1:
                firstArg = fret + 4
            args = idc.GetStrucSize(fframe) - firstArg
            return args
        except:
            pass     
            return
    def get_number_of_arguments(self):
        try:
            args = self.get_size_of_argument_bytes()
            out = args/4
        except:
            out = 0
            pass
        return out
    def get_size_of_local_variables(self):
        try:
            locals = idc.GetFunctionAttr(self.start_ea, idc.FUNCATTR_FRSIZE)
            return locals
        except:
            pass
            return
    def get_function_flags(self):
        try:
            return idc.GetFunctionFlags(self.start_ea)
        except:
            pass
            return
    def get_name(self):
        return idaapi.get_func_name(self.start_ea)
    def get_demangled_name(self):
        mangled = idaapi.get_func_name(self.start_ea)
        demangled = idc.Demangle(mangled, idc.GetLongPrm(idc.INF_SHORT_DN))
        if  demangled == None:
             return "" #mangled
        else:
            #print " --Demangling..."
            return demangled
    def get_instruction_eas(self):
        result = []
        for chunk in self.__get_chunks():
            ins_ea = idc.FindCode(chunk.startEA-1, DIRECTION_FORW)
            while ins_ea <= chunk.endEA and ins_ea != idc.BADADDR:
                result.append(ins_ea)
                ins_ea = idc.FindCode(ins_ea, DIRECTION_FORW)
        return result    
    def get_instructions(self):
        return [Instruction(iea, ) for iea in self.get_instruction_eas()]
    def get_code_refs_from(self):
        result = []
        for iea in self.get_instruction_eas():
            inst = Instruction(iea, )
            result += inst.get_code_refs_from()
        return list(set(result))
    def get_data_refs_from(self):
        result = []
        for iea in self.get_instruction_eas():
            inst = Instruction(iea, )
            result += inst.get_data_refs_from()
        return list(set(result))
    def set_comment(self, comment, repeatable = False):
        func = self.__get_func_t()
        idaapi.set_func_cmt(func, comment, repeatable)
    def get_comment(self, repeatable = False):
        func = self.__get_func_t()
        return idaapi.get_func_cmt(func, repeatable)
    def __get_chunks(self):
        func = self.__get_func_t()
        result = [func]
        ft_iter = idaapi.func_tail_iterator_t(func, self.start_ea)
        if ft_iter.first():
            result.append(ft_iter.chunk())
        while ft_iter.next():
            result.append(ft_iter.chunk())
        return result
    def __get_func_t(self):
        result = idaapi.get_func(self.start_ea)
        if not result:
            raise RuntimeError, \
                  "Cannot retrieve function information @ address %s" % \
                  self.start_ea
        return result
    def __repr__(self):
        return self.get_name()

class Instruction(object):
    def __init__(self, inst_ea):
        self.iea = inst_ea
        
    def get_code_refs_from(self):
        return idautils.CodeRefsFrom(self.iea, False)
    def get_data_refs_from(self):
        return idautils.DataRefsFrom(self.iea, False)
    def get_operands(self):
        result = []
        ins = self.__get_insn_t()
        if not ins:
            print >> sys.stderr, \
                  "Cannot retrieve operand information @ address %s" % \
                  hex(self.iea)
            return result
        for i in xrange(6):
            oper = ins[i]
            if oper.type != idaapi.o_void:
                result.append( Operand(i, self.iea, ) )
        return result
    def __get_insn_t(self):
        ins = idautils.DecodeInstruction(self.iea)
        if ins:
	    return ins
        return None
    def __repr__(self):        
        return idaapi.tag_remove( idaapi.generate_disasm_line(self.iea) )
        
class Operand(object):
    def __init__(self, oper_num, inst_ea):
        self.onum = oper_num
        self.iea = inst_ea
        
        
    def get_immediate(self):
        oper = self.__get_op_t()
        if not oper or oper.type != idaapi.o_imm:
            return None        
        value = oper.value
        if value in idautils.DataRefsFrom(self.iea):
            return None
        return value
    def get_string(self):
        mem = self.get_memory_address()
        if not mem:
            return None
        flags = idaapi.getFlags(mem)
        if not idaapi.isASCII(flags):
            return None
        tinfo = idaapi.opinfo_t()
        idaapi.get_opinfo(mem, 0, flags, tinfo)
        slen = idaapi.get_max_ascii_length(mem, tinfo.strtype)
        return idaapi.get_ascii_contents2(mem, slen, tinfo.strtype)
        
    def get_symbolicName(self):
        #Check if constant:
        value = self.get_immediate()
        if value is None:
            return None
        
        #Now lets try to see if it uses symbolic names
        operandCounter = 0
        operandText = GetOpnd(self.iea, self.onum)
        if (operandText != ""):
                if operandText[-1] == "h":
                    operandTextTemp = operandText[:-1]
                try:
                    result = int(operandTextTemp, 16)
                    return None
                except:
                    return operandText
        #End of symbolic names chases...
        
    def get_memory_address(self):
        oper = self.__get_op_t()
        if oper.type == idaapi.o_mem:
            return oper.addr
        elif oper.type == idaapi.o_imm and self.iea != idc.BADADDR:
            ref = oper.value
            if ref in idautils.DataRefsFrom(self.iea):
                return ref
        elif (oper.type == idaapi.o_displ or oper.type == idaapi.o_phrase) \
                 and not self.is_stackref():
            return oper.addr
        return None
    def is_stackref(self):
        oper = self.__get_op_t()
        if not oper.type in [idaapi.o_displ, idaapi.o_phrase]:
            return False
        offset = u2signed(oper.addr)
        return ( idaapi.get_stkvar(oper, offset) != None )
    def __get_op_t(self):
        ins = idautils.DecodeInstruction(self.iea)
        if not ins:
            return None
        else:
            return ins.Operands[self.onum]
             

 
#-----------------------------------------------------------------------
# IdaProExtractor 
# This class implements IDA Pro extraction capabilities. It also uses 
# a UI part (IdaProExtractorUI) in order to get 
# configuration selection from the User
#-----------------------------------------------------------------------
class IdaProTextExtractorScript():
    
    def __init__(self, sList=[], flg=0):
        self._sList = sList #func, eas for selected functions
        self._flg = flg
    #
    # This method is in charge of features extraction coordination from an IdaPro Input source
    # it returns an array containing all source features built with the following format:
    # [(functionName, [("searchTerm", "type") ..]), ..]
    # returns None if it encounters an error
    # Input file singleFile should be built like:
    # (fileName, [func_ea, ..])
    #
    def pluginExtract(self, _flg, _sList):
        if _flg:
            return self.getFunctionFeatures(self.getSelectedFunctionNamesAndEas(_sList))
        else:
            return self.getFunctionFeatures(self.getAllFunctionNamesAndEas())

    #
    # This method is used so plugin user could have the chance
    # to select functions to be extracted in a list of all the functions
    # that are inside a specific 
    #
   
    def getAllFunctionNamesAndEas(self):
        result = []
        allFuncEAS = get_all_function_eas()
        for ea in allFuncEAS:
            result.append((idaapi.get_func_name(ea), ea))
        return result


    def getSelectedFunctionNamesAndEas(self, _sList):
        result = []
        allFuncEAS = _sList
        for ea in allFuncEAS:
            result.append((idaapi.get_func_name(ea), ea))
        return result
 
    #
    # This method aims at returning all extracted features for a 
    # specific range of function. Its returns a features list.
    #
    def getFunctionFeatures(self, funcEaList=None):
        result = []
        if funcEaList:
            for cidx, currFunc in enumerate(funcEaList):
			
                tempFunc = FunctionX(currFunc[1])
                currFuncFeatures = self.extractFeatures(tempFunc)
                result.append(((tempFunc.get_name(),currFunc[1]), currFuncFeatures))
        #Check for strings!
        idbStringList = getAllStrings()
        
        for singleResult in result:
            for searchTerm in singleResult[1]:
                for singleString in idbStringList:
                    if searchTerm[1] in singleString[0]:
                        idbStringList.remove(singleString)
        
        alreadyInList = [] #This is used as a buffer to prevent duplicate strings
        for string in idbStringList:
            #Prevent duplicate strings
            if string[0] not in alreadyInList:
                #hash = hashlib.md5(string).hexdigest()
                result.append((("Orphan String " + str(string[0]), string[1]),[('s', str(string[0]))]))
                alreadyInList.append(string[0])
        #print len(idbStringList)
        #print result[0]        
        return result

    
    def extractFeatures(self, func):
        #print " --Extracting..."
        IDATA_SEGMENTS1 = get_import_segments()
        joint=[]
        name=[]
        demangle=[]
        constants=[]
        strlist=[]
        implist=[]
        impset = set()
        plist=[]
        rtype=[]
        arglist=[]
        numinst=[]
        numargs=[]
        argbytes=[]
        varbytes=[]
        fnflags=[]
        tagmal=[]
        tagapi=[]
        outcall=[]
        calllist=[]
        fname = func.get_name()
        dename = func.get_demangled_name()
        name.append(('n',fname))
        if dename:
            demangle.append(('ne',dename)) #ne: demangled C++ names
        for inst in func.get_instructions():
            for oper in inst.get_operands():
                val = oper.get_immediate()
                if val:
                    constants.append(('c', "%x" % val))
                    #Symbolic name can't be used in const we have to put it in strings
                    symbolicName = oper.get_symbolicName()
                    if symbolicName is not None and len(symbolicName) > 0:
                        try:
                            transition = int(symbolicName)
                        except:
                            strlist.append(('s',"%s" % symbolicName))
                    
                soper = oper.get_string()
                if soper:
                    if len(soper)>0:
                        #soper = replace_all(soper,BlackDict) 
                        strlist.append(('s',"%s" % soper))
                coderefs = inst.get_code_refs_from()
                for ref in coderefs:
                    if len([start for (start, end) in IDATA_SEGMENTS1 \
                            if start <= ref <= end]) > 0:
                        iname = idaapi.get_name(inst.iea, ref)
                        if iname:
                            impset.add( sanitize_name(iname) )
        for item in impset:
            implist.append( ( 'i', sanitize_name(item) ) )
        addr = func.start_ea 
        fend = idc.GetFunctionAttr(addr, idc.FUNCATTR_END)
        tt = getFuncDeclaration(addr)
        plist.append( ( 'p', tt ) ) #p: prototype
        params =  tt[tt.find("(")+1:tt.find(")")]
        lparams = params.split(',')
        sl = [(par.strip()) for par in lparams]
        for arg in sl:
            if arg:
                arglist.append( ( 'a', arg ) ) #a: arguments
        rt = tt[:tt.find(' ')]
        if rt:
            rtype.append( ( 'r', rt ) ) #r: return type    
        numinst.append( ( 'm', str(func.get_number_of_instructions()) ) ) #m: number of instructions
        g=func.get_number_of_arguments()
        if g:
            numargs.append( ( 'g', str(g) ) ) #g: number of arguments
        bbb=func.get_size_of_argument_bytes()
        if bbb:
            argbytes.append( ( 'b', str(bbb) ) ) #b: size of argument bytes
        varbytes.append( ( 'l', str(func.get_size_of_local_variables()) ) ) #l: size of local vars bytes
        fnflags.append( ( 'f', str(func.get_function_flags()) ) ) #f: function flags
        for elmntz in implist:
            if elmntz[1] in WinMalFuncInSight:
                tagmal.append(('d','MAL'))
        ad=0
        for api_row in api_matrix:
            l = api_row[0]
            apis = api_row[1:]
            for api in apis:
                ref_addrs = idautils.CodeRefsTo(idc.LocByName(api),0)
                for ref in ref_addrs:
                    func_name = idc.GetFunctionName(ref)
                    func_addr = idc.LocByName(func_name)
                    ad=addr 
                    if func_addr == ad:
                        tagapi.append(('t',l))
                    
                    
        bb=func.get_code_refs_from()
        outcall.append(('o', str( len(bb) ) )) # o: number of code refs from this function
        items = idautils.FuncItems(func.start_ea)
        for i in items:
            for xref in idautils.XrefsFrom(i, 0):
                if xref.type == idc.fl_CN or xref.type == idc.fl_CF:
                    dstf = idc.Name(xref.to)
                    calllist.append(('k', dstf )) # o: number of code refs from this function
                    demangled = idc.Demangle(dstf, idc.GetLongPrm(idc.INF_SHORT_DN))
                    if  demangled != None:
                        calllist.append(('ke', demangled )) #ke: demangled C++ call
        joint = name+demangle+constants+strlist+implist+rtype+arglist+numinst+numargs+argbytes+varbytes+fnflags+tagmal+tagapi+outcall+calllist
        z=fend - ad
        if z:
            joint.append(('z', str(z))) #function size (bytes)
        joint.append(('cx',str(len(constants))))
        joint.append(('sx',str(len(strlist))))
        joint.append(('mx',str(numinst[0][1])))
        if g:
            joint.append(('gx',str(numargs[0][1])))
        joint.append(('ix',str(len(implist))))
        joint.append(('dx',str(len(tagmal))))
        joint.append(('tx',str(len(tagapi))))
        joint.append(('ox',str(len(outcall))))
        joint.append(('kx',str(len(calllist))))
        return joint

class AsmFunctionList(Choose2):
    def __init__(self, title, flags=0):
        Choose2.__init__(self,
                         title,
                         [ ["Address", 10 | Choose2.CHCOL_HEX],
                           ["Function Name", 15 | Choose2.CHCOL_PLAIN] ],
                         embedded=True, width=25, height=5, flags=flags)
        self.n = 0
        self.selcount = 0
        self.icon = 41
        self.PopulateItems()
    def PopulateItems(self):
        global funcListG
        funcListG = [ [hex(x), GetFunctionName(x), x] for x in idautils.Functions() ] #x=ea
        self.items = funcListG
    def OnClose(self):
        pass
    def OnRefresh(self, n):
        self.Refresh()
        return self.n
    def OnRefreshed(self):
        return self.n
    def OnSelectLine(self, n):
        self.selcount += 1
    def OnGetLine(self, n):
        return self.items[n]
    def OnGetSize(self):
        n = len(self.items)
        return n
		
class ReSrcForm(Form):
    def __init__(self):
        global funcListG
        self.EChooser = AsmFunctionList("asm", flags=Choose2.CH_MULTI)
        Form.__init__(self,
r"""BUTTON YES* NONE
BUTTON CANCEL Exit
BinSourcerer
{FormChangeCb}
Function Selection:
<Please use [ctrl+click] or [shift+click] for multiple selections.:{cEChooser}>
Search Configuration:
<#Selecting this option will disable the list.#Select all functions:{rSearchAll}>
<Use offline analyzer for malware:{rOfflineMal}>{cSearchCfgGroup}>
Feature Extraction:
<Path:{iPath}>
<File:{iFSave}>
<Feature Extraction:{iBatchM}><API Tagging:{iAPI}>

""", {
            'cSearchCfgGroup': Form.ChkGroupControl(("rSearchAll", "rOfflineMal")),
            'iBatchM': Form.ButtonInput(self.OnFormChange),
            'iPath': Form.DirInput(swidth=30),
	    'iFSave': Form.FileInput(save=True, swidth=30),
            'iAPI': Form.ButtonInput(self.OnFormChange),
            'FormChangeCb': Form.FormChangeCb(self.OnFormChange),
            'cEChooser' : Form.EmbeddedChooserControl(self.EChooser)
        })
        
    def validPath(self, inputFPath):
        if os.access(os.path.dirname(inputFPath), os.W_OK):
            return True
        else:
            return False
        
    def OnFormChange(self, fid):
        if fid == self.iBatchM.id:
            p = self.GetControlValue(self.iPath)
	    f = self.GetControlValue(self.iFSave)
	    if p and f and self.validPath(p+"\\"+f):
                print "Status: BinSourcerer is ready for magic..."
                IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = p + "\\"
                IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE = f
                print "Status: Extracting features. This could take a few minutes..."

                stt = self.GetControlValue(self.rSearchAll)
                if stt==1:
                    x = IdaProTextExtractorScript()
                    results = x.pluginExtract(0, [])
                else:
                    global funcListG
                    selFuncEAS = []
                    selectedFuncsList = self.GetControlValue(self.cEChooser)
                    if len(selectedFuncsList) > 0:
                            for funcIndex in selectedFuncsList:
                                    selFuncEAS.append( funcListG[funcIndex][2] )
                            x = IdaProTextExtractorScript(selFuncEAS, 1)
                            results = x.pluginExtract(1, selFuncEAS)
                    else:
                            idc.Warning("Please select multiple functions from the list.")
                print "Status: Features extraction completed. Saving results..."
                try:
                    f = open(IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH + IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE, "w")
                    if results:
                        for func in results:
                            f.write(str(func) + "\n")
			outputPath = IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH + IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE
                    
                        print "Status: Features file saved in " + os.path.normpath(outputPath)
                except:
                    print "I/O Error! Could not write to the output file!"
                finally:
                    f.close()
                    
            else:
                idc.Warning("Please select a valid path and file name!\n")
       
        elif fid == self.rSearchAll.id:
            stt = self.GetControlValue(self.rSearchAll)
            if stt==1:
                self.EnableField(self.cEChooser, False)
            else:
                self.EnableField(self.cEChooser, True)

	elif fid == self.iAPI.id:
            potentialTags=APITags(0)
            choice=AskYN(1,"Approximately "+ str(potentialTags)+" tags are to be assigned to functions.\nProceed with name changes?")
            if choice==1:
                APITags(1)
                self.EChooser.PopulateItems()
                self.RefreshField(self.cEChooser)
        return 1

def APITags(flag):
    apilist=[]
    if flag==1:
        save=True
    else:
        save=False
    count=0
    for idx, api_row in enumerate(api_matrix):
        l = api_row[0]
        apis = api_row[1:]
        for api in apis:
            ref_addrs = CodeRefsTo(LocByName(api),0)
            for ref in ref_addrs:
                func_addr = LocByName(GetFunctionName(ref))
                fnsAddr = GetFunctionAttr( func_addr, FUNCATTR_START )
                func_name = GetFunctionName(ref)
                if l not in func_name:
                    count +=1 #counter
                    apilist.append( ( 't', l ) ) #t: api tags
                    ss = l + '_' + func_name
                    if save:
                        MakeNameEx(func_addr , ss  , SN_NOWARN)		
    return count		
#-------------------------------------------------------
# The following vars have to be configured to matching
# values from the BinSourcerer configuration file
#-------------------------------------------------------
IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = 'C:\\Users\\UserName\\Desktop\\Features\\'
IDA_PRO_TEXT_EXTRACTOR_EXTRACTED_FILE = 'Features.xtrak'
funcListG = []

class TextExtract_t(idaapi.plugin_t):
    flags = 0
    comment = "Extract all the available features"
    help = "Features will be saved in the output file: [extractedFeatures.extract]"
    wanted_name = "BinSourcerer Features Extractor"
    wanted_hotkey = "Ctrl-Shift-J"
   
    def init(self):
        idaapi.msg("Status: BinSourcerer Features Extractor plug-in loaded.\n")
        return idaapi.PLUGIN_KEEP
    def run(self, arg):
	global fm
	fm = ReSrcForm()
	fm.Compile()
        fm.rSearchAll.checked = False
	fm.rOfflineMal.checked = True
	cwdPath = os.getcwd() + "\\"
	IDA_PRO_TEXT_EXTRACTOR_EXTRACTION_PATH = cwdPath	
	fm.iPath.value = cwdPath
	fm.iFSave.value = "Features.xtrak"
	ok = fm.Execute()
	fm.Free()
    def term(self):
        pass

def PLUGIN_ENTRY():
    return TextExtract_t()



    


repository: binsrc/BinSourcerer
file: IDA Pro Plugin/IdaProTextExtractor_HRC.py
description: Assembly to Source Code Matching Framework
wanted_name: BinSourcerer Features Extractor
comment: Extract all the available features
created_at: 2015-02-04 16:42:49+00:00
pushed_at: 2015-02-04 16:46:13+00:00
url:
https://github.com/binsrc/BinSourcerer/blob/2eab5d88d721909f440427042a896dfa591053c6/IDA%20Pro%20Plugin/IdaProTextExtractor_HRC.py


repository: vAlerainTech/vAlerain-Ark
file: cmake-build-debug/IDA_Pro_7.7/python/examples/hexrays/vds17.py
wanted_name: Structure offsets (IDAPython)
comment: Sample plugin17 for Hex-Rays decompiler
created_at: 2024-03-25 14:26:36+00:00
pushed_at: 2024-07-27 05:07:46+00:00
description:
🔭This will be my first long-term update project, and vAlerainArk is committed to helping security personnel with testing.

url:
https://github.com/vAlerainTech/vAlerain-Ark/blob/df8bcafeda091d30ceade7bb2c7c73b5ae351a38/cmake-build-debug/IDA_Pro_7.7/python/examples/hexrays/vds17.py

content:
"""
summary: using the "Select offsets" widget

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

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

  Place cursor on the union field and press Shift+T
"""

import ida_idaapi
import ida_hexrays
import ida_lines
import ida_typeinf
import ida_kernwin

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

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

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

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

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

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

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

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

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

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

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

        return 1

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


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

def PLUGIN_ENTRY():
    return my_plugin_t()

"""
# A few notes about the VDS17 sample

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

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

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


## How the user interface works

Let us suppose that we have the following local types:

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

and the decompiler generates the following pseudocode:

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

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

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

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

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

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

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

The right pane will change to:

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

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

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


### What do we need this plugin for?

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


## API details

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

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

Then the magic begins:

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


### The devil is in the details

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

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

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

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

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

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

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


