[原创]**加固分析
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。
用ida加载kadp_armeabi,发现没有.init_array段,于是直接看JNI_OnLoad函数,代码很简单,只调用了init函数
init函数的代码也很简单,判断了下sdk版本和vm版本,然后注册了两个native方法
看下off_2C124的数据,注册了attachBaseContext和onCreate这两个方法
接着看native_attachBaseContext,首先调用了init_class
init_class函数中获取了一些字段和方法的id进行缓存
回到native_attachBaseContext,获取dalvik.system.DexFile的构造方法缓存起来,然后调用函数extractDexToMemMap_zlib从apk获取dex文件,接着调用函数cfile_init对dex进行解析
函数extractDexToMemMap_zlib通过zlib库遍历apk中的文件,获取classes.dex文件并mmap
函数cfile_init先判断文件类型,找到dex的基址,计算dex所属内存页,修改属性,用于后面解密直接修改
然后解析一些用于解密dex的字段
dex中用于解密的数据从dex的data数据之后开始,即起始位置的偏移为 dex_header.data_size+dex_header.data_off
结构如下:
magic字段内容如下,不匹配则解析失败
dex文件中的实际数据如下,
0x2e60处是magic
0x2e68处是dexCount,所以加密的dex文件数量为10个
0x2e6c处是dataOff,所以加密的dex数据起始位置为0x2e60+0x60=0x2ec0
0x2e70处是第一个***iDexItem,所以第一个加密的dex文件大小为0x8f84f8,起始位置为0x2ec0+0=0x2ec0
继续回到native_attachBaseContext,重新构造dexElements并替换,然后多线程执行函数thread_loadDex
函数thread_loadDex中主要调用mem_loadDex
函数mem_loadDex中首先调用函数cfile_load_file
函数cfile_load_file调用函数decrypt_buf进行dex解密
函数decrypt_buf的解密很简单,dex只有前9字节被加密了,算法是一个简单的异或
回到函数mem_loadDex中,调用write_mix_dex、openmemory_load_dex和load_dex_by_byteBuffer进行dex加载,
由于我的设备是sdk27,这几个函数中实际只有load_dex_by_byteBuffer这个函数有用。
然后通过make_dex_elements将DexFile添加到列表中
dex加载完成后,继续回到native_attachBaseContext,
首先通过reback_prot恢复内存页属性
然后通过old_application获取原始application的类名,原始application的类名作为metaData存放在AndroidManifest.xml,name为KWS_MAIN_APP
然后就是app相关的字段替换,就不分析了
脱壳代码
#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;
}
脱壳后
上传的附件:
- src.cpp (1.86kb,18次下载)
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK