diff --git a/baksmali/src/main/java/org/jf/baksmali/Baksmali.java b/baksmali/src/main/java/org/jf/baksmali/Baksmali.java index 1958153c..d34fd130 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/Baksmali.java @@ -30,6 +30,7 @@ import org.jf.baksmali.Adaptors.ClassDefinition; import org.jf.baksmali.formatter.BaksmaliWriter; +import org.jf.dexlib2.extra.DexMarker; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.util.ClassFileNameHandler; @@ -58,6 +59,14 @@ public static boolean disassembleDexFile(DexFile dexFile, File outputDir, int jo //may still change of course List classDefs = ListUtil.sortedCopy(dexFile.getClasses()); + if(options.dumpMarkers) { + File markerFile = new File(outputDir, DexMarker.FILE_NAME); + try { + DexMarker.writeMarkers(dexFile.getMarkers(), markerFile); + } catch (IOException ignored) { + } + } + final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDir, ".smali"); ExecutorService executor = Executors.newFixedThreadPool(jobs); diff --git a/baksmali/src/main/java/org/jf/baksmali/BaksmaliOptions.java b/baksmali/src/main/java/org/jf/baksmali/BaksmaliOptions.java index 509b343a..b7ad3509 100644 --- a/baksmali/src/main/java/org/jf/baksmali/BaksmaliOptions.java +++ b/baksmali/src/main/java/org/jf/baksmali/BaksmaliOptions.java @@ -82,6 +82,8 @@ public class BaksmaliOptions { public ClassPath classPath = null; public SyntheticAccessorResolver syntheticAccessorResolver = null; + public boolean dumpMarkers; + public BaksmaliOptions(){ } diff --git a/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java b/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java index 98b85c75..50abb83e 100644 --- a/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java +++ b/baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java @@ -142,6 +142,10 @@ public class DisassembleCommand extends DexInputCommand { @ExtendedParameter(argumentNames = "classes") private List classes = null; + @Parameter(names = {"--dump-markers", "--markers"}, + description = "Dump markers to file") + private boolean dumpMarkers = false; + public DisassembleCommand(@Nonnull List commandAncestors) { super(commandAncestors); } @@ -293,6 +297,8 @@ protected BaksmaliOptions getOptions() { options.allowOdex = true; } + options.dumpMarkers = dumpMarkers; + return options; } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java index 1b39fb09..270c34ef 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java @@ -39,6 +39,7 @@ import org.jf.dexlib2.dexbacked.reference.*; import org.jf.dexlib2.dexbacked.util.FixedSizeList; import org.jf.dexlib2.dexbacked.util.FixedSizeSet; +import org.jf.dexlib2.extra.DexMarker; import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.reference.Reference; import org.jf.dexlib2.util.DexUtil; @@ -227,7 +228,12 @@ public boolean supportsOptimizedOpcodes() { return false; } + @Override + public List getMarkers(){ + return DexMarker.listMarkers(getStringSection().iterator()); + } @Nonnull + @Override public Set getClasses() { return new FixedSizeSet() { @Nonnull diff --git a/dexlib2/src/main/java/org/jf/dexlib2/extra/DexMarker.java b/dexlib2/src/main/java/org/jf/dexlib2/extra/DexMarker.java new file mode 100644 index 00000000..a06ef536 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/extra/DexMarker.java @@ -0,0 +1,121 @@ +package org.jf.dexlib2.extra; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class DexMarker { + + private final String marker; + + private DexMarker(String marker) { + this.marker = marker; + } + + public String getMarker() { + return marker; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DexMarker dexMarker = (DexMarker) o; + return marker.equals(dexMarker.marker); + } + + @Override + public int hashCode() { + return marker.hashCode(); + } + + @Override + public String toString() { + return marker; + } + + public static List listMarkers(Iterator iterator) { + List markerList = new ArrayList<>(); + while (iterator.hasNext()){ + DexMarker marker = of(iterator.next()); + if(marker != null){ + markerList.add(marker); + } + } + return markerList; + } + public static void writeMarkers(List markerList, File file) throws IOException { + StringBuilder builder = new StringBuilder(); + boolean append = false; + for(DexMarker marker : markerList){ + if(append){ + builder.append('\n'); + } + builder.append(marker.getMarker()); + append = true; + } + byte[] bytes = builder.toString().getBytes(StandardCharsets.UTF_8); + File dir = file.getParentFile(); + if(dir != null && !dir.exists()){ + dir.mkdirs(); + } + FileOutputStream outputStream = new FileOutputStream(file); + outputStream.write(bytes, 0, bytes.length); + outputStream.close(); + } + public static List readMarkers(File file) throws IOException { + return readMarkers(new FileInputStream(file)); + } + public static List readMarkers(InputStream inputStream) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer, 0, buffer.length)) != -1){ + outputStream.write(buffer, 0, length); + } + inputStream.close(); + outputStream.close(); + buffer = outputStream.toByteArray(); + String text = new String(buffer, 0, buffer.length, StandardCharsets.UTF_8); + return parseMarkers(text); + } + public static List parseMarkers(String lineSeparatedMarkers) { + List markerList = new ArrayList<>(); + String[] lines = lineSeparatedMarkers.split("\n"); + for(String line : lines){ + DexMarker marker = of(line.trim()); + if(marker != null){ + markerList.add(marker); + } + } + return markerList; + } + public static DexMarker of(String text){ + if(isMarker(text)){ + return new DexMarker(text); + } + return null; + } + public static boolean isMarker(String text){ + if(text == null){ + return false; + } + int i = text.length() - 1; + if(i < 5){ + return false; + } + if(text.charAt(0) != '~' || text.charAt(i) != '}'){ + return false; + } + return text.startsWith(PREFIX_D8) || text.startsWith(PREFIX_R8); + } + public static final String PREFIX_D8 = "~~D8{"; + public static final String PREFIX_R8 = "~~R8{"; + public static final String FILE_NAME = "markers.txt"; +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java index 474bfb13..58f6ecd1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java @@ -32,14 +32,18 @@ package org.jf.dexlib2.iface; import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.extra.DexMarker; import javax.annotation.Nonnull; +import java.util.List; import java.util.Set; /** * This class is a high level representation of a dex file - essentially a set of class definitions. */ public interface DexFile { + + List getMarkers(); /** * Get a set of the classes defined in this dex file. * @@ -47,12 +51,14 @@ public interface DexFile { * * @return A set of the classes defined in this dex file */ - @Nonnull Set getClasses(); + @Nonnull + Set getClasses(); /** * Get the Opcodes associated with this dex file * * @return The Opcodes instance representing the possible opcodes that can be encountered in this dex file */ - @Nonnull Opcodes getOpcodes(); + @Nonnull + Opcodes getOpcodes(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java index 4b2f739f..576cfae8 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java @@ -32,13 +32,16 @@ package org.jf.dexlib2.immutable; import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.extra.DexMarker; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import org.jf.util.ImmutableUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Set; public class ImmutableDexFile implements DexFile { @@ -47,6 +50,8 @@ public class ImmutableDexFile implements DexFile { @Nonnull private final Opcodes opcodes; + private List markerList; + public ImmutableDexFile(@Nonnull Opcodes opcodes, @Nullable Collection classes) { this.classes = ImmutableClassDef.immutableSetOf(classes); this.opcodes = opcodes; @@ -61,9 +66,22 @@ public static ImmutableDexFile of(DexFile dexFile) { if (dexFile instanceof ImmutableDexFile) { return (ImmutableDexFile)dexFile; } - return new ImmutableDexFile(dexFile.getOpcodes(), dexFile.getClasses()); + ImmutableDexFile immutableDexFile = new ImmutableDexFile(dexFile.getOpcodes(), dexFile.getClasses()); + immutableDexFile.setMarkerList(dexFile.getMarkers()); + return immutableDexFile; + } + + public void setMarkerList(List markerList) { + this.markerList = markerList; } + @Override + public List getMarkers() { + if(this.markerList == null){ + this.markerList = new ArrayList<>(); + } + return markerList; + } @Nonnull @Override public Set getClasses() { return classes; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexFileRewriter.java b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexFileRewriter.java index cfae6e62..897ebc66 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexFileRewriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexFileRewriter.java @@ -32,10 +32,13 @@ package org.jf.dexlib2.rewriter; import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.extra.DexMarker; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.DexFile; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; import java.util.Set; public class DexFileRewriter implements Rewriter { @@ -53,17 +56,28 @@ public DexFile rewrite(@Nonnull DexFile value) { } protected class RewrittenDexFile implements DexFile { - @Nonnull protected final DexFile dexFile; + @Nonnull + protected final DexFile dexFile; + private final List markerList; public RewrittenDexFile(@Nonnull DexFile dexFile) { this.dexFile = dexFile; + this.markerList = new ArrayList<>(dexFile.getMarkers()); } - @Override @Nonnull public Set getClasses() { + @Override + public List getMarkers() { + return markerList; + } + @Nonnull + @Override + public Set getClasses() { return RewriterUtils.rewriteSet(rewriters.getClassDefRewriter(), dexFile.getClasses()); } - @Nonnull @Override public Opcodes getOpcodes() { + @Nonnull + @Override + public Opcodes getOpcodes() { return dexFile.getOpcodes(); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java index c683b04a..ebe0d74a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java @@ -34,6 +34,7 @@ import org.jf.dexlib2.HiddenApiRestriction; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.extra.DexMarker; import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.AnnotationElement; import org.jf.dexlib2.iface.MethodImplementation; @@ -71,6 +72,17 @@ public DexBuilder(@Nonnull Opcodes opcodes) { super(opcodes); } + public void addMarkers(Iterator markers){ + while (markers.hasNext()){ + addMarker(markers.next()); + } + } + public void addMarker(DexMarker marker){ + if(marker != null){ + stringSection.internString(marker.getMarker()); + } + } + @Nonnull @Override protected SectionProvider getSectionProvider() { diff --git a/smali/src/main/java/org/jf/smali/AssembleCommand.java b/smali/src/main/java/org/jf/smali/AssembleCommand.java index f8a4330b..0cf61031 100644 --- a/smali/src/main/java/org/jf/smali/AssembleCommand.java +++ b/smali/src/main/java/org/jf/smali/AssembleCommand.java @@ -35,11 +35,13 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.beust.jcommander.validators.PositiveInteger; +import org.jf.dexlib2.extra.DexMarker; import org.jf.util.jcommander.Command; import org.jf.util.jcommander.ExtendedParameter; import org.jf.util.jcommander.ExtendedParameters; import javax.annotation.Nonnull; +import java.io.File; import java.io.IOException; import java.util.List; @@ -69,6 +71,11 @@ public class AssembleCommand extends Command { @ExtendedParameter(argumentNames = "file") private String output = "out.dex"; + @Parameter(names = {"-markers", "--markers"}, + description = "The name/path of file containing line separated markers") + @ExtendedParameter(argumentNames = "file") + private String markers; + @Parameter(names = "--verbose", description = "Generate verbose error messages.") private boolean verbose = false; @@ -99,6 +106,20 @@ public void run() { throw new RuntimeException(ex); } } + private String getMarkers(){ + if(this.markers == null){ + for(String inputPath : input){ + File file = new File(inputPath); + file = new File(file, DexMarker.FILE_NAME); + if(file.isFile()){ + return file.getAbsolutePath(); + } + } + }else if(this.markers.trim().length() == 0){ + return null; + } + return this.markers; + } protected SmaliOptions getOptions() { SmaliOptions options = new SmaliOptions(); @@ -108,6 +129,7 @@ protected SmaliOptions getOptions() { options.outputDexFile = output; options.allowOdexOpcodes = allowOdexOpcodes; options.verboseErrors = verbose; + options.markersListFile = getMarkers(); return options; } diff --git a/smali/src/main/java/org/jf/smali/Smali.java b/smali/src/main/java/org/jf/smali/Smali.java index 966830d8..02208ee9 100644 --- a/smali/src/main/java/org/jf/smali/Smali.java +++ b/smali/src/main/java/org/jf/smali/Smali.java @@ -38,6 +38,7 @@ import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.extra.DexMarker; import org.jf.dexlib2.writer.builder.DexBuilder; import org.jf.dexlib2.writer.io.FileDataStore; import org.jf.util.StringUtils; @@ -75,6 +76,10 @@ public static boolean assemble(final SmaliOptions options, String... input) thro public static boolean assemble(final SmaliOptions options, List input) throws IOException { boolean success = false; final DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel)); + if(options.markersListFile != null){ + List markerList = DexMarker.readMarkers(new File(options.markersListFile)); + dexBuilder.addMarkers(markerList.iterator()); + } for (String fileToProcess: input) { File argFile = new File(fileToProcess); diff --git a/smali/src/main/java/org/jf/smali/SmaliOptions.java b/smali/src/main/java/org/jf/smali/SmaliOptions.java index ac385fe6..b3d42bb2 100644 --- a/smali/src/main/java/org/jf/smali/SmaliOptions.java +++ b/smali/src/main/java/org/jf/smali/SmaliOptions.java @@ -39,4 +39,6 @@ public class SmaliOptions { public boolean allowOdexOpcodes = false; public boolean verboseErrors = false; public boolean printTokens = false; + + public String markersListFile; }