首页
社区
课程
招聘
[原创]某企业级加固[四代壳]VMP解释执行+指令还原
发表于: 2020-1-5 05:42 21999

[原创]某企业级加固[四代壳]VMP解释执行+指令还原

2020-1-5 05:42
21999

  

A | G | op BBBB F|E|D|C 

根据opcode克制总共6个字节,对应的就是 

A=1  G=0 op=6f  BBBB就是 331c,然后是C=1 D=0 E=0 F=1

    所以这里转换过来就是

next

--------------------------------------------------------------------------------------------------------------------------------

5511   6d00[A=2]op{vC, vD},kind@BBBB

1155 invoke-virtual  

006d  取MethodiD  requestWindowFeature     (I)Z

0001  参数

iput-object p0, p0,Lcom/wangzhong/fortune/ui/activity/BaseActivity;->a:Lcom/wangzhong/fortune/ui/activity/BaseActivity;;

--------------------------------------------------------------------------------------------------------------------------------


next  继续往下走,

--------------------------------------------------------------------------------------------------------------------------------

53 10 6D 00         [A=2]op{vC, vD},kind@BBBB

1053 invoke-virtual  

006d  取MethodiD  requestWindowFeature     (I)Z

0001  参数

iget-object p0, p0,Lcom/wangzhong/fortune/ui/activity/BaseActivity;->a:Lcom/wangzhong/fortune/ui/activity/BaseActivity;

--------------------------------------------------------------------------------------------------------------------------------



next  脚本执行结果如下

--------------------------------------------------------------------------------------------------------------------------------

  72 10  60 01 00 00    [A=2]op{vC, vD},kind@BBBB

10 72  invoke-virtual  

0160   取MethodiD  requestWindowFeature     (I)Z

0000  参数编号

invoke-static {v0}, Lcom/wangzhong/fortune/f/c;->a(Landroid/app/Activity;)V

--------------------------------------------------------------------------------------------------------------------------------

next


--------------------------------------------------------------------------------------------------------------------------------

69 00   这个指令比较简单就是

return-void

--------------------------------------------------------------------------------------------------------------------------------

这里的5c最终是要到dex里取查找的 


把下面这部分指令的根据分析经过转换

A3 20 5C 00 01 00 6B 10  CC 20 13 02 01 00 55 11
6D 00 53 10 6D 00 72 10  60 01 00 00 69 00 00 00 


用流程图来说明下





得到

修复前


现在的VMP的比较常见了,应该也是稳定性满足要求了,今天来分析一波,如有不当还请各位大佬指正
实际上 libdexjni.so在不同的APP中体积会不一样,应该是硬编码写入字符串和指令导致的

1-VMP还是先看下opcode部分知识,DEX指令格式

代码转换成DEX指令先看代码

对应的第一条指令是 
每条指令是2字节,所以先看第一条 6f 20,根据官方文档  6F的解释是 invoke-super    格式为35c

  

A | G | op BBBB F|E|D|C 

根据opcode克制总共6个字节,对应的就是 

A=1  G=0 op=6f  BBBB就是 331c,然后是C=1 D=0 E=0 F=1

    所以这里转换过来就是

invoke-super {p0}, Landroidx/fragment/app/FragmentActivity;->getResources()Landroid/content/res/Resources;

2-反调试

通过常规手段,在关键的open函数观察,然后逆向查找
发现几处反调试 
0x47CAC 处是创建线程,检测运行时间,getpid 然后  linux_eabi_syscall(__NR_kill, a1, a2)来杀死进程
0x047C70  处是cmdline反调试,https://bbs.pediy.com/thread-223460.htm  这位大佬提到过
0x489EC    处是 /proc/status检测反调试   
实际可能还有,但是在找到这三处之后,我发现特殊的地方是刚好在JNI_OnLoad处有个总的入口,所以直接
nop指令反调试就gg了 
我用 arm64调试的    mov w1,w1 对应的的hex是E103012A
然后dump出dex,先内存找到dex.035
import struct
 

start = 0x75172191ec

dump_so = "/Users/beita/tmp/bangbang/dump_vmp.dex"
length = 0x6ee27c
file = open(dump_so,'w')
file.close()
fn = AskStr(dump_so ,"save as:")
with open(fn,"wb+") as f:
    for addr in range(start , start+length):
        f.write(struct.pack("B" , Byte(addr)))
    print "success to save as " 

3-VMP的具体分析

通过常规手段,在关键的open函数观察,然后逆向查找
发现几处反调试 
0x47CAC 处是创建线程,检测运行时间,getpid 然后  linux_eabi_syscall(__NR_kill, a1, a2)来杀死进程
0x047C70  处是cmdline反调试,https://bbs.pediy.com/thread-223460.htm  这位大佬提到过
0x489EC    处是 /proc/status检测反调试   
实际可能还有,但是在找到这三处之后,我发现特殊的地方是刚好在JNI_OnLoad处有个总的入口,所以直接
nop指令反调试就gg了 
我用 arm64调试的    mov w1,w1 对应的的hex是E103012A
然后dump出dex,先内存找到dex.035
import struct
 

start = 0x75172191ec

dump_so = "/Users/beita/tmp/bangbang/dump_vmp.dex"
length = 0x6ee27c
file = open(dump_so,'w')
file.close()
fn = AskStr(dump_so ,"save as:")
with open(fn,"wb+") as f:
    for addr in range(start , start+length):
        f.write(struct.pack("B" , Byte(addr)))
    print "success to save as " 

import struct
 

start = 0x75172191ec

