返回列表 发新帖

【原创教程】某企业加固dex vmp简单分析

  [复制链接]

7

主题

1496

回帖

6981

积分

硕士生

Rank: 6Rank: 6

金币
530
好评
22
信誉
112
发表于 2026-6-6 12:24:34 | 显示全部楼层 | 阅读模式  来自 云南
本帖最后由 秋菱 于 2026-6-6 20:12 编辑

前言
本人小白一枚,想尝试学习一些加固,在身边朋友的推荐下选择了这个壳子.恰巧最近论坛上也有很多大佬发布了自己的分析贴(比如乐子人大佬的帖子),只是复现下来后发现后续的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应该是没有检测的),根据打印出的数据,大概猜测结构体结构:
  1. struct method_info __packed
  2. {
  3.     uint32_t idx;
  4.     uint32_t code_item_size;
  5.     void* code_item;
  6.     uint32_t reg_count;
  7.     uint32_t ins;
  8.     uint32_t outs;
  9.     uint32_t is_static;//这个没去验证,纯经验猜测
  10.     char* shorty;
  11. };
复制代码
*注:这里的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应该不难运行起来,事实也的确如此:
  1. JNIEnv->FindClass([Z) was called from RWX@0x120170e4[libdexjni.so]0x170e4
  2. JNIEnv->NewGlobalRef(class [Z) was called from RWX@0x120170f8[libdexjni.so]0x170f8
  3. JNIEnv->FindClass([B) was called from RWX@0x12014e44[libdexjni.so]0x14e44
  4. JNIEnv->NewGlobalRef(class [B) was called from RWX@0x12014e58[libdexjni.so]0x14e58
  5. JNIEnv->FindClass([S) was called from RWX@0x1201503c[libdexjni.so]0x1503c
  6. JNIEnv->NewGlobalRef(class [S) was called from RWX@0x12015050[libdexjni.so]0x15050
  7. JNIEnv->FindClass([C) was called from RWX@0x12014000[libdexjni.so]0x14000
  8. JNIEnv->NewGlobalRef(class [C) was called from RWX@0x12014014[libdexjni.so]0x14014
  9. JNIEnv->FindClass([I) was called from RWX@0x12010320[libdexjni.so]0x10320
  10. JNIEnv->NewGlobalRef(class [I) was called from RWX@0x12010334[libdexjni.so]0x10334
  11. JNIEnv->FindClass([J) was called from RWX@0x12010628[libdexjni.so]0x10628
  12. JNIEnv->NewGlobalRef(class [J) was called from RWX@0x1201063c[libdexjni.so]0x1063c
  13. JNIEnv->FindClass([F) was called from RWX@0x12010494[libdexjni.so]0x10494
  14. JNIEnv->NewGlobalRef(class [F) was called from RWX@0x120104a8[libdexjni.so]0x104a8
  15. JNIEnv->FindClass([D) was called from RWX@0x120112b4[libdexjni.so]0x112b4
  16. JNIEnv->NewGlobalRef(class [D) was called from RWX@0x120112c8[libdexjni.so]0x112c8
  17. JNIEnv->FindClass(java/lang/Class) was called from RWX@0x120112f8[libdexjni.so]0x112f8
  18. JNIEnv->NewGlobalRef(class java/lang/Class) was called from RWX@0x1201130c[libdexjni.so]0x1130c
  19. JNIEnv->FindClass(java/lang/Object) was called from RWX@0x12010574[libdexjni.so]0x10574
  20. JNIEnv->NewGlobalRef(class java/lang/Object) was called from RWX@0x12010588[libdexjni.so]0x10588
  21. JNIEnv->FindClass(java/lang/reflect/Field) was called from RWX@0x120105e4[libdexjni.so]0x105e4
  22. JNIEnv->NewGlobalRef(class java/lang/reflect/Field) was called from RWX@0x120105f8[libdexjni.so]0x105f8
  23. JNIEnv->FindClass(java/lang/String) was called from RWX@0x12015fb8[libdexjni.so]0x15fb8
  24. JNIEnv->NewGlobalRef(class java/lang/String) was called from RWX@0x12015fcc[libdexjni.so]0x15fcc
  25. JNIEnv->FindClass(java/lang/reflect/Proxy) was called from RWX@0x12015d14[libdexjni.so]0x15d14
  26. JNIEnv->NewGlobalRef(class java/lang/reflect/Proxy) was called from RWX@0x12015d28[libdexjni.so]0x15d28
  27. JNIEnv->FindClass(java/lang/reflect/AccessibleObject) was called from RWX@0x120100d4[libdexjni.so]0x100d4
  28. JNIEnv->NewGlobalRef(class java/lang/reflect/AccessibleObject) was called from RWX@0x120100e8[libdexjni.so]0x100e8
  29. JNIEnv->FindClass(com/fort/andjni/JniLib) was called from RWX@0x12010734[libdexjni.so]0x10734
  30. JNIEnv->NewGlobalRef(class com/fort/andjni/JniLib) was called from RWX@0x12010748[libdexjni.so]0x10748
  31. JNIEnv->FindClass(java/lang/Boolean) was called from RWX@0x12014f24[libdexjni.so]0x14f24
  32. JNIEnv->NewGlobalRef(class java/lang/Boolean) was called from RWX@0x12014f38[libdexjni.so]0x14f38
  33. JNIEnv->FindClass(java/lang/Byte) was called from RWX@0x12014ea0[libdexjni.so]0x14ea0
  34. JNIEnv->NewGlobalRef(class java/lang/Byte) was called from RWX@0x12014eb4[libdexjni.so]0x14eb4
  35. JNIEnv->FindClass(java/lang/Character) was called from RWX@0x12014dd0[libdexjni.so]0x14dd0
  36. JNIEnv->NewGlobalRef(class java/lang/Character) was called from RWX@0x12014de4[libdexjni.so]0x14de4
  37. JNIEnv->FindClass(java/lang/Short) was called from RWX@0x1201428c[libdexjni.so]0x1428c
  38. JNIEnv->NewGlobalRef(class java/lang/Short) was called from RWX@0x120142a0[libdexjni.so]0x142a0
  39. JNIEnv->FindClass(java/lang/Integer) was called from RWX@0x120106a8[libdexjni.so]0x106a8
  40. JNIEnv->NewGlobalRef(class java/lang/Integer) was called from RWX@0x120106bc[libdexjni.so]0x106bc
  41. JNIEnv->FindClass(java/lang/Long) was called from RWX@0x120107c8[libdexjni.so]0x107c8
  42. JNIEnv->NewGlobalRef(class java/lang/Long) was called from RWX@0x120107dc[libdexjni.so]0x107dc
  43. JNIEnv->FindClass(java/lang/Float) was called from RWX@0x12010364[libdexjni.so]0x10364
  44. JNIEnv->NewGlobalRef(class java/lang/Float) was called from RWX@0x12010378[libdexjni.so]0x10378
  45. JNIEnv->FindClass(java/lang/Double) was called from RWX@0x12015f4c[libdexjni.so]0x15f4c
  46. JNIEnv->NewGlobalRef(class java/lang/Double) was called from RWX@0x12015f60[libdexjni.so]0x15f60
  47. JNIEnv->FindClass(java/lang/AbstractMethodError) was called from RWX@0x1201357c[libdexjni.so]0x1357c
  48. JNIEnv->NewGlobalRef(class java/lang/AbstractMethodError) was called from RWX@0x12013590[libdexjni.so]0x13590
  49. JNIEnv->FindClass(java/lang/ArithmeticException) was called from RWX@0x120170a0[libdexjni.so]0x170a0
  50. JNIEnv->NewGlobalRef(class java/lang/ArithmeticException) was called from RWX@0x120170b4[libdexjni.so]0x170b4
  51. JNIEnv->FindClass(java/lang/ArrayIndexOutOfBoundsException) was called from RWX@0x120171c8[libdexjni.so]0x171c8
  52. JNIEnv->NewGlobalRef(class java/lang/ArrayIndexOutOfBoundsException) was called from RWX@0x120171dc[libdexjni.so]0x171dc
  53. JNIEnv->FindClass(java/lang/ArrayStoreException) was called from RWX@0x12017128[libdexjni.so]0x17128
  54. JNIEnv->NewGlobalRef(class java/lang/ArrayStoreException) was called from RWX@0x1201713c[libdexjni.so]0x1713c
  55. JNIEnv->FindClass(java/lang/ClassCastException) was called from RWX@0x12015d88[libdexjni.so]0x15d88
  56. JNIEnv->NewGlobalRef(class java/lang/ClassCastException) was called from RWX@0x12015d9c[libdexjni.so]0x15d9c
  57. JNIEnv->FindClass(java/lang/ClassCircularityError) was called from RWX@0x120101e0[libdexjni.so]0x101e0
  58. JNIEnv->NewGlobalRef(class java/lang/ClassCircularityError) was called from RWX@0x120101f4[libdexjni.so]0x101f4
  59. JNIEnv->FindClass(java/lang/ClassNotFoundException) was called from RWX@0x12011360[libdexjni.so]0x11360
  60. JNIEnv->NewGlobalRef(class java/lang/ClassNotFoundException) was called from RWX@0x12011374[libdexjni.so]0x11374
  61. JNIEnv->FindClass(java/lang/ClassFormatError) was called from RWX@0x1201126c[libdexjni.so]0x1126c
  62. JNIEnv->NewGlobalRef(class java/lang/ClassFormatError) was called from RWX@0x12011280[libdexjni.so]0x11280
  63. JNIEnv->FindClass(java/lang/Error) was called from RWX@0x12015ddc[libdexjni.so]0x15ddc
  64. JNIEnv->NewGlobalRef(class java/lang/Error) was called from RWX@0x12015df0[libdexjni.so]0x15df0
  65. JNIEnv->FindClass(java/lang/ExceptionInInitializerError) was called from RWX@0x12014d0c[libdexjni.so]0x14d0c
  66. JNIEnv->NewGlobalRef(class java/lang/ExceptionInInitializerError) was called from RWX@0x12014d20[libdexjni.so]0x14d20
  67. JNIEnv->FindClass(java/io/FileNotFoundException) was called from RWX@0x12017184[libdexjni.so]0x17184
  68. JNIEnv->NewGlobalRef(class java/io/FileNotFoundException) was called from RWX@0x12017198[libdexjni.so]0x17198
  69. JNIEnv->FindClass(java/io/IOException) was called from RWX@0x120108d8[libdexjni.so]0x108d8
  70. JNIEnv->NewGlobalRef(class java/io/IOException) was called from RWX@0x120108ec[libdexjni.so]0x108ec
  71. JNIEnv->FindClass(java/lang/IllegalAccessError) was called from RWX@0x12015e78[libdexjni.so]0x15e78
  72. JNIEnv->NewGlobalRef(class java/lang/IllegalAccessError) was called from RWX@0x12015e8c[libdexjni.so]0x15e8c
  73. JNIEnv->FindClass(java/lang/IllegalAccessException) was called from RWX@0x12013520[libdexjni.so]0x13520
  74. JNIEnv->NewGlobalRef(class java/lang/IllegalAccessException) was called from RWX@0x12013534[libdexjni.so]0x13534
  75. JNIEnv->FindClass(java/lang/IllegalArgumentException) was called from RWX@0x12013fa4[libdexjni.so]0x13fa4
  76. JNIEnv->NewGlobalRef(class java/lang/IllegalArgumentException) was called from RWX@0x12013fb8[libdexjni.so]0x13fb8
  77. JNIEnv->FindClass(java/lang/IllegalMonitorStateException) was called from RWX@0x120102bc[libdexjni.so]0x102bc
  78. JNIEnv->NewGlobalRef(class java/lang/IllegalMonitorStateException) was called from RWX@0x120102d0[libdexjni.so]0x102d0
  79. JNIEnv->FindClass(java/lang/IllegalStateException) was called from RWX@0x1201414c[libdexjni.so]0x1414c
  80. JNIEnv->NewGlobalRef(class java/lang/IllegalStateException) was called from RWX@0x12014160[libdexjni.so]0x14160
  81. JNIEnv->FindClass(java/lang/IllegalThreadStateException) was called from RWX@0x12014f68[libdexjni.so]0x14f68
  82. JNIEnv->NewGlobalRef(class java/lang/IllegalThreadStateException) was called from RWX@0x12014f7c[libdexjni.so]0x14f7c
  83. JNIEnv->FindClass(java/lang/IncompatibleClassChangeError) was called from RWX@0x120140c4[libdexjni.so]0x140c4
  84. JNIEnv->NewGlobalRef(class java/lang/IncompatibleClassChangeError) was called from RWX@0x120140d8[libdexjni.so]0x140d8
  85. JNIEnv->FindClass(java/lang/InstantiationError) was called from RWX@0x12010230[libdexjni.so]0x10230
  86. JNIEnv->NewGlobalRef(class java/lang/InstantiationError) was called from RWX@0x12010244[libdexjni.so]0x10244
  87. JNIEnv->FindClass(java/lang/InstantiationException) was called from RWX@0x12014044[libdexjni.so]0x14044
  88. JNIEnv->NewGlobalRef(class java/lang/InstantiationException) was called from RWX@0x12014058[libdexjni.so]0x14058
  89. JNIEnv->FindClass(java/lang/InternalError) was called from RWX@0x1201433c[libdexjni.so]0x1433c
  90. JNIEnv->NewGlobalRef(class java/lang/InternalError) was called from RWX@0x12014350[libdexjni.so]0x14350
  91. JNIEnv->FindClass(java/lang/InterruptedException) was called from RWX@0x120104e8[libdexjni.so]0x104e8
  92. JNIEnv->NewGlobalRef(class java/lang/InterruptedException) was called from RWX@0x120104fc[libdexjni.so]0x104fc
  93. JNIEnv->FindClass(java/lang/LinkageError) was called from RWX@0x12010868[libdexjni.so]0x10868
  94. JNIEnv->NewGlobalRef(class java/lang/LinkageError) was called from RWX@0x1201087c[libdexjni.so]0x1087c
  95. JNIEnv->FindClass(java/lang/NegativeArraySizeException) was called from RWX@0x12010920[libdexjni.so]0x10920
  96. JNIEnv->NewGlobalRef(class java/lang/NegativeArraySizeException) was called from RWX@0x12010934[libdexjni.so]0x10934
  97. JNIEnv->FindClass(java/lang/NoClassDefFoundError) was called from RWX@0x12010530[libdexjni.so]0x10530
  98. JNIEnv->NewGlobalRef(class java/lang/NoClassDefFoundError) was called from RWX@0x12010544[libdexjni.so]0x10544
  99. JNIEnv->FindClass(java/lang/NoSuchFieldError) was called from RWX@0x12014d74[libdexjni.so]0x14d74
  100. JNIEnv->NewGlobalRef(class java/lang/NoSuchFieldError) was called from RWX@0x12014d88[libdexjni.so]0x14d88
  101. JNIEnv->FindClass(java/lang/NoSuchFieldException) was called from RWX@0x120103e4[libdexjni.so]0x103e4
  102. JNIEnv->NewGlobalRef(class java/lang/NoSuchFieldException) was called from RWX@0x120103f8[libdexjni.so]0x103f8
  103. JNIEnv->FindClass(java/lang/NoSuchMethodError) was called from RWX@0x12014fac[libdexjni.so]0x14fac
  104. JNIEnv->NewGlobalRef(class java/lang/NoSuchMethodError) was called from RWX@0x12014fc0[libdexjni.so]0x14fc0
  105. JNIEnv->FindClass(java/lang/NullPointerException) was called from RWX@0x1201080c[libdexjni.so]0x1080c
  106. JNIEnv->NewGlobalRef(class java/lang/NullPointerException) was called from RWX@0x12010820[libdexjni.so]0x10820
  107. JNIEnv->FindClass(java/lang/OutOfMemoryError) was called from RWX@0x12013604[libdexjni.so]0x13604
  108. JNIEnv->NewGlobalRef(class java/lang/OutOfMemoryError) was called from RWX@0x12013618[libdexjni.so]0x13618
  109. JNIEnv->FindClass(java/lang/RuntimeException) was called from RWX@0x120142d0[libdexjni.so]0x142d0
  110. JNIEnv->NewGlobalRef(class java/lang/RuntimeException) was called from RWX@0x120142e4[libdexjni.so]0x142e4
  111. JNIEnv->FindClass(java/lang/StackOverflowError) was called from RWX@0x1201419c[libdexjni.so]0x1419c
  112. JNIEnv->NewGlobalRef(class java/lang/StackOverflowError) was called from RWX@0x120141b0[libdexjni.so]0x141b0
  113. JNIEnv->GetMethodID(java/lang/Class.getField(Ljava/lang/String;)Ljava/lang/reflect/Field;) => 0xd585d5a3 was called from RWX@0x12015164[libdexjni.so]0x15164
  114. JNIEnv->GetMethodID(java/lang/Class.getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;) => 0x987c467d was called from RWX@0x120152c4[libdexjni.so]0x152c4
  115. JNIEnv->GetMethodID(java/lang/reflect/Field.get(Ljava/lang/Object;)Ljava/lang/Object;) => 0x7abc2552 was called from RWX@0x120155ac[libdexjni.so]0x155ac
  116. JNIEnv->GetMethodID(java/lang/reflect/Field.getBoolean(Ljava/lang/Object;)Z) => 0xfae67c5e was called from RWX@0x12015828[libdexjni.so]0x15828
  117. JNIEnv->GetMethodID(java/lang/reflect/Field.getByte(Ljava/lang/Object;)B) => 0x35776224 was called from RWX@0x12015a80[libdexjni.so]0x15a80
  118. JNIEnv->GetMethodID(java/lang/reflect/Field.getShort(Ljava/lang/Object;)S) => 0xaf19c8c3 was called from RWX@0x12015ce0[libdexjni.so]0x15ce0
  119. JNIEnv->GetMethodID(java/lang/reflect/Field.getChar(Ljava/lang/Object;)C) => 0x50a94c97 was called from RWX@0x12012668[libdexjni.so]0x12668
  120. JNIEnv->GetMethodID(java/lang/reflect/Field.getInt(Ljava/lang/Object;)I) => 0x82f4a366 was called from RWX@0x12012890[libdexjni.so]0x12890
  121. JNIEnv->GetMethodID(java/lang/reflect/Field.getLong(Ljava/lang/Object;)J) => 0xd72cad38 was called from RWX@0x12012ae4[libdexjni.so]0x12ae4
  122. JNIEnv->GetMethodID(java/lang/reflect/Field.getFloat(Ljava/lang/Object;)F) => 0xba8696d6 was called from RWX@0x12012d44[libdexjni.so]0x12d44
  123. JNIEnv->GetMethodID(java/lang/reflect/Field.getDouble(Ljava/lang/Object;)D) => 0xfe53f3d was called from RWX@0x12012fb0[libdexjni.so]0x12fb0
  124. JNIEnv->GetMethodID(java/lang/reflect/Field.set(Ljava/lang/Object;Ljava/lang/Object;)V) => 0xf25f2330 was called from RWX@0x12013270[libdexjni.so]0x13270
  125. JNIEnv->GetMethodID(java/lang/reflect/Field.setBoolean(Ljava/lang/Object;Z)V) => 0xc42cc02e was called from RWX@0x120134f8[libdexjni.so]0x134f8
  126. JNIEnv->GetMethodID(java/lang/reflect/Field.setChar(Ljava/lang/Object;C)V) => 0xb43f5b1f was called from RWX@0x12016270[libdexjni.so]0x16270
  127. JNIEnv->GetMethodID(java/lang/reflect/Field.setByte(Ljava/lang/Object;B)V) => 0x6933f390 was called from RWX@0x120164b8[libdexjni.so]0x164b8
  128. JNIEnv->GetMethodID(java/lang/reflect/Field.setShort(Ljava/lang/Object;S)V) => 0x3c37f3fb was called from RWX@0x12016710[libdexjni.so]0x16710
  129. JNIEnv->GetMethodID(java/lang/reflect/Field.setInt(Ljava/lang/Object;I)V) => 0x256c9664 was called from RWX@0x12016958[libdexjni.so]0x16958
  130. JNIEnv->GetMethodID(java/lang/reflect/Field.setFloat(Ljava/lang/Object;F)V) => 0x9e64b90e was called from RWX@0x12016b9c[libdexjni.so]0x16b9c
  131. JNIEnv->GetMethodID(java/lang/reflect/Field.setLong(Ljava/lang/Object;J)V) => 0xfe28280c was called from RWX@0x12016e10[libdexjni.so]0x16e10
  132. JNIEnv->GetMethodID(java/lang/reflect/Field.setDouble(Ljava/lang/Object;D)V) => 0xe7ac6ddb was called from RWX@0x12017064[libdexjni.so]0x17064
  133. JNIEnv->GetMethodID(java/lang/reflect/Field.setAccessible(Z)V) => 0xcb19eff2 was called from RWX@0x12014440[libdexjni.so]0x14440
  134. JNIEnv->GetMethodID(java/lang/reflect/AccessibleObject.setAccessible(Z)V) => 0xc15280ab was called from RWX@0x120144f0[libdexjni.so]0x144f0
  135. JNIEnv->GetMethodID(java/lang/String.<init>([B)V) => 0xd5642694 was called from RWX@0x1201464c[libdexjni.so]0x1464c
  136. JNIEnv->GetMethodID(java/lang/String.intern()Ljava/lang/String;) => 0x38d73d3 was called from RWX@0x12014714[libdexjni.so]0x14714
  137. JNIEnv->GetMethodID(java/lang/Object.getClass()Ljava/lang/Class;) => 0xb529717c was called from RWX@0x1201495c[libdexjni.so]0x1495c
  138. JNIEnv->GetMethodID(java/lang/Class.getName()Ljava/lang/String;) => 0x4a974877 was called from RWX@0x12014a40[libdexjni.so]0x14a40
  139. JNIEnv->GetStaticMethodID(java/lang/reflect/Proxy.isProxyClass(Ljava/lang/Class;)Z) => 0x7698fdb9 was called from RWX@0x12014cc0[libdexjni.so]0x14cc0
  140. JNIEnv->GetStaticMethodID(com/fort/andjni/JniLib.InvokeObject([Ljava/lang/Object;)Ljava/lang/Object;) => 0xac6fba43 was called from RWX@0x12011700[libdexjni.so]0x11700
  141. JNIEnv->GetStaticMethodID(com/fort/andjni/JniLib.getAllSuperClassesAndInterfaces(Ljava/lang/Class;)[Ljava/lang/Class;) => 0x2c346997 was called from RWX@0x12011b04[libdexjni.so]0x11b04
  142. JNIEnv->GetStaticMethodID(java/lang/Boolean.valueOf(Z)Ljava/lang/Boolean;) => 0x1d8c249f was called from RWX@0x12011cc0[libdexjni.so]0x11cc0
  143. JNIEnv->GetStaticMethodID(java/lang/Byte.valueOf(B)Ljava/lang/Byte;) => 0x25bc8287 was called from RWX@0x12011e40[libdexjni.so]0x11e40
  144. JNIEnv->GetStaticMethodID(java/lang/Character.valueOf(C)Ljava/lang/Character;) => 0xba1ae606 was called from RWX@0x12011fec[libdexjni.so]0x11fec
  145. JNIEnv->GetStaticMethodID(java/lang/Short.valueOf(S)Ljava/lang/Short;) => 0xe91382b0 was called from RWX@0x12012184[libdexjni.so]0x12184
  146. JNIEnv->GetStaticMethodID(java/lang/Integer.valueOf(I)Ljava/lang/Integer;) => 0x8f152ce2 was called from RWX@0x12012338[libdexjni.so]0x12338
  147. JNIEnv->GetStaticMethodID(java/lang/Float.valueOf(F)Ljava/lang/Float;) => 0xdac25823 was called from RWX@0x120137fc[libdexjni.so]0x137fc
  148. JNIEnv->GetStaticMethodID(java/lang/Double.valueOf(D)Ljava/lang/Double;) => 0xca7b90a5 was called from RWX@0x12013984[libdexjni.so]0x13984
  149. JNIEnv->GetStaticMethodID(java/lang/Long.valueOf(J)Ljava/lang/Long;) => 0x1a324bff was called from RWX@0x12013b0c[libdexjni.so]0x13b0c
  150. INFO [com.github.unidbg.spi.Dlfcn] (Dlfcn:31) - Find symbol "dvmDecodeIndirectRef" failed: handle=0x0, LR=RWX@0x12013c8c[libdexjni.so]0x13c8c
  151. INFO [com.github.unidbg.spi.Dlfcn] (Dlfcn:31) - Find symbol "dvmFindStaticFieldHier" failed: handle=0x0, LR=RWX@0x12013e34[libdexjni.so]0x13e34
  152. JNIEnv->GetMethodID(java/lang/Integer.intValue()I) => 0x5d9f068b was called from RWX@0x12013ed8[libdexjni.so]0x13ed8
  153. JNIEnv->GetMethodID(java/lang/Short.intValue()I) => 0xa3b9250d was called from RWX@0x12013f70[libdexjni.so]0x13f70
  154. JNIEnv->GetMethodID(java/lang/Character.charValue()C) => 0x71f71ced was called from RWX@0x12010ae4[libdexjni.so]0x10ae4
  155. JNIEnv->GetMethodID(java/lang/Byte.intValue()I) => 0x4d014b99 was called from RWX@0x12010b80[libdexjni.so]0x10b80
  156. JNIEnv->GetMethodID(java/lang/Boolean.booleanValue()Z) => 0x31f67dab was called from RWX@0x12010d20[libdexjni.so]0x10d20
  157. JNIEnv->GetMethodID(java/lang/Long.longValue()J) => 0x44606195 was called from RWX@0x12010e84[libdexjni.so]0x10e84
  158. JNIEnv->GetMethodID(java/lang/Float.floatValue()F) => 0x6ff98ad7 was called from RWX@0x12010ff8[libdexjni.so]0x10ff8
  159. JNIEnv->GetMethodID(java/lang/Double.doubleValue()D) => 0x8acb0bf9 was called from RWX@0x12011178[libdexjni.so]0x11178
  160. JNIEnv->FindClass(android/os/Build$VERSION) was called from RWX@0x1201732c[libdexjni.so]0x1732c
  161. JNIEnv->GetStaticFieldID(android/os/Build$VERSION.SDK_INTI) => 0x1e4ff4f1 was called from RWX@0x12017354[libdexjni.so]0x17354
  162. JNIEnv->GetStaticIntField(class android/os/Build$VERSION, SDK_INT => 0x17) was called from RWX@0x1201736c[libdexjni.so]0x1736c
  163. JNIEnv->FindClass(com/fort/andjni/JniLib) was called from RWX@0x1200fea4[libdexjni.so]0xfea4
  164. JNIEnv->RegisterNatives(com/fort/andjni/JniLib, RW@0x1255fe50[libdexjni.so]0x55fe50, 10) was called from RWX@0x1200ff58[libdexjni.so]0xff58
  165. RegisterNative(com/fort/andjni/JniLib, cV([Ljava/lang/Object;)V, RWX@0x12017770[libdexjni.so]0x17770)
  166. RegisterNative(com/fort/andjni/JniLib, cI([Ljava/lang/Object;)I, RWX@0x120177c4[libdexjni.so]0x177c4)
  167. RegisterNative(com/fort/andjni/JniLib, cL([Ljava/lang/Object;)Ljava/lang/Object;, RWX@0x1201781c[libdexjni.so]0x1781c)
  168. RegisterNative(com/fort/andjni/JniLib, cS([Ljava/lang/Object;)S, RWX@0x12017874[libdexjni.so]0x17874)
  169. RegisterNative(com/fort/andjni/JniLib, cC([Ljava/lang/Object;)C, RWX@0x120178cc[libdexjni.so]0x178cc)
  170. RegisterNative(com/fort/andjni/JniLib, cB([Ljava/lang/Object;)B, RWX@0x12017924[libdexjni.so]0x17924)
  171. RegisterNative(com/fort/andjni/JniLib, cJ([Ljava/lang/Object;)J, RWX@0x1201797c[libdexjni.so]0x1797c)
  172. RegisterNative(com/fort/andjni/JniLib, cZ([Ljava/lang/Object;)Z, RWX@0x120179d4[libdexjni.so]0x179d4)
  173. RegisterNative(com/fort/andjni/JniLib, cF([Ljava/lang/Object;)F, RWX@0x12017a2c[libdexjni.so]0x17a2c)
  174. RegisterNative(com/fort/andjni/JniLib, cD([Ljava/lang/Object;)D, RWX@0x12017a84[libdexjni.so]0x17a84)
复制代码

整个运行流程非常清晰,可以依据这个来补sub_12010028中的结构体.

这里我为了方便选择调用0号函数(这张图好像是第三次出现了233)

  1. vm.resolveClass("com/fort/andjni/JniLib").callStaticJniMethod(emulator, "cV([Ljava/lang/Object;)V", new ArrayObject(vm.resolveClass("android/app/Activity"), vm.resolveClass("android/app/Activity").newObject(null), vm.resolveClass("android/os/Bundle").newObject(null), DvmInteger.valueOf(vm, 0)));
复制代码

因为函数简单(毕竟类都叫BlankActivity了),没有复杂逻辑,只补了几个简单函数就跑完了,整个流程被顺利trace下来.

毕竟是vmp,接下来的首要任务是先找到insns.
这里通过定位我命名的那个code_item结构然后偏移0x10即为insns,而code_item又在j__lSSO05lI$I5IllOl_0lS$_lllIII__l$IOlI55$O550IS__lS5$函数返回的结构体中:


在trace中搜索0x12018194:
  1. [libdexjni.so 0x018194] [f80300aa] 0x12018194: "mov x24, x0" x0=0x12893030 => x24=0x12893030
复制代码
得到结构体内存地址0x12893030,再查找其偏移0x8处的code_item读取:

  1. [libdexjni.so 0x018228] [080740f9] 0x12018228: "ldr x8, [x24, #8]" ; mem[READ] abs=0x12893038 x24=0x12893030 => x8=0x128ac000
复制代码
对比一下code_item的标准结构(这里不去关注后面的try_item)
  1. struct code_item {
  2.     uint16_t registers_size;
  3.     uint16_t ins_size;
  4.     uint16_t outs_size;
  5.     uint16_t tries_size;
  6.     uint32_t debug_info_off;
  7.     uint32_t insns_size;
  8.     uint16_t insns[insns_size];
  9. }
复制代码

前0x10字节的确非常符合code_item的结构,并且可以验证偏移0xc处的0x30正是insns_size(这段数据没有try_item,长度共0x70,符合0x10+0x30*2),因此后面0x60字节即为insns.
查找对0x128ac010处的读取:
  1. [libdexjni.so 0x04e5cc] [090140b9] 0x1204e5cc: "ldr w9, [x8]" ; mem[READ] abs=0x128ac010 x8=0x128ac010 => w9=0x9c2e
复制代码

读出了0x00009c2e四个字节,低16位被拿去查表0x12098a30,结果是0x12098a30 + (0x8d6c << 3)) = 0x120e6ba0
然后从这个位置读出了一个地址,最后br跳过去了

至于这个目标地址...依然在解释器函数之内:

(0x12029578-0x1206e068,这函数实在太大了)
而高16位则是
  1. [libdexjni.so 0x04e5d0] [e9ab0af9] 0x1204e5d0: "str x9, [sp, #0x1550]" ; mem[WRITE] abs=0xe4ffe500 x9=0x9c2e sp=0xe4ffcfb0 => x9=0x9c2e

  2. ->

  3. [libdexjni.so 0x038d24] [e8ab4af9] 0x12038d24: "ldr x8, [sp, #0x1550]" ; mem[READ] abs=0xe4ffe500 sp=0xe4ffcfb0 => x8=0x9c2e
  4. [libdexjni.so 0x038d28] [085d1053] 0x12038d28: "ubfx w8, w8, #0x10, #8" w8=0x9c2e => w8=0x0
  5. [libdexjni.so 0x038d2c] [e8a70af9] 0x12038d2c: "str x8, [sp, #0x1548]" ; mem[WRITE] abs=0xe4ffe4f8 x8=0x0 sp=0xe4ffcfb0 => x8=0x0

  6. ->

  7. [libdexjni.so 0x056770] [e8a74af9] 0x12056770: "ldr x8, [sp, #0x1548]" ; mem[READ] abs=0xe4ffe4f8 sp=0xe4ffcfb0 => x8=0x0
  8. [libdexjni.so 0x056774] [083d4092] 0x12056774: "and x8, x8, #0xffff" x8=0x0 => x8=0x0
  9. [libdexjni.so 0x056778] [e11742f9] 0x12056778: "ldr x1, [sp, #0x428]" ; mem[READ] abs=0xe4ffd3d8 sp=0xe4ffcfb0 => x1=0x35ef1869
  10. [libdexjni.so 0x05677c] [e9b34af9] 0x1205677c: "ldr x9, [sp, #0x1560]" ; mem[READ] abs=0xe4ffe510 sp=0xe4ffcfb0 => x9=0x134d1080
  11. [libdexjni.so 0x056780] [217928f8] 0x12056780: "str x1, [x9, x8, lsl #3]" ; mem[WRITE] abs=0x134d1080 x1=0x35ef1869 x9=0x134d1080 x8=0x0 => x1=0x35ef1869
复制代码
通过高16位查询了一个表并写入了一个值.

低16位的查表跳转,很像是在解释指令;而高16位的查表写入,则像是对寄存器的操作.
但是标准的opcode明明是uint8_t,这里却是uint16_t,难不成魔改了?
简单翻了一下表0x12098a30,发现其中元素大多为0,但非0元素全部指向解释器函数内部,加大了uint16_t的vmcode的可能性.
为了验证,直接查一下第0xffff位的元素地址,看看这个表是否真的有这么大:
0x12098a30 + (0xffff << 3) = 0x12118a28

结果非常神奇,恰好达到了一个数据分界处,0x12118a28之上的部分即为跳转表,之下的部分则是其他数据,所以这个表的确有0xffff个元素,涵盖了整个uint16_t范围.
种种迹象表明,vmcode就是uint16_t类型的,因此这个vmp必然存在魔改.
跳转表得到后,使用unidbg对整个表范围的读取进行监听:
  1. emulator.traceRead(0x12098a30, 0x12118a28);
复制代码
得到结果:
  1. Memory READ at 0x120e6ba0, data size = 8, data value = 0x000000001206588c, PC=RWX@0x1204e5e0[libdexjni.so]0x4e5e0, LR=RWX@0x12050708[libdexjni.so]0x50708
  2. Memory READ at 0x120df590, data size = 8, data value = 0x00000000120449fc, PC=RWX@0x1204b05c[libdexjni.so]0x4b05c, LR=RWX@0x1205081c[libdexjni.so]0x5081c
  3. Memory READ at 0x120bdb80, data size = 8, data value = 0x0000000012065158, PC=RWX@0x120553a4[libdexjni.so]0x553a4, LR=RWX@0x12031234[libdexjni.so]0x31234
  4. Memory READ at 0x120f24d0, data size = 8, data value = 0x0000000012044d90, PC=RWX@0x1202e93c[libdexjni.so]0x2e93c, LR=RWX@0x12051cfc[libdexjni.so]0x51cfc
  5. Memory READ at 0x120c6498, data size = 8, data value = 0x00000000120651c0, PC=RWX@0x120570c8[libdexjni.so]0x570c8, LR=RWX@0x12056044[libdexjni.so]0x56044
  6. Memory READ at 0x12116fa8, data size = 8, data value = 0x000000001205cba4, PC=RWX@0x12052584[libdexjni.so]0x52584, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
  7. Memory READ at 0x120aba20, data size = 8, data value = 0x0000000012064fd8, PC=RWX@0x12064ae8[libdexjni.so]0x64ae8, LR=RWX@0x12049f78[libdexjni.so]0x49f78
  8. Memory READ at 0x120c6498, data size = 8, data value = 0x00000000120651c0, PC=RWX@0x12053570[libdexjni.so]0x53570, LR=RWX@0x12049f78[libdexjni.so]0x49f78
  9. Memory READ at 0x120c6498, data size = 8, data value = 0x00000000120651c0, PC=RWX@0x12052584[libdexjni.so]0x52584, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
  10. Memory READ at 0x120bc770, data size = 8, data value = 0x0000000012065708, PC=RWX@0x12052584[libdexjni.so]0x52584, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
复制代码
对读取的地址进行逆向操作,还原出vmcode:
vmcode = (address - 0x12098a30) >>> 3
逐个翻译得:0x9c2e、0x8d6c、0x4a2a、0xb354、0x5b4d、0xfcaf、0x25fe、0x5b4d、0x5b4d、0x47a8.
并以此为特征将指令分割:

现在vmcode已知了,接下来想要分析指令操作,还有必不可少的一步:寄存器监听.
在上文有一个地方我们看到了对寄存器内存的分配:

unidbg断点可得分配的内存地址为0x134d1080,而这个方法的registers_size为4,因此监听:
  1. emulator.traceRead(0x134d1080, 0x134d10a0);
  2. emulator.traceWrite(0x134d1080, 0x134d10a0);
复制代码
其中0x134d1080为v0寄存器、0x134d1088 v1、0x134d1090 p0、0x134d1098 p1

可以清楚看到先对寄存器进行了一些初始化,写入了p0(this)和p1(android/os/Bundle)的值.
Memory READ at 0x120e6ba0后开始运行第一条指令,在v0寄存器上写入了值0x000000004bb4de6a;第二条指令在v1寄存器写入值0x000000002f8f5f62,并且两条指令执行的作用看上去出奇一致,都是在初始化一个jstring,不过vmcode不同.
从log中可以猜到,这两个值正是来自Memory WRITE上方的JNIEnv->NewGlobalRef.

这里凭空出现了一个字符串,自然要搞清来由.
直接跳转到上方jni调用的地址处,发现是从j__l$I0lOll00SOlSI_ISl50IO0lOI_Ol$_IIIlIIlIl5IIll5IS5$读取的

跟过去发现又是在查表:

先不急着去追数据,先打印一下用于查表的idx:
  1. emulator.getBackend().hook_add_new(new CodeHook() {
  2.     @Override
  3.     public void hook(Backend backend, long address, int size, Object user) {
  4.         System.out.printf("idx:%#x\n", backend.reg_read(Unicorn.UC_ARM64_REG_X0).intValue());
  5.     }

  6.     @Override
  7.     public void onAttach(UnHook unHook) {
  8.     }

  9.     @Override
  10.     public void detach() {
  11.     }
  12. }, 0x1200cea0, 0x1200cea0, null);
复制代码

再回头看一眼insns,这不就在这嘛:

所以这两字节就是用于查字符串表的索引喽.
交叉引用一下字符串表,依旧是熟悉的感觉:

看到这里,你可能已经猜到了,没错,这就是JNI_OnLoad调用的第二个函数.(之前跳过的函数最后都没逃过qwq)

依然是通过MessagePack解析二进制数据,并且这个函数初始化的数据不只有这一个表,函数中一共对五个表进行了初始化:

依旧是等这个函数运行完后,使用unidbg直接打印出表,然后猜测含义,这里直接给出分析结果:











结构体:
  1. struct dvm_string __packed
  2. {
  3. char* string;
  4. uint64_t len;
  5. };

  6. struct dvm_method __packed
  7. {
  8. char* class_name;
  9. char* method_args;
  10. char* method_name;
  11. };

  12. struct dvm_method2 __packed
  13. {
  14. char* class_name;
  15. char* method_sig;
  16. char* method_name;
  17. char* method_shorty;
  18. uint64_t zero;
  19. };
复制代码

看了这么多,至于被解析的数据在哪,在函数开头有这么一个调用:


这个函数就是对so中一段数据进行lz4解压,所以来源就很明显了.


现在数据来源都清楚了,那么写入v0还是v1是怎么决定的呢,其实就是vmcode后面的两字节,也就是之前追踪的“0x00009c2e的高16位”:

第一条指令是0x00,所以写入v0寄存器;第二条指令是0x01,写入v1寄存器.
至于最后0x8a08和0x6e4d那两字节数据,在trace中也没有读取的记录,只读了个半字
  1. [libdexjni.so 0x038d1c] [01094079] 0x12038d1c: "ldrh w1, [x8, #4]" ; mem[READ] abs=0x128ac014 x8=0x128ac010 => w1=0x4f9
复制代码

应该是用于对齐的无用数据了(后面同样有大量这种数据).
因此这两条指令翻译过来就是
  1. const-string v0, "BlankActivity"
  2. const-string v1, "onCreate"
复制代码


然后再来看下一条,这条指令显然是在调用com/albert/xchatkit/d.b(Ljava/lang/String;Ljava/lang/String;)V方法

不出意外要用到刚才解析出来的method表.
为了省事,直接把四个表(有一个表不存在这样的查表函数,因此是四个)的读取全部hook一下:

  1. emulator.getBackend().hook_add_new(new CodeHook() {
  2.     @Override
  3.     public void hook(Backend backend, long address, int size, Object user) {
  4.         System.out.printf("g_string_list2:%#x\n", backend.reg_read(Unicorn.UC_ARM64_REG_X0).intValue());
  5.     }

  6.     @Override
  7.     public void onAttach(UnHook unHook) {
  8.     }

  9.     @Override
  10.     public void detach() {
  11.     }
  12. }, 0x1200cea0, 0x1200cea0, null);

  13. emulator.getBackend().hook_add_new(new CodeHook() {
  14.     @Override
  15.     public void hook(Backend backend, long address, int size, Object user) {
  16.         System.out.printf("g_int_list:%#x\n", backend.reg_read(Unicorn.UC_ARM64_REG_X0).intValue());
  17.     }

  18.     @Override
  19.     public void onAttach(UnHook unHook) {
  20.     }

  21.     @Override
  22.     public void detach() {
  23.     }
  24. }, 0x1200ceec, 0x1200ceec, null);

  25. emulator.getBackend().hook_add_new(new CodeHook() {
  26.     @Override
  27.     public void hook(Backend backend, long address, int size, Object user) {
  28.         System.out.printf("g_method_list:%#x\n", backend.reg_read(Unicorn.UC_ARM64_REG_X0).intValue());
  29.     }

  30.     @Override
  31.     public void onAttach(UnHook unHook) {
  32.     }

  33.     @Override
  34.     public void detach() {
  35.     }
  36. }, 0x1200cf38, 0x1200cf38, null);

  37. emulator.getBackend().hook_add_new(new CodeHook() {
  38.     @Override
  39.     public void hook(Backend backend, long address, int size, Object user) {
  40.         System.out.printf("g_method_list2:%#x\n", backend.reg_read(Unicorn.UC_ARM64_REG_X0).intValue());
  41.     }

  42.     @Override
  43.     public void onAttach(UnHook unHook) {
  44.     }

  45.     @Override
  46.     public void detach() {
  47.     }
  48. }, 0x1200cf84, 0x1200cf84, null);
复制代码
重新运行一下得到method idx是0x6c3



由于调用的jni函数是CallStaticVoidMethodA,因此这条指令应该是invoke-static,并且从寄存器读取或者结合前两条指令可以得出两个入参jstring是v0和v1.
现在这条指令被魔改成了这样,尝试找一下其中是否存在规律.
invoke-static原是35c格式的,如果要写出原指令的字节码,应是(我的图做得好烂啊hhhhh):


有没有发现有点眼熟?忽视掉用于对齐的垃圾数据:

除了opcode变成了二字节的vmcode,其他数据简直一模一样!
所以这条invoke指令也就被还原了:
  1. invoke-static {v0, v1}, Lcom/albert/xchatkit/d;->b(Ljava/lang/String;Ljava/lang/String;)V
复制代码


后面的指令根据trace大概看了下,和上述逻辑均相同,都是由标准结构塞入一些对齐数据来魔改的,本质和标准结构类似.
这里给出trace、vm字节码以及还原对照:

第四条指令:
trace:
  1. Memory READ at 0x120f24d0, data size = 8, data value = 0x0000000012044d90, PC=RWX@0x1202e93c[libdexjni.so]0x2e93c, LR=RWX@0x12051cfc[libdexjni.so]0x51cfc
  2. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1204f910[libdexjni.so]0x4f910, LR=RWX@0x12051cfc[libdexjni.so]0x51cfc
  3. Memory READ at 0x134d1080, data size = 8, data value = 0x000000003bd94634, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1205f96c[libdexjni.so]0x5f96c
  4. Memory READ at 0x134d1088, data size = 8, data value = 0x0000000074294adb, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  5. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  6. Memory READ at 0x134d1098, data size = 8, data value = 0x0000000017baae6e, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  7. g_method_list2:0x40
  8. JNIEnv->FindClass(android/app/Activity) was called from RWX@0x1205f990[libdexjni.so]0x5f990
  9. JNIEnv->FindClass(java/lang/Class) was called from RWX@0x1205f9ac[libdexjni.so]0x5f9ac
  10. JNIEnv->GetMethodID(java/lang/Class.isInterface()Z) => 0x789061df was called from RWX@0x1205f9d0[libdexjni.so]0x5f9d0
  11. JNIEnv->CallBooleanMethodV(class android/app/Activity, isInterface() => true) was called from RWX@0x12018df0[libdexjni.so]0x18df0
  12. JNIEnv->GetMethodID(android/app/Activity.onCreate(Landroid/os/Bundle;)V) => 0x2f2ecda8 was called from RWX@0x1204adc4[libdexjni.so]0x4adc4
  13. Memory READ at 0x134d1098, data size = 8, data value = 0x0000000017baae6e, PC=RWX@0x1202b0b8[libdexjni.so]0x2b0b8, LR=RWX@0x1204adc4[libdexjni.so]0x4adc4
  14. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1204e57c[libdexjni.so]0x4e57c, LR=RWX@0x1204adc4[libdexjni.so]0x4adc4
  15. JNIEnv->CallNonVirtualVoidMethodA(android.app.Activity@43d7741f, android/app/Activity, onCreate(android.os.Bundle@17baae6e)) was called from RWX@0x12055d44[libdexjni.so]0x55d44
复制代码
  1. 字节码:54 B3 20 00 40 00 82 5E 32 00 D7 FA
  2. 0xb354 -> opcode:invoke-super
  3. 0x(00)20 -> registerCount=2, regG=0
  4. 0x0040 -> g_method_list2 idx[android/app/Activity.onCreate(Landroid/os/Bundle;)V]
  5. 0x5e82 -> padding
  6. 0x0032 -> regC=2(p0), regD=3(p1), regE=0, regF=0
  7. 0xfad7 -> padding
  8. Smali:invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V;
复制代码

第五条指令:
trace:
  1. Memory READ at 0x120c6498, data size = 8, data value = 0x00000000120651c0, PC=RWX@0x120570c8[libdexjni.so]0x570c8, LR=RWX@0x12056044[libdexjni.so]0x56044
  2. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1205da04[libdexjni.so]0x5da04, LR=RWX@0x12056044[libdexjni.so]0x56044
  3. Memory READ at 0x134d1080, data size = 8, data value = 0x000000003bd94634, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x12062f18[libdexjni.so]0x62f18
  4. Memory READ at 0x134d1088, data size = 8, data value = 0x0000000074294adb, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  5. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  6. Memory READ at 0x134d1098, data size = 8, data value = 0x0000000017baae6e, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  7. g_method_list2:0x3a
  8. JNIEnv->FindClass(android/app/Activity) was called from RWX@0x12062f3c[libdexjni.so]0x62f3c
  9. JNIEnv->GetMethodID(android/app/Activity.getWindow()Landroid/view/Window;) => 0x4333f814 was called from RWX@0x120327b4[libdexjni.so]0x327b4
  10. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x12055a5c[libdexjni.so]0x55a5c, LR=RWX@0x1204bc14[libdexjni.so]0x4bc14
  11. JNIEnv->CallObjectMethodA(android.app.Activity@43d7741f, getWindow() => android.view.Window@130f889) was called from RWX@0x1205ee14[libdexjni.so]0x5ee14
复制代码
  1. 字节码:4D 5B 10 00 3A 00 55 9B 02 00 73 66
  2. 0x5b4d -> opcode:invoke-virtual
  3. 0x(00)10 -> registerCount=1, regG=0
  4. 0x003a -> g_method_list2 idx[android/app/Activity.getWindow()Landroid/view/Window;]
  5. 0x9b55 -> padding
  6. 0x0002 -> regC=2(p0), regD=0, regE=0, regF=0
  7. 0x6673 -> padding
  8. Smali:invoke-virtual {p0}, Landroid/app/Activity;->getWindow()Landroid/view/Window;
复制代码

第六条指令:
trace:
  1. Memory READ at 0x12116fa8, data size = 8, data value = 0x000000001205cba4, PC=RWX@0x12052584[libdexjni.so]0x52584, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
  2. Memory WRITE at 0x134d1098, data size = 8, data value = 0x000000000130f889, PC=RWX@0x12049f68[libdexjni.so]0x49f68, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
复制代码
  1. 字节码:AF FC 03 00
  2. 0xfcaf -> opcode:move-result-object
  3. 0x0003 -> reg=3(p1)
  4. Smali:move-result-object p1
复制代码

第七条指令:
trace:
  1. Memory READ at 0x120aba20, data size = 8, data value = 0x0000000012064fd8, PC=RWX@0x12064ae8[libdexjni.so]0x64ae8, LR=RWX@0x12049f78[libdexjni.so]0x49f78
  2. Memory WRITE at 0x134d1080, data size = 8, data value = 0x0000000000280400, PC=RWX@0x12035028[libdexjni.so]0x35028, LR=RWX@0x12049f78[libdexjni.so]0x49f78
复制代码
  1. 字节码:FE 25 00 00 00 04 5B CE 28 00 FD 58
  2. 0x25fe -> opcode:const
  3. 0x(00)00 -> reg=0(v0)
  4. 0x0400 -> low 16 bits
  5. 0xce5b -> padding
  6. 0x0028 -> high 16 bits
  7. 0x58fd -> padding
  8. Smali:const v0, 0x00280400
复制代码

第八条指令:
trace:
  1. Memory READ at 0x120c6498, data size = 8, data value = 0x00000000120651c0, PC=RWX@0x12053570[libdexjni.so]0x53570, LR=RWX@0x12049f78[libdexjni.so]0x49f78
  2. Memory READ at 0x134d1098, data size = 8, data value = 0x000000000130f889, PC=RWX@0x1205da04[libdexjni.so]0x5da04, LR=RWX@0x12049f78[libdexjni.so]0x49f78
  3. Memory READ at 0x134d1080, data size = 8, data value = 0x0000000000280400, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x12062f18[libdexjni.so]0x62f18
  4. Memory READ at 0x134d1088, data size = 8, data value = 0x0000000074294adb, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  5. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  6. Memory READ at 0x134d1098, data size = 8, data value = 0x000000000130f889, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  7. g_method_list2:0x301
  8. JNIEnv->FindClass(android/view/Window) was called from RWX@0x12062f3c[libdexjni.so]0x62f3c
  9. JNIEnv->GetMethodID(android/view/Window.addFlags(I)V) => 0x3448735f was called from RWX@0x120327b4[libdexjni.so]0x327b4
  10. Memory READ at 0x134d1080, data size = 8, data value = 0x0000000000280400, PC=RWX@0x1202ed4c[libdexjni.so]0x2ed4c, LR=RWX@0x1204bc14[libdexjni.so]0x4bc14
  11. Memory READ at 0x134d1098, data size = 8, data value = 0x000000000130f889, PC=RWX@0x12055a5c[libdexjni.so]0x55a5c, LR=RWX@0x1204bc14[libdexjni.so]0x4bc14
  12. JNIEnv->CallVoidMethodA(android.view.Window@130f889, addFlags(0x280400)) was called from RWX@0x1205eec8[libdexjni.so]0x5eec8
复制代码
  1. 字节码:4D 5B 20 00 01 03 F5 4D 03 00 E5 23
  2. 0x5b4d分析前面已经给出
  3. Smali:invoke-virtual {p1, v0}, Landroid/view/Window;->addFlags(I)V
复制代码

第九条指令:
trace:
  1. Memory READ at 0x120c6498, data size = 8, data value = 0x00000000120651c0, PC=RWX@0x12052584[libdexjni.so]0x52584, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
  2. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1205da04[libdexjni.so]0x5da04, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
  3. Memory READ at 0x134d1080, data size = 8, data value = 0x0000000000280400, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x12062f18[libdexjni.so]0x62f18
  4. Memory READ at 0x134d1088, data size = 8, data value = 0x0000000074294adb, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  5. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  6. Memory READ at 0x134d1098, data size = 8, data value = 0x000000000130f889, PC=RWX@0x1206e2fc[libdexjni.so]0x6e2fc, LR=RWX@0x1206e308[libdexjni.so]0x6e308
  7. g_method_list2:0x2d
  8. JNIEnv->FindClass(android/app/Activity) was called from RWX@0x12062f3c[libdexjni.so]0x62f3c
  9. JNIEnv->GetMethodID(android/app/Activity.finish()V) => 0xa8352854 was called from RWX@0x120327b4[libdexjni.so]0x327b4
  10. Memory READ at 0x134d1090, data size = 8, data value = 0x0000000043d7741f, PC=RWX@0x12055a5c[libdexjni.so]0x55a5c, LR=RWX@0x1204bc14[libdexjni.so]0x4bc14
  11. JNIEnv->CallVoidMethodA(android.app.Activity@43d7741f, finish()) was called from RWX@0x1205eec8[libdexjni.so]0x5eec8
复制代码
  1. 字节码:4D 5B 10 00 2D 00 48 1E 02 00 1C 66
  2. 0x5b4d分析前面已经给出
  3. Smali:invoke-virtual {p0}, Landroid/app/Activity;->finish()V
复制代码

第十条指令:
trace:
  1. Memory READ at 0x120bc770, data size = 8, data value = 0x0000000012065708, PC=RWX@0x12052584[libdexjni.so]0x52584, LR=RWX@0x1205f1cc[libdexjni.so]0x5f1cc
复制代码
  1. 字节码:A8 47 F4 62
  2. 0x47a8 -> opcode:return-void
  3. 0x62f4 -> padding
  4. Smali:return-void
复制代码

所以这个函数还原下来就是:
  1. const-string v0, "BlankActivity"
  2. const-string v1, "onCreate"
  3. invoke-static {v0, v1}, Lcom/albert/xchatkit/d;->b(Ljava/lang/String;Ljava/lang/String;)V
  4. invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V;
  5. invoke-virtual {p0}, Landroid/app/Activity;->getWindow()Landroid/view/Window;
  6. move-result-object p1
  7. const v0, 0x00280400
  8. invoke-virtual {p1, v0}, Landroid/view/Window;->addFlags(I)V
  9. invoke-virtual {p0}, Landroid/app/Activity;->finish()V
  10. return-void
复制代码

因为所选函数本身比较简单,所以还原起来并不复杂,如果包含一些跳转指令的话也许会复杂一些,不过大概原理应该就是这些啦.


总结
整个运行流程还是比较常规的,解压释放text代码段,JNI_OnLoad初始化全部vm数据并注册跳板函数,跳板函数通过idx查数据进行一些初始化进入解释器解释执行.
比较有意思的就是vm的字节码进行了一些魔改,好像每个指令长度都变成了之前的两倍(不确定,但帖子中见到的都符合),添加了很多用于对齐的随机数据,是我见到的第一个魔改了指令结构的dex vmp.

大概是对这个vmp的整体进行了一通简单的分析,不过因为控制流以及自身能力的缘故,可能有很多东西我没能看到,就交给其他大佬来作补充吧~
已有10人评分好评 金币 理由
人从众 + 1 + 1 nb
WZTong + 1 + 1 大佬NB!
kingconst + 1
+ 1 + 1 淡定
dryzh + 1 + 1 很给力!
Steve2280 + 1
一个普通的人 + 1 + 1 牛批
青春向上 + 1 感谢分享。
Flash丶光芒 + 1 + 1
紫檀 + 1 NB

查看全部评分 总评分:好评 +6  金币 +10 

回复

使用道具 举报

7

主题

1496

回帖

6981

积分

硕士生

Rank: 6Rank: 6

金币
530
好评
22
信誉
112
发表于 2026-6-6 12:25:33 | 显示全部楼层  来自 云南
本帖最后由 秋菱 于 2026-6-6 20:07 编辑

注:本人原创帖子,在吾爱看雪都有发,考虑到论坛这边还没有一篇关于vmp的帖子,所以顺便往这里也发一份,不要说什么图片水印乱七八糟的东西

完整帖已上传,但感觉有点bug会吞东西,如果有什么地方缺了说一声
已有2人评分金币 理由
+ 1
Steve2280 + 1

查看全部评分 总评分:金币 +2 

回复

使用道具 举报

8

主题

1269

回帖

5740

积分

硕士生

棺材板

Rank: 6Rank: 6

金币
2329
好评
51
信誉
103
发表于 2026-6-6 12:45:40 来自手机  | 显示全部楼层  来自 四川
太酷啦
回复

使用道具 举报

9

主题

2691

回帖

1万

积分

博士生

WZTong

Rank: 7Rank: 7Rank: 7

金币
2732
好评
42
信誉
476

MT论坛最佳新人MT论坛新人考神MT论坛帅哥

发表于 2026-6-6 12:46:33 来自手机  | 显示全部楼层  来自 上海
围观膜拜(白嫖)大佬!
回复

使用道具 举报

22

主题

2490

回帖

6020

积分

硕士生

Rank: 6Rank: 6

金币
3876
好评
0
信誉
98
发表于 2026-6-6 12:53:04 | 显示全部楼层  来自 河南
感谢大佬分享!
回复

使用道具 举报

18

主题

850

回帖

8892

积分

硕士生

Rank: 6Rank: 6

金币
3310
好评
25
信誉
173
发表于 2026-6-6 13:10:46 来自手机  | 显示全部楼层  来自 广东
大佬牛逼
回复

使用道具 举报

33

主题

92

回帖

2890

积分

大学生

Rank: 5Rank: 5

金币
1358
好评
132
信誉
120
QQ
发表于 2026-6-6 13:13:16 来自手机  | 显示全部楼层  来自 广东
看看大佬分析dexvm
回复

使用道具 举报

4

主题

604

回帖

2123

积分

大学生

Rank: 5Rank: 5

金币
1224
好评
1
信誉
102
发表于 2026-6-6 13:15:02 来自手机  | 显示全部楼层  来自 河南
还原这种VMP的前提还是dexlib2指令集规范要先背好:ImmutableMethodReference methodRef = new ImmutableMethodReference(
    "Lcom/albert/xchatkit/d;",  
    "b",                        
    ImmutableList.of(           
        "Ljava/lang/String;",
        "Ljava/lang/String;"
    ),
    "V"                        
);


ImmutableInstruction35c instruction = new ImmutableInstruction35c(
    Opcode.INVOKE_STATIC,      
    2,                          
    0,                          
    1,                          
    0,                          
    0,                          
    0,                          
    methodRef                  
);
回复

使用道具 举报

1

主题

1693

回帖

4470

积分

大学生

Rank: 5Rank: 5

金币
720
好评
0
信誉
100
发表于 2026-6-6 13:22:16 来自手机  | 显示全部楼层  来自 广西
围观大佬
回复

使用道具 举报

14

主题

1609

回帖

5888

积分

硕士生

Rank: 6Rank: 6

金币
4229
好评
3
信誉
100

MT论坛最佳新人考神

发表于 2026-6-6 13:31:49 来自手机  | 显示全部楼层  来自 山西
很详细,感谢分享,收藏一下
回复

使用道具 举报

55

主题

3632

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
896
好评
190
信誉
181

MT论坛新人MT论坛最佳新人MT论坛帅哥考神MT论坛活跃会员挂机大佬

发表于 2026-6-6 13:35:49 来自手机  | 显示全部楼层  来自 陕西
围观大佬
回复

使用道具 举报

54

主题

4538

回帖

1万

积分

博士生

辰夕Mod

Rank: 7Rank: 7Rank: 7

金币
7152
好评
35
信誉
169

MT论坛帅哥考神MT论坛活跃会员

QQ
发表于 2026-6-6 13:43:47 来自手机  | 显示全部楼层  来自 辽宁
这个加固很神奇,如果开发者选了so保护的话,so会混淆然后下面会有b_a_n_c_l_e字符,这几个字符还是特殊字符,如果开发者没选择的话企业版相比普通版就多一个vmp(车来了,XYJSQ),迅雷这个属于特殊的企业版,不知道里面加什么了,没有vm,但是直接改会闪退而且没有闪退日志,还有一些企业版在so层还有校验,直接删会闪退同样没有闪退日志,神奇
回复

使用道具 举报

345

主题

2370

回帖

8077

积分

硕士生

Rank: 6Rank: 6

金币
1114
好评
9
信誉
101
发表于 2026-6-6 13:49:27 来自手机  | 显示全部楼层  来自 浙江
是bangbang企业加固吗
回复

使用道具 举报

0

主题

3232

回帖

7346

积分

硕士生

Rank: 6Rank: 6

金币
3553
好评
0
信誉
100

MT论坛新人考神MT论坛帅哥

发表于 2026-6-6 14:23:50 来自手机  | 显示全部楼层  来自 浙江
感谢分享
回复

使用道具 举报

10

主题

739

回帖

4630

积分

大学生

Rank: 5Rank: 5

金币
244
好评
21
信誉
101
发表于 2026-6-6 14:24:14 来自手机  | 显示全部楼层  来自 福建
太强了
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表