diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java index 6b64f83b..b1556b33 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java @@ -31,6 +31,7 @@ package org.jf.baksmali.Adaptors.Format; import org.jf.baksmali.Adaptors.MethodItem; import org.jf.baksmali.Adaptors.ReferenceFormatter; import org.jf.baksmali.Adaptors.RegisterFormatter; +import org.jf.dexlib.Code.Format.Instruction20bc; import org.jf.util.IndentingWriter; import org.jf.baksmali.Renderers.LongRenderer; import org.jf.dexlib.Code.*; @@ -84,6 +85,13 @@ public class InstructionMethodItem extends MethodItem { writer.write(", "); writeSecondRegister(writer); return true; + case Format20bc: + writeOpcode(writer); + writer.write(' '); + writeVerificationErrorType(writer); + writer.write(", "); + writeReference(writer); + return true; case Format20t: case Format30t: writeOpcode(writer); @@ -305,4 +313,9 @@ public class InstructionMethodItem extends MethodItem { Item item = ((InstructionWithReference)instruction).getReferencedItem(); ReferenceFormatter.writeReference(writer, item); } + + protected void writeVerificationErrorType(IndentingWriter writer) throws IOException { + VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType(); + writer.write(validationErrorType.getName()); + } } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java index 9ca95852..4ed9fec0 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java @@ -36,6 +36,7 @@ public enum Format { Format11n(Instruction11n.Factory, 2), Format11x(Instruction11x.Factory, 2), Format12x(Instruction12x.Factory, 2), + Format20bc(Instruction20bc.Factory, 4), Format20t(Instruction20t.Factory, 4), Format21c(Instruction21c.Factory, 4), Format21h(Instruction21h.Factory, 4), diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java new file mode 100644 index 00000000..75af9df7 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java @@ -0,0 +1,100 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 Ben Gruver + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib.Code.Format; + +import org.jf.dexlib.*; +import org.jf.dexlib.Code.*; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction20bc extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + private VerificationErrorType validationErrorType; + + public Instruction20bc(Opcode opcode, VerificationErrorType validationErrorType, Item referencedItem) { + super(opcode, referencedItem, getReferenceType(referencedItem)); + + this.validationErrorType = validationErrorType; + } + + private static ReferenceType getReferenceType(Item item) { + if (item instanceof TypeIdItem) { + return ReferenceType.type; + } + if (item instanceof FieldIdItem) { + return ReferenceType.field; + } + if (item instanceof MethodIdItem) { + return ReferenceType.method; + } + return null; + } + + private Instruction20bc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]); + validationErrorType = VerificationErrorType.getValidationErrorType(val & 0x3f); + } + + protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) { + short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]); + short referenceType = (short)(val >> 6); + return ReferenceType.fromValidationErrorReferenceType(referenceType); + } + + @Override + public Format getFormat() { + return Format.Format20bc; + } + + @Override + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if(opcode == Opcode.CONST_STRING && getReferencedItem().getIndex() > 0xFFFF) { + throw new RuntimeException("String offset is too large for const-string. Use string-const/jumbo instead."); + } + + out.writeByte(opcode.value); + out.writeByte((this.getReferenceType().getValidationErrorReferenceType() << 6) & + validationErrorType.getValue()); + + out.writeShort(getReferencedItem().getIndex()); + } + + public VerificationErrorType getValidationErrorType() { + return validationErrorType; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction20bc(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java index c0957592..b2e26c0f 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java @@ -33,17 +33,27 @@ import org.jf.dexlib.Util.NumberUtils; public abstract class InstructionWithReference extends Instruction { private Item referencedItem; + private ReferenceType referenceType; protected InstructionWithReference(Opcode opcode, Item referencedItem) { super(opcode); this.referencedItem = referencedItem; + this.referenceType = opcode.referenceType; + checkReferenceType(); + } + + protected InstructionWithReference(Opcode opcode, Item referencedItem, ReferenceType referenceType) { + super(opcode); + this.referencedItem = referencedItem; + this.referenceType = referenceType; checkReferenceType(); } protected InstructionWithReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { super(opcode); - int itemIndex = getReferencedItemIndex(buffer, bufferIndex); + this.referenceType = readReferenceType(opcode, buffer, bufferIndex); + int itemIndex = getReferencedItemIndex(buffer, bufferIndex); lookupReferencedItem(dexFile, opcode, itemIndex); } @@ -51,12 +61,20 @@ public abstract class InstructionWithReference extends Instruction { return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); } + public ReferenceType getReferenceType() { + return referenceType; + } + public Item getReferencedItem() { return referencedItem; } + protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) { + return opcode.referenceType; + } + private void lookupReferencedItem(DexFile dexFile, Opcode opcode, int itemIndex) { - switch (opcode.referenceType) { + switch (referenceType) { case field: referencedItem = dexFile.FieldIdsSection.getItemByIndex(itemIndex); return; @@ -73,7 +91,7 @@ public abstract class InstructionWithReference extends Instruction { private void checkReferenceType() { - switch (opcode.referenceType) { + switch (referenceType) { case field: if (!(referencedItem instanceof FieldIdItem)) { throw new RuntimeException(referencedItem.getClass().getSimpleName() + diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java index 4a15a1bf..65dc077f 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java @@ -263,6 +263,7 @@ public enum Opcode SGET_WIDE_VOLATILE((byte)0xea, "sget-wide-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), SPUT_WIDE_VOLATILE((byte)0xeb, "sput-wide-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + THROW_VERIFICATION_ERROR((byte)0xed, "throw-verification-error", ReferenceType.none, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW), EXECUTE_INLINE((byte)0xee, "execute-inline", ReferenceType.none, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), EXECUTE_INLINE_RANGE((byte)0xef, "execute-inline/range", ReferenceType.none, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_DIRECT_EMPTY((byte)0xf0, "invoke-direct-empty", ReferenceType.method, Format.Format35s, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java b/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java index f87770ee..f6d147ab 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java @@ -32,11 +32,17 @@ import org.jf.dexlib.*; public enum ReferenceType { - string, - type, - field, - method, - none; + string(-1), + type(0), + field(1), + method(2), + none(-1); + + private int validationErrorReferenceType; + + private ReferenceType(int validationErrorReferenceType) { + this.validationErrorReferenceType = validationErrorReferenceType; + } public boolean checkItem(Item item) { switch (this) { @@ -48,9 +54,27 @@ public enum ReferenceType return item instanceof FieldIdItem; case method: return item instanceof MethodIdItem; - case none: - return item == null; } return false; } + + public static ReferenceType fromValidationErrorReferenceType(int validationErrorReferenceType) { + switch (validationErrorReferenceType) { + case 0: + return type; + case 1: + return field; + case 2: + return method; + } + return null; + } + + public int getValidationErrorReferenceType() { + if (validationErrorReferenceType == -1) { + throw new RuntimeException("This reference type cannot be referenced from a throw-validation-error" + + " instruction"); + } + return validationErrorReferenceType; + } } \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java b/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java new file mode 100644 index 00000000..2670eaf5 --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java @@ -0,0 +1,83 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 Ben Gruver + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib.Code; + +public enum VerificationErrorType { + None(0, "no-error"), + Generic(1, "generic-error"), + NoClass(2, "no-such-class"), + NoField(3, "no-such-field"), + NoMethod(4, "no-such-method"), + AccessClass(5, "illegal-class-access"), + AccessField(6, "illegal-field-access"), + AccessMethod(7, "illegal-method-access"), + ClassChange(8, "class-change-error"), + Instantiation(9, "instantiation-error"); + + private int value; + private String name; + private VerificationErrorType(int value, String name) { + this.value = value; + this.name = name; + } + + public int getValue() { + return value; + } + + public String getName() { + return name; + } + + public static VerificationErrorType getValidationErrorType(int validationErrorType) { + switch (validationErrorType) { + case 0: + return None; + case 1: + return Generic; + case 2: + return NoClass; + case 3: + return NoField; + case 4: + return NoMethod; + case 5: + return AccessClass; + case 6: + return AccessField; + case 7: + return AccessMethod; + case 8: + return ClassChange; + case 9: + return Instantiation; + } + return null; + } +} diff --git a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g index 5dc2dbe1..ceac9545 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g @@ -331,6 +331,18 @@ ACCESS_SPEC | 'volatile' | 'transient'; +VERIFICATION_ERROR_TYPE + : 'no-error' + | 'generic-error' + | 'no-such-class' + | 'no-such-field' + | 'no-such-method' + | 'illegal-class-access' + | 'illegal-field-access' + | 'illegal-method-access' + | 'class-change-error' + | 'instantiation-error'; + VTABLE_OFFSET : 'vtable@0x' HEX_DIGIT+; @@ -434,6 +446,9 @@ INSTRUCTION_FORMAT12x | 'div-double/2addr' | 'rem-double/2addr'; +INSTRUCTION_FORMAT20bc + : 'throw-verification-error'; + INSTRUCTION_FORMAT20t : 'goto/16'; diff --git a/smali/src/main/antlr3/org/jf/smali/smaliParser.g b/smali/src/main/antlr3/org/jf/smali/smaliParser.g index 62dc0ab5..9181a9a7 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliParser.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliParser.g @@ -419,6 +419,7 @@ the indicated type OR an identifier, depending on the context*/ simple_name : SIMPLE_NAME | ACCESS_SPEC -> SIMPLE_NAME[$ACCESS_SPEC] + | VERIFICATION_ERROR_TYPE -> SIMPLE_NAME[$VERIFICATION_ERROR_TYPE] | POSITIVE_INTEGER_LITERAL -> SIMPLE_NAME[$POSITIVE_INTEGER_LITERAL] | NEGATIVE_INTEGER_LITERAL -> SIMPLE_NAME[$NEGATIVE_INTEGER_LITERAL] | INTEGER_LITERAL -> SIMPLE_NAME[$INTEGER_LITERAL] @@ -684,6 +685,11 @@ instruction returns [int size] | //e.g. move v1 v2 instruction_format12x REGISTER COMMA REGISTER {$size = Format.Format12x.size;} -> ^(I_STATEMENT_FORMAT12x[$start, "I_STATEMENT_FORMAT12x"] instruction_format12x REGISTER REGISTER) + | //e.g. throw-verification-error generic-error, Lsome/class; + INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE COMMA (CLASS_DESCRIPTOR | fully_qualified_field | fully_qualified_method) + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT20bc.text); + } | //e.g. goto/16 endloop: INSTRUCTION_FORMAT20t label_ref_or_offset {$size = Format.Format20t.size;} -> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t label_ref_or_offset) diff --git a/smali/src/main/jflex/smaliLexer.flex b/smali/src/main/jflex/smaliLexer.flex index 9d534363..ab1dca9b 100644 --- a/smali/src/main/jflex/smaliLexer.flex +++ b/smali/src/main/jflex/smaliLexer.flex @@ -364,6 +364,11 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor} return newToken(ACCESS_SPEC); } + "no-error" | "generic-error" | "no-such-class" | "no-such-field" | "no-such-method" | "illegal-class-access" | + "illegal-field-access" | "illegal-method-access" | "class-change-error" | "instantiation-error" { + return newToken(VERIFICATION_ERROR_TYPE); + } + "vtable@0x" {HexDigit}+ { return newToken(VTABLE_OFFSET); } "field@0x" {HexDigit}+ { return newToken(FIELD_OFFSET); } @@ -407,6 +412,10 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor} return newToken(INSTRUCTION_FORMAT12x); } + "throw-verification-error" { + return newToken(INSTRUCTION_FORMAT20bc); + } + "goto/16" { return newToken(INSTRUCTION_FORMAT20t); }