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)


免責聲明!

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



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