From e24e70f9ad584d45d2a2af911c1d056163f1bc74 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Wed, 23 Dec 2009 05:24:27 +0000 Subject: [PATCH] Handle unresolvable instructions in a more intellegent manner Don't replace an unresolvable instruction with replacement instruction(s) that are larger, to avoid inserting extra bytes, and possibly moving jump type instructions too far from their targets. git-svn-id: https://smali.googlecode.com/svn/trunk@497 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../UnresolvedNullReferenceMethodItem.java | 20 +++++++++++ .../baksmali/Adaptors/MethodDefinition.java | 19 +++++++++- .../templates/templates/baksmali.stg | 14 +++++--- .../src/test/smali/deodex_test1/main.smali | 36 +++++++++++++++++++ 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java index aea5f7be..002cf4d0 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedNullReferenceMethodItem.java @@ -34,6 +34,16 @@ import org.antlr.stringtemplate.StringTemplateGroup; import org.antlr.stringtemplate.StringTemplate; public class UnresolvedNullReferenceMethodItem extends InstructionFormatMethodItem { + private boolean isLastInstruction; + + public boolean getIsLastInstruction() { + return isLastInstruction; + } + + public void setIsLastInstruction(boolean isLastInstruction) { + this.isLastInstruction = isLastInstruction; + } + public UnresolvedNullReferenceMethodItem(CodeItem codeItem, int offset, StringTemplateGroup stg, UnresolvedNullReference instruction) { super(codeItem, offset, stg, instruction); @@ -41,5 +51,15 @@ public class UnresolvedNullReferenceMethodItem extends InstructionFormatMethodIt protected void setAttributes(StringTemplate template) { template.setAttribute("Register", formatRegister(instruction.ObjectRegisterNum)); + switch (instruction.OriginalInstruction.opcode) + { + case INVOKE_VIRTUAL_RANGE_QUICK: + case INVOKE_SUPER_RANGE_QUICK: + template.setAttribute("UseInvokeRange", 1); + if (isLastInstruction) { + template.setAttribute("AddGoto", 1); + } + } + } } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index 2119b5ca..d1c31578 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -227,6 +227,23 @@ public class MethodDefinition { offset += instruction.getSize()/2; } + + /* + * Look for the last uncommented instruction. If it is an UnresolvedNullReference, + * then set IsLastInstruction, so a goto will be added after it, to avoid validation + * issues + */ + for (int i=this.instructions.size()-1; i>=0; i--) { + MethodItem ins = this.instructions.get(i); + if (ins instanceof UnresolvedNullReferenceMethodItem) { + ((UnresolvedNullReferenceMethodItem)ins).setIsLastInstruction(true); + break; + } + + if (!(ins instanceof CommentedOutMethodItem)) { + break; + } + } } else { final byte[] encodedInstructions = codeItem.getEncodedInstructions(); @@ -543,7 +560,7 @@ public class MethodDefinition { if (index < 0) { index = (index * -1) - 1; } - //index should never by 0, so this should be safe + //index should never be 0, so this should be safe if (index == instructions.size()) { //if the end address is the same as the address of the last instruction, then //this try item ends at the next to last instruction. diff --git a/baksmali/src/main/resources/templates/templates/baksmali.stg b/baksmali/src/main/resources/templates/templates/baksmali.stg index 26cf0927..12355355 100644 --- a/baksmali/src/main/resources/templates/templates/baksmali.stg +++ b/baksmali/src/main/resources/templates/templates/baksmali.stg @@ -288,16 +288,20 @@ CommentedOutMethodItem(MethodItem) ::= # >> -UnresolvedNullReference(Opcode, Register) ::= +UnresolvedNullReference(Opcode, Register, UseInvokeRange, AddGoto) ::= << -#Couldn't resolve optimized instruction while deodexing, due to the object register -#always being null. Replaced with generic method call + +#Replaced unresolvable optimized invoke-*-range-quick instruction +#with a generic method call that will throw a NullPointerException invoke-virtual/range { .. }, Ljava/lang/Object;->hashCode()I -goto/32 0 +goto/32 0 + +#Replaced unresolvable optimized instruction with a throw +throw + >> - ArrayData(Opcode, ElementWidth, Values) ::= << .array-data diff --git a/baksmali/src/test/smali/deodex_test1/main.smali b/baksmali/src/test/smali/deodex_test1/main.smali index 946b8a4e..99ffecad 100644 --- a/baksmali/src/test/smali/deodex_test1/main.smali +++ b/baksmali/src/test/smali/deodex_test1/main.smali @@ -71,4 +71,40 @@ return-void +.end method + +.method public static UnresolvedInstructionTest1()Lsuperclass; + .registers 2 + const v0, 0 + + #this is an unresolvable instruction, due to v0 always being null + #this instruction should be replaced with "throw v0", followed by + #a "goto/32 0", since it would otherwise be the last instruction + #in the method, which isn't allowed + invoke-virtual/range {v0 .. v0}, Lrandomclass;->getSuperclass()Lsuperclass; + + #the following instructions should be commented out + move-result-object v1 + return-object v1 +.end method + +.method public static UnresolvedInstructionTest2()Lsuperclass; + .registers 2 + const v0, 0 + + if-eqz v0, :here + + #this is an unresolvable instruction, due to v0 always being null + #this instruction should be replaced with "throw v0". There shouldn't + #be a "goto/32 0" afterwards, since it won't be the last instruction + #in the method. + invoke-virtual/range {v0 .. v0}, Lrandomclass;->getSuperclass()Lsuperclass; + + #the following instructions should be commented out + move-result-object v1 + return-object v1 + + #and now back to our normal programming + :here + return-object v0 .end method \ No newline at end of file