首页
社区
课程
招聘
和爱豆更近一步——某爱豆聊天App反调试绕过
发表于: 2天前 1446

和爱豆更近一步——某爱豆聊天App反调试绕过

2天前
1446

我分析的这款应用是韩国某大型娱乐公司旗下的爱豆聊天App。先选择想聊天的爱豆,然后付费就能和TA聊天了,可以和TA主动发消息。也能看到TA的日常动态。

这个应用的包名是:Y29tLmV2ZXJ5c2luZy5seXNu,版本:MS42LjEw。调试时为最新版。

本文有的地方参考了GPT,有错误的还请大家指正。
有了这个软件,你可以和下图的或其他爱豆聊天了。

使用的是Root过的Google Pixel 3 手机,手机存在的风险有以下几点:

可以看到这个应用至少检测到了我的两个风险,frida和root。

frida脚本如下:

输出结果如下:

这说明检测逻辑位于libdxxcuxd.so 。接下来就应该对这个so进行分析了。

先看看它有哪些初始化函数,现在的壳或者风险检测组件都喜欢把检测的逻辑放在这里,如下图所示。

只有DT_INIT_ARRAY 类型的函数,没有DT_INIT 类型的。如果有DT_INIT 类型的,那么我们优先看DT_INIT 类型的,因为它的执行时间早于DT_INIT_ARRAY 类型的函数。且两者都优先于JNI_OnLoad执行。如下图所示。
图片描述

有两个初始化函数,为了方便阅读,我已经重命名了。

先说说调试的结论,要想用frida绕过这个风险检测,直接nop一个函数是不行的,因为会各种各样奇怪的崩溃问题。所以个人觉得更好的做法是修改跳转,让程序检测不出来即可。

这个函数如下图所示。
图片描述

GPT回答是:

Android App 进程里:
/app_process64 或者:com.xxx.xxx 取决于:
是否被 zygote 派生
有没有改 argv[0]
有没有调用 setproctitle
是否做了反调试/伪装
很多壳、加固、反作弊都会检查它。

我的手机的值如下:

看到字符串.sandboxcom.lbe.parallel ,说明它在检测沙箱、双开环境。

很多虚拟环境,如VirtualApp,双开助手,平行空间,VirtualXposed。会注入:SOMETHING=.sandbox 。或者包含路径/data/user/0/com.xxx.sandbox/

com.lbe.parallel 是平行空间(Parallel Space),著名的双开框架。

检测environ 是因为沙箱特别喜欢修改环境变量,例如:

或者添加:

因此environ 是反虚拟化的重要检测点。

sub_294E0中调用了my_crc32 来检查内存,偏移是sub_10B44
图片描述
图片描述

下图是sub_294E0 的伪代码,含注释。
图片描述

可以看到从A4078处开始,检查10CA8h个字节的内存crc32值。sub_294E0 是被init_arr1 调用的,首次调用时记下crc32到全局变量dword_A4088 ,之后被调用时对比这段内存是否和最初的保持一致,如果不一致,就说明内存被修改了并调用Kill_15DF0 以创建线程的形式退出程序。

LOG_7E2A8 是日志函数,它会打印出风险字符串,也是弹窗显示的文字。

本小节的 sub_294E0 在别处被引用多次,说明有多个地方做了内存完整性检查,因此我们要注意。

sub_164A8 检查了_progname 是否含blackdex 字符串,blackdex是一款脱壳的工具,自然也被组件视为风险因素了。
图片描述

这个函数位于sub_3DDB4,且我使用的frida版本是16.5.9的。

有计算_progname的crc32,和安装异常处理的sigHandler。
图片描述

然后就是经典的检测frida环节。调用了scandir 搜索/proc/%d/task ,%d是进程PID。看看它有哪些线程?

再读取/proc/%s/comm ,看看有没有和frida相关的字符串,有就说明App被frida“入侵”了。
图片描述

这段代码里有好几个flag,可以说是用于做风险分数评估的。根据这些分数风控组件来决定要不要继续执行。

为什么要看/proc/%d/task/proc/%s/comm 呢?

先用frida注入一个自己写的App试试看。列举出有哪些线程。

图片描述

然后逐个线程的看。
图片描述

所以说这是libdxxcuxd.so 在这里检查frida的原因。

不要忘了,本节的sub_3DDB4 还检查了其它的风险,调用了函数RiskDetect_1D25C ,如下图所示。

图片描述

检测的字符串有以下:(/proc/%d/maps中)

返回0表示没有风险,-1表示有风险。

这个函数代码少,暂时不是绕过反调试的关键。
图片描述
sub_1017C 如下图所示。
图片描述

dlopen_46914是自定义的dlopen,v19是输出变量,存放so文件各区段的关键信息。

dlsym_10138dlsym_4B3E8 都是寻找符号的函数,就是找到对应函数名的地址。其中dlsym_10138 内部调用了dlsym_4B3E8

图片描述

图片描述

图片描述

寻找/linker64里的关键函数,然后放到全局变量里。

先寻找切入点。因为风控组件的代码启动的比较早,所以我习惯在linker即将.init_proc 时注入frida的脚本。把linker64 拷贝到电脑上。

用IDA打开google_pixel3_linker64 ,即拷贝出来的linker64

