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中的数据。
代码流程:
#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[])
{
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=;;attribIndex++)
{
attribName=xml.GetAttribName(attribIndex);
if (attribName.GetLength()!=) //方法若返回empty string,即表示属性结束,结束循环
{
MCD_STRattribVal = xml.GetAttrib(attribName);//否则读取属性值,以子元素加入dxml
//------判断是否为包名...
packName = attribName.GetString();
if ( == 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=; i<nLen; i++)
{
h1 = pbSrc[*i];
h2 = pbSrc[*i+];
s1 = toupper(h1) - 0x30;
if (s1> )
s1 -= ;
s2 = toupper(h2) - 0x30;
if (s2> )
s2 -= ;
pbDest[i] = s1* + s2;
}
}
int_tmain(intargc, _TCHAR* argv[])
{
charstrAPK[] = {};
charapkd[] = "";
charFileDirectory[] = {};
chardexDirectory[] = {};
charPackName[] = {};
BOOLret = FALSE;
structrc4_staterc4_test;
FILE *fp;
DWORDfileSize = ;
BYTE *ptr = NULL;
DWORDDecOffset = 0X1000;//文件偏移
DWORDDecSize = 0X0;//大小
DWORDindex = ;//循环解密的次数
stringkey;
printf("请输入要脱壳的apk包路径:\n");
scanf("%s",strAPK);
if (NULL == strAPK)
{
printf("路径不能为空!\n");
return -;
}
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 ;
}
printf("apk解包成功!...\n");
strcpy(dexDirectory, FileDirectory);
//得到包名
ret = GetPackName(FileDirectory,PackName);
if (FALSE == ret)
{
printf("获得包名失败...\n");
return -;
}
//计算MD5值
MD5md5(PackName);
key = md5.md5();
strcat(dexDirectory, "\\assets\\rsprotect.dat");
fp=fopen(dexDirectory, "rb");
if(fp==NULL)
printf("打开文件失败!...");
//求文件大小
fseek(fp, , SEEK_END);
fileSize = ftell(fp);
fseek(fp, , SEEK_SET);
ptr = (BYTE*)malloc(fileSize);
if (NULL == ptr)
{
puts("malloc error");
}
memset(ptr,fileSize,);
fread(ptr, sizeof(BYTE), fileSize, fp);
fclose(fp);
//--解密dex
DecSize = *(DWORD*)(ptr+);
index = *(DWORD*)(ptr+0x10);
ptr += DecOffset;
if ( == DecSize)
{
printf("要解密的dex大小出错\n");
return -;
}
memset(&rc4_test,,sizeof(rc4_test));
unsignedcharDecKey[] = {};
for (inti=; i<; i++)
{
DecKey[i] = key[i];
}
unsignedcharkey1[] ={};
StrToHex(key1, DecKey, 0x10);
//--生成解密后的dex文件
fp = fopen("classes.dex","wb");
if (NULL == fp)
{
printf("File open error\n");
}
for (inti=; 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 ;
}
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加固之静态脱壳机编写入门的更多相关文章
- Android FART脱壳机流程分析
本文首发于安全客 链接:https://www.anquanke.com/post/id/219094 0x1 前言 在Android平台上,程序员编写的Java代码最终将被编译成字节码在Androi ...
- APK加固之类抽取分析与修复
0x00 简单介绍 目前我己知的APK加固主要有以下两种方式(或有其它的方式有待发现) 隐藏dex文件:通过对目标DEX文件进行整体加密或压缩方式把整个dex转换为另外一个文件存放在assets文 ...
- Android Apk加固的初步实现思路(dex整体加固)
一.前 言 Android Apk加固的发展已经有一段时间了,相对来说本篇博客要记录的Android加壳的实现思路是4年的东西了,已经被老鸟玩烂了,Android加固的安全厂商也不会采用这么粗犷的方式 ...
- 基于御安全APK加固的游戏反外挂方案
一. 前言 随着移动互联网的兴起,移动游戏市场近几年突然爆发,收入规模快速增长.根据第三方数据统计,国内移动游戏2015年市场规模已达514.6亿.由于手游市场强势兴起,而且后续增长势头会愈加猛烈.火 ...
- linux库文件编写入门(笔记)
linux库文件的编写 作者: laomai地址: http://blog.csdn.net/laomai 本文主要参考了如下资料⑴hcj写的"Linux静态/动态链接库的创建和使用&quo ...
- 国内apk加固的破解方法
国内apk加固的破解方法 By Bob Pan 国内的apk加固技术都使用了将原有的dex隐藏, 在运行时解压, 并且通过修改app的类加载器的方式实现加固. 参考: AndoridAPK反逆向解决方 ...
- 010 Editor 8.0.1 之 逆向分析及注册机编写
前言一.工具及软件介绍二.逆向分析2.1.找到提示错误注册弹窗2.2.分析跳转处代码2.3.=2D 函数分析2.3.1.获取注册码处分析2.3.2.3处分支分析2.3.2.1.9C情况2.3.2.2. ...
- Gulp:插件编写入门
之前挖了个坑,准备写篇gulp插件编写入门的科普文,之后迟迟没有动笔,因为不知道该肿么讲清楚Stream这货,毕竟,gulp插件的实现不像grunt插件的实现那么直观. 好吧,于是决定单刀直入了.文中 ...
- [.NET] [.net 脱壳工具]Sixxpack 最新脱壳机 通杀Sixxpack全版本by -=Msdn5 君临=
[.net 脱壳工具]Sixxpack 最新脱壳机 通杀Sixxpack全版本by -=Msdn5 君临=- 识别方法: 如果无法调戏,请上传附件艾特我.............发帖不易啊..身处大西 ...
随机推荐
- 模板【洛谷P3811】 【模板】乘法逆元
P3811 [模板]乘法逆元 给定n,p求1~n中所有整数在模p意义下的乘法逆元. T两个点的费马小定理求法: code: #include <iostream> #include < ...
- springloud系列搭建注册中心
首先搭建父工程: 点击next父工程就搭建完成; pom.xml文件: <?xml version="1.0" encoding="UTF-8"?> ...
- 解决浏览器location.href重定向失效问题
在[location.href]赋值语句后,添加页面刷新代码[location.reload(true)],参数为[true]这样就等价于F5刷新页面了. 需要注意的是:不能把[location.re ...
- sharepoint_study_11
描述:修改管理员密码和账户后,管理中心无法打开.(提示:HTTP Error 503. The service is unavailable.) 解决: 1.打开iis 2.右击“应用程序池”下的”S ...
- Luogu P1120 小木棍 [数据加强版] 来来来我们一起来剪枝,剪枝,剪枝、、、
好啊...太棒了... dfs(拼到第几根木棍,这根木棍剩余长度,上一根木棍的位置) len是木棍的长度,cnt是木棍的个数 震撼人心的剪枝: 1.枚举长度从最大的木棍开始,直到sum/2,因为之后只 ...
- chrome插件 crap jsonview
最近有用到几个非常好用的Chrome 记录一下: 1.Crap Api debug,直接在Chrome标签打开的,界面大概长这样,方便调试 2. adblock 比较大众 3. tampermonk ...
- Autel MaxiSys Pro MS908P
Autel MaxiSys pro MS908P is an evolutionary smart solution for specialized automotive diagnosis and ...
- NETCORE 之 openSUSE docker 安装
openSUSE docker 安装https://www.jianshu.com/p/c725a06447d5 http://www.importnew.com/24684.htmlSuse安装Do ...
- Java学习笔记day07_琐碎知识_水仙花数_ASCII码_冒泡排序_简单选择排序_折半查找
琐碎知识: 水仙花数, ASCII码, 冒泡排序, 简单选择排序, 折半查找 1.水仙花数 每位数的平方的和等于本身. 如100到999之间的水仙花数满足: 个位的平方+十位的平方+百位的平方 = 本 ...
- maya2016无法安装卸载激活失败
AUTODESK系列软件着实令人头疼,安装失败之后不能完全卸载!!!(比如maya,cad,3dsmax等).有时手动删除注册表重装之后还是会出现各种问题,每个版本的C++Runtime和.NET f ...