一、Cydia Substrate 简介
Cydia Substrate(http://www.cydiasubstrate.com/)是 saurik 开发的跨平台 Native 层 Hook 框架。在 Android 上,它提供了类似 Xposed 的模块化 Hook 能力,但作用在 C/C++ 层。核心 API 包括 MSHookFunction(inline hook)和 MSHookMessageEx(Objective-C 消息 Hook,主要用于 iOS)。
相比 Frida,Cydia Substrate 的 Hook 是持久化的(设备重启后仍生效),适合开发可发布的 Native Hook 模块。
二、Inline Hook 原理
MSHookFunction 使用标准的 inline hook 技术:
原始函数开头: PUSH {R4-R7, LR} SUB SP, SP, #0x10 ...
Hook 后: LDR PC, [PC, #-4] ; 跳转到 trampoline 0xXXXXXXXX ; trampoline 地址 ...
Trampoline(跳板): 保存寄存器 调用 Hook 回调函数 ; 自定义逻辑 恢复寄存器 执行被覆盖的原始指令 跳回原始函数继续执行
|
三、核心 API 详解
MSHookFunction
#include <substrate.h>
typedef int (*original_func_t)(const char* str, int flags);
static original_func_t original_func = NULL;
int hooked_func(const char* str, int flags) { printf("[Hook] Called with: %s, flags=%d\n", str, flags);
int result = original_func(str, flags);
printf("[Hook] Return value: %d\n", result);
return result; }
void install_hook() { void* target_addr = dlsym(RTLD_DEFAULT, "target_function_name");
MSHookFunction( target_addr, (void*)hooked_func, (void**)&original_func ); }
|
MSHookMessageEx
MSHookMessageEx( [targetClass class], @selector(targetMethod:), imp_implementationWithBlock(replacementBlock), (IMP*)&original_imp );
|
四、实战:Hook libc 函数
Hook fopen 监控文件访问
#include <substrate.h> #include <stdio.h>
static FILE* (*original_fopen)(const char* path, const char* mode) = NULL;
FILE* hooked_fopen(const char* path, const char* mode) { printf("[fopen] Path: %s, Mode: %s\n", path, mode);
if (path && strstr(path, "/sdcard/secret")) { printf("[fopen] Blocked access to secret file!\n"); return NULL; }
return original_fopen(path, mode); }
MSInitialize { MSHookFunction((void*)fopen, (void*)hooked_fopen, (void**)&original_fopen); }
|
Hook dlopen 监控 so 加载
#include <dlfcn.h>
static void* (*original_dlopen)(const char* filename, int flags) = NULL;
void* hooked_dlopen(const char* filename, int flags) { printf("[dlopen] Loading: %s\n", filename); void* handle = original_dlopen(filename, flags); printf("[dlopen] Handle: %p\n", handle); return handle; }
MSInitialize { MSHookFunction((void*)dlopen, (void*)hooked_dlopen, (void**)&original_dlopen); }
|
五、Hook JNI 方法
#include <jni.h>
jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6);
jclass targetClass = (*env)->FindClass(env, "com/example/MyClass"); jmethodID methodId = (*env)->GetMethodID(env, targetClass, "myMethod", "()V");
return JNI_VERSION_1_6; }
|
六、Cydia Substrate vs Frida vs Xposed
| 特性 |
Cydia Substrate |
Frida |
Xposed |
| Hook 层级 |
Native 层 |
Native + Java 层 |
Java 层 |
| 持久化 |
是(重启有效) |
否(需重新 attach) |
是 |
| 脚本语言 |
C/C++ |
JavaScript |
Java |
| Android 兼容 |
4.0 - 7.x(已过时) |
4.2 - 14(持续更新) |
5.0 - 14(LSPosed) |
| 灵活度 |
低(需编译 so) |
高(动态注入 JS) |
中(需编译 APK) |
| 适用场景 |
发布持久化 Native Hook 模块 |
逆向分析动态调试 |
发布持久化 Java Hook 模块 |
现代替代方案: 由于 Cydia Substrate 已多年未更新,推荐使用 Frida 的 Interceptor.attach(动态 Hook)或 Dobby(https://github.com/jmpews/Dobby,轻量级 inline hook 框架)作为替代。
面试常考问题
Q1:inline hook 的原理是什么?有哪些实现方式?
A:inline hook 通过修改目标函数的开头指令,替换为跳转指令(ARM:LDR PC, [PC, #-4] 或 x86:JMP)指向 Hook 函数。关键挑战:(1)需保存并恢复被覆盖的指令(trampoline);(2)需处理 ARM Thumb/ARM 模式切换(地址最低位为 1 表示 Thumb 模式);(3)需刷新指令缓存(cacheflush)确保处理器看到新指令。常见实现有 MSHookFunction、Frida 的 Interceptor、Substrate、Dobby 等。
Q2:Cydia Substrate 已停止维护,为什么还要学习它?
A:(1)理解 inline hook 原理是 Native 逆向的基础,Cydia Substrate 的代码是最清晰的参考实现之一;(2)很多旧版加固方案仍使用 Substrate 对抗;(3)它的架构设计影响了后来的 Hook 框架(如 Frida 的部分设计);(4)在分析一些旧应用时可能直接遇到 Substrate 的模块代码,需要能读懂。
Q3:Hook 一个被混淆的 so 中的函数,如何确定函数地址?
A:方法一:使用 ida64 分析 so 的导出表和 String Xref 找到目标函数偏移,加上 so 的基址(从 /proc/pid/maps 获取)得到绝对地址。方法二:Frida 中调用 Module.findExportByName 或 Module.getExportByName 获取地址。方法三:通过 JNI 方法签名反向查找——Hook RegisterNatives 获取 Java 方法到 Native 函数的映射表。方法四:通过特征字节码(函数序言如 STP X29, X30, [SP, #-...])在内存中搜索定位。