某安全so库深度解析

admin 2026-03-19 16:59:44 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档深入分析了一个针对Frida框架的Android高级主动防御引擎,揭示了其利用Frida必须Hookart::ArtMethod::PrettyMethod的特性设置内存完整性监控陷阱。系统阐述了栈上字符串解密、动态加载隐藏导入表、环境指纹检测、手动ELF加载绕过命名空间限制、看门狗线程全方位扫描、状态机混淆处决引擎及Shellcode动态解密执行等核心技术。文档提供了完整的静态分析方法和三种可行的绕过方案,对理解Anti-Frida机制和移动安全对抗具有重要参考价值。 综合评分: 90 文章分类: 逆向分析,移动安全,二进制安全,安全建设,实战经验


cover_image

某安全so库深度解析

教教我吧~ 教教我吧~

看雪学苑

2026年3月17日 17:59 上海

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

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

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

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

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

1. 核心结论 (Executive Summary)

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

  • 核心机制

    利用 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 日志。

2. 详细分析:入口与初始化 (sub_1B924)

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

2.1 字符串解密:栈上异或 (Stack String Obfuscation)

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

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

  • 密钥序列 (Key): 循环使用[0x99, 0xA7, 0xEC]。

  • 操作:Plaintext[i] = Ciphertext[i] ^ Key[i % 3]。

  • 还原结果

    分析意义:这解释了后续v26函数指针的真实身份——它是线程创建函数,而非 Hook 函数。

v20(库名):"libart.so"(或libc.so,视上下文,此处用于获取线程函数)。

v21(函数名):"pthread_create"。

2.2 动态加载 (Dynamic Loading)

result = dlopen(v20, 2); // 加载 libc.so/libart.so
if ( result ) {
    v25 = dlsym(v22, v21); // 获取 pthread_create 地址
    v26 = ...; // v26 保存 pthread_create 指针
}
  • 目的:防止在 ELF 的Import Table(导入表) 中留下pthread_create的痕迹,对抗静态分析工具的交叉引用分析。

2.3 环境指纹检测 (Anti-Environment)

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

  • 配置检查 (sub_CAA8)

    :检查全局变量dword_48810是否为248(0xF8)。这通常是 SDK 版本适配或功能开关。

  • 硬件黑名单 (sub_12D9C)

  • 逻辑

    读取ro.product.model,检查是否包含字符串"Firefly-RK3399"

  • 对抗意图

    Firefly 开发板是安全研究员常用的低成本 ARM 逆向平台。代码检测到此环境直接跳过核心注入,导致分析人员在特定设备上无法复现恶意行为(“装死”)。

3. 核心部署:陷阱安装 (sub_1CEF8)

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

3.1 绕过 Android 7.0+ 命名空间限制

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

  • 代码逻辑

  • 检查 SDK 版本 (*off_47FB8)。

  • SDK >= 24

    调用sub_18D54("libart.so")

  • sub_18D54原理

    这是一个手动 ELF 加载器。它读取/proc/self/maps找到libart.so的基址,然后手动解析 ELF Header、Program Header 和 Dynamic Segment,从而在不通过系统 Linker 的情况下查找符号。

3.2 锁定“诱饵” (Targeting PrettyMethod)

  • 符号解密

    函数内部解密出字符串_ZN3art9ArtMethod12PrettyMethodEb

  • 符号含义

    这是 ART 运行时函数art::ArtMethod::PrettyMethod(bool)的 C++ Mangled Name。

  • 为什么选它?

  • Frida 的frida-java-bridge源码中包含fixupArtQuickDeliverExceptionBug。(关于frida检测的一个新思路)

  • 为了修复 Native 线程抛出异常时栈帧缺失导致的 Crash,Frida必须Hook 这个函数。

  • 结论

    攻击者利用了 Frida 的“刚需”,将其设为陷阱。

3.3 启动监控线程

// a1: pthread_create (由上层传入)
// sub_1C544: 线程执行体 (看门狗)
// v20: PrettyMethod 的内存地址 (作为参数传递)
return a1(&thread_id, 0, sub_1C544, v20);

4. 终极监控:看门狗线程 (sub_1C544)

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

