安全公司-* * * *-面試題:_ 安卓逆向分析分享


1 總覽:

a)     查看清單文件AndroidManifest注冊的各種信息(入口類、權限、廣播、服務等)

l  發現只有入口類信息:

Ø  com.test.pac.demo.MainActivity

b)   安裝程序查看大致行為

l  輸入UserName(長度為16

l  PassWord 可以輸入,也可以點擊Generate 生成

l  點擊Check按鈕驗證PassWord是否正確

 

確認目標:分析PassWord生成算法並寫出該算法程序,最后驗證逆向的算法是否正確。

2 逆向過程:

2.1 配置.so 調試環境

       為了更好的觀察數據變化,需要配置.so調試環境

l  配置動態分析so 文件的環境

手機沒有root,額嘗試了也root不了,提示沒有匹配機型,動態調試so文件失敗。。。。。。

嗯,浪費了很多時間被多篇博客誤導了;原來可以直接使用IDA 直接附加夜神模擬器進程動態調試so文件,配置參考:

https://cloud.tencent.com/developer/article/1357098;但是其中附加方法我變了一下,原文在我機器上行不通;效果如下圖

clip_image002

 

2.2 分析關鍵點

使用反編譯工具Jadx 查看反編譯代碼,確定關鍵調用:

clip_image004

分析:

1>  首先根據長度信息和控件之間的對應關系,可以確定f7997xUserName編輯框控件、f7996wPassWord編輯框控件

2>  進一步可以確定getck() 是關鍵校驗算法getbt() 關鍵密碼生成算法

2.3 密碼校驗函數getck()

l  查看該函數信息

clip_image006

分析:

       可以看到getck()是一個native 層的函數。PrtUtil.m7581a() 對 輸入的PassWord 進行了數據轉換:從左到右每兩個字符轉換成一個16進制數值,再強轉此數值為一個字符。例如: “31323368656c6c6f》“123hello

       clip_image008

 

l  進入so 文件后,發現getck() 函數有4個參數,但是實際是2個參數,,然后就有點小懵(以前還沒有分析過)

clip_image010

l  重新設定a1 參數類型為JNIEnv* 類型,就清晰很多了

clip_image012

再通過查閱網上資料,發現a2 代表返回類型,a3 a4 分別是傳入的參數。

 

l  繼續分析 getck()

a)      SSE向量運算-轉換UserName為其Ascii

clip_image014

分析:

Ø  進入getck() 函數之后,該函數對UserName 通過一系列SSE2向量運算計算出一個數據(供后面計算摘要使用);經過分析,這里應該是優化后的代碼實際只是求UserName串的Ascii

例如

 “321cba” Ascii:"333231636261"

--> 16進制串如下:

"\x33\x33\x33\x32\x33\x31\x36\x33\x36\x32\x36\x31"

                            所以此處的C代碼暫定為:

                            unsignedchar * GetAsAs(char * Dest,intLen)

{

                    unsignedchar * Ret = (unsignedchar *)malloc(Len * 2);

                    memset(Ret, 0, Len * 2);

                    for (int i = 0; i < Len*2; i+=2)

                    {

                        unsignedchar Tmp = Dest[i/2]>>4& 0x0f|0x30;

                        Ret[i] = Tmp;

                        Ret[i + 1] = Dest[i/2]& 0x0f | 0x30;

                    }

                    return Ret;

}

b)     HMAC_sha256 簽名

clip_image016

分析:

