首页
社区
课程
招聘
[原创]某安全so库深度解析
发表于: 2025-12-3 21:28 3809

[原创]某安全so库深度解析

2025-12-3 21:28
3809

分析对象: sub_1B924 及其完整调用链(so文件见附件)

分析目标: 还原代码逻辑、提取核心对抗算法、复现 Shellcode、制定防御策略

分析深度: 指令级/内核级

技术标签: Anti-Frida, Watchdog, Shellcode Injection, State Machine, ELF Parsing, Ptrace

说明: 仅作为安全技术交流,如有侵权联系删除

经过对提供的 C 伪代码进行逐行审计和静态还原,确认该模块是一个针对 Frida 框架的高级主动防御引擎

这是防御逻辑的起点。代码通过极其繁琐的步骤来隐藏其真实意图。

攻击者没有将字符串存储在 .rodata 段,而是通过硬编码的十六进制数在栈上动态还原。

图片描述

代码定位

解密算法

还原结果

在启动核心逻辑前,代码执行了严格的环境检查。

图片描述

此函数负责定位攻击目标,并启动看门狗线程。它是连接初始化与监控的桥梁。

Android 7.0+ 引入了 Linker Namespace,禁止 APP 直接 dlopen 系统私有库(如 libart.so)。

图片描述

图片描述

图片描述

图片描述

这是一个死循环函数 (__noreturn),负责全方位的环境扫描。

图片描述

图片描述

初始化:解密出一系列黑名单字符串(如 "Xposed", "Frida", "Magisk")。

循环体 (while(1))

这是整个防御体系中最坚固、最隐蔽的堡垒。它通过状态机混淆控制流,通过动态 Shellcode 执行处决。

函数内部维护一个状态变量 v4,初始状态为 293539132

当检测到 Hook 时,代码会解密并执行一段 Shellcode。我们完全静态还原了这段代码。

步骤 A: 提取密钥

图片描述

图片描述

​ 代码逻辑:v10 = *((_DWORD *)&qword_30794 + v8 + -3 * (v9 / 3) + 1);

步骤 B: 数据解密

图片描述

​ 源数据位于 xmmword_30760。代码首先将首字节置为 0x08。

步骤 C: 最终修正 (The Final Trick)

图片描述

代码执行:*(_DWORD *)v16 += 3008; (对前4字节进行整数加法)。

步骤 D: 最终载荷 (The Payload)

将结果解释为 ARM64 汇编:

如果配置不允许 Hook(状态码非 249),程序会进入备用防御模式。

图片描述

图片描述

针对“看门狗+自爆”架构,最优雅的破解方式是让看门狗失效

