正在使用什么方法来保护此 Android APK:Reflection?加密?我如何对其进行逆向工程和分析?

What methods are being used to protect this Android APK: Reflection? Encryption? How do I reverse engineer it and analyze it?

我知道 java 编程的基础知识,但我是逆向工程 APK 的新手,所以解释会很好!


我有一个 APK 文件,但没有 Java 源。在线反编译APK后:

大部分应用程序隐藏在

assets > classes.dex.dat

我找到的唯一 java 文件是

com > ... > util > ProtectedUtils.java

我下面有ProtectedUtils.java: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();
    }
}

我认为它使用了某种加密方式以及 Java.Reflection API。如果你能解释一下这个文件的作用就太好了。

如果我想分析应用程序的工作方式,然后修改其行为、重新编译并运行,最好的开始方式是什么?

我是否尝试重建解密方法并尝试解密所有字符串和 dex?

有没有什么好用的工具?

(如果您需要查看文件的其余部分,请告诉我)


另请注意:由于 APK 中设置的限制,我无法 运行 我的设备上的应用程序。它会说 "The application has stopped immediately after I try to open it."


编辑:

我一直在尝试测试各个方法:

我粘贴了 mark() 和它所依赖的 methods/variables(例如 m0d()attachBaseContext()f2dapkversion)在一个新的 java class.

当我尝试 运行 时,它卡在 "running",进度条冻结在 0。

ProGuard 工具通过删除未使用的代码并使用语义模糊的名称重命名 类、字段和方法来缩小、优化和混淆您的代码。结果是一个更小的 .apk 文件更难逆向工程。

您可以在这里阅读更多内容:http://developer.android.com/intl/es/tools/help/proguard.html

代码来自 DexGuard - ProGuard 的高级商业版本。它的工作方式不同。

尝试阅读此处的答案:Whosebug: How does DexGuard encrypts classes?

我认为我不应该在这里复制它,但答案的总结是你必须非常熟悉Java,反射和Dalvik和ART的工作方式,所以你可以手动解密类。即使是专业人士也很难。

无论如何,即使你这样做了,你仍然看不到原来的代码结构,因为所有的变量都失去了原来的名字,方法被重命名为无意义的东西,原来 类 可以(而且我认为他们会)被分成多个更小的类。

如果你真的想开始这个过程,我认为你应该找到一些混淆了 ProGuard 的 APK 并尝试了解它的作用。在您了解它的工作原理并且能够很好地阅读混淆代码后,尝试使用您从应用程序中获得的方法创建一个应用程序,看看它到底做了什么。我认为在某些时候您将获得 类 和解密 .dat 文件的方法,并且能够看到它们的内容。祝你好运。