IdanBanani/ELF-Processs-Injection-Linux-Android
GitHub: IdanBanani/ELF-Processs-Injection-Linux-Android
Android/Linux ELF 进程注入技术研究索引,系统梳理从目标进入、远程内存操作、自定义 ELF 加载到 ART/JNI 桥接的完整注入链条与现代 Android 安全边界。
Stars: 9 | Forks: 1
欢迎提交 Pull requests。
- [ ] 尝试运行给定的注入技术代码。
- [ ] 理解每种技术的工作原理
- [ ] 理解攻击向量及链条的不同部分(阶段)(即桥头堡 shellcode、注入到进程内存、LPE、何时创建新进程等。)
- [ ] 描述对自定义静态编译的 PIC ELF(共享对象库)加载器 shellcode 的需求。
- [ ] 运行时注入与修补的对比?
- [ ] 自己实现/改进它。
# 现代 Android 笔记
本仓库是一个研究索引,而不是一份复制粘贴的实现指南。对于现代 Android 而言,有意义的问题不再仅仅是“如何在另一个进程中调用 `dlopen`?”。更棘手的问题是:
- 你能控制进程生命周期的哪个阶段?
- 哪些 SELinux/域和 ptrace 限制会适用?
- 谁来映射库:Android 的 linker 还是自定义加载器?
- 映射地址需要达到何种程度的确定性?
- 一旦驻留,原生代码将如何跨入 ART/JNI/DEX 代码?
以下笔记假设处于一个经过授权的后渗透研究环境中。当本文档在 Android 目标进入(entry)讨论中提到“RCE”时,应将其理解为一条已完成链条的简写:原生代码执行已经存在,已提权至有用的 system/root 同等上下文,并且 SELinux/域限制要么已被绕过,要么已通过策略更改,要么目前不是所研究的阻碍。本仓库探讨的是在此之后加载器/注入器所要执行的操作。
## 简短结论
- 对于现代 zygote 或 `system_server` 生命周期模型,请学习 [ReZygisk](https://github.com/PerformanC/ReZygisk),而不是旧的 BinderJack 风格示例。其有趣的部分不在于模块 API;而在于早期 zygote 追踪器、自定义加载器以及围绕应用/system-server 特化的 hooks。
- 对于向已经运行且可附加的进程进行后期注入,请学习 [AndKittyInjector](https://github.com/MJx0/AndKittyInjector)。它比下面列出的旧示例是一个更完整的现代基于 ptrace 的注入器。
- 对于代码进入目标进程后的 linker 命名空间和符号解析工作,请学习 [xDL](https://github.com/hexhacking/xDL)。
- 对于由 linker 管理的放置,不要跳过 `android_dlextinfo`。`ANDROID_DLEXT_RESERVED_ADDRESS`、`ANDROID_DLEXT_RESERVED_ADDRESS_HINT` 和 `ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE` 标志是请求 linker 使用调用者提供的地址空间的公开 Android 接口。
- 对于真正的自定义 ELF 加载,请与 Chromium/NDK 的 Crazy Linker 和 AOSP bionic 的 linker 进行比较。对于内存中由 linker 介导的加载,请与 LibcoreSyscall 的 `DlExtLibraryLoader` 进行比较。
- 对于驻留后的原生或 ART hooks,请学习 [ShadowHook](https://github.com/bytedance/android-inline-hook)、[ByteHook](https://github.com/bytedance/bhook) 和 [LSPlant](https://github.com/LSPosed/LSPlant)。它们本身不是进程注入器。
- 将 [injectvm-binderjack](https://github.com/Chainfire/injectvm-binderjack) 和简单的 ptrace 示例视为历史参考。它们有助于理解概念,但不足以模拟现代 Android linker 命名空间、zygote 时机、ART 更改或当前策略边界。
## 心智模型
将进程注入视为一系列独立问题的链条:
1. **目标进入**:执行首次跨越进入目标进程的方式。常见类型包括后期 ptrace 附加、早期 zygote 追踪、preload/模块框架,或已经运行中的进程内代码。
2. **远程内存**:暂存内存、文件字节和最终可执行映射如何出现在目标地址空间中。
3. **ELF 加载**:是由 Android 的 linker 执行 `dlopen` / `android_dlopen_ext`,还是由自定义加载器映射 `PT_LOAD` 段并自行应用重定位。
4. **符号发现**:如何定位目标中的地址,通常通过 `/proc//maps`、ELF 解析、同一映射对象从本地到远程的偏移量、linker 元数据或 ART/运行时导出完成。
5. **执行交接**:注入的库是通过导出函数、`JNI_OnLoad`、zygote 模块回调、JNI 原生方法替换还是 ART hook 启动。
6. **托管代码桥接**:仅原生驻留不足以执行 DEX。DEX 字节仍需 ART 可见的加载路径、可恢复的 `JavaVM *`、当前线程的 `JNIEnv *`、类加载器上下文,并与隐藏 API 和进程生命周期限制兼容。
## 内存映射与放置
注入的共享库落入的地址由加载路径决定,而不仅仅是因为发生了远程调用。
**系统 linker 路径**
当目标进程执行 `dlopen` 或 `android_dlopen_ext` 时,Android 的 linker 掌控最终的映射选择、重定位处理、依赖解析和构造函数调用。由 memfd 支持的加载会更改底层对象并避免使用常规文件系统路径,但它本身并不能使最终基地址变得确定。ASLR、现有的 VMA、linker 命名空间规则以及特定于架构的加载器行为依然有影响。
这是许多后期注入器使用的路径。例如,AndKittyInjector 解析远程的 `dlopen` / `__loader_dlopen`,并可通过 `android_dlopen_ext` 使用 memfd 加上 `ANDROID_DLEXT_USE_LIBRARY_FD`。
**`android_dlextinfo` 保留地址路径**
`android_dlopen_ext` 通过 `android_dlextinfo` 接受特定于 Android 的加载选项。这是“让 linker 决定一切”和“编写完整的自定义 ELF 加载器”之间的重要中间地带。
与放置相关的字段是 `reserved_addr` 和 `reserved_size`;它们仅在配对使用以下保留地址标志时才有意义。
这意味着只有当远程 `mmap` 调用创建的保留区域随后通过 `android_dlopen_ext` 和 `android_dlextinfo` 交给目标进程 linker 时,该调用才对最终的库放置产生影响。仅用于字符串、临时缓冲区或 RPC 参数的暂存 `mmap` 并不能约束 `dlopen` 映射最终共享对象的位置。
**注入器进程与目标进程**
注入器进程中的操作不会直接在目标进程中保留内存、更改 linker 状态或选择加载地址。Android 进程拥有独立的虚拟地址空间和独立的动态链接器状态。本地的 `dlopen` / `android_dlopen_ext` 实验有助于识别同一磁盘上库的 ABI、API 级别行为、ELF 大小、依赖闭包、SONAME、重定位样式和符号偏移,但它不能使目标进程选择相同的基地址。
有用的关系是间接的:
- **静态元数据** 可以在本地获取。段跨度、对齐方式、`DT_NEEDED` 依赖、导出的符号偏移和 RELRO 大小是 ELF 文件的属性。这些值有助于估计目标侧预留需要多大。
- **绝对地址是进程局部的。** 注入器中的 `dlsym` 结果不是目标地址。最多,如果两个进程中映射了完全相同的库构建版本,则在找到目标自身的映射后,可以重用相对于该库加载偏移的符号偏移。
- **fork 后的本地加载不会传播。** 在从 zygote 分叉后的应用进程内加载库仅影响该进程。
- **fork 前的 zygote 状态可以传播。** 在 fork 前 zygote 中创建的映射和保留空洞可由应用进程和 `system_server` 继承。WebView 的保留地址/RELRO 设计依赖于这种受控的生命周期,而不是一个随机注入器进程在事后影响另一个进程。
- **共享 RELRO 是显式的。** 在一个进程中创建的 RELRO 文件仅当库和相关依赖项加载到相同地址,并且目标加载使用匹配的 `android_dlextinfo` 标志时,才对另一个进程有用。
因此规则是:本地操作可以为目标侧的加载计划提供信息,fork 前的 zygote 操作可以塑造后代,但目标布局仅受目标侧映射、继承的映射以及目标进程实际使用的 `android_dlopen_ext` 参数影响。
其他 `android_dlextinfo` 标志解决相邻问题。将它们视为 linker 策略输入,而不是通用的远程内存 API:
| 标志 | 更改内容 | 重要注意事项 |
| --- | --- | --- |
| `ANDROID_DLEXT_RESERVED_ADDRESS` | 要求 linker 加载到调用者提供的保留范围内。 | 该范围必须已存在于目标进程中且足够大,否则加载将失败。 |
| `ANDROID_DLEXT_RESERVED_ADDRESS_HINT` | 将调用者提供的范围视为首选放置位置。 | 如果范围不合适,linker 可以忽略该提示并选择其他地址。 |
| `ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE` | 将保留地址和 RELRO 行为扩展到新加载的依赖项。 | `reserved_size` 必须覆盖主库加上新的依赖项。不包括已加载的现有依赖项,因此可复现性取决于目标的先前加载状态。 |
| `ANDROID_DLEXT_USE_LIBRARY_FD` | 从 `library_fd` 读取库,而不是通过路径打开它。 | `filename` 参数仍然向 linker 标识该库。 |
| `ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET` | 从 `library_fd_offset` 开始读取。 | 仅在与 `ANDROID_DLEXT_USE_LIBRARY_FD` 一起使用时有效;对于未压缩的 ZIP 条目等嵌入式库很有用。 |
| `ANDROID_DLEXT_FORCE_LOAD` | 避免基于 `stat(2)` 的常规已加载检查。 | 它可以加载具有相同文件名的另一个文件,但当 `DT_SONAME` 重叠时,依赖解析可能仍然倾向于第一个库。它不是放置控制。 |
| `ANDROID_DLEXT_USE_NAMESPACE` | 请求在 `library_namespace` 中加载。 | 在公开文档中标记为内部,因为 NDK 不公开命名空间 API。 |
| `ANDROID_DLEXT_WRITE_RELRO` | 将重定位的 GNU RELRO 页写入 `relro_fd`。 | 仅当另一个进程稍后可以在相同地址映射同一库时才有用。隐含 `ANDROID_DLEXT_USE_RELRO`。 |
| `ANDROID_DLEXT_USE_RELRO` | 从 `relro_fd` 复用相同的重定位 RELRO 页。 | 主要用于地址放置和依赖状态受控的 WebView 风格共享库布局。 |
递归保留地址模式是最接近确定性多库放置的公开 linker 特性:新加载的依赖项按 `DT_NEEDED` 顺序从 `reserved_addr` 连续放置。这种确定性是有条件的。它取决于保留区域是否足够大,以及未加载的依赖项集是否稳定。
**RELRO 共享不是放置**
`ANDROID_DLEXT_WRITE_RELRO` 和 `ANDROID_DLEXT_USE_RELRO` 常出现在保留地址加载旁边,但它们解决的是不同的问题。`WRITE_RELRO` 将已重定位的 GNU RELRO 页序列化到 `relro_fd`。`USE_RELRO` 稍后将目标进程已重定位的 RELRO 页与该文件进行比较,并将文件支持的页映射到匹配的页范围。
这意味着 RELRO 共享依赖于布局的一致性;它不会创造布局的一致性。如果库或其新加载的依赖项落在不同的基地址上,许多 RELRO 页可能会因为包含重定位的绝对指针而不同。然后 linker 可以跳过不匹配的页,仍然完成加载。因此 `USE_RELRO` 本身不是一个严格的确定性放置检查,也不是一种强制 linker 选择原始地址的方法。
确定性的 WebView 风格模式是组合:
- 在受控的生命周期点预留足够大的地址范围;
- 使用 `ANDROID_DLEXT_RESERVED_ADDRESS`、`ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE` 和 `ANDROID_DLEXT_WRITE_RELRO` 加载一次;
- 之后使用相同的保留范围和 `ANDROID_DLEXT_USE_RELRO` 加载;
- 保持新加载依赖项的集合和顺序一致。
在该模式中,保留地址加载是放置机制,而 RELRO 是内存共享/结果复用机制。RELRO 可以揭示部分一致性,因为相同的页从文件中重新映射,但它不应被视为使布局具有确定性的因素。
**选择保留范围**
对于后期的任意目标,没有通用的“接近 100%”的固定起始地址。可靠的基元不是猜测一个神奇的空洞;而是在目标地址空间中创建保留,然后将确切返回的范围提供给目标 linker。
对于一个已经运行的目标进程,最可靠的模型是:
- 创建一个足以用于预期 linker 管理加载的目标侧匿名 `PROT_NONE` 保留;
- 让目标内核选择地址,而不是硬编码一个地址;
- 保持该保留活跃,直到 `android_dlopen_ext` 通过 `reserved_addr` / `reserved_size` 消耗它;
- 将返回的地址视为仅对该目标进程和该时刻有效。
这可以使范围在目标中“现在可用”,但不能使地址在不同的进程或启动之间具有确定性。如果要求在一组进程中使用相同的地址,则更强的设计是在 zygote 或另一个受控生命周期点进行 fork 前保留。这就是为什么 WebView 在 zygote 中保留,而不是在每个应用进程已存在后试图寻找完美空洞。
通过读取 `/proc//maps` 并寻找大的间隙来选择固定只是一种启发式方法。它可以在安静的进程中工作,但存在竞争和可移植性问题:其他目标线程可以在观察和保留之间进行分配,ART/JIT/原生分配可以更改 VMA 布局,ASLR 策略各异,且 32 位进程的空间要小得多。如果必须尝试固定放置,则非破坏性的保留基元更为可取。在 Linux 上,为此类原子性的“使用此确切范围或失败”行为添加了 `MAP_FIXED_NOREPLACE`;旧内核可能不以相同方式支持它,因此健壮的代码必须验证返回的地址是否确实是请求的地址。普通的 `MAP_FIXED` 用于此目的是危险的,因为重叠会丢弃现有的映射。
“足够大”也有精确的含义。对于主 ELF,从页对齐后所有 `PT_LOAD` 段的跨度开始,而不是文件大小。Bionic 的 linker 在保留地址空间之前根据程序头计算此加载跨度。在较新的设备上,页大小迁移和 16 KiB 兼容性可以增加填充。对于 `ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE`,保留还必须覆盖将要新加载的每个依赖项,按 linker 加载它们的顺序。目标中已存在的依赖项不消耗该保留范围,这是可复现性取决于目标先前 linker 状态的另一个原因。
在 64 位系统上,超额保留通常足够实用,以至于生产代码可以慷慨地保留;当前的 WebView 因此使用了大量的 zygote 保留。在 32 位系统上,大保留更容易失败或耗尽进程,因此精确的依赖/加载跨度估计更为重要。
**保留地址标志的已知公开用途**
典型的生产用途是 Android WebView,而不是公共注入器。WebView 在 zygote 中保留地址空间,通过将原生库加载到该区域来创建 RELRO 文件,稍后在 WebView 进程中使用相同的保留区域加上 RELRO 文件加载同一库。当前的 AOSP 加载器代码使用以下组合:
- `ANDROID_DLEXT_RESERVED_ADDRESS`
- `ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE`
- `ANDROID_DLEXT_WRITE_RELRO`
- `ANDROID_DLEXT_USE_RELRO`
- `ANDROID_DLEXT_USE_NAMESPACE`
这正是这些标志设计用于的可靠性模型:受控的多进程加载,其中加载器控制保留地址范围、命名空间、RELRO 文件和依赖集。这并不是任意后期注入器可以通过一次远程 `mmap` 强制实现确定性放置的一般保证。
**WebView 模式的影响和意义**
WebView 值得研究,因为它展示了保留地址加上 RELRO 加载的预期端到端契约:
- **保留是继承的,不是猜测的。** zygote 在 fork 应用进程之前创建一个大的匿名 `PROT_NONE` 保留。后代继承相同的虚拟地址空洞,因此后来的 WebView 加载可以请求 linker 消耗该已知范围。这比扫描 fork 后的进程以寻找方便的间隙要强大得多。
- **RELRO 是从特定地址的加载中生成的。** 一个单独的 RELRO 创建者进程使用 `ANDROID_DLEXT_WRITE_RELRO` 将 WebView 库加载到保留范围。生成的文件包含重定位的只读页,只有当相关布局和依赖状态匹配时,其内容才与将来的加载相匹配。
- **后期的应用加载复用 RELRO 文件,而不是从中重定位。** 应用进程使用相同的保留和 `ANDROID_DLEXT_USE_RELRO` 加载相同的库。linker 仍执行实际的加载和重定位;RELRO 文件允许它用文件支持的共享页替换匹配的重定位只读页。
- **实际收益是内存共享和可预测的布局。** 许多应用使用 WebView,因此共享相同的重定位 RELRO 页减少了每个进程的内存成本。可预测的放置使这种共享成为可能。
- **代价是地址空间的承诺。** 保留的空洞必须足以容纳主库和新加载的依赖项。Android 可以在 64 位上慷慨地保留,但相同的想法在 32 位上受到更多限制。
- **依赖集很重要。** 递归保留地址加载适用于新加载的依赖项。如果一个依赖项已在一个进程中加载,而在另一个进程中未加载,则保留范围消耗和 RELRO 内容可能会有所不同。
- **失败会降低优化或严格加载的效果。** 如果无法满足严格保留的放置,linker 管理的加载可能会失败。如果 RELRO 页不匹配,则这些页的 RELRO 共享将被减少或跳过,而不是成为神奇的放置修复。
对于注入研究,意义是具体的:如果代码仅在一个正常的 fork 后应用进程中运行,它不能使不相关的目标进程继承该应用的本地保留。为了在未来的目标中获得类似 WebView 的确定性,保留必须在公共祖先(如 zygote)中创建,或者必须通过其自身目标侧的保留独立处理每个目标。
Bionic 自己的 `dlext_test.cpp` 也直接演示了该行为:在预保留范围内的 `RESERVED_ADDRESS` 加载,当范围不合适时 `RESERVED_ADDRESS_HINT` 回退,过小的严格保留失败,以及带有依赖项和 RELRO 共享的递归保留地址加载测试。
Chromium 历史上的 Crazy Linker 相邻但接口不同:在现代 WebView/Trichrome 路径更多依赖 `android_dlopen_ext` 之前,它实现了自己的固定地址加载和 RELRO 共享。想法是相似的:预先安排足够的地址空间,以便多个进程可以加载相同的原生代码布局并共享 RELRO 页。
在为本仓库检查的公开注入器风格项目中,这不那么常见。AndKittyInjector 的 memfd 路径使用 `ANDROID_DLEXT_USE_LIBRARY_FD`,而不是保留地址标志。LibcoreSyscall 的 `DlExtLibraryLoader` 构建了一个 `android_dlextinfo` 并公开扩展标志,但其主要记录价值在于内存中由 linker 介导的加载,而不是 WebView 风格的确定性保留布局。ReZygisk 则通过自定义加载器行为走向另一个方向,而不是依赖 `android_dlextinfo` 保留放置。
换句话说:`android_dlextinfo` 可以影响目标布局,但它影响的是 linker 的映射决定。它不会追溯性地改变不相关的远程分配的行为。
**远程系统调用或 RPC 路径**
远程调用、ptrace 寄存器操作和远程系统调用是传输机制。它们可以在目标中分配暂存缓冲区或保留地址范围,但如果最终交接仍然是普通的 `dlopen`,则 linker 仍然控制库映射。要使远程分配影响由 linker 管理的加载,加载器路径必须通过 `android_dlextinfo` 承载该保留区域。
这就是为什么“我可以调用远程 `mmap`”和“我可以选择最终 `.so` 加载的确切位置”是不同的主张。
**自定义加载器路径**
自定义 ELF 加载器可以使放置更具确定性,因为它可以保留地址范围并相对于选择的加载偏移映射 `PT_LOAD` 段。这伴随着真正的复杂性:重定位、导入符号、TLS、init 数组、内存保护、RELRO、命名空间假设以及 Android/架构差异。ReZygisk 的远程 CSOLoader 路径是比旧的“仅调用远程 dlopen”项目更相关的现代示例。
## 自定义 ELF 加载器示例
对术语要严格。真正的自定义 ELF 加载器不仅仅是“从内存加载字节”,也不仅仅是 `dlopen` 的包装器。严格来说,它自己完成 linker 的大部分工作:
- 解析 ELF 头和程序头;
- 保留地址空间并使用正确的保护映射 `PT_LOAD` 段;
- 解析 `PT_DYNAMIC`;
- 加载或定位依赖项;
- 解析导入的符号;
- 应用 `REL`、`RELA`、`RELR`、PLT/GOT 和特定于架构的重定位;
- 设置最终的内存保护,包括相关的 RELRO;
- 以正确的顺序运行 preinit/init/init-array 代码;
- 当 payload 预期 `JNI_OnLoad` 时桥接到 JNI。
有用的示例分为三个不同的类别:
| 项目 | 类别 | 为什么重要 |
| --- | --- | --- |
| Chromium / NDK Crazy Linker | 严格的自定义 linker,历史性 | 自行映射共享库,支持固定地址和文件偏移加载,处理依赖/重定位,并实现 RELRO 共享。较老,但是最好的公开 Android 自定义 linker 参考之一。 |
| ReZygisk `remote_csoloader.c` | 专用远程加载器 | 不是 Android linker 的通用替代品,但相关,因为它展示了作为 zygote/进程驻留一部分的自定义加载,而不是常规的本地 `dlopen`。 |
| LibcoreSyscall `DlExtLibraryLoader` | 内存中,由 linker 介导的加载器 | 使用 Java/原生内存技巧和 `android_dlopen_ext` 从内存加载 `.so` 的强大现代参考。它很有用,但它不是一个完整的自定义 linker,因为 Android 的 linker 仍然执行最终的加载/重定位工作。 |
| AOSP bionic linker | 参考实现 | 非自定义,但这是自定义加载器试图复制或有意避免的行为:段映射、重定位、命名空间、TLS、构造函数、RELRO 和 linker 簿记。 |
如果 Python2 被冻结到原生可执行文件中或嵌入了 CPython 运行时,它可以在运行时出现。如果这些依赖项被捆绑或在目标进程中可加载,它也可以使用原生 ELF 库或绑定,例如 `libelf`/elfutils 风格的解析器。因此语言不是决定性因素。
决定性因素是真正的加载器语义所在的位置。如果冻结的 Python 仅协调原生辅助程序、调用捆绑的 C 扩展,或将字节交给 Android 的 linker,则它是一个 Python 前端加载器或协调器。如果嵌入式运行时本身解析 ELF、选择映射、驱动目标 `mmap`/`mprotect` 或等效基元、应用架构重定位、处理符号解析,并在目标进程内运行 init/JNI 入口点,那么称之为 Python 实现的自定义加载器是合理的。
在实践中,C/C++、带有原生内存基元的 Java/Kotlin 或小的汇编/shellcode 存根更常用于目标内加载器核心,因为它们避免了发布大型解释器,减少了依赖/引导问题,并使特定于架构的重定位、TLS、缓存维护以及信号或线程状态约束更容易控制。Python 或 Python2 更常作为加载器周围的工具、打包器/生成器、exploit 协调器或冻结载体出现。
快速过滤器很简单:如果一个项目不映射 `PT_LOAD`、解析 `PT_DYNAMIC`、解析符号、应用重定位、设置段保护、运行 init 数组以及在预期 JNI 时处理 `JNI_OnLoad`,它可能不是一个严格的自定义 Android ELF 加载器。它仍然可能是一个非常有用的 `dlopen`、`android_dlopen_ext`、memfd、内存中加载或 hook 框架。
## 目标中的符号定位
不要假设来自注入器进程的符号地址在目标进程中有效。常见模式是:
- 在目标进程中找到库映射;
- 识别等效的本地库或直接解析目标 ELF;
- 计算相对于映射对象的符号偏移,然后将这些偏移重定位到目标映射上;
- 考虑 Android 版本、APEX 路径、linker 命名空间行为、剥离的符号和架构。
对于 ART/JNI 工作,现代项目倾向于避免旧的直接 `android::AndroidRuntime::mJavaVM` 假设。更便携的进程内模式是在可用时通过导出的 JNI/ART 入口点恢复 `JavaVM *`,然后使用 `GetEnv` 或 `AttachCurrentThread` 为当前线程获取 `JNIEnv *`。另一个有效的模式是在生命周期点运行,此时 Android 将当前线程的 `JNIEnv *` 传递给 hook 或回调。
例如,AndKittyInjector 在目标的 `libart.so` 中寻找 `JNI_GetCreatedJavaVMs`,如果它能恢复有效的 `JavaVM`,则调用 `JNI_OnLoad`。
对于代码驻留后的 linker 命名空间和符号发现,xDL 是一个有用的源代码级参考,因为它包含针对 linker 内部和调用者地址敏感加载的 Android 版本特定处理。
## DEX 和托管代码
将 DEX 字节写入目标进程仅是存储。这与将 Java/Kotlin 代码加载到 ART 中不同。
托管代码桥接通常需要:
- 原生代码已在目标进程中运行;
- 有效的 `JavaVM *` 加上当前附加线程的 `JNIEnv *`;
- 兼容的类加载器策略,例如适合进程的加载器或在支持它的 Android 版本上的内存中加载器;
- 了解隐藏 API 限制、进程类路径、目标 UID/域,以及进程是处于特化前、特化后还是已经在运行正常的框架代码。
对于 zygote 和 `system_server`,生命周期点比 DEX 容器更重要。在特化之前运行的代码具有不同的可见性、权限、文件描述符、挂载命名空间和类加载器假设,而不是注入到长时间运行的进程中的代码。
## 基于源码的项目笔记
| 项目 | 学习什么 | 为什么重要 |
| --- | --- | --- |
| [ReZygisk](https://github.com/PerformanC/ReZygisk) | 早期 zygote 追踪、自定义远程加载器、zygote JNI hooks、应用/服务器特化回调 | 理解 zygote 和 `system_server` 生命周期注入概念的最佳现代公开来源 |
| [AndKittyInjector](https://github.com/MJx0/AndKittyInjector) | 后期 ptrace 附加、远程函数/系统调用、memfd + `android_dlopen_ext`、`JNI_OnLoad` 交接 | 向已经运行且可附加的进程进行直接注入的更强现代基线。其当前的 memfd 路径使用 `ANDROID_DLEXT_USE_LIBRARY_FD`;保留地址 `android_dlextinfo` 行为是一个单独的放置关注点。 |
| Chromium / NDK Crazy Linker | 自定义 ELF 映射、依赖加载、重定位、固定地址加载、RELRO 共享 | 在评估“自定义加载器”主张时进行比较的最佳历史公开 Android 自定义 linker。 |
| LibcoreSyscall `DlExtLibraryLoader` | 内存中 `.so` 加载、Java 原生内存基元、`android_dlopen_ext`、手动 `JNI_OnLoad` | 有用的现代进程内加载器参考;不是远程注入器,也不是严格的自定义 linker。 |
| AOSP bionic linker | Android 的真正 linker 实现 | 命名空间、重定位、TLS、RELRO、构造函数和由 linker 管理的映射的参考行为。 |
| [xDL](https://github.com/hexhacking/xDL) | Linker 命名空间绕过概念、增强的 `dlopen` / `dlsym`、`dl_iterate_phdr` 处理 | 在驻留后用于在现代 Android linker 行为下定位库和符号很有用。 |
| [ShadowHook](https://github.com/bytedance/android-inline-hook) | 内联原生 hooks | 注入后的原生检测,本身不是目标进入。 |
| [ByteHook](https://github.com/bytedance/bhook) | PLT/GOT hooks 和 `dlopen` 监控 | 注入后的 hook 管理和动态库加载监控。 |
| [LSPlant](https://github.com/LSPosed/LSPlant) | ART 方法 hooking | 在原生代码已经驻留后的托管/ART 行为。 |
| [AndroidPtraceInject](https://github.com/SsageParuders/AndroidPtraceInject) | 教育 ptrace `mmap` + `dlopen` 流程 | 适合学习经典形式,作为现代 Android 设计较弱。 |
| [injectvm-binderjack](https://github.com/Chainfire/injectvm-binderjack) | 历史 `system_server` 实验、`AndroidRuntime::mJavaVM`、Binder 交换 | 宝贵的历史背景,但不是现代基线。 |
## 技术矩阵
本节故意以源码级别和比较的方式进行。它旨在帮助读者理解项目设计选择和先决条件,而不是提供有序的特权进程注入配方。
**ReZygisk**
- 目标进入技巧:从 root/模块上下文监视进程诞生,追踪 `init` fork/exec 事件,检测 `app_process` / zygote 启动,然后将新的 zygote 进程交给 ABI 匹配的追踪器。
- 内存/加载技巧:在早期进程启动期间使用自定义远程 CSOLoader 路径映射其 zygisk 库。这更接近于专用 ELF 映射器,而不是简单的远程 `dlopen`。
- RPC 技巧:使用 ptrace 寄存器控制、`process_vm_readv` / `process_vm_writev`、远程系统调用以及受控的返回/故障点,以便在目标侧执行后重新获得追踪器控制。
- ART/JNI 技巧:驻留后,它从进程内部初始化 JNI hooks。它查找 `JNI_GetCreatedJavaVMs`,获取 `JavaVM`,为当前线程调用 `GetEnv`,并在 Android 自然传递当前线程 `JNIEnv *` 的地方 hook zygote 原生方法。
- 生命周期技巧:hook `nativeForkAndSpecialize`、`nativeSpecializeAppProcess` 和 `nativeForkSystemServer` 的变体,为其提供应用和服务器特化的前/后回调。
- 主要先决条件:root/模块环境、追踪 `init`/zygote 的能力、兼容的 ABI、可写的模块文件、SELinux 策略支持,以及足够早地重新启动或捕获 zygote 的能力。
**AndKittyInjector**
- 目标进入技巧:尽可能使用 `PTRACE_SEIZE` 附加到已经运行的目标 PID,否则回退到 `PTRACE_ATTACH`。
- 内存/加载技巧:使用远程暂存内存和对目标 linker 的远程调用。它可以按路径加载,或者将库复制到目标侧的 memfd 中并使用 `ANDROID_DLEXT_USE_LIBRARY_FD` 调用 `android_dlopen_ext`。
- RPC 技巧:通过保存目标寄存器、根据 ABI 将参数放入目标寄存器/堆栈、将 PC 设置为目标函数、等待完成,然后恢复状态来构建远程函数调用。
- 符号技巧:扫描目标 maps/ELF 对象,在目标地址空间中解析 `dlopen`、`android_dlopen_ext`、`dlsym`、`dlclose` 和 linker 符号,而不是直接复用本地地址。
- ART/JNI 技巧:库映射后,它在目标 `libart.so` 中找到 `JNI_GetCreatedJavaVMs`,远程调用它以恢复 `JavaVM`,然后调用注入库的 `JNI_OnLoad(JavaVM *, key)`。然后库可以通过正常的 JNI 规则获取其自己当前线程的 `JNIEnv *`。
- 主要先决条件:作为可以 ptrace 目标的进程执行原生代码、匹配的 32/64 位 ABI、目标可读的 maps/内存、目标 linker 符号对于所选路径足够可用、SELinux/域权限,以及可以安全中断的目标进程。
**injectvm-binderjack**
- 目标进入技巧:经典 ptrace 附加到如 `system_server` 的目标,然后对目标 libc/linker 函数进行远程调用。
- 内存/加载技巧:使用远程 `calloc` 分配目标内存,写入字符串/参数,然后远程调用 `dlopen`。
- VM 技巧:从注入器进程的 `libandroid_runtime.so` 中解析 `android::AndroidRuntime::mJavaVM`,将该符号重定位到目标进程,并将生成的 `JavaVM **` 传递给导出的 payload 函数。
- 托管代码技巧:payload 调用 `AttachCurrentThread` 以获取当前线程的 `JNIEnv *`,创建或复用类加载器,加载 Java 类,并为其 Binder 实验注册原生方法。
- 历史原因:这依赖于旧的框架内部结构、linker 命名空间假设、Binder 对象布局假设以及 root shell 风格的执行模型。它对学习有用,但不作为现代基线。
**AndroidPtraceInject**
- 目标进入技巧:直接 `PTRACE_ATTACH` 到目标 PID。
- 内存/加载技巧:远程调用 `mmap`,将库路径写入目标内存,远程调用 `dlopen`,然后可选地远程调用使用 `dlsym` 解析的符号。
- ART/JNI 技巧:无内置。它是一个原生加载器示例,而不是现代 ART/DEX 桥接。
- 主要先决条件:ptrace 权限、ABI 兼容性、足够宽松的 SELinux/域状态、目标 linker 可以加载的路径以及容忍停止目标进程。
**xDL, ShadowHook, ByteHook, LSPlant**
- 目标进入技巧:无。这些库假设代码已驻留在进程中。
- xDL 帮助驻留后的 linker 命名空间和符号查找。
- ShadowHook 和 ByteHook 帮助驻留后的原生内联/PLT/GOT hooks。
- LSPlant 帮助在驻留后并从具有有效 `JNIEnv *` 的线程初始化后进行 ART 方法 hooks,并支持 ART 符号解析。
## JavaVM、JNIEnv 和 DEX 笔记
在现代 Android 上,“在内存中查找 Dalvik VM”通常是错误的心智模型。现代 Android 使用 ART,并且 `JNIEnv *` 是线程局部的。稳定的全局 `JNIEnv *` 通常不是这些项目恢复或存储的。
通常的模式是:
- 恢复或接收 `JavaVM *`,然后在特定线程需要其自己有效的 `JNIEnv *` 时调用 `GetEnv` 或 `AttachCurrentThread`;
- 在 Android 已经传递了 `JNIEnv *` 的原生 JNI 方法 hook 内运行;
- 使用有效的 `JavaVM *` 在注入的库中调用 `JNI_OnLoad`,以便库可以使用正常的 JNI 约定进行初始化。
对于 DEX 加载,原生内存写入只是第一步。目标进程仍然需要 ART 可见的加载路径、合适的类加载器以及附加到 VM 的线程。BinderJack 演示了旧的 `PathClassLoader` 风格;现代的内存中加载或 zygote 时加载具有不同的约束,并强烈依赖于 Android 版本、隐藏 API 策略和目标进程生命周期。
## 访问模型
物理访问不是核心的技术要求。等效的链条后执行上下文才是。
对于本文档的基线,假设早期的链条已经产生了通常由 adb/root shell、Magisk/KernelSU/APatch 模块上下文或特权原生进程提供的功能:原生执行、system/root 同等权限,以及足够的 SELinux/域控制来追踪、映射、写入或重启正在讨论的进程。因此,仅在授权的 exploit 链达到该状态之后,非物理情况才在范围内。
重要边界:
- 如果你刻意将假设缩小到仅非特权应用进程 RCE,本笔记的其余部分不再直接适用:该状态不足以在生产 Android 设备上 ptrace `zygote`、`system_server` 或任意更高特权的进程。
- 后期 ptrace 注入器需要追踪/读/写目标进程的权限,并且必须匹配目标 ABI。
- Zygote 生命周期方法假设 root/模块级别或同等的控制,并且在设备可以重启 zygote 或重启时更容易。
- 诸如 xDL、ShadowHook、ByteHook 和 LSPlant 之类的库不解决目标进入问题。它们仅在原生代码已经驻留后才变得有用。
## 商业间谍软件比较
在公开报道中看到的商业 Android 间谍软件应作为一个端到端系统进行比较,而不是作为独立的注入器。植入物加载器只是一个阶段。典型的远程 0-click 或 1-click 操作至少有四层:
1. **投递和 exploit 链**:链接、浏览器、消息传递、网络注入或其他获得初始代码执行的远程向量。
2. **沙箱逃逸 / 提权**:足够的权限以从第一个受损进程跨越到有用的 Android 安全上下文。
3. **加载器/暂存器**:为植入物准备内存、进程驻留、IPC 和持久化条件的代码。
4. **植入框架**:长期监视逻辑、模块加载、命令/控制、数据收集、反取证和更新处理。
这就是为什么将开源注入器直接与商业间谍软件进行比较可能会产生误导的原因。像 AndKittyInjector 这样的项目主要是在操作员已经具有同等本地权限时建模阶段 3。ReZygisk 建模了一个感知 zygote 的加载器/生命周期框架,但它假设的是模块/root 环境,而不是自带远程 exploit 链。
**较早的商业示例**
- Hacking Team RCS Android 是较旧 Android 技术的有用历史比较,但不是因为它是现代 zygote 注入器。Citizen Lab 的公开分析描述了通过社交链接播种的恶意 APK、旧版 rooting exploit 检查以及名为 `rilcap` 的丢弃特权助手,其行为类似于隐藏的 root 命令桥。它与这里的关联在于操作员心态:一旦代码在设备上,加载器/助手的存在是为了创造持久的特权和广泛的控制。它作为现代进程注入基线不太有用。
- Lookout 和 Google 记录的 Pegasus for Android / Chrysaor 是一个 Android 端不同于 iOS 零日故事的例子。公开报道说它使用了一种已知的 rooting 技术,并具有在 rooting 失败时基于权限的回退。对于本仓库,教训是商业 Android 植入物通常针对跨设备状态的操作可靠性进行优化,而不仅仅是最优雅的内存加载器。
- Cytrox / Intellexa Predator 是更相关的现代比较。Google TAG 报告了在浏览器和 Android/内核阶段之后交付 ALIEN(与 PREDATOR 相关的加载器)的 exploit 链。TAG 描述 ALIEN 驻留在多个特权进程内,并通过 IPC 从 PREDATOR 接收命令。Cisco Talos 稍后评估 ALIEN 不仅仅是一个简单的加载器:它帮助设置间谍软件使用的低级功能,并与特权 Android 进程上下文进行交互。
**加载器/注入器视角**
| 报告的家族 | 公开可见的加载器形态 | 最接近的公开研究类比 | 重要不匹配 |
| --- | --- | --- | --- |
| Hacking Team RCS Android | APK 阶段植入加上 root/持久化助手;权限被转换为命令执行和文件系统/系统控制。 | 经典的 Android root 时代工具,不是本仓库中的特定项目。 | 它更多是模拟旧的持久化/助手设计,而不是跨进程 ELF 加载到 ART 时代的特权服务中。 |
| Pegasus for Android / Chrysaor | Root 尝试加上权限回退,当无法获得完全权限时植入行为继续。 | 仅访问模型比较。 | 公开报道对于操作回退设计比加载器内部更有用。 |
| Cytrox / Intellexa Predator | ALIEN 作为围绕 PREDATOR 的加载器/工作者,具有特权进程驻留、inder/IPC 协调以及 zygote/进程上下文感知。 | ReZygisk(生命周期形态),AndKittyInjector(后期原生加载概念),ByteHook/xHook 风格库(进程内 hooks)。 | 商业链条包括公开研究注入器故意不实现的远程 exploit 投递、沙箱逃逸、提权、隐蔽性和植入物编排。 |
因此最有用的比较是架构上的:
- Hacking Team 时代的 Android 技术通常看起来像“获取 root,丢弃助手,持久化,暴露特权操作”。
- 简单的开源注入器看起来像“在拥有足够本地权限的情况下,在另一个进程中映射或 `dlopen` 此共享库”。
- Predator 时代的报道看起来像“使用 exploit 链输出将加载器放置在特权 Android 进程内,然后围绕进程权限和 IPC 协调模块化植入物”。
最后一类是为什么 ReZygisk 在概念上具有信息量,尽管它是一个 root/模块项目:它演示了为什么 zygote 时机、进程特化、JNI 可用性和多进程生命周期 hooks 很重要。它仍然不替换远程 0-click 或 1-click 案例所需的 exploit 链和权限转换层。
**这如何映射到开源项目**
| 商业间谍软件需求 | 最接近的公开研究类比 | 差距 |
| --- | --- | --- |
| 远程投递和初始 RCE | 本仓库中无 | 这属于 exploit 链研究,而不是 ELF 加载器研究。 |
| 沙箱逃逸 / LPE / SELinux 域跨越 | 无直接对应 | 公开注入器通常假设这已经解决。 |
| 向可附加进程进行后期原生库加载 | AndKittyInjector, AndroidPtraceInject | 需要 ptrace/内存权限和兼容的 ABI。 |
| 感知 zygote 的进程生命周期控制 | ReZygisk | 假设 root/模块控制,而不是远程 exploit 链。 |
| 驻留后的 linker 命名空间和符号发现 | xDL | 不提供目标进入。 |
| 驻留后的原生/ART hooks | ShadowHook, ByteHook, LSPlant | 不提供目标进入或持久化。 |
| 多进程植入协调 | ReZygisk 在概念上比后期 ptrace 注入器更接近 | 间谍软件实现增加了隐蔽性、IPC、更新、收集和反取证。 |
**远程 0-click / 1-click 先决条件的现实**
对于非物理情况,加载器不是困难的起点。困难的起点是获得允许加载器执行任何有用操作的进程和权限状态。关于商业间谍软件的公开报道反复显示,在植入加载器可以在特权上下文中操作之前,结合了浏览器或应用 RCE、沙箱逃逸以及 Android 内核或权限 bug 的 exploit 链。没有早期的链条,从 adb/root 或 Magisk 风格模块工作的加载器不会变得可远程部署。
保持这种区别:
- **Exploit 链**:获得代码执行和权限。
- **加载器/注入器**:在所选进程中放置并启动代码。
- **植入物**:执行长期收集、控制和更新行为。
本仓库主要关于第二层及其周围的 Android 运行时/linker 内部结构。
## iOS 野外比较
比较现代 iOS 野外链条确实是有意义的,但仅在架构层进行。平台基元的差异足以使 iOS 代码不应被视为 Android 实现参考。
[DarkSword](https://github.com/ghh-jb/DarkSword/blob/24c9d1419540dd3675a15fb73285d3a4171ae86f/pe_main.js) 在这里很有用,因为公开报道和恢复的源代码显示了与严重 Android 间谍软件相同的更高层模式:
1. 获得初始浏览器/JIT/运行时执行;
2. 逃逸第一个沙箱;
3. 获得更强的进程或内核基元;
4. 选择目标进程以获取其安全上下文;
5. 将更高级别的运行时/payload 注入这些进程;
6. 从所选进程上下文内部运行特定任务的模块。
最接近的 Android 类比不是“DarkSword 等于 `dlopen`”。更好的类比是“DarkSword 的加载器层等于一个特权进程驻留和运行时执行层”,在角色上类似于 Android 上报道的 ALIEN/PREDATOR 关系。
**DarkSword 展示了什么**
- 运行时加载而不仅是 Mach-O 加载:`pe_main.js` 将 JavaScriptCore 加载到远程目标中,创建 `JSContext`,连接原生调用桥,并在该目标进程内评估注入的 JavaScript。
- 进程上下文选择:payload 被放入诸如 `configd`、`wifid`、`securityd`、`UserEventAgent` 等进程和主代理进程中,因为这些进程具有有用的沙箱、entitlement、文件系统或数据访问属性。
- 远程调用机制:代码从 Mach task/thread 知识、异常端口处理、线程状态操作、PAC 感知地址签名和远程任务中的暂存内存构建目标侧函数调用。
- 沙箱交接:`launchd` 和沙箱扩展逻辑被用作将有用访问移入远程任务的一部分。
- 分阶段链设计:Google GTIG 报告 DarkSword 是一个完整的 iOS exploit 链,包括 WebKit/JavaScriptCore RCE、GPU 进程和沙箱阶段、XNU 提权以及 JavaScript 最终 payload。
**这与 Android 有何不同**
| 问题 | 本仓库中的 Android 重点 | iOS DarkSword 风格重点 |
| --- | --- | --- |
| 进程基元 | `ptrace`、`/proc//maps`、`process_vm_*`、SELinux/域检查、zygote 生命周期 | Mach task/thread 对象、异常端口、launchd、沙箱扩展、PAC 签名的线程状态 |
| 加载器/运行时 | Android linker、`android_dlopen_ext`、自定义 ELF 加载器、ART/JNI/DEX 桥接 | `dyld`/`dlopen`、JavaScriptCore 注入、Objective-C 运行时、远程 `JSContext` 执行 |
| 地址控制 | Linker 选择、`android_dlextinfo` 保留范围或自定义 ELF 映射 | Mach VM 映射和 task 内存基元;最终 payload 可能是 JS 而不是原生镜像 |
| 托管代码桥接 | 恢复 `JavaVM *`、获取每线程的 `JNIEnv *`、加载 DEX/类、处理 ART 约束 | 创建或复用 JSC/Objective-C 运行时对象并在所选进程中评估 JavaScript |
| 进程选择 | UID、SELinux 域、zygote/system_server/应用生命周期 | 沙箱配置文件、entitlement/数据访问、daemon 角色、launchd 管理的进程上下文 |
因此比较是有价值的,因为它突出了现代商业链条优化的是什么:不仅仅是“加载一个库”,而是“将 exploit 链输出转化为可复用的远程调用/运行时底层,然后在现有权限解决数据访问问题的进程内运行模块”。这一教训可以完美地转移到 Android。API 细节则不能。
## 源码里程碑
这些是阅读实际实现的有用起点:
- 从 `init`/`app_process` 的 ReZygisk 监视器和交接:
[monitor.c](https://github.com/PerformanC/ReZygisk/blob/e337a086b916923d6190c6fe078abaf0368f0d69/loader/src/ptracer/monitor.c#L544-L779)
- ReZygisk 早期 zygote 注入和返回条目逻辑:
[ptracer.c](https://github.com/PerformanC/ReZygisk/blob/e337a086b916923d6190c6fe078abaf0368f0d69/loader/src/ptracer/ptracer.c#L230-L430)
- ReZygisk 自定义远程 ELF 映射路径:
[remote_csoloader.c](https://github.com/PerformanC/ReZygisk/blob/e337a086b916923d6190c6fe078abaf0368f0d69/loader/src/ptracer/remote_csoloader.c#L701-L960)
- ReZygisk 用于应用和 system-server 特化的 zygote JNI hooks:
[jni_hooks.h](https://github.com/PerformanC/ReZygisk/blob/e337a086b916923d6190c6fe078abaf0368f0d69/loader/src/injector/jni_hooks.h#L422-L458)
- ReZygisk `JavaVM` 恢复和 JNI hook 设置之前的当前线程 `JNIEnv` 设置:
[hook.c](https://github.com/PerformanC/ReZygisk/blob/e337a086b916923d6190c6fe078abaf0368f0d69/loader/src/injector/hook.c#L450-L510)
- Crazy Linker 公共 API 和功能摘要:
[crazy_linker.h](https://android.googlesource.com/platform/ndk/+/0f5c91926653588a6811e633fcfc9b15f6ef2e5f/sources/android/crazy_linker/include/crazy_linker.h)
- Crazy Linker `PT_LOAD` 映射和固定地址加载逻辑:
[crazy_linker_elf_loader.cpp](https://chromium.googlesource.com/android_tools/+/53862978424412e190e9bc40c7637a71fdd7d298/ndk/sources/android/crazy_linker/src/crazy_linker_elf_loader.cpp)
- Crazy Linker 重定位实现:
[crazy_linker_elf_relocator.cpp](https://chromium.googlesource.com/android_tools/+/53862978424412e190e9bc40c7637a71fdd7d298/ndk/sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp)
- Chromium 关于 Android 原生库、Crazy Linker 和 RELRO 共享的笔记:
[android_native_libraries.md](https://chromium.googlesource.com/chromium/src/+/main/docs/android_native_libraries.md)
- LibcoreSyscall 内存中 `android_dlopen_ext` 加载器:
[DlExtLibraryLoader.java](https://github.com/cinit/LibcoreSyscall/blob/086efbb58549b27ab147419ad7d8854953d43663/core-syscall/src/main/java/dev/tmpfs/libcoresyscall/elfloader/DlExtLibraryLoader.java#L139-L407)
- LibcoreSyscall ELF 视图和符号解析:
[ElfView.java](https://github.com/cinit/LibcoreSyscall/blob/086efbb58549b27ab147419ad7d8854953d43663/core-syscall/src/main/java/dev/tmpfs/libcoresyscall/elfloader/ElfView.java#L116-L160)
- LibcoreSyscall 手动 `JNI_OnLoad` 示例:
[TestNativeLoader.java](https://github.com/cinit/LibcoreSyscall/blob/086efbb58549b27ab147419ad7d8854953d43663/demo-app/src/main/java/com/example/test/app/TestNativeLoader.java#L116-L130)
- AOSP bionic linker 段映射和重定位参考:
[linker_phdr.cpp](https://android.googlesource.com/platform/bionic/+/master/linker/linker_phdr.cpp) 和
[linker_relocate.cpp](https://android.googlesource.com/platform/bionic/+/master/linker/linker_relocate.cpp)
- Bionic linker加载跨度计算和调用者提供的保留处理:
[linker_phdr.cpp](https://android.googlesource.com/platform/bionic/+/master/linker/linker_phdr.cpp#415)
- `WRITE_RELRO` 和 `USE_RELRO` 使用的 Bionic GNU RELRO 序列化/映射行为:
[linker_phdr.cpp](https://android.googlesource.com/platform/bionic/+/master/linker/linker_phdr.cpp#1231)
- 关于 `MAP_FIXED`、`MAP_FIXED_NOREPLACE` 和内核选择地址的 Linux `mmap(2)` 笔记:
[mmap(2)](https://man7.org/linux/man-pages/man2/mmap.2.html)
- AOSP WebView Java 侧地址空间保留和 RELRO 编排:
[WebViewLibraryLoader.java](https://android.googlesource.com/platform/frameworks/base/+/main/core/java/android/webkit/WebViewLibraryLoader.java#173)
- 使用带有 `android_dlopen_ext` 的保留地址、递归、命名空间和 RELRO 标志的 AOSP WebView 原生加载器:
[native/webview/loader/loader.cpp](https://android.googlesource.com/platform/frameworks/base/+/master/native/webview/loader/loader.cpp#90)
- 用于保留地址、提示、递归和 RELRO 共享行为的 Bionic 测试:
[dlext_test.cpp](https://android.googlesource.com/platform/bionic/+/master/tests/dlext_test.cpp#267)
- AndKittyInjector ptrace 附加和 ABI 完整性检查:
[main.cpp](https://github.com/MJx0/AndKittyInjector/blob/a3fd7e6b3b9de54b55d77df8355bba23e5c53f9b/AndKittyInjector/src/main.cpp#L192-L250)
- AndKittyInjector 远程 linker 符号解析:
[KittyInjector.cpp](https://github.com/MJx0/AndKittyInjector/blob/a3fd7e6b3b9de54b55d77df8355bba23e5c53f9b/AndKittyInjector/src/Injector/KittyInjector.cpp#L82-L123)
- AndKittyInjector memfd + `android_dlopen_ext` 路径:
[KittyInjector.cpp](https://github.com/MJx0/AndKittyInjector/blob/a3fd7e6b3b9de54b55d77df8355bba23e5c53f9b/AndKittyInjector/src/Injector/KittyInjector.cpp#L551-L623)
- AndKittyInjector 目标 `JavaVM` 发现和 `JNI_OnLoad` 交接:
[KittyInjector.cpp](https://github.com/MJx0/AndKittyInjector/blob/a3fd7e6b3b9de54b55d77df8355bba23e5c53f9b/AndKittyInjector/src/Injector/KittyInjector.cpp#L1171-L1247)
- BinderJack 远程调用基元:
[inject.cpp](https://github.com/Chainfire/injectvm-binderjack/blob/8eb5fee9085d1192fb0c268eefe5f517f36c154e/app/src/main/jni/libinject/inject.cpp#L303-L375)
- BinderJack VM 查找原理:
[README.md](https://github.com/Chainfire/injectvm-binderjack/blob/8eb5fee9085d1192fb0c268eefe5f517f36c154e/README.md#L157-L205)
- Google TAG 关于 Cytrox/Predator Android exploit 链和 ALIEN/PREDATOR:
[Protecting Android users from 0-Day attacks](https://blog.google/threat-analysis-group/protecting-android-users-from-0-day-attacks/)
- Cisco Talos 关于 ALIEN/PREDATOR 植入架构:
[Mercenary mayhem](https://blog.talosintelligence.com/mercenary-intellexa-predator/)
- Lookout 关于 Pegasus for Android / Chrysaor:
[Pegasus for Android](https://www.lookout.com/threat-intelligence/article/pegasus-android)
- Citizen Lab 关于 Hacking Team RCS Android:
[Hacking Team's Tradecraft and Android Implant](https://citizenlab.ca/research/backdoor-hacking-teams-tradecraft-android-implant/)
- Google GTIG 关于 DarkSword 的 iOS exploit 链和 payload 分阶段:
[The Proliferation of DarkSword](https://cloud.google.com/blog/topics/threat-intelligence/darksword-ios-exploit-chain)
- DarkSword 恢复的 `pe_main.js` 进程/运行时注入层:
[pe_main.js](https://github.com/ghh-jb/DarkSword/blob/24c9d1419540dd3675a15fb73285d3a4171ae86f/pe_main.js)
- xDL 特定于 Android 版本的 linker 内部:
[xdl_linker.c](https://github.com/hexhacking/xDL/blob/7112b158e7926cfa932169a8c9ea9813338614ef/xdl/src/main/cpp/xdl_linker.c#L35-L108)
- xDL 调用者地址敏感的强制加载路径:
[xdl_linker.c](https://github.com/hexhacking/xDL/blob/7112b158e7926cfa932169a8c9ea9813338614ef/xdl/src/main/cpp/xdl_linker.c#L197-L230)
- 跨 Android 版本的 ByteHook 动态加载器监控:
[bh_dl_monitor.c](https://github.com/bytedance/bhook/blob/6dc79330472327ac89617e51ecfcc3ee97f311cc/bytehook/src/main/cpp/bh_dl_monitor.c#L53-L70)
- Android NDK `android_dlextinfo` 结构和标志语义:
[android_dlextinfo](https://developer.android.com/ndk/reference/structandroid/dlextinfo) 和
[Dynamic Linker flags](https://developer.android.com/ndk/reference/group/libdl)
- BinderJack 历史 `system_server` 注入路径:
[inject.cpp](https://github.com/Chainfire/injectvm-binderjack/blob/8eb5fee9085d1192fb0c268eefe5f517f36c154e/app/src/main/jni/libinject/inject.cpp#L528-L604)
## 阅读问题
- 目标是否已可附加,或者你需要赢得更早的生命周期点,例如 zygote 启动?
- 设计是否需要确定性基地址,还是仅在加载后进行可靠的符号重定位?
- 如果确定性放置很重要,由 linker 管理的保留地址加载是否足够,还是依赖项/重定位迫使采用自定义加载器设计?
- `dlopen` 是否足够,还是 linker 命名空间和支持文件约束需要 `android_dlopen_ext`、memfd 或自定义加载器?
- 你是仅尝试运行原生代码,还是最终桥接到 ART/DEX?
- 哪些假设在 Android 版本、架构、供应商构建、APEX 布局和 SELinux 域之间会失效?
# 研究论文和文章
- [Linkers & Loaders](https://www.wh0rd.org/books/linkers-and-loaders/linkers_and_loaders.pdf),作者 John R. Levine (1999)
- [Using procfs to execute ELF without touching the disk](https://blog.entysec.com/2023-04-02-remote-elf-loading/)
- [The Nexus between Static and Position Independent Code](https://tmpout.sh/1/10/)
- [Enabling SHELF Loading in Chrome for fun and profit](https://tmpout.sh/2/5.html)
- [General Linux Process injection techniques](https://github.com/itaymigdal/awesome-injection#linux-injection)
# 项目和代码仓库
## 2023
- [Playstation5 ELF Manipulation](
标签:Android安全, ART虚拟机, ELF文件, JNI, LPE, Ptrace, SELinux绕过, Shellcode, SO库加载, SSH蜜罐, UML, Web报告查看器, Zygote, 云资产清单, 共享库注入, 内存Patch, 动态加载, 协议分析, 技术调研, 数据展示, 本地提权, 权限提升, 目录枚举, 移动安全, 红队, 网络安全, 自定义Loader, 进程注入, 逆向工程, 隐私保护