APK加固之靜態脫殼機編寫入門
目錄:
0x00APK加固簡介與靜態脫殼機的編寫思路
1.大家都知道Android中的程式反編譯比較簡單,辛苦開發出一個APK輕易被人反編譯了,所以現在就有很多APK加固的第三方平臺,比如愛加密和梆梆加固等。
2.一般的加固保護通常能夠提供如下保護:加密、防逆向、防篡改、反除錯、反竊取等功能,編寫靜態脫殼機須要資訊有加密後的原始DEX資料、解密演算法、解密金鑰、要想獲得這些資訊我們首先要解決的問題是過反除錯、動態分析解密流程、獲取金鑰,獲得原始DEX資料存放位置、分析解密演算法。
0x01殼簡單分析
1.整體來看一下加固前APK包和加固後的APK包結構相關變化,如圖1所示。
圖1
圖1所示加固後的APK增加了librsprotect.so、librsprotect_x86.so、rsprotect.dat檔案,發生變化的有AndroidManifest.xml、classes.dex檔案。
2.反編譯加固後APK,APK中的AndroidManifest.xml檔案的入口被修改,如圖2所示。
圖2
3.入口類中主要會呼叫librsprotect.so中的3個函式,如圖3所示。
private native void initialize(Context paramContext);
private native Application makeApplication(String paramString);
private native void applicationOnCreate();
圖3
0x02 SO檔案脫殼
1.既然主要是呼叫librsprotect.so中的函式,我們將librsprotect.so放到IDA Pro中分析,發現程式碼都是亂碼 圖4所示,說明被加密了。
圖4
2.一般加殼的SO的殼程式碼都在INIT段或INIT_ARRAY段,我們先看下被加殼以後的SO資訊,用readelf -a命令檢視,圖5所示
圖5
可以看到INIT值為0x2ea91,到 IDA中看看該地址的內容,就是殼的入口了,明顯是UPX的加殼,圖6所示,有人會問你為什麼會知道是UPX的殼,“只是因為在人群中多看了你一眼,再也沒能忘掉你容顏!(^_^)”。
圖6
3.嘗試用upx -d脫殼,因為這樣脫方便、乾淨、省事、提示圖7所示的資訊。
圖7
檢視UPX原始碼後發現可能是沒有找到UPX!的標誌,用16進位制工具開啟librsprotect.so發現標誌被改成了RSP!,將其改成UPX!後再將嘗試,出現 圖8所示的資訊。
圖8
出現這種錯誤可能是做變形處理了或者是版本不對,通過分析librsprotect.so的殼程式碼好像沒有變形處理,所以決定重新編一個3.92版本的來試試,編譯好後脫殼成功,如圖9所示。
圖9
將脫殼後的so放到IDA Pro中分析,程式碼正常,圖10所示,SO脫殼完成。
圖10
0x03 反除錯分析
1.如何使用IDA除錯android的SO模組,網上教程也太多太多了,這裡不多說,將脫殼後的librsprotect.so替換掉原始有殼的SO後(也可不用替換沒影響,這裡只是為了測試)簽名安裝進行動態分析。
2.通過動態除錯該加殼程式,它用到的反除錯方法是首先試探性讀取/proc/pid/status獲取程式狀態去判斷是否有偵錯程式,如果發現被除錯就kill掉本程式,如圖11所示
圖11
3.通過讀取/proc/net/tcp檢視正在執行應用的本地埠號是否有android_server埠,如果有就建立一個反除錯執行緒,如圖12所示,每隔幾秒檢查一次,過反除錯就很簡單了直接把返回值改成假就成了。
圖12 android_server執行後埠
圖13
0x04解密流程分析
1. 根據演算法中的常量值猜測該演算法為MD5,如圖14所示
圖14
2.獲取包名並計算MD5值 圖15所示,將該值做為金鑰。
com.droider.crackme0201
F2 E8 F0 62 85 17 9C 3C 99 F5 67 9F A6 27 FC 55
圖15
2.開啟並讀取/data/data/com.droider.crackme0201/files/.rsdata/rsprotect.dat資料,該檔案是從APK包中的assets資料夾中拷貝過來的,判斷前4位元組是"RSFL"是否與so中的相同,不同則退出,rsprotect.dat前0x1000位元組存放原始DEX大小與迴圈解密的次數,每次解密0x1000位元組,根據金鑰初始化流程發現解密演算法為RC4,圖16示(也可以看IDB)。
圖16
0x05脫殼機編寫
1.通過分析,已經知道了殼的資料、金鑰、演算法、解密過程, 現在來寫脫殼機。
必要步驟如下:
1。解包獲得rsprotect.da資料。
2.XML解析獲得包名。
3.MD5計算獲得金鑰。
4.RC4解密rsprotect.dat中的資料。
程式碼流程:
1 #include"stdafx.h" 2 #include<afxwin.h> 3 #include<stdio.h> 4 #include<windows.h> 5 #include<process.h> 6 #include<assert.h> 7 #include<string> 8 #include<iostream> 9 #include"CMarkup.h" 10 #include"md5.h" 11 #include"rc4.h" 12 #include<string> 13 usingnamespacestd; 14 BOOLGetPackName(char* pathXml, charoutPackName[256]) 15 { 16 CMarkupxml; 17 boolflag; 18 CStringpackName; 19 CStringAppandroidname; 20 MCD_STRmyapkName; 21 MCD_STRattribName; 22 char* strXML = "\\AndroidManifest.xml"; 23 strcat(pathXml,strXML); 24 flag = xml.Load((MCD_STR)pathXml); 25 if ( FALSE == flag) 26 { 27 printf("獲得包名失敗...\n"); 28 returnFALSE; 29 } 30 flag = xml.FindElem((MCD_STR)"manifest"); 31 //獲取包名......... 32 for(intattribIndex=0;;attribIndex++) 33 { 34 attribName=xml.GetAttribName(attribIndex); 35 if (attribName.GetLength()!=0) //方法若返回empty string,即表示屬性結束,結束迴圈 36 { 37 MCD_STRattribVal = xml.GetAttrib(attribName);//否則讀取屬性值,以子元素加入dxml 38 //------判斷是否為包名... 39 packName = attribName.GetString(); 40 if ( 0 == strcmp(packName.GetString(), "package")) 41 { 42 myapkName = attribVal; 43 strcpy(outPackName, attribVal.GetString()); 44 returnTRUE; 45 } 46 } 47 else 48 break; 49 } 50 returnFALSE; 51 } 52 voidStrToHex(BYTE *pbDest, BYTE *pbSrc, intnLen) 53 { 54 charh1,h2; 55 BYTEs1,s2; 56 inti; 57 for (i=0; i<nLen; i++) 58 { 59 h1 = pbSrc[2*i]; 60 h2 = pbSrc[2*i+1]; 61 s1 = toupper(h1) - 0x30; 62 if (s1> 9) 63 s1 -= 7; 64 s2 = toupper(h2) - 0x30; 65 if (s2> 9) 66 s2 -= 7; 67 pbDest[i] = s1*16 + s2; 68 } 69 } 70 int_tmain(intargc, _TCHAR* argv[]) 71 { 72 charstrAPK[512] = {0}; 73 charapkd[256] = ""; 74 charFileDirectory[512] = {0}; 75 chardexDirectory[512] = {0}; 76 charPackName[256] = {0}; 77 BOOLret = FALSE; 78 structrc4_staterc4_test; 79 FILE *fp; 80 DWORDfileSize = 0; 81 BYTE *ptr = NULL; 82 DWORDDecOffset = 0X1000;//檔案偏移 83 DWORDDecSize = 0X0;//大小 84 DWORDindex = 0;//迴圈解密的次數 85 stringkey; 86 printf("請輸入要脫殼的apk包路徑:\n"); 87 scanf("%s",strAPK); 88 if (NULL == strAPK) 89 { 90 printf("路徑不能為空!\n"); 91 return -1; 92 } 93 strcpy(apkd, "java -jar apktool.jar d "); 94 strcat(apkd, strAPK); 95 //--------bat解包 96 CFileapktool("apktool.bat", CFile::modeCreate | CFile::modeReadWrite); 97 apktool.Write(apkd, strlen(apkd)); 98 apktool.Write("\r\n", strlen("\r\n")); 99 apktool.Close(); 100 char* cmd1 = "apktool.bat"; 101 STARTUPINFOsi1; 102 GetStartupInfo(&si1); 103 si1.dwFlags = STARTF_USESHOWWINDOW; 104 si1.wShowWindow = SW_HIDE; 105 PROCESS_INFORMATIONpi1; 106 CreateProcess(NULL, 107 (LPSTR)cmd1, 108 NULL, 109 NULL, 110 FALSE, 111 CREATE_NEW_CONSOLE, 112 NULL, 113 NULL, 114 &si1, 115 &pi1); 116 printf("正在解包apk...\n"); 117 WaitForSingleObject(pi1.hProcess,INFINITE); 118 DeleteFile("apktool.bat"); 119 printf("解包完成...\n"); 120 //-----判斷是否解包成功.......... 121 strncpy(FileDirectory, strAPK, strlen(strAPK)-strlen(".apk")); 122 DWORDdwFileAtt; 123 dwFileAtt = GetFileAttributes(FileDirectory); 124 //判斷是否為目錄 125 if( dwFileAtt != FILE_ATTRIBUTE_DIRECTORY) 126 { 127 printf("apk解包失敗!...\n"); 128 return 0; 129 } 130 printf("apk解包成功!...\n"); 131 strcpy(dexDirectory, FileDirectory); 132 //得到包名 133 ret = GetPackName(FileDirectory,PackName); 134 if (FALSE == ret) 135 { 136 printf("獲得包名失敗...\n"); 137 return -1; 138 } 139 //計算MD5值 140 MD5md5(PackName); 141 key = md5.md5(); 142 strcat(dexDirectory, "\\assets\\rsprotect.dat"); 143 fp=fopen(dexDirectory, "rb"); 144 if(fp==NULL) 145 printf("開啟檔案失敗!..."); 146 //求檔案大小 147 fseek(fp, 0, SEEK_END); 148 fileSize = ftell(fp); 149 fseek(fp, 0, SEEK_SET); 150 ptr = (BYTE*)malloc(fileSize); 151 if (NULL == ptr) 152 { 153 puts("malloc error"); 154 } 155 memset(ptr,fileSize,0); 156 fread(ptr, sizeof(BYTE), fileSize, fp); 157 fclose(fp); 158 //--解密dex 159 DecSize = *(DWORD*)(ptr+12); 160 index = *(DWORD*)(ptr+0x10); 161 ptr += DecOffset; 162 if (0 == DecSize) 163 { 164 printf("要解密的dex大小出錯\n"); 165 return -1; 166 } 167 memset(&rc4_test,0,sizeof(rc4_test)); 168 unsignedcharDecKey[32] = {0}; 169 for (inti=0; i<32; i++) 170 { 171 DecKey[i] = key[i]; 172 } 173 unsignedcharkey1[32] ={0}; 174 StrToHex(key1, DecKey, 0x10); 175 //--生成解密後的dex檔案 176 fp = fopen("classes.dex","wb"); 177 if (NULL == fp) 178 { 179 printf("File open error\n"); 180 } 181 for (inti=0; i<index; i++) 182 { 183 //初始化Key 184 init_Key(&rc4_test, key1, 0x10); 185 //解密資料 186 rc4_crypt(&rc4_test, ptr, DecOffset); 187 188 fwrite(ptr, sizeof(BYTE), DecOffset, fp); 189 ptr+=DecOffset; 190 } 191 fclose(fp); 192 if (NULL != ptr) 193 { 194 free(Temp); 195 ptr = NULL; 196 Temp = NULL; 197 } 198 199 printf("解密完成!^_^\n"); 200 return 0; 201 }
0x06 測試與總結
1.執行UnPack.exe輸入要解密的APK包路徑,成功解密後重新打包並正常反編譯,如圖17 圖18所示。
圖17
圖18
2.以上就是簡單實現一般APK加固靜態脫殼機的編寫步驟,由於該加固核心so檔案使用UPX預設加殼並未做變形處理,導致so被輕鬆的靜態脫卓,而so模組中的反除錯手段比較初級且模組化,可以非常簡單的手工patch函式一處反回值就可完全過掉,總的來說無論是靜態脫殼還是動態dump都是很容易的。
完。
樣本及PDF IDB下載
http://yunpan.cn/cFzNPXB27awau (提取碼:6937)
相關文章
- 某當網apk加固脫個so2022-10-29APK
- Android之Apk加殼2018-12-11AndroidAPK
- 效能優化 (八) APK 加固之動態替換 Application2019-06-04優化APKAPP
- Java自學入門之靜態變數2021-01-28Java變數
- Android加固之後Apk重簽名2024-03-27AndroidAPK
- iOS逆向學習之五(加殼?脫殼?)2019-10-10iOS
- 教你如何寫UPX脫殼指令碼2019-05-11指令碼
- 逆向基礎——軟體手動脫殼技術入門2020-08-19
- Asp.Net Core入門之靜態檔案2020-06-20ASP.NET
- scala入門之編寫scala指令碼2018-10-26指令碼
- 十、iOS逆向之《越獄砸殼/ipa脫殼》2021-03-18iOS
- Od跟進之脫殼(待完善)2018-10-20
- 資料庫靜態脫敏2020-01-15資料庫
- 編寫靜態頁面經驗總結2020-12-10
- APK包的加固手段收集(淺)2024-07-27APK
- Android.Hook框架Cydia篇(脫殼機制作)2020-08-19AndroidHook框架
- upx手動脫殼2020-10-26
- apk加殼加密工具(apk protect) v1.0下載2020-12-15APK加密
- 自動化漏洞挖掘:靜態程式分析入門2022-12-07
- 效能優化 (七) APK 加固之 Dex 加解密,反編譯都看不到專案主要程式碼。2019-06-02優化APK解密編譯
- 某IOT蠕蟲病毒分析之UPX脫殼實戰2018-04-11
- Java入門系列之重寫2020-07-11Java
- vue3編譯最佳化之“靜態提升”2024-05-14Vue編譯
- linux裝置驅動編寫入門2021-07-08Linux
- 尋找適合編寫靜態分析規則的語言2024-07-05
- Android NDK祕籍--編譯靜態庫、呼叫靜態庫2019-04-21Android編譯
- 《Flask 入門教程》第 4 章:使用靜態檔案2018-12-19Flask
- Android第一代殼demo編寫2021-02-01Android
- Linux下快速靜態編譯Qt以及Qt動態/靜態版本共存2018-03-11Linux編譯QT
- Flink 入門篇之 寫個WordCount2020-04-05
- 3.1 IDA Pro編寫IDC指令碼入門2023-11-11指令碼
- 用typescript寫靜態頁面2019-02-16TypeScript
- 如何寫好靜態的TableView2018-03-27View
- Pelican 入門:一個 Python 靜態網站生成器2019-01-23Python網站
- Nginx配置靜態檔案服務從入門到精通2021-03-16Nginx
- 一次簡單的脫殼2024-08-30
- 淺談安卓apk加固原理和實現2018-12-04安卓APK
- Java代理之靜態代理2020-09-07Java
- Visual Studio 2022 靜態庫編譯2024-11-22編譯