Ø  然后,將前面計算的數據(UserNameAscii串的HMAC 摘要簽名;並將簽名信息作為參數(參數還有用戶名、常見字符表、密碼起始地址+4、密碼起始地址)作為參數傳入sub_34CC0()》這里我重命名KeyFunc_KKKKK()函數進一步計算。

 

l  詳細分析sub_34cc0() / KeyFunc_KKKKK() 函數
clip_image018

分析:

Ø  可以看到;此函數只是一些OpenSSL函數的調用;簡述流程就是創建Ctx對象,使用AES_256_gcm引擎;以UserName作為IV初始向量、前面計算的HMACResult簽名作為密鑰,解密出密文。(Aes_256_gcm 算法的的參考鏈接: https://www.cnblogs.com/0616--ataozhijia/p/11271433.html)

 

l  最后以EVP_DecryptFinal_ex() 返回值作為這次getck() 函數成功與否的標識

clip_image020

分析:

Ø  EVP_DecryptFinal_ex() 成功就返回一個正數,不然就標識着失敗;這也驗證了反編譯出的源碼中的調用返回結果的判別getck()>0 == { success;};

clip_image021

2.4 密碼生成函數getbt()

l  查看該函數信息:

clip_image023

分析:

Ø  可以看到getbt() 也是一個native層的函數;而PrtUtil.m7580a()是將目標字符串轉換成其Ascii16進制格式字符串,例如“123hello》“31323368656c6c6f”。特殊情況:如果字符Ascii16進制<0x10,就會填充一個0;例如:字符Ascii16進制為0x8  -->  08

clip_image025

 

 

l  getbt()函數中使用到的全局168240處的64Bytes數據分析

看到getbt() 一開始就使用了一個全局變量;一全局的8qword = 64Bytes 的值,其起始地址168240,就分析了一下,如下:

clip_image027

clip_image029

              分析:

Ø  8qword = 64 Bytes的值是由一開始隨機產生32 Bytes,還經過了一些計算(3HMAC計算,后面有分析)來填充到這64Bytes的區域的。在JNI_Onload 的時候會調用這個函數。一旦程序加載成功JNI_Onload調用之后;在程序運行過程中,這64Bytes 的數值就是穩定的。(暫時認為這個時候每次程序運行都是不相等的,所以想要編寫注冊機的話,需要每次動態讀取這一塊內存,加入密碼計算過程)。(也可以使用調試查看數據,但畢竟不適合非專業人員使用)

Ø  這時候注冊機應該會涉及到Android的跨進程內存讀寫:

(參考:https://blog.csdn.net/mldxs/article/details/14486827)

 

Ø  進一步分析根據32Byres隨機數據填充該168240位置64Bytes的算法

clip_image031

分析:

n  使用了3HMAC 簽名算法,計算出的簽名值,用來填充168240開始的64字節區域。

l  接着計算通過167004位置的cipher表映射+計算出的特征值

clip_image033

分析:

Ø  結合后面的代碼,這里的HIWORD(V45) = w_FeatureDigit就是密碼的偏移2處的2Byte(這里w_FeatureDigit是重命名的),查看了 上下文,這個特征值在后面HMACMD計算、密碼的數據都用到了;所以這塊隨機數據處理后的值,目前看來不能缺少。

l  轉換UserName為其Ascii

(前面驗證函數有詳細分析,這里就不贅述了)

clip_image035

l  計算HMAC SHA256簽名

clip_image037

分析:

Ø  這里面HMAC就使用到了 前面計算了那64Bytes 的特征值(WORD)

l  計算UserNameAscii全局映射表中計算出的特征

clip_image039

l  創建一份常見字符串文本(從后面代碼看,是用來作為AAD數據

clip_image041

l  調用過程函數35210() 加密

clip_image043

分析:

Ø  加密了UserNameAscii串經過Map_167004計算的特征值的密文2Byte並傳出;然后將16Byte tag值也傳出。

 

l  最后組合密碼

clip_image045

分析:

Ø  根據剛才傳出的區域EncryptRegion,組合v45為起始的密碼區域。

3 總結歸納編寫注冊機

3.1 總結密碼的構成分析

              clip_image047

       解釋

       該密碼 由3部分組成:

Ø  第一部分:是由UserNameAscii 串經過 全局的映射表(167004h)計算后的的2Bytes 特征值,使用Aes_256_gcm 加密后的密文2Byte值。

 

Ø  第二部分:原始數據是JNI_Onload里面調用函數生成32Bytes的隨機值復制兩份,然后經過3HMAC運算結果填充到168240h處的64Byte;經167004h處的映射表計算,得出的2Byte值。

 

Ø  第三部分:是經過AES_256_gcm 加密算法后的16Byte Tag

3.2 總結需讀進程內存的數據

Ø  全局64Bytes數據(168240h起始的)

解釋:此區域數據,每次程序啟動調用JNI_Onload函數后,會調用里面的

35520()函數產生隨機32Bytes數據,然后調用35330()函數將這32Bytes的的數據進行3HMAC_SHA256簽名計算。將各層MD簽名值賦值到168240h起始的64Bytes

 

Ø  全局CipherTable (167004h 起始的,也可以手工從鏡像中提取,但是都到這一步了,順道就提取了)

解釋:此表在注冊機中,在計算中提供了一種映射關系;每次程序啟動

調用JNI_Onload函數的會在168240h起始的64Bytes位置產生新的數據,而映射表,在計算中會因為不同的Bytes值,得到不同的特征值,供后面的HMAC簽名運算和密碼生成運算。

3.3 注冊機編寫思路

(1) 通過進程讀取內存數據(168240h起始的64bytes,也可順帶把那個全局映射表也取出來)

       https://blog.csdn.net/mldxs/article/details/14486827

       步驟:

a)      通過獲取libnative-lib.so 所在模塊的數據區段位置,來定位該數據,如下圖:

clip_image049

分析:該模塊具有很明顯的特征 :所屬libnative-lib.so並且具有rw權限。全局64Bytes 數據距離該區段起始位置的偏移為:1240h;全局映射表所在偏移:4h

b) 編寫程序獲取數據(此程序在linux環境或者Cygwin中編譯好,放到Android中運行)

                    

(2) 編寫小函數:UserName字符串轉換成其Ascii串的Ascii

       如:“123》“333133323333

       代碼:

                     

unsignedchar * GetAsAs(char * Dest,intLen)

{

                unsignedchar * Ret = (unsignedchar *)malloc(Len * 2);

                memset(Ret, 0, Len * 2);

                for (int i = 0; i < Len*2; i+=2)

                {

                    unsignedchar Tmp = Dest[i/2]>>4 &0x0f|0x30;

                    Ret[i] = Tmp;

                    Ret[i + 1] = Dest[i/2] & 0x0f | 0x30;

                }

    return Ret;

}

 

 

 

    // 后面就扣取代碼了

(3) 根據跨進程獲取到的全局映射表(167004h)、全局64Bytes數據(168240h

計算映射特征值,代碼:

             

(4) 計算 HMACAES_256_gcm

       (需要安裝配置openssl )

       代碼:

             

(6) 得出結果

   

   

3.4 代碼

注: 這里 還沒有使用 進程內存讀寫,使用了讀取出來 ,應該就可以使用下面的代碼了

    // OpenSSL_DE_EN.cpp : 此文件包含"main" 函數。程序執行將在此處開始並結束。

  1 //
  2 
  3  
  4 
  5 #include"pch.h"
  6 
  7 #include<iostream>
  8 
  9 #include<openssl/ssl.h>
 10 
 11 #include<openssl/err.h>
 12 
 13 #include<Windows.h>
 14 
 15 #pragmacomment(lib,"libssl.lib")
 16 
 17 #pragmacomment(lib,"libcrypto.lib")
 18 
 19 unsignedchar g_Bytes[64];
 20 
 21 unsignedlonglongint g_4QW[4];
 22 
 23  
 24 
 25 // 獲取UserName 的Ascii
 26 
 27 unsignedchar * GetAsAs(char * Dest, intLen)
 28 
 29 {
 30 
 31     unsignedchar * Ret = (unsignedchar *)malloc(Len * 2);
 32 
 33     memset(Ret, 0, Len * 2);
 34 
 35     for (int i = 0; i < Len * 2; i += 2)
 36 
 37     {
 38 
 39         unsignedchar Tmp = Dest[i / 2] >> 4& 0x0f | 0x30;
 40 
 41         Ret[i] = Tmp;
 42 
 43         Ret[i + 1] = Dest[i / 2]& 0x0f | 0x30;
 44 
 45     }
 46 
 47     return Ret;
 48 
 49 }
 50 
 51 // 加密算法
 52 
 53  
 54 
 55 size_t__cdecl Encrypt_sub_35210(unsignedchar* key, unsignedchar * IV_, intIV_len, unsignedchar * UsualChars_AAD, intAAD_Len, unsignedchar * plainText, intplain_Len_2, void *EncryptRegion)
 56 
 57 {
 58 
 59     int CtxObj_; // esi
 60 
 61     int Len_ = 0;
 62 
 63     int v13;
 64 
 65     unsignedchar * cipherText = (unsignedchar *)malloc(0x400);
 66 
 67     EVP_CIPHER_CTX* CtxObj = EVP_CIPHER_CTX_new();               // 創建一個 Ctx
 68 
 69     EVP_EncryptInit_ex(CtxObj, EVP_aes_256_gcm(), 0, 0,0);// 綁定Aes 引擎
 70 
 71     EVP_CIPHER_CTX_ctrl(CtxObj, 9, IV_len, 0);   // 設置IV 的長度
 72 
 73     EVP_EncryptInit_ex(CtxObj, 0, 0, key, IV_);  // 初始化Key密鑰 和IV初始化向量
 74 
 75     EVP_EncryptUpdate(CtxObj, 0, &Len_, UsualChars_AAD, AAD_Len);// 提供任意AAD 數據;此方式可調用多次
 76 
 77     EVP_EncryptUpdate(CtxObj, cipherText, &Len_, plainText, plain_Len_2);// 提供需要被加密的文本,以及接收加密后的文本
 78 
 79     memcpy(EncryptRegion, cipherText, Len_);     // 拷貝 前一步2Byte 密文
 80 
 81     EVP_EncryptFinal_ex(CtxObj, cipherText, &v13);// 加密!
 82 
 83     EVP_CIPHER_CTX_ctrl(CtxObj, 16, 16, cipherText);// 獲取Aes_256_gcm 此次的 Tag  16Byte
 84 
 85     int v10 = Len_;
 86 
 87     *(unsignedlonglong  *)((char *)EncryptRegion + Len_ + 8) = *((unsignedlonglong  *)&cipherText+1);// 這兩步將16Byte 的tag 值輸出到EnCryptRegion
 88 
 89     *(unsignedlonglong  *)((char *)EncryptRegion + v10) = *(unsignedlonglong  *)&cipherText;;
 90 
 91     EVP_CIPHER_CTX_free(CtxObj);
 92 
 93     free(cipherText);
 94 
 95     return 0;
 96 
 97 }
 98 
 99  
100 
101  
102 
103 int main()
104 
105 {
106 
107     printf("請輸入 那64Bytes 數據:\n");
108 
109     for (int i = 0; i < 4; i++)
110 
111     {
112 
113         scanf_s("%i64x", g_4QW + i);
114 
115     }
116 
117  
118 
119     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
120 
121     std::cout <<"Hello World!\n";
122 
123  
124 
125     WORD v45[10];
126 
127     unsignedchar UserName_Bytes[32];
128 
129     //getCiphertTable
130 
131     unsignedlonglong  w_167004_MapTable_[1024];                                          // 當程序JNI_Onload()運行后,這隨機值就穩定了;這時候輸入的UserName 后Generate的密碼就是穩定的。
132 
133     //get 64Bytes
134 
135     unsignedlong data64Bytes; //  通過引用查看 這是一個 隨機值,賦值所在函數35520() ;在程序啟動JNI_Onload調用的時候會調用35520()函數進行初始化。
136 
137     unsignedlonglong *p64Bytes = (unsignedlonglong *)&g_4QW;                                    // 但是重新進入程序,輸入相同的UserName會發現這個值不是穩定的。就是因為JNI_ONload里面調用了35520()進行了隨機數據賦值。
138 
139     int count_64 = 64;
140 
141     short  w_FeatureDigit = 0;
142 
143     do                                            //  計算出 程序JNIEnv_Onload程序的時候隨機出的數據64bytes 通過167004處的全局cipher表;找出的隨機數據的特征值
144 
145     {                                             // 根據 隨機出的數據;查167004處的cipher表計算出特征值
146 
147         w_FeatureDigit = w_167004_MapTable_[*(unsignedchar *)p64Bytes ^ ((unsignedint)w_FeatureDigit >> 8)] ^ (w_FeatureDigit << 8);
148 
149         p64Bytes = (unsigned__int64 *)((unsignedchar *)p64Bytes + 1);
150 
151         --count_64;
152 
153     } while (count_64);
154 
155     short w_Rand64Bytes_FeatureDigit = w_FeatureDigit;
156 
157     v45[1] = (WORD)w_FeatureDigit;
158 
159     int v32 = 0;
160 
161  
162 
163     // 獲取UserName 的Ascii
164 
165     char Name[] = { "123456789abcdefg" };
166 
167     unsignedchar * AscData = GetAsAs(Name, 0x10);
168 
169     HMAC_CTX *Ctx = HMAC_CTX_new();// 創建一個 Ctx
170 
171     HMAC_Init_ex(Ctx, AscData, 32, EVP_sha256(), NULL);// 創建一個 摘要引擎 EVP_sha256
172 
173     HMAC_Update(Ctx, (constunsignedchar *)&w_Rand64Bytes_FeatureDigit, 2);// 此函數 可以重復調用,將數據塊加入到 計算結果中
174 
175     unsignedint len1 = 0;
176 
177     unsignedchar * result = (unsignedchar*)malloc(sizeof(char) * 0x20);
178 
179     HMAC_Final(Ctx, result, &len1);
180 
181     int v26 = 32;                                    // v45-32 的位置 剛好是 前面計算UserName 的Ascii串
182 
183     int v27 = 0;                                      // int16   - Word
184 
185     do
186 
187         v27 = w_167004_MapTable_[*((unsigned__int8 *)AscData + --v26) ^ ((unsignedint)v27 >> 8)] ^ (v27 << 8);
188 
189     while (v26);
190 
191     short UserNameMapFeature = v27;
192 
193     unsignedchar * UsualCharNum = (unsignedchar *)malloc(0x25);
194 
195     memcpy(UsualCharNum, "abcdefghijklmnopqrstuvwxyz0123456789", 0x25u);// 准備AES_GCM的AAD數據
196 
197     unsignedchar * EncryptRegion_ = (unsignedchar *)malloc(0x400);
198 
199     memset(&EncryptRegion_, 0, 0x400u);
200 
201     Encrypt_sub_35210(
202 
203         result,
204 
205         (unsignedchar *)UserName_Bytes,
206 
207         12,
208 
209         UsualCharNum,
210 
211         37,
212 
213         (unsignedchar *)UserNameMapFeature,
214 
215         2,
216 
217         EncryptRegion_);
218 
219     v45[0] = *(WORD *)EncryptRegion_;      // 密碼的前2Byte
220 
221     *(unsignedlonglong *)&v45[2] = *(unsignedlonglong *)(EncryptRegion_ + 4);
222 
223     *(unsignedlonglong *)&v45[6] = *(unsignedlonglong *)(EncryptRegion_ + 12);// 這兩步 是復制密碼的tag 域16Byte/ / v45、v46、v47 這三個連續的一片區域就是 我們的密碼
224 
225  
226 
227 }
228 
229  

 

參考資料:

[1] so文件調試環境搭建:

       https://cloud.tencent.com/developer/article/1357098

[2] Intel白皮書SSE2相關指令查詢:

https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=SSE2&text=_mm_cmpeq_epi32&expand=773

[3] HMAC 摘要簽名計算:

       https://www.xuebuyuan.com/3193335.html

http://gmssl.org/docs/evp-api.html

[4] OpenSSL AES_256_gcm加解密講解

       https://s0www0openssl0org.icopy.site/docs/man1.1.1/man3/EVP_CipherFinal_ex.html

       http://gmssl.org/docs/evp-api.html

[5] AES 的多種加解密模式

       https://www.cnblogs.com/0616--ataozhijia/p/11271433.html

[6] Android 跨進程讀取內存數據

       https://blog.csdn.net/mldxs/article/details/14486827

[7] VS2017 使用OpenSSL

       https://blog.csdn.net/xiejie0226/article/details/80494458


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM