Tencent2016A安卓靜態逆向分析
安卓靜態逆向分析——Tencent2016A
引入
本文是我在學習安卓逆向時的一些步驟、心得,逆向的是2016年騰訊遊戲安全技術競賽提供的檔案,因為檔案比較簡單僅僅從靜態分析便能找到對應的註冊碼。 所用到的工具有: IDA、CodeBlocks。
逆向步驟
首先先看下程式執行結果,如下圖所示: 這是一個輸入name和password的程式,我們輸入後會出現check failed的提示,那麼問題就簡單了,猜想:是把驗證放到了native層,下面通過靜態分析來驗證。 將apk檔案中的libCheckRegister.so拖入到ida中,開啟的圖片如下所示。 隨便在函式列表翻翻,便可發現一個奇怪的函式Java_com_tencent_tencent2016a_MainActivity_NativeCheckRegister,它的命名規則剛好符合Android Native層命名規則,之前的猜想成立,真的就是把驗證放到了native層,這個就是我們要分析的函式。 雙擊函式跳轉到相應的ARM彙編程式碼如下圖所示
bool __fastcall sub_1634(char *cname, char *cpw) { char *v2; // r6 int v3; // r5 _BOOL4 result; // r0 int v5; // r4 char *v6; // r7 int v7; // r3 int v8; // r4 int v9; // r4 int v10; // r1 char *v11; // [sp+Ch] [bp-464h] int v12; // [sp+18h] [bp-458h] int v13; // [sp+1Ch] [bp-454h] int v14; // [sp+20h] [bp-450h] int v15; // [sp+24h] [bp-44Ch] int v16; // [sp+28h] [bp-448h] char v17[4]; // [sp+2Ch] [bp-444h] int v18; // [sp+30h] [bp-440h] int v19; // [sp+34h] [bp-43Ch] int v20; // [sp+38h] [bp-438h] int v21; // [sp+3Ch] [bp-434h] char v22[20]; // [sp+40h] [bp-430h] char v23[936]; // [sp+54h] [bp-41Ch] v2 = cpw; v11 = cname; v3 = j_strlen(); if ( (unsigned int)(v3 - 6) > 0xE ) return 0; j_memset(v22, 0, 20); v5 = 0; do { v6 = &v22[v5]; v7 = (unsigned __int8)v11[v5 % v3] * (v5 + 20160126) * v3; ++v5; *(_DWORD *)v6 += v7; } while ( v5 != 16 ); j_memset(v23, 0, 1024); if ( sub_146C(v2) > 1024 ) return 0; v8 = sub_1498(v23, v2); if ( v8 != 20 ) return 0; j_memset(&v12, 0, 20); j_memset(v17, 0, 20); v9 = 0; do { v10 = *(_DWORD *)&v23[v9]; *(int *)((char *)&v12 + v9) = *(_DWORD *)&v22[v9] / 10; *(_DWORD *)&v17[v9] = v10; v9 += 4; } while ( v9 != 20 ); result = 0; if ( v21 + v12 == v19 && v21 + v12 + v13 == 2 * v21 && v14 + v20 == *(_DWORD *)v17 && v14 + v20 + v15 == 2 * v20 ) result = (unsigned int)(v16 + v18 - 3 * v14) <= 0; return result; }
從(cname_length - 6) > 0xE 中可以看出名字的長度需要在[6, 20]間,將函式變數稍微處理處理得到以下程式碼:
bool __fastcall sub_1634(char *cname, char *cpw) { char *pw_copy; // r6 signed int cname_length; // r5 _BOOL4 result; // r0 int i; // r4 char *tmp; // r7 int v7; // r3 int pw_encode_length; // r4 int j; // r4 int *temp; // r1 char *name_copy; // [sp+Ch] [bp-464h] int *v12; // [sp+18h] [bp-458h] int v13; // [sp+1Ch] [bp-454h] int v14; // [sp+20h] [bp-450h] int v15; // [sp+24h] [bp-44Ch] int v16; // [sp+28h] [bp-448h] int *v17; // [sp+2Ch] [bp-444h] int v18; // [sp+30h] [bp-440h] int v19; // [sp+34h] [bp-43Ch] int v20; // [sp+38h] [bp-438h] int v21; // [sp+3Ch] [bp-434h] char name_encode[20]; // [sp+40h] [bp-430h] char pw_encode[936]; // [sp+54h] [bp-41Ch] pw_copy = cpw; name_copy = cname; cname_length = j_strlen(cname); if ( (cname_length - 6) > 0xE ) return 0; j_memset(name_encode, 0, 20); i = 0; do { tmp = &name_encode[i]; v7 = name_copy[i % cname_length] * (i + 20160126) * cname_length; ++i; *tmp += v7; } while ( i != 16 ); j_memset(pw_encode, 0, 1024); if ( sub_146C(pw_copy) > 1024 ) return 0; pw_encode_length = sub_1498(pw_encode, pw_copy); if ( pw_encode_length != 20 ) return 0; j_memset(&v12, 0, 20); j_memset(&v17, 0, 20); j = 0; do { temp = *&pw_encode[j * 4]; (&v12)[j] = (*&name_encode[j * 4] / 10); (&v17)[j] = temp; ++j; } while ( j != 5 ); result = False; if ( (v12 + v21) == v19 && (v12 + v21 + v13) == (2 * v21) && (v14 + v20) == v17 && v14 + v20 + v15 == 2 * v20 ) result = (v16 + v18 - 3 * v14) <= 0; return result;
那麼問題就變得簡單了,知道name怎麼轉化成name_encode,通過最後的if判斷將name_endoce轉化成pw_encode再將pw_encode轉化成pw就行了。前面的轉化通過程式碼可簡單實現,最後一步需關注sub_146C(pw_copy)和sub_1498(pw_encode, pw_copy)函式,在程式碼中可以很清楚的看到載入一個表, 因此在逆向時需要將這個表寫入到程式碼中,如下圖所示。
通過簡單的列印輸出便可找到相應的對應關係。如下圖所示。 (因時間有限,我寫的C++逆向程式碼還有bug就不貼了,下週補上。未完待續…)