package com.android.reverse.smali; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.annotation.Nonnull; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.Token; import org.antlr.runtime.TokenSource; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.jf.dexlib2.writer.builder.DexBuilder; import org.jf.dexlib2.writer.io.FileDataStore; import org.jf.smali.LexerErrorInterface; import org.jf.smali.smaliFlexLexer; import org.jf.smali.smaliParser; import org.jf.smali.smaliTreeWalker; import com.android.reverse.collecter.ModuleContext; import com.android.reverse.util.Logger; import com.google.common.collect.Lists; public class DexFileBuilder { public static boolean buildDexFile(String smaliPath,String dexFileName) { int jobs = 8; boolean allowOdex = false; boolean verboseErrors = false; boolean printTokens = false; int apiLevel = ModuleContext.getInstance().getApiLevel(); try { LinkedHashSet<File> filesToProcess = new LinkedHashSet<File>(); File argFile = new File(smaliPath); if (!argFile.exists()) { throw new RuntimeException("Cannot find file or directory \"" + smaliPath + "\""); } if (argFile.isDirectory()) { getSmaliFilesInDir(argFile, filesToProcess); } boolean errors = false; final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel); ExecutorService executor = Executors.newFixedThreadPool(jobs); List<Future<Boolean>> tasks = Lists.newArrayList(); final boolean finalVerboseErrors = verboseErrors; final boolean finalPrintTokens = printTokens; final boolean finalAllowOdex = allowOdex; final int finalApiLevel = apiLevel; for (final File file : filesToProcess) { tasks.add(executor.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return assembleSmaliFile(file, dexBuilder, finalVerboseErrors, finalPrintTokens, finalAllowOdex, finalApiLevel); } })); } for (Future<Boolean> task : tasks) { while (true) { try { if (!task.get()) { errors = true; } } catch (InterruptedException ex) { continue; } break; } } executor.shutdown(); if (errors) { Logger.log("build the dexfile error0"); return false; } dexBuilder.writeTo(new FileDataStore(new File(dexFileName))); Logger.log("build the dexfile ok"); return true; } catch (RuntimeException ex) { Logger.log("build the dexfile error1"); return false; } catch (Throwable ex) { Logger.log("build the dexfile error2"); return false; } } private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) { File[] files = dir.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { getSmaliFilesInDir(file, smaliFiles); } else if (file.getName().endsWith(".smali")) { smaliFiles.add(file); } } } } private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors, boolean printTokens, boolean allowOdex, int apiLevel) throws Exception { CommonTokenStream tokens; LexerErrorInterface lexer; FileInputStream fis = new FileInputStream(smaliFile.getAbsolutePath()); InputStreamReader reader = new InputStreamReader(fis, "UTF-8"); lexer = new smaliFlexLexer(reader); ((smaliFlexLexer) lexer).setSourceFile(smaliFile); tokens = new CommonTokenStream((TokenSource) lexer); if (printTokens) { tokens.getTokens(); for (int i = 0; i < tokens.size(); i++) { Token token = tokens.get(i); if (token.getChannel() == smaliParser.HIDDEN) { continue; } System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText()); } } smaliParser parser = new smaliParser(tokens); parser.setVerboseErrors(verboseErrors); parser.setAllowOdex(allowOdex); parser.setApiLevel(apiLevel); smaliParser.smali_file_return result = parser.smali_file(); if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { return false; } CommonTree t = result.getTree(); CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); treeStream.setTokenStream(tokens); smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); dexGen.setVerboseErrors(verboseErrors); dexGen.setDexBuilder(dexBuilder); dexGen.smali_file(); return dexGen.getNumberOfSyntaxErrors() == 0; } }