/* * Copyright (C) 2010-2016 JPEXS, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.jpexs.decompiler.flash.abc.avm2; import com.jpexs.decompiler.flash.IdentifiersDeobfuscation; import com.jpexs.decompiler.flash.SWF; import com.jpexs.decompiler.flash.abc.RenameType; import com.jpexs.decompiler.graph.DottedChain; import com.jpexs.helpers.Helper; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; /** * * @author JPEXS */ public class AVM2Deobfuscation { private static final int DEFAULT_FOO_SIZE = 10; public static final String VALID_FIRST_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; public static final String VALID_NEXT_CHARACTERS = VALID_FIRST_CHARACTERS + "0123456789"; public static final String VALID_NS_CHARACTERS = ".:$"; private final SWF swf; private final AVM2ConstantPool constants; private final Map<String, Integer> usageTypesCount = new HashMap<>(); public static final DottedChain FLASH_PROXY = new DottedChain(new String[]{"flash", "utils", "flash_proxy"}, ""); public static final DottedChain BUILTIN = new DottedChain(new String[]{"-"}, ""); public AVM2Deobfuscation(SWF swf, AVM2ConstantPool constants) { this.swf = swf; this.constants = constants; } private boolean isValidNSPart(String s) { boolean isValid = true; if (IdentifiersDeobfuscation.isReservedWord2(s)) { isValid = false; } if (isValid) { for (int i = 0; i < s.length(); i++) { if (s.charAt(i) > 127) { isValid = false; break; } } } if (isValid) { Pattern pat = Pattern.compile("^([" + Pattern.quote(VALID_FIRST_CHARACTERS) + "]" + "[" + Pattern.quote(VALID_FIRST_CHARACTERS + VALID_NEXT_CHARACTERS + VALID_NS_CHARACTERS) + "]*)*$"); if (!pat.matcher(s).matches()) { isValid = false; } } return isValid; } public DottedChain builtInNs(String ns) { if (ns == null) { return null; } if (ns.equals("http://www.adobe.com/2006/actionscript/flash/proxy")) { return FLASH_PROXY; } if (ns.equals("http://adobe.com/AS3/2006/builtin")) { return BUILTIN; } return null; } private String fooString(HashMap<DottedChain, DottedChain> deobfuscated, String orig, boolean firstUppercase, String usageType, RenameType renameType) { if (usageType == null) { usageType = "name"; } String ret = null; boolean found; int rndSize = DEFAULT_FOO_SIZE; do { found = false; if (renameType == RenameType.TYPENUMBER) { ret = Helper.getNextId(usageType, usageTypesCount, true); } else if (renameType == RenameType.RANDOMWORD) { ret = IdentifiersDeobfuscation.fooString(firstUppercase, rndSize); } if (swf.as3StringConstantExists(ret) || IdentifiersDeobfuscation.isReservedWord2(ret) || deobfuscated.containsValue(DottedChain.parseWithSuffix(ret))) { found = true; rndSize++; } } while (found); deobfuscated.put(DottedChain.parseWithSuffix(orig), DottedChain.parseWithSuffix(ret)); return ret; } public int deobfuscatePackageName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, RenameType renameType) { if (strIndex <= 0) { return strIndex; } String s = constants.getString(strIndex); if (builtInNs(s) != null) { return strIndex; } boolean isValid = isValidNSPart(s); if (!isValid) { DottedChain sChain = DottedChain.parseWithSuffix(s); DottedChain newName; if (namesMap.containsKey(sChain)) { newName = namesMap.get(sChain); constants.setString(strIndex, newName.toRawString()); } else { List<String> ret = new ArrayList<>(); for (int p = 0; p < sChain.size(); p++) { String part = sChain.get(p); if (!isValidNSPart(part)) { ret.add(fooString(namesMap, part, false, "package", renameType)); } else { ret.add(part); } } newName = new DottedChain(ret); namesMap.put(sChain, newName); } if (stringUsages.contains(strIndex)) { strIndex = constants.addString(newName.toRawString()); } else { constants.setString(strIndex, newName.toRawString()); } } return strIndex; } public int deobfuscateName(Map<Integer, String> stringUsageTypes, Set<Integer> stringUsages, Set<Integer> namespaceUsages, HashMap<DottedChain, DottedChain> namesMap, int strIndex, boolean firstUppercase, RenameType renameType) { if (strIndex <= 0) { return strIndex; } String s = constants.getString(strIndex); boolean isValid = true; if (IdentifiersDeobfuscation.isReservedWord2(s)) { isValid = false; } if (isValid) { for (int i = 0; i < s.length(); i++) { if (s.charAt(i) > 127) { isValid = false; break; } } } if (isValid) { Pattern pat = Pattern.compile("^[" + Pattern.quote(VALID_FIRST_CHARACTERS) + "]" + "[" + Pattern.quote(VALID_FIRST_CHARACTERS + VALID_NEXT_CHARACTERS) + "]*$"); if (!pat.matcher(s).matches()) { isValid = false; } } if (!isValid) { DottedChain newname; DottedChain sChain = DottedChain.parseWithSuffix(s); if (namesMap.containsKey(sChain)) { newname = namesMap.get(sChain); } else { String str = fooString(namesMap, constants.getString(strIndex), firstUppercase, stringUsageTypes.get(strIndex), renameType); newname = DottedChain.parseWithSuffix(str); } if (stringUsages.contains(strIndex) || namespaceUsages.contains(strIndex)) { // this name is already referenced as String strIndex = constants.addString(s); // add new index } constants.setString(strIndex, newname.toRawString()); if (!namesMap.containsKey(sChain)) { namesMap.put(sChain, DottedChain.parseWithSuffix(constants.getString(strIndex))); } } return strIndex; } }