對某APP的逆向之旅(1)

最近在研究遊戲輔助相關的技術,因此有了下面的文章。
前面的準備工作,再此就不多介紹了,直接進入主題。
java層代碼如下:

    static {
        System.loadLibrary("encode");
    }

    public static int a(Context arg4) {
        if(Thread.currentThread().getName().equals("main")) {
            throw new IllegalStateException("runRootCommand can\'t run on main thread.");
        }

        int v0 = -1;
        try {
            String v1 = String.valueOf(arg4.getFilesDir().getAbsolutePath()) + "/gearcorez";
            String v2 = System.getProperty("os.arch");
            if(v2 != null && !v2.contains("arm")) {
                return v0;
            }

            RootManager.a(arg4, "gearcore", v1);
            RootManager.a(arg4, "speedman", arg4.getFilesDir() + "/libspeedman.so");
            String v0_2 = String.valueOf(arg4.getFilesDir().getAbsolutePath()) + "/gearcore";
            RootManager.decode(arg4.getApplicationContext(), v1, v0_2);
            v0 = RootManager.a(arg4.getApplicationContext(), v0_2);
            ag.a("suc=" + v0);
        }
        catch(IOException v0_1) {
            IOException v1_1 = v0_1;
            v0 = -2;
            v1_1.printStackTrace();
        }

        arg4.deleteFile("gearcore");
        return v0;
    }

可以看到加載了encode動態庫,並在函數a(Context arg4)中進行處理。過程爲首先通過RootManager.a(, ,)函數進行文件拷貝,然後通過RootManager.decode函數進行解密,最後通過函數RootManager.a(,)運行解密後的文件。
其中函數RootManager.a(, ,)如下:

private static void a(Context arg5, String arg6, String arg7) {
        int v3;
        BufferedInputStream v1 = new BufferedInputStream(arg5.getAssets().open(arg6));
        FileOutputStream v2 = new FileOutputStream(arg7);
        byte[] v0 = new byte[4096];
        try {
            while(true) {
            label_6:
                v3 = v1.read(v0);
                if(v3 != -1) {
                    goto label_14;
                }

                v2.flush();
                break;
            }
        }
        catch(Throwable v0_1) {
            goto label_17;
        }

        v2.close();
        v1.close();
        return;
        try {
        label_14:
            v2.write(v0, 0, v3);
            goto label_6;
        }
        catch(Throwable v0_1) {
        label_17:
            v2.close();
            v1.close();
            throw v0_1;
        }
    }

將assets中文件拷貝到指定目錄中,函數比較簡單。
本節,我們主要來分析下解密函數
IDA載入encode動態庫文件,來到decode函數處
這裏寫圖片描述

F5,其中參數名和變量名,是我修改過的,這個根據個人習慣,分析時修改即可。
這裏寫圖片描述

獲取包名
這裏寫圖片描述

然後,通過一個算法解密字符串,算法如下:
這裏寫圖片描述

然後,將解密後的字符傳和包名比較,根據比較結果,進行文件的解密操作。
這裏寫圖片描述

最後,附上算法還原後的代碼(我也寫到了so文件中),如下:

static unsigned char key[48] = { 0x70,0,0xc8,0,0x74,0,0x6a,0,0x32,0,
                                 0x78,0,0x68,0,0x7b,0,0x77,0,0x78,0,
                                 0x76,0,0x7e,0,0x3a,0,0x74,0,0x6f,0,
                                 0x7c,0,0x75,0,0x84,0,0x82,0,0x78,0,
                                 0x79,0,0x79,0,0x16,0,0};

JNIEXPORT void JNICALL Java_example_com_applications_MainActivity_decode(JNIEnv *env, jobject obj, jobject context, jstring encodePath, jstring decodePath)
{
    char temp[48];
    char decodeKey[24];
    //Context的類
    jclass context_clazz = env->GetObjectClass(context);
    // 得到 getPackageName 方法的 ID
    jmethodID methodId_package = env->GetMethodID(context_clazz, "getPackageName", "()Ljava/lang/String;");
    if (methodId_package != NULL)
    {
        // 獲得當前應用的包名
        jstring appPackageName = (jstring)env->CallObjectMethod(context, methodId_package);
        const char* strPackage = env->GetStringUTFChars(appPackageName, 0);
        LOGD("package name: %s", strPackage);
        memcpy(temp, key, 0x2e);

        int value = -1, index = 0, salt = 0;
        for (int i = 0, j = 1; j < 23; ++i, ++j, ++value)
        {
            salt = 0;
            index = i;
            if (i > 0)
            {
                if (i == 1)
                    continue;
                index = value;
                salt = value;
            }

            decodeKey[index] = temp[2 * i] - 1 - salt;
        }

        char* pMem = NULL;
        pMem = (char*)malloc(0x16);
        snprintf(pMem, 0x16, "%s", decodeKey);
        LOGD("decodeKey: %s", decodeKey);

        //簡單的保護工作
        if (!strcmp(strPackage, pMem))
        {
            const char* strEncodePath = env->GetStringUTFChars(encodePath, 0);
            const char* strDecodePath = env->GetStringUTFChars(decodePath, 0);
            FILE *fpEncode = fopen(strEncodePath, "rb");
            if (fpEncode)
            {
                FILE *fpDecode = fopen(strDecodePath, "wb");
                if (fpDecode)
                {
                    fseek(fpEncode, 0, 0);
                    uint8_t nextByte;
                    fread(&nextByte, 1, 1, fpEncode);
                    uint8_t xorKey = nextByte;
                    uint8_t decodeByte;

                    while (fread(&nextByte, 1, 1, fpEncode))
                    {
                        decodeByte = ~((nextByte - 30) ^ xorKey) & 0xFF;
                        nextByte = decodeByte;
                        fputc(decodeByte, fpDecode);
                    }

                    fclose(fpDecode);
                    chmod(strDecodePath, 0x1ED);
                }
                fclose(fpEncode);
            }
            remove(strEncodePath);
            env->ReleaseStringUTFChars(encodePath, strEncodePath);
            env->ReleaseStringUTFChars(decodePath, strDecodePath);
            env->ReleaseStringUTFChars(appPackageName, strPackage);
            free(pMem);
        }
        else 
        {
            env->ReleaseStringUTFChars(appPackageName, strPackage);
            free(pMem);
        }
    }
}

其中上面解密後的字符串爲
這裏寫圖片描述

分析過程比較簡單,僅供娛樂!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章