首页
社区
课程
招聘
[原创]百度加固免费版分析及VMP修复
发表于: 2020-3-3 14:45 39015

[原创]百度加固免费版分析及VMP修复

2020-3-3 14:45
39015

本来计划年后跑路的,不知道是我太菜,还是疫情原因,投简历都没人搭理我。现在又不能出门,只好自己找点事干了。

本文基于Android8.1分析。如果不想看分析过程,可以直接跳到最后的总结。

自己随便写个app,上传到百度开发者平台去加固。


加固后反编译看下。包名com.example.test下原来的类都没了,多了个com.baidu.protect,assets下面多了几个文件,lib下面多了一个so。猜测是通过libbaiduprotect.so将assets下的文件解密出dex,然后加载。



通过AndroidManifest.xml知道最先执行的class为com.baidu.protect.StubApplication


找到attachBaseContext方法,可以看到先通过Debug.isDebuggerConnected检测是否被调试,如果被调试就不会加载so,直接进行application替换,这样肯定是不行的,因为原来的class都没有被加载进来,程序直接就会崩溃,所以要调试的话需要把这里过掉。


然后看loadLibrary方法,可以看到直接System.loadLibrary(AppInfo.LIBNAME)加载so,AppInfo.LIBNAME就是baiduprotect。



将libbaiduprotect.so用ida打开。可以看到有.init_array,这个数组包含好几个函数,而其他很多函数,包括JNI_OnLoad都被加密了。





现在详细分析.init_array中的每一个函数。



先跳过第一个函数,看下后面的。进到函数sub_6FC4,看下c代码。没有被加密,也没有被混淆,但是其中调用的loc_3E73C被加密了。



现在只能回去分析第一个函数sub_88060了,把垃圾代码删除之后,分析出调用流程,发现全是一些字符操作,应该是在解密,没有发现有反调试的地方。


所以直接动态调试,当第一个函数sub_88060执行完后,dump解密后的so。将其用ida打开,可以看到函数都已经被解密了。



继续分析.init_array中的函数,通过分析,从sub_6FC4到sub_7578这8个函数都在做一件事情,就是向一个列表添加函数,每个函数添加的时候会指定一个数字(索引),将其通过索引排序插入列表。后面的过程中会按顺序调用列表中所有函数。

这8个函数这里就分析其中的sub_6FC4,其余的都类似,有兴趣的可以自己去看。



最后分析得到一个索引(即调用顺序)和函数的映射关系。



.init_array中最后两个函数sub_76BC和sub_7744是初始化tls和mutex相关的,就不看了。




将垃圾代码删除后,得到真实流程。开始我以为会通过时间来判断是否被调试,其实这里获取时间只是统计信息上报。


这里的函数sub_3E628就是调用队列中的所有函数,它的第2个参数会被作为参数传到函数中,判断函数是否该执行。


所以,首先用1作为参数调用队列中的所有函数,通过分析只有sub_B3B4会执行。该函数通过dlsym将libc中的符号获取保存下来,之后调用这些函数都通过指针调用。



继续回到JNI_OnLoad,执行完函数队列中的函数,就该执行函数sub_91E4了,该函数是注册com.baidu.protect.A中的部分本地函数,n001,n002,n003,分别对应的函数为sub_9318,sub_94E4,sub_9564



继续回到JNI_OnLoad,注册函数后,失败就直接返回了,成功则以2作为参数再次调用队列中的所有函数。通过分析,只有sub_3E29C会执行。可以看到,该函数可以接受2和3,现在我们只看参数为2的情况。



现在进入sub_3E36C,其主要就是调用sub_3E3F0,而sub_3E3F0就是通过读取/proc/self/maps文件,通过加载的虚拟机文件,判断虚拟机类型。



至此,libbaiduprotect.so的加载流程就执行完了。



sub_9318中将参数保存起来,然后最主要就是调用了sub_781C,



sub_781C也是被混淆的,但是代码很少,稍微看下,就知道只做了一件事,就是用3作为参数调用函数队列中的所有函数。



通过分析,会有sub_3E29C、sub_40CF8、sub_3DFC4、sub_11F5C、sub_45964这几个函数执行。

先看sub_3E29C,这个函数之前执行过参数为2的部分,现在来看参数为3的部分。先执行sub_13880,通过分析,该函数是获取apk包的签名,然后计算签名的MD5。然后sub_66064将签名的MD5值进行扩展,变为176字节。然后将签名的MD5和扩展后的内容存放在qword_BE690。


再看sub_40CF8,该函数可以接受3和4作为参数,现在先看3,



再看sub_3DFC4,这个函数最主要就是sub_3D6AC,通过解析apk中assets,生成各种路径,然后通过qword_BE2F0生成目录,qword_BE2F0就是之前从libc中获取的mkdir的指针。 



再看sub_11F5C,该函数只是调用了sub_BC60,sub_BC60也被混淆了,删除垃圾代码后,流程如下。因为我用的手机是8.1的,所以只看了sdk大于26的,


先看sub_188AC,该函数也是被混淆的,删除垃圾代码后流程如下,



sub_4029C读取/proc/self/maps文件,查找对应的so,修改内存页属性


sub_3FF9C是对函数进行hook,通过动态节,找到重定位节和got,然后替换指定标签的地址。这里分别hook了__android_log_print和mmap,__android_log_print被替换为sub_1B044,这是个空函数,禁用log。mmap被替换为sub_1B070,在加载dex的时候有用,稍后分析。

