Skip to content

Instantly share code, notes, and snippets.

@nfalliere
Last active July 8, 2020 21:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nfalliere/209e1c27e358eb3cd22595b7326409eb to your computer and use it in GitHub Desktop.
Save nfalliere/209e1c27e358eb3cd22595b7326409eb to your computer and use it in GitHub Desktop.
// 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