What methods are being used to protect this Androi

2019-02-28 14:47发布

问题:

I know the basics to java programming, but I'm new to reverse engineering APKs, so explanations would be nice!


I have an APK file file, but not the Java source. After decompiling the APK online:

the bulk of the application is hidden under

assets > classes.dex.dat

the only java file I found is

com > ... > util > ProtectedUtils.java

I have ProtectedUtils.java below: Link to full file if anyone is interested

   import android.app.Application;
    import android.app.Instrumentation;
    import android.content.Context;
    import android.os.Build.VERSION;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.List;

    public class ProtectedApplicationUtils extends Application {
        private static Application f0d;
        private static boolean f1d;
        private static transient Object[] f2d;
        private static Application dd;
        private static boolean gfgf;

        public ProtectedApplicationUtils() {
            dd = this;
        }

        private static final int attachBaseContext(int i, int i2) {
            int i3 = (i2 + i) >> 24;
            return (i >>> i2) | (i << (-i2));
        }

        public static Context attachBaseContext(Context context) {
            attachBaseContext();
            return context == dd ? f0d : context;
        }

        private static void attachBaseContext() {
            if (!f1d) {
                f1d = true;
                Class cls = Class.forName(m1d("\u50b3\uc849\ue145\ud010\udf4f\u45e5\u6b13\u36e0\u0e7b\ucae7\u774e\uc2b0\ub84a\udeeb\u9071\u3fd2\u3dd6\u3676\u95ca\u031b\udc13\ufaca\u3bf1\u0935\u75af\ud3d6"));
                Class[] clsArr = new Class[0];
                Object invoke = cls.getMethod(m1d("\u50b1\uc852\ue153\ud010\udf45\u45e2\u6b03\u368f\u0e79\ucae3\u7757\uc2e8\ub862\udefc\u907c\u3fef\u3dc8\u366d\u95db\u0303\udc23"), clsArr).invoke(null, new Object[0]);
                Field declaredField = cls.getDeclaredField(m1d("\u50bf\uc866\ue14d\ud00e\udf61\u45fc\u6b07\u36a2\u0e73\ucaf4\u775f\uc2ea\ub862\udee7\u906b\u3fc8"));
                declaredField.setAccessible(true);
                ((List) declaredField.get(invoke)).add(0, f0d);
                Field declaredField2 = cls.getDeclaredField(m1d("\u50bf\uc86e\ue14f\ud00b\udf54\u45e5\u6b16\u36a2\u0e5b\ucae7\u774e\uc2f2\ub862\udeeb\u9064\u3fcf\u3dc9\u3670\u95d0"));
                declaredField2.setAccessible(true);
                declaredField2.set(invoke, f0d);
                Field declaredField3 = cls.getDeclaredField(m1d("\u50bf\uc865\ue14e\ud017\udf4e\u45e8\u6b36\u36be\u0e6a\ucafb\u7757\uc2fd\ub86a\udefc\u906c\u3fd4\u3dce"));
                declaredField3.setAccessible(true);
                Object obj = declaredField3.get(invoke);
                Field declaredField4 = obj.getClass().getDeclaredField(m1d("\u50bb\uc849\ue147\ud00d"));
                declaredField4.setAccessible(true);
                Object obj2 = declaredField4.get(obj);
                Field declaredField5 = obj2.getClass().getDeclaredField(m1d("\u50bf\uc866\ue151\ud012\udf4c\u45e5\u6b14\u36af\u0e6e\ucafe\u7751\uc2f0"));
                declaredField5.setAccessible(true);
                declaredField5.set(obj2, f0d);
                Context baseContext = f0d.getBaseContext();
                Field declaredField6 = baseContext.getClass().getDeclaredField(m1d("\u50bf\uc868\ue154\ud016\udf45\u45fe\u6b34\u36a1\u0e74\ucae3\u775b\uc2e6\ub87f"));
                declaredField6.setAccessible(true);
                declaredField6.set(baseContext, f0d);
            }
        }

        private static final int m0d(byte[] bArr, int i) {
            Object obj = null;
            int i2 = bArr[14] << 16;
            Object obj2 = null;
            while (obj2 == null) {
                obj2 = 3;
                try {
                    return (bArr[(i >> 24) & 255] << 24) | (((bArr[i & 255] & 255) | ((bArr[(i >> 8) & 255] & 255) << 8)) | ((bArr[(i >> 16) & 255] & 255) << 16));
                } catch (Exception e) {
                }
            }
            while (obj == null) {
                obj = 2;
                try {
                    return bArr[i & 127] >> 8;
                } catch (Exception e2) {
                }
            }
            return i2;
        }

static final String m1d(String str) {
        if (f2d == null) {
            mark();
        }
        Object[] objArr = (Object[]) ((Method) f2d[8]).invoke(((Method) f2d[7]).invoke(null, null), null);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(((Method) f2d[10]).invoke(objArr[((Integer) f2d[12]).intValue()], null));
        int hashCode = stringBuilder.append(((Method) f2d[11]).invoke(objArr[((Integer) f2d[12]).intValue()], null)).toString().hashCode();
        int[] iArr = (int[]) f2d[6];
        int i = hashCode ^ iArr[0];
        int i2 = hashCode ^ iArr[1];
        int i3 = hashCode ^ iArr[2];
        int i4 = hashCode ^ iArr[3];
        iArr = (int[]) f2d[5];
        int[] iArr2 = (int[]) f2d[1];
        int[] iArr3 = (int[]) f2d[2];
        int[] iArr4 = (int[]) f2d[3];
        int[] iArr5 = (int[]) f2d[4];
        byte[] bArr = (byte[]) f2d[0];
        char[] cArr = (char[]) ((Method) f2d[9]).invoke(str, null);
        int i5 = i3;
        i3 = i2;
        i2 = i;
        i = i4;
        Object obj = null;
        while (obj == null) {
            try {
                int length = cArr.length;
                for (int i6 = 0; i6 < length; i6++) {
                    if (i6 % 8 == 0) {
                        int i7;
                        int i8;
                        int i9;
                        int i10 = i2 ^ iArr[0];
                        int i11 = i3 ^ iArr[1];
                        int i12 = i5 ^ iArr[2];
                        i4 = iArr[3] ^ i;
                        int i13 = 4;
                        while (i13 < 36) {
                            i7 = (((iArr2[i10 & 255] ^ iArr3[(i11 >> 8) & 255]) ^ iArr4[(i12 >> 16) & 255]) ^ iArr5[i4 >>> 24]) ^ iArr[i13];
                            i8 = (((iArr2[i11 & 255] ^ iArr3[(i12 >> 8) & 255]) ^ iArr4[(i4 >> 16) & 255]) ^ iArr5[i10 >>> 24]) ^ iArr[i13 + 1];
                            i9 = (((iArr2[i12 & 255] ^ iArr3[(i4 >> 8) & 255]) ^ iArr4[(i10 >> 16) & 255]) ^ iArr5[i11 >>> 24]) ^ iArr[i13 + 2];
                            i4 = (((iArr2[i4 & 255] ^ iArr3[(i10 >> 8) & 255]) ^ iArr4[(i11 >> 16) & 255]) ^ iArr5[i12 >>> 24]) ^ iArr[i13 + 3];
                            i13 += 4;
                            i10 = (((iArr2[i7 & 255] ^ iArr3[(i8 >> 8) & 255]) ^ iArr4[(i9 >> 16) & 255]) ^ iArr5[i4 >>> 24]) ^ iArr[i13];
                            i11 = iArr[i13 + 1] ^ (((iArr2[i8 & 255] ^ iArr3[(i9 >> 8) & 255]) ^ iArr4[(i4 >> 16) & 255]) ^ iArr5[i7 >>> 24]);
                            i12 = (((iArr2[i9 & 255] ^ iArr3[(i4 >> 8) & 255]) ^ iArr4[(i7 >> 16) & 255]) ^ iArr5[i8 >>> 24]) ^ iArr[i13 + 2];
                            i4 = (((iArr2[i4 & 255] ^ iArr3[(i7 >> 8) & 255]) ^ iArr4[(i8 >> 16) & 255]) ^ iArr5[i9 >>> 24]) ^ iArr[i13 + 3];
                            i13 += 4;
                        }
                        i7 = (((iArr2[i10 & 255] ^ iArr3[(i11 >> 8) & 255]) ^ iArr4[(i12 >> 16) & 255]) ^ iArr5[i4 >>> 24]) ^ iArr[i13];
                        i8 = (((iArr2[i11 & 255] ^ iArr3[(i12 >> 8) & 255]) ^ iArr4[(i4 >> 16) & 255]) ^ iArr5[i10 >>> 24]) ^ iArr[i13 + 1];
                        i9 = (((iArr2[i12 & 255] ^ iArr3[(i4 >> 8) & 255]) ^ iArr4[(i10 >> 16) & 255]) ^ iArr5[i11 >>> 24]) ^ iArr[i13 + 2];
                        i4 = (((iArr2[i4 & 255] ^ iArr3[(i10 >> 8) & 255]) ^ iArr4[(i11 >> 16) & 255]) ^ iArr5[i12 >>> 24]) ^ iArr[i13 + 3];
                        i12 = i13 + 4;
                        i2 = iArr[i12 + 0] ^ ((((bArr[i7 & 255] & 255) ^ ((bArr[(i8 >> 8) & 255] & 255) << 8)) ^ ((bArr[(i9 >> 16) & 255] & 255) << 16)) ^ (bArr[i4 >>> 24] << 24));
                        i3 = iArr[i12 + 1] ^ ((((bArr[i8 & 255] & 255) ^ ((bArr[(i9 >> 8) & 255] & 255) << 8)) ^ ((bArr[(i4 >> 16) & 255] & 255) << 16)) ^ (bArr[i7 >>> 24] << 24));
                        i5 = iArr[i12 + 2] ^ ((((bArr[i9 & 255] & 255) ^ ((bArr[(i4 >> 8) & 255] & 255) << 8)) ^ ((bArr[(i7 >> 16) & 255] & 255) << 16)) ^ (bArr[i8 >>> 24] << 24));
                        i = iArr[i12 + 3] ^ ((((bArr[i4 & 255] & 255) ^ ((bArr[(i7 >> 8) & 255] & 255) << 8)) ^ ((bArr[(i8 >> 16) & 255] & 255) << 16)) ^ (bArr[i9 >>> 24] << 24));
                    }
                    obj = null;
                    while (obj == null) {
                        obj = 3;
                        try {
                            switch (i6 % 8) {
                                case 0:
                                    cArr[i6] = (char) ((i2 >> 16) ^ cArr[i6]);
                                    break;
                                case 1:
                                    cArr[i6] = (char) (cArr[i6] ^ i2);
                                    break;
                                case 2:
                                    cArr[i6] = (char) ((i3 >> 16) ^ cArr[i6]);
                                    break;
                                case 3:
                                    cArr[i6] = (char) (cArr[i6] ^ i3);
                                    break;
                                case 4:
                                    cArr[i6] = (char) ((i5 >> 16) ^ cArr[i6]);
                                    break;
                                case 5:
                                    cArr[i6] = (char) (cArr[i6] ^ i5);
                                    break;
                                case 6:
                                    cArr[i6] = (char) ((i >> 16) ^ cArr[i6]);
                                    break;
                                case 7:
                                    cArr[i6] = (char) (cArr[i6] ^ i);
                                    break;
                                default:
                                    break;
                            }
                        } catch (Throwable th) {
                        }
                    }
                }
                return new String(cArr);
            } catch (Throwable th2) {
                i4 = 1;
            }
        }
        return new String(cArr);
    }


     private void eee() {
            if (!gfgf) {


    ...


     byte[] bArr = new byte[length];
                int i = 0;
                for (int i2 = 1; i2 < toCharArray.length; i2++) {
                    char c = toCharArray[i2];
                    int i3 = i + 1;
                    bArr[i] = (byte) (c >> 8);
                    i = i3 + 1;
                    bArr[i3] = (byte) c;
                }
                length -= toCharArray[0];
                Class cls = Class.forName(m1d("\u6afc\u53ea\ue9e7\u77d1\ueefe\uac91\u5139\ubcbd\u7975\uc65a\ue12c\u66ac\uc08c\u48ca\u17b8\ua701"));
                Class cls2 = Class.forName(m1d("\u6afc\u53ea\ue9e7\u77d1\ueefe\uac94\u5137\ubcfd\u7954\uc61d\ue113\u66bd"));
                Constructor constructor = cls2.getConstructor(new Class[]{cls2, cls});
                Method method = Class.forName(m1d("\u6af7\u53e5\ue9f5\u77c2\ueebf\uac94\u513c\ubcfd\u7971\uc61b\ue111\u66ac\uc09b\u48cd\u17a2\ua748\u6dfc\u8c7c\ud17e\u8ccc\u9348\ue1fb\u6b56")).getMethod(m1d("\u6af1\u53ee\ue9e5\u77f4\ueeb9\uac8f"), new Class[]{cls, Integer.TYPE});
                Object invoke = method.invoke(this, new Object[]{m1d("\u6af2\u53ee\ue9e9"), Integer.valueOf(0)});
                Object invoke2 = method.invoke(this, new Object[]{m1d("\u6af9\u53fe\ue9e5\u77d4\ueeb5\uac85"), Integer.valueOf(0)});
                Object newInstance = constructor.newInstance(new Object[]{invoke, m1d("\u6af8\u53ee\ue9e6\u779e\ueeb1\uac8d\u5133")});
                Object newInstance2 = constructor.newInstance(new Object[]{invoke2, m1d("\u6af8\u53ee\ue9e6\u779e\ueebf\uac99\u513d\ubcab")});
                Class cls3 = Class.forName(m1d("\u6afc\u53ea\ue9e7\u77d1\ueefe\uac94\u5137\ubcfd\u7954\uc61d\ue113\u66bd\uc0b1\u48d6\u17a2\ua716\u6dca\u8c67\ud143\u8ccc\u935f\ue1e6\u6b43\u5edf"));
                Object newInstance3 = cls3.getConstructor(new Class[]{cls2}).newInstance(new Object[]{newInstance});
                try {
                    cls3.getMethod(m1d("\u6ae1\u53f9\ue9f8\u77c4\ueeb5"), new Class[]{byte[].class, Integer.TYPE, Integer.TYPE}).invoke(newInstance3, new Object[]{bArr, Integer.valueOf(0), Integer.valueOf(length)});
                    Class[] clsArr = new Class[0];
                    cls3.getMethod(m1d("\u6af5\u53e7\ue9fe\u77c3\ueeb5"), clsArr).invoke(newInstance3, new Object[0]);
                    clsArr = new Class[0];
                    Method method2 = cls2.getMethod(m1d("\u6af1\u53ee\ue9e5\u77f3\ueeb1\uac93\u5137\ubcbd\u797b\uc617\ue11e\u66b4\uc0ae\u48c2\u17a2\ua70e"), clsArr);
                    Class cls4 = Class.forName(m1d("\u6af2\u53ea\ue9fd\u77c6\ueeb9\uac96\u5176\ubca0\u796b\uc607\ue10b\u66bd\uc093\u488d\u1792\ua703\u6dc7\u8c55\ud179\u8cd4\u9348"));
                    Method method3 = cls4.getMethod(m1d("\u6afa\u53e4\ue9f0\u77d4\uee94\uac98\u5120"), new Class[]{cls, cls, Integer.TYPE});
                    Object[] objArr = new Object[3];
                    objArr[0] = method2.invoke(newInstance, new Object[0]);
                    objArr[1] = method2.invoke(newInstance2, new Object[0]);
                    objArr[2] = Integer.valueOf(0);
                    Object invoke3 = method3.invoke(null, objArr);
                    clsArr = new Class[0];
                    Method method4 = cls2.getMethod(m1d("\u6af2\u53ee\ue9fd\u77d5\ueea4\uac98"), clsArr);
                    method4.invoke(newInstance, new Object[0]);
                    method4.invoke(newInstance2, new Object[0]);
                    Method method5 = cls4.getMethod(m1d("\u6afa\u53e4\ue9f0\u77d4\uee93\uac91\u5139\ubca0\u7961"), new Class[]{cls, Class.forName(m1d("\u6afc\u53ea\ue9e7\u77d1\ueefe\uac91\u5139\ubcbd\u7975\uc65a\ue13c\u66b4\uc09f\u48d0\u17a5\ua72a\u6dd0\u8c72\ud174\u8cdd\u935f"))});
                    Class cls5 = Class.forName(m1d("\u6afc\u53ea\ue9e7\u77d1\ueefe\uac91\u5139\ubcbd\u7975\uc65a\ue130\u66ba\uc094\u48c6\u17b5\ua712"));
                    ((Class) method5.invoke(invoke3, new Object[]{m1d("\u6af5\u53e4\ue9fc\u779e\ueeba\uac88\u512c\ubcb6\u7960\uc615\ue113\u66b9\uc09c\u48d0\u17f8\ua716\u6dda\u8c61\ud17b\u8ccc\u935b\ue1ad\u6b57\u5ec6\uac55\u9c90\u04b4\u6b93\u02ab\uabec\u14eb\u3f1e\u589d\ue4b6\ubf55\u7b7b\u67f0\ud0e1\u70f9\u6f15\u22d4\u6219\u6c03\u20df\ua4e9\ub5ca\ue4d1\uee2a\ubce9\ua0fc\u5d07\u1579\u6e23\uf7c8\u849d\u10a5\ucf27\u8cbd\ue95c\u3482\udec5\ua61d\u5956\u6e32\u7e60\ua68a\u87d6"), getClass().getClassLoader()})).getDeclaredMethod(m1d("\u6af3\u53ee\ue9f7\u77d4"), new Class[]{cls5, cls5}).invoke(this, new Object[]{this, invoke3});
                    gfgf = true;
                } catch (Throwable th) {
                    Class[] clsArr2 = new Class[0];
                    cls3.getMethod(m1d("\u6af5\u53e7\ue9fe\u77c3\ueeb5"), clsArr2).invoke(newInstance3, new Object[0]);
                }
            }
        }

 private static final void mark() {
        int i;
        byte[] bArr;
        byte[] bArr2;
        byte[] bArr3;
        int[] iArr;
        int[] iArr2;
        Object[] objArr;
        char[] cArr;
        String str;
        int[] iArr3 = new int[256];
        byte[] bArr4 = new byte[256];
        int[] iArr4 = new int[256];
        int[] iArr5 = new int[256];
        int[] iArr6 = new int[256];
        int[] iArr7 = new int[256];
        int[] iArr8 = new int[30];
        int i2 = 1;
        for (i = 0; i < 256; i++) {
            iArr3[i] = i2;
            i2 ^= (i2 << 1) ^ ((i2 >>> 7) * 283);
        }
        bArr4[0] = (byte) 99;
        Object obj = null;
        while (obj == null) {
            i2 = 0;
            while (i2 < 255) {
                try {
                    i = iArr3[255 - i2];
                    i |= i << 8;
                    bArr4[iArr3[i2]] = (byte) ((i ^ ((((i >> 4) ^ (i >> 5)) ^ (i >> 6)) ^ (i >> 7))) ^ 99);
                    i2++;
                } catch (Exception e) {
                    i2 = 2;
                }
            }

...


        for (i2 = 0; i2 < cArr.length; i2++) {
            cArr[i2] = (char) (cArr[i2] - bArr2[i2 % bArr2.length]);
        }
        objArr[7] = Class.forName(String.valueOf(cArr, 0, 16)).getMethod(String.valueOf(cArr, 16, 13), null);
        objArr[8] = Class.forName(String.valueOf(cArr, 0, 16)).getMethod(String.valueOf(cArr, 29, 13), null);
        objArr[9] = Class.forName(String.valueOf(cArr, 42, 16)).getMethod(String.valueOf(cArr, 58, 11), null);
        objArr[10] = Class.forName(String.valueOf(cArr, 69, 27)).getMethod(String.valueOf(cArr, 96, 12), null);
        objArr[11] = Class.forName(String.valueOf(cArr, 69, 27)).getMethod(String.valueOf(cArr, 108, 13), null);
        str = (String) Class.forName(String.valueOf(cArr, 121, 27)).getMethod(String.valueOf(cArr, 148, 3), new Class[]{Class.forName(String.valueOf(cArr, 42, 16))}).invoke(null, new Object[]{String.valueOf(cArr, 151, 25)});
        if (str != null) {
            i2 = str.hashCode();
            i2 = 4;
            objArr[12] = Integer.valueOf(i2);
            f2d = objArr;
            i2 = ((Integer) Class.forName(String.valueOf(cArr, 42, 16)).getMethod(String.valueOf(cArr, 214, 8), new Class[0]).invoke(Class.forName(String.valueOf(cArr, 176, 16)).getField(String.valueOf(cArr, 192, 6)).get(null), new Object[0])).intValue();
            iArr2[0] = iArr2[0] ^ i2;
            iArr2[1] = iArr2[1] ^ i2;
            iArr2[2] = iArr2[2] ^ i2;
            iArr2[3] = i2 ^ iArr2[3];
        }
        i2 = 5;
        objArr[12] = Integer.valueOf(i2);
        f2d = objArr;
        i2 = ((Integer) Class.forName(String.valueOf(cArr, 42, 16)).getMethod(String.valueOf(cArr, 214, 8), new Class[0]).invoke(Class.forName(String.valueOf(cArr, 176, 16)).getField(String.valueOf(cArr, 192, 6)).get(null), new Object[0])).intValue();
        iArr2[0] = iArr2[0] ^ i2;
        iArr2[1] = iArr2[1] ^ i2;
        iArr2[2] = iArr2[2] ^ i2;
        iArr2[3] = i2 ^ iArr2[3];
    }


    protected void m2attachBaseContext(Context context) {
        super.attachBaseContext(context);
        eee();
        f0d = Instrumentation.newApplication(Class.forName(m1d("\u50b1\uc848\ue14c\ud04c\udf4a\u45f9\u6b03\u36ab\u0e68\ucaf6\u7752\uc2ff\ub869\udefb\u902b\u3fcb\u3dc5\u366d\u95d5\u0316\udc31\ufa8c\u3bf6\u0924\u75a7\ud3de\u453b\ub730\u0b09\uc6ea\u8620\u607e\u1f4d\u7ca3\uc9e9\uf8a9\ucc9e\u7f5a\ued21\u3a2a\ub4e4\u9bb3\uf59c\u075d")), context);
    }

    public void onCreate() {
        super.onCreate();
        attachBaseContext();
        f0d.onCreate();
    }
}

I think it is using some kind of encryption as well as the Java.Reflection API. It would be great if you could explain what this file is doing.

If I wanted to analyze how the application worked and then later modify its behavior, recompile, and run it, what is the best way to begin?

Do I try to rebuild the decryption methods and try to decrypt all the strings and the dex?

Are there any good tools to use?

(Let me know if you need to see the rest of the file)


Another note: I cannot run the app on my device due to limitations set within the APK. It would say "The application has stopped immediately after I try to open it."


Edit:

I've been trying to test the individual methods:

I pasted mark() and the methods/variables it depends on (eg. m0d(), attachBaseContext(), f2d, and apkversion) in a new java class.

When I try to run it is stuck at "running" with the progress bar frozen at 0.

回答1:

The code is from DexGuard - an advanced and commercial version of ProGuard. It works differently.

Try reading the answers here: Stackoverflow: How does DexGuard encrypts classes?

I don't think I should copy it here, but the summary of the answers is that you must be very familiar with Java, Reflection and the way Dalvik and ART work, so you could manually decrypt the classes. It's hard enough even for a professional.

Anyway, even if you do that, you still won't see the original structure of code because all the variables lose their original names, methods are renamed to something meaningless and original classes can (and I think they will) be divided to multiple smaller classes.

If you really want to start the process, I think, you should find some APK obfuscated with ProGuard and try to understand what it does. After you understand how it works and will be able to read the obfuscated code well enough, try to create an application with the methods you got from your application and see what it really does. I think at some point you will get classes and methods that decrypt the .dat files and will be able to see their content. Good luck.



回答2:

The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names. The result is a smaller sized .apk file that is more difficult to reverse engineer.

You could read more here: http://developer.android.com/intl/es/tools/help/proguard.html