JesusFreke / smali

smali/baksmali
6.29k stars 1.07k forks source link

Why does the Java verifier reject this class? #797

Closed fmresearchnovak closed 3 years ago

fmresearchnovak commented 3 years ago

I wrote an Android app and used apktool (smali) to convert it into smali code. I then wrote many new smali assembly instructions through-out the app. For some reason, in the class FragmentFactory the verifier rejects the method loadFragmentClass due to the code I added on lines 423 and 425 https://pastebin.com/F1VbWJ07 When those lines are present the application crashes when running on a Nexus 5x. I get this java verify error: https://pastebin.com/NCC6fdfZ

If I comment out lines 423 and 425 in that file, the verifier does not raise any issue and the application runs fine. I am confused, because the lines in question don't seem to have anything to do with the verify error, and they seem to be perfectly acceptable smali assembly instructions.

Why does the java verifier reject this class? https://pastebin.com/F1VbWJ07

JesusFreke commented 3 years ago

The first thing to do in a situation like this is to disassemble the problematic dex file with register information. baksmali d --register-info ARGS,DEST,MERGE blah.dex

Then take a look at the line where the verification exception occurs. Based on the error message, you should see a register with the type Ljava/lang/Object;, which should be Ljava/lang/String;. You'll want to follow the control flow backward from there, to identify where the register type is changed to Ljava/lang/Object;. I haven't looked at your code in detail, but I suspect it's due to the code you added causing an extra control flow edge to be added to the exception handler covering that code, which then affects the result of a register type merge from the incoming edges.

fmresearchnovak commented 3 years ago

Yes, thank you Mr. Gruver (JesusFreke). Your comments about "an extra control flow edge" helped me realize and solve the problem. THANK YOU!

For others reading this, the problematic code that I added to the project happened to be added inside of a try / catch block. Because the instructions I added could they themselves have thrown a "class not found" exception, they introduced a new path through the program. The Java verifier correctly identified that on that new path, the register v0 would not contain a String even though it needs to for some StringBuilder.append() calls on that path.