4.1 扫描逻辑详解

  • 初始化:解密出一系列黑名单字符串(如 “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 秒轮询一次。

5. 黑盒揭秘:处决引擎 (sub_26334)

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

5.1 状态机检测逻辑

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

  • 检测阶段

  • 如果等于0x58000050(发现 Hook) -> 状态跳转至887579370(处决状态)

  • 如果不等(未发现 Hook) -> 状态跳转至安全退出。

  • 读取PrettyMethod函数头部的4 字节机器码

  • 比对特征值:1476395088(Hex:0x58000050)。

  • 特征含义

    0x58000050对应 ARM64 指令LDR X16, #8。这是 Inline Hook Trampoline 的标准起手式(将跳转地址加载到 X16,然后 BR X16)。(具体原因参考文章:关于frida检测的一个新思路)

  • 分支判定

5.2 Shellcode 静态还原实战 (Step-by-Step)

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

步骤 A: 提取密钥

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

  • 基址:0x30794
  • 内存值:99 00...,A7 00...,A9 00...
  • 密钥序列:0xA7, 0xA9, 0x99(循环使用)

步骤 B: 数据解密

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

| 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 |

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

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

  • 前4字节 (Little Endian):0xD2800008
  • 加数:3008(0xBC0)
  • 运算:0xD2800008 + 0x00000BC0 = 0xD2800BC8
  • 修正后前4字节:C8 0B 80 D2

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

将结果解释为 ARM64 汇编:

| 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 | 之前强制退出了,所以此处为不可达代码 |

5.3 处决机制详解

  • 行为

    mmap分配 RWX 内存 -> 写入 Shellcode ->j___clear_cache-> 执行。

  • 效果

    :调用exit_group(0)

  • 杀伤力

    :这比exit()更底层、更彻底。它会立即杀死当前进程组中的所有线程。由于没有异常抛出,Frida 甚至来不及捕获 detaching 事件,APP 就会直接消失。

6. 辅助防御体系:sub_1B380

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

  1. 进程伪装 (sub_1B144)
  • 读取/proc/self/cmdline

  • 使用prctl(PR_SET_NAME)和 Java 反射setArgV0修改进程名。

  • 目的

    :在ps列表中伪装成合法系统进程,防止被用户轻易发现并 Kill。

  1. 双进程反调试 (sub_1A0D8)
  • 利用fork创建子进程。

  • 子进程对父进程执行ptrace(PTRACE_ATTACH)

  • 目的

    :抢占 Ptrace 调试端口。当 IDA/GDB 试图附加时,会因端口被占用而失败 (EPERMEBUSY)。

7. 总结与防御建议 (Bypass)

7.1 终极 Bypass 方案

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

  1. 方案 A (推荐): 沉默看门狗
  • 操作

    :Hookpthread_create

  • 逻辑

    :在sub_1CEF8执行期间,检查传入的线程函数地址。如果地址匹配sub_1C544,将其替换为NOP(空函数)。

  • 效果

    :线程正常启动,但没有任何检测逻辑运行。

  1. 方案 B (进阶): 欺骗视觉
  • 操作

    :Hook 内存读取相关函数。

  • 逻辑

    :当sub_26334试图读取PrettyMethod地址时,返回原始未 Hook 的指令字节 (0x...),而非被 Frida 修改后的跳转指令。

  • 效果

    :看门狗永远认为内存是纯净的。

  1. 方案 C (硬核): 阻断自杀
  • 操作

    :Hooksvc指令或mmap

  • 逻辑

    :监控sub_26334内的mmap调用(大小为 28 字节是强特征),或者直接 Patch 掉sub_26334的状态机跳转逻辑。

#

看雪ID:教教我吧~

https://bbs.kanxue.com/user-home-940077.htm

*本文为看雪论坛优秀文章,由 教教我吧~ 原创,转载请注明来自看雪社区

往期推荐

逆向分析某手游基于异常的内存保护

解决Il2cppapi混淆,通杀DumpUnityCs文件

记录一次Unity加固的探索与实现

DLINK路由器命令注入漏洞从1DAY到0DAY

量子安全 quantum ctf Global Hyperlink Zone Hack the box

球分享

球点赞

球在看

点击阅读原文查看更多


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:看雪学苑 教教我吧~ 教教我吧~《某安全so库深度解析》

评论:0   参与:  0