首页
社区
课程
招聘
[原创]Android ARM64 内核 Hook 实验框架
发表于: 1天前 1167

[原创]Android ARM64 内核 Hook 实验框架

1天前
1167

KernelHook 是一个面向 Android ARM64/GKI 内核的可加载内核模块(LKM)Hook 框架,源自B佬的KernelPatch 项目。该框架提供了完整的内核函数拦截与修改能力,支持多种 Hook 模式,可用于内核研究、安全分析、系统定制等场景。

接触到kpm不久后就移植了kp的hook和kpm加载框架,中途一直有涂涂改改,但没上什么应用场景测试,索性就开源,欢迎佬们进行应用测试,最终反哺回kp。当前代码框架基本与kp一致,底层稍微动了一下,但不影响kpm的转换

Inline Hook 通过直接修改目标函数入口的指令来实现拦截:

工作流程:

Wrap 模式允许在原函数执行前后插入多个回调:

执行顺序:

针对函数指针表的 Hook,适用于:

框架自动生成支持 0-12 个参数的回调类型:

Inline Hook 覆盖目标函数入口后,需要确保原函数能继续正确执行。ARM64 大量使用 PC-relative 指令,必须重写这些指令的目标地址。

支持的指令类型:

重定位策略:

模块加载时分配一段可执行内存区域用于存储 Hook 元数据和 trampoline 代码:

内存布局:

生命周期管理:

安全的代码修补后端,支持两种路径:

高版本 Android 内核启用 KCFI(Kernel Control Flow Integrity)后,间接调用会检查函数类型。本框架通过 Hook CFI 检查函数来实现绕过:

绕过逻辑:

提供了对系统调用的便捷 Hook 支持:

封装策略:

Rust 编写的模块加载器,简化设备端部署:

功能:

快速使用:

推荐测试组合:

一些小说明

项目链接 658K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6F1K9i4q4A6N6i4q4A6N6i4S2Q4x3V1k6S2M7X3@1$3y4q4)9#2k6X3E0W2M7X3&6W2L8q4)9#2k6X3S2G2L8$3D9`.

指令类型 说明 重定位长度
B, BL 相对跳转 6-8 条
B.cond 条件分支 8 条
ADR, ADRP 地址加载 4 条
LDR literal 常量池加载 6 条
CBZ/CBNZ 条件比较跳转 6 条
TBZ/TBNZ 位测试跳转 6 条
其他 原样复制 2 条
参数 默认值 说明
enable_selftests 0 运行时执行本地自测
enable_inline_selftests 0 执行 inline hook/wrap 自测
enable_fp_selftests 0 执行函数指针 hook/wrap 自测
enable_syscall_selftests 0 执行 syscall hook 自测
enable_vfs_demo 1 安装 vfs_open demo hook
arm64_kernel_hook/
├── lkm.c                    # 模块入口、自测逻辑、vfs_open demo
├── hook.h                   # 对外 API 头文件
├── hook_types.h             # 核心数据类型定义
│
├── hook_chain.c             # Inline hook / wrap 实现
├── fp_hook.c                # 函数指针 hook / wrap 实现
├── hook_reloc.c             # ARM64 指令重定位
├── hook_chain_ops.h         # Hook 链槽位管理
│
├── hotpatch.c               # Text/data patch 后端
├── hmem.c                   # Hook 内存池管理
├── pgtable.c                # 页表操作与权限修改
├── cache.h                  # Cache/TLB flush 辅助
│
├── secpass.c                # KCFI 绕过实现
├── kallsyms_name.c          # 内核符号解析
├── syscall.c                # Syscall hook 封装
│
├── log.h                    # 日志输出宏
├── hook_utils.h             # 工具函数
│
├── hook_runtime.h           # 运行时定义
├── hook_transit_ops.h       # 跳转操作
│
└── loader/                  # Rust 编写的设备端加载器
    ├── src/main.rs
    ├── Cargo.toml
    └── .cargo/config.toml
