package com.meituan.robust.utils; import com.meituan.robust.Constants; import com.meituan.robust.autopatch.ClassMapping; import com.meituan.robust.autopatch.Config; import com.meituan.robust.autopatch.NameManger; import com.meituan.robust.autopatch.ReadMapping; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.regex.Matcher; import javassist.CtClass; import javassist.CtMethod; import javassist.CtPrimitiveType; import static com.meituan.robust.Constants.PACKNAME_END; import static com.meituan.robust.Constants.PACKNAME_START; import static com.meituan.robust.autopatch.Config.classPool; import static com.meituan.robust.autopatch.Config.invokeSuperMethodMap; /** * Created by mivanzhang on 17/2/8. */ public class SmaliUitils { private static SmaliUitils instance; public static SmaliUitils getInstance() { if (instance == null) { instance = new SmaliUitils(); } return instance; } private SmaliUitils() { } public void dealObscureInSmali() { File diretory = new File(Config.robustGenerateDirectory + "classout" + File.separator + Config.patchPackageName.replaceAll("\\.", Matcher.quoteReplacement(File.separator))); if (!diretory.isDirectory() || diretory == null) { throw new RuntimeException(Config.robustGenerateDirectory + Config.patchPackageName.replaceAll(".", Matcher.quoteReplacement(File.separator)) + " contains no smali file error!! "); } List<File> smaliFileList = covertPathToFile(Config.robustGenerateDirectory + "classout" + File.separator, Config.newlyAddedClassNameList); for (File file : diretory.listFiles()) { smaliFileList.add(file); } for (File file : smaliFileList) { BufferedWriter writer = null; BufferedReader reader = null; StringBuilder fileContent = new StringBuilder(); try { reader = new BufferedReader(new FileReader(file)); String line; int lineNo = 1; // 一次读入一行,直到读入null为文件结束 while ((line = reader.readLine()) != null) { // 显示行号 fileContent.append(dealWithSmaliLine(line, JavaUtils.getFullClassNameFromFile(file.getPath())) + "\n"); lineNo++; } writer = new BufferedWriter(new FileWriter(file)); writer.write(fileContent.toString()); writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (writer != null) { try { writer.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } } private List<File> covertPathToFile(String directory, List<String> packNameList) { if (packNameList == null) { return new ArrayList<>(); } List<File> fileList = new ArrayList<>(); for (String packname : packNameList) { fileList.add(new File(directory + packname.replaceAll("\\.", Matcher.quoteReplacement(File.separator)) + ".smali")); } return fileList; } private String dealWithSmaliLine(final String line, String fullClassName) { if (null == line || line.length() < 1 || line.startsWith("#")) { return line; } String result = invokeSuperMethodInSmali(line, fullClassName); int packageNameIndex; int previousPackageNameIndex = 0; List<String> packageNameList = getPackNameFromSmaliLine(result); Collections.sort(packageNameList, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o2.length() - o1.length(); } }); for (int index = 0; packageNameList != null && index < packageNameList.size(); index++) { if (result.indexOf(packageNameList.get(index)) != result.lastIndexOf(packageNameList.get(index))) { packageNameIndex = result.indexOf(packageNameList.get(index), previousPackageNameIndex); previousPackageNameIndex = packageNameIndex + packageNameList.get(index).length(); } else { packageNameIndex = result.indexOf(packageNameList.get(index)); } //invoke-virtual {v0, v5, v6, p0}, Landroid/support/v4/app/LoaderManager;->initLoader(ILandroid/os/Bundle;Landroid/support/v4/app/bi;)Landroid/support/v4/content/Loader; if (result.contains("invoke") && (packageNameIndex + packageNameList.get(index).length() + 3 < result.length()) && result.substring(packageNameIndex + packageNameList.get(index).length() + 1, packageNameIndex + packageNameList.get(index).length() + 3).equals("->")) { //方法调用的替换 result = result.replace(result.substring(packageNameIndex + packageNameList.get(index).length() + 3, result.indexOf(")") + 1), getObscuredMethodSignure(result.substring(packageNameIndex + packageNameList.get(index).length() + 3), packageNameList.get(index).replaceAll("/", "\\."))); } else if (result.contains("->") && (result.indexOf("(") == -1) && ((packageNameIndex + packageNameList.get(index).length() + 3) < result.length())) { // 字段处理 //sget-object v4, Lcom/sankuai/meituan/fingerprint/FingerprintConfig;->accelerometerInfoList:Ljava/util/List; String fieldName = result.substring(packageNameIndex + packageNameList.get(index).length() + 3, result.lastIndexOf(":")); result = result.replace(fieldName, getObscuredMemberName(packageNameList.get(index).replaceAll("/", "\\."), fieldName)); } } for (int index = 0; packageNameList != null && index < packageNameList.size(); index++) { result = result.replace(packageNameList.get(index), getObscuredClassName(packageNameList.get(index))); } return result; } private boolean isInStaticRobustMethod = false; private String invokeSuperMethodInSmali(final String line, String fullClassName) { if (line.startsWith(".method public static staticRobust")) { isInStaticRobustMethod = true; } String result = line; String returnType; List<CtMethod> invokeSuperMethodList = invokeSuperMethodMap.get(NameManger.getInstance().getPatchNameMap().get(fullClassName)); if (isInStaticRobustMethod && line.contains(Constants.SMALI_INVOKE_VIRTUAL_COMMAND)) { for (CtMethod ctMethod : invokeSuperMethodList) { //java method signure if ((ctMethod.getName().replaceAll("\\.", "/") + ctMethod.getSignature().subSequence(0, ctMethod.getSignature().indexOf(")") + 1)).equals(getMethodSignureInSmaliLine(line))) { result = line.replace(Constants.SMALI_INVOKE_VIRTUAL_COMMAND, Constants.SMALI_INVOKE_SUPER_COMMAND); try { if (!ctMethod.getReturnType().isPrimitive()) { returnType = "L" + ctMethod.getReturnType().getName().replaceAll("\\.", "/"); } else { returnType = String.valueOf(((CtPrimitiveType) ctMethod.getReturnType()).getDescriptor()); } if (NameManger.getInstance().getPatchNameMap().get(fullClassName).equals(fullClassName)) { result = result.replace("p0", "p1"); } String fullClassNameInSmali = ctMethod.getDeclaringClass().getClassPool().get(fullClassName).getSuperclass().getName().replaceAll("\\.", "/"); result = result.replace(result.substring(result.indexOf(PACKNAME_START) + 1, result.indexOf(PACKNAME_END)), fullClassNameInSmali); result = result.substring(0, result.indexOf(")") + 1) + returnType; if (!ctMethod.getReturnType().isPrimitive()) { result += ";"; } } catch (Exception e) { throw new RuntimeException(e); } } } } if (isInStaticRobustMethod && line.startsWith(".end method")) { isInStaticRobustMethod = false; } // System.out.println(" result is " + result); return result; } private String getMethodSignureInSmaliLine(String s) { return s.substring(s.indexOf("->") + 2, s.indexOf(")") + 1); } private List<String> getPackNameFromSmaliLine(String line) { ArrayList<String> packageNameList = new ArrayList<>(); if (null == line) { return packageNameList; } int startIndex; int endIndex; for (; line != null && line.length() > 0; ) { startIndex = 0; for (; ; ) { startIndex = line.indexOf(Constants.PACKNAME_START, startIndex + 1); if (startIndex < 0 || !Character.isLetter(line.charAt(startIndex - 1)) || line.lastIndexOf(Constants.PACKNAME_START) == startIndex) { break; } } endIndex = line.indexOf(Constants.PACKNAME_END, startIndex); if (startIndex < 0 || endIndex < 0) { break; } packageNameList.add(line.substring(startIndex + 1, endIndex)); line = line.substring(endIndex); } // if (packageNameList.size() > 0) // System.out.println("getPackNameFromSmaliLine " + packageNameList); return packageNameList; } public static void main(String[] args) { SmaliUitils smaliUitils=new SmaliUitils(); smaliUitils.getObscuredMethodSignure("invokeReflectConstruct(Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;","com.meituan.second"); } private String getObscuredMethodSignure(final String line, String className) { if (className.endsWith(Constants.PATCH_SUFFIX) && Config.modifiedClassNameList.contains(className.substring(0, className.indexOf(Constants.PATCH_SUFFIX)))) { className = className.substring(0, className.indexOf(Constants.PATCH_SUFFIX)); } StringBuilder methodSignureBuilder = new StringBuilder(); methodSignureBuilder.append(line.substring(0, line.indexOf("(") + 1)); String parameter = line.substring(line.indexOf("("), line.indexOf(")") + 1); int endIndex = line.indexOf(")"); String methodSigure = line.substring(0, endIndex + 1); //invokeReflectConstruct(Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object; boolean isArray=false; for (int index = line.indexOf("(") + 1; index < endIndex; index++) { if (Constants.PACKNAME_START.equals(String.valueOf(methodSigure.charAt(index))) && methodSigure.contains(Constants.PACKNAME_END)) { methodSignureBuilder.append(methodSigure.substring(index + 1, methodSigure.indexOf(Constants.PACKNAME_END, index)).replaceAll("/", "\\.")); if(isArray){ methodSignureBuilder.append("[]"); isArray=false; } index = methodSigure.indexOf(";", index); methodSignureBuilder.append(","); } if (Constants.PRIMITIVE_TYPE.contains(String.valueOf(methodSigure.charAt(index)))) { switch (methodSigure.charAt(index)) { case 'Z': methodSignureBuilder.append("boolean"); break; case 'C': methodSignureBuilder.append("char"); break; case 'B': methodSignureBuilder.append("byte"); break; case 'S': methodSignureBuilder.append("short"); break; case 'I': methodSignureBuilder.append("int"); break; case 'J': methodSignureBuilder.append("long"); break; case 'F': methodSignureBuilder.append("float"); break; case 'D': methodSignureBuilder.append("double"); break; case 'V': methodSignureBuilder.append("void"); break; default: break; } if(isArray){ methodSignureBuilder.append("[]"); isArray=false; } methodSignureBuilder.append(","); } if (Constants.ARRAY_TYPE.equals(String.valueOf(methodSigure.charAt(index)))) { isArray=true; } } List<String> returnTypeList = gePackageNameFromSmaliLine(line.substring(endIndex + 1)); if (String.valueOf(methodSignureBuilder.charAt(methodSignureBuilder.toString().length() - 1)).equals(",")) methodSignureBuilder.deleteCharAt(methodSignureBuilder.toString().length() - 1); methodSignureBuilder.append(")"); String obscuredMethodSignure = methodSignureBuilder.toString(); String obscuredMethodName = getObscuredMemberName(className, ReadMapping.getInstance().getMethodSignureWithReturnType(returnTypeList.get(0), obscuredMethodSignure)); obscuredMethodSignure = obscuredMethodName + parameter; // System.out.println("getObscuredMethodSignure is "+obscuredMethodSignure.substring(0, obscuredMethodSignure.indexOf("(")) + parameter); return obscuredMethodSignure.substring(0, obscuredMethodSignure.indexOf("(")) + parameter; } private List<String> gePackageNameFromSmaliLine(String smaliLine) { List<String> packageNameList = new ArrayList<>(); for (int index = 0; index < smaliLine.length(); index++) { if (Constants.PACKNAME_START.equals(String.valueOf(smaliLine.charAt(index))) && smaliLine.indexOf(Constants.PACKNAME_END) != -1) { packageNameList.add(smaliLine.substring(index + 1, smaliLine.indexOf(Constants.PACKNAME_END, index)).replaceAll("/", "\\.")); index = smaliLine.indexOf(";", index); } if (Constants.PRIMITIVE_TYPE.contains(String.valueOf(smaliLine.charAt(index)))) { switch (smaliLine.charAt(index)) { case 'Z': packageNameList.add("boolean"); break; case 'C': packageNameList.add("char"); break; case 'B': packageNameList.add("byte"); break; case 'S': packageNameList.add("short"); break; case 'I': packageNameList.add("int"); break; case 'J': packageNameList.add("long"); break; case 'F': packageNameList.add("float"); break; case 'D': packageNameList.add("double"); break; case 'V': packageNameList.add("void"); break; default: break; } } } return packageNameList; } private String getObscuredMemberName(String className, String memberName) { ClassMapping classMapping = ReadMapping.getInstance().getClassMapping(className); if (classMapping == null) { System.out.println("Warning: getObscuredMemberName class name " + className + " member name is " + memberName + " robust can not find in mapping!!! "); return JavaUtils.eradicatReturnType(memberName); } while (classMapping != null && !"java.lang.Object".equals(classMapping.getClassName())) { if (classMapping.getMemberMapping().get(memberName) != null) { return classMapping.getMemberMapping().get(memberName); } else { try { CtClass superClass = classPool.get(classMapping.getClassName()).getSuperclass(); while (ReadMapping.getInstance().getClassMapping(superClass.getName()) == null && !"java.lang.Object".equals(superClass.getName())) { superClass = superClass.getSuperclass(); } classMapping = ReadMapping.getInstance().getClassMapping(superClass.getName()); } catch (Exception e) { throw new RuntimeException(e); } } } return JavaUtils.eradicatReturnType(memberName); } private String getObscuredClassName(String className) { ClassMapping classMapping = ReadMapping.getInstance().getClassMapping(className.replaceAll("/", "\\.")); if (null == classMapping || classMapping.getValueName() == null) { return className; } return classMapping.getValueName().replaceAll("\\.","/"); } }