fuqiuluo’s blog

记录美好生活

技术分享

LSPosed - 检测LSPosed的那些事

#LSPosed#安全#安卓
type
Post
status
Published
date
May 3, 2026
slug
summary
在 Android 应用安全领域,LSPosed 作为 Xposed 框架的现代化继承者,为开发者提供了强大的系统级 Hook 能力。然而,对于应用开发者和安全研究人员来说,检测设备上是否存在 LSPosed 框架已成为一项重要的安全需求。本文将详细介绍几种有效的 LSPosed 检测方法,帮助开发者提升应用的安全防护能力。
tags
LSPosed
安全
安卓
category
技术分享
icon
password

引言

在 Android 应用安全领域,LSPosed 作为 Xposed 框架的现代化继承者,为开发者提供了强大的系统级 Hook 能力。然而,对于应用开发者和安全研究人员来说,检测设备上是否存在 LSPosed 框架已成为一项重要的安全需求。本文将详细介绍几种有效的 LSPosed 检测方法,帮助开发者提升应用的安全防护能力。
提前声明:这些检测都没什么鸟用(((

方法一:检测 Linker SO 列表

原理说明

通过扫描进程中加载的动态链接库(SO)列表,检测是否存在与 LSPosed、Magisk、Zygisk 等 Hook 框架相关的特征字符串。这是最基础但也最实用的检测方法之一。

实现代码

技术要点

  • SO List(共享对象列表):操作系统维护的已加载动态链接库列表
  • 多路径扫描:结合 dl_iterate、linker 内部结构和 /proc/self/maps 三种方式,提高检测准确率
  • 字符串归一化:移除下划线和连字符,转换为小写,避免简单的混淆绕过

方法二:检测 dex2oat 编译参数特征

原理说明

LSPosed 通过 mount --bind 方式拦截了 dex2oat 的调用,并添加了 --inline-max-code-units=0 参数以确保正常 Hook。该参数会在编译后的 base.odex 文件中留下痕迹。

检测步骤

  1. 定位应用的 odex 文件:
  1. 提取编译参数:
  1. 检查是否包含 -inline-max-code-units=0 参数

技术说明

  • dex2oat:Android 运行时的 AOT(Ahead-Of-Time)编译器
  • inline-max-code-units:控制方法内联的参数,设置为 0 表示禁用内联优化
  • 此特征在 LSPosed 启用时会持久化到 odex 文件中

方法三:检查 ArtMethod 结构异常

背景知识

ArtMethod 是 Android Runtime(ART)中表示 Java 方法的 C++ 结构体。LSPosed 通过修改 ArtMethod 的入口点(Entry Point)来实现 Hook。

实现步骤

1. 定位 ArtMethod 字段偏移

2. Native 层获取入口点偏移

具体源代码可以看这里:https://github.com/bytedance/btrace/blob/f259e584ba455f44531877c0936b89b62773a344/btrace-android/rhea-library/rhea-inhouse/src/main/cpp/utils/JNIHook.cpp#L155

3. 检测匿名 RWX 内存区域

关键检测点

LSPosed 启用后会自动 Hook 以下方法:
  1. java.lang.Thread.dispatchUncaughtException
  1. android.app.ActivityThread.attach
  1. dalvik.system.DexFile.openInMemoryDexFile
  1. dalvik.system.DexFile.openInMemoryDexFiles
  1. dalvik.system.DexFile.openDexFile
检测策略:获取这些方法的 Method 对象,传递给 Native 层检查入口点是否指向匿名 RWX 内存区域。

技术原理

  • 早期 Xposed:将 Java 方法改为 Native 方法,通过 JNI 入口点实现 Hook
  • LSPosed:创建匿名的可读可写可执行(RWX)内存区域,修改入口点指向该区域
  • 检测依据:正常的方法入口点应指向有名称的代码段,而非匿名 RWX 区域

方法四:内存漫游检测 ClassLoader

原理说明

通过 JVMTI 或反射技术遍历堆内存中的所有 ClassLoader 实例,检查是否存在与 LSPosed 相关的 ClassLoader。

实现代码

实现方式

  • Native 方式:基于 JVMTI 的 API
  • Java 方式:利用反射和 Debug API 遍历对象

参考资源

详细实现可参考 @珍惜 的相关文章或看雪论坛的 ChooseUtils 实现。

方法五:基于时序的侧信道攻击

原理说明

LSPosed 拦截了 ActivityManagerService,对特定的 Transaction Code 会执行额外的检查和处理逻辑。通过测量 Binder 调用的耗时差异,可以推断是否存在 LSPosed。

Transaction Code 定义

检测实现

LSPosed-IT 的变种 Code

LSPosed-IT 版本使用基于 GitHub 用户名生成的动态 Code,以下是已知泄露的部分 Code:

局限性

Android 15+ 限制:当多次触发 Binder 错误时,Binder 连接会被系统冻结,导致此检测方法失效。此外,LSPosed-IT 的 Code 并非固定值,需要持续收集和更新。

方法六:/data/misc 目录异常检测

原理说明

LSPosed 和其他一些Xposed模块在运行时可能会在 /data/misc 目录下创建额外的子目录用于存储配置、日志等数据。由于普通应用无法直接 ls 列出该目录内容,我们可以通过以下策略检测异常:
  1. 使用 stat 系统调用获取 /data/misc 的子目录总数(通过 st_nlink 字段)
  1. 遍历所有已知的系统合法子目录,使用 access 检查其是否存在
  1. 如果实际子目录数量多于已知目录数量,说明存在未知目录
st_nlink 字段:在 Unix/Linux 文件系统中,目录的硬链接数等于 子目录数量 + 2... 各占一个链接)。因此实际子目录数 = st_nlink - 2
权限限制:由于 SELinux 策略限制,普通应用无法使用 opendir/readdir 遍历 /data/misc,但可以:
  • 使用 stat 获取目录元数据
  • 使用 access 检查特定路径是否存在

实现代码

• 由于不同厂商和 Android 版本可能有额外的系统目录,建议根据不同厂商定制不同的列表,减少遍历耗时 • 不同的android版本也有差异,我们可以收集一下不同安卓版本的差异去做一个针对安卓版本的表,减少遍历耗时

参考资料

  1. 看雪论坛 - LSPosed 检测技术讨论
  1. ByteDance btrace 开源项目
  1. LSPosed 开源项目代码分析
  1. Android Runtime (ART) 官方文档

免责声明:本文内容仅供安全研究和技术交流使用,请勿用于非法目的。开发者应遵守相关法律法规和平台规则。
Loading...