原始函数入口:     备份并重定位的 trampoline:
  LDR X17, #8        ->  [原始指令1] NOP
  RET X17            ->  [原始指令2] NOP
                     ->  JUMP BACK
hook_wrap(func, argno, before, after, udata);
before A
  before B
    origin_func()  // 原始函数
  after B
after A
// 直接替换
fp_hook(fp_addr, replace_func, &backup);

// 包装模式
fp_hook_wrap(fp_addr, argno, before, after, udata);
typedef void (*hook_chainN_callback)(hook_fargsN_t *fargs, void *udata);

// 使用示例
hook_wrap2(func, my_before, my_after, NULL);  // 2 参数
fp_hook_wrap4(fp_addr, before, after, udata);  // 4 参数
// 模块初始化
text = hook_alloc(PAGE_SIZE * 10);  // 分配 40KB
hook_mem_add((uint64_t)text, size); // 注册到分配器
stop_machine()  // 停止所有在线 CPU
  ↓
fixmap 映射目标页  // 临时映射物理页
  ↓
copy_to_kernel_nofault()写入  // 内核写入
  ↓
caches_clean_inval_pou()  // 按范围同步 I-cache
  ↓
其他 CPU isb()完成  // 同步完成
// 绑定的符号
report_cfi_failure
__cfi_slowpath_diag
__cfi_slowpath
__cfi_check
__cfi_check_fail
syscall_init();  // 初始化,解析 syscall table

// 自动选择最佳路径
hook_syscalln(nr, narg, before, after, udata);
unhook_syscalln(nr, before, after);
# 一键 adb 部署
cargo run -- --adb-run \
  --device-loader target/aarch64-linux-android/release/hook-loader \
  --name hook_module \
  ../hook_module.ko \
  enable_selftests=0 enable_vfs_demo=1
# 仅验证 fp hook
enable_selftests=1 enable_inline_selftests=0 enable_fp_selftests=1 enable_syscall_selftests=0 enable_vfs_demo=0

# 验证全部自测
enable_selftests=1 enable_inline_selftests=1 enable_fp_selftests=1 enable_syscall_selftests=1 enable_vfs_demo=0

# 验证真实 vfs hook
enable_selftests=0 enable_inline_selftests=0 enable_fp_selftests=0 enable_syscall_selftests=0 enable_vfs_demo=1
推荐使用ddk进行构建,ci中也构建好了demo,可在action下载测试
cd loader

# 检查编译
cargo check

# 构建 Android ARM64 版本
cargo build --target aarch64-linux-android --release
// 安装 Hook
hook_err_t hook(void *func, void *replace, void **backup);

// 卸载 Hook
void unhook(void *func);

// 安装 Wrap
hook_err_t hook_wrap(void *func, int32_t argno, void *before, void *after, void *udata);

// 卸载 Wrap
void hook_unwrap(void *func, void *before, void *after);

// 类型化辅助(0-12 参数)
hook_wrap0() ~ hook_wrap12()
// 安装 Hook
int fp_hook(uintptr_t fp_addr, void *replace, void **backup);

// 卸载 Hook
int fp_unhook(uintptr_t fp_addr, void *backup);

// 安装 Wrap
hook_err_t fp_hook_wrap(uintptr_t fp_addr, int32_t argno, void *before, void *after, void *udata);

// 卸载 Wrap
void fp_hook_unwrap(uintptr_t fp_addr, void *before, void *after);

// 类型化辅助(0-12 参数)
fp_hook_wrap0() ~ fp_hook_wrap12()
void my_before(hook_fargs4_t *fargs, void *udata) {
    // 修改参数
    fargs->arg0 += 1;
    // 设置跳过原函数
    // fargs->skip_origin = 1;
}

void my_after(hook_fargs4_t *fargs, void *udata) {
    // 修改返回值
    fargs->ret += 100;
}

// 使用
hook_wrap4(some_func, my_before, my_after, NULL);

