本教程只适合菜鸟学习,大牛请过......{:1_930:}{:1_928:}
目录
1.大家都知道Android中的程序反编译比较简单,辛苦开发出一个APK轻易被人反编译了,所以现在就有很多APK加固的第三方平台,比如爱加密和梆梆加固等。 2.一般的加固保护通常能够提供如下保护:加密、防逆向、防篡改、反调试、反窃取等功能,编写静态脱壳机须要信息有加密后的原始DEX数据、解密算法、解密密钥、要想获得这些信息我们首先要解决的问题是过反调试、动态分析解密流程、获取密钥,获得原始DEX数据存放位置、分析解密算法。 0x01 壳简单分析
1.整体来看一下加固前APK包和加固后的APK包结构相关变化,如图1所示。
1
图1 图1所示加固后的APK增加了librsprotect.so、librsprotect_x86.so、rsprotect.dat文件,发生变化的有AndroidManifest.xml、classes.dex文件。 2.反编译加固后APK,APK中的AndroidManifest.xml文件的入口被修改,如图2所示。
2
图2 3.入口类中主要会调用librsprotect.so中的3个函数,如图3所示。 private native void initialize(ContextparamContext); private native ApplicationmakeApplication(String paramString);
private native void applicationOnCreate();
3
图3 0x02SO文件静态脱壳 1. 既然主要是调用librsprotect.so中的函数,我们将librsprotect.so放到IDA Pro中分析,发现代码都是乱码 图4所示,说明被加密了。
4
图4 2.一般加壳的SO的壳代码都在INIT段或INIT_ARRAY段,我们先看下被加壳以后的SO信息,用readelf -a命令查看,图5所示
5
图5 可以看到INIT值为0x2ea91,到 IDA中看看该地址的内容,就是壳的入口了,明显是UPX的加壳,图6所示,有人会问你为什么会知道是UPX的壳,“只是因为在人群中多看了你一眼,再也没能忘掉你容颜!(^_^)”。
6
图6 3.尝试用upx -d脱壳,因为这样脱方便、干净、省事、提示图7所示的错误信息。
7
图7 查看UPX源码后发现可能是没有找到UPX!的标志的原因,用16进制工具打开librsprotect.so发现标志被改成了RSP!,将其改成UPX!后再将尝试,出现 图8所示的信息。
8
图8 出现这种错误可能是做变形处理了或者是版本不对,通过分析librsprotect.so的壳代码好像没有变形处理,所以决定重新编一个3.92版本的来试试,编译好后脱壳成功,如图9所示。
9
图9 将脱壳后的so放到IDA Pro中分析,代码正常,图10所示,SO脱壳完成。
10
图10 0x03反调试分析 1.如何使用IDA调试android的SO模块,网上教程也太多太多了,这里不多说,将脱壳后的librsprotect.so替换掉原始有壳的SO后(也可不用替换没影响,这里只是为了测试)签名安装进行动态分析。
2.通过动态调试该加壳程序,它用到的反调试方法是首先试探性读取/proc/pid/status获取进程状态去判断是否有调试器,如果发现被调试就kill掉本进程,如图11所示
11
图11 3.通过读取/proc/net/tcp查看正在运行应用的本地端口号是否有android_server端口,如果有就创建一个反调试线程,如图12、13所示,每隔几秒检查一次,过反调试就很简单了直接把返回值改成假就成了。
12
图12 android_server运行后端口
13
图13 0x04 解密流程分析
1. 根据算法中的常量值猜测该算法为MD5,图14所示
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
图15 2.打开并读取/data/data/com.droider.crackme0201/files/.rsdata/rsprotect.dat数据,该文件是从APK包中的assets文件夹中拷贝过来的,判断前4字节是"RSFL"是否与so中的相同,不同则退出,rsprotect.dat前0x1000字节存放原始DEX大小与循环解密的次数,每次解密0x1000字节,根据密钥初始化流程发现解密算法为RC4,图16示。
16
图16 0x05 脱壳机编写 1.通过分析,已经知道了壳的数据、密钥、算法、解密过程, 现在来写脱壳机。 必要步骤如下: 1。解包获得rsprotect.da数据。 2.XML解析获得包名。 3.MD5计算获得密钥。 4.RC4解密rsprotect.dat中的数据。
代码流程: [C++] 纯文本查看 复制代码 #include"stdafx.h"
#include<afxwin.h>
#include<stdio.h>
#include<windows.h>
#include<process.h>
#include<assert.h>
#include<string>
#include<iostream>
#include"CMarkup.h"
#include"md5.h"
#include"rc4.h"
#include<string>
usingnamespacestd;
BOOLGetPackName(char* pathXml, charoutPackName[256])
{
CMarkupxml;
boolflag;
CStringpackName;
CStringAppandroidname;
MCD_STRmyapkName;
MCD_STRattribName;
char* strXML = "\\AndroidManifest.xml";
strcat(pathXml,strXML);
flag = xml.Load((MCD_STR)pathXml);
if ( FALSE == flag)
{
printf("获得包名失败...\n");
returnFALSE;
}
flag = xml.FindElem((MCD_STR)"manifest");
//获取包名.........
for(intattribIndex=0;;attribIndex++)
{
attribName=xml.GetAttribName(attribIndex);
if (attribName.GetLength()!=0) //方法若返回empty string,即表示属性结束,结束循环
{
MCD_STRattribVal = xml.GetAttrib(attribName);//否则读取属性值,以子元素加入dxml
//------判断是否为包名...
packName = attribName.GetString();
if ( 0 == strcmp(packName.GetString(), "package"))
{
myapkName = attribVal;
strcpy(outPackName, attribVal.GetString());
returnTRUE;
}
}
else
break;
}
returnFALSE;
}
voidStrToHex(BYTE *pbDest, BYTE *pbSrc, intnLen)
{
charh1,h2;
BYTEs1,s2;
inti;
for (i=0; i<nLen; i++)
{
h1 = pbSrc[2*i];
h2 = pbSrc[2*i+1];
s1 = toupper(h1) - 0x30;
if (s1> 9)
s1 -= 7;
s2 = toupper(h2) - 0x30;
if (s2> 9)
s2 -= 7;
pbDest[i] = s1*16 + s2;
}
}
int_tmain(intargc, _TCHAR* argv[])
{
charstrAPK[512] = {0};
charapkd[256] = "";
charFileDirectory[512] = {0};
chardexDirectory[512] = {0};
charPackName[256] = {0};
BOOLret = FALSE;
structrc4_staterc4_test;
FILE *fp;
DWORDfileSize = 0;
BYTE *ptr = NULL;
DWORDDecOffset = 0X1000;//文件偏移
DWORDDecSize = 0X0;//大小
DWORDindex = 0;//循环解密的次数
stringkey;
printf("请输入要脱壳的apk包路径:\n");
scanf("%s",strAPK);
if (NULL == strAPK)
{
printf("路径不能为空!\n");
return -1;
}
strcpy(apkd, "java -jar apktool.jar d ");
strcat(apkd, strAPK);
//--------bat解包
CFileapktool("apktool.bat", CFile::modeCreate | CFile::modeReadWrite);
apktool.Write(apkd, strlen(apkd));
apktool.Write("\r\n", strlen("\r\n"));
apktool.Close();
char* cmd1 = "apktool.bat";
STARTUPINFOsi1;
GetStartupInfo(&si1);
si1.dwFlags = STARTF_USESHOWWINDOW;
si1.wShowWindow = SW_HIDE;
PROCESS_INFORMATIONpi1;
CreateProcess(NULL,
(LPSTR)cmd1,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si1,
&pi1);
printf("正在解包apk...\n");
WaitForSingleObject(pi1.hProcess,INFINITE);
DeleteFile("apktool.bat");
printf("解包完成...\n");
//-----判断是否解包成功..........
strncpy(FileDirectory, strAPK, strlen(strAPK)-strlen(".apk"));
DWORDdwFileAtt;
dwFileAtt = GetFileAttributes(FileDirectory);
//判断是否为目录
if( dwFileAtt != FILE_ATTRIBUTE_DIRECTORY)
{
printf("apk解包失败!...\n");
return 0;
}
printf("apk解包成功!...\n");
strcpy(dexDirectory, FileDirectory);
//得到包名
ret = GetPackName(FileDirectory,PackName);
if (FALSE == ret)
{
printf("获得包名失败...\n");
return -1;
}
//计算MD5值
MD5md5(PackName);
key = md5.md5();
strcat(dexDirectory, "\\assets\\rsprotect.dat");
fp=fopen(dexDirectory, "rb");
if(fp==NULL)
printf("打开文件失败!...");
//求文件大小
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
ptr = (BYTE*)malloc(fileSize);
if (NULL == ptr)
{
puts("malloc error");
}
memset(ptr,fileSize,0);
fread(ptr, sizeof(BYTE), fileSize, fp);
fclose(fp);
//--解密dex
DecSize = *(DWORD*)(ptr+12);
index = *(DWORD*)(ptr+0x10);
ptr += DecOffset;
if (0 == DecSize)
{
printf("要解密的dex大小出错\n");
return -1;
}
memset(&rc4_test,0,sizeof(rc4_test));
unsignedcharDecKey[32] = {0};
for (inti=0; i<32; i++)
{
DecKey[i] = key[i];
}
unsignedcharkey1[32] ={0};
StrToHex(key1, DecKey, 0x10);
//--生成解密后的dex文件
fp = fopen("classes.dex","wb");
if (NULL == fp)
{
printf("File open error\n");
}
for (inti=0; i<index; i++)
{
//初始化Key
init_Key(&rc4_test, key1, 0x10);
//解密数据
rc4_crypt(&rc4_test, ptr, DecOffset);
fwrite(ptr, sizeof(BYTE), DecOffset, fp);
ptr+=DecOffset;
}
fclose(fp);
if (NULL != ptr)
{
free(Temp);
ptr = NULL;
Temp = NULL;
}
printf("解密完成!^_^\n");
return 0;
}
0x06测试与总结
1.运行UnPack.exe输入要解密的APK包路径,成功解密后重新打包并正常反编译,如图17、18所示。
17
图17
18
图18 2.以上就是简单实现一般APK加固静态脱壳机的编写步骤,由于该加固核心so文件使用UPX默认加壳并未做变形处理,导致so被轻松的静态脱卓,而so模块中的反调试手段比较初级且模块化,可以非常简单的手工patch函数一处反回值就可完全过掉,总的来说无论是静态脱壳还是动态dump都是很容易的。
完。 样本 src pdf IDB 下载
src_样本_IDB_pdf.part03.rar
(2 MB, 下载次数: 342)
src_样本_IDB_pdf.part04.rar
(2 MB, 下载次数: 320)
src_样本_IDB_pdf.part05.rar
(2 MB, 下载次数: 319)
src_样本_IDB_pdf.part06.rar
(1.22 MB, 下载次数: 326)
src_样本_IDB_pdf.part02.rar
(2 MB, 下载次数: 416)
|