diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java index 0650ab3c..916147ec 100644 --- a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -58,6 +58,7 @@ import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.iface.reference.TypeReference; import org.jf.dexlib2.util.InstructionUtil; import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.util.ReferenceUtil; import org.jf.dexlib2.writer.io.DeferredOutputStream; import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory; import org.jf.dexlib2.writer.io.DexDataStore; @@ -196,6 +197,33 @@ public abstract class DexWriter< classSection.getItems().size() * ClassDefItem.ITEM_SIZE; } + @Nonnull + public List getMethodReferences() { + List methodReferences = Lists.newArrayList(); + for (Entry methodReference: methodSection.getItems()) { + methodReferences.add(ReferenceUtil.getMethodDescriptor(methodReference.getKey())); + } + return methodReferences; + } + + @Nonnull + public List getFieldReferences() { + List fieldReferences = Lists.newArrayList(); + for (Entry fieldReference: fieldSection.getItems()) { + fieldReferences.add(ReferenceUtil.getFieldDescriptor(fieldReference.getKey())); + } + return fieldReferences; + } + + @Nonnull + public List getTypeReferences() { + List classReferences = Lists.newArrayList(); + for (Entry typeReference: typeSection.getItems()) { + classReferences.add(typeReference.getKey().toString()); + } + return classReferences; + } + public void writeTo(@Nonnull DexDataStore dest) throws IOException { this.writeTo(dest, MemoryDeferredOutputStream.getFactory()); } diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java index 31f65a88..98fb7a1f 100644 --- a/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java @@ -28,7 +28,9 @@ package org.jf.smali; +import com.google.common.base.Strings; import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.Token; import org.antlr.runtime.TokenSource; @@ -113,6 +115,15 @@ public class main { boolean printTokens = false; boolean experimental = false; + boolean listMethods = false; + String methodListFilename = null; + + boolean listFields = false; + String fieldListFilename = null; + + boolean listTypes = false; + String typeListFilename = null; + int apiLevel = 15; String outputDexFile = "out.dex"; @@ -153,6 +164,18 @@ public class main { case 'j': jobs = Integer.parseInt(commandLine.getOptionValue("j")); break; + case 'm': + listMethods = true; + methodListFilename = commandLine.getOptionValue("m"); + break; + case 'f': + listFields = true; + fieldListFilename = commandLine.getOptionValue("f"); + break; + case 't': + listTypes = true; + typeListFilename = commandLine.getOptionValue("t"); + break; case 'V': verboseErrors = true; break; @@ -232,6 +255,27 @@ public class main { System.exit(1); } + if (listMethods) { + if (Strings.isNullOrEmpty(methodListFilename)) { + methodListFilename = outputDexFile + ".methods"; + } + writeReferences(dexBuilder.getMethodReferences(), methodListFilename); + } + + if (listFields) { + if (Strings.isNullOrEmpty(fieldListFilename)) { + fieldListFilename = outputDexFile + ".fields"; + } + writeReferences(dexBuilder.getFieldReferences(), fieldListFilename); + } + + if (listTypes) { + if (Strings.isNullOrEmpty(typeListFilename)) { + typeListFilename = outputDexFile + ".types"; + } + writeReferences(dexBuilder.getTypeReferences(), typeListFilename); + } + dexBuilder.writeTo(new FileDataStore(new File(outputDexFile))); } catch (RuntimeException ex) { System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); @@ -244,6 +288,23 @@ public class main { } } + private static void writeReferences(List references, String filename) { + PrintWriter writer = null; + try { + writer = new PrintWriter(new BufferedWriter(new FileWriter(filename))); + + for (String reference: Ordering.natural().sortedCopy(references)) { + writer.println(reference); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } finally { + if (writer != null) { + writer.close(); + } + } + } + private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set smaliFiles) { File[] files = dir.listFiles(); if (files != null) { @@ -378,6 +439,27 @@ public class main { .withArgName("API_LEVEL") .create("a"); + Option listMethodsOption = OptionBuilder.withLongOpt("list-methods") + .withDescription("Lists all the method references to FILE" + + " (.methods by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("m"); + + Option listFieldsOption = OptionBuilder.withLongOpt("list-fields") + .withDescription("Lists all the field references to FILE" + + " (.fields by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("f"); + + Option listClassesOption = OptionBuilder.withLongOpt("list-types") + .withDescription("Lists all the type references to FILE" + + " (.types by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("t"); + Option experimentalOption = OptionBuilder.withLongOpt("experimental") .withDescription("enable experimental opcodes to be assembled, even if they " + " aren't necessarily supported by the Android runtime yet") @@ -405,6 +487,9 @@ public class main { basicOptions.addOption(apiLevelOption); basicOptions.addOption(experimentalOption); basicOptions.addOption(jobsOption); + basicOptions.addOption(listMethodsOption); + basicOptions.addOption(listFieldsOption); + basicOptions.addOption(listClassesOption); debugOptions.addOption(verboseErrorsOption); debugOptions.addOption(printTokensOption);