接触到kpm不久后就移植了kp的hook和kpm加载框架,中途一直有涂涂改改,但没上什么应用场景测试,索性就开源,欢迎佬们进行应用测试,最终反哺回kp。当前代码框架基本与kp一致,底层稍微动了一下,但不影响kpm的转换

指令类型 说明 重定位长度
B, BL 相对跳转 6-8 条
B.cond 条件分支 8 条
ADR, ADRP 地址加载 4 条
LDR literal 常量池加载 6 条
CBZ/CBNZ 条件比较跳转 6 条
TBZ/TBNZ 位测试跳转 6 条
其他 原样复制 2 条
参数 默认值 说明
enable_selftests 0 运行时执行本地自测
enable_inline_selftests 0 执行 inline hook/wrap 自测
enable_fp_selftests 0 执行函数指针 hook/wrap 自测
enable_syscall_selftests 0 执行 syscall hook 自测
enable_vfs_demo 1 安装 vfs_open demo hook
  • Inline Hook:直接修改目标函数入口指令,实现函数拦截
  • Inline Wrap:支持在函数调用前后插入多个回调,形成链式拦截
  • Function Pointer Hook:修改函数指针表(如 syscall table、ops 结构)
  • Function Pointer Wrap:函数指针的链式包装与回调
  • ARM64 指令重定位:自动处理 PC-relative 指令的重定位
  • KCFI 绕过:兼容高版本内核的 CFI 安全机制
  • 热补丁支持:使用 fixmap + stop_machine 实现安全的代码修补
  • before 回调可以修改参数(fargs->arg0 等)
  • after 回调可以修改返回值(fargs->ret
  • 设置 skip_origin = 1 可跳过原函数
  • Syscall Table(sys_call_table[nr]
  • VFS operations(file_operationsinode_operations
  • 其他函数指针数组
  • 将分配区域划分为多个 hook_mem_warp_t 槽位
  • 每个槽位可存储 hook_thook_chain_tfp_hook_chain_t
  • 使用位图快速查找空闲槽位
  • 支持引用计数,防止提前释放
  • hook_mem_zalloc(): 分配 Hook 内存
  • hook_mem_get(): 增加引用计数
  • hook_mem_put(): 减少引用计数
  • hook_mem_retire(): 标记为已释放但不复用(防止野指针)
  • 临时修改页表权限为可写
  • 直接内存写入
  • flush I-cache
  • 恢复页表权限
  • 检查目标地址是否在本模块 Hook 内存范围内
  • 如果是,直接返回/忽略,允许执行
  • 模块卸载时恢复所有修改
  • 如果能获取 sys_call_table,使用函数指针 hook
  • 否则通过符号名解析,使用 inline hook
  • 自动检测是否使用 syscall wrapper
  • TRAMPOLINE_MAX_NUM = 6
  • RELOCATE_INST_NUM = 56
  • HOOK_CHAIN_NUM = 16
  • FP_HOOK_CHAIN_NUM = 32
  • 1.有的地方函数并没有适配的很好,因为默认测试6.1和6.6的设备,一些有问题的地方可以发issue
  • 2.在hook_alloc和hook_module_prepare_text这个地方有需要的可以去改改
  • 3.可以将hook函数gpl导出,安装hook模块后,编写新的模块直接用(后面有空会规范一下代码推上去,都测试好了的
  1. 备份目标函数入口前 4-5 条指令
  2. 生成跳转指令写入函数入口,跳转到替换函数
  3. 将备份指令搬迁到可执行内存区域并重定位
  4. 卸载时恢复原始指令
  1. 解码指令,提取目标地址
  2. 如果目标在 trampoline 区域,映射到重定位后的新地址
  3. 生成等价的绝对跳转序列

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 14
支持
分享
最新回复 (7)
雪    币: 76
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
看看
1天前
0
雪    币: 375
活跃值: (3736)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
学习一下
1天前
0
雪    币: 104
活跃值: (8407)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
tql
13小时前
0
雪    币: 13
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
qq
10小时前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
tql
5小时前
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
66
3小时前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
666
2小时前
0
游客
登录 | 注册 方可回帖
返回