什么方法被用来保护这个Android APK:反射?加密?如何对其进行逆向工程和分析



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


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

大部分应用程序隐藏在下

assets>classes.dex.dat

我找到的唯一一个java文件是

com>…>util>ProtectedUtils.java

我在下面有ProtectedUtils.java:如果有人对感兴趣,链接到完整文件

   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("u50b3uc849ue145ud010udf4fu45e5u6b13u36e0u0e7bucae7u774euc2b0ub84audeebu9071u3fd2u3dd6u3676u95cau031budc13ufacau3bf1u0935u75afud3d6"));
                Class[] clsArr = new Class[0];
                Object invoke = cls.getMethod(m1d("u50b1uc852ue153ud010udf45u45e2u6b03u368fu0e79ucae3u7757uc2e8ub862udefcu907cu3fefu3dc8u366du95dbu0303udc23"), clsArr).invoke(null, new Object[0]);
                Field declaredField = cls.getDeclaredField(m1d("u50bfuc866ue14dud00eudf61u45fcu6b07u36a2u0e73ucaf4u775fuc2eaub862udee7u906bu3fc8"));
                declaredField.setAccessible(true);
                ((List) declaredField.get(invoke)).add(0, f0d);
                Field declaredField2 = cls.getDeclaredField(m1d("u50bfuc86eue14fud00budf54u45e5u6b16u36a2u0e5bucae7u774euc2f2ub862udeebu9064u3fcfu3dc9u3670u95d0"));
                declaredField2.setAccessible(true);
                declaredField2.set(invoke, f0d);
                Field declaredField3 = cls.getDeclaredField(m1d("u50bfuc865ue14eud017udf4eu45e8u6b36u36beu0e6aucafbu7757uc2fdub86audefcu906cu3fd4u3dce"));
                declaredField3.setAccessible(true);
                Object obj = declaredField3.get(invoke);
                Field declaredField4 = obj.getClass().getDeclaredField(m1d("u50bbuc849ue147ud00d"));
                declaredField4.setAccessible(true);
                Object obj2 = declaredField4.get(obj);
                Field declaredField5 = obj2.getClass().getDeclaredField(m1d("u50bfuc866ue151ud012udf4cu45e5u6b14u36afu0e6eucafeu7751uc2f0"));
                declaredField5.setAccessible(true);
                declaredField5.set(obj2, f0d);
                Context baseContext = f0d.getBaseContext();
                Field declaredField6 = baseContext.getClass().getDeclaredField(m1d("u50bfuc868ue154ud016udf45u45feu6b34u36a1u0e74ucae3u775buc2e6ub87f"));
                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("u6afcu53eaue9e7u77d1ueefeuac91u5139ubcbdu7975uc65aue12cu66acuc08cu48cau17b8ua701"));
                Class cls2 = Class.forName(m1d("u6afcu53eaue9e7u77d1ueefeuac94u5137ubcfdu7954uc61due113u66bd"));
                Constructor constructor = cls2.getConstructor(new Class[]{cls2, cls});
                Method method = Class.forName(m1d("u6af7u53e5ue9f5u77c2ueebfuac94u513cubcfdu7971uc61bue111u66acuc09bu48cdu17a2ua748u6dfcu8c7cud17eu8cccu9348ue1fbu6b56")).getMethod(m1d("u6af1u53eeue9e5u77f4ueeb9uac8f"), new Class[]{cls, Integer.TYPE});
                Object invoke = method.invoke(this, new Object[]{m1d("u6af2u53eeue9e9"), Integer.valueOf(0)});
                Object invoke2 = method.invoke(this, new Object[]{m1d("u6af9u53feue9e5u77d4ueeb5uac85"), Integer.valueOf(0)});
                Object newInstance = constructor.newInstance(new Object[]{invoke, m1d("u6af8u53eeue9e6u779eueeb1uac8du5133")});
                Object newInstance2 = constructor.newInstance(new Object[]{invoke2, m1d("u6af8u53eeue9e6u779eueebfuac99u513dubcab")});
                Class cls3 = Class.forName(m1d("u6afcu53eaue9e7u77d1ueefeuac94u5137ubcfdu7954uc61due113u66bduc0b1u48d6u17a2ua716u6dcau8c67ud143u8cccu935fue1e6u6b43u5edf"));
                Object newInstance3 = cls3.getConstructor(new Class[]{cls2}).newInstance(new Object[]{newInstance});
                try {
                    cls3.getMethod(m1d("u6ae1u53f9ue9f8u77c4ueeb5"), 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("u6af5u53e7ue9feu77c3ueeb5"), clsArr).invoke(newInstance3, new Object[0]);
                    clsArr = new Class[0];
                    Method method2 = cls2.getMethod(m1d("u6af1u53eeue9e5u77f3ueeb1uac93u5137ubcbdu797buc617ue11eu66b4uc0aeu48c2u17a2ua70e"), clsArr);
                    Class cls4 = Class.forName(m1d("u6af2u53eaue9fdu77c6ueeb9uac96u5176ubca0u796buc607ue10bu66bduc093u488du1792ua703u6dc7u8c55ud179u8cd4u9348"));
                    Method method3 = cls4.getMethod(m1d("u6afau53e4ue9f0u77d4uee94uac98u5120"), 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("u6af2u53eeue9fdu77d5ueea4uac98"), clsArr);
                    method4.invoke(newInstance, new Object[0]);
                    method4.invoke(newInstance2, new Object[0]);
                    Method method5 = cls4.getMethod(m1d("u6afau53e4ue9f0u77d4uee93uac91u5139ubca0u7961"), new Class[]{cls, Class.forName(m1d("u6afcu53eaue9e7u77d1ueefeuac91u5139ubcbdu7975uc65aue13cu66b4uc09fu48d0u17a5ua72au6dd0u8c72ud174u8cddu935f"))});
                    Class cls5 = Class.forName(m1d("u6afcu53eaue9e7u77d1ueefeuac91u5139ubcbdu7975uc65aue130u66bauc094u48c6u17b5ua712"));
                    ((Class) method5.invoke(invoke3, new Object[]{m1d("u6af5u53e4ue9fcu779eueebauac88u512cubcb6u7960uc615ue113u66b9uc09cu48d0u17f8ua716u6ddau8c61ud17bu8cccu935bue1adu6b57u5ec6uac55u9c90u04b4u6b93u02abuabecu14ebu3f1eu589due4b6ubf55u7b7bu67f0ud0e1u70f9u6f15u22d4u6219u6c03u20dfua4e9ub5caue4d1uee2aubce9ua0fcu5d07u1579u6e23uf7c8u849du10a5ucf27u8cbdue95cu3482udec5ua61du5956u6e32u7e60ua68au87d6"), getClass().getClassLoader()})).getDeclaredMethod(m1d("u6af3u53eeue9f7u77d4"), new Class[]{cls5, cls5}).invoke(this, new Object[]{this, invoke3});
                    gfgf = true;
                } catch (Throwable th) {
                    Class[] clsArr2 = new Class[0];
                    cls3.getMethod(m1d("u6af5u53e7ue9feu77c3ueeb5"), 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("u50b1uc848ue14cud04cudf4au45f9u6b03u36abu0e68ucaf6u7752uc2ffub869udefbu902bu3fcbu3dc5u366du95d5u0316udc31ufa8cu3bf6u0924u75a7ud3deu453bub730u0b09uc6eau8620u607eu1f4du7ca3uc9e9uf8a9ucc9eu7f5aued21u3a2aub4e4u9bb3uf59cu075d")), context);
    }
    public void onCreate() {
        super.onCreate();
        attachBaseContext();
        f0d.onCreate();
    }
}

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

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

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

有什么好工具可以用吗?

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


另一个注意事项:由于APK中设置的限制,我无法在设备上运行该应用程序。它会说"应用程序在我尝试打开后立即停止。"


编辑:

我一直在尝试测试单独的方法:

我将mark()和它所依赖的方法/变量(例如m0d()attachBaseContext()f2dapkversion)粘贴到一个新的java类中。

当我尝试运行时,它被困在"正在运行",进度条冻结在0。

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

试着在这里阅读答案:堆栈溢出:DexGuard如何加密类?

我不认为我应该在这里复制它,但答案的总结是,你必须非常熟悉Java、Reflection以及Dalvik和ART的工作方式,所以你可以手动解密类。这对一个专业人士来说已经够难的了。

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

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

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

您可以在此处阅读更多信息:http://developer.android.com/intl/es/tools/help/proguard.html

最新更新