搜索关键字call_constructors ,得到这个函数和偏移是2C274

图片描述

frida脚本如下:

然后就能打印出以下内容:

这说明我们的代码是注入的比较早的。我们得先绕过frida检测,才能绕过其它的风险检测。

该函数被sub_3DDB4 调用。这个函数只是检测了许许多多的风险字符串,内部调用的只是字符串拷贝或者寻找子串的函数,没有额外的影响线程运行的函数。因此把它的返回值改为0即可。

这个函数头部的指令可以改为如下:

对应的frida脚本如下:

调用:

图片描述

我们不能粗暴地把scandir 改成非法的,比如-1 ,这样按照执行流程的话会让App退出的。
图片描述

此外,还有一个全局变量flag2_A5D9C 的值要是0x20 。即绿色部分的指令应该被执行。再来看看init_arr1 处的代码,如下图所示。
图片描述

如果flag2_A5D9C & 0x20 为0,则App会退出。所以此时的想法是让程序在执行scandir 时直接跳转至loc_3E928 处,即不让风控组件扫描目录。

图片描述
图片描述

这样就能让flag2_A5D9C & 0x20 不为0了,也就不会调用kill_15FD0 函数了。

经过调试flag2_A5D9C 可能会被其它的线程或函数修改成flag2_A5D9C & 0x20 为0的情况,为了保险起见,我们还要看哪里调用了sub_3DDB4

有两处调用了sub_3DDB4 ,如下图所示。
图片描述

把条件跳转TBNZ W1, #5, loc_100E8 ,改成B loc_100E8

图片描述

TBNZ W1, #5, loc_8419C 改成b loc_8419C

frida实现强制跳转的代码如下:

调用:

写好的frida脚本此时如下:

但是有报错:

重点是:signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7a4299fcbfpc 0000007a0c45a918 这有助于我们锁定报错的位置。

7a0c45a918 - 7a0c44a000 = 10918h,说明报错的位置在so的10918h处。SEGV_ACCERR 的意思是访问的内存地址是存在的,但是因为权限问题报错。例如某段内存是只读,但这条指令尝试写入,就会出现这种错误。

10918h如下图所示。
图片描述

这是一个字符串拷贝的函数,STRB在存储字符到内存时有权限问题。被调用的地方看lr 0000007a0c4ceba4 ,偏移值是84BA4
图片描述

个人不理解为啥这里会报错?v66,也就是x24的值,如果是风险分数,分数越高,是不是就访问到了权限为只读的内存了呢?这里有点奇怪……加上这个函数代码量太大了,实在分析不下去了哈哈。有懂的朋友可以帮忙看下,谢谢啦。
图片描述

无论如何,先试试再说吧。把出错的内存权限改成rw-。

调用:

部分输出如下:

如此看来确实是因为权限不够导致的错误,至少现在没有再报访问错误了。

root检测一般是检查以下路径的文件是否存在:

幸运的是,这个App的风控组件没有用到什么SVC #0 去打开文件,不然真不好用frida去hook了。

写出的hookFopen脚本如下:

调用:

这里不建议用console.log('Call stack:\n' + Thread.backtrace(null, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n')); 去打印日志,因为打印出来的调用堆栈是错的,在这里准确的方法是打印出x30lr 寄存器的值。并过滤出参数含/su 的。

对了,这里再提一下,组件提前把函数地址写到了全局变量,直接hook 11F2C 效果会好些,能少跟踪一个层级。
图片描述

函数Fill_Global_FuncTable_18FA4 获取libc.so 的很多函数地址写入全局变量里。
图片描述

输出如下:

还有一段错误,提示找不到so文件了,暂且不管它,我们绕过root检测再说。

因为我们的错误是/sbin/su 存在,所以看到这里。

0x78167a8138 - libdxxcuxd.so! 0x7816795000 = 0x13138
图片描述

经过多次调试,得到以下调用的逻辑链条。
图片描述

下两张图分别是sub_1C058sub_3FEA0 的截图。
图片描述
图片描述

那么保险的做法是:

此时完整的代码如下:

App没有提示frida或root。但是它重启了,并再次提示root。这属于正常现象,因为重启之后我们注入的frida代码是不存在的。

也许程序调用了kill 相关的函数促使进程退出的。


[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

上传的附件:
收藏
免费 35
支持
分享
最新回复 (13)
雪    币: 48
活跃值: (434)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
期待更多优质内容的分享,论坛有你更精彩!
2天前
0
雪    币: 158
活跃值: (2216)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2天前
0
雪    币: 3641
活跃值: (4749)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习一下
2天前
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
向大佬学习
1天前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
tql
1天前
0
雪    币: 1115
活跃值: (1841)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
7
666
1天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
终于找到提供app包名的帖子了,其他帖子一个包名都没有提供,这让新人想跟练的机会都没有,感谢UP
1天前
1
雪    币: 158
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
66
13小时前
0
雪    币: 228
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
学习
4小时前
0
雪    币: 292
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
55555555555
1小时前
0
雪    币: 375
活跃值: (3827)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
12
学习一下
1小时前
0
雪    币: 104
活跃值: (8527)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
666
41分钟前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
6分钟前
0
游客
登录 | 注册 方可回帖
返回