dump_so = "/Users/beita/tmp/bangbang/dump_vmp.dex"
length = 0x6ee27c
file = open(dump_so,'w')
file.close()
fn = AskStr(dump_so ,"save as:")
with open(fn,"wb+") as f:
    for addr in range(start , start+length):
        f.write(struct.pack("B" , Byte(addr)))
    print "success to save as " 

3-VMP的具体分析

得到dex之后,转成jar,看了下,大部分函数是 JniLib.cV等来做的,但是有一个Integer.valueof,是一个函数索引,用来查找指令的
附加调试发现实际在这里解开这个java数组也就是 new Object的这个数组
这里用onCreate来分析  索引是18=0x12 
 JniLib.cV(new Object[] { this, paramBundle, Integer.valueOf(18) });
调试往下走,根据这个索引,会取出一个结构体信息,结合上下文信息

这里取出  0x7517a96b50的值 是  0x12

strut JavaInfo {
	uint32_t index;     // 0x12   这是java层传递的
	uint32_t unknow2;   // 0x2e   未知
	uint64_t dexcode;   // dexcode指针
	uint32_t unknow4;   // 0x03
	uint32_t unknow5;   // 0x02
	uint32_t unknow6;   // 0x02  这里看起来没有用到  但是貌似是DexCode的内容
};
跳转到dexcode的位置看下内容
strut JavaInfo {
	uint32_t index;     // 0x12   这是java层传递的
	uint32_t unknow2;   // 0x2e   未知
	uint64_t dexcode;   // dexcode指针
	uint32_t unknow4;   // 0x03
	uint32_t unknow5;   // 0x02
	uint32_t unknow6;   // 0x02  这里看起来没有用到  但是貌似是DexCode的内容
};
跳转到dexcode的位置看下内容
struct DexCode {
    u2  registersSize;   // 3
    u2  insSize;           、、 2
    u2  outsSize;
    u2  triesSize;
    u4  debugInfoOff;       /* file offset to debug info stream */
    u4  insnsSize;          /* size of the insns array, in u2 units */
    u2  insns[1]; 
    }

struct DexCode {
    u2  registersSize;   // 3
    u2  insSize;           、、 2
    u2  outsSize;
    u2  triesSize;
    u4  debugInfoOff;       /* file offset to debug info stream */
    u4  insnsSize;          /* size of the insns array, in u2 units */
    u2  insns[1]; 
    }

registerSize = 3
insSize = 2
outsSize = 0
.....
主要看
insnsSize = 0xf
共15条指令   ,但是这个指令不是 标准的dex指令 opcode被改过,且字符串信息也是被改过,就是是说他不是系统来解析的,而且会有一个对应关系

A3 20 5C 00 21 00 6B 10  CC 20 13 02 01 00 55 11
6D 00 53 10 6D 00 72 10  60 01 00 00 69 00 

进入到vm_parse函数之前的代码还能F5看下逻辑,但是到  vm_parse地址是29b70位置处,F5不好用了,貌似是刻意把这个函数写的非常大,
有点像dalvik里边的HANDLE那种搞到一起, 这样在加固过程中OLLVM混淆之后,更加复杂

在解析opcode之前会进行数据保存
信息看起来是保存到一组结构中
	struct Infos1{
		uint64_t data1;
		uint64_t *data2;  // data2 = malloc(32)   是根据JavaInfo的dexCode来的
		uint64_t data3;
		uint64_t data4;
		uint64_t data5;
		uint64_t data6;
		uint64_t data7;
		uint64_t data8;	// JavaInfo的data3的值 
	};


	struct Infos1{
		uint64_t data1;
		uint64_t *data2;  // data2 = malloc(32)   是根据JavaInfo的dexCode来的
		uint64_t data3;
		uint64_t data4;
		uint64_t data5;
		uint64_t data6;
		uint64_t data7;
		uint64_t data8;	// JavaInfo的data3的值 
	};



调试继续往下走,来到 j___Sl_I5_lO000_0SSIO_I0_O__OI_5I___lSSl0_lO5_0I5I5S5_  这个函数,这个函数不能F5了,要根据汇编来分析具体的vm是如何
解析opcde来实现代码运行的

最终的 入口是  29b70这个函数 
调用获取GetMethodID的过程是

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-1-21 11:13 被贝a塔编辑 ,原因:
上传的附件:
收藏
免费 15
支持
分享
最新回复 (14)
雪    币: 1841
活跃值: (1290)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
2
先收藏哈哈
2020-1-5 06:49
0
雪    币: 1841
活跃值: (1290)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
3
感谢分享,膜拜
2020-1-5 07:03
0
雪    币: 0
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
收藏。
2020-1-5 10:11
0
雪    币: 3712
活跃值: (1401)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
java2c + arm指令虚拟化相对于dexVMP要牺牲体积和性能
2020-1-5 11:14
0
雪    币: 7
活跃值: (263)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
bangbang的企业级吧,貌似棍棍的只看到这篇文章
2020-1-6 11:34
0
雪    币: 758
活跃值: (78)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
m
2020-1-6 17:18
0
雪    币: 26245
活跃值: (63287)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
9
感谢分享!
2020-1-10 10:04
0
雪    币: 4248
活跃值: (2789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
求样本
2020-1-10 22:03
0
雪    币: 19950
活跃值: (4942)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
收藏了
2020-6-10 21:16
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
牛了。
2020-6-18 23:05
0
雪    币: 170
活跃值: (499)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
求样本
2020-7-12 22:54
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
请问指令格式里面有形如kind@BBBB的,最后是如何确定这个kind的呀?
2020-8-1 20:33
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
大神求样本,等级不能私信,看到请回,摆脱
2022-4-12 20:16
0
游客
登录 | 注册 方可回帖
返回
//