1. 程式人生 > >Tencent2016A安卓靜態逆向分析

Tencent2016A安卓靜態逆向分析

安卓靜態逆向分析——Tencent2016A

引入

本文是我在學習安卓逆向時的一些步驟、心得,逆向的是2016年騰訊遊戲安全技術競賽提供的檔案,因為檔案比較簡單僅僅從靜態分析便能找到對應的註冊碼。 所用到的工具有: IDA、CodeBlocks。

逆向步驟

首先先看下程式執行結果,如下圖所示: 在這裡插入圖片描述 這是一個輸入name和password的程式,我們輸入後會出現check failed的提示,那麼問題就簡單了,猜想:是把驗證放到了native層,下面通過靜態分析來驗證。 將apk檔案中的libCheckRegister.so拖入到ida中,開啟的圖片如下所示。 在這裡插入圖片描述隨便在函式列表翻翻,便可發現一個奇怪的函式Java_com_tencent_tencent2016a_MainActivity_NativeCheckRegister,它的命名規則剛好符合Android Native層命名規則,之前的猜想成立,真的就是把驗證放到了native層,這個就是我們要分析的函式。 雙擊函式跳轉到相應的ARM彙編程式碼如下圖所示 在這裡插入圖片描述

按F5反編譯得到對應C程式碼,如下圖所示。 在這裡插入圖片描述發現傳進去的引數有點不太對,第一個應該是JNIEnv,第二個是jobject或者jclass,因此我們在Structures檢視中載入並修改下資料型別,修改後結果如下圖所示: 在這裡插入圖片描述然後返回到C程式碼中在相應引數上按Y修改引數型別,可以發現下面函式中對應的函式名已經找到,修改四個引數型別以及名字以便閱讀,如下圖所示: 在這裡插入圖片描述 很顯然,sub_1634(v7, v8)這個函式就是呼叫驗證的函式, 點進去。分析這個函式,顯然返回0為失敗,返回1為成功,返回結果放在result變數中。

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就不貼了,下週補上。未完待續…)