package com.googlecode.dex2jar.tools; import java.io.File; import java.io.IOException; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import org.objectweb.asm.ClassReader; import org.objectweb.asm.tree.ClassNode; import com.googlecode.d2j.dex.writer.DexFileWriter; import com.googlecode.d2j.jasmin.Jasmins; import com.googlecode.d2j.map.*; import com.googlecode.d2j.reader.DexFileReader; import com.googlecode.d2j.reader.zip.ZipUtil; import com.googlecode.d2j.smali.Smali; import com.googlecode.d2j.visitors.DexFileVisitor; import com.googlecode.dex2jar.tools.BaseCmd.Syntax; @Syntax(cmd = "d2j-dex-mapping", syntax = "[options] <dex>", desc = "remap names in dex file") public class DexRemapCmd extends BaseCmd { @Opt(opt = "o", longOpt = "output", description = "the name of the dex file that will be written. The default is out.dex", argName = "FILE") private File output; @Opt(opt = "L", longOpt = "library", description = "libraries we want load ',' separated") private String library; @Opt(opt = "C", longOpt = "config-file", description = "the config file generated by proguard", argName = "conf") private Path config; public static void main(String[] args) { new DexRemapCmd().doMain(args); } void V(String fmt, Object... args) { System.err.println(String.format(fmt, args)); } @Override protected void doCommandLine() throws Exception { if (remainingArgs.length < 1) { System.err.println("ERROR: no file to process"); return; } if (config == null) { System.err.println("ERROR: please set the config file -L,--config-file"); return; } if (output == null) { output = new File("out.dex"); } final InheritanceTree tree = buildRenameTree(); DexFileWriter fw = new DexFileWriter(); DexFileVisitor fv = new DexFileVisitor(fw) { @Override public void visitEnd() { // } }; fv = new DexMappingAdapter(tree, fv); for (String s : remainingArgs) { V(">> Remap %s -> %s", s, output); DexFileReader reader = new DexFileReader(ZipUtil.readDex(new File(s))); reader.accept(fv); } fw.visitEnd(); byte[] data = fw.toByteArray(); Files.write(output.toPath(),data); } private InheritanceTree buildRenameTree() throws IOException { final InheritanceTree tree = new InheritanceTree(); AutoDetectSourceProcess processer = new AutoDetectSourceProcess() { DexInheritanceFileVisitor dfv = new DexInheritanceFileVisitor(tree); AsmInheritanceClassVisitor acv = new AsmInheritanceClassVisitor(tree); @Override protected void onDex(Path file) throws IOException { DexFileReader r = new DexFileReader(ZipUtil.readDex(Files.readAllBytes(file))); r.accept(dfv, DexFileReader.SKIP_CODE | DexFileReader.SKIP_ANNOTATION | DexFileReader.SKIP_FIELD_CONSTANT); } @Override protected void onClass(Path file) throws IOException { ClassReader cr = new ClassReader(Files.readAllBytes(file)); cr.accept(acv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); } @Override protected void onJasmin(Path file) throws IOException { ClassNode cn = Jasmins.parse(file); cn.accept(acv); } @Override protected void onSmali(Path file) throws IOException { Smali smali = new Smali(); smali.smaliFile(file, dfv); } }; for (String lib : library.split(",")) { V("<< Load Lib %s", lib); tree.updateFrom(lib, true); processer.process(lib); } for (String s : remainingArgs) { V("<< Load App %s", s); tree.updateFrom(s, false); processer.process(s); } V("<< Link for rename"); tree.link(); V("<< Apply config file %s", config); try (Reader configReadre = Files.newBufferedReader(config, StandardCharsets.UTF_8)) { new ProguardMappingParser().parse(configReadre, tree); } return tree; } }