From a91e87bb043c6a6d4304bb5157e29df815a09ed7 Mon Sep 17 00:00:00 2001 From: Connor Tumbleson Date: Sun, 19 Jan 2014 10:37:31 -0600 Subject: [PATCH] update to smali 2.0.3 --- CHANGES | 2 +- .../jf/baksmali/Adaptors/ClassDefinition.java | 12 +- .../Adaptors/Format/ArrayDataMethodItem.java | 2 + .../Format/InstructionMethodItem.java | 203 ++++++++++----- .../Format/PackedSwitchMethodItem.java | 3 + .../Format/SparseSwitchMethodItem.java | 1 + .../baksmali/Adaptors/MethodDefinition.java | 62 ++++- .../baksmali/Adaptors/ReferenceFormatter.java | 3 + .../main/java/org/jf/baksmali/baksmali.java | 85 +++++-- .../java/org/jf/baksmali/baksmaliOptions.java | 13 + .../src/main/java/org/jf/baksmali/main.java | 28 ++- .../java/org/jf/baksmali/AnalysisTest.java | 5 +- .../src/main/java/org/jf/dexlib2/Opcodes.java | 5 +- .../java/org/jf/dexlib2/ReferenceType.java | 48 +++- .../org/jf/dexlib2/VerificationError.java | 2 + .../jf/dexlib2/builder/MethodLocation.java | 81 ++++-- .../builder/MutableMethodImplementation.java | 8 + .../instruction/BuilderInstruction20bc.java | 2 + .../instruction/BuilderInstruction21c.java | 1 + .../instruction/BuilderInstruction22c.java | 1 + .../instruction/BuilderInstruction31c.java | 1 + .../instruction/BuilderInstruction35c.java | 1 + .../instruction/BuilderInstruction3rc.java | 1 + .../dexlib2/dexbacked/DexBackedDexFile.java | 2 +- .../instruction/DexBackedInstruction20bc.java | 13 +- .../instruction/DexBackedInstruction21c.java | 5 + .../instruction/DexBackedInstruction22c.java | 5 + .../instruction/DexBackedInstruction31c.java | 5 + .../instruction/DexBackedInstruction35c.java | 5 + .../instruction/DexBackedInstruction3rc.java | 6 + .../DexBackedUnknownInstruction.java | 9 +- .../jf/dexlib2/dexbacked/raw/CodeItem.java | 236 +++++++++++------- .../instruction/ReferenceInstruction.java | 1 + .../formats/UnknownInstruction.java | 2 +- .../instruction/ImmutableInstruction20bc.java | 2 + .../instruction/ImmutableInstruction21c.java | 1 + .../instruction/ImmutableInstruction22c.java | 1 + .../instruction/ImmutableInstruction31c.java | 1 + .../instruction/ImmutableInstruction35c.java | 1 + .../instruction/ImmutableInstruction3rc.java | 1 + .../ImmutableUnknownInstruction.java | 6 +- .../org/jf/dexlib2/util/AnnotatedBytes.java | 57 +++-- .../jf/dexlib2/util/InstructionOffsetMap.java | 30 ++- .../writer/JumboStringConversionTest.java | 3 + .../smali/src/main/antlr3/smaliParser.g | 4 +- .../smali/src/main/antlr3/smaliTreeWalker.g | 4 +- .../org/jf/util/ClassFileNameHandler.java | 2 +- 47 files changed, 718 insertions(+), 254 deletions(-) diff --git a/CHANGES b/CHANGES index 254dc0a5..e21fe133 100644 --- a/CHANGES +++ b/CHANGES @@ -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. diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index 60ef448c..a4eb6913 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -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; diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java index f621a589..1d4957ec 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/ArrayDataMethodItem.java @@ -65,6 +65,8 @@ public class ArrayDataMethodItem extends InstructionMethodItem { 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); diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java index b4508005..300efd81 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java @@ -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 extends MethodItem { @Nonnull protected final MethodDefinition methodDef; @@ -59,29 +65,105 @@ public class InstructionMethodItem 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 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 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 extends MethodItem { writeFirstRegister(writer); writer.write(", "); writeTargetLabel(writer); - return true; + break; case Format22b: case Format22s: writeOpcode(writer); @@ -158,7 +242,7 @@ public class InstructionMethodItem extends MethodItem { writeSecondRegister(writer); writer.write(", "); writeLiteral(writer); - return true; + break; case Format22c: writeOpcode(writer); writer.write(' '); @@ -166,8 +250,8 @@ public class InstructionMethodItem 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 extends MethodItem { writeSecondRegister(writer); writer.write(", "); writeFieldOffset(writer); - return true; + break; case Format22t: writeOpcode(writer); writer.write(' '); @@ -185,7 +269,7 @@ public class InstructionMethodItem extends MethodItem { writeSecondRegister(writer); writer.write(", "); writeTargetLabel(writer); - return true; + break; case Format22x: case Format32x: writeOpcode(writer); @@ -193,7 +277,7 @@ public class InstructionMethodItem extends MethodItem { writeFirstRegister(writer); writer.write(", "); writeSecondRegister(writer); - return true; + break; case Format23x: writeOpcode(writer); writer.write(' '); @@ -202,52 +286,59 @@ public class InstructionMethodItem 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 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 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 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)); - } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java index 45b68c28..5d7dea7e 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java @@ -82,9 +82,12 @@ public class PackedSwitchMethodItem extends InstructionMethodItem "); target.writeTargetTo(writer); + writeResourceId(writer, target.getKey()); writer.write('\n'); } writer.deindent(4); diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index 8ebd1e78..5ce971f5 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -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; + } + } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java index a39b66d6..91d142a8 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java @@ -57,6 +57,9 @@ public class ReferenceFormatter { return; case ReferenceType.FIELD: ReferenceUtil.writeFieldDescriptor(writer, (FieldReference)reference); + return; + default: + throw new IllegalStateException("Unknown reference type"); } } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java index 64b35b81..fcc89d7a 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -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 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 task: tasks) { - while(true) { - try { - if (!task.get()) { - errorOccurred = true; + try { + for (Future 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(); } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java index 759fa98f..07a05869 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java @@ -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 bootClassPathEntries = Lists.newArrayList(); public List extraClassPathEntries = Lists.newArrayList(); + public Map resourceIdFileEntries = new HashMap(); + public Map resourceIds = new HashMap(); + 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]); + } + } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java index 83009a6d..a8b1e241 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java @@ -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" + " (.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); diff --git a/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java b/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java index a3b58254..38219491 100644 --- a/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java +++ b/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java @@ -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)); } } diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java index e52db0b4..d6e5532e 100644 --- a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java @@ -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; } } } diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java index 5b74e7dc..3371f818 100644 --- a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java @@ -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; } } diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java index 4e9ccd05..d0aa0297 100644 --- a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java @@ -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: diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java index f7c0effc..5a707e92 100644 --- a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java @@ -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