本帖最后由 linglian 于 2026-6-6 13:25 编辑
前言
本人小白一枚,想尝试学习一些加固,在身边朋友的推荐下选择了这个壳子.恰巧最近论坛上也有很多大佬发布了自己的分析贴(比如乐子人大佬的帖子),只是复现下来后发现后续的dex vmp相关帖子比较少(很不幸只找到了爱吃菠菜大佬的这篇帖子,不过因为年代久远甚至指令结构都不同了),于是写篇帖子记录一下.
新人发帖,如有错误还望大佬指出.
帖子使用的样本不方便说出来,不过是今年的.(因为据说这个样本保护项开得多)
Java层特征
被vmp保护的方法调用了Lcom/fort/andjni/JniLib;类的一个native方法
JniLib类中有很多种不同的native函数,显然是通过不同的函数签名来处理不同返回值的跳板函数. 既然这个类加载了libdexjni.so,那么这些native函数大概率就是被这个so注册的.
Native层初探
通过BinaryNinja加载so(这里把so基址设为了0x12000000),尝试在函数表中搜索Java_来定位,发现没有结果.
扫了一眼导出表
虽然有些明显的名称混淆,但JNI_OnLoad这些关键函数还在,导出表应该是没有被保护的.
既然静态注册搜不到,那么这些native函数必然是动态注册的了.
直接来到JNI_OnLoad:
发现函数不可解析,应该是被保护了.
往上查找.init.array:
对应的地址完全是空的,那么只可能是在更早执行的.init进行解密了.
先从dynamic里找到init地址
sub_125617dc分配了一块内存并复制了arg2的数据,数据大小被存储在了arg1 + 0x28.
而sub_1256150c主要是进行了数据解压(疑似还有其他操作,但我这个样本并没有执行到,就没去分析):
解压算法是nrv2d,以上数据均来自init函数最开始的参数0x12561b48:
具体逻辑搞清楚了,修复就简单了.可选择:
1.直接使用支持nrv2d的库对0x120096d8处大小0x33dfd的数据进行解压;
2.在init函数运行完后直接dump下0x120096d8处大小0x7e500的数据.
得到解压数据后直接塞回so文件对应位置即可.
这里需要补充一段,完成这步之后BinaryNinja已经可以正常解析函数了,但是后面在使用ida分析so时发现虽然导入表和导出表正确,但ida未能将符号对应的函数解析完整:
简单分析发现这是因为这个so的section被动了手脚:
节中将一个很大的区域标记成SHT_NOBITS扰乱了ida的分析,只需把type改成SHT_NULL(0)或者SHT_PROGBITS(1)即可使ida正常解析.
虚拟机入口分析
解压数据回填后,JNI_OnLoad已经可以正常反编译了,映入眼帘的便是三个函数调用.
挨个翻了一下,发现前两个函数都有很明显的控制流:
而第三个函数看上去非常清晰,并且一眼就能看到很多关键数据(为了方便直观这里稍微处理了一下):
既然前两个函数不太好看,那么就先分析第三个函数好了.
这个函数很简单,首先sub_12010028初始化了一些全局对象:
初始化完成后就注册了JniLib类的native函数:
既然要找的函数已经在这了,不妨直接进去看看吧:
sub_12017adc就是虚拟机的入口了,这里命名为vm_entrance.
由上图不难看出,这个函数的签名大概是(JNIEnv* env, jobjectArray args, void* result)
到了这里,先回顾一下函数的入参:
jobjectArray第0位是jclass,若为实例方法则第1位是实例的jobject,最后一位jint是vm函数的idx.
入口函数先获取了jobjectArray最后一位的Integer,然后调用了intValue函数读到jint传给j__lSSO05lI$I5IllOl_0lS$_lllIII__l$IOlI55$O550IS__lS5$:
而这个函数通过vm_idx去查了表data_1255ff48,因此这个表里一定存储着所有vm方法的数据.
交叉引用可以看到另一个函数对这个表进行了很多操作,应该就是初始化了:
至于这个函数,我们在上面的流程里其实已经见过了,正是JNI_OnLoad调用的第一个函数.
既然终究是躲不过的话,就只好顶着控制流简单看一下喽:
这个表里的结构体大小是0x28,通过一些函数读取数据,读取格式是MessagePack:
这里偷懒用frida在这个函数初始化完后去遍历表data_1255ff48(这里只处理了libDexHelper.so的检测,libdexjni.so应该是没有检测的),根据打印出的数据,大概猜测结构体结构:
[C] 纯文本查看 复制代码 struct method_info __packed
{
uint32_t idx;
uint32_t code_item_size;
void* code_item;
uint32_t reg_count;
uint32_t ins;
uint32_t outs;
uint32_t is_static;//这个没去验证,纯经验猜测
char* shorty;
};
*注:这里的code_item是我先入为主瞎取的名字,并不是真正的标准code_item,因为这个vmp的字节码很像code_item的结构,具体长这样:
而code_item_size则是这段字节码数据的size.
后面会对其进行详细分析,这个命名我感觉还是比较贴合的,就是有点容易混淆,因此提一嘴.
重新回到vm_entrance函数,可以看到在栈上重新排了一个结构体:
最终传入了函数sub_12018544.
sub_12018544大概扫了眼,应该是整理vm方法的参数,给包装类拆箱:
这里创建了一个大小0x48的结构体,与寄存器有关,偏移0x8处的列表用于存储寄存器
最终来到函数j__lIl_0ll5OIlIlISl00lI_lI5lIll$I$IIlIl00I$lIIIl5S0S5$
跳到这个函数后binaryninja就有点不好使了,加载了半天也没加载好,控制流混淆得很严重:
并且函数体量奇大,根据以上种种,不难猜到这个就是解释器函数了.
面对这一望无际的工作量,死磕明显不太现实.
尝试过写反混淆插件,但只能处理些简单的,效果不尽人意,若要兼容更多情况,插件工程量也相当大.
反混淆无果后,决定依靠trace. 试了下frida trace感觉不太稳定,经常会闪退,最后选择了unidbg trace.
虚拟机解释器分析
简单看了一下,这个so的依赖相当少,甚至没有对libDexHepler.so的依赖:
因此unidbg应该不难运行起来,事实也的确如此:
[Plain Text] 纯文本查看 复制代码 JNIEnv->FindClass([Z) was called from RWX@0x120170e4[libdexjni.so]0x170e4
JNIEnv->NewGlobalRef(class [Z) was called from RWX@0x120170f8[libdexjni.so]0x170f8
JNIEnv->FindClass([B) was called from RWX@0x12014e44[libdexjni.so]0x14e44
JNIEnv->NewGlobalRef(class [B) was called from RWX@0x12014e58[libdexjni.so]0x14e58
JNIEnv->FindClass([S) was called from RWX@0x1201503c[libdexjni.so]0x1503c
JNIEnv->NewGlobalRef(class [S) was called from RWX@0x12015050[libdexjni.so]0x15050
JNIEnv->FindClass([C) was called from RWX@0x12014000[libdexjni.so]0x14000
JNIEnv->NewGlobalRef(class [C) was called from RWX@0x12014014[libdexjni.so]0x14014
JNIEnv->FindClass([I) was called from RWX@0x12010320[libdexjni.so]0x10320
JNIEnv->NewGlobalRef(class [I) was called from RWX@0x12010334[libdexjni.so]0x10334
JNIEnv->FindClass([J) was called from RWX@0x12010628[libdexjni.so]0x10628
JNIEnv->NewGlobalRef(class [J) was called from RWX@0x1201063c[libdexjni.so]0x1063c
JNIEnv->FindClass([F) was called from RWX@0x12010494[libdexjni.so]0x10494
JNIEnv->NewGlobalRef(class [F) was called from RWX@0x120104a8[libdexjni.so]0x104a8
JNIEnv->FindClass([D) was called from RWX@0x120112b4[libdexjni.so]0x112b4
JNIEnv->NewGlobalRef(class [D) was called from RWX@0x120112c8[libdexjni.so]0x112c8
JNIEnv->FindClass(java/lang/Class) was called from RWX@0x120112f8[libdexjni.so]0x112f8
JNIEnv->NewGlobalRef(class java/lang/Class) was called from RWX@0x1201130c[libdexjni.so]0x1130c
JNIEnv->FindClass(java/lang/Object) was called from RWX@0x12010574[libdexjni.so]0x10574
JNIEnv->NewGlobalRef(class java/lang/Object) was called from RWX@0x12010588[libdexjni.so]0x10588
JNIEnv->FindClass(java/lang/reflect/Field) was called from RWX@0x120105e4[libdexjni.so]0x105e4
JNIEnv->NewGlobalRef(class java/lang/reflect/Field) was called from RWX@0x120105f8[libdexjni.so]0x105f8
JNIEnv->FindClass(java/lang/String) was called from RWX@0x12015fb8[libdexjni.so]0x15fb8
JNIEnv->NewGlobalRef(class java/lang/String) was called from RWX@0x12015fcc[libdexjni.so]0x15fcc
JNIEnv->FindClass(java/lang/reflect/Proxy) was called from RWX@0x12015d14[libdexjni.so]0x15d14
JNIEnv->NewGlobalRef(class java/lang/reflect/Proxy) was called from RWX@0x12015d28[libdexjni.so]0x15d28
JNIEnv->FindClass(java/lang/reflect/AccessibleObject) was called from RWX@0x120100d4[libdexjni.so]0x100d4
JNIEnv->NewGlobalRef(class java/lang/reflect/AccessibleObject) was called from RWX@0x120100e8[libdexjni.so]0x100e8
JNIEnv->FindClass(com/fort/andjni/JniLib) was called from RWX@0x12010734[libdexjni.so]0x10734
JNIEnv->NewGlobalRef(class com/fort/andjni/JniLib) was called from RWX@0x12010748[libdexjni.so]0x10748
JNIEnv->FindClass(java/lang/Boolean) was called from RWX@0x12014f24[libdexjni.so]0x14f24
JNIEnv->NewGlobalRef(class java/lang/Boolean) was called from RWX@0x12014f38[libdexjni.so]0x14f38
JNIEnv->FindClass(java/lang/Byte) was called from RWX@0x12014ea0[libdexjni.so]0x14ea0
JNIEnv->NewGlobalRef(class java/lang/Byte) was called from RWX@0x12014eb4[libdexjni.so]0x14eb4
JNIEnv->FindClass(java/lang/Character) was called from RWX@0x12014dd0[libdexjni.so]0x14dd0
JNIEnv->NewGlobalRef(class java/lang/Character) was called from RWX@0x12014de4[libdexjni.so]0x14de4
JNIEnv->FindClass(java/lang/Short) was called from RWX@0x1201428c[libdexjni.so]0x1428c
JNIEnv->NewGlobalRef(class java/lang/Short) was called from RWX@0x120142a0[libdexjni.so]0x142a0
JNIEnv->FindClass(java/lang/Integer) was called from RWX@0x120106a8[libdexjni.so]0x106a8
JNIEnv->NewGlobalRef(class java/lang/Integer) was called from RWX@0x120106bc[libdexjni.so]0x106bc
JNIEnv->FindClass(java/lang/Long) was called from RWX@0x120107c8[libdexjni.so]0x107c8
JNIEnv->NewGlobalRef(class java/lang/Long) was called from RWX@0x120107dc[libdexjni.so]0x107dc
JNIEnv->FindClass(java/lang/Float) was called from RWX@0x12010364[libdexjni.so]0x10364
JNIEnv->NewGlobalRef(class java/lang/Float) was called from RWX@0x12010378[libdexjni.so]0x10378
JNIEnv->FindClass(java/lang/Double) was called from RWX@0x12015f4c[libdexjni.so]0x15f4c
JNIEnv->NewGlobalRef(class java/lang/Double) was called from RWX@0x12015f60[libdexjni.so]0x15f60
JNIEnv->FindClass(java/lang/AbstractMethodError) was called from RWX@0x1201357c[libdexjni.so]0x1357c
JNIEnv->NewGlobalRef(class java/lang/AbstractMethodError) was called from RWX@0x12013590[libdexjni.so]0x13590
JNIEnv->FindClass(java/lang/ArithmeticException) was called from RWX@0x120170a0[libdexjni.so]0x170a0
JNIEnv->NewGlobalRef(class java/lang/ArithmeticException) was called from RWX@0x120170b4[libdexjni.so]0x170b4
JNIEnv->FindClass(java/lang/ArrayIndexOutOfBoundsException) was called from RWX@0x120171c8[libdexjni.so]0x171c8
JNIEnv->NewGlobalRef(class java/lang/ArrayIndexOutOfBoundsException) was called from RWX@0x120171dc[libdexjni.so]0x171dc
JNIEnv->FindClass(java/lang/ArrayStoreException) was called from RWX@0x12017128[libdexjni.so]0x17128
JNIEnv->NewGlobalRef(class java/lang/ArrayStoreException) was called from RWX@0x1201713c[libdexjni.so]0x1713c
JNIEnv->FindClass(java/lang/ClassCastException) was called from RWX@0x12015d88[libdexjni.so]0x15d88
JNIEnv->NewGlobalRef(class java/lang/ClassCastException) was called from RWX@0x12015d9c[libdexjni.so]0x15d9c
JNIEnv->FindClass(java/lang/ClassCircularityError) was called from RWX@0x120101e0[libdexjni.so]0x101e0
JNIEnv->NewGlobalRef(class java/lang/ClassCircularityError) was called from RWX@0x120101f4[libdexjni.so]0x101f4
JNIEnv->FindClass(java/lang/ClassNotFoundException) was called from RWX@0x12011360[libdexjni.so]0x11360
JNIEnv->NewGlobalRef(class java/lang/ClassNotFoundException) was called from RWX@0x12011374[libdexjni.so]0x11374
JNIEnv->FindClass(java/lang/ClassFormatError) was called from RWX@0x1201126c[libdexjni.so]0x1126c
JNIEnv->NewGlobalRef(class java/lang/ClassFormatError) was called from RWX@0x12011280[libdexjni.so]0x11280
JNIEnv->FindClass(java/lang/Error) was called from RWX@0x12015ddc[libdexjni.so]0x15ddc
JNIEnv->NewGlobalRef(class java/lang/Error) was called from RWX@0x12015df0[libdexjni.so]0x15df0
JNIEnv->FindClass(java/lang/ExceptionInInitializerError) was called from RWX@0x12014d0c[libdexjni.so]0x14d0c
JNIEnv->NewGlobalRef(class java/lang/ExceptionInInitializerError) was called from RWX@0x12014d20[libdexjni.so]0x14d20
JNIEnv->FindClass(java/io/FileNotFoundException) was called from RWX@0x12017184[libdexjni.so]0x17184
JNIEnv->NewGlobalRef(class java/io/FileNotFoundException) was called from RWX@0x12017198[libdexjni.so]0x17198
JNIEnv->FindClass(java/io/IOException) was called from RWX@0x120108d8[libdexjni.so]0x108d8
JNIEnv->NewGlobalRef(class java/io/IOException) was called from RWX@0x120108ec[libdexjni.so]0x108ec
JNIEnv->FindClass(java/lang/IllegalAccessError) was called from RWX@0x12015e78[libdexjni.so]0x15e78
JNIEnv->NewGlobalRef(class java/lang/IllegalAccessError) was called from RWX@0x12015e8c[libdexjni.so]0x15e8c
JNIEnv->FindClass(java/lang/IllegalAccessException) was called from RWX@0x12013520[libdexjni.so]0x13520
JNIEnv->NewGlobalRef(class java/lang/IllegalAccessException) was called from RWX@0x12013534[libdexjni.so]0x13534
JNIEnv->FindClass(java/lang/IllegalArgumentException) was called from RWX@0x12013fa4[libdexjni.so]0x13fa4
JNIEnv->NewGlobalRef(class java/lang/IllegalArgumentException) was called from RWX@0x12013fb8[libdexjni.so]0x13fb8
JNIEnv->FindClass(java/lang/IllegalMonitorStateException) was called from RWX@0x120102bc[libdexjni.so]0x102bc
JNIEnv->NewGlobalRef(class java/lang/IllegalMonitorStateException) was called from RWX@0x120102d0[libdexjni.so]0x102d0
JNIEnv->FindClass(java/lang/IllegalStateException) was called from RWX@0x1201414c[libdexjni.so]0x1414c
JNIEnv->NewGlobalRef(class java/lang/IllegalStateException) was called from RWX@0x12014160[libdexjni.so]0x14160
JNIEnv->FindClass(java/lang/IllegalThreadStateException) was called from RWX@0x12014f68[libdexjni.so]0x14f68
JNIEnv->NewGlobalRef(class java/lang/IllegalThreadStateException) was called from RWX@0x12014f7c[libdexjni.so]0x14f7c
JNIEnv->FindClass(java/lang/IncompatibleClassChangeError) was called from RWX@0x120140c4[libdexjni.so]0x140c4
JNIEnv->NewGlobalRef(class java/lang/IncompatibleClassChangeError) was called from RWX@0x120140d8[libdexjni.so]0x140d8
JNIEnv->FindClass(java/lang/InstantiationError) was called from RWX@0x12010230[libdexjni.so]0x10230
JNIEnv->NewGlobalRef(class java/lang/InstantiationError) was called from RWX@0x12010244[libdexjni.so]0x10244
JNIEnv->FindClass(java/lang/InstantiationException) was called from RWX@0x12014044[libdexjni.so]0x14044
JNIEnv->NewGlobalRef(class java/lang/InstantiationException) was called from RWX@0x12014058[libdexjni.so]0x14058
JNIEnv->FindClass(java/lang/InternalError) was called from RWX@0x1201433c[libdexjni.so]0x1433c
JNIEnv->NewGlobalRef(class java/lang/InternalError) was called from RWX@0x12014350[libdexjni.so]0x14350
JNIEnv->FindClass(java/lang/InterruptedException) was called from RWX@0x120104e8[libdexjni.so]0x104e8
JNIEnv->NewGlobalRef(class java/lang/InterruptedException) was called from RWX@0x120104fc[libdexjni.so]0x104fc
JNIEnv->FindClass(java/lang/LinkageError) was called from RWX@0x12010868[libdexjni.so]0x10868
JNIEnv->NewGlobalRef(class java/lang/LinkageError) was called from RWX@0x1201087c[libdexjni.so]0x1087c
JNIEnv->FindClass(java/lang/NegativeArraySizeException) was called from RWX@0x12010920[libdexjni.so]0x10920
JNIEnv->NewGlobalRef(class java/lang/NegativeArraySizeException) was called from RWX@0x12010934[libdexjni.so]0x10934
JNIEnv->FindClass(java/lang/NoClassDefFoundError) was called from RWX@0x12010530[libdexjni.so]0x10530
JNIEnv->NewGlobalRef(class java/lang/NoClassDefFoundError) was called from RWX@0x12010544[libdexjni.so]0x10544
JNIEnv->FindClass(java/lang/NoSuchFieldError) was called from RWX@0x12014d74[libdexjni.so]0x14d74
JNIEnv->NewGlobalRef(class java/lang/NoSuchFieldError) was called from RWX@0x12014d88[libdexjni.so]0x14d88
JNIEnv->FindClass(java/lang/NoSuchFieldException) was called from RWX@0x120103e4[libdexjni.so]0x103e4
JNIEnv->NewGlobalRef(class java/lang/NoSuchFieldException) was called from RWX@0x120103f8[libdexjni.so]0x103f8
JNIEnv->FindClass(java/lang/NoSuchMethodError) was called from RWX@0x12014fac[libdexjni.so]0x14fac
JNIEnv->NewGlobalRef(class java/lang/NoSuchMethodError) was called from RWX@0x12014fc0[libdexjni.so]0x14fc0
JNIEnv->FindClass(java/lang/NullPointerException) was called from RWX@0x1201080c[libdexjni.so]0x1080c
JNIEnv->NewGlobalRef(class java/lang/NullPointerException) was called from RWX@0x12010820[libdexjni.so]0x10820
JNIEnv->FindClass(java/lang/OutOfMemoryError) was called from RWX@0x12013604[libdexjni.so]0x13604
JNIEnv->NewGlobalRef(class java/lang/OutOfMemoryError) was called from RWX@0x12013618[libdexjni.so]0x13618
JNIEnv->FindClass(java/lang/RuntimeException) was called from RWX@0x120142d0[libdexjni.so]0x142d0
JNIEnv->NewGlobalRef(class java/lang/ |