v27 = 0xA700000099LL; // 密钥低位 0x99, 高位 0xA7
v28 = 236;            // 密钥 0xEC
// ...
*(_QWORD *)v15 = 0xF69F89FA8ECEF5LL; // 密文数据
v27 = 0xA700000099LL; // 密钥低位 0x99, 高位 0xA7
v28 = 236;            // 密钥 0xEC
// ...
*(_QWORD *)v15 = 0xF69F89FA8ECEF5LL; // 密文数据
result = dlopen(v20, 2); // 加载 libc.so/libart.so
if ( result ) {
    v25 = dlsym(v22, v21); // 获取 pthread_create 地址
    v26 = ...; // v26 保存 pthread_create 指针
}
result = dlopen(v20, 2); // 加载 libc.so/libart.so
if ( result ) {
    v25 = dlsym(v22, v21); // 获取 pthread_create 地址
    v26 = ...; // v26 保存 pthread_create 指针
}
// a1: pthread_create (由上层传入)
// sub_1C544: 线程执行体 (看门狗)
// v20: PrettyMethod 的内存地址 (作为参数传递)
return a1(&thread_id, 0, sub_1C544, v20);
// a1: pthread_create (由上层传入)
// sub_1C544: 线程执行体 (看门狗)
// v20: PrettyMethod 的内存地址 (作为参数传递)
return a1(&thread_id, 0, sub_1C544, v20);
Index 原始字节 Key 运算 (XOR) 结果 (Hex)
0 0x08 - Set to 0x08 08
1 0xA7 0xA7 A7 ^ A7 00
2 0x29 0xA9 29 ^ A9 80
3 0x4B 0x99 4B ^ 99 D2
4 0xA6 0xA7 A6 ^ A7 01
5 0xA9 0xA9 A9 ^ A9 00
6 0x99 0x99 99 ^ 99 00
7 0x73 0xA7 73 ^ A7 D4
8 0x69 0xA9 69 ^ A9 C0
9 0x9A 0x99 9A ^ 99 03
10 0xF8 0xA7 F8 ^ A7 5F
11 0x7F 0xA9 7F ^ A9 D6
Hex 汇编指令 含义
C8 0B 80 D2 MOV X8, #94 系统调用号 94 (__NR_exit_group)
01 00 00 D4 SVC #0 Trap to Kernel (执行系统调用,强制退出了)
C0 03 5F D6 RET 之前强制退出了,所以此处为不可达代码
1
2
3
4
v27 = 0xA700000099LL; // 密钥低位 0x99, 高位 0xA7
v28 = 236;            // 密钥 0xEC
// ...
*(_QWORD *)v15 = 0xF69F89FA8ECEF5LL; // 密文数据
1
2
3
4
5
result = dlopen(v20, 2); // 加载 libc.so/libart.so
if ( result ) {
    v25 = dlsym(v22, v21); // 获取 pthread_create 地址
    v26 = ...; // v26 保存 pthread_create 指针
}
1
2
3
4
// a1: pthread_create (由上层传入)
// sub_1C544: 线程执行体 (看门狗)
// v20: PrettyMethod 的内存地址 (作为参数传递)
return a1(&thread_id, 0, sub_1C544, v20);
Index 原始字节 Key 运算 (XOR) 结果 (Hex)
0 0x08 - Set to 0x08 08
1 0xA7 0xA7 A7 ^ A7 00
2 0x29 0xA9 29 ^ A9 80
3 0x4B 0x99 4B ^ 99 D2
4 0xA6 0xA7 A6 ^ A7 01
5 0xA9 0xA9 A9 ^ A9 00
6 0x99 0x99 99 ^ 99 00
7 0x73 0xA7 73 ^ A7 D4
8 0x69 0xA9 69 ^ A9 C0
9 0x9A 0x99 9A ^ 99 03
10 0xF8 0xA7 F8 ^ A7 5F
11 0x7F 0xA9 7F ^ A9 D6
Hex 汇编指令 含义
C8 0B 80 D2 MOV X8, #94 系统调用号 94 (__NR_exit_group)
01 00 00 D4 SVC #0 Trap to Kernel (执行系统调用,强制退出了)
C0 03 5F D6 RET 之前强制退出了,所以此处为不可达代码
  • 核心机制:利用 Frida 为了修复 ART Bug 而必须 Hook art::ArtMethod::PrettyMethod 的特性,部署了一个内存完整性监控陷阱
  • 执行架构
    1. 主线程 (sub_1B924):负责环境清洗、反模拟器检测,并根据 Android 版本适配加载 ART 库。
    2. 监控线程 (sub_1C544):通过 pthread_create 启动,在后台死循环运行,负责扫描文件系统、内存映射和核心函数完整性。
    3. 处决引擎 (sub_26334):一个混淆的状态机函数。一旦检测到异常(Hook),动态解密 Shellcode 并执行 exit_group(0) 强制抹杀进程。
  • 隐蔽性:全程无显式字符串(全部栈上解密),无直接系统调用(通过 Shellcode 完成),无常规 Crash 日志。
  • 代码定位

    1
    2
    3
    4
    v27 = 0xA700000099LL; // 密钥低位 0x99, 高位 0xA7
    v28 = 236;            // 密钥 0xEC
    // ...
    *(_QWORD *)v15 = 0xF69F89FA8ECEF5LL; // 密文数据
  • 解密算法

    • 密钥序列 (Key): 循环使用 [0x99, 0xA7, 0xEC]
    • 操作Plaintext[i] = Ciphertext[i] ^ Key[i % 3]
  • 还原结果

    1. v20 (库名): "libart.so" (或 libc.so,视上下文,此处用于获取线程函数)。
    2. v21 (函数名): "pthread_create"
    • 分析意义:这解释了后续 v26 函数指针的真实身份——它是线程创建函数,而非 Hook 函数。
  • 密钥序列 (Key): 循环使用 [0x99, 0xA7, 0xEC]
  • 操作Plaintext[i] = Ciphertext[i] ^ Key[i % 3]
  • 分析意义:这解释了后续 v26 函数指针的真实身份——它是线程创建函数,而非 Hook 函数。
  • 目的:防止在 ELF 的 Import Table (导入表) 中留下 pthread_create 的痕迹,对抗静态分析工具的交叉引用分析。
  • 配置检查 (sub_CAA8):检查全局变量 dword_48810 是否为 248 (0xF8)。这通常是 SDK 版本适配或功能开关。
  • 硬件黑名单 (sub_12D9C)
    • 逻辑:读取 ro.product.model,检查是否包含字符串 "Firefly-RK3399"
    • 对抗意图:Firefly 开发板是安全研究员常用的低成本 ARM 逆向平台。代码检测到此环境直接跳过核心注入,导致分析人员在特定设备上无法复现恶意行为(“装死”)。
  • 逻辑:读取 ro.product.model,检查是否包含字符串 "Firefly-RK3399"
  • 对抗意图:Firefly 开发板是安全研究员常用的低成本 ARM 逆向平台。代码检测到此环境直接跳过核心注入,导致分析人员在特定设备上无法复现恶意行为(“装死”)。
  • 代码逻辑
    • 检查 SDK 版本 (*off_47FB8)。
    • SDK >= 24: 调用 sub_18D54("libart.so")
    • sub_18D54 原理:这是一个手动 ELF 加载器。它读取 /proc/self/maps 找到 libart.so 的基址,然后手动解析 ELF Header、Program Header 和 Dynamic Segment,从而在不通过系统 Linker 的情况下查找符号。
  • 检查 SDK 版本 (*off_47FB8)。
  • SDK >= 24: 调用 sub_18D54("libart.so")
  • sub_18D54 原理:这是一个手动 ELF 加载器。它读取 /proc/self/maps 找到 libart.so 的基址,然后手动解析 ELF Header、Program Header 和 Dynamic Segment,从而在不通过系统 Linker 的情况下查找符号。
  • 符号解密:函数内部解密出字符串 _ZN3art9ArtMethod12PrettyMethodEb
  • 符号含义:这是 ART 运行时函数 art::ArtMethod::PrettyMethod(bool) 的 C++ Mangled Name。
  • 为什么选它?
    • Frida 的 frida-java-bridge 源码中包含 fixupArtQuickDeliverExceptionBug。(关于frida检测的一个新思路
      • 图片描述
    • 为了修复 Native 线程抛出异常时栈帧缺失导致的 Crash,Frida 必须 Hook 这个函数。
    • 结论:攻击者利用了 Frida 的“刚需”,将其设为陷阱。
  • Frida 的 frida-java-bridge 源码中包含 fixupArtQuickDeliverExceptionBug。(关于frida检测的一个新思路
    • 图片描述
  • 为了修复 Native 线程抛出异常时栈帧缺失导致的 Crash,Frida 必须 Hook 这个函数。
  • 结论:攻击者利用了 Frida 的“刚需”,将其设为陷阱。
  • 图片描述
  • 图片描述

  • 初始化:解密出一系列黑名单字符串(如 "Xposed", "Frida", "Magisk")。

  • 循环体 (while(1))

    1. sub_1BFAC (Mounts Check):遍历 /proc/mounts,查找 MagiskHide 留下的痕迹(如 core/mirror, tmpfs 挂载)。
    2. sub_1C158 (Symlink Check):检查 /system/bin 下的关键文件是否被重定向(Root 隐藏常用手段)。
    3. sub_1C26C (Maps Check):读取 /proc/self/maps,查找内存中是否加载了 frida-agent.so, io.swag.xposed.bridge 等模块。
    4. sub_26334(a1) (核心完整性校验)这是最致命的一步。它检查传入的 PrettyMethod 地址 (a1) 是否被篡改。
    5. 休眠sleep(4),每 4 秒轮询一次。
  • 检测阶段
    • 读取 PrettyMethod 函数头部的 4 字节机器码
    • 比对特征值:1476395088 (Hex: 0x58000050)。
    • 特征含义0x58000050 对应 ARM64 指令 LDR X16, #8。这是 Inline Hook Trampoline 的标准起手式(将跳转地址加载到 X16,然后 BR X16)。(具体原因参考文章:关于frida检测的一个新思路
    • 分支判定
      • 如果等于 0x58000050(发现 Hook) -> 状态跳转至 887579370 (处决状态)
      • 如果不等(未发现 Hook) -> 状态跳转至安全退出。
  • 读取 PrettyMethod 函数头部的 4 字节机器码
  • 比对特征值:1476395088 (Hex: 0x58000050)。
  • 特征含义0x58000050 对应 ARM64 指令 LDR X16, #8。这是 Inline Hook Trampoline 的标准起手式(将跳转地址加载到 X16,然后 BR X16)。(具体原因参考文章:关于frida检测的一个新思路
  • 分支判定
    • 如果等于 0x58000050(发现 Hook) -> 状态跳转至 887579370 (处决状态)
    • 如果不等(未发现 Hook) -> 状态跳转至安全退出。
  • 如果等于 0x58000050(发现 Hook) -> 状态跳转至 887579370 (处决状态)
  • 如果不等(未发现 Hook) -> 状态跳转至安全退出。
  • 基址:0x30794
  • 内存值:99 00..., A7 00..., A9 00...
  • 密钥序列0xA7, 0xA9, 0x99 (循环使用)
  • 前4字节 (Little Endian): 0xD2800008
  • 加数: 3008 (0xBC0)
  • 运算: 0xD2800008 + 0x00000BC0 = 0xD2800BC8
  • 修正后前4字节: C8 0B 80 D2

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-12-4 09:12 被教教我吧~编辑 ,原因: 上传附件
上传的附件:
收藏
免费 144
支持
分享
最新回复 (85)
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2025-12-3 21:48
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-12-3 21:54
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
1
2025-12-3 22:06
0
雪    币: 11
活跃值: (720)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2025-12-3 22:08
0
雪    币: 347
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
学习学习
2025-12-3 22:09
0
雪    币: 3004
活跃值: (3839)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
学习一下
2025-12-4 03:16
0
雪    币: 530
活跃值: (1965)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
666
2025-12-4 10:15
0
雪    币: 4978
活跃值: (5039)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
6666
2025-12-4 10:27
0
雪    币: 228
活跃值: (386)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
大佬厉害
2025-12-4 10:55
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
111
2025-12-4 10:57
0
雪    币: 1495
活跃值: (3678)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
12
1
2025-12-4 11:03
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
```
2025-12-4 11:06
0
雪    币: 0
活跃值: (105)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
感谢~
2025-12-4 11:43
0
雪    币: 433
活跃值: (802)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
感谢分享
2025-12-4 12:00
0
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
666
2025-12-4 12:27
0
雪    币: 3644
活跃值: (5752)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
17
感谢分享,很有用的帖子
2025-12-4 12:31
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
thanks
2025-12-4 12:39
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
666
2025-12-4 12:46
0
雪    币: 240
活跃值: (490)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
666
2025-12-4 13:01
0
雪    币: 144
活跃值: (1878)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
6666
2025-12-4 13:14
0
雪    币: 8195
活跃值: (4698)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
666
2025-12-4 14:44
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
感谢分享
2025-12-4 15:16
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24
666
2025-12-4 15:50
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
1
2025-12-4 16:04
0
游客
登录 | 注册 方可回帖
返回