After reading this interesting article about code obfuscation in Android, I'm trying to do it for research purposes but after applying the technique into a classes.dex file I'm getting a crash.
The next is the code I'm trying to run after applying the technique:
0006e8: |[0006e8] com.example.root.bji.MainActivity.paintGUI:()V
0006f8: 1202 |0000: const/4 v2, #int 0 // #0
0006fa: 1a01 0000 |0001: const-string v1, "" // string@0000
0006fe: 1200 |0003: const/4 v0, #int 0 // #0
000700: 1303 1400 |0004: const/16 v3, #int 20 // #14
000704: 3244 0900 |0006: if-eq v4, v4, 000f // +0009
000708: 2600 0300 0000 |0008: fill-array-data v0, 0000000b // +00000003
00070e: 0003 0100 1600 0000 1212 0000 0000 ... |000b: array-data (15 units)
00072c: 0000 |001a: nop // spacer
00072e: 0000 |001b: nop // spacer
... more NOPs ...
000742: 0000 |0025: nop // spacer
000744: 0000 |0026: nop // spacer
000746: 1503 087f |0027: const/high16 v3, #int 2131230720 // #7f08
...
To give you some context, I want to keep clear some assignations like the 0 value into the v2 register at 0x6f8 ("const/4 v2, 0" => 12 02), which will be shown in the GUI at the end of this method (at 0x746 and beyond); and using this obfuscation technique, "hide" the modification of the v2 register setting a value of 1 into the v2 register at 0x716 ("const/4 v2, 1" => 12 12). If you follow the code at 0x704 the branch is done to 0x716, where the "const/4 v2, 1"r esides, inside the fill-data-array-payload.
And the problem I'm facing is a crash when I'm running the code (I've tried it from 4.3 to 5.1), and what logcat tells me when the crash happens is:
W/dalvikvm(13874): VFY: invalid branch target 9 (-> 0xf) at 0x6
W/dalvikvm(13874): VFY: rejected Lcom/example/root/bji/MainActivity;.paintGUI ()V
W/dalvikvm(13874): VFY: rejecting opcode 0x32 at 0x0006
W/dalvikvm(13874): VFY: rejected Lcom/example/root/bji/MainActivity;.paintGUI ()V
W/dalvikvm(13874): Verifier rejected class Lcom/example/root/bji/MainActivity;
W/dalvikvm(13874): Class init failed in newInstance call (Lcom/example/root/bji/MainActivity;)
D/AndroidRuntime(13874): Shutting down VM
For what I understand in the logs, the OS is rejecting the "if-eq" jump because the offset pointed (I've tried other branch instructions but the result is the same). The only way the code works is if I point to an offset outside the fill-array-data-payload, but then there is no obfuscation technique applied :P.
Anyone have tried something similar to this technique or have fight against this branch verification rejection?
This is not expected to work. The bytecode verifier explicitly checks all branches for validity. The question of whether or not an address is an instruction or data is determined by a linear walk through the method. Data chunks are essentially very large instructions, so they get stepped over.
You can make this work if you modify the .odex output, and set the "pre-verified" flag on the class so the verifier doesn't examine it again -- but you can't distribute an APK that way.