1. 程式人生 > >Android逆向學習筆記---逆向騰訊2016遊戲安全挑戰賽Tencent2016A.apk

Android逆向學習筆記---逆向騰訊2016遊戲安全挑戰賽Tencent2016A.apk

首先,用jeb將apk逆向,發現程式是通過NativeCheckRegister(String arg1)函式來判斷輸入
JEB逆向
進一步看這個函式:
native定義:

呼叫本地native 函式,而這類函式一般存在apk的lib目錄裡面的.so檔案當中。
使用IDA6.6 匯出so檔案找到NativeCheckRegister(String arg1)函式

這裡寫圖片描述
看方框裡面的sub_1634()函式
這裡寫圖片描述
這個程式的功能是根據傳入的Name字串,然後生成一個Code,與輸入的code 對比。
這裡寫圖片描述
首先 ,分析上面一段演算法。轉換成Java程式碼:

for (; i < 16; i++, j = i % name.length) {
     nameCrypt[i] = ((name[j] * (20160126
+ i) * name.length) + tmp) & 0x0ff; tmp = (((name[j] * (20160126 + i) * name.length) + tmp) >> 8) & 0x0ffffffff; }
這裡主要功能是把輸入的name進行一個加密轉換;

這裡有幾點值得注意的v11 是char型別,佔用1個位元組,所以後面要和 0x0ff相與 保留後兩個位元組,v6強制型別轉換成dword型別,佔用8個位元組,因此結果和0x0ffffffff相與。
再看這個:
這裡寫圖片描述

for (i = 0; i < 5; i++) {
            nameCrypt2[i] = nameCrypt[i * 4
+ 3]; nameCrypt2[i] = nameCrypt2[i] << 8 | nameCrypt[i * 4 + 2]; nameCrypt2[i] = nameCrypt2[i] << 8 | nameCrypt[i * 4 + 1]; nameCrypt2[i] = nameCrypt2[i] << 8 | nameCrypt[i * 4]; nameCrypt2[i] = nameCrypt2[i] / 10; }
進一步對轉換過的nameCrypt轉換(有點繞)

同道理也是位元組位運算,V10是int型別,V15轉換成dword型別,所以先算出來的位元組往左移8位作高位位元組,後八位作低位。

然後這邊是sub_1498()函式,這個函式主要將輸入的Code字串Code進行驗證:

這裡寫圖片描述
這裡寫圖片描述
根據上面的虛擬碼,用java程式碼表示:

public String getCode() {
        StringBuilder str = new StringBuilder();

        codeCrypt[3] = nameCrypt2[2] + nameCrypt2[3];
        codeCrypt[0] = codeCrypt[3] + nameCrypt2[2];

        codeCrypt[1] = 3 * nameCrypt2[2] - nameCrypt2[4];

        codeCrypt[4] = nameCrypt2[0] + nameCrypt2[1];
        codeCrypt[2] = codeCrypt[4] + nameCrypt2[0];


        for (int i = 0; i < 5; i++) {
            codeCrypt2[i * 4    ] = (byte) (codeCrypt[i] & 0x0ff);
            codeCrypt2[i * 4 + 1] = (byte) ((codeCrypt[i] >> 8) & 0x0ff);
            codeCrypt2[i * 4 + 2] = (byte) ((codeCrypt[i] >> 16) & 0x0ff);
            codeCrypt2[i * 4 + 3] = (byte) ((codeCrypt[i] >> 24) & 0x0ff);
        }

        for (int i = 0; i <= 6; i++) {
            byte tmp = 0;

            if (i == 6) {
                tmp = (byte) ((codeCrypt2[i * 3] & 0x0ff) >> 2);
                str.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2[i * 3] & 0x03) << 4) | ((codeCrypt2[i * 3 + 1] >> 4)) & 0x0f);
                str.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2[i * 3 + 1] & 0xf) << 2) | ((codeCrypt2[i * 3 + 2] & 0x0ff) >> 6));
                str.append((char) lookupTableRev(tmp));
            } else {
                tmp = (byte) ((codeCrypt2[i * 3] & 0x0ff) >> 2);
                str.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2[i * 3] & 0x03) << 4) | ((codeCrypt2[i * 3 + 1] >> 4)) & 0x0f);
                str.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2[i * 3 + 1] & 0xf) << 2) | ((codeCrypt2[i * 3 + 2] & 0x0ff) >> 6));
                str.append((char) lookupTableRev(tmp));
                tmp = (byte) (codeCrypt2[i * 3 + 2] & 0x3f);
                str.append((char) lookupTableRev(tmp));
            }
        }
        return str.toString();
    }

這裡寫圖片描述

上面的a456789[] 在彙編下是這麼一個字串,

這裡寫圖片描述
轉換成java程式碼:

private int lookupTableRev(byte content) {
        for (int i = 0; i < 256; i++) {
            if (table[i] == content) {
                return i;
            } else {
                continue;
            }
        }
        return -1;
    }
查表,使經過轉換的code根據其在表中的位置得到0xff內的可見字元。

結果有:
這裡寫圖片描述

總結一下程式碼的驗證流程:

使用者輸入的name和code;
首先對輸入的name進行一系列加密處理,然後根據加密後的nameCrypt,生成codeCrypt加密後的code,最後根據表得到相應字串值,然後輸出比較。