2

[原创]**加固分析

 2 years ago
source link: https://bbs.pediy.com/thread-271491.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这篇帖子起因是前段时间想挂个协和的号,随便拿了个测试机下载好APP后打开卡在启动界面,以为是测试机root之类的东西被检测到后拒绝启动了。

于是准备看看怎么回事,发现这玩意儿使用了**加固,正准备分析分析,突然发现测试机WiFi没连上,连上网之后就正常了,于是就把它丢在一边没管了。

然后过年在家闲着没事,想起还有这么个东西,正好**加固以前没有遇到过,于是又在垃圾桶里把它翻出来了。

分析完后,实在是忍不住想吐槽一下,,不知道是不是外包公司没给钱,这加固做得就跟期末大作业水平差不多,全程就像读源码一样,,和前一篇帖子分析的乐固差远了。。

分析

老规矩,先把apk用jadx反编译,从AndroidManifest.xml找到application的类com.***ivm.security.StubApplication,

发现这个类的attachBaseContext和onCreate方法都是native方法,然后有个静态代码块。

在SDK大于等于29(android9)的时候,先解除对隐藏api的反射限制。

然后init方法判断cpu架构,从apk的assets目录释放对应的so进行加载。

选择分析arm32的文件kadp_armeabi。

745332_8QU2YC8BFGGRWE7.jpg

用ida加载kadp_armeabi,发现没有.init_array段,于是直接看JNI_OnLoad函数,代码很简单,只调用了init函数

745332_HHTFA679U88V64R.jpg

init函数的代码也很简单,判断了下sdk版本和vm版本,然后注册了两个native方法

745332_3ZQJEYN9EVJRSGD.jpg

看下off_2C124的数据,注册了attachBaseContext和onCreate这两个方法

745332_GTZDBH4X3JPRSY6.jpg

接着看native_attachBaseContext,首先调用了init_class

745332_PFNEN5YPPDUH496.jpg

init_class函数中获取了一些字段和方法的id进行缓存

745332_9CHYCC5VB4B5Q5A.jpg

回到native_attachBaseContext,获取dalvik.system.DexFile的构造方法缓存起来,然后调用函数extractDexToMemMap_zlib从apk获取dex文件,接着调用函数cfile_init对dex进行解析

745332_7KPCCCWNHRV4AJZ.jpg

函数extractDexToMemMap_zlib通过zlib库遍历apk中的文件,获取classes.dex文件并mmap

745332_PXB6FSD7TPAR473.jpg

函数cfile_init先判断文件类型,找到dex的基址,计算dex所属内存页,修改属性,用于后面解密直接修改

然后解析一些用于解密dex的字段

745332_HC2WM8TWGPE4RPQ.jpg

dex中用于解密的数据从dex的data数据之后开始,即起始位置的偏移为 dex_header.data_size+dex_header.data_off

结构如下:

745332_45QJW8G2DYCWB2P.jpg

magic字段内容如下,不匹配则解析失败

745332_HN6WQSGUGNGFMDA.jpg

dex文件中的实际数据如下,

0x2e60处是magic

0x2e68处是dexCount,所以加密的dex文件数量为10个

0x2e6c处是dataOff,所以加密的dex数据起始位置为0x2e60+0x60=0x2ec0

0x2e70处是第一个***iDexItem,所以第一个加密的dex文件大小为0x8f84f8,起始位置为0x2ec0+0=0x2ec0

745332_3HRH2Q6EXYQQ2XY.jpg

继续回到native_attachBaseContext,重新构造dexElements并替换,然后多线程执行函数thread_loadDex

745332_PDZP97HWMBG6W6T.jpg

函数thread_loadDex中主要调用mem_loadDex

745332_94RQDGMWQP5XGUT.jpg

函数mem_loadDex中首先调用函数cfile_load_file

745332_NYEGXB89B5AXB2D.jpg

函数cfile_load_file调用函数decrypt_buf进行dex解密

745332_HVJJ7UR69ZFC52P.jpg

函数decrypt_buf的解密很简单,dex只有前9字节被加密了,算法是一个简单的异或

745332_BAEDW7YHC3XDGR5.jpg

回到函数mem_loadDex中,调用write_mix_dex、openmemory_load_dex和load_dex_by_byteBuffer进行dex加载,

由于我的设备是sdk27,这几个函数中实际只有load_dex_by_byteBuffer这个函数有用。

然后通过make_dex_elements将DexFile添加到列表中

745332_XQPQ8Y8FNSPQDQJ.jpg

dex加载完成后,继续回到native_attachBaseContext,

首先通过reback_prot恢复内存页属性

然后通过old_application获取原始application的类名,原始application的类名作为metaData存放在AndroidManifest.xml,name为KWS_MAIN_APP

然后就是app相关的字段替换,就不分析了

745332_KKHPGRM4B4ECMH2.jpg

745332_98729U4F2G5NTPB.jpg

745332_DC32XSDT77FRSXD.jpg

脱壳代码

#include<fstream>
#include<string>
typedef unsigned char u1;
typedef unsigned int  u4;
struct DexHeader {
u1  magic[8];
u4  checksum;
u1  signature[20];
u4  fileSize;
u4  headerSize;
u4  endianTag;
u4  linkSize;
u4  linkOff;
u4  mapOff;
u4  stringIdsSize;
u4  stringIdsOff;
u4  typeIdsSize;
u4  typeIdsOff;
u4  protoIdsSize;
u4  protoIdsOff;
u4  fieldIdsSize;
u4  fieldIdsOff;
u4  methodIdsSize;
u4  methodIdsOff;
u4  classDefsSize;
u4  classDefsOff;
u4  dataSize;
u4  dataOff;
};
struct ***iDexItem {
u4 dexSize;
u4 dexOff;
};
struct ***iHeader {
u1 magic[8];
u4 dexCount;
u4 dataOff;
***iDexItem items[0];
};
int main() {
std::ifstream ifs("classes.dex", std::ifstream::binary);
ifs.seekg(0, ifs.end);
auto fileLen = static_cast<int>(ifs.tellg());
auto rawDexBuf = new u1[fileLen];
ifs.seekg(0, ifs.beg);
ifs.read(reinterpret_cast<char *>(rawDexBuf), fileLen);
ifs.close();
auto dexHeader = reinterpret_cast<DexHeader*>(rawDexBuf);
auto ***iHeader = reinterpret_cast<***iHeader*>(rawDexBuf + dexHeader->dataOff + dexHeader->dataSize);
auto ***iDexItems = ***iHeader->items;
auto dexCount = ***iHeader->dexCount;
auto dexBasePtr = reinterpret_cast<u4>(***iHeader) + ***iHeader->dataOff;
for (size_t dexIndex = 0; dexIndex < dexCount; dexIndex++)
{
auto  dexPtr = reinterpret_cast<u1*>(dexBasePtr + ***iDexItems[dexIndex].dexOff);
for (size_t i = 0; i < 9; ++i)
{
dexPtr[i] ^= dexIndex + i + dexCount + 2;
}
std::ofstream ofs("classes_" + std::to_string(dexIndex) + ".dex", std::ofstream::binary);
ofs.write(reinterpret_cast<char*>(dexPtr), ***iDexItems[dexIndex].dexSize);
ofs.close();
}
return 0;
}

脱壳后

745332_B3K7F94R78TFAWY.jpg

【公告】欢迎大家踊跃尝试高研班11月试题,挑战自己的极限!

最后于 4天前 被kanxue编辑 ,原因:

上传的附件:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK