This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ref: https://www.pnfsoftware.com/blog/reversing-dexguard-virtualization/ | |
// classes BEFORE devirtualization: PCodeVM, VClass | |
// they were cleaned-up a bit (e.g., removed a method and fields related to string obfuscation, | |
// which is auto cleaned-up by JEB and not relevant to DexGuard Virtualization) | |
package o; | |
import android.content.Context; | |
// generic p-code virtual machine | |
class PCodeVM { | |
public float a; | |
public int b; | |
public int c; | |
public long d; | |
public long e; | |
public double f; | |
public Object g; | |
public float h; | |
public Object i; | |
public double j; | |
private final long[] jstk; | |
private int stkidx; | |
private final int[] istk; | |
private final float[] fstk; | |
private int peekidx; | |
private final Object[] lstk; | |
private final double[] dstk; | |
public PCodeVM(Object arg3, Object arg4) { | |
this.istk = new int[12]; | |
this.jstk = new long[12]; | |
this.fstk = new float[12]; | |
this.dstk = new double[12]; | |
this.lstk = new Object[12]; | |
this.lstk[5] = arg3; | |
this.lstk[6] = arg4; | |
this.stkidx = 0; | |
this.peekidx = -1; | |
} | |
// returns 0 on completed execution, else, the input opcode | |
public int exec(int opcode) { | |
int v0 = 1; | |
switch(opcode) { | |
case 1: { | |
int v2 = this.stkidx; | |
this.stkidx = v2 + 1; | |
this.lstk[v2] = this.lstk[6]; | |
int v3 = this.stkidx - 1; | |
Object v0_1 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3] = ((byte[])v0_1).length; | |
this.lstk[this.stkidx - 1] = new byte[this.istk[this.stkidx - 1]]; | |
return 0; | |
} | |
case 2: { | |
--this.stkidx; | |
Object v3_1 = this.lstk[this.stkidx]; | |
this.lstk[this.stkidx] = null; | |
this.lstk[7] = v3_1; | |
return 0; | |
} | |
case 3: { | |
int v2_1 = this.stkidx; | |
this.stkidx = v2_1 + 1; | |
this.istk[v2_1] = 0; | |
--this.stkidx; | |
this.istk[8] = this.istk[this.stkidx]; | |
int v2_2 = this.stkidx; | |
this.stkidx = v2_2 + 1; | |
this.istk[v2_2] = 0; | |
return 0; | |
} | |
case 4: { | |
--this.stkidx; | |
this.istk[9] = this.istk[this.stkidx]; | |
return 0; | |
} | |
case 5: { | |
int v2_3 = this.stkidx; | |
this.stkidx = v2_3 + 1; | |
this.istk[v2_3] = 0; | |
--this.stkidx; | |
this.istk[11] = this.istk[this.stkidx]; | |
return 0; | |
} | |
case 6: { | |
Object v0_2 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.i = v0_2; | |
return 0; | |
} | |
case 7: { | |
int v2_4 = this.stkidx; | |
this.stkidx = v2_4 + 1; | |
this.lstk[v2_4] = this.lstk[7]; | |
return 0; | |
} | |
case 8: { | |
int v2_5 = this.stkidx; | |
this.stkidx = v2_5 + 1; | |
this.istk[v2_5] = this.c; | |
return 0; | |
} | |
case 9: { | |
int v2_6 = this.stkidx; | |
this.stkidx = v2_6 + 1; | |
this.istk[v2_6] = 43; | |
--this.stkidx; | |
this.istk[this.stkidx - 1] += this.istk[this.stkidx]; | |
int v2_7 = this.stkidx; | |
this.stkidx = v2_7 + 1; | |
this.istk[v2_7] = this.istk[this.stkidx - 2]; | |
return 0; | |
} | |
case 10: { | |
int v2_8 = this.stkidx; | |
this.stkidx = v2_8 + 1; | |
this.istk[v2_8] = 0x80; | |
return 0; | |
} | |
case 11: { | |
int v0_3 = this.stkidx - this.c; | |
this.stkidx = v0_3; | |
this.peekidx = v0_3; | |
return 0; | |
} | |
case 12: { | |
int v2_9 = this.peekidx; | |
this.peekidx = v2_9 + 1; | |
this.b = this.istk[v2_9]; | |
return 0; | |
} | |
case 13: { | |
--this.stkidx; | |
this.istk[this.stkidx - 1] %= this.istk[this.stkidx]; | |
return 0; | |
} | |
case 14: { | |
int v2_10 = this.stkidx; | |
this.stkidx = v2_10 + 1; | |
this.istk[v2_10] = 2; | |
return 0; | |
} | |
case 15: { | |
--this.stkidx; | |
if(this.istk[this.stkidx] != 0) { | |
v0 = 0; | |
} | |
this.b = v0; | |
return 0; | |
} | |
case 16: { | |
int v2_11 = this.stkidx; | |
this.stkidx = v2_11 + 1; | |
this.istk[v2_11] = 0x5F; | |
return 0; | |
} | |
case 17: { | |
int v2_12 = this.stkidx; | |
this.stkidx = v2_12 + 1; | |
this.istk[v2_12] = 98; | |
return 0; | |
} | |
case 18: { | |
int v2_13 = this.stkidx; | |
this.stkidx = v2_13 + 1; | |
this.istk[v2_13] = 0; | |
return 0; | |
} | |
case 19: { | |
++this.istk[10]; | |
return 0; | |
} | |
case 20: { | |
int v2_14 = this.stkidx; | |
this.stkidx = v2_14 + 1; | |
this.lstk[v2_14] = this.lstk[7]; | |
int v2_15 = this.stkidx; | |
this.stkidx = v2_15 + 1; | |
this.istk[v2_15] = this.istk[11]; | |
return 0; | |
} | |
case 21: { | |
int v2_16 = this.stkidx; | |
this.stkidx = v2_16 + 1; | |
this.lstk[v2_16] = this.lstk[6]; | |
int v2_17 = this.stkidx; | |
this.stkidx = v2_17 + 1; | |
this.istk[v2_17] = this.istk[11]; | |
--this.stkidx; | |
int v3_2 = this.stkidx - 1; | |
Object v0_4 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_2] = ((byte[])v0_4)[this.istk[this.stkidx]]; | |
return 0; | |
} | |
case 22: { | |
int v2_18 = this.stkidx; | |
this.stkidx = v2_18 + 1; | |
this.istk[v2_18] = this.istk[10]; | |
return 0; | |
} | |
case 23: { | |
int v2_19 = this.stkidx; | |
this.stkidx = v2_19 + 1; | |
this.istk[v2_19] = 3; | |
--this.stkidx; | |
this.istk[this.stkidx - 1] += this.istk[this.stkidx]; | |
--this.stkidx; | |
this.istk[this.stkidx - 1] ^= this.istk[this.stkidx]; | |
return 0; | |
} | |
case 24: { | |
this.istk[this.stkidx - 1] = (byte)this.istk[this.stkidx - 1]; | |
return 0; | |
} | |
case 25: { | |
this.stkidx += -3; | |
Object v0_5 = this.lstk[this.stkidx]; | |
this.lstk[this.stkidx] = null; | |
((byte[])v0_5)[this.istk[this.stkidx + 1]] = this.istk[this.stkidx + 2]; | |
++this.istk[11]; | |
return 0; | |
} | |
case 26: { | |
int v3_3 = this.stkidx; | |
this.stkidx = v3_3 + 1; | |
this.istk[v3_3] = 1; | |
return 0; | |
} | |
case 27: { | |
int v2_20 = this.stkidx; | |
this.stkidx = v2_20 + 1; | |
this.istk[v2_20] = 0x1F; | |
return 0; | |
} | |
case 28: { | |
--this.stkidx; | |
this.istk[this.stkidx - 1] += this.istk[this.stkidx]; | |
int v2_21 = this.stkidx; | |
this.stkidx = v2_21 + 1; | |
this.istk[v2_21] = this.istk[this.stkidx - 2]; | |
int v2_22 = this.stkidx; | |
this.stkidx = v2_22 + 1; | |
this.istk[v2_22] = 0x80; | |
return 0; | |
} | |
case 29: { | |
--this.stkidx; | |
if(this.istk[this.stkidx] == 0) { | |
v0 = 0; | |
} | |
this.b = v0; | |
return 0; | |
} | |
case 30: { | |
int v2_23 = this.stkidx - 1; | |
this.stkidx = v2_23; | |
this.b = this.istk[v2_23]; | |
return 0; | |
} | |
case 31: { | |
--this.stkidx; | |
this.lstk[this.stkidx] = null; | |
return 0; | |
} | |
case 32: { | |
int v3_4 = this.stkidx; | |
this.stkidx = v3_4 + 1; | |
this.istk[v3_4] = this.istk[8]; | |
int v3_5 = this.stkidx; | |
this.stkidx = v3_5 + 1; | |
this.istk[v3_5] = 1; | |
--this.stkidx; | |
this.istk[this.stkidx - 1] += this.istk[this.stkidx]; | |
return 0; | |
} | |
case 33: { | |
int v2_24 = this.stkidx; | |
this.stkidx = v2_24 + 1; | |
this.istk[v2_24] = 0xFF; | |
--this.stkidx; | |
this.istk[this.stkidx - 1] &= this.istk[this.stkidx]; | |
return 0; | |
} | |
case 34: { | |
--this.stkidx; | |
this.istk[8] = this.istk[this.stkidx]; | |
return 0; | |
} | |
case 35: { | |
int v2_25 = this.stkidx; | |
this.stkidx = v2_25 + 1; | |
this.istk[v2_25] = this.istk[9]; | |
return 0; | |
} | |
case 36: { | |
int v2_26 = this.stkidx; | |
this.stkidx = v2_26 + 1; | |
this.lstk[v2_26] = this.lstk[5]; | |
return 0; | |
} | |
case 37: { | |
int v2_27 = this.peekidx; | |
this.peekidx = v2_27 + 1; | |
Object v3_6 = this.lstk[v2_27]; | |
this.lstk[v2_27] = null; | |
this.i = v3_6; | |
return 0; | |
} | |
case 38: { | |
int v2_28 = this.stkidx; | |
this.stkidx = v2_28 + 1; | |
this.lstk[v2_28] = this.g; | |
return 0; | |
} | |
case 39: { | |
int v2_29 = this.stkidx; | |
this.stkidx = v2_29 + 1; | |
this.istk[v2_29] = this.istk[8]; | |
--this.stkidx; | |
int v3_7 = this.stkidx - 1; | |
Object v0_6 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_7] = ((byte[])v0_6)[this.istk[this.stkidx]]; | |
return 0; | |
} | |
case 40: { | |
--this.stkidx; | |
this.istk[this.stkidx - 1] += this.istk[this.stkidx]; | |
return 0; | |
} | |
case 41: { | |
int v2_30 = this.stkidx; | |
this.stkidx = v2_30 + 1; | |
this.istk[v2_30] = 0xFF; | |
return 0; | |
} | |
case 42: { | |
--this.stkidx; | |
this.istk[this.stkidx - 1] &= this.istk[this.stkidx]; | |
return 0; | |
} | |
case 43: { | |
--this.stkidx; | |
this.istk[9] = this.istk[this.stkidx]; | |
int v2_31 = this.stkidx; | |
this.stkidx = v2_31 + 1; | |
this.lstk[v2_31] = this.lstk[5]; | |
return 0; | |
} | |
case 44: { | |
int v2_32 = this.stkidx; | |
this.stkidx = v2_32 + 1; | |
this.istk[v2_32] = this.istk[9]; | |
--this.stkidx; | |
int v3_8 = this.stkidx - 1; | |
Object v0_7 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_8] = ((byte[])v0_7)[this.istk[this.stkidx]]; | |
--this.stkidx; | |
this.istk[10] = this.istk[this.stkidx]; | |
return 0; | |
} | |
case 45: { | |
int v2_33 = this.stkidx; | |
this.stkidx = v2_33 + 1; | |
Object v3_9 = this.lstk[this.stkidx - 2]; | |
this.lstk[this.stkidx - 2] = null; | |
this.lstk[v2_33] = v3_9; | |
this.istk[this.stkidx - 2] = this.istk[this.stkidx - 3]; | |
this.lstk[this.stkidx - 3] = v3_9; | |
return 0; | |
} | |
case 46: { | |
int v2_34 = this.stkidx; | |
this.stkidx = v2_34 + 1; | |
this.istk[v2_34] = this.istk[8]; | |
return 0; | |
} | |
case 47: { | |
--this.stkidx; | |
int v3_10 = this.stkidx - 1; | |
Object v0_8 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_10] = ((byte[])v0_8)[this.istk[this.stkidx]]; | |
this.stkidx += -3; | |
Object v0_9 = this.lstk[this.stkidx]; | |
this.lstk[this.stkidx] = null; | |
((byte[])v0_9)[this.istk[this.stkidx + 1]] = this.istk[this.stkidx + 2]; | |
return 0; | |
} | |
case 48: { | |
int v2_35 = this.stkidx; | |
this.stkidx = v2_35 + 1; | |
this.istk[v2_35] = this.istk[10]; | |
this.stkidx += -3; | |
Object v0_10 = this.lstk[this.stkidx]; | |
this.lstk[this.stkidx] = null; | |
((byte[])v0_10)[this.istk[this.stkidx + 1]] = this.istk[this.stkidx + 2]; | |
return 0; | |
} | |
case 49: { | |
--this.stkidx; | |
int v3_11 = this.stkidx - 1; | |
Object v0_11 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_11] = ((byte[])v0_11)[this.istk[this.stkidx]]; | |
int v2_36 = this.stkidx; | |
this.stkidx = v2_36 + 1; | |
this.lstk[v2_36] = this.lstk[5]; | |
return 0; | |
} | |
case 50: { | |
int v2_37 = this.stkidx; | |
this.stkidx = v2_37 + 1; | |
this.istk[v2_37] = this.istk[9]; | |
--this.stkidx; | |
int v3_12 = this.stkidx - 1; | |
Object v0_12 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_12] = ((byte[])v0_12)[this.istk[this.stkidx]]; | |
--this.stkidx; | |
this.istk[this.stkidx - 1] += this.istk[this.stkidx]; | |
return 0; | |
} | |
case 51: { | |
--this.stkidx; | |
this.istk[this.stkidx - 1] &= this.istk[this.stkidx]; | |
--this.stkidx; | |
this.istk[10] = this.istk[this.stkidx]; | |
return 0; | |
} | |
case 52: { | |
--this.stkidx; | |
int v3_13 = this.stkidx - 1; | |
Object v0_13 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_13] = ((byte[])v0_13)[this.istk[this.stkidx]]; | |
return 0; | |
} | |
case 53: { | |
--this.stkidx; | |
this.istk[10] = this.istk[this.stkidx]; | |
int v2_38 = this.stkidx; | |
this.stkidx = v2_38 + 1; | |
this.istk[v2_38] = this.istk[11]; | |
int v2_39 = this.stkidx; | |
this.stkidx = v2_39 + 1; | |
this.istk[v2_39] = 3; | |
return 0; | |
} | |
case 54: { | |
int v2_40 = this.stkidx; | |
this.stkidx = v2_40 + 1; | |
this.istk[v2_40] = this.istk[11]; | |
int v2_41 = this.stkidx; | |
this.stkidx = v2_41 + 1; | |
this.lstk[v2_41] = this.lstk[6]; | |
int v3_14 = this.stkidx - 1; | |
Object v0_14 = this.lstk[this.stkidx - 1]; | |
this.lstk[this.stkidx - 1] = null; | |
this.istk[v3_14] = ((byte[])v0_14).length; | |
return 0; | |
} | |
case 55: { | |
break; | |
} | |
default: { | |
return opcode; | |
} | |
} | |
this.stkidx += -2; | |
if(this.istk[this.stkidx] >= this.istk[this.stkidx + 1]) { | |
v0 = 0; | |
} | |
this.b = v0; | |
return 0; | |
} | |
} | |
/* | |
virtualized class (1 virtualized method) | |
identifiers were renamed for clarity | |
*/ | |
public final class VClass { | |
private Context ctx; | |
private int keylen; | |
private byte[] sbox; | |
private byte[] tbox; | |
private static int guard0; | |
private static int guard1; | |
/* | |
the constructor is NOT virtualized | |
we recognize a key scheduling algorithm | |
*/ | |
public VClass(Context ctx, byte[] key) { | |
this.sbox = new byte[0x100]; | |
this.tbox = new byte[0x100]; | |
this.ctx = ctx; | |
if(key.length <= 0 || key.length > 0x100) { | |
throw new IllegalArgumentException("illegal key length"); | |
} | |
this.keylen = key.length; | |
int i; | |
for(i = 0; i < 0x100; ++i) { | |
this.sbox[i] = (byte)i; | |
this.tbox[i] = key[i % this.keylen]; | |
} | |
int j = 0; | |
int k = 0; | |
while(j < 0x100) { | |
k = this.sbox[j] + k + this.tbox[j] & 0xFF; | |
byte v0 = this.sbox[k]; | |
this.sbox[k] = this.sbox[j]; | |
this.sbox[j] = v0; | |
++j; | |
} | |
} | |
// virtualized method, likely doing encryption/decryption | |
public final byte[] d(byte[] arg10) { | |
PCodeVM vm = new PCodeVM(this, arg10); | |
int[] pcode = {-1, 1, 2, 3, 4, 5, -2, 7, -3, -4, 9, 10, 13, -5, 14, 13, -6, -7, -8, 16, -9, 17, -9, 18, -10, 19, -11, 20, 21, 22, 23, 24, 25, -12, 26, -10, -13, 27, 28, 13, -14, 14, 13, -15, -16, -17, -18, 14, 14, 13, 0x1F, -19, 14, 14, 13, 0x1F, -12, -8, -20, -17, 0x20, 33, 34, 35, 36, -21, 39, 40, 41, 42, 43, -21, 44, 35, 36, -21, 45, 46, 0x2F, 36, -21, 46, 0x30, 36, -21, 46, 49, -21, 50, 41, 51, 36, -21, 22, 52, 53, 13, -22, -23, -24, 54, -25, -26, -27}; | |
int idx = 0; | |
next: | |
int idx1 = idx + 1; | |
switch(vm.exec(pcode[idx])) { | |
case -27: { | |
idx = 34; | |
goto next; | |
} | |
case -26: { | |
idx = 23; | |
goto next; | |
} | |
case -25: { | |
vm.exec(55); | |
if(vm.intLoaded == 0) { | |
idx = 103; | |
goto next; | |
} | |
idx = idx1; | |
goto next; | |
} | |
case -24: { | |
idx = 19; | |
goto next; | |
} | |
case -23: { | |
idx = 21; | |
goto next; | |
} | |
case -22: { | |
vm.exec(15); | |
if(vm.intLoaded == 0) { | |
idx = 99; | |
goto next; | |
} | |
idx = idx1; | |
goto next; | |
} | |
case -21: { | |
vm.intStored = 1; | |
vm.exec(11); | |
vm.exec(37); | |
vm.objStored = ((VClass)vm.objLoaded).sbox; | |
vm.exec(38); | |
idx = idx1; | |
goto next; | |
} | |
case -20: { | |
vm.exec(30); | |
switch(vm.intLoaded) { | |
case 0: { | |
idx = 9; | |
goto next; | |
} | |
case 1: { | |
idx = 7; | |
goto next; | |
} | |
} | |
idx = 7; | |
goto next; | |
idx = 9; | |
goto next; | |
idx = 7; | |
goto next; | |
} | |
case -19: { | |
idx = 1; | |
goto next; | |
} | |
case -18: { | |
vm.exec(30); | |
switch(vm.intLoaded) { | |
case 95: { | |
idx = 27; | |
goto next; | |
} | |
case 98: { | |
idx = 25; | |
goto next; | |
} | |
} | |
idx = 27; | |
goto next; | |
idx = 25; | |
goto next; | |
idx = 27; | |
goto next; | |
} | |
case -17: { | |
idx = 52; | |
goto next; | |
} | |
case -16: { | |
idx = 59; | |
goto next; | |
} | |
case -15: { | |
vm.exec(29); | |
if(vm.intLoaded == 0) { | |
idx = 45; | |
goto next; | |
} | |
idx = idx1; | |
goto next; | |
} | |
case -14: { | |
vm.intStored = 1; | |
vm.exec(11); | |
vm.exec(12); | |
VClass.guard0 = vm.intLoaded; | |
idx = idx1; | |
goto next; | |
} | |
case -13: { | |
vm.intStored = 1; | |
vm.exec(8); | |
idx = idx1; | |
goto next; | |
} | |
case -12: { | |
idx = 100; | |
goto next; | |
} | |
case -11: { | |
idx = 27; | |
goto next; | |
} | |
case -10: { | |
idx = 58; | |
goto next; | |
} | |
case -9: { | |
idx = 46; | |
goto next; | |
} | |
case -8: { | |
idx = 60; | |
goto next; | |
} | |
case -7: { | |
idx = 57; | |
goto next; | |
} | |
case -6: { | |
vm.exec(15); | |
if(vm.intLoaded == 0) { | |
idx = 18; | |
goto next; | |
} | |
idx = idx1; | |
goto next; | |
} | |
case -5: { | |
vm.intStored = 1; | |
vm.exec(11); | |
vm.exec(12); | |
VClass.guard1 = vm.intLoaded; | |
idx = idx1; | |
goto next; | |
} | |
case -4: { | |
vm.intStored = 0; | |
vm.exec(8); | |
idx = idx1; | |
goto next; | |
} | |
case -3: { | |
vm.exec(6); | |
return (byte[])vm.objLoaded; | |
} | |
case -2: { | |
idx = 36; | |
goto next; | |
} | |
case -1: { | |
idx = 0x2F; | |
goto next; | |
} | |
default: { | |
idx = idx1; | |
goto next; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment