最近找到一份apk加固样本,粗略的看了下,确定是某搜索引擎的加固,并且部分函数带了DexVMP,可以参考以下大佬的文章
某企业加固dex vmp简单分析
某企业壳逆向分析——从过检测到dex代码抽取还原
简单分析onCreate
nmmp基于dex-vm运行dalvik字节码从而对dex进行保护
老套路,先Hook linker call_array,判断加载到哪个so闪退

观察Logcat,发现退出会打印 XOX: state=545

JNI_OnLoad 被加密,第三个init_array函数出现调用未解密的函数,由此判断解密的逻辑在前两个init_array函数


明显能看出,第一个函数在做一些偏移计算,并且sub_9F090调用syscall mprotect,这是要修改内存里的代码、数据的前提条件


sub_9F130 解密data段,依次XOR 67 69 BD E7 11 D1 54 3C ,v2起始地址,qword_D0010对应的是长度


sub_9B7D4 对一个区域的代码做刷新缓存,让解密后的代码/数据生效(D-cache),Hook sub_9B7D4打印参数一、参数二,出现两次结果,先看第一次


0xad080 是data段 起始地址,那么0xb5898对应的是结束地址,


sub_9F370 插了很多不透明谓词,把代码段的解密、修补、权限切换、cache flush 打散进状态机


这么一看,还原成算法有点麻烦,不过手里有数据段跟代码段需要解密的起始地址跟长度,在执行完sub_9F370,把这两段给dump下来
dump下来的数据段、代码段,在010编辑器找到对应的偏移,覆盖掉,图下前后对比


修复完的so,直接打开,不需要sofix(假如内存dump整个so,再sofix,会出现写内存地址,导入函数识别错误的情况)JNI_OnLoad能正常查看

字符串表多了一大堆十六进制的字符串

随便挑一个字符串引用的地方,发现统一当做参数1来传给xxx函数

malloc申请对应的大小内存,byte_AE4B9当做key,循环按位异或解密出字符串,算法很简单,但是有很多个字符串跟秘钥,只能用ida python来匹配出字符串+秘钥的组合再模拟解密

粗略的看下,发现解密算法都一样,唯独key不一样,对应的汇编指令也一样,取下面这组指令,在.text段从头到尾匹配一遍
大概的逻辑是
1、匹配包含算法特征的函数
2、函数匹配出key地址
3、查找当前函数引用
4、匹配待解密字符串地址
这样就能解密出大部分的字符串

开头有提到Logcat 打印的XOX: state=545,在字符串表搜索state=,随便找个引用的地方,一目了然,甚至直接打印对应的数字,这就好办,直接搜索545这个常数

有三处地方打印了545,都在同一个函数

发现sub_30310里面全是检测Frida的特征,Interceptor.replace该函数发现应用不闪退了


分析JNI_OnLoad,发现一堆不透明谓词混淆,根据混淆设定,这些判断条件永远都不执行,因为dword_D7ED0所在可读可写区域,ida不清楚它会是什么值,所以伪代码将这些垃圾代码保留,让用户自己去判断,这种情况要么将dword_D7ED0所在的段修改成只读不可写,或者手动patch 0

patch完,ida F5重新分析函数,JNI_Onload变得清新脱俗

用yang佬的dump dex 直接dump出所有dex(会dump出一些奇奇怪怪的)
yang佬的dump dex脚本
详细的分析过程就不写了,太长了,感觉单独出一篇也够了,索性直接列出
这些文件开头都有共同的特征,14 94 B5 35

他没有调用assets open,而是打开/data/app/xxx/base.apk,然后按照zip 的方式提取出需要的项目

提取出来的文件,需要经过一次解密,把头部0x100(d.jar是0x200)给解密还原成zlib正确的文件头,准确来说

已知 key 是0x10个字节,固定存放在baiduprotect.md,直接dump g_payload_index_header,让AI给生成对应的解密算法

GPT5.5 一拳下去,裤子都给打掉,太强了(代码在文章末尾)

OnCreate原本的函数体变成AB.v调用,不同的函数,参数一的数值不一样,估计就是用这个来区分vmp method


AB.v 函数动态注册指向0x4e020
段落引用
[RegisterNatives] java_class: com.sagittarius.v6.AB name: v sig: (ILjava/lang/Object;[Ljava/lang/Object;)V fnPtr: 0x78a6d27020 module_name: libbaiduprotect.so module_base: 0x78a6cd9000 offset: 0x4e020
方便后续分析,先trace一份日志,推荐几个大佬写的trace项目
db5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6*7k6%4V1H3P5o6l9I4i4K6u0r3f1g2c8J5j5h3y4W2
43dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6D9K9h3c8G2L8X3N6&6L8$3!0G2i4K6u0r3c8%4g2E0g2s2u0S2j5$3f1`. 文章用的这个
trace过程会报错中断,需要修改transform_callback,跳过报错指令,不然trace不下去
trace完才13M,估计是原本的函数代码也没多少

0x4e020也是很简单的直接调用,这里因为堆栈的原因,ida识别参数错误了,应该后面不是0 0 0 0,而是i2的低16位,跟obj objarr等

g_vmp_context_by_id[256]是已经初始化好的上下文,来源 baiduprotect1.d.jar,跟dex的方式一样,提取、解密头0x200字节、zlib解压,最后vmp_load_context_from_dex_and_register_bridge解析

进来就看到解析Dex头部,解析出一堆Dex 字段偏移地址

大概还原下返回的结构体,主要method_ids、class_defs、field_ids、string_ids,因为ins 转换成smali是需要用上这些信息
while循环将初始化的 context 塞进 vmp_context 数组里

回到vmp_execute_method,参数一命名为vmp_ctx,参数二是method_id,其他都是Java层传进来的参数,首先一进来会把JNI参数给压到栈里,最终的handler会去使用

Tips:DexVmp 最后都逃不掉JNI的 反射调用(除if等逻辑运算,所以那些dexvmp dump系统用的jni trace也只能还原一部分call调用)
vmp_interpreter_enter 是最终分发 opcode handler的函数,这是强制让ida 识别为Switch的Graph图

大概还原下vmp_interpreter_enter参数3 method_desc 的结构体
从g_vmp_base_handler_table 取handler,间接跳转过去

g_vmp_base_handler_table 并不是按照默认的顺序,第一次调用会根据vmp_ctx 进行一次重排


Frida hook vmp_build_runtime_handler_table,循环打印g_vmp_base_handler_table 256次,取指针减去libso base地址,然后patch到ida
重排后的前后对比


当重排handler表,看第一次分发的指令对应的handler,第一次在0x59FF0

0x59FF0在日志只出现一次,它并不是循环解析来调用,类似控制PC,或者goto的形式,第一次调用的是0x60bcc,0x60bcc在handler表排第151(0x97)

0x59f9c 取x26寄存器的两个字节到w22,即0x2097
0x59fa0 add x8, x16, w22, uxtb #3; w22取低8位,即0x97,对应了上面的0x60bcc

0x789bb1ef02 取值来自 [x19, #0x18],对上了 uint16_t *insns; // +0x18, VMP 指令起始地址

Frida hook vmp_interpreter_enter 打印出当前函数所有ins

97 20 对上符合 trace日志,注意,目前还是DEX CodeItem 里的 Dalvik 字节码(可能混淆加密过的)

一进来看到各种JNI GetMethodID,这时候就可以大胆推测是模拟 invoke-***

dex_get_method_ref_info(a1, method_idx, out_class_idx, out_class_desc, out_name, out_sig, out_return_type) 结合AI给的注释,Frida hook 打印一下,基本确定就是invoke-*** ,并且只改了opcode,method_idx 1486也对得上dex里的method[1486]


先看下正常的Dalvik 字节码格式跟smali的关系
[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。