update to smali 2.0.3

This commit is contained in:
Connor Tumbleson 2014-01-19 10:37:31 -06:00
parent 2af523d5d4
commit a91e87bb04
47 changed files with 718 additions and 254 deletions

View File

@ -1,5 +1,5 @@
v2.0.0 (TBA)
-Updated to smali/baksmali to v2.0.0
-Updated to smali/baksmali to v2.0.3
-Updated to Gradle 1.8
-Fixed (issue #8) - Correctly uses -c to retain original manifest and META-INF. (Thanks M1cha)
-Fixed (issue #63) - Correctly handles apk's that have unknown files outside of the standard aapt allowed resources.

View File

@ -32,6 +32,7 @@ import com.google.common.collect.Lists;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.instruction.formats.Instruction21c;
@ -79,8 +80,15 @@ public class ClassDefinition {
case SPUT_SHORT:
case SPUT_WIDE: {
Instruction21c ins = (Instruction21c)instruction;
FieldReference fieldRef = (FieldReference)ins.getReference();
if (fieldRef.getDefiningClass().equals((classDef.getType()))) {
FieldReference fieldRef = null;
try {
fieldRef = (FieldReference)ins.getReference();
} catch (InvalidItemIndex ex) {
// just ignore it for now. We'll deal with it later, when processing the instructions
// themselves
}
if (fieldRef != null &&
fieldRef.getDefiningClass().equals((classDef.getType()))) {
fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef));
}
break;

View File

@ -65,6 +65,8 @@ public class ArrayDataMethodItem extends InstructionMethodItem<ArrayPayload> {
for (Number number: elements) {
LongRenderer.writeSignedIntOrLongTo(writer, number.longValue());
writer.write(suffix);
if (elementWidth == 4)
writeResourceId(writer, number.intValue());
writer.write("\n");
}
writer.deindent(4);

View File

@ -29,20 +29,26 @@
package org.jf.baksmali.Adaptors.Format;
import org.jf.baksmali.Adaptors.MethodDefinition;
import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload;
import org.jf.baksmali.Adaptors.MethodItem;
import org.jf.baksmali.Adaptors.ReferenceFormatter;
import org.jf.baksmali.Renderers.LongRenderer;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.VerificationError;
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
import org.jf.util.IndentingWriter;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Map;
public class InstructionMethodItem<T extends Instruction> extends MethodItem {
@Nonnull protected final MethodDefinition methodDef;
@ -59,29 +65,105 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
return 100;
}
private boolean isAllowedOdex(@Nonnull Opcode opcode) {
baksmaliOptions options = methodDef.classDef.options;
if (options.allowOdex) {
return true;
}
if (methodDef.classDef.options.apiLevel >= 14) {
return false;
}
return opcode.isOdexedInstanceVolatile() || opcode.isOdexedStaticVolatile() ||
opcode == Opcode.THROW_VERIFICATION_ERROR;
}
@Override
public boolean writeTo(IndentingWriter writer) throws IOException {
boolean invalidReference = false;
if (instruction instanceof ReferenceInstruction) {
try {
Reference reference = ((ReferenceInstruction)instruction).getReference();
} catch (InvalidItemIndex ex) {
invalidReference = true;
Opcode opcode = instruction.getOpcode();
String verificationErrorName = null;
String referenceString = null;
writer.write("#invalid ");
writer.write(ReferenceType.toString(instruction.getOpcode().referenceType));
writer.write(" index: ");
writer.printSignedIntAsDec(ex.getInvalidIndex());
writer.write("\n#");
boolean commentOutInstruction = false;
if (instruction instanceof Instruction20bc) {
int verificationError = ((Instruction20bc)instruction).getVerificationError();
verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
if (verificationErrorName == null) {
writer.write("#was invalid verification error type: ");
writer.printSignedIntAsDec(verificationError);
writer.write("\n");
verificationErrorName = "generic-error";
}
}
if (instruction instanceof ReferenceInstruction) {
ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
try {
Reference reference = referenceInstruction.getReference();
referenceString = ReferenceUtil.getReferenceString(reference);
assert referenceString != null;
} catch (InvalidItemIndex ex) {
writer.write("#");
writer.write(ex.getMessage());
writer.write("\n");
commentOutInstruction = true;
referenceString = String.format("%s@%d",
ReferenceType.toString(referenceInstruction.getReferenceType()),
ex.getInvalidIndex());
} catch (ReferenceType.InvalidReferenceTypeException ex) {
writer.write("#invalid reference type: ");
writer.printSignedIntAsDec(ex.getReferenceType());
commentOutInstruction = true;
referenceString = "invalid_reference";
}
}
if (instruction instanceof Instruction31t) {
Opcode payloadOpcode;
switch (instruction.getOpcode()) {
case PACKED_SWITCH:
payloadOpcode = Opcode.PACKED_SWITCH_PAYLOAD;
break;
case SPARSE_SWITCH:
payloadOpcode = Opcode.SPARSE_SWITCH_PAYLOAD;
break;
case FILL_ARRAY_DATA:
payloadOpcode = Opcode.ARRAY_PAYLOAD;
break;
default:
throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
}
try {
methodDef.findSwitchPayload(this.codeAddress + ((Instruction31t)instruction).getCodeOffset(),
payloadOpcode);
} catch (InvalidSwitchPayload ex) {
writer.write("#invalid payload reference");
commentOutInstruction = true;
}
}
if (opcode.odexOnly()) {
if (!isAllowedOdex(opcode)) {
writer.write("#disallowed odex opcode\n");
commentOutInstruction = true;
}
}
if (commentOutInstruction) {
writer.write("#");
}
switch (instruction.getOpcode().format) {
case Format10t:
writeOpcode(writer);
writer.write(' ');
writeTargetLabel(writer);
return true;
break;
case Format10x:
if (instruction instanceof UnknownInstruction) {
writer.write("#unknown opcode: 0x");
@ -89,47 +171,47 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write('\n');
}
writeOpcode(writer);
return true;
break;
case Format11n:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeLiteral(writer);
return true;
break;
case Format11x:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
return true;
break;
case Format12x:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeSecondRegister(writer);
return true;
break;
case Format20bc:
writeOpcode(writer);
writer.write(' ');
writeVerificationErrorType(writer);
writer.write(verificationErrorName);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format20t:
case Format30t:
writeOpcode(writer);
writer.write(' ');
writeTargetLabel(writer);
return true;
break;
case Format21c:
case Format31c:
writeOpcode(writer);
writer.write(' ');
writeFirstRegister(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format21ih:
case Format21lh:
case Format21s:
@ -140,7 +222,9 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeFirstRegister(writer);
writer.write(", ");
writeLiteral(writer);
return true;
if (instruction.getOpcode().setsWideRegister() == false)
writeResourceId(writer);
break;
case Format21t:
case Format31t:
writeOpcode(writer);
@ -148,7 +232,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeFirstRegister(writer);
writer.write(", ");
writeTargetLabel(writer);
return true;
break;
case Format22b:
case Format22s:
writeOpcode(writer);
@ -158,7 +242,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeLiteral(writer);
return true;
break;
case Format22c:
writeOpcode(writer);
writer.write(' ');
@ -166,8 +250,8 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write(", ");
writeSecondRegister(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format22cs:
writeOpcode(writer);
writer.write(' ');
@ -176,7 +260,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeFieldOffset(writer);
return true;
break;
case Format22t:
writeOpcode(writer);
writer.write(' ');
@ -185,7 +269,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeTargetLabel(writer);
return true;
break;
case Format22x:
case Format32x:
writeOpcode(writer);
@ -193,7 +277,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeFirstRegister(writer);
writer.write(", ");
writeSecondRegister(writer);
return true;
break;
case Format23x:
writeOpcode(writer);
writer.write(' ');
@ -202,52 +286,59 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writeSecondRegister(writer);
writer.write(", ");
writeThirdRegister(writer);
return true;
break;
case Format35c:
writeOpcode(writer);
writer.write(' ');
writeInvokeRegisters(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format35mi:
writeOpcode(writer);
writer.write(' ');
writeInvokeRegisters(writer);
writer.write(", ");
writeInlineIndex(writer);
return true;
break;
case Format35ms:
writeOpcode(writer);
writer.write(' ');
writeInvokeRegisters(writer);
writer.write(", ");
writeVtableIndex(writer);
return true;
break;
case Format3rc:
writeOpcode(writer);
writer.write(' ');
writeInvokeRangeRegisters(writer);
writer.write(", ");
writeReference(writer);
return true;
writer.write(referenceString);
break;
case Format3rmi:
writeOpcode(writer);
writer.write(' ');
writeInvokeRangeRegisters(writer);
writer.write(", ");
writeInlineIndex(writer);
return true;
break;
case Format3rms:
writeOpcode(writer);
writer.write(' ');
writeInvokeRangeRegisters(writer);
writer.write(", ");
writeVtableIndex(writer);
return true;
break;
default:
assert false;
return false;
}
assert false;
return false;
if (commentOutInstruction) {
writer.write("\nnop");
}
return true;
}
protected void writeOpcode(IndentingWriter writer) throws IOException {
@ -337,6 +428,18 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
}
protected void writeResourceId(IndentingWriter writer) throws IOException {
writeResourceId(writer, ((NarrowLiteralInstruction)instruction).getNarrowLiteral());
}
protected void writeResourceId(IndentingWriter writer, int val) throws IOException {
Map<Integer,String> resourceIds = methodDef.classDef.options.resourceIds;
String resource = resourceIds.get(Integer.valueOf(val));
if (resource != null) {
writer.write(" # ");
writer.write(resource);
}
}
protected void writeFieldOffset(IndentingWriter writer) throws IOException {
writer.write("field@0x");
@ -352,20 +455,4 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write("vtable@");
writer.printSignedIntAsDec(((VtableIndexInstruction)instruction).getVtableIndex());
}
protected void writeReference(IndentingWriter writer) throws IOException {
try {
ReferenceFormatter.writeReference(writer, instruction.getOpcode().referenceType,
((ReferenceInstruction)instruction).getReference());
} catch (InvalidItemIndex ex) {
writer.write(ReferenceType.toString(instruction.getOpcode().referenceType));
writer.write("@");
writer.printSignedIntAsDec(ex.getInvalidIndex());
}
}
protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {
int verificationError = ((Instruction20bc)instruction).getVerificationError();
writer.write(VerificationError.getVerificationErrorName(verificationError));
}
}

View File

@ -82,9 +82,12 @@ public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchPa
IntegerRenderer.writeTo(writer, firstKey);
writer.indent(4);
writer.write('\n');
int key = firstKey;
for (PackedSwitchTarget target: targets) {
target.writeTargetTo(writer);
writeResourceId(writer, key);
writer.write('\n');
key++;
}
writer.deindent(4);
writer.write(".end packed-switch");

View File

@ -71,6 +71,7 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchPa
IntegerRenderer.writeTo(writer, target.getKey());
writer.write(" -> ");
target.writeTargetTo(writer);
writeResourceId(writer, target.getKey());
writer.write('\n');
}
writer.deindent(4);

View File

@ -39,6 +39,7 @@ import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.analysis.AnalysisException;
import org.jf.dexlib2.analysis.AnalyzedInstruction;
import org.jf.dexlib2.analysis.MethodAnalyzer;
import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.debug.DebugItem;
import org.jf.dexlib2.iface.instruction.Instruction;
@ -46,6 +47,7 @@ import org.jf.dexlib2.iface.instruction.OffsetInstruction;
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.util.InstructionOffsetMap;
import org.jf.dexlib2.util.InstructionOffsetMap.InvalidInstructionOffset;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
import org.jf.dexlib2.util.TypeUtils;
@ -92,15 +94,31 @@ public class MethodDefinition {
Opcode opcode = instruction.getOpcode();
if (opcode == Opcode.PACKED_SWITCH) {
boolean valid = true;
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
packedSwitchMap.append(targetOffset, codeOffset);
try {
targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
} catch (InvalidSwitchPayload ex) {
valid = false;
}
if (valid) {
packedSwitchMap.append(targetOffset, codeOffset);
}
} else if (opcode == Opcode.SPARSE_SWITCH) {
boolean valid = true;
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
sparseSwitchMap.append(targetOffset, codeOffset);
try {
targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
} catch (InvalidSwitchPayload ex) {
valid = false;
// The offset to the payload instruction was invalid. Nothing to do, except that we won't
// add this instruction to the map.
}
if (valid) {
sparseSwitchMap.append(targetOffset, codeOffset);
}
}
}
}catch (Exception ex) {
@ -187,8 +205,13 @@ public class MethodDefinition {
writer.write(".end method\n");
}
private int findSwitchPayload(int targetOffset, Opcode type) {
int targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
public int findSwitchPayload(int targetOffset, Opcode type) {
int targetIndex;
try {
targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
} catch (InvalidInstructionOffset ex) {
throw new InvalidSwitchPayload(targetOffset);
}
//TODO: does dalvik let you pad with multiple nops?
//TODO: does dalvik let a switch instruction point to a non-payload instruction?
@ -205,7 +228,7 @@ public class MethodDefinition {
}
}
}
throw new ExceptionWithContext("No switch payload at offset 0x%x", targetOffset);
throw new InvalidSwitchPayload(targetOffset);
} else {
return targetOffset;
}
@ -337,10 +360,16 @@ public class MethodDefinition {
Opcode opcode = instruction.getOpcode();
if (opcode.referenceType == ReferenceType.METHOD) {
MethodReference methodReference =
(MethodReference)((ReferenceInstruction)instruction).getReference();
MethodReference methodReference = null;
try {
methodReference = (MethodReference)((ReferenceInstruction)instruction).getReference();
} catch (InvalidItemIndex ex) {
// just ignore it for now. We'll deal with it later, when processing the instructions
// themselves
}
if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) {
if (methodReference != null &&
SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) {
SyntheticAccessorResolver.AccessedMember accessedMember =
classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference);
if (accessedMember != null) {
@ -510,4 +539,17 @@ public class MethodDefinition {
return labels.values();
}
}
public static class InvalidSwitchPayload extends ExceptionWithContext {
private final int payloadOffset;
public InvalidSwitchPayload(int payloadOffset) {
super("No switch payload at offset: %d", payloadOffset);
this.payloadOffset = payloadOffset;
}
public int getPayloadOffset() {
return payloadOffset;
}
}
}

View File

@ -57,6 +57,9 @@ public class ReferenceFormatter {
return;
case ReferenceType.FIELD:
ReferenceUtil.writeFieldDescriptor(writer, (FieldReference)reference);
return;
default:
throw new IllegalStateException("Unknown reference type");
}
}
}

View File

@ -39,11 +39,19 @@ import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
import org.jf.util.ClassFileNameHandler;
import org.jf.util.IndentingWriter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.*;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
public class baksmali {
public static boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
@ -60,9 +68,50 @@ public class baksmali {
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
options.apiLevel);
} catch (Exception ex) {
System.err.println("\n\nError occured while loading boot class path files. Aborting.");
System.err.println("\n\nError occurred while loading boot class path files. Aborting.");
ex.printStackTrace(System.err);
System.exit(1);
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;
}
}
}
@ -70,7 +119,7 @@ public class baksmali {
if (!outputDirectoryFile.exists()) {
if (!outputDirectoryFile.mkdirs()) {
System.err.println("Can't create the output directory " + options.outputDirectory);
System.exit(1);
return false;
}
}
@ -98,22 +147,24 @@ public class baksmali {
}
boolean errorOccurred = false;
for (Future<Boolean> task: tasks) {
while(true) {
try {
if (!task.get()) {
errorOccurred = true;
try {
for (Future<Boolean> task: tasks) {
while(true) {
try {
if (!task.get()) {
errorOccurred = true;
}
} catch (InterruptedException ex) {
continue;
} catch (ExecutionException ex) {
throw new RuntimeException(ex);
}
} catch (InterruptedException ex) {
continue;
} catch (ExecutionException ex) {
throw new RuntimeException(ex);
break;
}
break;
}
} finally {
executor.shutdown();
}
executor.shutdown();
return !errorOccurred;
}
@ -168,7 +219,7 @@ public class baksmali {
writer = new IndentingWriter(bufWriter);
classDefinition.writeTo((IndentingWriter)writer);
} catch (Exception ex) {
System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
System.err.println("\n\nError occurred while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
ex.printStackTrace();
// noinspection ResultOfMethodCallIgnored
smaliFile.delete();
@ -180,7 +231,7 @@ public class baksmali {
try {
writer.close();
} catch (Throwable ex) {
System.err.println("\n\nError occured while closing file " + smaliFile.toString());
System.err.println("\n\nError occurred while closing file " + smaliFile.toString());
ex.printStackTrace();
}
}

View File

@ -37,7 +37,9 @@ import org.jf.dexlib2.analysis.InlineMethodResolver;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class baksmaliOptions {
// register info values
@ -58,12 +60,16 @@ public class baksmaliOptions {
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 ignoreErrors = false;
public boolean checkPackagePrivateAccess = false;
@ -84,4 +90,11 @@ public class baksmaliOptions {
}
extraClassPathEntries.addAll(Arrays.asList(extraClassPath.split(":")));
}
public void setResourceIdFiles(String resourceIdFiles) {
for (String resourceIdFile: resourceIdFiles.split(":")) {
String[] entry = resourceIdFile.split("=");
resourceIdFileEntries.put(entry[1], entry[0]);
}
}
}

View File

@ -202,6 +202,10 @@ public class main {
case 'j':
options.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
break;
case 'i':
String rif = commandLine.getOptionValue("i");
options.setResourceIdFiles(rif);
break;
case 'N':
disassemble = false;
break;
@ -215,9 +219,6 @@ public class main {
case 'T':
options.inlineResolver = new CustomInlineMethodResolver(options.classPath, new File(commandLine.getOptionValue("T")));
break;
case 'K':
options.checkPackagePrivateAccess = true;
break;
default:
assert false;
}
@ -235,6 +236,10 @@ public class main {
}
}
if (options.apiLevel >= 17) {
options.checkPackagePrivateAccess = true;
}
String inputDexFileName = remainingArgs[0];
File dexFileFile = new File(inputDexFileName);
@ -251,6 +256,7 @@ public class main {
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;
@ -407,6 +413,14 @@ public class main {
.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 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")
@ -430,12 +444,6 @@ public class main {
.withArgName("FILE")
.create("T");
Option checkPackagePrivateAccess = OptionBuilder.withLongOpt("check-package-private-access")
.withDescription("When deodexing, use the new virtual table generation logic that " +
"prevents overriding an inaccessible package private method. This is a temporary option " +
"that will be removed once this new functionality can be tied to a specific api level.")
.create("K");
basicOptions.addOption(versionOption);
basicOptions.addOption(helpOption);
basicOptions.addOption(outputDirOption);
@ -451,12 +459,12 @@ public class main {
basicOptions.addOption(noAccessorCommentsOption);
basicOptions.addOption(apiLevelOption);
basicOptions.addOption(jobsOption);
basicOptions.addOption(resourceIdFilesOption);
debugOptions.addOption(dumpOption);
debugOptions.addOption(ignoreErrorsOption);
debugOptions.addOption(noDisassemblyOption);
debugOptions.addOption(inlineTableOption);
debugOptions.addOption(checkPackagePrivateAccess);
for (Object option: basicOptions.getOptions()) {
options.addOption((Option)option);

View File

@ -104,8 +104,9 @@ public class AnalysisTest {
className.substring(1, className.length() - 1));
String smaliContents = readResource(smaliPath);
Assert.assertEquals(smaliContents.replace("\r", "").replace("\n", System.lineSeparator()),
stringWriter.toString().replace("\r", "").replace("\n", System.lineSeparator()));
String newline = System.getProperty("line.separator");
Assert.assertEquals(smaliContents.replace("\r", "").replace("\n", newline),
stringWriter.toString().replace("\r", "").replace("\n", newline));
}
}

View File

@ -69,7 +69,10 @@ public class Opcodes {
case 0x300:
return Opcode.ARRAY_PAYLOAD;
default:
return opcodesByValue[opcodeValue];
if (opcodeValue >= 0 && opcodeValue < opcodesByValue.length) {
return opcodesByValue[opcodeValue];
}
return null;
}
}
}

View File

@ -31,6 +31,9 @@
package org.jf.dexlib2;
import org.jf.dexlib2.iface.reference.*;
import org.jf.util.ExceptionWithContext;
public final class ReferenceType {
public static final int STRING = 0;
public static final int TYPE = 1;
@ -49,7 +52,50 @@ public final class ReferenceType {
case METHOD:
return "method";
default:
throw new IllegalArgumentException("Invalid reference type: " + referenceType);
throw new InvalidReferenceTypeException(referenceType);
}
}
public static int getReferenceType(Reference reference) {
if (reference instanceof StringReference) {
return STRING;
} else if (reference instanceof TypeReference) {
return TYPE;
} else if (reference instanceof FieldReference) {
return FIELD;
} else if (reference instanceof MethodReference) {
return METHOD;
} else {
throw new IllegalStateException("Invalid reference");
}
}
/**
* Validate a specific reference type. Note that the NONE placeholder is specifically not considered valid here.
*
* @throws InvalidReferenceTypeException
*/
public static void validateReferenceType(int referenceType) {
if (referenceType < 0 || referenceType > 3) {
throw new InvalidReferenceTypeException(referenceType);
}
}
public static class InvalidReferenceTypeException extends ExceptionWithContext {
private final int referenceType;
public InvalidReferenceTypeException(int referenceType) {
super("Invalid reference type: %d", referenceType);
this.referenceType = referenceType;
}
public InvalidReferenceTypeException(int referenceType, String message, Object... formatArgs) {
super(message, formatArgs);
this.referenceType = referenceType;
}
public int getReferenceType() {
return referenceType;
}
}

View File

@ -34,6 +34,7 @@ package org.jf.dexlib2;
import com.google.common.collect.Maps;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nullable;
import java.util.HashMap;
public class VerificationError {
@ -61,6 +62,7 @@ public class VerificationError {
verificationErrorNames.put("instantiation-error", INSTANTIATION_ERROR);
}
@Nullable
public static String getVerificationErrorName(int verificationError) {
switch (verificationError) {
case GENERIC:

View File

@ -31,7 +31,7 @@
package org.jf.dexlib2.builder;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
import org.jf.dexlib2.builder.debug.*;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.reference.StringReference;
@ -39,18 +39,21 @@ import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.*;
public class MethodLocation {
@Nullable BuilderInstruction instruction;
int codeAddress;
int index;
private List<Label> labels = Lists.newArrayList();
List<BuilderDebugItem> debugItems = Lists.newArrayList();
// We end up creating and keeping around a *lot* of MethodLocation objects
// when building a new dex file, so it's worth the trouble of lazily creating
// the labels and debugItems lists only when they are needed
@Nullable
private List<Label> labels = null;
@Nullable
private List<BuilderDebugItem> debugItems = null;
MethodLocation(@Nullable BuilderInstruction instruction, int codeAddress, int index) {
this.instruction = instruction;
@ -71,19 +74,51 @@ public class MethodLocation {
return index;
}
@Nonnull
private List<Label> getLabels(boolean mutable) {
if (labels == null) {
if (mutable) {
labels = new ArrayList<Label>(1);
return labels;
}
return ImmutableList.of();
}
return labels;
}
@Nonnull
private List<BuilderDebugItem> getDebugItems(boolean mutable) {
if (debugItems == null) {
if (mutable) {
debugItems = new ArrayList<BuilderDebugItem>(1);
return debugItems;
}
return ImmutableList.of();
}
return debugItems;
}
void mergeInto(@Nonnull MethodLocation other) {
for (Label label: labels) {
label.location = other;
other.labels.add(label);
if (this.labels != null || other.labels != null) {
List<Label> otherLabels = other.getLabels(true);
for (Label label: this.getLabels(false)) {
label.location = other;
otherLabels.add(label);
}
this.labels = null;
}
// We need to keep the debug items in the same order. We add the other debug items to this list, then reassign
// the list.
for (BuilderDebugItem debugItem: debugItems) {
debugItem.location = other;
if (this.debugItems != null || other.labels != null) {
// We need to keep the debug items in the same order. We add the other debug items to this list, then reassign
// the list.
List<BuilderDebugItem> debugItems = getDebugItems(true);
for (BuilderDebugItem debugItem: debugItems) {
debugItem.location = other;
}
debugItems.addAll(other.getDebugItems(false));
other.debugItems = debugItems;
this.debugItems = null;
}
debugItems.addAll(other.debugItems);
other.debugItems = debugItems;
}
@Nonnull
@ -91,7 +126,7 @@ public class MethodLocation {
return new AbstractSet<Label>() {
@Nonnull
@Override public Iterator<Label> iterator() {
final Iterator<Label> it = labels.iterator();
final Iterator<Label> it = getLabels(false).iterator();
return new Iterator<Label>() {
private @Nullable Label currentLabel = null;
@ -115,7 +150,7 @@ public class MethodLocation {
}
@Override public int size() {
return labels.size();
return getLabels(false).size();
}
@Override public boolean add(@Nonnull Label label) {
@ -124,7 +159,7 @@ public class MethodLocation {
"it from its current location first.");
}
label.location = MethodLocation.this;
labels.add(label);
getLabels(true).add(label);
return true;
}
};
@ -133,7 +168,7 @@ public class MethodLocation {
@Nonnull
public Label addNewLabel() {
Label label = new Label(this);
labels.add(label);
getLabels(true).add(label);
return label;
}
@ -142,7 +177,7 @@ public class MethodLocation {
return new AbstractSet<BuilderDebugItem>() {
@Nonnull
@Override public Iterator<BuilderDebugItem> iterator() {
final Iterator<BuilderDebugItem> it = debugItems.iterator();
final Iterator<BuilderDebugItem> it = getDebugItems(false).iterator();
return new Iterator<BuilderDebugItem>() {
private @Nullable BuilderDebugItem currentDebugItem = null;
@ -166,7 +201,7 @@ public class MethodLocation {
}
@Override public int size() {
return labels.size();
return getDebugItems(false).size();
}
@Override public boolean add(@Nonnull BuilderDebugItem debugItem) {
@ -175,7 +210,7 @@ public class MethodLocation {
"method. You must remove it from its current location first.");
}
debugItem.location = MethodLocation.this;
debugItems.add(debugItem);
getDebugItems(true).add(debugItem);
return true;
}
};

View File

@ -435,6 +435,14 @@ public class MutableMethodImplementation implements MethodImplementation {
}
case SPARSE_SWITCH_PAYLOAD:
case PACKED_SWITCH_PAYLOAD:
if (((BuilderSwitchPayload)instruction).referrer == null) {
// if the switch payload isn't referenced, just remove it
removeInstruction(index);
index--;
madeChanges = true;
break;
}
// intentional fall-through
case ARRAY_PAYLOAD: {
if ((location.codeAddress & 0x01) != 0) {
int previousIndex = location.index - 1;

View File

@ -33,6 +33,7 @@ package org.jf.dexlib2.builder.instruction;
import org.jf.dexlib2.Format;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.builder.BuilderInstruction;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.reference.Reference;
@ -56,6 +57,7 @@ public class BuilderInstruction20bc extends BuilderInstruction implements Instru
@Override public int getVerificationError() { return verificationError; }
@Nonnull @Override public Reference getReference() { return reference; }
@Override public int getReferenceType() { return ReferenceType.getReferenceType(reference); }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -56,6 +56,7 @@ public class BuilderInstruction21c extends BuilderInstruction implements Instruc
@Override public int getRegisterA() { return registerA; }
@Nonnull @Override public Reference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -60,6 +60,7 @@ public class BuilderInstruction22c extends BuilderInstruction implements Instruc
@Override public int getRegisterA() { return registerA; }
@Override public int getRegisterB() { return registerB; }
@Nonnull @Override public Reference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -56,6 +56,7 @@ public class BuilderInstruction31c extends BuilderInstruction implements Instruc
@Override public int getRegisterA() { return registerA; }
@Nonnull @Override public Reference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -76,6 +76,7 @@ public class BuilderInstruction35c extends BuilderInstruction implements Instruc
@Override public int getRegisterF() { return registerF; }
@Override public int getRegisterG() { return registerG; }
@Nonnull @Override public Reference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -61,6 +61,7 @@ public class BuilderInstruction3rc extends BuilderInstruction implements Instruc
@Override public int getStartRegister() { return startRegister; }
@Override public int getRegisterCount() { return registerCount; }
@Nonnull @Override public Reference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -184,7 +184,7 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
public int getMethodIdItemOffset(int methodIndex) {
if (methodIndex < 0 || methodIndex >= methodCount) {
throw new InvalidItemIndex(methodIndex, "Method findex out of bounds: %d", methodIndex);
throw new InvalidItemIndex(methodIndex, "Method index out of bounds: %d", methodIndex);
}
return methodStartOffset + methodIndex*MethodIdItem.ITEM_SIZE;
}

View File

@ -32,6 +32,7 @@
package org.jf.dexlib2.dexbacked.instruction;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.dexbacked.reference.DexBackedReference;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
@ -51,7 +52,13 @@ public class DexBackedInstruction20bc extends DexBackedInstruction implements In
@Nonnull
@Override
public Reference getReference() {
int refType = (dexFile.readUbyte(instructionStart + 1) >>> 6) + 1;
return DexBackedReference.makeReference(dexFile, refType, dexFile.readUshort(instructionStart + 2));
int referenceType = getReferenceType();
return DexBackedReference.makeReference(dexFile, referenceType, dexFile.readUshort(instructionStart + 2));
}
}
@Override public int getReferenceType() {
int referenceType = (dexFile.readUbyte(instructionStart + 1) >>> 6) + 1;
ReferenceType.validateReferenceType(referenceType);
return referenceType;
}
}

View File

@ -53,4 +53,9 @@ public class DexBackedInstruction21c extends DexBackedInstruction implements Ins
public Reference getReference() {
return DexBackedReference.makeReference(dexFile, opcode.referenceType, dexFile.readUshort(instructionStart + 2));
}
@Override
public int getReferenceType() {
return opcode.referenceType;
}
}

View File

@ -62,4 +62,9 @@ public class DexBackedInstruction22c extends DexBackedInstruction implements Ins
public Reference getReference() {
return DexBackedReference.makeReference(dexFile, opcode.referenceType, dexFile.readUshort(instructionStart + 2));
}
@Override
public int getReferenceType() {
return opcode.referenceType;
}
}

View File

@ -54,4 +54,9 @@ public class DexBackedInstruction31c extends DexBackedInstruction implements Ins
return DexBackedReference.makeReference(dexFile, opcode.referenceType,
dexFile.readSmallUint(instructionStart + 2));
}
@Override
public int getReferenceType() {
return opcode.referenceType;
}
}

View File

@ -82,4 +82,9 @@ public class DexBackedInstruction35c extends DexBackedInstruction implements Ins
return DexBackedReference.makeReference(dexFile, opcode.referenceType,
dexFile.readUshort(instructionStart + 2));
}
@Override
public int getReferenceType() {
return opcode.referenceType;
}
}

View File

@ -61,4 +61,10 @@ public class DexBackedInstruction3rc extends DexBackedInstruction implements Ins
return DexBackedReference.makeReference(dexFile, opcode.referenceType,
dexFile.readUshort(instructionStart + 2));
}
@Override
public int getReferenceType() {
return opcode.referenceType;
}
}

View File

@ -43,7 +43,12 @@ public class DexBackedUnknownInstruction extends DexBackedInstruction implements
super(dexFile, Opcode.NOP, instructionStart);
}
@Override public short getOriginalOpcode() {
return (short)dexFile.readUbyte(instructionStart);
@Override public int getOriginalOpcode() {
int opcode = dexFile.readUbyte(instructionStart);
if (opcode == 0) {
opcode = dexFile.readUshort(instructionStart);
}
return opcode;
}
}

View File

@ -41,6 +41,7 @@ import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.*;
import org.jf.dexlib2.util.AnnotatedBytes;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
import org.jf.util.NumberUtils;
import javax.annotation.Nonnull;
@ -84,121 +85,160 @@ public class CodeItem {
@Override
public void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) {
DexReader reader = dexFile.readerAt(out.getCursor());
try {
DexReader reader = dexFile.readerAt(out.getCursor());
int registers = reader.readUshort();
out.annotate(2, "registers_size = %d", registers);
int registers = reader.readUshort();
out.annotate(2, "registers_size = %d", registers);
int inSize = reader.readUshort();
out.annotate(2, "ins_size = %d", inSize);
int inSize = reader.readUshort();
out.annotate(2, "ins_size = %d", inSize);
int outSize = reader.readUshort();
out.annotate(2, "outs_size = %d", outSize);
int outSize = reader.readUshort();
out.annotate(2, "outs_size = %d", outSize);
int triesCount = reader.readUshort();
out.annotate(2, "tries_size = %d", triesCount);
int triesCount = reader.readUshort();
out.annotate(2, "tries_size = %d", triesCount);
int debugInfoOffset = reader.readSmallUint();
out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
int debugInfoOffset = reader.readSmallUint();
out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
if (debugInfoOffset != 0) {
addDebugInfoIdentity(debugInfoOffset, itemIdentity);
}
int instructionSize = reader.readSmallUint();
out.annotate(4, "insns_size = 0x%x", instructionSize);
out.annotate(0, "instructions:");
out.indent();
int end = reader.getOffset() + instructionSize*2;
while (reader.getOffset() < end) {
Instruction instruction = DexBackedInstruction.readFrom(reader);
switch (instruction.getOpcode().format) {
case Format10x:
annotateInstruction10x(out, instruction);
break;
case Format35c:
annotateInstruction35c(out, (Instruction35c)instruction);
break;
case Format3rc:
annotateInstruction3rc(out, (Instruction3rc)instruction);
break;
case ArrayPayload:
annotateArrayPayload(out, (ArrayPayload)instruction);
break;
case PackedSwitchPayload:
annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction);
break;
case SparseSwitchPayload:
annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction);
break;
default:
annotateDefaultInstruction(out, instruction);
break;
if (debugInfoOffset != 0) {
addDebugInfoIdentity(debugInfoOffset, itemIdentity);
}
assert reader.getOffset() == out.getCursor();
}
out.deindent();
int instructionSize = reader.readSmallUint();
out.annotate(4, "insns_size = 0x%x", instructionSize);
if (triesCount > 0) {
if ((reader.getOffset() % 4) != 0) {
reader.readUshort();
out.annotate(2, "padding");
}
out.annotate(0, "try_items:");
out.annotate(0, "instructions:");
out.indent();
for (int i=0; i<triesCount; i++) {
out.annotate(0, "try_item[%d]:", i);
out.indent();
int startAddr = reader.readSmallUint();
out.annotate(4, "start_addr = 0x%x", startAddr);
int instructionCount = reader.readUshort();
out.annotate(2, "insn_count = 0x%x", instructionCount);
out.setLimit(out.getCursor(), out.getCursor() + instructionSize * 2);
int handlerOffset = reader.readUshort();
out.annotate(2, "handler_off = 0x%x", handlerOffset);
int end = reader.getOffset() + instructionSize*2;
try {
while (reader.getOffset() < end) {
Instruction instruction = DexBackedInstruction.readFrom(reader);
// if we read past the end of the instruction list
if (reader.getOffset() > end) {
out.annotateTo(end, "truncated instruction");
reader.setOffset(end);
} else {
switch (instruction.getOpcode().format) {
case Format10x:
annotateInstruction10x(out, instruction);
break;
case Format35c:
annotateInstruction35c(out, (Instruction35c)instruction);
break;
case Format3rc:
annotateInstruction3rc(out, (Instruction3rc)instruction);
break;
case ArrayPayload:
annotateArrayPayload(out, (ArrayPayload)instruction);
break;
case PackedSwitchPayload:
annotatePackedSwitchPayload(out, (PackedSwitchPayload)instruction);
break;
case SparseSwitchPayload:
annotateSparseSwitchPayload(out, (SparseSwitchPayload)instruction);
break;
default:
annotateDefaultInstruction(out, instruction);
break;
}
}
assert reader.getOffset() == out.getCursor();
}
} catch (ExceptionWithContext ex) {
ex.printStackTrace(System.err);
out.annotate(0, "annotation error: %s", ex.getMessage());
out.moveTo(end);
reader.setOffset(end);
} finally {
out.clearLimit();
out.deindent();
}
out.deindent();
int handlerListCount = reader.readSmallUleb128();
out.annotate(0, "encoded_catch_handler_list:");
out.annotateTo(reader.getOffset(), "size = %d", handlerListCount);
out.indent();
for (int i=0; i<handlerListCount; i++) {
out.annotate(0, "encoded_catch_handler[%d]", i);
if (triesCount > 0) {
if ((reader.getOffset() % 4) != 0) {
reader.readUshort();
out.annotate(2, "padding");
}
out.annotate(0, "try_items:");
out.indent();
int handlerCount = reader.readSleb128();
out.annotateTo(reader.getOffset(), "size = %d", handlerCount);
boolean hasCatchAll = handlerCount <= 0;
handlerCount = Math.abs(handlerCount);
if (handlerCount != 0) {
out.annotate(0, "handlers:");
out.indent();
for (int j=0; j<handlerCount; j++) {
out.annotate(0, "encoded_type_addr_pair[%d]", i);
try {
for (int i=0; i<triesCount; i++) {
out.annotate(0, "try_item[%d]:", i);
out.indent();
int typeIndex = reader.readSmallUleb128();
out.annotateTo(reader.getOffset(), TypeIdItem.getReferenceAnnotation(dexFile, typeIndex));
try {
int startAddr = reader.readSmallUint();
out.annotate(4, "start_addr = 0x%x", startAddr);
int handlerAddress = reader.readSmallUleb128();
out.annotateTo(reader.getOffset(), "addr = 0x%x", handlerAddress);
out.deindent();
int instructionCount = reader.readUshort();
out.annotate(2, "insn_count = 0x%x", instructionCount);
int handlerOffset = reader.readUshort();
out.annotate(2, "handler_off = 0x%x", handlerOffset);
} finally {
out.deindent();
}
}
} finally {
out.deindent();
}
if (hasCatchAll) {
int catchAllAddress = reader.readSmallUleb128();
out.annotateTo(reader.getOffset(), "catch_all_addr = 0x%x", catchAllAddress);
int handlerListCount = reader.readSmallUleb128();
out.annotate(0, "encoded_catch_handler_list:");
out.annotateTo(reader.getOffset(), "size = %d", handlerListCount);
out.indent();
try {
for (int i=0; i<handlerListCount; i++) {
out.annotate(0, "encoded_catch_handler[%d]", i);
out.indent();
try {
int handlerCount = reader.readSleb128();
out.annotateTo(reader.getOffset(), "size = %d", handlerCount);
boolean hasCatchAll = handlerCount <= 0;
handlerCount = Math.abs(handlerCount);
if (handlerCount != 0) {
out.annotate(0, "handlers:");
out.indent();
try {
for (int j=0; j<handlerCount; j++) {
out.annotate(0, "encoded_type_addr_pair[%d]", i);
out.indent();
try {
int typeIndex = reader.readSmallUleb128();
out.annotateTo(reader.getOffset(), TypeIdItem.getReferenceAnnotation(dexFile, typeIndex));
int handlerAddress = reader.readSmallUleb128();
out.annotateTo(reader.getOffset(), "addr = 0x%x", handlerAddress);
} finally {
out.deindent();
}
}
} finally {
out.deindent();
}
}
if (hasCatchAll) {
int catchAllAddress = reader.readSmallUleb128();
out.annotateTo(reader.getOffset(), "catch_all_addr = 0x%x", catchAllAddress);
}
} finally {
out.deindent();
}
}
} finally {
out.deindent();
}
out.deindent();
}
out.deindent();
} catch (ExceptionWithContext ex) {
out.annotate(0, "annotation error: %s", ex.getMessage());
}
}
@ -263,13 +303,17 @@ public class CodeItem {
}
}
} else if (instruction instanceof VerificationErrorInstruction) {
args.add(VerificationError.getVerificationErrorName(
((VerificationErrorInstruction)instruction).getVerificationError()));
String verificationError = VerificationError.getVerificationErrorName(
((VerificationErrorInstruction) instruction).getVerificationError());
if (verificationError != null) {
args.add(verificationError);
} else {
args.add("invalid verification error type");
}
}
if (instruction instanceof ReferenceInstruction) {
args.add(ReferenceUtil.getReferenceString(
((ReferenceInstruction)instruction).getReference()));
args.add(ReferenceUtil.getReferenceString(((ReferenceInstruction)instruction).getReference()));
} else if (instruction instanceof OffsetInstruction) {
int offset = ((OffsetInstruction)instruction).getCodeOffset();
String sign = offset>=0?"+":"-";

View File

@ -37,4 +37,5 @@ import javax.annotation.Nonnull;
public interface ReferenceInstruction extends Instruction {
@Nonnull Reference getReference();
int getReferenceType();
}

View File

@ -32,5 +32,5 @@
package org.jf.dexlib2.iface.instruction.formats;
public interface UnknownInstruction extends Instruction10x {
short getOriginalOpcode();
int getOriginalOpcode();
}

View File

@ -33,6 +33,7 @@ package org.jf.dexlib2.immutable.instruction;
import org.jf.dexlib2.Format;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.immutable.reference.ImmutableReference;
@ -67,6 +68,7 @@ public class ImmutableInstruction20bc extends ImmutableInstruction implements In
@Override public int getVerificationError() { return verificationError; }
@Nonnull @Override public ImmutableReference getReference() { return reference; }
@Override public int getReferenceType() { return ReferenceType.getReferenceType(reference); }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -67,6 +67,7 @@ public class ImmutableInstruction21c extends ImmutableInstruction implements Ins
@Override public int getRegisterA() { return registerA; }
@Nonnull @Override public ImmutableReference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -72,6 +72,7 @@ public class ImmutableInstruction22c extends ImmutableInstruction implements Ins
@Override public int getRegisterA() { return registerA; }
@Override public int getRegisterB() { return registerB; }
@Nonnull @Override public ImmutableReference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -67,6 +67,7 @@ public class ImmutableInstruction31c extends ImmutableInstruction implements Ins
@Override public int getRegisterA() { return registerA; }
@Nonnull @Override public ImmutableReference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -92,6 +92,7 @@ public class ImmutableInstruction35c extends ImmutableInstruction implements Ins
@Override public int getRegisterF() { return registerF; }
@Override public int getRegisterG() { return registerG; }
@Nonnull @Override public ImmutableReference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -73,6 +73,7 @@ public class ImmutableInstruction3rc extends ImmutableInstruction implements Ins
@Override public int getStartRegister() { return startRegister; }
@Override public int getRegisterCount() { return registerCount; }
@Nonnull @Override public ImmutableReference getReference() { return reference; }
@Override public int getReferenceType() { return opcode.referenceType; }
@Override public Format getFormat() { return FORMAT; }
}

View File

@ -38,9 +38,9 @@ import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
public class ImmutableUnknownInstruction extends ImmutableInstruction implements UnknownInstruction {
public static final Format FORMAT = Format.Format10x;
protected final short originalOpcode;
protected final int originalOpcode;
public ImmutableUnknownInstruction(short originalOpcode) {
public ImmutableUnknownInstruction(int originalOpcode) {
super(Opcode.NOP);
this.originalOpcode = originalOpcode;
}
@ -53,5 +53,5 @@ public class ImmutableUnknownInstruction extends ImmutableInstruction implements
}
@Override public Format getFormat() { return FORMAT; }
@Override public short getOriginalOpcode() { return originalOpcode; }
@Override public int getOriginalOpcode() { return originalOpcode; }
}

View File

@ -34,6 +34,8 @@ package org.jf.dexlib2.util;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.util.ExceptionWithContext;
import org.jf.util.Hex;
import org.jf.util.TwoColumnOutput;
@ -78,6 +80,9 @@ public class AnnotatedBytes {
*/
private int hexCols = 8;
private int startLimit = -1;
private int endLimit = -1;
public AnnotatedBytes(int width) {
this.outputWidth = width;
}
@ -115,7 +120,16 @@ public class AnnotatedBytes {
* @param formatArgs format arguments to pass to String.format
*/
public void annotate(int length, @Nonnull String msg, Object... formatArgs) {
String formattedMsg = String.format(msg, formatArgs);
if (startLimit != -1 && endLimit != -1 && (cursor < startLimit || cursor >= endLimit)) {
throw new ExceptionWithContext("Annotating outside the parent bounds");
}
String formattedMsg;
if (formatArgs != null && formatArgs.length > 0) {
formattedMsg = String.format(msg, formatArgs);
} else {
formattedMsg = msg;
}
int exclusiveEndOffset = cursor + length;
AnnotationEndpoint endPoint = null;
@ -129,19 +143,20 @@ public class AnnotatedBytes {
AnnotationEndpoint previousAnnotations = previousEntry.getValue();
AnnotationItem previousRangeAnnotation = previousAnnotations.rangeAnnotation;
if (previousRangeAnnotation != null) {
throw new IllegalStateException(
String.format("Cannot add annotation %s, due to existing annotation %s",
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(previousEntry.getKey(), previousRangeAnnotation.annotation)));
formatAnnotation(previousEntry.getKey(),
previousRangeAnnotation.annotation));
}
}
} else if (length > 0) {
AnnotationItem existingRangeAnnotation = startPoint.rangeAnnotation;
if (existingRangeAnnotation != null) {
throw new IllegalStateException(
String.format("Cannot add annotation %s, due to existing annotation %s",
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(cursor, existingRangeAnnotation.annotation)));
formatAnnotation(cursor, existingRangeAnnotation.annotation));
}
}
@ -156,23 +171,23 @@ public class AnnotatedBytes {
AnnotationEndpoint nextEndpoint = nextEntry.getValue();
AnnotationItem nextRangeAnnotation = nextEndpoint.rangeAnnotation;
if (nextRangeAnnotation != null) {
throw new IllegalStateException(
String.format("Cannot add annotation %s, due to existing annotation %s",
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(nextKey, nextRangeAnnotation.annotation)));
formatAnnotation(nextKey, nextRangeAnnotation.annotation));
}
if (nextEndpoint.pointAnnotations.size() > 0) {
throw new IllegalStateException(
String.format("Cannot add annotation %s, due to existing annotation %s",
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation %s",
formatAnnotation(cursor, cursor + length, formattedMsg),
formatAnnotation(nextKey, nextKey,
nextEndpoint.pointAnnotations.get(0).annotation)));
nextEndpoint.pointAnnotations.get(0).annotation));
}
// There are no annotations on this endpoint. This "shouldn't" happen. We can still throw an exception.
throw new IllegalStateException(
String.format("Cannot add annotation %s, due to existing annotation endpoint at %d",
throw new ExceptionWithContext(
"Cannot add annotation %s, due to existing annotation endpoint at %d",
formatAnnotation(cursor, cursor + length, formattedMsg),
nextKey));
nextKey);
}
if (nextKey == exclusiveEndOffset) {
@ -312,4 +327,14 @@ public class AnnotatedBytes {
twoc.write(left, "");
}
}
public void setLimit(int start, int end) {
this.startLimit = start;
this.endLimit = end;
}
public void clearLimit() {
this.startLimit = -1;
this.endLimit = -1;
}
}

View File

@ -59,7 +59,7 @@ public class InstructionOffsetMap {
int index = Arrays.binarySearch(instructionCodeOffsets, codeOffset);
if (index < 0) {
if (exact) {
throw new ExceptionWithContext("No instruction at offset %d", codeOffset);
throw new InvalidInstructionOffset(codeOffset);
} else {
// This calculation would be incorrect if index was -1 (i.e. insertion point of 0). Luckily, we can
// ignore this case, because codeOffset will always be non-negative, and the code offset of the first
@ -72,8 +72,34 @@ public class InstructionOffsetMap {
public int getInstructionCodeOffset(int index) {
if (index < 0 || index >= instructionCodeOffsets.length) {
throw new ExceptionWithContext("Index out of bounds: %d", index);
throw new InvalidInstructionIndex(index);
}
return instructionCodeOffsets[index];
}
public static class InvalidInstructionOffset extends ExceptionWithContext {
private final int instructionOffset;
public InvalidInstructionOffset(int instructionOffset) {
super("No instruction at offset %d", instructionOffset);
this.instructionOffset = instructionOffset;
}
public int getInstructionOffset() {
return instructionOffset;
}
}
public static class InvalidInstructionIndex extends ExceptionWithContext {
private final int instructionIndex;
public InvalidInstructionIndex(int instructionIndex) {
super("Instruction index out of bounds: %d", instructionIndex);
this.instructionIndex = instructionIndex;
}
public int getInstructionIndex() {
return instructionIndex;
}
}
}

View File

@ -37,6 +37,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.builder.MethodImplementationBuilder;
import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c;
@ -136,6 +137,8 @@ public class JumboStringConversionTest {
return ref;
}
@Override public int getReferenceType() { return ReferenceType.STRING; }
@Override public Opcode getOpcode() {
return Opcode.CONST_STRING;
}

View File

@ -922,8 +922,8 @@ insn_format21c_string
insn_format21c_type
: //e.g. const-class v2, Lorg/jf/HelloWorld2/HelloWorld2;
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA reference_type_descriptor
-> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor);
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA nonvoid_type_descriptor
-> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor);
insn_format21ih
: //e.g. const/high16 v1, 1234

View File

@ -840,13 +840,13 @@ insn_format21c_string
insn_format21c_type
: //e.g. const-class v2, org/jf/HelloWorld2/HelloWorld2
^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor)
^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor)
{
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text);
short regA = parseRegister_byte($REGISTER.text);
$method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
dexBuilder.internTypeReference($reference_type_descriptor.type)));
dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
};
insn_format21ih

View File

@ -142,7 +142,7 @@ public class ClassFileNameHandler {
writer.close();
f.delete(); //doesn't throw IOException
} catch (IOException ex) {
//if an exception occured, it's likely that we're on a windows system.
//if an exception occurred, it's likely that we're on a windows system.
return true;
}
}