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;
}
}