mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 12:20:11 +02:00
Implement a new command line interface for baksmali
This commit is contained in:
parent
93100e57b2
commit
5a5eafb818
@ -41,8 +41,8 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(':util')
|
compile project(':util')
|
||||||
compile project(':dexlib2')
|
compile project(':dexlib2')
|
||||||
compile depends.commons_cli
|
|
||||||
compile depends.guava
|
compile depends.guava
|
||||||
|
compile depends.jcommander
|
||||||
|
|
||||||
testCompile depends.junit
|
testCompile depends.junit
|
||||||
testCompile project(':smali')
|
testCompile project(':smali')
|
||||||
@ -59,7 +59,7 @@ task fatJar(type: Jar) {
|
|||||||
classifier = 'fat'
|
classifier = 'fat'
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes('Main-Class': 'org.jf.baksmali.main')
|
attributes('Main-Class': 'org.jf.baksmali.Main')
|
||||||
}
|
}
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
@ -100,3 +100,17 @@ task proguard(type: proguard.gradle.ProGuardTask, dependsOn: fatJar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.getByPath(':release').dependsOn(proguard)
|
tasks.getByPath(':release').dependsOn(proguard)
|
||||||
|
|
||||||
|
task fastbuild(dependsOn: build) {
|
||||||
|
}
|
||||||
|
|
||||||
|
task fb(dependsOn: fastbuild) {
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.getByPath('javadoc').onlyIf({
|
||||||
|
!gradle.taskGraph.hasTask(fastbuild)
|
||||||
|
})
|
||||||
|
|
||||||
|
tasks.getByPath('test').onlyIf({
|
||||||
|
!gradle.taskGraph.hasTask(fastbuild)
|
||||||
|
})
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -42,7 +42,7 @@ public class CatchMethodItem extends MethodItem {
|
|||||||
private final LabelMethodItem tryEndLabel;
|
private final LabelMethodItem tryEndLabel;
|
||||||
private final LabelMethodItem handlerLabel;
|
private final LabelMethodItem handlerLabel;
|
||||||
|
|
||||||
public CatchMethodItem(@Nonnull baksmaliOptions options, @Nonnull MethodDefinition.LabelCache labelCache,
|
public CatchMethodItem(@Nonnull BaksmaliOptions options, @Nonnull MethodDefinition.LabelCache labelCache,
|
||||||
int codeAddress, @Nullable String exceptionType, int startAddress, int endAddress,
|
int codeAddress, @Nullable String exceptionType, int startAddress, int endAddress,
|
||||||
int handlerAddress) {
|
int handlerAddress) {
|
||||||
super(codeAddress);
|
super(codeAddress);
|
||||||
|
@ -28,8 +28,7 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
|
||||||
import org.jf.dexlib2.AccessFlags;
|
import org.jf.dexlib2.AccessFlags;
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
|
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
|
||||||
@ -46,16 +45,16 @@ import java.io.IOException;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ClassDefinition {
|
public class ClassDefinition {
|
||||||
@Nonnull public final baksmaliOptions options;
|
@Nonnull public final BaksmaliOptions options;
|
||||||
@Nonnull public final ClassDef classDef;
|
@Nonnull public final ClassDef classDef;
|
||||||
@Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
|
@Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
|
||||||
|
|
||||||
protected boolean validationErrors;
|
protected boolean validationErrors;
|
||||||
|
|
||||||
public ClassDefinition(@Nonnull baksmaliOptions options, @Nonnull ClassDef classDef) {
|
public ClassDefinition(@Nonnull BaksmaliOptions options, @Nonnull ClassDef classDef) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.classDef = classDef;
|
this.classDef = classDef;
|
||||||
fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor();
|
fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor(classDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hadValidationErrors() {
|
public boolean hadValidationErrors() {
|
||||||
@ -63,7 +62,7 @@ public class ClassDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private HashSet<String> findFieldsSetInStaticConstructor() {
|
private static HashSet<String> findFieldsSetInStaticConstructor(@Nonnull ClassDef classDef) {
|
||||||
HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
|
HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
|
||||||
|
|
||||||
for (Method method: classDef.getDirectMethods()) {
|
for (Method method: classDef.getDirectMethods()) {
|
||||||
@ -166,7 +165,7 @@ public class ClassDefinition {
|
|||||||
writer.write("# annotations\n");
|
writer.write("# annotations\n");
|
||||||
|
|
||||||
String containingClass = null;
|
String containingClass = null;
|
||||||
if (options.useImplicitReferences) {
|
if (options.implicitReferences) {
|
||||||
containingClass = classDef.getType();
|
containingClass = classDef.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,14 +28,14 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
public class EndTryLabelMethodItem extends LabelMethodItem {
|
public class EndTryLabelMethodItem extends LabelMethodItem {
|
||||||
private int endTryAddress;
|
private int endTryAddress;
|
||||||
|
|
||||||
public EndTryLabelMethodItem(@Nonnull baksmaliOptions options, int codeAddress, int endTryAddress) {
|
public EndTryLabelMethodItem(@Nonnull BaksmaliOptions options, int codeAddress, int endTryAddress) {
|
||||||
super(options, codeAddress, "try_end_");
|
super(options, codeAddress, "try_end_");
|
||||||
this.endTryAddress = endTryAddress;
|
this.endTryAddress = endTryAddress;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor;
|
import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor;
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.dexlib2.AccessFlags;
|
import org.jf.dexlib2.AccessFlags;
|
||||||
import org.jf.dexlib2.iface.Annotation;
|
import org.jf.dexlib2.iface.Annotation;
|
||||||
import org.jf.dexlib2.iface.Field;
|
import org.jf.dexlib2.iface.Field;
|
||||||
@ -41,7 +41,7 @@ import java.io.IOException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public class FieldDefinition {
|
public class FieldDefinition {
|
||||||
public static void writeTo(baksmaliOptions options, IndentingWriter writer, Field field,
|
public static void writeTo(BaksmaliOptions options, IndentingWriter writer, Field field,
|
||||||
boolean setInStaticConstructor) throws IOException {
|
boolean setInStaticConstructor) throws IOException {
|
||||||
EncodedValue initialValue = field.getInitialValue();
|
EncodedValue initialValue = field.getInitialValue();
|
||||||
int accessFlags = field.getAccessFlags();
|
int accessFlags = field.getAccessFlags();
|
||||||
@ -68,7 +68,7 @@ public class FieldDefinition {
|
|||||||
writer.write(" = ");
|
writer.write(" = ");
|
||||||
|
|
||||||
String containingClass = null;
|
String containingClass = null;
|
||||||
if (options.useImplicitReferences) {
|
if (options.implicitReferences) {
|
||||||
containingClass = field.getDefiningClass();
|
containingClass = field.getDefiningClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ public class FieldDefinition {
|
|||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
|
|
||||||
String containingClass = null;
|
String containingClass = null;
|
||||||
if (options.useImplicitReferences) {
|
if (options.implicitReferences) {
|
||||||
containingClass = field.getDefiningClass();
|
containingClass = field.getDefiningClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ import org.jf.baksmali.Adaptors.MethodDefinition;
|
|||||||
import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
|
import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
|
||||||
import org.jf.baksmali.Adaptors.MethodItem;
|
import org.jf.baksmali.Adaptors.MethodItem;
|
||||||
import org.jf.baksmali.Renderers.LongRenderer;
|
import org.jf.baksmali.Renderers.LongRenderer;
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.dexlib2.Opcode;
|
import org.jf.dexlib2.Opcode;
|
||||||
import org.jf.dexlib2.ReferenceType;
|
import org.jf.dexlib2.ReferenceType;
|
||||||
import org.jf.dexlib2.VerificationError;
|
import org.jf.dexlib2.VerificationError;
|
||||||
@ -67,7 +67,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAllowedOdex(@Nonnull Opcode opcode) {
|
private boolean isAllowedOdex(@Nonnull Opcode opcode) {
|
||||||
baksmaliOptions options = methodDef.classDef.options;
|
BaksmaliOptions options = methodDef.classDef.options;
|
||||||
if (options.allowOdex) {
|
if (options.allowOdex) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
Reference reference = referenceInstruction.getReference();
|
Reference reference = referenceInstruction.getReference();
|
||||||
|
|
||||||
String classContext = null;
|
String classContext = null;
|
||||||
if (methodDef.classDef.options.useImplicitReferences) {
|
if (methodDef.classDef.options.implicitReferences) {
|
||||||
classContext = methodDef.method.getDefiningClass();
|
classContext = methodDef.method.getDefiningClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ package org.jf.baksmali.Adaptors.Format;
|
|||||||
|
|
||||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.dexlib2.Opcode;
|
import org.jf.dexlib2.Opcode;
|
||||||
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
@ -41,7 +41,7 @@ import java.io.IOException;
|
|||||||
public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<OffsetInstruction> {
|
public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<OffsetInstruction> {
|
||||||
protected LabelMethodItem label;
|
protected LabelMethodItem label;
|
||||||
|
|
||||||
public OffsetInstructionFormatMethodItem(@Nonnull baksmaliOptions options, @Nonnull MethodDefinition methodDef,
|
public OffsetInstructionFormatMethodItem(@Nonnull BaksmaliOptions options, @Nonnull MethodDefinition methodDef,
|
||||||
int codeAddress, OffsetInstruction instruction) {
|
int codeAddress, OffsetInstruction instruction) {
|
||||||
super(methodDef, codeAddress, instruction);
|
super(methodDef, codeAddress, instruction);
|
||||||
|
|
||||||
|
@ -28,18 +28,18 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class LabelMethodItem extends MethodItem {
|
public class LabelMethodItem extends MethodItem {
|
||||||
private final baksmaliOptions options;
|
private final BaksmaliOptions options;
|
||||||
private final String labelPrefix;
|
private final String labelPrefix;
|
||||||
private int labelSequence;
|
private int labelSequence;
|
||||||
|
|
||||||
public LabelMethodItem(@Nonnull baksmaliOptions options, int codeAddress, @Nonnull String labelPrefix) {
|
public LabelMethodItem(@Nonnull BaksmaliOptions options, int codeAddress, @Nonnull String labelPrefix) {
|
||||||
super(codeAddress);
|
super(codeAddress);
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.labelPrefix = labelPrefix;
|
this.labelPrefix = labelPrefix;
|
||||||
@ -76,7 +76,7 @@ public class LabelMethodItem extends MethodItem {
|
|||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
writer.write(':');
|
writer.write(':');
|
||||||
writer.write(labelPrefix);
|
writer.write(labelPrefix);
|
||||||
if (options.useSequentialLabels) {
|
if (options.sequentialLabels) {
|
||||||
writer.printUnsignedLongAsHex(labelSequence);
|
writer.printUnsignedLongAsHex(labelSequence);
|
||||||
} else {
|
} else {
|
||||||
writer.printUnsignedLongAsHex(this.getLabelAddress());
|
writer.printUnsignedLongAsHex(this.getLabelAddress());
|
||||||
|
@ -32,7 +32,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
|
import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
|
||||||
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
|
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.dexlib2.AccessFlags;
|
import org.jf.dexlib2.AccessFlags;
|
||||||
import org.jf.dexlib2.Format;
|
import org.jf.dexlib2.Format;
|
||||||
import org.jf.dexlib2.Opcode;
|
import org.jf.dexlib2.Opcode;
|
||||||
@ -163,7 +163,7 @@ public class MethodDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void writeEmptyMethodTo(IndentingWriter writer, Method method,
|
public static void writeEmptyMethodTo(IndentingWriter writer, Method method,
|
||||||
baksmaliOptions options) throws IOException {
|
BaksmaliOptions options) throws IOException {
|
||||||
writer.write(".method ");
|
writer.write(".method ");
|
||||||
writeAccessFlags(writer, method.getAccessFlags());
|
writeAccessFlags(writer, method.getAccessFlags());
|
||||||
writer.write(method.getName());
|
writer.write(method.getName());
|
||||||
@ -180,7 +180,7 @@ public class MethodDefinition {
|
|||||||
writeParameters(writer, method, methodParameters, options);
|
writeParameters(writer, method, methodParameters, options);
|
||||||
|
|
||||||
String containingClass = null;
|
String containingClass = null;
|
||||||
if (options.useImplicitReferences) {
|
if (options.implicitReferences) {
|
||||||
containingClass = method.getDefiningClass();
|
containingClass = method.getDefiningClass();
|
||||||
}
|
}
|
||||||
AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
|
AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
|
||||||
@ -212,7 +212,7 @@ public class MethodDefinition {
|
|||||||
writer.write('\n');
|
writer.write('\n');
|
||||||
|
|
||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
if (classDef.options.useLocalsDirective) {
|
if (classDef.options.localsDirective) {
|
||||||
writer.write(".locals ");
|
writer.write(".locals ");
|
||||||
writer.printSignedIntAsDec(methodImpl.getRegisterCount() - parameterRegisterCount);
|
writer.printSignedIntAsDec(methodImpl.getRegisterCount() - parameterRegisterCount);
|
||||||
} else {
|
} else {
|
||||||
@ -228,7 +228,7 @@ public class MethodDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String containingClass = null;
|
String containingClass = null;
|
||||||
if (classDef.options.useImplicitReferences) {
|
if (classDef.options.implicitReferences) {
|
||||||
containingClass = method.getDefiningClass();
|
containingClass = method.getDefiningClass();
|
||||||
}
|
}
|
||||||
AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
|
AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
|
||||||
@ -313,18 +313,18 @@ public class MethodDefinition {
|
|||||||
|
|
||||||
private static void writeParameters(IndentingWriter writer, Method method,
|
private static void writeParameters(IndentingWriter writer, Method method,
|
||||||
List<? extends MethodParameter> parameters,
|
List<? extends MethodParameter> parameters,
|
||||||
baksmaliOptions options) throws IOException {
|
BaksmaliOptions options) throws IOException {
|
||||||
boolean isStatic = AccessFlags.STATIC.isSet(method.getAccessFlags());
|
boolean isStatic = AccessFlags.STATIC.isSet(method.getAccessFlags());
|
||||||
int registerNumber = isStatic?0:1;
|
int registerNumber = isStatic?0:1;
|
||||||
for (MethodParameter parameter: parameters) {
|
for (MethodParameter parameter: parameters) {
|
||||||
String parameterType = parameter.getType();
|
String parameterType = parameter.getType();
|
||||||
String parameterName = parameter.getName();
|
String parameterName = parameter.getName();
|
||||||
Collection<? extends Annotation> annotations = parameter.getAnnotations();
|
Collection<? extends Annotation> annotations = parameter.getAnnotations();
|
||||||
if ((options.outputDebugInfo && parameterName != null) || annotations.size() != 0) {
|
if ((options.debugInfo && parameterName != null) || annotations.size() != 0) {
|
||||||
writer.write(".param p");
|
writer.write(".param p");
|
||||||
writer.printSignedIntAsDec(registerNumber);
|
writer.printSignedIntAsDec(registerNumber);
|
||||||
|
|
||||||
if (parameterName != null && options.outputDebugInfo) {
|
if (parameterName != null && options.debugInfo) {
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
ReferenceFormatter.writeStringReference(writer, parameterName);
|
ReferenceFormatter.writeStringReference(writer, parameterName);
|
||||||
}
|
}
|
||||||
@ -335,7 +335,7 @@ public class MethodDefinition {
|
|||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
|
|
||||||
String containingClass = null;
|
String containingClass = null;
|
||||||
if (options.useImplicitReferences) {
|
if (options.implicitReferences) {
|
||||||
containingClass = method.getDefiningClass();
|
containingClass = method.getDefiningClass();
|
||||||
}
|
}
|
||||||
AnnotationFormatter.writeTo(writer, annotations, containingClass);
|
AnnotationFormatter.writeTo(writer, annotations, containingClass);
|
||||||
@ -374,11 +374,11 @@ public class MethodDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addTries(methodItems);
|
addTries(methodItems);
|
||||||
if (classDef.options.outputDebugInfo) {
|
if (classDef.options.debugInfo) {
|
||||||
addDebugInfo(methodItems);
|
addDebugInfo(methodItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classDef.options.useSequentialLabels) {
|
if (classDef.options.sequentialLabels) {
|
||||||
setLabelSequentialNumbers();
|
setLabelSequentialNumbers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,7 +415,7 @@ public class MethodDefinition {
|
|||||||
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classDef.options.addCodeOffsets) {
|
if (classDef.options.codeOffsets) {
|
||||||
methodItems.add(new MethodItem(currentCodeAddress) {
|
methodItems.add(new MethodItem(currentCodeAddress) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -432,7 +432,7 @@ public class MethodDefinition {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!classDef.options.noAccessorComments && (instruction instanceof ReferenceInstruction)) {
|
if (classDef.options.accessorComments && (instruction instanceof ReferenceInstruction)) {
|
||||||
Opcode opcode = instruction.getOpcode();
|
Opcode opcode = instruction.getOpcode();
|
||||||
|
|
||||||
if (opcode.referenceType == ReferenceType.METHOD) {
|
if (opcode.referenceType == ReferenceType.METHOD) {
|
||||||
@ -493,7 +493,7 @@ public class MethodDefinition {
|
|||||||
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classDef.options.addCodeOffsets) {
|
if (classDef.options.codeOffsets) {
|
||||||
methodItems.add(new MethodItem(currentCodeAddress) {
|
methodItems.add(new MethodItem(currentCodeAddress) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -597,7 +597,7 @@ public class MethodDefinition {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getContainingClassForImplicitReference() {
|
private String getContainingClassForImplicitReference() {
|
||||||
if (classDef.options.useImplicitReferences) {
|
if (classDef.options.implicitReferences) {
|
||||||
return classDef.classDef.getType();
|
return classDef.classDef.getType();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
||||||
import org.jf.dexlib2.analysis.RegisterType;
|
import org.jf.dexlib2.analysis.RegisterType;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
@ -60,12 +60,12 @@ public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
|||||||
int registerCount = analyzedInstruction.getRegisterCount();
|
int registerCount = analyzedInstruction.getRegisterCount();
|
||||||
BitSet registers = new BitSet(registerCount);
|
BitSet registers = new BitSet(registerCount);
|
||||||
|
|
||||||
if ((registerInfo & baksmaliOptions.ALL) != 0) {
|
if ((registerInfo & BaksmaliOptions.ALL) != 0) {
|
||||||
registers.set(0, registerCount);
|
registers.set(0, registerCount);
|
||||||
} else {
|
} else {
|
||||||
if ((registerInfo & baksmaliOptions.ALLPOST) != 0) {
|
if ((registerInfo & BaksmaliOptions.ALLPOST) != 0) {
|
||||||
registers.set(0, registerCount);
|
registers.set(0, registerCount);
|
||||||
} else if ((registerInfo & baksmaliOptions.DEST) != 0) {
|
} else if ((registerInfo & BaksmaliOptions.DEST) != 0) {
|
||||||
addDestRegs(registers, registerCount);
|
addDestRegs(registers, registerCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
||||||
import org.jf.dexlib2.analysis.MethodAnalyzer;
|
import org.jf.dexlib2.analysis.MethodAnalyzer;
|
||||||
import org.jf.dexlib2.analysis.RegisterType;
|
import org.jf.dexlib2.analysis.RegisterType;
|
||||||
@ -68,29 +68,29 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
|||||||
BitSet registers = new BitSet(registerCount);
|
BitSet registers = new BitSet(registerCount);
|
||||||
BitSet mergeRegisters = null;
|
BitSet mergeRegisters = null;
|
||||||
|
|
||||||
if ((registerInfo & baksmaliOptions.ALL) != 0) {
|
if ((registerInfo & BaksmaliOptions.ALL) != 0) {
|
||||||
registers.set(0, registerCount);
|
registers.set(0, registerCount);
|
||||||
} else {
|
} else {
|
||||||
if ((registerInfo & baksmaliOptions.ALLPRE) != 0) {
|
if ((registerInfo & BaksmaliOptions.ALLPRE) != 0) {
|
||||||
registers.set(0, registerCount);
|
registers.set(0, registerCount);
|
||||||
} else {
|
} else {
|
||||||
if ((registerInfo & baksmaliOptions.ARGS) != 0) {
|
if ((registerInfo & BaksmaliOptions.ARGS) != 0) {
|
||||||
addArgsRegs(registers);
|
addArgsRegs(registers);
|
||||||
}
|
}
|
||||||
if ((registerInfo & baksmaliOptions.MERGE) != 0) {
|
if ((registerInfo & BaksmaliOptions.MERGE) != 0) {
|
||||||
if (analyzedInstruction.isBeginningInstruction()) {
|
if (analyzedInstruction.isBeginningInstruction()) {
|
||||||
addParamRegs(registers, registerCount);
|
addParamRegs(registers, registerCount);
|
||||||
}
|
}
|
||||||
mergeRegisters = new BitSet(registerCount);
|
mergeRegisters = new BitSet(registerCount);
|
||||||
addMergeRegs(mergeRegisters, registerCount);
|
addMergeRegs(mergeRegisters, registerCount);
|
||||||
} else if ((registerInfo & baksmaliOptions.FULLMERGE) != 0 &&
|
} else if ((registerInfo & BaksmaliOptions.FULLMERGE) != 0 &&
|
||||||
(analyzedInstruction.isBeginningInstruction())) {
|
(analyzedInstruction.isBeginningInstruction())) {
|
||||||
addParamRegs(registers, registerCount);
|
addParamRegs(registers, registerCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((registerInfo & baksmaliOptions.FULLMERGE) != 0) {
|
if ((registerInfo & BaksmaliOptions.FULLMERGE) != 0) {
|
||||||
if (mergeRegisters == null) {
|
if (mergeRegisters == null) {
|
||||||
mergeRegisters = new BitSet(registerCount);
|
mergeRegisters = new BitSet(registerCount);
|
||||||
addMergeRegs(mergeRegisters, registerCount);
|
addMergeRegs(mergeRegisters, registerCount);
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
import org.jf.baksmali.baksmaliOptions;
|
import org.jf.baksmali.BaksmaliOptions;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -38,11 +38,11 @@ import java.io.IOException;
|
|||||||
* This class contains the logic used for formatting registers
|
* This class contains the logic used for formatting registers
|
||||||
*/
|
*/
|
||||||
public class RegisterFormatter {
|
public class RegisterFormatter {
|
||||||
@Nonnull public final baksmaliOptions options;
|
@Nonnull public final BaksmaliOptions options;
|
||||||
public final int registerCount;
|
public final int registerCount;
|
||||||
public final int parameterRegisterCount;
|
public final int parameterRegisterCount;
|
||||||
|
|
||||||
public RegisterFormatter(@Nonnull baksmaliOptions options, int registerCount, int parameterRegisterCount) {
|
public RegisterFormatter(@Nonnull BaksmaliOptions options, int registerCount, int parameterRegisterCount) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.registerCount = registerCount;
|
this.registerCount = registerCount;
|
||||||
this.parameterRegisterCount = parameterRegisterCount;
|
this.parameterRegisterCount = parameterRegisterCount;
|
||||||
@ -58,7 +58,7 @@ public class RegisterFormatter {
|
|||||||
* @param lastRegister the last register in the range
|
* @param lastRegister the last register in the range
|
||||||
*/
|
*/
|
||||||
public void writeRegisterRange(IndentingWriter writer, int startRegister, int lastRegister) throws IOException {
|
public void writeRegisterRange(IndentingWriter writer, int startRegister, int lastRegister) throws IOException {
|
||||||
if (!options.noParameterRegisters) {
|
if (options.parameterRegisters) {
|
||||||
assert startRegister <= lastRegister;
|
assert startRegister <= lastRegister;
|
||||||
|
|
||||||
if (startRegister >= registerCount - parameterRegisterCount) {
|
if (startRegister >= registerCount - parameterRegisterCount) {
|
||||||
@ -86,7 +86,7 @@ public class RegisterFormatter {
|
|||||||
* @param register the register number
|
* @param register the register number
|
||||||
*/
|
*/
|
||||||
public void writeTo(IndentingWriter writer, int register) throws IOException {
|
public void writeTo(IndentingWriter writer, int register) throws IOException {
|
||||||
if (!options.noParameterRegisters) {
|
if (options.parameterRegisters) {
|
||||||
if (register >= registerCount - parameterRegisterCount) {
|
if (register >= registerCount - parameterRegisterCount) {
|
||||||
writer.write('p');
|
writer.write('p');
|
||||||
writer.printSignedIntAsDec((register - (registerCount - parameterRegisterCount)));
|
writer.printSignedIntAsDec((register - (registerCount - parameterRegisterCount)));
|
||||||
|
@ -28,105 +28,21 @@
|
|||||||
|
|
||||||
package org.jf.baksmali;
|
package org.jf.baksmali;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||||
import org.jf.dexlib2.analysis.ClassPath;
|
|
||||||
import org.jf.dexlib2.analysis.CustomInlineMethodResolver;
|
|
||||||
import org.jf.dexlib2.iface.ClassDef;
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
import org.jf.dexlib2.iface.DexFile;
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
|
||||||
import org.jf.util.ClassFileNameHandler;
|
import org.jf.util.ClassFileNameHandler;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.xml.sax.Attributes;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.annotation.Nonnull;
|
||||||
import javax.xml.parsers.SAXParser;
|
|
||||||
import javax.xml.parsers.SAXParserFactory;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
public class baksmali {
|
public class Baksmali {
|
||||||
|
public static boolean disassembleDexFile(DexFile dexFile, File outputDir, int jobs, final BaksmaliOptions options) {
|
||||||
public static boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
|
|
||||||
if (options.registerInfo != 0 || options.deodex || options.normalizeVirtualMethods) {
|
|
||||||
try {
|
|
||||||
Iterable<String> extraClassPathEntries;
|
|
||||||
if (options.extraClassPathEntries != null) {
|
|
||||||
extraClassPathEntries = options.extraClassPathEntries;
|
|
||||||
} else {
|
|
||||||
extraClassPathEntries = ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
|
|
||||||
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
|
|
||||||
options.apiLevel, options.checkPackagePrivateAccess, options.experimental);
|
|
||||||
|
|
||||||
if (options.customInlineDefinitions != null) {
|
|
||||||
options.inlineResolver = new CustomInlineMethodResolver(options.classPath,
|
|
||||||
options.customInlineDefinitions);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
System.err.println("\n\nError occurred while loading boot class path files. Aborting.");
|
|
||||||
ex.printStackTrace(System.err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.resourceIdFileEntries != null) {
|
|
||||||
class PublicHandler extends DefaultHandler {
|
|
||||||
String prefix = null;
|
|
||||||
public PublicHandler(String prefix) {
|
|
||||||
super();
|
|
||||||
this.prefix = prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startElement(String uri, String localName,
|
|
||||||
String qName, Attributes attr) throws SAXException {
|
|
||||||
if (qName.equals("public")) {
|
|
||||||
String type = attr.getValue("type");
|
|
||||||
String name = attr.getValue("name").replace('.', '_');
|
|
||||||
Integer public_key = Integer.decode(attr.getValue("id"));
|
|
||||||
String public_val = new StringBuffer()
|
|
||||||
.append(prefix)
|
|
||||||
.append(".")
|
|
||||||
.append(type)
|
|
||||||
.append(".")
|
|
||||||
.append(name)
|
|
||||||
.toString();
|
|
||||||
options.resourceIds.put(public_key, public_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Entry<String,String> entry: options.resourceIdFileEntries.entrySet()) {
|
|
||||||
try {
|
|
||||||
SAXParser saxp = SAXParserFactory.newInstance().newSAXParser();
|
|
||||||
String prefix = entry.getValue();
|
|
||||||
saxp.parse(entry.getKey(), new PublicHandler(prefix));
|
|
||||||
} catch (ParserConfigurationException e) {
|
|
||||||
continue;
|
|
||||||
} catch (SAXException e) {
|
|
||||||
continue;
|
|
||||||
} catch (IOException e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File outputDirectoryFile = new File(options.outputDirectory);
|
|
||||||
if (!outputDirectoryFile.exists()) {
|
|
||||||
if (!outputDirectoryFile.mkdirs()) {
|
|
||||||
System.err.println("Can't create the output directory " + options.outputDirectory);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
|
//sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
|
||||||
//name collisions, then we'll use the same name for each class, if the dex file goes through multiple
|
//name collisions, then we'll use the same name for each class, if the dex file goes through multiple
|
||||||
@ -134,13 +50,9 @@ public class baksmali {
|
|||||||
//may still change of course
|
//may still change of course
|
||||||
List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
|
List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
|
||||||
|
|
||||||
if (!options.noAccessorComments) {
|
final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDir, ".smali");
|
||||||
options.syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getOpcodes(), classDefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
|
ExecutorService executor = Executors.newFixedThreadPool(jobs);
|
||||||
|
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
|
|
||||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||||
|
|
||||||
for (final ClassDef classDef: classDefs) {
|
for (final ClassDef classDef: classDefs) {
|
||||||
@ -174,7 +86,7 @@ public class baksmali {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
|
private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
|
||||||
baksmaliOptions options) {
|
BaksmaliOptions options) {
|
||||||
/**
|
/**
|
||||||
* The path for the disassembly file is based on the package name
|
* The path for the disassembly file is based on the package name
|
||||||
* The class descriptor will look something like:
|
* The class descriptor will look something like:
|
||||||
@ -243,4 +155,75 @@ public class baksmali {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static List<String> getDefaultBootClassPath(int apiLevel) {
|
||||||
|
if (apiLevel < 9) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar");
|
||||||
|
} else if (apiLevel < 12) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/core-junit.jar");
|
||||||
|
} else if (apiLevel < 14) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/apache-xml.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/core-junit.jar");
|
||||||
|
} else if (apiLevel < 16) {
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/apache-xml.jar",
|
||||||
|
"/system/framework/filterfw.jar");
|
||||||
|
} else if (apiLevel < 21) {
|
||||||
|
// this is correct as of api 17/4.2.2
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/mms-common.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/services.jar",
|
||||||
|
"/system/framework/apache-xml.jar");
|
||||||
|
} else { // api >= 21
|
||||||
|
// TODO: verify, add new ones?
|
||||||
|
return Lists.newArrayList(
|
||||||
|
"/system/framework/core-libart.jar",
|
||||||
|
"/system/framework/conscrypt.jar",
|
||||||
|
"/system/framework/okhttp.jar",
|
||||||
|
"/system/framework/core-junit.jar",
|
||||||
|
"/system/framework/bouncycastle.jar",
|
||||||
|
"/system/framework/ext.jar",
|
||||||
|
"/system/framework/framework.jar",
|
||||||
|
"/system/framework/telephony-common.jar",
|
||||||
|
"/system/framework/voip-common.jar",
|
||||||
|
"/system/framework/ims-common.jar",
|
||||||
|
"/system/framework/mms-common.jar",
|
||||||
|
"/system/framework/android.policy.jar",
|
||||||
|
"/system/framework/apache-xml.jar");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -31,19 +31,37 @@
|
|||||||
|
|
||||||
package org.jf.baksmali;
|
package org.jf.baksmali;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.jf.dexlib2.analysis.ClassPath;
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.SAXParser;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
|
|
||||||
|
public class BaksmaliOptions {
|
||||||
|
public int apiLevel = 15;
|
||||||
|
|
||||||
|
public boolean parameterRegisters = true;
|
||||||
|
public boolean localsDirective = false;
|
||||||
|
public boolean sequentialLabels = false;
|
||||||
|
public boolean debugInfo = true;
|
||||||
|
public boolean codeOffsets = false;
|
||||||
|
public boolean accessorComments = true;
|
||||||
|
public boolean allowOdex = false;
|
||||||
|
public boolean deodex = false;
|
||||||
|
public boolean experimentalOpcodes = false;
|
||||||
|
public boolean implicitReferences = false;
|
||||||
|
public boolean normalizeVirtualMethods = false;
|
||||||
|
|
||||||
public class baksmaliOptions {
|
|
||||||
// register info values
|
// register info values
|
||||||
public static final int ALL = 1;
|
public static final int ALL = 1;
|
||||||
public static final int ALLPRE = 2;
|
public static final int ALLPRE = 2;
|
||||||
@ -53,56 +71,53 @@ public class baksmaliOptions {
|
|||||||
public static final int MERGE = 32;
|
public static final int MERGE = 32;
|
||||||
public static final int FULLMERGE = 64;
|
public static final int FULLMERGE = 64;
|
||||||
|
|
||||||
public int apiLevel = 15;
|
|
||||||
public String outputDirectory = "out";
|
|
||||||
@Nullable public String dexEntry = null;
|
|
||||||
public List<String> bootClassPathDirs = Lists.newArrayList();
|
|
||||||
|
|
||||||
public List<String> bootClassPathEntries = Lists.newArrayList();
|
|
||||||
public List<String> extraClassPathEntries = Lists.newArrayList();
|
|
||||||
|
|
||||||
public Map<String,String> resourceIdFileEntries = new HashMap<String,String>();
|
|
||||||
public Map<Integer,String> resourceIds = new HashMap<Integer,String>();
|
|
||||||
|
|
||||||
public boolean noParameterRegisters = false;
|
|
||||||
public boolean useLocalsDirective = false;
|
|
||||||
public boolean useSequentialLabels = false;
|
|
||||||
public boolean outputDebugInfo = true;
|
|
||||||
public boolean addCodeOffsets = false;
|
|
||||||
public boolean noAccessorComments = false;
|
|
||||||
public boolean allowOdex = false;
|
|
||||||
public boolean deodex = false;
|
|
||||||
public boolean experimental = false;
|
|
||||||
public boolean ignoreErrors = false;
|
|
||||||
public boolean checkPackagePrivateAccess = false;
|
|
||||||
public boolean useImplicitReferences = false;
|
|
||||||
public boolean normalizeVirtualMethods = false;
|
|
||||||
public File customInlineDefinitions = null;
|
|
||||||
public InlineMethodResolver inlineResolver = null;
|
|
||||||
public int registerInfo = 0;
|
public int registerInfo = 0;
|
||||||
public ClassPath classPath = null;
|
|
||||||
public int jobs = Runtime.getRuntime().availableProcessors();
|
|
||||||
public boolean disassemble = true;
|
|
||||||
public boolean dump = false;
|
|
||||||
public String dumpFileName = null;
|
|
||||||
|
|
||||||
|
public Map<Integer,String> resourceIds = new HashMap<Integer,String>();
|
||||||
|
public InlineMethodResolver inlineResolver = null;
|
||||||
|
public ClassPath classPath = null;
|
||||||
public SyntheticAccessorResolver syntheticAccessorResolver = null;
|
public SyntheticAccessorResolver syntheticAccessorResolver = null;
|
||||||
|
|
||||||
public void setBootClassPath(String bootClassPath) {
|
/**
|
||||||
bootClassPathEntries = Lists.newArrayList(bootClassPath.split(":"));
|
* Load the resource ids from a set of public.xml files.
|
||||||
}
|
*
|
||||||
|
* @param resourceFiles A map of resource prefixes -> public.xml files
|
||||||
|
*/
|
||||||
|
public void loadResourceIds(Map<String, File> resourceFiles) {
|
||||||
|
class PublicResourceHandler extends DefaultHandler {
|
||||||
|
|
||||||
public void addExtraClassPath(String extraClassPath) {
|
@Nonnull private final String prefix;
|
||||||
if (extraClassPath.startsWith(":")) {
|
|
||||||
extraClassPath = extraClassPath.substring(1);
|
public PublicResourceHandler(@Nonnull String prefix) {
|
||||||
|
super();
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
extraClassPathEntries.addAll(Arrays.asList(extraClassPath.split(":")));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceIdFiles(String resourceIdFiles) {
|
for (Map.Entry<String, File> entry: resourceFiles.entrySet()) {
|
||||||
for (String resourceIdFile: resourceIdFiles.split(":")) {
|
try {
|
||||||
String[] entry = resourceIdFile.split("=");
|
SAXParser saxp = SAXParserFactory.newInstance().newSAXParser();
|
||||||
resourceIdFileEntries.put(entry[1], entry[0]);
|
final String prefix = entry.getKey();
|
||||||
|
saxp.parse(entry.getValue(), new DefaultHandler() {
|
||||||
|
public void startElement(String uri, String localName, String qName, Attributes attr)
|
||||||
|
throws SAXException {
|
||||||
|
if (qName.equals("public")) {
|
||||||
|
String resourceType = attr.getValue("type");
|
||||||
|
String resourceName = attr.getValue("name").replace('.', '_');
|
||||||
|
Integer resourceId = Integer.decode(attr.getValue("id"));
|
||||||
|
String qualifiedResourceName =
|
||||||
|
String.format("%s.%s.%s", prefix, resourceType, resourceName);
|
||||||
|
resourceIds.put(resourceId, qualifiedResourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
continue;
|
||||||
|
} catch (SAXException e) {
|
||||||
|
continue;
|
||||||
|
} catch (IOException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
36
baksmali/src/main/java/org/jf/baksmali/Command.java
Normal file
36
baksmali/src/main/java/org/jf/baksmali/Command.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali;
|
||||||
|
|
||||||
|
public interface Command {
|
||||||
|
void run();
|
||||||
|
}
|
98
baksmali/src/main/java/org/jf/baksmali/DeodexCommand.java
Normal file
98
baksmali/src/main/java/org/jf/baksmali/DeodexCommand.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import org.jf.dexlib2.analysis.CustomInlineMethodResolver;
|
||||||
|
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||||
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Deodexes an odex/oat file")
|
||||||
|
public class DeodexCommand extends DisassembleCommand {
|
||||||
|
@Parameter(names = "--check-package-private-access",
|
||||||
|
description = "Use the package-private access check when calculating vtable indexes. This should " +
|
||||||
|
"only be needed for 4.2.0 odexes. It was reverted in 4.2.1.")
|
||||||
|
private boolean checkPackagePrivateAccess = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--inline-table",
|
||||||
|
description = "Specify a file containing a custom inline method table to use. See the " +
|
||||||
|
"\"deodexerant\" tool in the smali github repository to dump the inline method table from a " +
|
||||||
|
"device that uses dalvik.")
|
||||||
|
private String inlineTable;
|
||||||
|
|
||||||
|
public DeodexCommand(@Nonnull JCommander jc) {
|
||||||
|
super(jc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected BaksmaliOptions getOptions(DexFile dexFile) {
|
||||||
|
BaksmaliOptions options = super.getOptions(dexFile);
|
||||||
|
|
||||||
|
options.deodex = true;
|
||||||
|
|
||||||
|
if (dexFile instanceof DexBackedOdexFile) {
|
||||||
|
if (inlineTable == null) {
|
||||||
|
options.inlineResolver = InlineMethodResolver.createInlineMethodResolver(
|
||||||
|
((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||||
|
} else {
|
||||||
|
File inlineTableFile = new File(inlineTable);
|
||||||
|
if (!inlineTableFile.exists()) {
|
||||||
|
System.err.println(String.format("Could not find file: %s", inlineTable));
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
options.inlineResolver = new CustomInlineMethodResolver(options.classPath, inlineTableFile);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err.println(String.format("Error while reading file: %s", inlineTableFile));
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected boolean shouldCheckPackagePrivateAccess() {
|
||||||
|
return checkPackagePrivateAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected boolean needsClassPath() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
330
baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java
Normal file
330
baksmali/src/main/java/org/jf/baksmali/DisassembleCommand.java
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import com.beust.jcommander.validators.PositiveInteger;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import org.jf.dexlib2.DexFileFactory;
|
||||||
|
import org.jf.dexlib2.analysis.ClassPath;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||||
|
import org.jf.dexlib2.dexbacked.OatFile;
|
||||||
|
import org.jf.dexlib2.iface.DexFile;
|
||||||
|
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||||
|
import org.jf.util.jcommander.CommaColonParameterSplitter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Disassembles a dex file.")
|
||||||
|
public class DisassembleCommand implements Command {
|
||||||
|
|
||||||
|
@Nonnull private final JCommander jc;
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information for this command.")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-a", "--api"},
|
||||||
|
description = "The numeric api level of the file being disassembled.")
|
||||||
|
private int apiLevel = 15;
|
||||||
|
|
||||||
|
@Parameter(names = "--debug-info", arity = 1,
|
||||||
|
description = "Whether to include debug information in the output (.local, .param, .line, etc.). Use " +
|
||||||
|
"--debug-info=false to disable.")
|
||||||
|
private boolean debugInfo = true;
|
||||||
|
|
||||||
|
@Parameter(names = {"-b", "--bootclasspath"},
|
||||||
|
description = "A comma/colon separated list of the bootclasspath jar/oat files to include in the " +
|
||||||
|
"classpath when analyzing the dex file. This will override any automatic selection of " +
|
||||||
|
"bootclasspath files that baksmali would otherwise perform. This is analogous to Android's " +
|
||||||
|
"BOOTCLASSPATH environment variable.",
|
||||||
|
splitter = CommaColonParameterSplitter.class)
|
||||||
|
private List<String> bootClassPath = new ArrayList<String>();
|
||||||
|
|
||||||
|
@Parameter(names = {"-c", "--classpath"},
|
||||||
|
description = "A comma/colon separated list of additional jar/oat files to include in the classpath " +
|
||||||
|
"when analyzing the dex file. These will be added to the classpath after any bootclasspath " +
|
||||||
|
"entries.",
|
||||||
|
splitter = CommaColonParameterSplitter.class)
|
||||||
|
private List<String> classPath = Lists.newArrayList();
|
||||||
|
|
||||||
|
@Parameter(names = {"-d", "--classpath-dir"},
|
||||||
|
description = "baksmali will search these directories in order for any classpath entries.")
|
||||||
|
private List<String> classPathDirectories = Lists.newArrayList(".");
|
||||||
|
|
||||||
|
@Parameter(names = {"--code-offsets"},
|
||||||
|
description = "Add comments to the disassembly containing the code offset within the method for each " +
|
||||||
|
"instruction.")
|
||||||
|
private boolean codeOffsets = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--resolve-resources", arity=1,
|
||||||
|
description = "This will attempt to find any resource id references within the bytecode and add a " +
|
||||||
|
"comment with the name of the resource being referenced. The value should be a comma/colon" +
|
||||||
|
"separated list of prefix=file pairs. For example R=res/values/public.xml:android.R=" +
|
||||||
|
"$ANDROID_HOME/platforms/android-19/data/res/values/public.xml")
|
||||||
|
private List<String> resourceIdFiles = Lists.newArrayList();
|
||||||
|
|
||||||
|
@Parameter(names = {"-j", "--jobs"},
|
||||||
|
description = "The number of threads to use. Defaults to the number of cores available.",
|
||||||
|
validateWith = PositiveInteger.class)
|
||||||
|
private int jobs = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
|
@Parameter(names = {"-l", "--use-locals"},
|
||||||
|
description = "When disassembling, output the .locals directive with the number of non-parameter " +
|
||||||
|
"registers instead of the .registers directive with the total number of registers.")
|
||||||
|
private boolean localsDirective = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--accessor-comments", arity = 1,
|
||||||
|
description = "Generate helper comments for synthetic accessors. Use --accessor-comments=false to disable.")
|
||||||
|
private boolean accessorComments = true;
|
||||||
|
|
||||||
|
@Parameter(names = "--normalize-virtual-methods",
|
||||||
|
description = "Normalize virtual method references to use the base class where the method is " +
|
||||||
|
"originally declared.")
|
||||||
|
private boolean normalizeVirtualMethods = false;
|
||||||
|
|
||||||
|
@Parameter(names = {"-o", "--output"},
|
||||||
|
description = "The directory to write the disassembled files to.")
|
||||||
|
private String outputDir = "out";
|
||||||
|
|
||||||
|
@Parameter(names = "--parameter-registers", arity = 1,
|
||||||
|
description = "Use the pNN syntax for registers that refer to a method parameter on method entry. Use" +
|
||||||
|
"--parameter-registers=false to disable.")
|
||||||
|
private boolean parameterRegisters = true;
|
||||||
|
|
||||||
|
@Parameter(names = {"-r", "--register-info"}, arity=1,
|
||||||
|
description = "Add comments before/after each instruction with information about register types. " +
|
||||||
|
"The value is a comma-separated list of any of ALL, ALLPRE, ALLPOST, ARGS, DEST, MERGE and " +
|
||||||
|
"FULLMERGE. See \"baksmali help register-info\" for more information.")
|
||||||
|
private List<String> registerInfoTypes = Lists.newArrayList();
|
||||||
|
|
||||||
|
@Parameter(names = "--sequential-labels",
|
||||||
|
description = "Create label names using a sequential numbering scheme per label type, rather than " +
|
||||||
|
"using the bytecode address.")
|
||||||
|
private boolean sequentialLabels = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--implicit-references",
|
||||||
|
description = "Use implicit (without the class name) method and field references for methods and " +
|
||||||
|
"fields from the current class.")
|
||||||
|
private boolean implicitReferences = false;
|
||||||
|
|
||||||
|
@Parameter(names = "--experimental",
|
||||||
|
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
||||||
|
"supported in the Android runtime yet.")
|
||||||
|
private boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
|
@Parameter(description = "<file> - A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " +
|
||||||
|
"files, you can specify which dex file to disassemble by appending the name of the dex file with a " +
|
||||||
|
"colon. E.g. \"something.apk:classes2.dex\"")
|
||||||
|
private List<String> inputList = Lists.newArrayList();
|
||||||
|
|
||||||
|
public DisassembleCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
if (help || inputList == null || inputList.isEmpty()) {
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputList.size() > 1) {
|
||||||
|
System.err.println("Too many files specified");
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String input = inputList.get(0);
|
||||||
|
File dexFileFile = new File(input);
|
||||||
|
String dexFileEntry = null;
|
||||||
|
if (!dexFileFile.exists()) {
|
||||||
|
int colonIndex = input.lastIndexOf(':');
|
||||||
|
|
||||||
|
if (colonIndex >= 0) {
|
||||||
|
dexFileFile = new File(input.substring(0, colonIndex));
|
||||||
|
dexFileEntry = input.substring(colonIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dexFileFile.exists()) {
|
||||||
|
System.err.println("Can't find the file " + input);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Read in and parse the dex file
|
||||||
|
DexBackedDexFile dexFile = null;
|
||||||
|
try {
|
||||||
|
dexFile = DexFileFactory.loadDexFile(dexFileFile, dexFileEntry, apiLevel, experimentalOpcodes);
|
||||||
|
} catch (DexFileFactory.MultipleDexFilesException ex) {
|
||||||
|
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
|
||||||
|
"disassemble with the -e option", dexFileFile.getName()));
|
||||||
|
System.err.println("Valid entries include:");
|
||||||
|
|
||||||
|
for (OatFile.OatDexFile oatDexFile : ex.oatFile.getDexFiles()) {
|
||||||
|
System.err.println(oatDexFile.filename);
|
||||||
|
}
|
||||||
|
System.exit(1);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dexFile.hasOdexOpcodes()) {
|
||||||
|
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
|
||||||
|
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
|
||||||
|
System.err.println("option");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsClassPath() && bootClassPath.isEmpty()) {
|
||||||
|
if (dexFile instanceof DexBackedOdexFile) {
|
||||||
|
bootClassPath = ((DexBackedOdexFile)dexFile).getDependencies();
|
||||||
|
} else {
|
||||||
|
bootClassPath = Baksmali.getDefaultBootClassPath(apiLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File outputDirectoryFile = new File(outputDir);
|
||||||
|
if (!outputDirectoryFile.exists()) {
|
||||||
|
if (!outputDirectoryFile.mkdirs()) {
|
||||||
|
System.err.println("Can't create the output directory " + outputDir);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Baksmali.disassembleDexFile(dexFile, outputDirectoryFile, jobs, getOptions(dexFile))) {
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected boolean needsClassPath() {
|
||||||
|
return !registerInfoTypes.isEmpty() || normalizeVirtualMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldCheckPackagePrivateAccess() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BaksmaliOptions getOptions(DexFile dexFile) {
|
||||||
|
final BaksmaliOptions options = new BaksmaliOptions();
|
||||||
|
|
||||||
|
if (needsClassPath()) {
|
||||||
|
try {
|
||||||
|
options.classPath = ClassPath.fromClassPath(classPathDirectories,
|
||||||
|
Iterables.concat(bootClassPath, classPath), dexFile, apiLevel,
|
||||||
|
shouldCheckPackagePrivateAccess(), experimentalOpcodes);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("\n\nError occurred while loading class path files. Aborting.");
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resourceIdFiles.isEmpty()) {
|
||||||
|
Map<String, File> resourceFiles = Maps.newHashMap();
|
||||||
|
|
||||||
|
for (String resourceIdFileSpec: resourceIdFiles) {
|
||||||
|
int separatorIndex = resourceIdFileSpec.indexOf('=');
|
||||||
|
if (separatorIndex == -1) {
|
||||||
|
System.err.println(String.format("Invalid resource id spec: %s", resourceIdFileSpec));
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
String prefix = resourceIdFileSpec.substring(0, separatorIndex);
|
||||||
|
String resourceIdFilePath = resourceIdFileSpec.substring(separatorIndex+1);
|
||||||
|
File resourceIdFile = new File(resourceIdFilePath);
|
||||||
|
|
||||||
|
if (!resourceIdFile.exists()) {
|
||||||
|
System.err.println(String.format("Can't find file: %s", resourceIdFilePath));
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceFiles.put(prefix, resourceIdFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.loadResourceIds(resourceFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.parameterRegisters = parameterRegisters;
|
||||||
|
options.localsDirective = localsDirective;
|
||||||
|
options.sequentialLabels = sequentialLabels;
|
||||||
|
options.debugInfo = debugInfo;
|
||||||
|
options.codeOffsets = codeOffsets;
|
||||||
|
options.accessorComments = accessorComments;
|
||||||
|
options.experimentalOpcodes = experimentalOpcodes;
|
||||||
|
options.implicitReferences = implicitReferences;
|
||||||
|
options.normalizeVirtualMethods = normalizeVirtualMethods;
|
||||||
|
|
||||||
|
options.registerInfo = 0;
|
||||||
|
|
||||||
|
for (String registerInfoType: registerInfoTypes) {
|
||||||
|
if (registerInfoType.equalsIgnoreCase("ALL")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.ALL;
|
||||||
|
} else if (registerInfoType.equalsIgnoreCase("ALLPRE")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.ALLPRE;
|
||||||
|
} else if (registerInfoType.equalsIgnoreCase("ALLPOST")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.ALLPOST;
|
||||||
|
} else if (registerInfoType.equalsIgnoreCase("ARGS")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.ARGS;
|
||||||
|
} else if (registerInfoType.equalsIgnoreCase("DEST")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.DEST;
|
||||||
|
} else if (registerInfoType.equalsIgnoreCase("MERGE")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.MERGE;
|
||||||
|
} else if (registerInfoType.equalsIgnoreCase("FULLMERGE")) {
|
||||||
|
options.registerInfo |= BaksmaliOptions.FULLMERGE;
|
||||||
|
} else {
|
||||||
|
System.err.println(String.format("Invalid register info type: %s", registerInfoType));
|
||||||
|
jc.usage(jc.getParsedCommand());
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((options.registerInfo & BaksmaliOptions.FULLMERGE) != 0) {
|
||||||
|
options.registerInfo &= ~BaksmaliOptions.MERGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessorComments) {
|
||||||
|
options.syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getOpcodes(),
|
||||||
|
dexFile.getClasses());
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
149
baksmali/src/main/java/org/jf/baksmali/DumpCommand.java
Normal file
149
baksmali/src/main/java/org/jf/baksmali/DumpCommand.java
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import org.jf.dexlib2.DexFileFactory;
|
||||||
|
import org.jf.dexlib2.Opcodes;
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||||
|
import org.jf.dexlib2.dexbacked.OatFile;
|
||||||
|
import org.jf.dexlib2.dexbacked.raw.RawDexFile;
|
||||||
|
import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
|
||||||
|
import org.jf.util.ConsoleUtil;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Prints an annotated hex dump for the given dex file")
|
||||||
|
public class DumpCommand implements Command {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private final JCommander jc;
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information for this command.")
|
||||||
|
public boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-a", "--api"},
|
||||||
|
description = "The numeric api level of the file being disassembled.")
|
||||||
|
public int apiLevel = 15;
|
||||||
|
|
||||||
|
@Parameter(names = "--experimental",
|
||||||
|
description = "Enable experimental opcodes to be disassembled, even if they aren't necessarily " +
|
||||||
|
"supported in the Android runtime yet.")
|
||||||
|
public boolean experimentalOpcodes = false;
|
||||||
|
|
||||||
|
@Parameter(description = "<file> - A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " +
|
||||||
|
"files, you can specify which dex file to disassemble by appending the name of the dex file with a " +
|
||||||
|
"colon. E.g. \"something.apk:classes2.dex\"")
|
||||||
|
public String input;
|
||||||
|
|
||||||
|
public DumpCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
if (help || input == null || input.isEmpty()) {
|
||||||
|
jc.usage("dump");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String inputDexPath = input;
|
||||||
|
|
||||||
|
File dexFileFile = new File(inputDexPath);
|
||||||
|
String dexFileEntry = null;
|
||||||
|
if (!dexFileFile.exists()) {
|
||||||
|
int colonIndex = inputDexPath.lastIndexOf(':');
|
||||||
|
|
||||||
|
if (colonIndex >= 0) {
|
||||||
|
dexFileFile = new File(inputDexPath.substring(0, colonIndex));
|
||||||
|
dexFileEntry = inputDexPath.substring(colonIndex+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dexFileFile.exists()) {
|
||||||
|
System.err.println("Can't find the file " + inputDexPath);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DexBackedDexFile dexFile = null;
|
||||||
|
try {
|
||||||
|
dexFile = DexFileFactory.loadDexFile(dexFileFile, dexFileEntry, apiLevel, experimentalOpcodes);
|
||||||
|
} catch (DexFileFactory.MultipleDexFilesException ex) {
|
||||||
|
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
|
||||||
|
"disassemble with the -e option", dexFileFile.getName()));
|
||||||
|
System.err.println("Valid entries include:");
|
||||||
|
|
||||||
|
for (OatFile.OatDexFile oatDexFile: ex.oatFile.getDexFiles()) {
|
||||||
|
System.err.println(oatDexFile.filename);
|
||||||
|
}
|
||||||
|
System.exit(1);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err.println("There was an error while reading the dex file");
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dump(dexFile, System.out, apiLevel);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err.println("There was an error while dumping the dex file");
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an annotated hex dump of the given dex file to output.
|
||||||
|
*
|
||||||
|
* @param dexFile The dex file to dump
|
||||||
|
* @param output An OutputStream to write the annotated hex dump to. The caller is responsible for closing this
|
||||||
|
* when needed.
|
||||||
|
* @param apiLevel The api level to use when dumping the dex file
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void dump(@Nonnull DexBackedDexFile dexFile, @Nonnull OutputStream output, int apiLevel)
|
||||||
|
throws IOException {
|
||||||
|
Writer writer = new BufferedWriter(new OutputStreamWriter(output));
|
||||||
|
|
||||||
|
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
||||||
|
if (consoleWidth <= 0) {
|
||||||
|
consoleWidth = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawDexFile rawDexFile = new RawDexFile(Opcodes.forApi(apiLevel), dexFile);
|
||||||
|
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
|
||||||
|
annotator.writeAnnotations(writer);
|
||||||
|
}
|
||||||
|
}
|
95
baksmali/src/main/java/org/jf/baksmali/HelpCommand.java
Normal file
95
baksmali/src/main/java/org/jf/baksmali/HelpCommand.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import com.beust.jcommander.Parameters;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.jf.util.ConsoleUtil;
|
||||||
|
import org.jf.util.StringWrapper;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parameters(commandDescription = "Shows usage information")
|
||||||
|
public class HelpCommand implements Command {
|
||||||
|
@Nonnull private final JCommander jc;
|
||||||
|
|
||||||
|
public HelpCommand(@Nonnull JCommander jc) {
|
||||||
|
this.jc = jc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameter(description = "If specified, only show the usage information for the given commands")
|
||||||
|
private List<String> commands = Lists.newArrayList();
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
if (commands == null || commands.isEmpty()) {
|
||||||
|
jc.usage();
|
||||||
|
} else {
|
||||||
|
for (String cmd : commands) {
|
||||||
|
if (cmd.equals("register-info")) {
|
||||||
|
String registerInfoHelp = "The --register-info parameter will cause baksmali to generate " +
|
||||||
|
"comments before and after every instruction containing register type " +
|
||||||
|
"information about some subset of registers. This parameter optionally accepts a " +
|
||||||
|
"comma-separated list of values specifying which registers and how much " +
|
||||||
|
"information to include. If no values are specified, \"ARGS,DEST\" is used as " +
|
||||||
|
"the default. Valid values include:\n" +
|
||||||
|
" ALL: all pre- and post-instruction registers\n" +
|
||||||
|
" ALLPRE: all pre-instruction registers\n" +
|
||||||
|
" ALLPOST: all post-instruction registers\n" +
|
||||||
|
" ARGS: any pre-instruction registers used as arguments to the instruction\n" +
|
||||||
|
" DEST: the post-instruction register used as the output of the instruction\n" +
|
||||||
|
" MERGE: any pre-instruction register that has been merged from multiple " +
|
||||||
|
"incoming code paths\n" +
|
||||||
|
" FULLMERGE: an extended version of MERGE that also includes a list of all " +
|
||||||
|
"the register types from incoming code paths that were merged";
|
||||||
|
|
||||||
|
Iterable<String> lines = StringWrapper.wrapStringOnBreaks(registerInfoHelp,
|
||||||
|
ConsoleUtil.getConsoleWidth());
|
||||||
|
for (String line : lines) {
|
||||||
|
System.out.println(line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
jc.usage(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(hidden = true)
|
||||||
|
public static class HlepCommand extends HelpCommand {
|
||||||
|
public HlepCommand(@Nonnull JCommander jc) {
|
||||||
|
super(jc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
baksmali/src/main/java/org/jf/baksmali/Main.java
Normal file
101
baksmali/src/main/java/org/jf/baksmali/Main.java
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali;
|
||||||
|
|
||||||
|
import com.beust.jcommander.JCommander;
|
||||||
|
import com.beust.jcommander.Parameter;
|
||||||
|
import org.jf.baksmali.HelpCommand.HlepCommand;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static final String VERSION = loadVersion();
|
||||||
|
|
||||||
|
@Parameter(names = {"-h", "-?", "--help"}, help = true,
|
||||||
|
description = "Show usage information")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Parameter(names = {"-v", "--version"}, help = true,
|
||||||
|
description = "Print the version of baksmali and then exit")
|
||||||
|
public boolean version;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Main main = new Main();
|
||||||
|
|
||||||
|
JCommander jc = new JCommander(main);
|
||||||
|
|
||||||
|
jc.addCommand("disassemble", new DisassembleCommand(jc), "dis", "d");
|
||||||
|
jc.addCommand("deodex", new DeodexCommand(jc), "de", "x");
|
||||||
|
jc.addCommand("dump", new DumpCommand(jc), "du");
|
||||||
|
jc.addCommand("help", new HelpCommand(jc), "h");
|
||||||
|
jc.addCommand("hlep", new HlepCommand(jc));
|
||||||
|
|
||||||
|
jc.parse(args);
|
||||||
|
|
||||||
|
if (jc.getParsedCommand() == null || main.help) {
|
||||||
|
jc.usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (main.version) {
|
||||||
|
version();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command command = (Command)jc.getCommands().get(jc.getParsedCommand()).getObjects().get(0);
|
||||||
|
command.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void version() {
|
||||||
|
System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
|
||||||
|
System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
|
||||||
|
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String loadVersion() {
|
||||||
|
InputStream propertiesStream = Baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
|
||||||
|
String version = "[unknown version]";
|
||||||
|
if (propertiesStream != null) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try {
|
||||||
|
properties.load(propertiesStream);
|
||||||
|
version = properties.getProperty("application.version");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* [The "BSD licence"]
|
|
||||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jf.baksmali;
|
|
||||||
|
|
||||||
import org.jf.dexlib2.Opcodes;
|
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
|
||||||
import org.jf.dexlib2.dexbacked.raw.RawDexFile;
|
|
||||||
import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
|
|
||||||
import org.jf.util.ConsoleUtil;
|
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
|
|
||||||
public class dump {
|
|
||||||
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
|
|
||||||
if (dumpFileName != null) {
|
|
||||||
Writer writer = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
writer = new BufferedWriter(new FileWriter(dumpFileName));
|
|
||||||
|
|
||||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
|
||||||
if (consoleWidth <= 0) {
|
|
||||||
consoleWidth = 120;
|
|
||||||
}
|
|
||||||
|
|
||||||
RawDexFile rawDexFile = new RawDexFile(Opcodes.forApi(apiLevel), dexFile);
|
|
||||||
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
|
|
||||||
annotator.writeAnnotations(writer);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.err.println("There was an error while dumping the dex file to " + dumpFileName);
|
|
||||||
ex.printStackTrace(System.err);
|
|
||||||
} finally {
|
|
||||||
if (writer != null) {
|
|
||||||
try {
|
|
||||||
writer.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
System.err.println("There was an error while closing the dump file " + dumpFileName);
|
|
||||||
ex.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,612 +0,0 @@
|
|||||||
/*
|
|
||||||
* [The "BSD licence"]
|
|
||||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jf.baksmali;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.apache.commons.cli.*;
|
|
||||||
import org.jf.dexlib2.DexFileFactory;
|
|
||||||
import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
|
|
||||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
|
||||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
|
||||||
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
|
||||||
import org.jf.dexlib2.iface.DexFile;
|
|
||||||
import org.jf.util.ConsoleUtil;
|
|
||||||
import org.jf.util.SmaliHelpFormatter;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
public class main {
|
|
||||||
|
|
||||||
public static final String VERSION;
|
|
||||||
|
|
||||||
private static final Options basicOptions;
|
|
||||||
private static final Options debugOptions;
|
|
||||||
private static final Options options;
|
|
||||||
|
|
||||||
static {
|
|
||||||
options = new Options();
|
|
||||||
basicOptions = new Options();
|
|
||||||
debugOptions = new Options();
|
|
||||||
buildOptions();
|
|
||||||
|
|
||||||
InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
|
|
||||||
if (templateStream != null) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
String version = "(unknown)";
|
|
||||||
try {
|
|
||||||
properties.load(templateStream);
|
|
||||||
version = properties.getProperty("application.version");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
VERSION = version;
|
|
||||||
} else {
|
|
||||||
VERSION = "[unknown version]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is uninstantiable.
|
|
||||||
*/
|
|
||||||
private main() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A more programmatic-friendly entry point for baksmali
|
|
||||||
*
|
|
||||||
* @param options a baksmaliOptions object with the options to run baksmali with
|
|
||||||
* @param inputDexFile The DexFile to disassemble
|
|
||||||
* @return true if disassembly completed with no errors, or false if errors were encountered
|
|
||||||
*/
|
|
||||||
public static boolean run(@Nonnull baksmaliOptions options, @Nonnull DexFile inputDexFile) throws IOException {
|
|
||||||
if (options.bootClassPathEntries.isEmpty() &&
|
|
||||||
(options.deodex || options.registerInfo != 0 || options.normalizeVirtualMethods)) {
|
|
||||||
if (inputDexFile instanceof DexBackedOdexFile) {
|
|
||||||
options.bootClassPathEntries = ((DexBackedOdexFile)inputDexFile).getDependencies();
|
|
||||||
} else {
|
|
||||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
|
|
||||||
options.experimental);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.customInlineDefinitions == null && inputDexFile instanceof DexBackedOdexFile) {
|
|
||||||
options.inlineResolver =
|
|
||||||
InlineMethodResolver.createInlineMethodResolver(
|
|
||||||
((DexBackedOdexFile)inputDexFile).getOdexVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean errorOccurred = false;
|
|
||||||
if (options.disassemble) {
|
|
||||||
errorOccurred = !baksmali.disassembleDexFile(inputDexFile, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.dump) {
|
|
||||||
if (!(inputDexFile instanceof DexBackedDexFile)) {
|
|
||||||
throw new IllegalArgumentException("Annotated hex-dumps require a DexBackedDexFile");
|
|
||||||
}
|
|
||||||
dump.dump((DexBackedDexFile)inputDexFile, options.dumpFileName, options.apiLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !errorOccurred;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run!
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
Locale locale = new Locale("en", "US");
|
|
||||||
Locale.setDefault(locale);
|
|
||||||
|
|
||||||
CommandLineParser parser = new PosixParser();
|
|
||||||
CommandLine commandLine;
|
|
||||||
|
|
||||||
try {
|
|
||||||
commandLine = parser.parse(options, args);
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
|
||||||
|
|
||||||
String[] remainingArgs = commandLine.getArgs();
|
|
||||||
Option[] clOptions = commandLine.getOptions();
|
|
||||||
|
|
||||||
for (int i=0; i<clOptions.length; i++) {
|
|
||||||
Option option = clOptions[i];
|
|
||||||
String opt = option.getOpt();
|
|
||||||
|
|
||||||
switch (opt.charAt(0)) {
|
|
||||||
case 'v':
|
|
||||||
version();
|
|
||||||
return;
|
|
||||||
case '?':
|
|
||||||
while (++i < clOptions.length) {
|
|
||||||
if (clOptions[i].getOpt().charAt(0) == '?') {
|
|
||||||
usage(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usage(false);
|
|
||||||
return;
|
|
||||||
case 'o':
|
|
||||||
options.outputDirectory = commandLine.getOptionValue("o");
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
options.noParameterRegisters = true;
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
options.useLocalsDirective = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
options.useSequentialLabels = true;
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
options.outputDebugInfo = false;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
options.bootClassPathDirs.add(option.getValue());
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
options.addCodeOffsets = true;
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
String[] values = commandLine.getOptionValues('r');
|
|
||||||
int registerInfo = 0;
|
|
||||||
|
|
||||||
if (values == null || values.length == 0) {
|
|
||||||
registerInfo = baksmaliOptions.ARGS | baksmaliOptions.DEST;
|
|
||||||
} else {
|
|
||||||
for (String value: values) {
|
|
||||||
if (value.equalsIgnoreCase("ALL")) {
|
|
||||||
registerInfo |= baksmaliOptions.ALL;
|
|
||||||
} else if (value.equalsIgnoreCase("ALLPRE")) {
|
|
||||||
registerInfo |= baksmaliOptions.ALLPRE;
|
|
||||||
} else if (value.equalsIgnoreCase("ALLPOST")) {
|
|
||||||
registerInfo |= baksmaliOptions.ALLPOST;
|
|
||||||
} else if (value.equalsIgnoreCase("ARGS")) {
|
|
||||||
registerInfo |= baksmaliOptions.ARGS;
|
|
||||||
} else if (value.equalsIgnoreCase("DEST")) {
|
|
||||||
registerInfo |= baksmaliOptions.DEST;
|
|
||||||
} else if (value.equalsIgnoreCase("MERGE")) {
|
|
||||||
registerInfo |= baksmaliOptions.MERGE;
|
|
||||||
} else if (value.equalsIgnoreCase("FULLMERGE")) {
|
|
||||||
registerInfo |= baksmaliOptions.FULLMERGE;
|
|
||||||
} else {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((registerInfo & baksmaliOptions.FULLMERGE) != 0) {
|
|
||||||
registerInfo &= ~baksmaliOptions.MERGE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
options.registerInfo = registerInfo;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
String bcp = commandLine.getOptionValue("c");
|
|
||||||
if (bcp != null && bcp.charAt(0) == ':') {
|
|
||||||
options.addExtraClassPath(bcp);
|
|
||||||
} else {
|
|
||||||
options.setBootClassPath(bcp);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
options.deodex = true;
|
|
||||||
break;
|
|
||||||
case 'X':
|
|
||||||
options.experimental = true;
|
|
||||||
break;
|
|
||||||
case 'm':
|
|
||||||
options.noAccessorComments = true;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
options.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
options.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
String rif = commandLine.getOptionValue("i");
|
|
||||||
options.setResourceIdFiles(rif);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
options.useImplicitReferences = true;
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
options.dexEntry = commandLine.getOptionValue("e");
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
options.checkPackagePrivateAccess = true;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
options.normalizeVirtualMethods = true;
|
|
||||||
break;
|
|
||||||
case 'N':
|
|
||||||
options.disassemble = false;
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
options.dump = true;
|
|
||||||
options.dumpFileName = commandLine.getOptionValue("D");
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
options.ignoreErrors = true;
|
|
||||||
break;
|
|
||||||
case 'T':
|
|
||||||
options.customInlineDefinitions = new File(commandLine.getOptionValue("T"));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingArgs.length != 1) {
|
|
||||||
usage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String inputDexPath = remainingArgs[0];
|
|
||||||
File dexFileFile = new File(inputDexPath);
|
|
||||||
if (!dexFileFile.exists()) {
|
|
||||||
System.err.println("Can't find the file " + inputDexPath);
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Read in and parse the dex file
|
|
||||||
DexBackedDexFile dexFile = null;
|
|
||||||
try {
|
|
||||||
dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel, options.experimental);
|
|
||||||
} catch (MultipleDexFilesException ex) {
|
|
||||||
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
|
|
||||||
"disassemble with the -e option", dexFileFile.getName()));
|
|
||||||
System.err.println("Valid entries include:");
|
|
||||||
|
|
||||||
for (OatDexFile oatDexFile: ex.oatFile.getDexFiles()) {
|
|
||||||
System.err.println(oatDexFile.filename);
|
|
||||||
}
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dexFile.hasOdexOpcodes()) {
|
|
||||||
if (!options.deodex) {
|
|
||||||
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
|
|
||||||
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
|
|
||||||
System.err.println("option");
|
|
||||||
options.allowOdex = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
options.deodex = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.dump) {
|
|
||||||
if (options.dumpFileName == null) {
|
|
||||||
options.dumpFileName = inputDexPath + ".dump";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!run(options, dexFile)) {
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
System.err.println(ex.getMessage());
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the usage message.
|
|
||||||
*/
|
|
||||||
private static void usage(boolean printDebugOptions) {
|
|
||||||
SmaliHelpFormatter formatter = new SmaliHelpFormatter();
|
|
||||||
int consoleWidth = ConsoleUtil.getConsoleWidth();
|
|
||||||
if (consoleWidth <= 0) {
|
|
||||||
consoleWidth = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.setWidth(consoleWidth);
|
|
||||||
|
|
||||||
formatter.printHelp("java -jar baksmali.jar [options] <dex-file>",
|
|
||||||
"disassembles and/or dumps a dex file", basicOptions, printDebugOptions?debugOptions:null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void usage() {
|
|
||||||
usage(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the version message.
|
|
||||||
*/
|
|
||||||
protected static void version() {
|
|
||||||
System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
|
|
||||||
System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)");
|
|
||||||
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("AccessStaticViaInstance")
|
|
||||||
private static void buildOptions() {
|
|
||||||
Option versionOption = OptionBuilder.withLongOpt("version")
|
|
||||||
.withDescription("prints the version then exits")
|
|
||||||
.create("v");
|
|
||||||
|
|
||||||
Option helpOption = OptionBuilder.withLongOpt("help")
|
|
||||||
.withDescription("prints the help message then exits. Specify twice for debug options")
|
|
||||||
.create("?");
|
|
||||||
|
|
||||||
Option outputDirOption = OptionBuilder.withLongOpt("output")
|
|
||||||
.withDescription("the directory where the disassembled files will be placed. The default is out")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("DIR")
|
|
||||||
.create("o");
|
|
||||||
|
|
||||||
Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers")
|
|
||||||
.withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method " +
|
|
||||||
"parameters")
|
|
||||||
.create("p");
|
|
||||||
|
|
||||||
Option deodexerantOption = OptionBuilder.withLongOpt("deodex")
|
|
||||||
.withDescription("deodex the given odex file. This option is ignored if the input file is not an " +
|
|
||||||
"odex file")
|
|
||||||
.create("x");
|
|
||||||
|
|
||||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
|
||||||
.withDescription("enable experimental opcodes to be disassembled, even if they aren't necessarily supported in the Android runtime yet")
|
|
||||||
.create("X");
|
|
||||||
|
|
||||||
Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
|
|
||||||
.withDescription("output the .locals directive with the number of non-parameter registers, rather" +
|
|
||||||
" than the .register directive with the total number of register")
|
|
||||||
.create("l");
|
|
||||||
|
|
||||||
Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels")
|
|
||||||
.withDescription("create label names using a sequential numbering scheme per label type, rather than " +
|
|
||||||
"using the bytecode address")
|
|
||||||
.create("s");
|
|
||||||
|
|
||||||
Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info")
|
|
||||||
.withDescription("don't write out debug info (.local, .param, .line, etc.)")
|
|
||||||
.create("b");
|
|
||||||
|
|
||||||
Option registerInfoOption = OptionBuilder.withLongOpt("register-info")
|
|
||||||
.hasOptionalArgs()
|
|
||||||
.withArgName("REGISTER_INFO_TYPES")
|
|
||||||
.withValueSeparator(',')
|
|
||||||
.withDescription("print the specificed type(s) of register information for each instruction. " +
|
|
||||||
"\"ARGS,DEST\" is the default if no types are specified.\nValid values are:\nALL: all " +
|
|
||||||
"pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " +
|
|
||||||
"post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " +
|
|
||||||
"instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " +
|
|
||||||
"pre-instruction register has been merged from more than 1 different post-instruction " +
|
|
||||||
"register from its predecessors\nFULLMERGE: For each register that would be printed by " +
|
|
||||||
"MERGE, also show the incoming register types that were merged")
|
|
||||||
.create("r");
|
|
||||||
|
|
||||||
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
|
||||||
.withDescription("A colon-separated list of bootclasspath jar/oat files to use for analysis. Add an " +
|
|
||||||
"initial colon to specify that the jars/oats should be appended to the default bootclasspath " +
|
|
||||||
"instead of replacing it")
|
|
||||||
.hasOptionalArg()
|
|
||||||
.withArgName("BOOTCLASSPATH")
|
|
||||||
.create("c");
|
|
||||||
|
|
||||||
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
|
|
||||||
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
|
|
||||||
"directory")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("DIR")
|
|
||||||
.create("d");
|
|
||||||
|
|
||||||
Option codeOffsetOption = OptionBuilder.withLongOpt("code-offsets")
|
|
||||||
.withDescription("add comments to the disassembly containing the code offset for each address")
|
|
||||||
.create("f");
|
|
||||||
|
|
||||||
Option noAccessorCommentsOption = OptionBuilder.withLongOpt("no-accessor-comments")
|
|
||||||
.withDescription("don't output helper comments for synthetic accessors")
|
|
||||||
.create("m");
|
|
||||||
|
|
||||||
Option apiLevelOption = OptionBuilder.withLongOpt("api-level")
|
|
||||||
.withDescription("The numeric api-level of the file being disassembled. If not " +
|
|
||||||
"specified, it defaults to 15 (ICS).")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("API_LEVEL")
|
|
||||||
.create("a");
|
|
||||||
|
|
||||||
Option jobsOption = OptionBuilder.withLongOpt("jobs")
|
|
||||||
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
|
||||||
"maximum of 6")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("NUM_THREADS")
|
|
||||||
.create("j");
|
|
||||||
|
|
||||||
Option resourceIdFilesOption = OptionBuilder.withLongOpt("resource-id-files")
|
|
||||||
.withDescription("the resource ID files to use, for analysis. A colon-separated list of prefix=file " +
|
|
||||||
"pairs. For example R=res/values/public.xml:" +
|
|
||||||
"android.R=$ANDROID_HOME/platforms/android-19/data/res/values/public.xml")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("FILES")
|
|
||||||
.create("i");
|
|
||||||
|
|
||||||
Option noImplicitReferencesOption = OptionBuilder.withLongOpt("implicit-references")
|
|
||||||
.withDescription("Use implicit (type-less) method and field references")
|
|
||||||
.create("t");
|
|
||||||
|
|
||||||
Option checkPackagePrivateAccessOption = OptionBuilder.withLongOpt("check-package-private-access")
|
|
||||||
.withDescription("When deodexing, use the package-private access check when calculating vtable " +
|
|
||||||
"indexes. It should only be needed for 4.2.0 odexes. The functionality was reverted for " +
|
|
||||||
"4.2.1.")
|
|
||||||
.create("k");
|
|
||||||
|
|
||||||
Option normalizeVirtualMethods = OptionBuilder.withLongOpt("normalize-virtual-methods")
|
|
||||||
.withDescription("Normalize virtual method references to the reference the base method.")
|
|
||||||
.create("n");
|
|
||||||
|
|
||||||
Option dumpOption = OptionBuilder.withLongOpt("dump-to")
|
|
||||||
.withDescription("dumps the given dex file into a single annotated dump file named FILE" +
|
|
||||||
" (<dexfile>.dump by default), along with the normal disassembly")
|
|
||||||
.hasOptionalArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("D");
|
|
||||||
|
|
||||||
Option ignoreErrorsOption = OptionBuilder.withLongOpt("ignore-errors")
|
|
||||||
.withDescription("ignores any non-fatal errors that occur while disassembling/deodexing," +
|
|
||||||
" ignoring the class if needed, and continuing with the next class. The default" +
|
|
||||||
" behavior is to stop disassembling and exit once an error is encountered")
|
|
||||||
.create("I");
|
|
||||||
|
|
||||||
Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly")
|
|
||||||
.withDescription("suppresses the output of the disassembly")
|
|
||||||
.create("N");
|
|
||||||
|
|
||||||
Option inlineTableOption = OptionBuilder.withLongOpt("inline-table")
|
|
||||||
.withDescription("specify a file containing a custom inline method table to use for deodexing")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("FILE")
|
|
||||||
.create("T");
|
|
||||||
|
|
||||||
Option dexEntryOption = OptionBuilder.withLongOpt("dex-file")
|
|
||||||
.withDescription("looks for dex file named DEX_FILE, defaults to classes.dex")
|
|
||||||
.withArgName("DEX_FILE")
|
|
||||||
.hasArg()
|
|
||||||
.create("e");
|
|
||||||
|
|
||||||
basicOptions.addOption(versionOption);
|
|
||||||
basicOptions.addOption(helpOption);
|
|
||||||
basicOptions.addOption(outputDirOption);
|
|
||||||
basicOptions.addOption(noParameterRegistersOption);
|
|
||||||
basicOptions.addOption(deodexerantOption);
|
|
||||||
basicOptions.addOption(experimentalOption);
|
|
||||||
basicOptions.addOption(useLocalsOption);
|
|
||||||
basicOptions.addOption(sequentialLabelsOption);
|
|
||||||
basicOptions.addOption(noDebugInfoOption);
|
|
||||||
basicOptions.addOption(registerInfoOption);
|
|
||||||
basicOptions.addOption(classPathOption);
|
|
||||||
basicOptions.addOption(classPathDirOption);
|
|
||||||
basicOptions.addOption(codeOffsetOption);
|
|
||||||
basicOptions.addOption(noAccessorCommentsOption);
|
|
||||||
basicOptions.addOption(apiLevelOption);
|
|
||||||
basicOptions.addOption(jobsOption);
|
|
||||||
basicOptions.addOption(resourceIdFilesOption);
|
|
||||||
basicOptions.addOption(noImplicitReferencesOption);
|
|
||||||
basicOptions.addOption(dexEntryOption);
|
|
||||||
basicOptions.addOption(checkPackagePrivateAccessOption);
|
|
||||||
basicOptions.addOption(normalizeVirtualMethods);
|
|
||||||
|
|
||||||
debugOptions.addOption(dumpOption);
|
|
||||||
debugOptions.addOption(ignoreErrorsOption);
|
|
||||||
debugOptions.addOption(noDisassemblyOption);
|
|
||||||
debugOptions.addOption(inlineTableOption);
|
|
||||||
|
|
||||||
for (Object option: basicOptions.getOptions()) {
|
|
||||||
options.addOption((Option)option);
|
|
||||||
}
|
|
||||||
for (Object option: debugOptions.getOptions()) {
|
|
||||||
options.addOption((Option)option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private static List<String> getDefaultBootClassPathForApi(int apiLevel, boolean experimental) {
|
|
||||||
if (apiLevel < 9) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar");
|
|
||||||
} else if (apiLevel < 12) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/core-junit.jar");
|
|
||||||
} else if (apiLevel < 14) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/apache-xml.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/core-junit.jar");
|
|
||||||
} else if (apiLevel < 16) {
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/core-junit.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/apache-xml.jar",
|
|
||||||
"/system/framework/filterfw.jar");
|
|
||||||
} else if (apiLevel < 21) {
|
|
||||||
// this is correct as of api 17/4.2.2
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core.jar",
|
|
||||||
"/system/framework/core-junit.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/telephony-common.jar",
|
|
||||||
"/system/framework/mms-common.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/services.jar",
|
|
||||||
"/system/framework/apache-xml.jar");
|
|
||||||
} else { // api >= 21
|
|
||||||
// TODO: verify, add new ones?
|
|
||||||
return Lists.newArrayList(
|
|
||||||
"/system/framework/core-libart.jar",
|
|
||||||
"/system/framework/conscrypt.jar",
|
|
||||||
"/system/framework/okhttp.jar",
|
|
||||||
"/system/framework/core-junit.jar",
|
|
||||||
"/system/framework/bouncycastle.jar",
|
|
||||||
"/system/framework/ext.jar",
|
|
||||||
"/system/framework/framework.jar",
|
|
||||||
"/system/framework/telephony-common.jar",
|
|
||||||
"/system/framework/voip-common.jar",
|
|
||||||
"/system/framework/ims-common.jar",
|
|
||||||
"/system/framework/mms-common.jar",
|
|
||||||
"/system/framework/android.policy.jar",
|
|
||||||
"/system/framework/apache-xml.jar");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -87,12 +87,12 @@ public class AnalysisTest {
|
|||||||
|
|
||||||
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15, false);
|
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15, false);
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
if (registerInfo) {
|
if (registerInfo) {
|
||||||
options.registerInfo = baksmaliOptions.ALL | baksmaliOptions.FULLMERGE;
|
options.registerInfo = BaksmaliOptions.ALL | BaksmaliOptions.FULLMERGE;
|
||||||
options.classPath = new ClassPath();
|
options.classPath = new ClassPath();
|
||||||
}
|
}
|
||||||
options.useImplicitReferences = false;
|
options.implicitReferences = false;
|
||||||
|
|
||||||
for (ClassDef classDef: dexFile.getClasses()) {
|
for (ClassDef classDef: dexFile.getClasses()) {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
@ -48,10 +48,10 @@ import java.io.StringWriter;
|
|||||||
|
|
||||||
public class BaksmaliTestUtils {
|
public class BaksmaliTestUtils {
|
||||||
public static void assertSmaliCompiledEquals(String source, String expected,
|
public static void assertSmaliCompiledEquals(String source, String expected,
|
||||||
baksmaliOptions options, boolean stripComments) throws IOException,
|
BaksmaliOptions options, boolean stripComments) throws IOException,
|
||||||
RecognitionException {
|
RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali(source, options.apiLevel,
|
ClassDef classDef = SmaliTestUtils.compileSmali(source, options.apiLevel,
|
||||||
options.experimental);
|
options.experimentalOpcodes);
|
||||||
|
|
||||||
// Remove unnecessary whitespace and optionally strip all comments from smali file
|
// Remove unnecessary whitespace and optionally strip all comments from smali file
|
||||||
String normalizedActual = getNormalizedSmali(classDef, options, stripComments);
|
String normalizedActual = getNormalizedSmali(classDef, options, stripComments);
|
||||||
@ -62,13 +62,13 @@ public class BaksmaliTestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void assertSmaliCompiledEquals(String source, String expected,
|
public static void assertSmaliCompiledEquals(String source, String expected,
|
||||||
baksmaliOptions options) throws IOException, RecognitionException {
|
BaksmaliOptions options) throws IOException, RecognitionException {
|
||||||
assertSmaliCompiledEquals(source, expected, options, false);
|
assertSmaliCompiledEquals(source, expected, options, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void assertSmaliCompiledEquals(String source, String expected)
|
public static void assertSmaliCompiledEquals(String source, String expected)
|
||||||
throws IOException, RecognitionException {
|
throws IOException, RecognitionException {
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
assertSmaliCompiledEquals(source, expected, options);
|
assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ public class BaksmaliTestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static String getNormalizedSmali(@Nonnull ClassDef classDef, @Nonnull baksmaliOptions options,
|
public static String getNormalizedSmali(@Nonnull ClassDef classDef, @Nonnull BaksmaliOptions options,
|
||||||
boolean stripComments)
|
boolean stripComments)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
@ -65,7 +65,7 @@ public abstract class DexTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected DexBackedDexFile getInputDexFile(@Nonnull String testName, @Nonnull baksmaliOptions options) {
|
protected DexBackedDexFile getInputDexFile(@Nonnull String testName, @Nonnull BaksmaliOptions options) {
|
||||||
try {
|
try {
|
||||||
// Load file from resources as a stream
|
// Load file from resources as a stream
|
||||||
byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
|
byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
|
||||||
|
@ -57,10 +57,10 @@ public class DisassemblyTest extends DexTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void runTest(@Nonnull String testName) {
|
protected void runTest(@Nonnull String testName) {
|
||||||
runTest(testName, new baksmaliOptions());
|
runTest(testName, new BaksmaliOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) {
|
protected void runTest(@Nonnull String testName, @Nonnull BaksmaliOptions options) {
|
||||||
try {
|
try {
|
||||||
DexBackedDexFile inputDex = getInputDexFile(testName, options);
|
DexBackedDexFile inputDex = getInputDexFile(testName, options);
|
||||||
Assert.assertEquals(1, inputDex.getClassCount());
|
Assert.assertEquals(1, inputDex.getClassCount());
|
||||||
|
@ -42,7 +42,7 @@ import org.junit.Test;
|
|||||||
public class FieldGapOrderTest extends DexTest {
|
public class FieldGapOrderTest extends DexTest {
|
||||||
@Test
|
@Test
|
||||||
public void testOldOrder() {
|
public void testOldOrder() {
|
||||||
DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions());
|
DexFile dexFile = getInputDexFile("FieldGapOrder", new BaksmaliOptions());
|
||||||
Assert.assertEquals(3, dexFile.getClasses().size());
|
Assert.assertEquals(3, dexFile.getClasses().size());
|
||||||
|
|
||||||
ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), false, 66);
|
ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), false, 66);
|
||||||
@ -56,7 +56,7 @@ public class FieldGapOrderTest extends DexTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNewOrder() {
|
public void testNewOrder() {
|
||||||
DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions());
|
DexFile dexFile = getInputDexFile("FieldGapOrder", new BaksmaliOptions());
|
||||||
Assert.assertEquals(3, dexFile.getClasses().size());
|
Assert.assertEquals(3, dexFile.getClasses().size());
|
||||||
|
|
||||||
ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), false, 67);
|
ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), false, 67);
|
||||||
|
@ -62,8 +62,8 @@ public class ImplicitReferenceTest {
|
|||||||
"return-void\n" +
|
"return-void\n" +
|
||||||
".end method\n";
|
".end method\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.implicitReferences = true;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -93,8 +93,8 @@ public class ImplicitReferenceTest {
|
|||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method\n";
|
".end method\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.implicitReferences = false;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -118,8 +118,8 @@ public class ImplicitReferenceTest {
|
|||||||
".field public static field3:Ljava/lang/reflect/Method; = I()V\n" +
|
".field public static field3:Ljava/lang/reflect/Method; = I()V\n" +
|
||||||
".field public static field4:Ljava/lang/Class; = I\n";
|
".field public static field4:Ljava/lang/Class; = I\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.implicitReferences = true;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -143,8 +143,8 @@ public class ImplicitReferenceTest {
|
|||||||
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
|
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
|
||||||
".field public static field4:Ljava/lang/Class; = I\n";
|
".field public static field4:Ljava/lang/Class; = I\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.implicitReferences = false;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -174,8 +174,8 @@ public class ImplicitReferenceTest {
|
|||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method\n";
|
".end method\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.implicitReferences = true;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -205,8 +205,8 @@ public class ImplicitReferenceTest {
|
|||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method\n";
|
".end method\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.implicitReferences = false;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -228,8 +228,8 @@ public class ImplicitReferenceTest {
|
|||||||
".field public static field2:Ljava/lang/reflect/Field; = V:I\n" +
|
".field public static field2:Ljava/lang/reflect/Field; = V:I\n" +
|
||||||
".field public static field3:Ljava/lang/reflect/Field; = I:I\n";
|
".field public static field3:Ljava/lang/reflect/Field; = I:I\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.implicitReferences = true;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
@ -251,8 +251,8 @@ public class ImplicitReferenceTest {
|
|||||||
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
|
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
|
||||||
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I\n";
|
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I\n";
|
||||||
|
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.implicitReferences = false;
|
||||||
|
|
||||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,6 @@ import org.junit.Test;
|
|||||||
public class InterfaceOrderTest extends IdenticalRoundtripTest {
|
public class InterfaceOrderTest extends IdenticalRoundtripTest {
|
||||||
@Test
|
@Test
|
||||||
public void testInterfaceOrder() {
|
public void testInterfaceOrder() {
|
||||||
runTest("InterfaceOrder", new baksmaliOptions());
|
runTest("InterfaceOrder", new BaksmaliOptions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,10 @@ import org.junit.Test;
|
|||||||
|
|
||||||
public class LambdaTest extends IdenticalRoundtripTest {
|
public class LambdaTest extends IdenticalRoundtripTest {
|
||||||
|
|
||||||
private baksmaliOptions createOptions() {
|
private BaksmaliOptions createOptions() {
|
||||||
baksmaliOptions options = new baksmaliOptions();
|
BaksmaliOptions options = new BaksmaliOptions();
|
||||||
options.apiLevel = 23; // since we need at least level 23 for lambda opcodes
|
options.apiLevel = 23; // since we need at least level 23 for lambda opcodes
|
||||||
options.experimental = true; // since these opcodes aren't implemented in runtime yet);
|
options.experimentalOpcodes = true; // since these opcodes aren't implemented in runtime yet);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,10 +69,10 @@ public abstract class RoundtripTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void runTest(@Nonnull String testName) {
|
protected void runTest(@Nonnull String testName) {
|
||||||
runTest(testName, new baksmaliOptions());
|
runTest(testName, new BaksmaliOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) {
|
protected void runTest(@Nonnull String testName, @Nonnull BaksmaliOptions options) {
|
||||||
try {
|
try {
|
||||||
// Load file from resources as a stream
|
// Load file from resources as a stream
|
||||||
String inputFilename = getInputFilename(testName);
|
String inputFilename = getInputFilename(testName);
|
||||||
|
@ -109,7 +109,8 @@ subprojects {
|
|||||||
jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
|
jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
|
||||||
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
|
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
|
||||||
dx: 'com.google.android.tools:dx:1.7',
|
dx: 'com.google.android.tools:dx:1.7',
|
||||||
gson: 'com.google.code.gson:gson:2.3.1'
|
gson: 'com.google.code.gson:gson:2.3.1',
|
||||||
|
jcommander: 'com.beust:jcommander:1.48'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ dependencies {
|
|||||||
compile depends.commons_cli
|
compile depends.commons_cli
|
||||||
compile depends.findbugs
|
compile depends.findbugs
|
||||||
compile depends.guava
|
compile depends.guava
|
||||||
|
compile depends.jcommander
|
||||||
testCompile depends.junit
|
testCompile depends.junit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +33,89 @@ package org.jf.util;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.text.BreakIterator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class StringWrapper {
|
public class StringWrapper {
|
||||||
|
/**
|
||||||
|
* Splits the given string into lines of maximum width maxWidth. The splitting is done using the current locale's
|
||||||
|
* rules for splitting lines.
|
||||||
|
*
|
||||||
|
* @param string The string to split
|
||||||
|
* @param maxWidth The maximum length of any line
|
||||||
|
* @return An iterable of Strings containing the wrapped lines
|
||||||
|
*/
|
||||||
|
public static Iterable<String> wrapStringOnBreaks(@Nonnull final String string, final int maxWidth) {
|
||||||
|
final BreakIterator breakIterator = BreakIterator.getLineInstance();
|
||||||
|
breakIterator.setText(string);
|
||||||
|
|
||||||
|
return new Iterable<String>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<String> iterator() {
|
||||||
|
return new Iterator<String>() {
|
||||||
|
private int currentLineStart = 0;
|
||||||
|
private boolean nextLineSet = false;
|
||||||
|
private String nextLine;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (!nextLineSet) {
|
||||||
|
calculateNext();
|
||||||
|
}
|
||||||
|
return nextLine != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateNext() {
|
||||||
|
int lineEnd = currentLineStart;
|
||||||
|
while (true) {
|
||||||
|
lineEnd = breakIterator.following(lineEnd);
|
||||||
|
if (lineEnd == BreakIterator.DONE) {
|
||||||
|
lineEnd = breakIterator.last();
|
||||||
|
if (lineEnd <= currentLineStart) {
|
||||||
|
nextLine = null;
|
||||||
|
nextLineSet = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineEnd - currentLineStart > maxWidth) {
|
||||||
|
lineEnd = breakIterator.preceding(lineEnd);
|
||||||
|
if (lineEnd <= currentLineStart) {
|
||||||
|
lineEnd = currentLineStart + maxWidth;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.charAt(lineEnd-1) == '\n') {
|
||||||
|
nextLine = string.substring(currentLineStart, lineEnd-1);
|
||||||
|
nextLineSet = true;
|
||||||
|
currentLineStart = lineEnd;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextLine = string.substring(currentLineStart, lineEnd);
|
||||||
|
nextLineSet = true;
|
||||||
|
currentLineStart = lineEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String next() {
|
||||||
|
String ret = nextLine;
|
||||||
|
nextLine = null;
|
||||||
|
nextLineSet = false;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits the given string into lines using on any embedded newlines, and wrapping the text as needed to conform to
|
* Splits the given string into lines using on any embedded newlines, and wrapping the text as needed to conform to
|
||||||
* the given maximum line width.
|
* the given maximum line width.
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.util.jcommander;
|
||||||
|
|
||||||
|
import com.beust.jcommander.converters.IParameterSplitter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JCommander parameter splitter that splits a parameter value by either commas or colons
|
||||||
|
*/
|
||||||
|
public class CommaColonParameterSplitter implements IParameterSplitter {
|
||||||
|
@Override
|
||||||
|
public List<String> split(String value) {
|
||||||
|
return Arrays.asList(value.split(":|,"));
|
||||||
|
}
|
||||||
|
}
|
@ -31,10 +31,34 @@
|
|||||||
|
|
||||||
package org.jf.util;
|
package org.jf.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class StringWrapperTest {
|
public class StringWrapperTest {
|
||||||
|
@Test
|
||||||
|
public void testWrapStringByWords() {
|
||||||
|
validateResult2(new String[]{"abc", "abcdef", "abcdef"},
|
||||||
|
"abc\nabcdefabcdef", 6);
|
||||||
|
|
||||||
|
validateResult2(new String[]{"abc", "abcdef", " ", "abcdef"},
|
||||||
|
"abc\nabcdef abcdef", 6);
|
||||||
|
|
||||||
|
validateResult2(new String[]{"abc", "abcde ", "fabcde", "f"},
|
||||||
|
"abc\nabcde fabcdef", 6);
|
||||||
|
|
||||||
|
validateResult2(new String[]{"abc def ghi ", "kjl mon pqr ", "stu vwx yz"},
|
||||||
|
"abc def ghi kjl mon pqr stu vwx yz", 14);
|
||||||
|
|
||||||
|
validateResult2(new String[]{"abcdefg", "hikjlmo", "npqrstu", "vwxyz"},
|
||||||
|
"abcdefghikjlmonpqrstuvwxyz", 7);
|
||||||
|
|
||||||
|
validateResult2(new String[]{"abc", "defhig"},
|
||||||
|
"abc\ndefhig", 20);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWrapString() {
|
public void testWrapString() {
|
||||||
validateResult(
|
validateResult(
|
||||||
@ -115,4 +139,15 @@ public class StringWrapperTest {
|
|||||||
Assert.assertEquals(expected[i], actual[i]);
|
Assert.assertEquals(expected[i], actual[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void validateResult2(String[] expected, String textToWrap, int maxWidth) {
|
||||||
|
List<String> result = Lists.newArrayList(StringWrapper.wrapStringOnBreaks(textToWrap, maxWidth));
|
||||||
|
|
||||||
|
Assert.assertEquals(expected.length, result.size());
|
||||||
|
int i;
|
||||||
|
for (i=0; i<result.size(); i++) {
|
||||||
|
Assert.assertTrue(i < expected.length);
|
||||||
|
Assert.assertEquals(expected[i], result.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user