再看sub_11AB0,最主要的是下面的这个循环,将assets下所有的jar解密为dex,然后通过InMemoryDexClassLoader加载,然后提取DexPathList$Element添加到源classloader中。



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

最后于 2020-3-3 20:21 被卧勒个槽编辑 ,原因: 附件
上传的附件:
收藏
免费 36
支持
分享
最新回复 (70)
雪    币: 4256
活跃值: (3843)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
D-t
2
不错
2020-3-3 16:08
0
雪    币: 5235
活跃值: (3260)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
3
支持楼主 学习了
2020-3-3 16:16
1
雪    币: 4256
活跃值: (3843)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
D-t
4
 
最后于 2020-3-3 18:07 被D-t编辑 ,原因:
2020-3-3 16:30
0
雪    币: 89
活跃值: (2013)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2020-3-3 16:41
0
雪    币: 14865
活跃值: (6088)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习。百度加固也vmp了。
Debug.isDebuggerConnected 怎么过?反编译修改然后再编译打包?
最后于 2020-3-3 17:36 被tDasm编辑 ,原因:
2020-3-3 17:33
0
雪    币: 1841
活跃值: (1290)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
7
学习了,感谢楼主
2020-3-3 18:25
0
雪    币: 6197
活跃值: (4629)
能力值: ( LV9,RANK:185 )
在线值:
发帖
回帖
粉丝
8
tDasm 学习。百度加固也vmp了。 Debug.isDebuggerConnected 怎么过?反编译修改然后再编译打包?
我是动态调试的,以调试模式启动app,

ida附加上进程后,在modules窗口搜art.so,

双击找到的模块,在新窗口搜索isDebuggerConnected,

双击找到的函数,在返回处下断,


程序运行后会断在这里,查看返回值x0,当第二次返回1的时候将其改为0就行了。
2020-3-3 19:56
2
雪    币: 977
活跃值: (435)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
9
大佬晚上好 文章中多次提到的 "把垃圾代码删除"  这个干掉 ollvm控制流平坦化的过程或者可以详细说一下。。。大佬写一下的话感觉又能多一个精华文章哪
2020-3-3 21:09
0
雪    币: 1498
活跃值: (1091)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
10
做个猜测,楼主是将混淆的代码f5之后直接复制到c++工程里面编译调试,通过这种方式删掉混淆的代码吗?
2020-3-3 21:17
0
雪    币: 6197
活跃值: (4629)
能力值: ( LV9,RANK:185 )
在线值:
发帖
回帖
粉丝
11
上海刘一刀 大佬晚上好 文章中多次提到的 "把垃圾代码删除" 这个干掉 ollvm控制流平坦化的过程或者可以详细说一下。。。大佬写一下的话感觉又能多一个精华文章哪
混淆后的代码,有很多判断条件是永远为真或永远为假的。
就像下面这个,一个变量x不需要知道它的值是多少,x*(x-1)永远为偶数,最后一位永远为0,将其和1按位与之后永远为0。
然后根据这些条件就能删掉很大一部分代码了。

2020-3-3 22:40
2
雪    币: 6197
活跃值: (4629)
能力值: ( LV9,RANK:185 )
在线值:
发帖
回帖
粉丝
12
my1988 做个猜测,楼主是将混淆的代码f5之后直接复制到c++工程里面编译调试,通过这种方式删掉混淆的代码吗?
当初还在实习的时候,第一次遇到混淆就是这么干的,一个函数五千行伪代码,调了一两天,
2020-3-3 22:41
2
雪    币: 4883
活跃值: (18890)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
13
膜膜膜膜
2020-3-3 22:53
0
雪    币: 1498
活跃值: (1091)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
14
卧勒个槽 当初还在实习的时候,第一次遇到混淆就是这么干的,一个函数五千行伪代码,调了一两天,[em_78]
我感觉这个方法挺实用的
2020-3-3 23:12
0
雪    币: 977
活跃值: (435)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
15
卧勒个槽 混淆后的代码,有很多判断条件是永远为真或永远为假的。就像下面这个,一个变量x不需要知道它的值是多少,x*(x-1)永远为偶数,最后一位永远为0,将其和1按位与之后永远为0。然后根据这些条件就能删掉很大 ...
受教了
2020-3-3 23:58
1
雪    币: 14530
活跃值: (17548)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
16
mark,大家辛苦了
2020-3-4 00:36
0
雪    币: 4584
活跃值: (2520)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
17
mark 感谢分享
2020-3-4 05:15
0
雪    币: 7796
活跃值: (3500)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
膜拜大佬,分析的很详细
2020-3-4 07:50
0
雪    币: 223
活跃值: (94)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
good 
2020-3-4 15:04
0
雪    币: 14865
活跃值: (6088)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
谢谢。
如果用你修复的libbaiduprotect.so直接替换原来的so,程序能否正常运行?
2020-3-4 16:08
0
雪    币: 6197
活跃值: (4629)
能力值: ( LV9,RANK:185 )
在线值:
发帖
回帖
粉丝
21
tDasm 谢谢。 如果用你修复的libbaiduprotect.so直接替换原来的so,程序能否正常运行?
不能吧,
2020-3-4 18:35
1
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
22
膜拜大佬
2020-3-4 19:33
0
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
 
最后于 2020-3-4 19:34 被卧勒个去编辑 ,原因:
2020-3-4 19:33
0
雪    币: 159
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
学习了,感谢楼主
2020-3-5 10:21
0
雪    币: 5303
活跃值: (1625)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
学习了~
2020-3-5 10:29
1
游客
登录 | 注册 方可回帖
返回
//