# Adobe Flash ActionScript3 processor module # # Copyright (C) 2018 Kaspersky Lab # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import idaapi import idc from ida_idp import * from ida_bytes import * from ida_auto import * from ida_name import * from ida_lines import * from ida_xref import * from ida_ua import * from ida_segment import * from ida_problems import * from PyQt5 import QtWidgets import cPickle import string import struct class ConstKind: CONSTANT_Void = 0x00 CONSTANT_Utf8 = 0x01 CONSTANT_Float = 0x02 CONSTANT_Int = 0x03 CONSTANT_UInt = 0x04 CONSTANT_PrivateNs = 0x05 CONSTANT_Double = 0x06 CONSTANT_Qname = 0x07 CONSTANT_Namespace = 0x08 CONSTANT_Multiname = 0x09 CONSTANT_False = 0x0A CONSTANT_True = 0x0B CONSTANT_Null = 0x0C CONSTANT_QnameA = 0x0D CONSTANT_MultinameA = 0x0E CONSTANT_RTQname = 0x0F CONSTANT_RTQnameA = 0x10 CONSTANT_RTQnameL = 0x11 CONSTANT_RTQnameLA = 0x12 CONSTANT_NamespaceSet = 0x15 CONSTANT_PackageNamespace = 0x16 CONSTANT_PackageInternalNs = 0x17 CONSTANT_ProtectedNamespace = 0x18 CONSTANT_ExplicitNamespace = 0x19 CONSTANT_StaticProtectedNs = 0x1A CONSTANT_MultinameL = 0x1B CONSTANT_MultinameLA = 0x1C CONSTANT_TypeName = 0x1D CONSTANT_Float4 = 0x1E Names = [ "Void", "Utf8", "Decimal", "Integer", "UInteger", "PrivateNamespace", "Double", "QName", "Namespace", "Multiname", "False", "True", "Null", "QNameA", "MultinameA", "RTQName", "RTQNameA", "RTQNameL", "RTQNameLA", "", "", "Namespace_Set", "PackageNamespace", "PackageInternalNs", "ProtectedNamespace", "ExplicitNamespace", "StaticProtectedNs", "MultinameL", "MultinameLA", "TypeName", "Float4", ] class MethodFlags: NEED_ARGUMENTS = 0x01 NEED_ACTIVATION = 0x02 NEED_REST = 0x04 HAS_OPTIONAL = 0x08 IGNORE_REST = 0x10 NATIVE = 0x20 SETS_DXNS = 0x40 HAS_PARAM_NAMES = 0x80 Names = [ "NEED_ARGUMENTS", "NEED_ACTIVATION", "NEED_REST", "HAS_OPTIONAL", "IGNORE_REST", "NATIVE", "SETS_DXNS", "HAS_PARAM_NAMES" ] class InstanceFlags: CONSTANT_ClassSealed = 0x01 CONSTANT_ClassFinal = 0x02 CONSTANT_ClassInterface = 0x04 CONSTANT_ClassProtectedNs = 0x08 class TraitKind: TRAIT_Slot = 0x00 TRAIT_Method = 0x01 TRAIT_Getter = 0x02 TRAIT_Setter = 0x03 TRAIT_Class = 0x04 TRAIT_Function = 0x05 TRAIT_Const = 0x06 Names = [ "slot", "method", "getter", "setter", "class", "function", "const" ] class TraitAttributes: ATTR_Final = 0x10 ATTR_Override = 0x20 ATTR_Metadata = 0x40 class OperandType: CONSTANT_Unknown = 0x00 CONSTANT_ByteImm = 0x01 CONSTANT_UByteImm = 0x02 CONSTANT_IntImm = 0x03 CONSTANT_UIntImm = 0x04 CONSTANT_Int = 0x05 CONSTANT_UInt = 0x06 CONSTANT_Double = 0x07 CONSTANT_String = 0x08 CONSTANT_Namespace = 0x09 CONSTANT_Multiname = 0x10 CONSTANT_Class = 0x11 CONSTANT_Method = 0x12 CONSTANT_Label = 0x13 CONSTANT_DefaultLabel = 0x14 CONSTANT_LabelsList = 0x15 class Reader: pos = 0 @staticmethod def read_byte(insn=None): if (insn): b = insn.get_next_byte() else: b = idc.get_wide_byte(Reader.pos) Reader.pos += 1 return b @staticmethod def read_encoded_u32(insn=None): value = 0 for i in xrange(5): b = Reader.read_byte(insn) value |= (b & 0x7F) << (7 * i) if not (b & 0x80): break return value @staticmethod def read_s24(insn=None): b = Reader.read_byte(insn) value = b b = Reader.read_byte(insn) value |= b << 8 b = Reader.read_byte(insn) value |= b << 0x10 if (value & 0x00800000): value |= 0xFF000000 return value @staticmethod def get_array_count(): return Reader.read_encoded_u32() class Tag: def __init__(self): self.start = 0 self.tag_code = 0 self.tag_length = 0 self.data_length = 0 self.flags = 0 self.name = "" self.minor_version = 0 self.major_version = 0 def find(self): ea = idc.get_first_seg() tags = [] while (ea != ida_idaapi.BADADDR): if (idc.get_segm_name(ea) == "DoABC"): name = idc.get_strlit_contents(ea + 0xA) tags.append("%d - %s" % (ea, name)) ea = idc.get_next_seg(ea) if (tags == []): return False if (len(tags) > 1): app = QtWidgets.QWidget() ea, ok = QtWidgets.QInputDialog.getItem(app, "Select DoABC tag", "List of DoABC tags", tags, 0, False) if (ea and ok): ea = long(ea.split()[0]) else: return False else: ea = long(tags[0].split()[0]) Reader.pos = ea return True def parse(self): self.start = Reader.pos tag_code_and_length = idc.get_wide_word(Reader.pos) Reader.pos += 2 self.tag_code = tag_code_and_length >> 6 self.tag_length = tag_code_and_length & 0x3F self.data_length = idc.get_wide_dword(Reader.pos) Reader.pos += 4 if (self.tag_code != 0x48): # DoABC1 self.flags = idc.get_wide_dword(Reader.pos) Reader.pos += 4 self.name = idc.get_strlit_contents(Reader.pos) if (self.name is not None): Reader.pos += len(self.name) Reader.pos += 1 self.minor_version = idc.get_wide_word(Reader.pos) Reader.pos += 2 self.major_version = idc.get_wide_word(Reader.pos) Reader.pos += 2 class ConstantPool: def __init__(self): self.abc_ints = [0] self.abc_uints = [0] self.abc_doubles = [0] self.abc_strings = ["null"] self.abc_namespaces = [] self.abc_namespace_sets = [] self.abc_multinames = [] self.abc_methods = [] self.abc_metadata = [] self.abc_instances = [] self.abc_scripts = [] self.abc_bodies = [] def parse_integers(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_ints") count = Reader.get_array_count() for i in xrange(1, count, 1): self.abc_ints.append(Reader.read_encoded_u32()) create_byte(start, Reader.pos - start) def parse_uintegers(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_uints") count = Reader.get_array_count() for i in xrange(1, count, 1): self.abc_uints.append(Reader.read_encoded_u32()) create_byte(start, Reader.pos - start) def parse_doubles(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_doubles") count = Reader.get_array_count() for i in xrange(1, count, 1): self.abc_doubles.append(idc.get_qword(Reader.pos)) Reader.pos += 8 create_byte(start, Reader.pos - start) def parse_strings(self): idc.set_name(Reader.pos, "cpool_strings") count = Reader.get_array_count() for i in xrange(1, count, 1): size = Reader.read_encoded_u32() start = Reader.pos string = "" for i in xrange(size): string += chr(Reader.read_byte()) self.abc_strings.append(string) create_strlit(start, size, idc.STRTYPE_C) def parse_namespaces(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_namespaces") self.abc_namespaces.append({"kind": 0, "name": 0}) count = Reader.get_array_count() for i in xrange(1, count, 1): kind = Reader.read_byte() name = Reader.read_encoded_u32() self.abc_namespaces.append({"kind": kind, "name": name}) create_byte(start, Reader.pos - start) def parse_namespace_sets(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_nsets") self.abc_namespace_sets.append([0]) count = Reader.get_array_count() for i in xrange(1, count, 1): namespace_count = Reader.get_array_count() offsets = [] for j in xrange(namespace_count): offsets.append(Reader.read_encoded_u32()) self.abc_namespace_sets.append(offsets) create_byte(start, Reader.pos - start) def parse_multinames(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_multinames") self.abc_multinames.append({}) count = Reader.get_array_count() for i in xrange(1, count, 1): kind = Reader.read_byte() if (kind == ConstKind.CONSTANT_Qname or kind == ConstKind.CONSTANT_QnameA): ns = Reader.read_encoded_u32() name = Reader.read_encoded_u32() self.abc_multinames.append({"kind": kind, "name": name, "value": ns}) elif (kind == ConstKind.CONSTANT_RTQname or kind == ConstKind.CONSTANT_RTQnameA): name = Reader.read_encoded_u32() self.abc_multinames.append({"kind": kind, "name": name}) elif (kind == ConstKind.CONSTANT_RTQnameL or kind == ConstKind.CONSTANT_RTQnameLA): self.abc_multinames.append({"kind": kind}) elif (kind == ConstKind.CONSTANT_Multiname or kind == ConstKind.CONSTANT_MultinameA): name = Reader.read_encoded_u32() ns_set = Reader.read_encoded_u32() self.abc_multinames.append({"kind": kind, "name": name, "value": ns_set}) elif (kind == ConstKind.CONSTANT_MultinameL or kind == ConstKind.CONSTANT_MultinameLA): ns_set = Reader.read_encoded_u32() self.abc_multinames.append({"kind": kind, "value": ns_set}) elif (kind == ConstKind.CONSTANT_TypeName): name = Reader.read_encoded_u32() param_count = Reader.get_array_count() params = [] for j in xrange(param_count): params.append(Reader.read_encoded_u32()) self.abc_multinames.append({"kind": kind, "name": name, "value": params}) else: raise Exception("parse_multinames: unknown kind") create_byte(start, Reader.pos - start) def parse_methods(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_methods") count = Reader.get_array_count() for i in xrange(count): pos = Reader.pos param_count = Reader.read_encoded_u32() return_type = Reader.read_encoded_u32() params = [] for j in xrange(param_count): params.append(Reader.read_encoded_u32()) name = Reader.read_encoded_u32() flags = Reader.read_byte() options = [] if ((flags & MethodFlags.HAS_OPTIONAL) != 0): option_count = Reader.get_array_count() for j in xrange(option_count): value = Reader.read_encoded_u32() kind = Reader.read_byte() options.append({"kind": kind, "value": value}) param_names = [] if ((flags & MethodFlags.HAS_PARAM_NAMES) != 0): for j in xrange(param_count): param_names.append(Reader.read_encoded_u32()) self.abc_methods.append({"paramtypes": params, "returntype": return_type, "name": name, "flags": flags, "options": options, "paramnames": param_names, "pos": pos, "id": i}) create_byte(start, Reader.pos - start) def parse_metadata(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_metadata") count = Reader.get_array_count() for i in xrange(count): name = Reader.read_encoded_u32() item_count = Reader.get_array_count() items = [] for j in xrange(item_count): key = Reader.read_encoded_u32() value = Reader.read_encoded_u32() items.append({"key": key, "value": value}) self.abc_metadata.append({"name": name, "items": items}) create_byte(start, Reader.pos - start) def parse_traits(self): count = Reader.get_array_count() traits = [] for i in xrange(count): name = Reader.read_encoded_u32() tag = Reader.read_byte() kind = tag & 0x0F attr = tag & 0xF0 gen_id = Reader.read_encoded_u32() gen_index = Reader.read_encoded_u32() value_index = 0 value_kind = 0 if (kind == TraitKind.TRAIT_Slot or kind == TraitKind.TRAIT_Const): value_index = Reader.read_encoded_u32() if (value_index != 0): value_kind = Reader.read_byte() metadata = [] if ((attr & TraitAttributes.ATTR_Metadata) != 0): metadata_count = Reader.get_array_count() for j in xrange(metadata_count): metadata.append(Reader.read_encoded_u32()) traits.append({"name": name, "tkind": kind, "attr": attr, "metadata": metadata, "id": gen_id, "index": gen_index, "value_id": value_index, "value_kind": value_kind}) return traits def parse_classes(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_classes") count = Reader.get_array_count() # read instances for i in xrange(count): name = Reader.read_encoded_u32() super_name = Reader.read_encoded_u32() flags = Reader.read_byte() protected_ns = 0 if ((flags & InstanceFlags.CONSTANT_ClassProtectedNs) != 0): protected_ns = Reader.read_encoded_u32() interface_count = Reader.get_array_count() interfaces = [] for j in xrange(interface_count): interface = Reader.read_encoded_u32() interfaces.append(interface) interface_init = Reader.read_encoded_u32() interface_traits = self.parse_traits() self.abc_instances.append({"name": name, "supername": super_name, "flags": flags, "protectedns": protected_ns, "interfaces": interfaces, "iinit": interface_init, "itraits": interface_traits}) # read classes for i in xrange(count): class_init = Reader.read_encoded_u32() class_traits = self.parse_traits() self.abc_instances[i]["cinit"] = class_init self.abc_instances[i]["ctraits"] = class_traits create_byte(start, Reader.pos - start) def parse_scripts(self): start = Reader.pos idc.set_name(Reader.pos, "cpool_scripts") count = Reader.get_array_count() for i in xrange(count): init = Reader.read_encoded_u32() traits = self.parse_traits() self.abc_scripts.append({"sinit": init, "traits": traits}) create_byte(start, Reader.pos - start) def parse_method_bodies(self): count = Reader.get_array_count() for i in xrange(count): start = Reader.pos idc.set_name(Reader.pos, "cpool_method%d_bodyinfo" % i) method = Reader.read_encoded_u32() max_stack = Reader.read_encoded_u32() local_count = Reader.read_encoded_u32() init_scope_depth = Reader.read_encoded_u32() max_scope_depth = Reader.read_encoded_u32() code_length = Reader.read_encoded_u32() code_pos = Reader.pos create_byte(start, Reader.pos - start) Reader.pos += code_length start = Reader.pos idc.set_name(Reader.pos, "cpool_method%d_exceptions" % i) exception_count = Reader.get_array_count() exceptions = [] for j in xrange(exception_count): exception_from = Reader.read_encoded_u32() exception_to = Reader.read_encoded_u32() exception_target = Reader.read_encoded_u32() exception_type = Reader.read_encoded_u32() exception_name = Reader.read_encoded_u32() exceptions.append({"from": exception_from, "to": exception_to, "target": exception_target, "type": exception_type, "name": exception_name}) create_byte(start, Reader.pos - start) start = Reader.pos idc.set_name(Reader.pos, "cpool_method%d_traits" % i) traits = self.parse_traits() create_byte(start, Reader.pos - start) self.abc_bodies.append({"pos": code_pos, "length": code_length, "method": method, "maxstack": max_stack, "locals": local_count, "init_depth": init_scope_depth, "max_depth": max_scope_depth, "exceptions": exceptions, "traits": traits}) def parse(self): self.parse_integers() self.parse_uintegers() self.parse_doubles() self.parse_strings() self.parse_namespaces() self.parse_namespace_sets() self.parse_multinames() self.parse_methods() self.parse_metadata() self.parse_classes() self.parse_scripts() self.parse_method_bodies() class ABC: def __init__(self, cpool): self.cpool = cpool self.namespaces = [] self.namespace_sets = [] self.multinames = [] self.methods = [] self.metadata = [] self.classes = [] self.instances = [] self.scripts = [] def get_int(self, index): return self.cpool.abc_ints[index] def get_uint(self, index): return self.cpool.abc_uints[index] def get_double(self, index): return self.cpool.abc_doubles[index] def get_string(self, index): return self.cpool.abc_strings[index] def get_namespace(self, ns): return '%s("%s")' % (ConstKind.Names[ns["kind"]], ns["name"]) def get_namespace_set(self, ns_set): s = "[" length = len(ns_set) for i in xrange(length): s += self.get_namespace(ns_set[i]) if (i < length-1): s += ", " s += "]" return s def get_multiname(self, mn): kind = mn["kind"] if (kind == ConstKind.CONSTANT_Qname or kind == ConstKind.CONSTANT_QnameA): name = mn["name"] namespace = mn["ns"] s = '%s, "%s"' % (self.get_namespace(namespace), name) elif (kind == ConstKind.CONSTANT_RTQname or kind == ConstKind.CONSTANT_RTQnameA): name = mn["name"] s = '"%s"' % name elif (kind == ConstKind.CONSTANT_Multiname or kind == ConstKind.CONSTANT_MultinameA): name = mn["name"] namespace_set = mn["ns_set"] s = '"%s", %s' % (name, self.get_namespace_set(namespace_set)) elif (kind == ConstKind.CONSTANT_MultinameL or kind == ConstKind.CONSTANT_MultinameLA): namespace_set = mn["ns_set"] s = self.get_namespace_set(namespace_set) elif (kind == ConstKind.CONSTANT_TypeName): name = mn["name"] params = mn["params"] s = self.get_multiname(name) s += "<" length = len(params) for i in xrange(length): self.get_multiname(params[i]) if (i < length-1): s += ", " s += ">" return "%s(%s)" % (ConstKind.Names[kind], s) def get_value(self, value): if (value["kind"] == ConstKind.CONSTANT_Int): s = "%d" % value["value"] elif (value["kind"] == ConstKind.CONSTANT_UInt): s = "%d" % value["value"] elif (value["kind"] == ConstKind.CONSTANT_Double): s = "%g" % struct.unpack(">d", struct.pack(">Q", value["value"]))[0] elif (value["kind"] == ConstKind.CONSTANT_Utf8): s = value["value"] elif (value["kind"] == ConstKind.CONSTANT_Namespace or value["kind"] == ConstKind.CONSTANT_PackageNamespace or value["kind"] == ConstKind.CONSTANT_PackageInternalNs or value["kind"] == ConstKind.CONSTANT_ProtectedNamespace or value["kind"] == ConstKind.CONSTANT_ExplicitNamespace or value["kind"] == ConstKind.CONSTANT_StaticProtectedNs or value["kind"] == ConstKind.CONSTANT_PrivateNs): s = self.get_namespace(value["value"]) else: s = "" return "%s(%s)" % (ConstKind.Names[value["kind"]], s) def convert_namespaces(self): for namespace in self.cpool.abc_namespaces: kind = namespace["kind"] name = self.get_string(namespace["name"]) self.namespaces.append({"kind": kind, "name": name}) def convert_namespace_sets(self): for namespace_set in self.cpool.abc_namespace_sets: ns_set = [] for namespace in namespace_set: ns_set.append(self.namespaces[namespace]) self.namespace_sets.append(ns_set) def convert_multinames(self): for multiname in self.cpool.abc_multinames: if (multiname == {}): self.multinames.append({}) continue kind = multiname["kind"] if (kind == ConstKind.CONSTANT_Qname or kind == ConstKind.CONSTANT_QnameA): name = self.get_string(multiname["name"]) ns = self.namespaces[multiname["value"]] self.multinames.append({"kind": kind, "ns": ns, "name": name}) elif (kind == ConstKind.CONSTANT_RTQname or kind == ConstKind.CONSTANT_RTQnameA): name = self.get_string(multiname["name"]) self.multinames.append({"kind": kind, "name": name}) elif (kind == ConstKind.CONSTANT_RTQnameL or kind == ConstKind.CONSTANT_RTQnameLA): self.multinames.append({"kind": kind}) elif (kind == ConstKind.CONSTANT_Multiname or kind == ConstKind.CONSTANT_MultinameA): name = self.get_string(multiname["name"]) ns_set = self.namespace_sets[multiname["value"]] self.multinames.append({"kind": kind, "name": name, "ns_set": ns_set}) elif (kind == ConstKind.CONSTANT_MultinameL or kind == ConstKind.CONSTANT_MultinameLA): ns_set = self.namespace_sets[multiname["value"]] self.multinames.append({"kind": kind, "ns_set": ns_set}) elif (kind == ConstKind.CONSTANT_TypeName): self.multinames.append({"kind": kind}) def convert_multiname_typenames(self): for i in xrange(len(self.cpool.abc_multinames)): multiname = self.cpool.abc_multinames[i] if (multiname == {}): continue kind = multiname["kind"] if (kind == ConstKind.CONSTANT_TypeName): name = self.multinames[multiname["name"]] params = [] for param in multiname["value"]: params.append(self.multinames[param]) self.multinames[i]["name"] = name self.multinames[i]["params"] = params def convert_value(self, kind, value): if (kind == ConstKind.CONSTANT_Int): return {"kind": kind, "value": self.get_int(value)} elif (kind == ConstKind.CONSTANT_UInt): return {"kind": kind, "value": self.get_uint(value)} elif (kind == ConstKind.CONSTANT_Double): return {"kind": kind, "value": self.get_double(value)} elif (kind == ConstKind.CONSTANT_Utf8): return {"kind": kind, "value": self.get_string(value)} elif (kind == ConstKind.CONSTANT_Namespace or kind == ConstKind.CONSTANT_PackageNamespace or kind == ConstKind.CONSTANT_PackageInternalNs or kind == ConstKind.CONSTANT_ProtectedNamespace or kind == ConstKind.CONSTANT_ExplicitNamespace or kind == ConstKind.CONSTANT_StaticProtectedNs or kind == ConstKind.CONSTANT_PrivateNs): return {"kind": kind, "value": self.namespaces[value]} elif (kind == ConstKind.CONSTANT_True or kind == ConstKind.CONSTANT_False or kind == ConstKind.CONSTANT_Null or kind == ConstKind.CONSTANT_Void): return {"kind": kind} else: raise Exception("convert_value: unknown kind") def convert_methods(self): for method in self.cpool.abc_methods: params = [] for param in method["paramtypes"]: params.append(self.multinames[param]) return_type = self.multinames[method["returntype"]] name = self.get_string(method["name"]) flags = method["flags"] options = [] for option in method["options"]: options.append(self.convert_value(option["kind"], option["value"])) param_names = [] for param_name in method["paramnames"]: param_names.append(self.get_string(param_name)) pos = method["pos"] index = method["id"] self.methods.append({"paramtypes": params, "returntype": return_type, "name": name, "flags": flags, "options": options, "paramnames": param_names, "pos": pos, "id": index, "refid": "", "body": None}) def convert_metadata(self): for metadata in self.cpool.abc_metadata: name = self.get_string(metadata["name"]) items = [] for item in metadata["items"]: key = self.get_string(item["key"]) value = self.get_string(item["value"]) items.append({"key": key, "value": value}) self.metadata.append({"name": name, "items": items}) def convert_traits(self, traits): new_traits = [] for trait in traits: name = self.multinames[trait["name"]] kind = trait["tkind"] attr = trait["attr"] new_trait = {"name": name, "tkind": kind, "attr": attr} if (kind == TraitKind.TRAIT_Slot or kind == TraitKind.TRAIT_Const): new_trait["slotid"] = trait["id"] new_trait["type"] = self.multinames[trait["index"]] new_trait["value"] = self.convert_value(trait["value_kind"], trait["value_id"]) elif (kind == TraitKind.TRAIT_Class): new_trait["slotid"] = trait["id"] new_trait["class"] = self.classes[trait["index"]] elif (kind == TraitKind.TRAIT_Function): new_trait["slotid"] = trait["id"] new_trait["function"] = self.methods[trait["index"]] elif (kind == TraitKind.TRAIT_Method or kind == TraitKind.TRAIT_Getter or kind == TraitKind.TRAIT_Setter): new_trait["dispid"] = trait["id"] new_trait["method"] = self.methods[trait["index"]] else: raise Exception("convert_traits: unknown kind") metadata = [] for entry in trait["metadata"]: metadata.append(self.metadata[entry]) new_trait["metadata"] = metadata new_traits.append(new_trait) return new_traits def convert_instances(self): for instance in self.cpool.abc_instances: name = instance["name"] super_name = self.multinames[instance["supername"]] flags = instance["flags"] protected_ns = self.namespaces[instance["protectedns"]] interfaces = [] for interface in instance["interfaces"]: interfaces.append(self.multinames[interface]) init = self.methods[instance["iinit"]] traits = self.convert_traits(instance["itraits"]) self.instances.append({"name": name, "supername": super_name, "flags": flags, "protectedns": protected_ns, "interfaces": interfaces, "iinit": init, "traits": traits}) def convert_classes(self): for i in xrange(len(self.cpool.abc_instances)): abc_class = self.cpool.abc_instances[i] init = self.methods[abc_class["cinit"]] traits = self.convert_traits(abc_class["ctraits"]) instance = self.instances[i] self.classes.append({"cinit": init, "traits": traits, "instance": instance}) def convert_scripts(self): for script in self.cpool.abc_scripts: init = self.methods[script["sinit"]] traits = self.convert_traits(script["traits"]) self.scripts.append({"sinit": init, "traits": traits}) def convert_method_bodies(self): for body in self.cpool.abc_bodies: code_pos = body["pos"] code_length = body["length"] method = self.methods[body["method"]] max_stack = body["maxstack"] local_count = body["locals"] init_scope_depth = body["init_depth"] max_scope_depth = body["max_depth"] exceptions = [] for exception in body["exceptions"]: exception_from = exception["from"] exception_to = exception["to"] exception_target = exception["target"] exception_type = self.multinames[exception["type"]] exception_name = self.multinames[exception["name"]] exceptions.append({"from": exception_from, "to": exception_to, "target": exception_target, "type": exception_type, "name": exception_name}) traits = self.convert_traits(body["traits"]) new_body = {"pos": code_pos, "length": code_length, "method": method, "maxstack": max_stack, "locals": local_count, "init_depth": init_scope_depth, "max_depth": max_scope_depth, "exceptions": exceptions, "traits": traits} self.methods[body["method"]]["body"] = new_body def convert(self): self.convert_namespaces() self.convert_namespace_sets() self.convert_multinames() self.convert_multiname_typenames() self.convert_methods() self.convert_metadata() self.convert_instances() self.convert_classes() self.convert_scripts() self.convert_method_bodies() class MultinameStrings: def __init__(self): self.names = {} self.namespaces = {} self.namespace_sets = {} def get_name_offset(self, name): return self.names[name] def get_namespace_offset(self, ns): return self.namespaces[ns] def get_namespace_set_offset(self, ns_set): return self.namespace_sets[ns_set] def find_last_segment(self): ea = idc.get_first_seg() while(idc.get_next_seg(ea) != ida_idaapi.BADADDR): ea = idc.get_next_seg(ea) return ea def find_last_address(self): ea = self.find_last_segment() # check if there are no segments if (ea == ida_idaapi.BADADDR): ea = 0 # there might be data not mapped to segments while(idc.next_addr(ea) != ida_idaapi.BADADDR): ea = idc.next_addr(ea) return ea def get_multiname_strings(self, abc, mn): kind = mn["kind"] if (kind == ConstKind.CONSTANT_Qname or kind == ConstKind.CONSTANT_QnameA): name = mn["name"] ns = abc.get_namespace(mn["ns"]) if (ns not in self.namespaces): self.namespaces[ns] = 0 if (name not in self.names): self.names[name] = 0 elif (kind == ConstKind.CONSTANT_RTQname or kind == ConstKind.CONSTANT_RTQnameA): name = mn["name"] if (name not in self.names): self.names[name] = 0 elif (kind == ConstKind.CONSTANT_RTQnameL or kind == ConstKind.CONSTANT_RTQnameLA): return elif (kind == ConstKind.CONSTANT_Multiname or kind == ConstKind.CONSTANT_MultinameA): name = mn["name"] ns_set = abc.get_namespace_set(mn["ns_set"]) if (name not in self.names): self.names[name] = 0 if (ns_set not in self.namespace_sets): self.namespace_sets[ns_set] = 0 elif (kind == ConstKind.CONSTANT_MultinameL or kind == ConstKind.CONSTANT_MultinameLA): ns_set = abc.get_namespace_set(mn["ns_set"]) if (ns_set not in self.namespace_sets): self.namespace_sets[ns_set] = 0 elif (kind == ConstKind.CONSTANT_TypeName): name = mn["name"] params = mn["params"] self.get_multiname_strings(abc, name) length = len(params) for i in xrange(length): self.get_multiname_strings(abc, params[i]) def get_strings(self, abc): for mn in abc.multinames: if (mn == {}): continue self.get_multiname_strings(abc, mn) def create_strings_segment(self): buf = "" addr = self.find_last_address() + 1 for key in self.namespace_sets: self.namespace_sets[key] = addr + len(buf) buf += key.replace('"', "")[1:-1] + "\x00" for key in self.namespaces: ns_set = "[%s]" % key if (ns_set in self.namespace_sets): self.namespaces[key] = self.namespace_sets[ns_set] continue self.namespaces[key] = addr + len(buf) buf += key.replace('"', "") + "\x00" for key in self.names: self.names[key] = addr + len(buf) buf += key.replace('"', "") + "\x00" add_segm(0, addr, addr + len(buf), "STRINGS", None) patch_bytes(addr, buf) idc.del_items(addr, idc.DELIT_SIMPLE, len(buf)) for key in self.namespaces: name = key[:-1].split("(")[1][1:-1] if (name == ""): name = "_" idc.set_name(self.namespaces[key], name, SN_NOCHECK | SN_NOWARN | SN_FORCE) for key in self.names: idc.set_name(self.names[key], key, SN_NOCHECK | SN_NOWARN | SN_FORCE) class Dumper: @staticmethod def make_line(ctx, s): ctx.out_line(s) ctx.flush_outbuf(0) @staticmethod def dump_string(ctx, abc, index): ctx.out_line('"' + abc.get_string(index) + '"', COLOR_STRING) @staticmethod def dump_namespace(ctx, ns): ctx.out_line(ConstKind.Names[ns["kind"]] + "(", COLOR_KEYWORD) ctx.out_line('"' + ns["name"] + '"', COLOR_STRING) ctx.out_line(")", COLOR_KEYWORD) @staticmethod def dump_multiname(ctx, op, abc, strings, mn): kind = mn["kind"] ctx.out_line(ConstKind.Names[kind] + "(", COLOR_KEYWORD) if (kind == ConstKind.CONSTANT_Qname or kind == ConstKind.CONSTANT_QnameA): name = mn["name"] namespace = abc.get_namespace(mn["ns"]) ctx.out_name_expr(op, strings.get_namespace_offset(namespace), ida_idaapi.BADADDR) ctx.out_line(", ", COLOR_KEYWORD) ctx.out_name_expr(op, strings.get_name_offset(name), ida_idaapi.BADADDR) if (get_first_dref_from(ctx.insn.ea) == ida_idaapi.BADADDR): add_dref(ctx.insn.ea, strings.get_namespace_offset(namespace), dr_I) add_dref(ctx.insn.ea, strings.get_name_offset(name), dr_I) elif (kind == ConstKind.CONSTANT_RTQname or kind == ConstKind.CONSTANT_RTQnameA): name = mn["name"] ctx.out_name_expr(op, strings.get_name_offset(name), ida_idaapi.BADADDR) if (get_first_dref_from(ctx.insn.ea) == ida_idaapi.BADADDR): add_dref(ctx.insn.ea, strings.get_name_offset(name), dr_I) elif (kind == ConstKind.CONSTANT_RTQnameL or kind == ConstKind.CONSTANT_RTQnameLA): return elif (kind == ConstKind.CONSTANT_Multiname or kind == ConstKind.CONSTANT_MultinameA): name = mn["name"] namespace_set = abc.get_namespace_set(mn["ns_set"]) ctx.out_name_expr(op, strings.get_name_offset(name), ida_idaapi.BADADDR) ctx.out_line(", [", COLOR_KEYWORD) ctx.out_name_expr(op, strings.get_namespace_set_offset(namespace_set), ida_idaapi.BADADDR) ctx.out_line("]", COLOR_KEYWORD) if (get_first_dref_from(ctx.insn.ea) == ida_idaapi.BADADDR): add_dref(ctx.insn.ea, strings.get_name_offset(name), dr_I) add_dref(ctx.insn.ea, strings.get_namespace_set_offset(namespace_set), dr_I) elif (kind == ConstKind.CONSTANT_MultinameL or kind == ConstKind.CONSTANT_MultinameLA): namespace_set = abc.get_namespace_set(mn["ns_set"]) ctx.out_line("[", COLOR_KEYWORD) ctx.out_name_expr(op, strings.get_namespace_set_offset(namespace_set), ida_idaapi.BADADDR) ctx.out_line("]", COLOR_KEYWORD) if (get_first_dref_from(ctx.insn.ea) == ida_idaapi.BADADDR): add_dref(ctx.insn.ea, strings.get_namespace_set_offset(namespace_set), dr_I) elif (kind == ConstKind.CONSTANT_TypeName): name = mn["name"] params = mn["params"] Dumper.dump_multiname(ctx, op, abc, strings, name) ctx.out_line("<", COLOR_KEYWORD) length = len(params) for i in xrange(length): Dumper.dump_multiname(ctx, op, abc, strings, params[i]) if (i < length-1): ctx.out_line(", ", COLOR_KEYWORD) ctx.out_line(">", COLOR_KEYWORD) ctx.out_line(")", COLOR_KEYWORD) @staticmethod def dump_class(ctx, abc, index): ctx.out_line('"' + abc.multinames[abc.classes[index]["instance"]["name"]]["name"] + '"', COLOR_STRING) @staticmethod def dump_flags(ctx, flags, names): i = 0 while (flags != 0): if (flags & 1): Dumper.make_line(ctx, "flag " + names[i]) i += 1 flags >>= 1 @staticmethod def dump_traits(ctx, abc, traits): for trait in traits: kind = trait["tkind"] name = abc.get_multiname(trait["name"]) s = "trait %s %s" % (TraitKind.Names[kind], name) if (kind == TraitKind.TRAIT_Slot or kind == TraitKind.TRAIT_Const): s += " slotid %d" % trait["slotid"] if (trait["type"] != {}): s += " type " s += abc.get_multiname(trait["type"]) if (trait["value"]["kind"] != 0): s += " value " s += abc.get_value(trait["value"]) elif (kind == TraitKind.TRAIT_Class): s += " slotid %d" % trait["slotid"] elif (kind == TraitKind.TRAIT_Function): s += " slotid %d" % trait["slotid"] elif (kind == TraitKind.TRAIT_Method or kind == TraitKind.TRAIT_Getter or kind == TraitKind.TRAIT_Setter): s += " dispid %d" % trait["dispid"] Dumper.make_line(ctx, s) @staticmethod def dump_method(ctx, abc, method): if (method is not None): if (method["refid"] != ""): Dumper.make_line(ctx, 'refid "%s"' % method["refid"]) for param in method["paramtypes"]: if param != {}: Dumper.make_line(ctx, "param %s" % abc.get_multiname(param)) if method["returntype"] != {}: Dumper.make_line(ctx, "returns %s" % abc.get_multiname(method["returntype"])) Dumper.dump_flags(ctx, method["flags"], MethodFlags.Names) for option in method["options"]: if option != {}: Dumper.make_line(ctx, "optional %s" % abc.get_value(option)) for param_name in method["paramnames"]: Dumper.make_line(ctx, "paramname %s" % param_name) Dumper.make_line(ctx, "maxstack %d" % method["body"]["maxstack"]) Dumper.make_line(ctx, "locals %d" % method["body"]["locals"]) Dumper.make_line(ctx, "init_depth %d" % method["body"]["init_depth"]) Dumper.make_line(ctx, "max_depth %d" % method["body"]["max_depth"]) Dumper.dump_traits(ctx, abc, method["body"]["traits"]) @staticmethod def dump_exception(ctx, abc, exception): if (exception is not None): Dumper.make_line(ctx, "try") Dumper.make_line(ctx, "from 0x%X" % exception["from"]) Dumper.make_line(ctx, "to 0x%X" % exception["to"]) if (exception["type"] != {}): Dumper.make_line(ctx, "type %s" % abc.get_multiname(exception["type"])) if (exception["name"] != {}): Dumper.make_line(ctx, "name %s" % abc.get_multiname(exception["name"])) @staticmethod def dump_name_expr(ctx, op, addr): r = ctx.out_name_expr(op, addr, ida_idaapi.BADADDR) if not r: ctx.out_tagon(COLOR_ERROR) ctx.out_btoa(addr, 16) ctx.out_tagoff(COLOR_ERROR) remember_problem(PR_NONAME, ctx.insn.ea) class as3_processor_t(processor_t): PLFM_SWF_AS3 = 0x8A53 # IDP id ( Numbers above 0x8000 are reserved for the third-party modules) id = PLFM_SWF_AS3 # Processor features flag = PR_USE32 | PR_DEFSEG32 | PR_RNAMESOK | PRN_HEX | PR_NO_SEGMOVE # Number of bits in a byte for code segments (usually 8) # IDA supports values up to 32 bits cnbits = 8 # Number of bits in a byte for non-code segments (usually 8) # IDA supports values up to 32 bits dnbits = 8 # short processor names # Each name should be shorter than 9 characters psnames = ["SWF-AS3"] # long processor names # No restriction on name lengthes. plnames = ["SWF ActionScript3"] # size of a segment register in bytes segreg_size = 0 # icode of the first instruction instruc_start = 0 # Size of long double (tbyte) for this processor # (meaningful only if ash.a_tbyte != NULL) tbyte_size = 0 # only one assembler is supported assembler = { # flag 'flag' : ASH_HEXF3 | AS_UNEQU | AS_COLON | ASB_BINF4, # user defined flags (local only for IDP) # you may define and use your own bits 'uflag' : 0, # Assembler name (displayed in menus) 'name': "SWF ActionScript3", # org directive 'origin': "org", # end directive 'end': "end", # comment string (see also cmnt2) 'cmnt': ";", # ASCII string delimiter 'ascsep': "\"", # ASCII char constant delimiter 'accsep': "'", # ASCII special chars (they can't appear in character and ascii constants) 'esccodes': "\"'", # # Data representation (db,dw,...): # # ASCII string directive 'a_ascii': "db", # byte directive 'a_byte': "db", # word directive 'a_word': "dw", # remove if not allowed 'a_dword': "dd", # remove if not allowed 'a_qword': "dq", # remove if not allowed 'a_oword': "xmmword", # float; 4bytes; remove if not allowed 'a_float': "dd", # double; 8bytes; NULL if not allowed 'a_double': "dq", # long double; NULL if not allowed 'a_tbyte': "dt", # array keyword. the following # sequences may appear: # #h - header # #d - size # #v - value # #s(b,w,l,q,f,d,o) - size specifiers # for byte,word, # dword,qword, # float,double,oword 'a_dups': "#d dup(#v)", # uninitialized data directive (should include '%s' for the size of data) 'a_bss': "%s dup ?", # 'seg ' prefix (example: push seg seg001) 'a_seg': "seg", # current IP (instruction pointer) symbol in assembler 'a_curip': "$", # "public" name keyword. NULL-gen default, ""-do not generate 'a_public': "public", # "weak" name keyword. NULL-gen default, ""-do not generate 'a_weak': "weak", # "extrn" name keyword 'a_extrn': "extrn", # "comm" (communal variable) 'a_comdef': "", # "align" keyword 'a_align': "align", # Left and right braces used in complex expressions 'lbrace': "(", 'rbrace': ")", # % mod assembler time operation 'a_mod': "%", # & bit and assembler time operation 'a_band': "&", # | bit or assembler time operation 'a_bor': "|", # ^ bit xor assembler time operation 'a_xor': "^", # ~ bit not assembler time operation 'a_bnot': "~", # << shift left assembler time operation 'a_shl': "<<", # >> shift right assembler time operation 'a_shr': ">>", # size of type (format string) 'a_sizeof_fmt': "size %s", } # Assembler tag = None abc = None multiname_strings = None switches = [] exceptions = [] FLo_SIGNED = 0x0001 # This is a signed operand # ---------------------------------------------------------------------- def analyze_instance_references(self, instance, line): if (instance["iinit"]["refid"] == ""): instance["iinit"]["refid"] = "%s/instance/init" % line for trait in instance["traits"]: name = instance["protectedns"]["name"] self.analyze_trait_references(trait, "%s/instance" % line, name) def analyze_class_references(self, abc_class, line): if (abc_class["cinit"]["refid"] == ""): abc_class["cinit"]["refid"] = "%s/class/init" % line for trait in abc_class["traits"]: name = abc_class["instance"]["protectedns"]["name"] self.analyze_trait_references(trait, "%s/class" % line, name) self.analyze_instance_references(abc_class["instance"], line) def analyze_trait_references(self, trait, line, namespace): kind = trait["tkind"] if (kind == TraitKind.TRAIT_Class): self.analyze_class_references(trait["class"], line) elif (kind == TraitKind.TRAIT_Function): if (trait["function"]["refid"] == ""): name = trait["name"]["name"] namespace_kind = trait["name"]["ns"]["kind"] namespace_name = trait["name"]["ns"]["name"] if (namespace_kind == ConstKind.CONSTANT_Namespace): trait["function"]["refid"] = "%s/%s:%s" % (line, namespace_name, name) elif (namespace_kind == ConstKind.CONSTANT_PrivateNamespace): trait["function"]["refid"] = "%s/%s/%s" % (line, namespace, name) else: trait["function"]["refid"] = "%s/%s" % (line, name) elif (kind == TraitKind.TRAIT_Method or kind == TraitKind.TRAIT_Getter or kind == TraitKind.TRAIT_Setter): if (trait["method"]["refid"] == ""): name = trait["name"]["name"] namespace_kind = trait["name"]["ns"]["kind"] namespace_name = trait["name"]["ns"]["name"] if (trait["name"]["kind"] == ConstKind.CONSTANT_Qname and (namespace_kind == ConstKind.CONSTANT_Namespace or namespace_kind == ConstKind.CONSTANT_PrivateNs)): if (namespace_kind == ConstKind.CONSTANT_Namespace): trait["method"]["refid"] = "%s/%s:%s" % (line, namespace_name, name) elif (namespace_kind == ConstKind.CONSTANT_PrivateNs): trait["method"]["refid"] = "%s/%s/%s" % (line, namespace, name) else: trait["method"]["refid"] = "%s/%s" % (line, name) if (kind == TraitKind.TRAIT_Getter): trait["method"]["refid"] += "/getter" if (kind == TraitKind.TRAIT_Setter): trait["method"]["refid"] += "/setter" def create_unique_references(self, abc): for method in self.abc.methods: if (method["refid"] == ""): continue group = [m for m in self.abc.methods if m["refid"] == method["refid"]] if (len(group) > 1): for i in xrange(len(group)): group[i]["refid"] += "_%d" % i def analyze_references(self, abc): for script in abc.scripts: for trait in script["traits"]: if (trait["name"]["kind"] == ConstKind.CONSTANT_Qname and trait["name"]["ns"]["kind"] != ConstKind.CONSTANT_PrivateNs): name = trait["name"]["name"] namespace_name = trait["name"]["ns"]["name"] if (namespace_name != ""): line = "%s:%s" % (namespace_name, name) else: line = name script["sinit"]["refid"] = "%s/init" % line for trait in script["traits"]: self.analyze_trait_references(trait, line, None) elif (trait["name"]["kind"] == ConstKind.CONSTANT_Multiname): name = trait["name"]["name"] if (len(script["traits"]) == 1): script["sinit"]["refid"] = "%s/init" % name self.analyze_trait_references(trait, name, None) self.create_unique_references(abc) def fix_bad_name(self, refid): name = "" for char in refid: if (char in string.digits or char in string.lowercase or char in string.uppercase or ord(char) in [0x2E, 0x2F, 0x3A, 0x5F]): name += char else: name += "\\x%02X" % ord(char) return name def analyze_code(self, is_new_file): for i in xrange(len(self.abc.methods)): if self.abc.methods[i]["body"] is not None: code_pos = self.abc.methods[i]["body"]["pos"] code_length = self.abc.methods[i]["body"]["length"] reference = self.abc.methods[i]["refid"] if (is_new_file): print("%X - %X - %s" % (code_pos, code_length, reference)) auto_make_proc(code_pos) if (reference != ""): idc.set_name(code_pos, self.fix_bad_name(reference), SN_NOCHECK) for exception in self.abc.methods[i]["body"]["exceptions"]: exception_from = code_pos + exception["from"] exception_to = code_pos + exception["to"] exception_target = code_pos + exception["target"] if (is_new_file): auto_make_code(exception_target) set_dummy_name(exception_from, exception_target) self.exceptions.append({"from": exception_from, "to": exception_to, "target": exception_target, "type": exception["type"], "name": exception["name"]}) def load_file(self, is_new_file): self.tag = Tag() if not (self.tag.find()): print('No "DoABC" tag!') return self.tag.parse() if (is_new_file): idc.del_items(self.tag.start, idc.DELIT_SIMPLE, self.tag.data_length) print("Parse ABC...") cpool = ConstantPool() cpool.parse() print("Convert ABC...") self.abc = ABC(cpool) self.abc.convert() self.multiname_strings = MultinameStrings() self.multiname_strings.get_strings(self.abc) if (is_new_file): print("Create strings segment...") self.multiname_strings.create_strings_segment() print("Analyze references...") self.analyze_references(self.abc) print("Analyze code...") self.analyze_code(is_new_file) # ---------------------------------------------------------------------- def notify_gen_map_file(self, qfile): """ Generate map file. If this function is absent then the kernel will create the map file. This function returns number of lines in output file. 0 - empty file, -1 - write error """ dump = [] for method in self.abc.methods: if (method["body"] is None): continue methodInfo1 = idc.get_qword(method["pos"]) methodInfo2 = idc.get_qword(method["pos"]+8) index = method["id"] ea = method["body"]["pos"] length = method["body"]["length"] name = get_name(ea) start = ea end = ea + length instructions = {} while (ea < end): line = generate_disasm_line(ea, GENDSM_REMOVE_TAGS) instructions[ea-start] = line ea += get_item_size(ea) dump.append({"id": index, "info": methodInfo1 + methodInfo2, "name": name, "instructions": instructions}) data = cPickle.dumps(dump) qfile.write(data) return len(data.splitlines()) # ---------------------------------------------------------------------- def notify_oldfile(self, filename): self.load_file(False) # ---------------------------------------------------------------------- def notify_newfile(self, filename): self.load_file(True) # ---------------------------------------------------------------------- def notify_get_autocmt(self, insn): """ Get instruction comment. 'insn' describes the instruction in question @return: None or the comment string """ if "cmt" in self.instruc[insn.itype]: return self.instruc[insn.itype]["cmt"] # ---------------------------------------------------------------------- def notify_can_have_type(self, op): """ Can the operand have a type as offset, segment, decimal, etc. (for example, a register AX can't have a type, meaning that the user can't change its representation. see bytes.hpp for information about types and flags) Returns: bool """ return op.type == o_imm # ---------------------------------------------------------------------- def notify_out_header(self, ctx): """function to produce start of disassembled text""" ctx.gen_block_cmt("+-------------------------------------------------------------------------+\n" \ "| Adobe Flash ActionScript3 processor module |\n" \ "| Author: Boris Larin |\n" \ "| <Boris.Larin@kaspersky.com> |\n" \ "+-------------------------------------------------------------------------+", COLOR_DEFAULT) ctx.flush_outbuf(0) # ---------------------------------------------------------------------- def notify_may_be_func(self, insn, state): """ can a function start here? the instruction is in 'insn' arg: state -- autoanalysis phase state == 0: creating functions == 1: creating chunks returns: probability 0..100 """ return False # ---------------------------------------------------------------------- def handle_operand(self, insn, op, isRead): optype = op.type if optype == o_near: itype = insn.itype if itype == self.itype_newfunction or itype == self.itype_callstatic: fl = fl_CN else: fl = fl_JN insn.add_cref(op.addr, op.offb, fl) if optype == o_idpspec4: for target in self.switches[op.value]: insn.add_cref(target, op.offb, fl_JN) # ---------------------------------------------------------------------- def notify_emu(self, insn): """ Emulate instruction, create cross-references, plan to analyze subsequent instructions, modify flags etc. Upon entrance to this function all information about the instruction is in 'insn' structure. If zero is returned, the kernel will delete the instruction. """ aux = self.get_auxpref(insn) feature = insn.get_canon_feature() if feature & CF_USE1: self.handle_operand(insn, insn.Op1, 1) if feature & CF_USE2: self.handle_operand(insn, insn.Op2, 1) if feature & CF_USE3: self.handle_operand(insn, insn.Op3, 1) if feature & CF_USE4: self.handle_operand(insn, insn.Op4, 1) if feature & CF_JUMP: remember_problem(PR_JUMP, insn.ea) uncond_jmp = insn.itype in [self.itype_jump, self.itype_lookupswitch] if (feature & CF_STOP == 0) and not uncond_jmp: add_cref(insn.ea, insn.ea + insn.size, fl_F) return True # ---------------------------------------------------------------------- def notify_out_operand(self, ctx, op): """ Generate text representation of an instructon operand. This function shouldn't change the database, flags or anything else. All these actions should be performed only by u_emu() function. The output text is placed in the output buffer initialized with init_output_buffer() This function uses out_...() functions from ua.hpp to generate the operand text Returns: 1-ok, 0-operand is hidden. """ optype = op.type fl = op.specval value = op.value signed = OOF_SIGNED if fl & self.FLo_SIGNED != 0 else 0 def_arg = is_defarg(get_flags(ctx.insn.ea), op.n) if optype == o_imm: ctx.out_value(op, OOFW_IMM | signed) elif optype == o_near: Dumper.dump_name_expr(ctx, op, op.addr) elif optype == o_idpspec0: Dumper.dump_string(ctx, self.abc, value) elif optype == o_idpspec1: Dumper.dump_namespace(ctx, self.abc.namespaces[value]) elif optype == o_idpspec2: Dumper.dump_multiname(ctx, op, self.abc, self.multiname_strings, self.abc.multinames[value]) elif optype == o_idpspec3: Dumper.dump_class(ctx, self.abc, value) elif optype == o_idpspec4: for target in self.switches[value]: ctx.out_line(", ", COLOR_KEYWORD) Dumper.dump_name_expr(ctx, op, target) else: return False return True # ---------------------------------------------------------------------- # Generate the instruction mnemonics def out_mnem(self, ctx): # Init output buffer postfix = "" ctx.out_mnem(16, postfix) # ---------------------------------------------------------------------- # Generate text representation of an instruction in 'ctx.insn' structure. # This function shouldn't change the database, flags or anything else. # All these actions should be performed only by u_emu() function. def notify_out_insn(self, ctx): ctx.out_mnemonic() ctx.out_one_operand(0) for i in xrange(1, 3): op = ctx.insn[i] if op.type == o_void: break ctx.out_symbol(",") ctx.out_char(" ") ctx.out_one_operand(i) ctx.set_gen_cmt() ctx.flush_outbuf() # ---------------------------------------------------------------------- def notify_out_label(self, ctx, label): """ The kernel is going to generate an instruction label line or a function header. args: ctx - output context label - label to output If returns value <0, then the kernel should not generate the label """ method = next((x for x in self.abc.methods if x["body"] is not None and x["body"]["pos"] == ctx.insn.ea), None) Dumper.dump_method(ctx, self.abc, method) exception = next((x for x in self.exceptions if x["target"] == ctx.insn.ea), None) Dumper.dump_exception(ctx, self.abc, exception) return True # ---------------------------------------------------------------------- def decode_instr(self, insn, opbyte): if (self.itable[opbyte].argtypes is None): return True if (self.itable[opbyte].name == "debug"): ubytev = Reader.read_byte(insn) insn.Op1.type = o_imm insn.Op1.dtype = dt_byte insn.Op1.value = ubytev index = Reader.read_encoded_u32(insn) length = len(self.abc.cpool.abc_strings) if (index >= length): print("Bad instr: %X - 0x%02X, %s, %X, %X" % (insn.ea, opbyte, self.itable[opbyte].name, index, length)) return False insn.Op2.type = o_idpspec0 insn.Op2.dtype = dt_string insn.Op2.value = index ubytev = Reader.read_byte(insn) insn.Op3.type = o_imm insn.Op3.dtype = dt_byte insn.Op3.value = ubytev uintv = Reader.read_encoded_u32(insn) insn.Op4.type = o_imm insn.Op4.dtype = dt_dword insn.Op4.value = uintv return True for i in xrange(len(self.itable[opbyte].argtypes)): op_type = self.itable[opbyte].argtypes[i] if (op_type == OperandType.CONSTANT_Unknown): print("Unknown operand: %s" % self.itable[opbyte].name) return False elif (op_type == OperandType.CONSTANT_ByteImm): bytev = Reader.read_byte(insn) insn.Op1.type = o_imm insn.Op1.dtype = dt_byte insn.Op1.value = bytev elif (op_type == OperandType.CONSTANT_UByteImm): ubytev = Reader.read_byte(insn) insn.Op1.type = o_imm insn.Op1.dtype = dt_byte insn.Op1.value = ubytev elif (op_type == OperandType.CONSTANT_IntImm): intv = Reader.read_encoded_u32(insn) insn.Op1.type = o_imm insn.Op1.dtype = dt_dword insn.Op1.value = intv insn.Op1.specval = self.FLo_SIGNED elif (op_type == OperandType.CONSTANT_UIntImm): uintv = Reader.read_encoded_u32(insn) if (i == 0): insn.Op1.type = o_imm insn.Op1.dtype = dt_dword insn.Op1.value = uintv else: insn.Op2.type = o_imm insn.Op2.dtype = dt_dword insn.Op2.value = uintv elif (op_type == OperandType.CONSTANT_Int or op_type == OperandType.CONSTANT_UInt or op_type == OperandType.CONSTANT_Double or op_type == OperandType.CONSTANT_String or op_type == OperandType.CONSTANT_Namespace or op_type == OperandType.CONSTANT_Multiname or op_type == OperandType.CONSTANT_Class or op_type == OperandType.CONSTANT_Method): index = Reader.read_encoded_u32(insn) if (op_type == OperandType.CONSTANT_Int): length = len(self.abc.cpool.abc_ints) elif (op_type == OperandType.CONSTANT_UInt): length = len(self.abc.cpool.abc_uints) elif (op_type == OperandType.CONSTANT_Double): length = len(self.abc.cpool.abc_doubles) elif (op_type == OperandType.CONSTANT_String): length = len(self.abc.cpool.abc_strings) elif (op_type == OperandType.CONSTANT_Namespace): length = len(self.abc.cpool.abc_namespaces) elif (op_type == OperandType.CONSTANT_Multiname): length = len(self.abc.cpool.abc_multinames) elif (op_type == OperandType.CONSTANT_Class): length = len(self.abc.cpool.abc_instances) elif (op_type == OperandType.CONSTANT_Method): length = len(self.abc.cpool.abc_methods) if (index >= length): print("Bad instr: %X - 0x%02X, %s, %X, %X" % (insn.ea, opbyte, self.itable[opbyte].name, index, length)) return False if (op_type == OperandType.CONSTANT_Int): insn.Op1.type = o_imm insn.Op1.dtype = dt_dword insn.Op1.value = self.abc.get_int(index) insn.Op1.specval = self.FLo_SIGNED elif (op_type == OperandType.CONSTANT_UInt): insn.Op1.type = o_imm insn.Op1.dtype = dt_dword insn.Op1.value = self.abc.get_uint(index) elif (op_type == OperandType.CONSTANT_Double): insn.Op1.type = o_imm insn.Op1.dtype = dt_qword insn.Op1.value = self.abc.get_double(index) elif (op_type == OperandType.CONSTANT_String): insn.Op1.type = o_idpspec0 insn.Op1.dtype = dt_string insn.Op1.value = index elif (op_type == OperandType.CONSTANT_Namespace): insn.Op1.type = o_idpspec1 insn.Op1.dtype = dt_string insn.Op1.value = index elif (op_type == OperandType.CONSTANT_Multiname): insn.Op1.type = o_idpspec2 insn.Op1.dtype = dt_string insn.Op1.value = index elif (op_type == OperandType.CONSTANT_Class): insn.Op1.type = o_idpspec3 insn.Op1.dtype = dt_string insn.Op1.value = index elif (op_type == OperandType.CONSTANT_Method): insn.Op1.type = o_near insn.Op1.dtype = dt_dword if (index >= len(self.abc.methods) or self.abc.methods[index]["body"] is None): print("Bad instr: %X - 0x%02X, %s, %X, %X" % (insn.ea, opbyte, self.itable[opbyte].name, index, length)) return False insn.Op1.addr = self.abc.methods[index]["body"]["pos"] elif (op_type == OperandType.CONSTANT_Label): delta = Reader.read_s24(insn) target = insn.ea + insn.size + delta insn.Op1.type = o_near insn.Op1.dtype = dt_dword insn.Op1.addr = target elif (op_type == OperandType.CONSTANT_DefaultLabel): delta = Reader.read_s24(insn) target = insn.ea + delta insn.Op1.type = o_near insn.Op1.dtype = dt_dword insn.Op1.addr = target elif (op_type == OperandType.CONSTANT_LabelsList): insn.Op2.type = o_idpspec4 insn.Op2.dtype = dt_dword insn.Op2.value = len(self.switches) length = Reader.read_encoded_u32(insn)+1 if (length > 0xFFFF): print("Bad instr: %X - 0x%02X, %s, %X, %X" % (insn.ea, opbyte, self.itable[opbyte].name, index, length)) return False targets = [] for label in xrange(length): delta = Reader.read_s24(insn) target = insn.ea + delta targets.append(target) self.switches.append(targets) return True def notify_ana(self, insn): """ Decodes an instruction into insn """ # take opcode byte opcode = insn.get_next_byte() # opcode supported? try: ins = self.itable[opcode] # set default itype insn.itype = getattr(self, "itype_" + ins.name) except: return False # call the decoder return insn.size if self.decode_instr(insn, opcode) else 0 # ---------------------------------------------------------------------- def init_instructions(self): class idef: """ Internal class that describes an instruction by: - instruction name - instruction decoding routine - canonical flags used by IDA """ def __init__(self, name, cf, argtypes, cmt = None): self.name = name self.cf = cf self.argtypes = argtypes self.cmt = cmt # # Instructions table # self.itable = { #0x00: 0x01: idef(name="bkpt", cmt="Breakpoint", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x02: idef(name="nop", cmt="No operation", cf = 0, argtypes=None), 0x03: idef(name="throw", cmt="Throw exception", cf = 0, argtypes=None), 0x04: idef(name="getsuper", cmt="Get parent class property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x05: idef(name="setsuper", cmt="Set parent class property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x06: idef(name="dxns", cmt="Set default XML namespace", cf = CF_USE1, argtypes=[OperandType.CONSTANT_String]), 0x07: idef(name="dxnslate", cmt="Set default XML namespace at runtime", cf = 0, argtypes=None), 0x08: idef(name="kill", cmt="Kill local register", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x09: idef(name="label", cmt="Target of a branch", cf = 0, argtypes=None), #0x0A: #0x0B: 0x0C: idef(name="ifnlt", cmt="Branch if not lower than", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x0D: idef(name="ifnle", cmt="Branch if not lower or equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x0E: idef(name="ifngt", cmt="Branch if not greater than", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x0F: idef(name="ifnge", cmt="Branch if not greater ot equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x10: idef(name="jump", cmt="Jump to location", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x11: idef(name="iftrue", cmt="Branch if true", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x12: idef(name="iffalse", cmt="Branch if false", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x13: idef(name="ifeq", cmt="Branch if equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x14: idef(name="ifne", cmt="Branch if not equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x15: idef(name="iflt", cmt="Branch if lower than", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x16: idef(name="ifle", cmt="Branch if lower or equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x17: idef(name="ifgt", cmt="Branch if greater than", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x18: idef(name="ifge", cmt="Branch if greater or equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x19: idef(name="ifstricteq", cmt="Branch if strict equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x1A: idef(name="ifstrictne", cmt="Branch if not strict equal", cf = CF_USE1 | CF_JUMP, argtypes=[OperandType.CONSTANT_Label]), 0x1B: idef(name="lookupswitch", cmt="Branch based on index", cf = CF_USE1 | CF_USE2 | CF_JUMP, argtypes=[OperandType.CONSTANT_DefaultLabel, OperandType.CONSTANT_LabelsList]), 0x1C: idef(name="pushwith", cmt="Push with onto scope stack", cf = 0, argtypes=None), 0x1D: idef(name="popscope", cmt="Pop from scope stack and discard value", cf = 0, argtypes=None), 0x1E: idef(name="nextname", cmt="Get name of next property", cf = 0, argtypes=None), 0x1F: idef(name="hasnext", cmt="Check if the object has more properties", cf = 0, argtypes=None), 0x20: idef(name="pushnull", cmt="Push null value on stack", cf = 0, argtypes=None), 0x21: idef(name="pushundefined", cmt="Push undefined value on stack", cf = 0, argtypes=None), 0x22: idef(name="pushuninitialized", cmt="Push float value on stack", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x23: idef(name="nextvalue", cmt="Get value of next property", cf = 0, argtypes=None), 0x24: idef(name="pushbyte", cmt="Push byte value on stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_ByteImm]), 0x25: idef(name="pushshort", cmt="Push short value on stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_IntImm]), 0x26: idef(name="pushtrue", cmt="Push true on stack", cf = 0, argtypes=None), 0x27: idef(name="pushfalse", cmt="Push false on stack", cf = 0, argtypes=None), 0x28: idef(name="pushnan", cmt="Push NaN value on stack", cf = 0, argtypes=None), 0x29: idef(name="pop", cmt="Pop top value from stack", cf = 0, argtypes=None), 0x2A: idef(name="dup", cmt="Duplicate value on stack", cf = 0, argtypes=None), 0x2B: idef(name="swap", cmt="Swap two values on top of the stack", cf = 0, argtypes=None), 0x2C: idef(name="pushstring", cmt="Push string value on the stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_String]), 0x2D: idef(name="pushint", cmt="Push integer value on the stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Int]), 0x2E: idef(name="pushuint", cmt="Push unsigned integer value on the stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UInt]), 0x2F: idef(name="pushdouble", cmt="Push double precision value on the stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Double]), 0x30: idef(name="pushscope", cmt="Push object on the scope stack", cf = 0, argtypes=None), 0x31: idef(name="pushnamespace", cmt="Push namespace on the stack", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Namespace]), 0x32: idef(name="hasnext2", cmt="Check if the object has more properties (register based)", cf = CF_USE1 | CF_USE2, argtypes=[OperandType.CONSTANT_UIntImm, OperandType.CONSTANT_UIntImm]), 0x33: idef(name="pushdecimal", cmt="Push decimal value on the stack", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x34: idef(name="pushdnan", cmt="Push decimal NaN value on the stack", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x35: idef(name="li8", cmt="Load 8bit integer value", cf = 0, argtypes=None), 0x36: idef(name="li16", cmt="Load 16bit integer value", cf = 0, argtypes=None), 0x37: idef(name="li32", cmt="Load 32bit integer value", cf = 0, argtypes=None), 0x38: idef(name="lf32", cmt="Load 32bit float value", cf = 0, argtypes=None), 0x39: idef(name="lf64", cmt="Load 64bit float value", cf = 0, argtypes=None), 0x3A: idef(name="si8", cmt="Store 8bit integer value", cf = 0, argtypes=None), 0x3B: idef(name="si16", cmt="Store 16bit integer value", cf = 0, argtypes=None), 0x3C: idef(name="si32", cmt="Store 32bit integer value", cf = 0, argtypes=None), 0x3D: idef(name="sf32", cmt="Store 32bit float value", cf = 0, argtypes=None), 0x3E: idef(name="sf64", cmt="Store 64bit float value", cf = 0, argtypes=None), #0x3F: 0x40: idef(name="newfunction", cmt="Create new Function object", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Method]), 0x41: idef(name="call", cmt="Call function on the stack", cf = CF_USE1 | CF_CALL, argtypes=[OperandType.CONSTANT_UIntImm]), 0x42: idef(name="construct", cmt="Call constructor function on the stack", cf = CF_USE1 | CF_CALL, argtypes=[OperandType.CONSTANT_UIntImm]), 0x43: idef(name="callmethod", cmt="Call method of object by dispatch id", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_UIntImm, OperandType.CONSTANT_UIntImm]), 0x44: idef(name="callstatic", cmt="Call method by method id in ABC file", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_Method, OperandType.CONSTANT_UIntImm]), 0x45: idef(name="callsuper", cmt="Call method on parent class", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_Multiname, OperandType.CONSTANT_UIntImm]), 0x46: idef(name="callproperty", cmt="Call property", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_Multiname, OperandType.CONSTANT_UIntImm]), 0x47: idef(name="returnvoid", cmt="Return from a method", cf = CF_STOP, argtypes=None), 0x48: idef(name="returnvalue", cmt="Return value from a method", cf = CF_STOP, argtypes=None), 0x49: idef(name="constructsuper", cmt="Call parent constructor of an object", cf = CF_USE1 | CF_CALL, argtypes=[OperandType.CONSTANT_UIntImm]), 0x4A: idef(name="constructprop", cmt="Construct a property of an object", cf = CF_USE1 | CF_USE2, argtypes=[OperandType.CONSTANT_Multiname, OperandType.CONSTANT_UIntImm]), 0x4B: idef(name="callsuperid", cmt="Call super id", cf = CF_CALL, argtypes=[OperandType.CONSTANT_Unknown]), 0x4C: idef(name="callproplex", cmt="Call property with null as this", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_Multiname, OperandType.CONSTANT_UIntImm]), 0x4D: idef(name="callinterface", cmt="Call interface", cf = CF_CALL, argtypes=[OperandType.CONSTANT_Unknown]), 0x4E: idef(name="callsupervoid", cmt="Call method on parent class, discard return value", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_Multiname, OperandType.CONSTANT_UIntImm]), 0x4F: idef(name="callpropvoid", cmt="Call property, discard return value", cf = CF_USE1 | CF_USE2 | CF_CALL, argtypes=[OperandType.CONSTANT_Multiname, OperandType.CONSTANT_UIntImm]), 0x50: idef(name="sxi1", cmt="Sign extend 1bit value to 32bits", cf = 0, argtypes=None), 0x51: idef(name="sxi8", cmt="Sign extend 8bit value to 32bits", cf = 0, argtypes=None), 0x52: idef(name="sxi16", cmt="Sign extend 16bit value to 32bits", cf = 0, argtypes=None), 0x53: idef(name="applytype", cmt="Apply type parameters", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), #0x54: 0x55: idef(name="newobject", cmt="Creates new object", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x56: idef(name="newarray", cmt="Creates new array", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x57: idef(name="newactivation", cmt="Creates new activation object", cf = 0, argtypes=None), 0x58: idef(name="newclass", cmt="Creates new class", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Class]), 0x59: idef(name="getdescendants", cmt="Get descendants", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x5A: idef(name="newcatch", cmt="Create new catch scope", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x5B: idef(name="deldescendants", cmt="Delete descendants", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), #0x5C: 0x5D: idef(name="findpropstrict", cmt="Search property in scope stack, error when not found", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x5E: idef(name="findproperty", cmt="Search property in scope stack, top object when not found", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x5F: idef(name="finddef", cmt="Search script level definition", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x60: idef(name="getlex", cmt="Find and get property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x61: idef(name="setproperty", cmt="Set property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x62: idef(name="getlocal", cmt="Get local register value", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x63: idef(name="setlocal", cmt="Set local register value", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x64: idef(name="getglobalscope", cmt="Get global scope", cf = 0, argtypes=None), 0x65: idef(name="getscopeobject", cmt="Get scope object", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UByteImm]), 0x66: idef(name="getproperty", cmt="Get property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x67: idef(name="getpropertylate", cmt="Get scope object on all levels", cf = 0, argtypes=None), 0x68: idef(name="initproperty", cmt="Initialize property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x69: idef(name="setpropertylate", cmt="Set property (stack based)", cf = 0, argtypes=None), 0x6A: idef(name="deleteproperty", cmt="Delete property", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x6B: idef(name="deletepropertylate", cmt="Delete property (stack based)", cf = 0, argtypes=None), 0x6C: idef(name="getslot", cmt="Get value of a slot", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x6D: idef(name="setslot", cmt="Set value of a slot", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x6E: idef(name="getglobalslot", cmt="Get value of slot on global scope", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x6F: idef(name="setglobalslot", cmt="Set value of slot on global scope", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x70: idef(name="convert_s", cmt="Convert value to string", cf = 0, argtypes=None), 0x71: idef(name="esc_xelem", cmt="Escape XML element", cf = 0, argtypes=None), 0x72: idef(name="esc_xattr", cmt="Escape XML attribute", cf = 0, argtypes=None), 0x73: idef(name="convert_i", cmt="Convert value to integer", cf = 0, argtypes=None), 0x74: idef(name="convert_u", cmt="Convert value to unsigned integer", cf = 0, argtypes=None), 0x75: idef(name="convert_d", cmt="Convert value to double", cf = 0, argtypes=None), 0x76: idef(name="convert_b", cmt="Convert value to boolean", cf = 0, argtypes=None), 0x77: idef(name="convert_o", cmt="Convert value to Object", cf = 0, argtypes=None), 0x78: idef(name="checkfilter", cmt="Check that object can have filter operation applied", cf = 0, argtypes=None), 0x79: idef(name="convert_m", cmt="Convert value to decimal", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x7A: idef(name="convert_m_p", cmt="Unary plus - coerce to numeric", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), #0x7B: #0x7C: #0x7D: #0x7E: #0x7F: 0x80: idef(name="coerce", cmt="Coerce value to specified type", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x81: idef(name="coerce_b", cmt="Coerce value to boolean", cf = 0, argtypes=None), 0x82: idef(name="coerce_a", cmt="Coerce value to any type", cf = 0, argtypes=None), 0x83: idef(name="coerce_i", cmt="Coerce value to integer", cf = 0, argtypes=None), 0x84: idef(name="coerce_d", cmt="Coerce value to double", cf = 0, argtypes=None), 0x85: idef(name="coerce_s", cmt="Coerce value to string", cf = 0, argtypes=None), 0x86: idef(name="astype", cmt="Return same value or null if not specified type", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0x87: idef(name="astypelate", cmt="Return same value or null if not specified type (stack based)", cf = 0, argtypes=None), 0x88: idef(name="coerce_u", cmt="Coerce value to unsigned integer", cf = 0, argtypes=None), 0x89: idef(name="coerce_o", cmt="Coerce value to Object", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), #0x8A: #0x8B: #0x8C: #0x8D: #0x8E: 0x8F: idef(name="negate_p", cmt="Negate value using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x90: idef(name="negate", cmt="Negate value", cf = 0, argtypes=None), 0x91: idef(name="increment", cmt="Increment value", cf = 0, argtypes=None), 0x92: idef(name="inclocal", cmt="Increment local register", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x93: idef(name="decrement", cmt="Decrement value", cf = 0, argtypes=None), 0x94: idef(name="declocal", cmt="Decrement local register", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0x95: idef(name="typeof", cmt="Get name of value type", cf = 0, argtypes=None), 0x96: idef(name="not", cmt="Boolean negate", cf = 0, argtypes=None), 0x97: idef(name="bitnot", cmt="Bitwise negate", cf = 0, argtypes=None), #0x98: #0x99: 0x9A: idef(name="concat", cmt="Concat", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x9B: idef(name="add_d", cmt="Add_d", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x9C: idef(name="increment_p", cmt="Increment value using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x9D: idef(name="inclocal_p", cmt="Increment local register using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x9E: idef(name="decrement_p", cmt="Decrement value using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0x9F: idef(name="declocal_p", cmt="Decrement local register using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0xA0: idef(name="add", cmt="Add two values", cf = 0, argtypes=None), 0xA1: idef(name="subtract", cmt="Subtract two values", cf = 0, argtypes=None), 0xA2: idef(name="multiply", cmt="Multiply two values", cf = 0, argtypes=None), 0xA3: idef(name="divide", cmt="Divide two values", cf = 0, argtypes=None), 0xA4: idef(name="modulo", cmt="Modulo divide two values", cf = 0, argtypes=None), 0xA5: idef(name="lshift", cmt="Bitwise left shift", cf = 0, argtypes=None), 0xA6: idef(name="rshift", cmt="Bitwise right shift", cf = 0, argtypes=None), 0xA7: idef(name="urshift", cmt="Unsigned bitwise right shift", cf = 0, argtypes=None), 0xA8: idef(name="bitand", cmt="Bitwise and", cf = 0, argtypes=None), 0xA9: idef(name="bitor", cmt="Bitwise or", cf = 0, argtypes=None), 0xAA: idef(name="bitxor", cmt="Bitwise xor", cf = 0, argtypes=None), 0xAB: idef(name="equals", cmt="Compare two values", cf = 0, argtypes=None), 0xAC: idef(name="strictequals", cmt="Strict compare two values", cf = 0, argtypes=None), 0xAD: idef(name="lessthan", cmt="Check that value is less than other value", cf = 0, argtypes=None), 0xAE: idef(name="lessequals", cmt="Check that value is less or equal than other value", cf = 0, argtypes=None), 0xAF: idef(name="greaterthan", cmt="Check that value is greater or equal than other value", cf = 0, argtypes=None), 0xB0: idef(name="greaterequals", cmt="Check that value is greater or equal than other value", cf = 0, argtypes=None), 0xB1: idef(name="instanceof", cmt="Check that type exists in object prototype chain", cf = 0, argtypes=None), 0xB2: idef(name="istype", cmt="Check that object is of specified type", cf = CF_USE1, argtypes=[OperandType.CONSTANT_Multiname]), 0xB3: idef(name="istypelate", cmt="Check that object is of specified type (stack based)", cf = 0, argtypes=None), 0xB4: idef(name="in", cmt="Check that object has named property", cf = 0, argtypes=None), 0xB5: idef(name="add_p", cmt="Add two values using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0xB6: idef(name="subtract_p", cmt="Subtract two values using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0xB7: idef(name="multiply_p", cmt="Multiply two values using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0xB8: idef(name="divide_p", cmt="Divide two values using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0xB9: idef(name="modulo_p", cmt="Modulo divide two values using number context", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), #0xBA: #0xBB: #0xBC: #0xBD: #0xBE: #0xBF: 0xC0: idef(name="increment_i", cmt="Increment integer value", cf = 0, argtypes=None), 0xC1: idef(name="decrement_i", cmt="Decrement integer value", cf = 0, argtypes=None), 0xC2: idef(name="inclocal_i", cmt="Increment local register integer value", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0xC3: idef(name="declocal_i", cmt="Decrement local register integer value", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0xC4: idef(name="negate_i", cmt="Negate integer value", cf = 0, argtypes=None), 0xC5: idef(name="add_i", cmt="Add two integer values", cf = 0, argtypes=None), 0xC6: idef(name="subtract_i", cmt="Subtract two integer values", cf = 0, argtypes=None), 0xC7: idef(name="multiply_i", cmt="Multiply two integer values", cf = 0, argtypes=None), #0xC8: #0xC9: #0xCA: #0xCB: #0xCC: #0xCD: #0xCE: #0xCF: 0xD0: idef(name="getlocal0", cmt="Get local register 0", cf = 0, argtypes=None), 0xD1: idef(name="getlocal1", cmt="Get local register 1", cf = 0, argtypes=None), 0xD2: idef(name="getlocal2", cmt="Get local register 2", cf = 0, argtypes=None), 0xD3: idef(name="getlocal3", cmt="Get local register 3", cf = 0, argtypes=None), 0xD4: idef(name="setlocal0", cmt="Set local register 0", cf = 0, argtypes=None), 0xD5: idef(name="setlocal1", cmt="Set local register 1", cf = 0, argtypes=None), 0xD6: idef(name="setlocal2", cmt="Set local register 2", cf = 0, argtypes=None), 0xD7: idef(name="setlocal3", cmt="Set local register 3", cf = 0, argtypes=None), #0xD8: #0xD9: #0xDA: #0xDB: #0xDC: #0xDD: #0xDE: #0xDF: #0xE0: #0xE1: #0xE2: #0xE3: #0xE4: #0xE5: #0xE6: #0xE7: #0xE8: #0xE9: #0xEA: #0xEB: #0xEC: #0xED: #0xEE: 0xEF: idef(name="debug", cmt="Debugging info", cf = CF_USE1 | CF_USE2 | CF_USE3 | CF_USE4, argtypes=[OperandType.CONSTANT_UByteImm, OperandType.CONSTANT_String, OperandType.CONSTANT_UByteImm, OperandType.CONSTANT_UIntImm]), 0xF0: idef(name="debugline", cmt="Debugging line number info", cf = CF_USE1, argtypes=[OperandType.CONSTANT_UIntImm]), 0xF1: idef(name="debugfile", cmt="Debugging file info", cf = CF_USE1, argtypes=[OperandType.CONSTANT_String]), 0xF2: idef(name="bkptline", cmt="Breakpoint on line", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), 0xF3: idef(name="timestamp", cmt="Timestamp", cf = 0, argtypes=[OperandType.CONSTANT_Unknown]), #0xF4: #0xF5: #0xF6: #0xF7: #0xF8: #0xF9: #0xFA: #0xFB: #0xFC: #0xFD: #0xFE: #0xFF: } # Now create an instruction table compatible with IDA processor module requirements Instructions = [] i = 0 for x in self.itable.values(): d = dict(name=x.name, feature=x.cf) if x.cmt is not None: d["cmt"] = x.cmt Instructions.append(d) setattr(self, "itype_" + x.name, i) i += 1 # icode of the last instruction + 1 self.instruc_end = len(Instructions) + 1 # Array of instructions self.instruc = Instructions # Icode of return instruction. It is ok to give any of possible return # instructions self.icode_return = self.itype_returnvoid # ---------------------------------------------------------------------- def init_registers(self): """This function parses the register table and creates corresponding ireg_XXX constants""" # Registers definition self.reg_names = [ # Fake segment registers "CS", "DS" ] # Create the ireg_XXXX constants for i in xrange(len(self.reg_names)): setattr(self, "ireg_" + self.reg_names[i], i) # Segment register information (use virtual CS and DS registers if your # processor doesn't have segment registers): self.reg_first_sreg = self.ireg_CS self.reg_last_sreg = self.ireg_DS # number of CS register self.reg_code_sreg = self.ireg_CS # number of DS register self.reg_data_sreg = self.ireg_DS # ---------------------------------------------------------------------- def __init__(self): processor_t.__init__(self) #self.PTRSZ = 4 # Assume PTRSZ = 4 by default self.init_instructions() self.init_registers() # ---------------------------------------------------------------------- def PROCESSOR_ENTRY(): return as3_processor_t()