几维加固分析(学习加固版本)

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

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

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

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

分析

老规矩,先把apk用jadx反编译,从AndroidManifest.xml找到application的类com.kiwivm.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处是第一个KiwiDexItem,所以第一个加密的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 KiwiDexItem {
    u4 dexSize;
    u4 dexOff;
};
 
struct KiwiHeader {
    u1 magic[8];
    u4 dexCount;
    u4 dataOff;
    KiwiDexItem 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 kiwiHeader = reinterpret_cast<KiwiHeader*>(rawDexBuf + dexHeader->dataOff + dexHeader->dataSize);
    auto kiwiDexItems = kiwiHeader->items;
    auto dexCount = kiwiHeader->dexCount;
    auto dexBasePtr = reinterpret_cast<u4>(kiwiHeader) + kiwiHeader->dataOff;
 
    for (size_t dexIndex = 0; dexIndex < dexCount; dexIndex++)
    {
        auto  dexPtr = reinterpret_cast<u1*>(dexBasePtr + kiwiDexItems[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), kiwiDexItems[dexIndex].dexSize);
        ofs.close();
    }
 
    return 0;
}


脱壳后

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Ackley函数是一个常用的多峰优化问题的测试函数,它在数学领域被称为Ackley函数。 Ackley函数的维度可以是任意的。一般而言,Ackley函数可以用于二维或更高维度的优化问题。在二维情况下,Ackley函数通常被用来测试优化算法的性能和效果。然而,Ackley函数也可以被扩展到更高维度的情况下,以满足其他优化问题的需求。 无论是二维还是更高维的情况下,Ackley函数的目标是寻找函数的最小值点。这个函数包含了多个局部最小值点和一个全局最小值点,对优化算法来说是一个具有挑战性的问题。 总之,Ackley函数的维度是可以灵活设定的,可以根据具体问题的需求来选择合适的维度。 ### 回答2: Ackley函数是一个经典的优化问题的测试函数,常用于评估优化算法的性能。Ackley函数的输入是一个n维的向量,因此可以说Ackley函数是n维的。 Ackley函数的表达式为: f(x) = -a * exp(-b * sqrt((1/n) * sum(xi^2))) - exp((1/n) * sum(cos(c * xi))) + a + exp(1) 其中,n表示向量的维度,xi表示向量中的第i个分量,a、b、c都是常数。 根据函数表达式可以看出,Ackley函数的自变量是一个n维向量,函数的计算过程需要对向量的每个分量进行运算。因此,我们可以得出Ackley函数是n维的结论。 在实际的优化问题中,通常选择不同维度的Ackley函数来评估优化算法的性能,以便能够更全面地评估算法对复杂问题的处理能力。因此,当我们提到Ackley函数时,需要明确指定其维度,如2维Ackley函数、3维Ackley函数等。 ### 回答3: Ackley函数是一个多变量函数,通常用来评估优化算法的性能。该函数的定义是一个复杂的数学公式,用来模拟复杂的非线性问题。Ackley函数的维度是n维,其中n表示自变量的个数。即使在低维度下,Ackley函数也会表现出复杂的行为,因此在高维度下难以找到全局最优解,这使它成为了一个经典的优化问题。对于Ackley函数来说,维度的增加会增加问题的难度,且维度越高,全局最优解越难以找到。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值