/* * Copyright (C) 2010-2014 JPEXS * * 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 . */ package com.jpexs.decompiler.flash.abc; import com.jpexs.decompiler.flash.EventListener; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.AVM2Deobfuscation; import com.jpexs.decompiler.flash.abc.avm2.ConstantPool; import com.jpexs.decompiler.flash.abc.avm2.UnknownInstructionCode; import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2Instruction; import com.jpexs.decompiler.flash.abc.avm2.instructions.executing.CallPropertyIns; import com.jpexs.decompiler.flash.abc.avm2.instructions.stack.PushStringIns; import com.jpexs.decompiler.flash.abc.types.ABCException; import com.jpexs.decompiler.flash.abc.types.ClassInfo; import com.jpexs.decompiler.flash.abc.types.InstanceInfo; import com.jpexs.decompiler.flash.abc.types.MetadataInfo; import com.jpexs.decompiler.flash.abc.types.MethodBody; import com.jpexs.decompiler.flash.abc.types.MethodInfo; import com.jpexs.decompiler.flash.abc.types.Multiname; import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.NamespaceSet; import com.jpexs.decompiler.flash.abc.types.ScriptInfo; import com.jpexs.decompiler.flash.abc.types.traits.Trait; import com.jpexs.decompiler.flash.abc.types.traits.TraitClass; import com.jpexs.decompiler.flash.abc.types.traits.TraitFunction; import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter; import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst; import com.jpexs.decompiler.flash.abc.types.traits.Traits; import com.jpexs.decompiler.flash.abc.usages.ClassNameMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.ConstVarNameMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.ConstVarTypeMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.DefinitionUsage; import com.jpexs.decompiler.flash.abc.usages.ExtendsMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.ImplementsMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MethodBodyMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MethodNameMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage; import com.jpexs.decompiler.flash.abc.usages.MultinameUsage; import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage; import com.jpexs.decompiler.flash.helpers.collections.MyEntry; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.helpers.MemoryInputStream; import com.jpexs.helpers.utf8.Utf8PrintWriter; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; public class ABC { public int major_version = 46; public int minor_version = 16; public ConstantPool constants = new ConstantPool(); public List method_info = new ArrayList<>(); public List metadata_info = new ArrayList<>(); public List instance_info = new ArrayList<>(); public List class_info = new ArrayList<>(); public List script_info = new ArrayList<>(); public List bodies = new ArrayList<>(); private List bodyIdxFromMethodIdx = new ArrayList<>(); public long[] stringOffsets; public static final int MINORwithDECIMAL = 17; protected Set listeners = new HashSet<>(); private static final Logger logger = Logger.getLogger(ABC.class.getName()); private final AVM2Deobfuscation deobfuscation; public SWF swf; public ABCContainerTag parentTag; public ABC(SWF swf) { this.deobfuscation = null; this.swf = swf; constants.constant_double.add(null); constants.constant_int.add(null); constants.constant_uint.add(null); constants.constant_string.add(null); constants.constant_multiname.add(null); constants.constant_namespace.add(null); constants.constant_namespace_set.add(null); } public int addMethodBody(MethodBody body) { bodies.add(body); if (body.method_info >= bodyIdxFromMethodIdx.size()) { int newlen = body.method_info + 1; int oldlen = bodyIdxFromMethodIdx.size(); for (int i = oldlen; i < newlen; i++) { bodyIdxFromMethodIdx.add(-1); } bodyIdxFromMethodIdx.set(body.method_info, bodies.size() - 1); } return bodies.size() - 1; } public int addMethodInfo(MethodInfo mi) { method_info.add(mi); return method_info.size() - 1; } public void addEventListener(EventListener listener) { listeners.add(listener); } public void removeEventListener(EventListener listener) { listeners.remove(listener); } protected void informListeners(String event, Object data) { for (EventListener listener : listeners) { listener.handleEvent(event, data); } } public int removeTraps() throws InterruptedException { int rem = 0; for (int s = 0; s < script_info.size(); s++) { rem += script_info.get(s).removeTraps(s, this, ""); } return rem; } public int removeDeadCode() throws InterruptedException { int rem = 0; for (MethodBody body : bodies) { rem += body.removeDeadCode(constants, null/*FIXME*/, method_info.get(body.method_info)); } return rem; } public void restoreControlFlow() throws InterruptedException { for (MethodBody body : bodies) { body.restoreControlFlow(constants, null/*FIXME*/, method_info.get(body.method_info)); } } public Set getNsStringUsages() { Set ret = new HashSet<>(); for (int n = 1; n < constants.getNamespaceCount(); n++) { ret.add(constants.getNamespace(n).name_index); } return ret; } public Set getStringUsages() { Set ret = new HashSet<>(); for (MethodBody body : bodies) { for (AVM2Instruction ins : body.code.code) { for (int i = 0; i < ins.definition.operands.length; i++) { if (ins.definition.operands[i] == AVM2Code.DAT_STRING_INDEX) { ret.add(ins.operands[i]); } } } } return ret; } private void setStringUsageType(Map ret, int strIndex, String usageType) { if (ret.containsKey(strIndex)) { if (!"name".equals(usageType)) { if (!ret.get(strIndex).equals(usageType)) { ret.put(strIndex, "name"); } } } else { ret.put(strIndex, usageType); } } private void getStringUsageTypes(Map ret, Traits traits, boolean classesOnly) { for (Trait t : traits.traits) { int strIndex = constants.getMultiname(t.name_index).name_index; String usageType = ""; if (t instanceof TraitClass) { TraitClass tc = (TraitClass) t; getStringUsageTypes(ret, class_info.get(tc.class_info).static_traits, classesOnly); getStringUsageTypes(ret, instance_info.get(tc.class_info).instance_traits, classesOnly); if (instance_info.get(tc.class_info).name_index != 0) { setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).name_index).name_index, "class"); } if (instance_info.get(tc.class_info).super_index != 0) { setStringUsageType(ret, constants.getMultiname(instance_info.get(tc.class_info).super_index).name_index, "class"); } usageType = "class"; } if (t instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t; usageType = "method"; MethodBody body = findBody(tm.method_info); if (body != null) { getStringUsageTypes(ret, body.traits, classesOnly); } } if (t instanceof TraitFunction) { TraitFunction tf = (TraitFunction) t; MethodBody body = findBody(tf.method_info); if (body != null) { getStringUsageTypes(ret, body.traits, classesOnly); } usageType = "function"; } if (t instanceof TraitSlotConst) { TraitSlotConst ts = (TraitSlotConst) t; if (ts.isVar()) { usageType = "var"; } if (ts.isConst()) { usageType = "const"; } } if (usageType.equals("class") || (!classesOnly)) { setStringUsageType(ret, strIndex, usageType); } } } public void getStringUsageTypes(Map ret, boolean classesOnly) { for (ScriptInfo script : script_info) { getStringUsageTypes(ret, script.traits, classesOnly); } } public void renameMultiname(int multinameIndex, String newname) { if (multinameIndex <= 0 || multinameIndex >= constants.getMultinameCount()) { throw new IllegalArgumentException("Multiname with index " + multinameIndex + " does not exist"); } Set stringUsages = getStringUsages(); Set namespaceUsages = getNsStringUsages(); int strIndex = constants.getMultiname(multinameIndex).name_index; if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { //name is used elsewhere as string literal strIndex = constants.getStringId(newname, true); constants.getMultiname(multinameIndex).name_index = strIndex; } else { constants.setString(strIndex, newname); } } public void deobfuscateIdentifiers(HashMap namesMap, RenameType renameType, boolean classesOnly) { Set stringUsages = getStringUsages(); Set namespaceUsages = getNsStringUsages(); Map stringUsageTypes = new HashMap<>(); informListeners("deobfuscate", "Getting usage types..."); getStringUsageTypes(stringUsageTypes, classesOnly); for (int i = 0; i < instance_info.size(); i++) { informListeners("deobfuscate", "class " + i + "/" + instance_info.size()); InstanceInfo insti = instance_info.get(i); if (insti.name_index != 0) { constants.getMultiname(insti.name_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.name_index).name_index, true, renameType); if (constants.getMultiname(insti.name_index).namespace_index != 0) { constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(constants.getMultiname(insti.name_index).namespace_index).name_index, renameType); } } if (insti.super_index != 0) { constants.getMultiname(insti.super_index).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(insti.super_index).name_index, true, renameType); } } if (classesOnly) { return; } for (int i = 1; i < constants.getMultinameCount(); i++) { informListeners("deobfuscate", "name " + i + "/" + constants.getMultinameCount()); constants.getMultiname(i).name_index = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, constants.getMultiname(i).name_index, false, renameType); } for (int i = 1; i < constants.getNamespaceCount(); i++) { informListeners("deobfuscate", "namespace " + i + "/" + constants.getNamespaceCount()); if (constants.getNamespace(i).kind != Namespace.KIND_PACKAGE) { //only packages continue; } constants.getNamespace(i).name_index = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, constants.getNamespace(i).name_index, renameType); } //process reflection using getDefinitionByName too for (MethodBody body : bodies) { for (int ip = 0; ip < body.code.code.size(); ip++) { if (body.code.code.get(ip).definition instanceof CallPropertyIns) { int mIndex = body.code.code.get(ip).operands[0]; if (mIndex > 0) { Multiname m = constants.getMultiname(mIndex); if (m.getNameWithNamespace(constants).equals("flash.utils.getDefinitionByName")) { if (ip > 0) { if (body.code.code.get(ip - 1).definition instanceof PushStringIns) { int strIndex = body.code.code.get(ip - 1).operands[0]; String fullname = constants.getString(strIndex); String pkg = ""; String name = fullname; if (fullname.contains(".")) { pkg = fullname.substring(0, fullname.lastIndexOf('.')); name = fullname.substring(fullname.lastIndexOf('.') + 1); } if (!pkg.isEmpty()) { int pkgStrIndex = constants.getStringId(pkg, true); pkgStrIndex = deobfuscation.deobfuscatePackageName(stringUsageTypes, stringUsages, namesMap, pkgStrIndex, renameType); pkg = constants.getString(pkgStrIndex); } int nameStrIndex = constants.getStringId(name, true); nameStrIndex = deobfuscation.deobfuscateName(stringUsageTypes, stringUsages, namespaceUsages, namesMap, nameStrIndex, true, renameType); name = constants.getString(nameStrIndex); String fullChanged = ""; if (!pkg.isEmpty()) { fullChanged = pkg + "."; } fullChanged += name; strIndex = constants.getStringId(fullChanged, true); body.code.code.get(ip - 1).operands[0] = strIndex; } } } } } } } } public ABC(ABCInputStream ais, SWF swf, ABCContainerTag tag) throws IOException { this.swf = swf; this.parentTag = tag; minor_version = ais.readU16("minor_version"); major_version = ais.readU16("major_version"); logger.log(Level.FINE, "ABC minor_version: {0}, major_version: {1}", new Object[]{minor_version, major_version}); constants = new ConstantPool(); deobfuscation = new AVM2Deobfuscation(constants); ais.newDumpLevel("constant_pool", "cpool_info"); //constant integers int constant_int_pool_count = ais.readU30("int_count"); constants.constant_int = new ArrayList<>(constant_int_pool_count); if (constant_int_pool_count > 0) { constants.addInt(0); } if (constant_int_pool_count > 1) { ais.newDumpLevel("integers", "integer[]"); for (int i = 1; i < constant_int_pool_count; i++) { //index 0 not used. Values 1..n-1 constants.addInt(ais.readS32("int")); } ais.endDumpLevel(); } //constant unsigned integers int constant_uint_pool_count = ais.readU30("uint_count"); constants.constant_uint = new ArrayList<>(constant_uint_pool_count); if (constant_uint_pool_count > 0) { constants.addUInt(0); } if (constant_uint_pool_count > 1) { ais.newDumpLevel("uintegers", "uinteger[]"); for (int i = 1; i < constant_uint_pool_count; i++) { //index 0 not used. Values 1..n-1 constants.addUInt(ais.readU32("uint")); } ais.endDumpLevel(); } //constant double int constant_double_pool_count = ais.readU30("double_count"); constants.constant_double = new ArrayList<>(constant_double_pool_count); if (constant_double_pool_count > 0) { constants.addDouble(0); } if (constant_double_pool_count > 1) { ais.newDumpLevel("doubles", "double[]"); for (int i = 1; i < constant_double_pool_count; i++) { //index 0 not used. Values 1..n-1 constants.addDouble(ais.readDouble("double")); } ais.endDumpLevel(); } //constant decimal if (minor_version >= MINORwithDECIMAL) { int constant_decimal_pool_count = ais.readU30("decimal_count"); constants.constant_decimal = new ArrayList<>(constant_decimal_pool_count); if (constant_decimal_pool_count > 0) { constants.addDecimal(null); } if (constant_decimal_pool_count > 1) { ais.newDumpLevel("decimals", "decimal[]"); for (int i = 1; i < constant_decimal_pool_count; i++) { //index 0 not used. Values 1..n-1 constants.addDecimal(ais.readDecimal("decimal")); } ais.endDumpLevel(); } } else { constants.constant_decimal = new ArrayList<>(0); } //constant string int constant_string_pool_count = ais.readU30("string_count"); constants.constant_string = new ArrayList<>(constant_string_pool_count); stringOffsets = new long[constant_string_pool_count]; if (constant_string_pool_count > 0) { constants.addString(""); } if (constant_string_pool_count > 1) { ais.newDumpLevel("strings", "string[]"); for (int i = 1; i < constant_string_pool_count; i++) { //index 0 not used. Values 1..n-1 long pos = ais.getPosition(); constants.addString(ais.readString("string")); stringOffsets[i] = pos; } ais.endDumpLevel(); } //constant namespace int constant_namespace_pool_count = ais.readU30("namespace_count"); constants.constant_namespace = new ArrayList<>(constant_namespace_pool_count); if (constant_namespace_pool_count > 0) { constants.addNamespace(null); } if (constant_namespace_pool_count > 1) { ais.newDumpLevel("namespaces", "namespace[]"); for (int i = 1; i < constant_namespace_pool_count; i++) { //index 0 not used. Values 1..n-1 constants.addNamespace(ais.readNamespace("namespace")); } ais.endDumpLevel(); } //constant namespace set int constant_namespace_set_pool_count = ais.readU30("ns_set_count"); constants.constant_namespace_set = new ArrayList<>(constant_namespace_set_pool_count); if (constant_namespace_set_pool_count > 0) { constants.addNamespaceSet(null); } if (constant_namespace_set_pool_count > 1) { ais.newDumpLevel("ns_sets", "ns_set[]"); for (int i = 1; i < constant_namespace_set_pool_count; i++) { //index 0 not used. Values 1..n-1 ais.newDumpLevel("ns_set_infos", "ns_set_info[]"); constants.addNamespaceSet(new NamespaceSet()); int namespace_count = ais.readU30("count"); constants.getNamespaceSet(i).namespaces = new int[namespace_count]; for (int j = 0; j < namespace_count; j++) { constants.getNamespaceSet(i).namespaces[j] = ais.readU30("ns"); } ais.endDumpLevel(); } ais.endDumpLevel(); } //constant multiname int constant_multiname_pool_count = ais.readU30("multiname_count"); constants.constant_multiname = new ArrayList<>(constant_multiname_pool_count); if (constant_multiname_pool_count > 0) { constants.addMultiname(null); } if (constant_multiname_pool_count > 1) { ais.newDumpLevel("multiname", "multinames[]"); for (int i = 1; i < constant_multiname_pool_count; i++) { //index 0 not used. Values 1..n-1 constants.addMultiname(ais.readMultiname("multiname")); } ais.endDumpLevel(); } ais.endDumpLevel(); //cpool_info //method info int methods_count = ais.readU30("methods_count"); method_info = new ArrayList<>(methods_count); //MethodInfo[methods_count]; bodyIdxFromMethodIdx = new ArrayList<>(methods_count); //[methods_count]; for (int i = 0; i < methods_count; i++) { method_info.add(ais.readMethodInfo("method")); bodyIdxFromMethodIdx.add(-1); } //metadata info int metadata_count = ais.readU30("metadata_count"); metadata_info = new ArrayList<>(metadata_count); for (int i = 0; i < metadata_count; i++) { int name_index = ais.readU30("name_index"); int values_count = ais.readU30("values_count"); int[] keys = new int[values_count]; for (int v = 0; v < values_count; v++) { keys[v] = ais.readU30("key"); } int[] values = new int[values_count]; for (int v = 0; v < values_count; v++) { values[v] = ais.readU30("value"); } metadata_info.add(new MetadataInfo(name_index, keys, values)); } int class_count = ais.readU30("class_count"); instance_info = new ArrayList<>(class_count); //InstanceInfo[class_count]; for (int i = 0; i < class_count; i++) { instance_info.add(ais.readInstanceInfo("instance")); } class_info = new ArrayList<>(class_count); //ClassInfo[class_count]; for (int i = 0; i < class_count; i++) { ais.newDumpLevel("class", "class_info"); ClassInfo ci = new ClassInfo(); ci.cinit_index = ais.readU30("cinit_index"); ci.static_traits = ais.readTraits("static_traits"); class_info.add(ci); ais.endDumpLevel(); } int script_count = ais.readU30("script_count"); script_info = new ArrayList<>(script_count); //ScriptInfo[script_count]; for (int i = 0; i < script_count; i++) { ais.newDumpLevel("script", "script_info"); ScriptInfo si = new ScriptInfo(); si.init_index = ais.readU30("init_index"); si.traits = ais.readTraits("traits"); script_info.add(si); ais.endDumpLevel(); } int bodies_count = ais.readU30("bodies_count"); bodies = new ArrayList<>(bodies_count); //MethodBody[bodies_count]; for (int i = 0; i < bodies_count; i++) { ais.newDumpLevel("method_body", "method_body_info"); MethodBody mb = new MethodBody(); mb.method_info = ais.readU30("method_info"); mb.max_stack = ais.readU30("max_stack"); mb.max_regs = ais.readU30("max_regs"); mb.init_scope_depth = ais.readU30("init_scope_depth"); mb.max_scope_depth = ais.readU30("max_scope_depth"); int code_length = ais.readU30("code_length"); mb.codeBytes = ais.readBytes(code_length, "code"); try { ABCInputStream ais2 = new ABCInputStream(new MemoryInputStream(mb.codeBytes)); mb.code = new AVM2Code(ais2); } catch (UnknownInstructionCode re) { mb.code = new AVM2Code(); Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, re); } mb.code.compact(); int ex_count = ais.readU30("ex_count"); mb.exceptions = new ABCException[ex_count]; for (int j = 0; j < ex_count; j++) { ABCException abce = new ABCException(); abce.start = ais.readU30("start"); abce.end = ais.readU30("end"); abce.target = ais.readU30("target"); abce.type_index = ais.readU30("type_index"); abce.name_index = ais.readU30("name_index"); mb.exceptions[j] = abce; } mb.traits = ais.readTraits("traits"); bodies.add(mb); method_info.get(mb.method_info).setBody(mb); bodyIdxFromMethodIdx.set(mb.method_info, i); ais.endDumpLevel(); } loadNamespaceMap(); /*for(int i=0;i(), false, t ,new ArrayList(), null); } catch (InterruptedException ex) { Logger.getLogger(ABC.class.getName()).log(Level.SEVERE, null, ex); } System.out.println(""+t.toString()); } System.exit(0);*/ } public void saveToStream(OutputStream os) throws IOException { ABCOutputStream aos = new ABCOutputStream(os); aos.writeU16(minor_version); aos.writeU16(major_version); aos.writeU30(constants.getIntCount()); for (int i = 1; i < constants.getIntCount(); i++) { aos.writeS32(constants.getInt(i)); } aos.writeU30(constants.getUIntCount()); for (int i = 1; i < constants.getUIntCount(); i++) { aos.writeU32(constants.getUInt(i)); } aos.writeU30(constants.getDoubleCount()); for (int i = 1; i < constants.getDoubleCount(); i++) { aos.writeDouble(constants.getDouble(i)); } if (minor_version >= MINORwithDECIMAL) { aos.writeU30(constants.getDecimalCount()); for (int i = 1; i < constants.getDecimalCount(); i++) { aos.writeDecimal(constants.getDecimal(i)); } } aos.writeU30(constants.getStringCount()); for (int i = 1; i < constants.getStringCount(); i++) { aos.writeString(constants.getString(i)); } aos.writeU30(constants.getNamespaceCount()); for (int i = 1; i < constants.getNamespaceCount(); i++) { aos.writeNamespace(constants.getNamespace(i)); } aos.writeU30(constants.getNamespaceSetCount()); for (int i = 1; i < constants.getNamespaceSetCount(); i++) { aos.writeU30(constants.getNamespaceSet(i).namespaces.length); for (int j = 0; j < constants.getNamespaceSet(i).namespaces.length; j++) { aos.writeU30(constants.getNamespaceSet(i).namespaces[j]); } } aos.writeU30(constants.getMultinameCount()); for (int i = 1; i < constants.getMultinameCount(); i++) { aos.writeMultiname(constants.getMultiname(i)); } aos.writeU30(method_info.size()); for (MethodInfo mi : method_info) { aos.writeMethodInfo(mi); } aos.writeU30(metadata_info.size()); for (MetadataInfo mi : metadata_info) { aos.writeU30(mi.name_index); aos.writeU30(mi.values.length); for (int j = 0; j < mi.values.length; j++) { aos.writeU30(mi.keys[j]); } for (int j = 0; j < mi.values.length; j++) { aos.writeU30(mi.values[j]); } } aos.writeU30(class_info.size()); for (InstanceInfo ii : instance_info) { aos.writeInstanceInfo(ii); } for (ClassInfo ci : class_info) { aos.writeU30(ci.cinit_index); aos.writeTraits(ci.static_traits); } aos.writeU30(script_info.size()); for (ScriptInfo si : script_info) { aos.writeU30(si.init_index); aos.writeTraits(si.traits); } aos.writeU30(bodies.size()); for (MethodBody mb : bodies) { aos.writeU30(mb.method_info); aos.writeU30(mb.max_stack); aos.writeU30(mb.max_regs); aos.writeU30(mb.init_scope_depth); aos.writeU30(mb.max_scope_depth); byte[] codeBytes = mb.code.getBytes(); aos.writeU30(codeBytes.length); aos.write(codeBytes); aos.writeU30(mb.exceptions.length); for (int j = 0; j < mb.exceptions.length; j++) { aos.writeU30(mb.exceptions[j].start); aos.writeU30(mb.exceptions[j].end); aos.writeU30(mb.exceptions[j].target); aos.writeU30(mb.exceptions[j].type_index); aos.writeU30(mb.exceptions[j].name_index); } aos.writeTraits(mb.traits); } } public MethodBody findBody(int methodInfo) { if (methodInfo < 0) { return null; } return method_info.get(methodInfo).getBody(); } public int findBodyIndex(int methodInfo) { if (methodInfo == -1) { return -1; } return bodyIdxFromMethodIdx.get(methodInfo); } public MethodBody findBodyByClassAndName(String className, String methodName) { for (int i = 0; i < instance_info.size(); i++) { if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList()))) { for (Trait t : instance_info.get(i).instance_traits.traits) { if (t instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; if (methodName.equals(t2.getName(this).getName(constants, new ArrayList()))) { for (MethodBody body : bodies) { if (body.method_info == t2.method_info) { return body; } } } } } //break; } } for (int i = 0; i < class_info.size(); i++) { if (className.equals(constants.getMultiname(instance_info.get(i).name_index).getName(constants, new ArrayList()))) { for (Trait t : class_info.get(i).static_traits.traits) { if (t instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter t2 = (TraitMethodGetterSetter) t; if (methodName.equals(t2.getName(this).getName(constants, new ArrayList()))) { for (MethodBody body : bodies) { if (body.method_info == t2.method_info) { return body; } } } } } //break; } } return null; } public boolean isStaticTraitId(int classIndex, int traitId) { if (traitId < class_info.get(classIndex).static_traits.traits.size()) { return true; } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { return false; } else { return true; //Can be class or instance initializer } } public Trait findTraitByTraitId(int classIndex, int traitId) { if (classIndex == -1) { return null; } if (traitId < class_info.get(classIndex).static_traits.traits.size()) { return class_info.get(classIndex).static_traits.traits.get(traitId); } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { traitId -= class_info.get(classIndex).static_traits.traits.size(); return instance_info.get(classIndex).instance_traits.traits.get(traitId); } else { return null; //Can be class or instance initializer } } public int findMethodIdByTraitId(int classIndex, int traitId) { if (classIndex == -1) { return -1; } if (traitId < class_info.get(classIndex).static_traits.traits.size()) { if (class_info.get(classIndex).static_traits.traits.get(traitId) instanceof TraitMethodGetterSetter) { return ((TraitMethodGetterSetter) class_info.get(classIndex).static_traits.traits.get(traitId)).method_info; } else { return -1; } } else if (traitId < class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size()) { traitId -= class_info.get(classIndex).static_traits.traits.size(); if (instance_info.get(classIndex).instance_traits.traits.get(traitId) instanceof TraitMethodGetterSetter) { return ((TraitMethodGetterSetter) instance_info.get(classIndex).instance_traits.traits.get(traitId)).method_info; } else { return -1; } } else { traitId -= class_info.get(classIndex).static_traits.traits.size() + instance_info.get(classIndex).instance_traits.traits.size(); if (traitId == 0) { return instance_info.get(classIndex).iinit_index; } else if (traitId == 1) { return class_info.get(classIndex).cinit_index; } else { return -1; } } } /* Map from multiname index of namespace value to namespace name**/ private HashMap namespaceMap; private void loadNamespaceMap() { namespaceMap = new HashMap<>(); for (ScriptInfo si : script_info) { for (Trait t : si.traits.traits) { if (t instanceof TraitSlotConst) { TraitSlotConst s = ((TraitSlotConst) t); if (s.isNamespace()) { String key = constants.getNamespace(s.value_index).getName(constants); //assume not null String val = constants.getMultiname(s.name_index).getNameWithNamespace(constants); namespaceMap.put(key, val); } } } } } public String nsValueToName(String value) { if (namespaceMap.containsKey(value)) { return namespaceMap.get(value); } else { String ns = deobfuscation.builtInNs(value); if (ns == null) { return ""; } else { return ns; } } } public List> getScriptPacks() { List> ret = new ArrayList<>(); for (int i = 0; i < script_info.size(); i++) { ret.addAll(script_info.get(i).getPacks(this, i)); } return ret; } public void dump(OutputStream os) { Utf8PrintWriter output; output = new Utf8PrintWriter(os); constants.dump(output); for (int i = 0; i < method_info.size(); i++) { output.println("MethodInfo[" + i + "]:" + method_info.get(i).toString(constants, new ArrayList())); } for (int i = 0; i < metadata_info.size(); i++) { output.println("MetadataInfo[" + i + "]:" + metadata_info.get(i).toString(constants)); } for (int i = 0; i < instance_info.size(); i++) { output.println("InstanceInfo[" + i + "]:" + instance_info.get(i).toString(this, new ArrayList())); } for (int i = 0; i < class_info.size(); i++) { output.println("ClassInfo[" + i + "]:" + class_info.get(i).toString(this, new ArrayList())); } for (int i = 0; i < script_info.size(); i++) { output.println("ScriptInfo[" + i + "]:" + script_info.get(i).toString(this, new ArrayList())); } for (int i = 0; i < bodies.size(); i++) { output.println("MethodBody[" + i + "]:"); //+ bodies[i].toString(this, constants, method_info)); } } private void checkMultinameUsedInMethod(List abcTags, int multinameIndex, int methodInfo, List ret, int classIndex, int traitIndex, boolean isStatic, boolean isInitializer, Traits traits, int parentTraitIndex) { for (int p = 0; p < method_info.get(methodInfo).param_types.length; p++) { if (method_info.get(methodInfo).param_types[p] == multinameIndex) { ret.add(new MethodParamsMultinameUsage(abcTags, this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); break; } } if (method_info.get(methodInfo).ret_type == multinameIndex) { ret.add(new MethodReturnTypeMultinameUsage(abcTags, this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); } MethodBody body = findBody(methodInfo); if (body != null) { findMultinameUsageInTraits(abcTags, body.traits, multinameIndex, isStatic, classIndex, ret, traitIndex); for (ABCException e : body.exceptions) { if ((e.name_index == multinameIndex) || (e.type_index == multinameIndex)) { ret.add(new MethodBodyMultinameUsage(abcTags, this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); return; } } for (AVM2Instruction ins : body.code.code) { for (int o = 0; o < ins.definition.operands.length; o++) { if (ins.definition.operands[o] == AVM2Code.DAT_MULTINAME_INDEX) { if (ins.operands[o] == multinameIndex) { ret.add(new MethodBodyMultinameUsage(abcTags, this, multinameIndex, classIndex, traitIndex, isStatic, isInitializer, traits, parentTraitIndex)); return; } } } } } } private void findMultinameUsageInTraits(List abcTags, Traits traits, int multinameIndex, boolean isStatic, int classIndex, List ret, int parentTraitIndex) { for (int t = 0; t < traits.traits.size(); t++) { if (traits.traits.get(t) instanceof TraitSlotConst) { TraitSlotConst tsc = (TraitSlotConst) traits.traits.get(t); if (tsc.name_index == multinameIndex) { ret.add(new ConstVarNameMultinameUsage(abcTags, this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); } if (tsc.type_index == multinameIndex) { ret.add(new ConstVarTypeMultinameUsage(abcTags, this, multinameIndex, classIndex, t, isStatic, traits, parentTraitIndex)); } } if (traits.traits.get(t) instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) traits.traits.get(t); if (tmgs.name_index == multinameIndex) { ret.add(new MethodNameMultinameUsage(abcTags, this, multinameIndex, classIndex, t, isStatic, false, traits, parentTraitIndex)); } checkMultinameUsedInMethod(abcTags, multinameIndex, tmgs.method_info, ret, classIndex, t, isStatic, false, traits, parentTraitIndex); } } } public List findMultinameDefinition(List abcTags, int multinameIndex) { List usages = findMultinameUsage(abcTags, multinameIndex); List ret = new ArrayList<>(); for (MultinameUsage u : usages) { if (u instanceof DefinitionUsage) { ret.add(u); } } return ret; } public List findMultinameUsage(List abcTags, int multinameIndex) { List ret = new ArrayList<>(); if (multinameIndex == 0) { return ret; } for (int c = 0; c < instance_info.size(); c++) { if (instance_info.get(c).name_index == multinameIndex) { ret.add(new ClassNameMultinameUsage(abcTags, this, multinameIndex, c)); } if (instance_info.get(c).super_index == multinameIndex) { ret.add(new ExtendsMultinameUsage(abcTags, this, multinameIndex, c)); } for (int i = 0; i < instance_info.get(c).interfaces.length; i++) { if (instance_info.get(c).interfaces[i] == multinameIndex) { ret.add(new ImplementsMultinameUsage(abcTags, this, multinameIndex, c)); } } checkMultinameUsedInMethod(abcTags, multinameIndex, instance_info.get(c).iinit_index, ret, c, 0, false, true, null, -1); checkMultinameUsedInMethod(abcTags, multinameIndex, class_info.get(c).cinit_index, ret, c, 0, true, true, null, -1); findMultinameUsageInTraits(abcTags, instance_info.get(c).instance_traits, multinameIndex, false, c, ret, -1); findMultinameUsageInTraits(abcTags, class_info.get(c).static_traits, multinameIndex, true, c, ret, -1); } loopm: for (int m = 1; m < constants.getMultinameCount(); m++) { if (constants.getMultiname(m).kind == Multiname.TYPENAME) { if (constants.getMultiname(m).qname_index == multinameIndex) { ret.add(new TypeNameMultinameUsage(abcTags, this, m)); continue; } for (int mp : constants.getMultiname(m).params) { if (mp == multinameIndex) { ret.add(new TypeNameMultinameUsage(abcTags, this, m)); continue loopm; } } } } return ret; } public int findMethodInfoByName(int classId, String methodName) { if (classId > -1) { for (Trait t : instance_info.get(classId).instance_traits.traits) { if (t instanceof TraitMethodGetterSetter) { if (t.getName(this).getName(constants, new ArrayList()).equals(methodName)) { return ((TraitMethodGetterSetter) t).method_info; } } } } return -1; } public int findMethodBodyByName(int classId, String methodName) { if (classId > -1) { for (Trait t : instance_info.get(classId).instance_traits.traits) { if (t instanceof TraitMethodGetterSetter) { if (t.getName(this).getName(constants, new ArrayList()).equals(methodName)) { return findBodyIndex(((TraitMethodGetterSetter) t).method_info); } } } } return -1; } public int findMethodBodyByName(String className, String methodName) { int classId = findClassByName(className); return findMethodBodyByName(classId, methodName); } public int findClassByName(String name) { for (int c = 0; c < instance_info.size(); c++) { String s = constants.getMultiname(instance_info.get(c).name_index).getNameWithNamespace(constants); if (name.equals(s)) { return c; } } return -1; } public List findScriptPacksByPath(String name) { List ret = new ArrayList<>(); List> allPacks = getScriptPacks(); if (name.endsWith(".**") || name.equals("**") || name.endsWith(".++") || name.equals("++")) { name = name.substring(0, name.length() - 2); for (MyEntry en : allPacks) { if (en.key.toString().startsWith(name)) { ret.add(en.value); } } } else if (name.endsWith(".*") || name.equals("*") || name.endsWith(".+") || name.equals("+")) { name = name.substring(0, name.length() - 1); for (MyEntry en : allPacks) { if (en.key.toString().startsWith(name)) { String rem = name.isEmpty() ? en.key.toString() : en.key.toString().substring(name.length()); if (!rem.contains(".")) { ret.add(en.value); } } } } else { ScriptPack p = findScriptPackByPath(name); if (p != null) { ret.add(p); } } return ret; } public ScriptPack findScriptPackByPath(String name) { List> packs = getScriptPacks(); for (MyEntry en : packs) { if (en.key.toString().equals(name)) { return en.value; } } return null; } private void removeClassFromTraits(Traits traits, int index) { for (Trait t : traits.traits) { if (t instanceof TraitClass) { TraitClass tc = (TraitClass) t; removeClassFromTraits(instance_info.get(tc.class_info).instance_traits, index); removeClassFromTraits(class_info.get(tc.class_info).static_traits, index); if (tc.class_info > index) { tc.class_info--; } } } } public void removeClass(int index) { for (MethodBody b : bodies) { for (AVM2Instruction ins : b.code.code) { for (int i = 0; i < ins.definition.operands.length; i++) { if (ins.definition.operands[i] == AVM2Code.DAT_CLASS_INDEX) { if (ins.operands[i] > index) { ins.operands[i]--; } } } } } for (ScriptInfo si : script_info) { removeClassFromTraits(si.traits, index); } instance_info.remove(index); class_info.remove(index); } private void removeMethodFromTraits(Traits traits, int index) { for (Trait t : traits.traits) { if (t instanceof TraitClass) { TraitClass tc = (TraitClass) t; removeMethodFromTraits(instance_info.get(tc.class_info).instance_traits, index); removeMethodFromTraits(class_info.get(tc.class_info).static_traits, index); } if (t instanceof TraitMethodGetterSetter) { TraitMethodGetterSetter tmgs = (TraitMethodGetterSetter) t; if (tmgs.method_info > index) { tmgs.method_info--; } } if (t instanceof TraitFunction) { TraitFunction tf = (TraitFunction) t; if (tf.method_info > index) { tf.method_info--; } } } } public void removeMethod(int index) { int bindex = -1; for (int b = 0; b < bodies.size(); b++) { if (bodies.get(b).method_info == index) { bodies.remove(b); bindex = b; b--; } } for (MethodBody b : bodies) { if (b.method_info > index) { b.method_info--; } for (AVM2Instruction ins : b.code.code) { for (int i = 0; i < ins.definition.operands.length; i++) { if (ins.definition.operands[i] == AVM2Code.DAT_METHOD_INDEX) { if (ins.operands[i] > index) { ins.operands[i]--; } } } } } for (int c = 0; c < instance_info.size(); c++) { InstanceInfo ii = instance_info.get(c); if (ii.iinit_index > index) { ii.iinit_index--; } ClassInfo ci = class_info.get(c); if (ci.cinit_index > index) { ci.cinit_index--; } } for (ScriptInfo si : script_info) { if (si.init_index > index) { si.init_index--; } removeMethodFromTraits(si.traits, index); } if (bindex > -1) { for (int mi = 0; mi < bodyIdxFromMethodIdx.size(); mi++) { if (bodyIdxFromMethodIdx.get(mi) > bindex) { bodyIdxFromMethodIdx.set(mi, bodyIdxFromMethodIdx.get(mi) - 1); } } } bodyIdxFromMethodIdx.remove(index); method_info.remove(index); } public void pack() { for (int c = 0; c < instance_info.size(); c++) { if (instance_info.get(c).deleted) { removeClass(c); c--; } } for (int m = 0; m < method_info.size(); m++) { if (method_info.get(m).deleted) { removeMethod(m); m--; } } } }