目录
  1. 1. 一、Cydia Substrate 简介
  2. 2. 二、Inline Hook 原理
  3. 3. 三、核心 API 详解
    1. 3.1. MSHookFunction
    2. 3.2. MSHookMessageEx
  4. 4. 四、实战:Hook libc 函数
    1. 4.1. Hook fopen 监控文件访问
    2. 4.2. Hook dlopen 监控 so 加载
  5. 5. 五、Hook JNI 方法
  6. 6. 六、Cydia Substrate vs Frida vs Xposed
  7. 7. 面试常考问题
【逆向安全技术-工具篇】Native层Hook神器Cydia Substrate

一、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;

// Hook 替换函数
int hooked_func(const char* str, int flags) {
// before: 查看/修改参数
printf("[Hook] Called with: %s, flags=%d\n", str, flags);

// 调用原始函数
int result = original_func(str, flags);

// after: 查看/修改返回值
printf("[Hook] Return value: %d\n", result);

return result; // 可以修改返回值
}

// 安装 Hook(在 so 加载时调用)
void install_hook() {
void* target_addr = dlsym(RTLD_DEFAULT, "target_function_name");

MSHookFunction(
target_addr, // 目标函数地址
(void*)hooked_func, // 替换函数
(void**)&original_func // 保存原始函数指针
);
}

MSHookMessageEx

// 主要用于 hook Objective-C 消息(iOS),Android 上较少使用
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);
}

// 安装 Hook
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>

// JNI_OnLoad 中实现
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");

// Hook 该 JNI 方法
// 注意:MSHookFunction 作用于 Native 函数地址
// JNI 方法的 Native 地址通过 RegisterNatives 或静态符号表获取

return JNI_VERSION_1_6;
}

// 需要先通过 dladdr 或解析 .dynsym 获取 JNI 函数的实际地址

六、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.findExportByNameModule.getExportByName 获取地址。方法三:通过 JNI 方法签名反向查找——Hook RegisterNatives 获取 Java 方法到 Native 函数的映射表。方法四:通过特征字节码(函数序言如 STP X29, X30, [SP, #-...])在内存中搜索定位。

打赏
  • 微信
  • 支付宝

评论