首页
社区
课程
招聘
[原创]Apatch内核模块之svc监控和栈回溯
发表于: 2025-8-5 17:04 8709

[原创]Apatch内核模块之svc监控和栈回溯

2025-8-5 17:04
8709

现代 app 的壳中有大量的 syscall 的直接调用,想要知道其中的调用参数以及结果有以下方法:
1、使用 frida 进行内存搜索 svc 0 指令的机器码(01 00 00 D4),然后自己想办法干掉
2、使用 seccomp 进行拦截,使用 seccomp 的过程中可能会遇到一些奇奇怪怪的问题
3、使用内核模块在内核态进行拦截
在 Apatch 之前编写的内核模块需要针对单独的内核进行编译,并且编译环境还需要完整内核的依赖文件,这种方法还需要修改内核设置并将内核放入到系统映像中重新刷机。非常复杂,使用 Apatch 编译的内核模块加载方便且具有一定的通用性。

下载官方的 github
04aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3q4^5x3e0t1I4i4K6u0r3d9$3g2J5L8X3g2D9f1r3q4@1j5$3S2Q4x3V1k6@1M7X3g2W2i4K6u0r3k6r3g2$3
下载 gun 编译工具链
ce4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2S2M7X3#2Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8%4N6F1L8r3!0S2k6s2y4Q4x3V1k6Q4x3X3c8Q4x3V1k6S2M7X3#2Q4x3X3c8Y4L8Y4g2Q4x3X3c8@1L8$3!0D9j5$3S2S2K9h3&6Q4x3X3c8V1L8%4N6F1L8r3!0S2k6s2x3`.
添加环境

打开官方目录中的 kpms/demo-hello,使用 make 编译

会在本目录下生成 hello.kpm 文件,把该文件 push 到手机里,然后在 Apatch 内核模块页面加载
加载成功后可以在 adb shell(root 权限)下使用 dmesg 指令输出内核日志

在官方目录中 kpms/demo-syscall 下有关于 openat 的 hook
我们注意到在 init 中有两种 apatch 提供的 hook 方式,一种是fp_hook_syscalln,一种是inline_hook_syscalln。
笔者的需求是监控 readlinkat 的入参和栈回溯,那么可以使用inline_hook_syscalln 实现
这里复制一下一整个目录,修改一下目录名
将 syscall.c 中无关的内容去除,新增一个before_readlinkat 函数,根据参数表
编写 log 输出

这里说明一下get_task_comm_func 函数,这个是内核导出函数get_task_comm
需要在前面定义函数类型,然后通过kallsyms_lookup_name 函数获取函数指针调用(相当于 dlsym 获取函数指针)

内核导出函数列表可以查看/proc/kallsyms 文件

我们想要定位哪里调用的 svc 就需要用到栈回溯,由于内核中没有直接可以进行栈回溯的函数,这个需要我们自己实现,arm64 下的栈回溯实现主要是靠 fp 寄存器。
首先知道,arm64 寄存器中 x29 是 fp 寄存器,x30 是 lr 寄存器,其中 lr 寄存器存储了函数的返回地址,如果是调用多层函数的话那么会把 lr 寄存器和 fp 寄存器中的值存储到栈中并将地址存储到 fp 寄存器中,具体如下图
图片描述
那么我们只需要反复读取 fp 寄存器中的值就可以拿到以往 lr 寄存器中的值。
栈回溯代码如下

此处解释一下几个函数
1、get_task_mm 获取进程 mm_struct(用户态虚拟内存) 防止被销毁
2、access_process_vm 读写用户态中的内存空间,第一个参数是 task 也就进程状态,第二个参数是目标地址,第三个参数是为读写数据分配的缓冲区地址 ,第四个参数是读写数据的长度,第五个参数是 0(读)1(写)
3、 mmput 只用于释放 get_task_mm() 获取的对象 。
实现效果如图:
图片描述

export PATH=$PATH:/opt/arm-gnu-toolchain/bin/aarch64-none-elf-
export PATH=$PATH:/opt/arm-gnu-toolchain/bin/aarch64-none-elf-
make
make
void before_readlinkat(hook_fargs4_t *args, void *udata){
    const char __user *filename = (typeof(filename))syscall_argn(args, 1);
    char buf[1024];
    compat_strncpy_from_user(buf, filename, sizeof(buf));
    struct task_struct *task = current;
    pid_t pid = -1, tgid = -1;
    if (__task_pid_nr_ns) {
        pid = __task_pid_nr_ns(task, PIDTYPE_PID, 0);
        tgid = __task_pid_nr_ns(task, PIDTYPE_TGID, 0);
    }
    char comm[1024];
    get_task_comm_func(comm, sizeof(comm), task);
    pr_info("readlinkat pid: %d, filename: %s, process: %s", pid, buf, comm);
    struct pt_regs *regs = _task_pt_reg(task);
    unwind_user_stack(task, pid);
}
void before_readlinkat(hook_fargs4_t *args, void *udata){
    const char __user *filename = (typeof(filename))syscall_argn(args, 1);
    char buf[1024];
    compat_strncpy_from_user(buf, filename, sizeof(buf));
    struct task_struct *task = current;
    pid_t pid = -1, tgid = -1;
    if (__task_pid_nr_ns) {
        pid = __task_pid_nr_ns(task, PIDTYPE_PID, 0);
        tgid = __task_pid_nr_ns(task, PIDTYPE_TGID, 0);
    }
    char comm[1024];
    get_task_comm_func(comm, sizeof(comm), task);
    pr_info("readlinkat pid: %d, filename: %s, process: %s", pid, buf, comm);
    struct pt_regs *regs = _task_pt_reg(task);
    unwind_user_stack(task, pid);
}
typedef struct mm_struct *(*get_task_mm)(struct task_struct *task);
get_task_mm get_task_mm_func;
get_task_comm_func = (typeof(__get_task_comm))kallsyms_lookup_name("__get_task_comm");
typedef struct mm_struct *(*get_task_mm)(struct task_struct *task);
get_task_mm get_task_mm_func;
get_task_comm_func = (typeof(__get_task_comm))kallsyms_lookup_name("__get_task_comm");
cat /proc/kallsyms
cat /proc/kallsyms
typedef int (*access_process_vm) (struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
access_process_vm access_process_vm_func;
typedef struct mm_struct *(*get_task_mm)(struct task_struct *task);
get_task_mm get_task_mm_func;
typedef void (*mmput) (struct mm_struct *mm);
mmput mmput_func;
 
struct user_frame {
    unsigned long fp;
    unsigned long lr;
};
 
bool unwind_user_stack(struct task_struct *task, pid_t pid){
    struct user_frame frame;
    int i;
 
    struct pt_regs *regs = _task_pt_reg(task);
    frame.fp = regs->regs[29];
    frame.lr = regs->regs[30];
    unsigned long pc = regs->pc;
 
    struct mm_struct* mm = get_task_mm_func(task);
    if(!mm)return false;
 
    for(i = 0; i < 16; i++){ 
        if(i == 0){
            pr_info("pid: %d, frame[%d]: lr: %llx, fp: %llx pc: %llx\n", pid, i, frame.lr, frame.fp, pc);
        }else{
            pr_info("pid: %d, frame[%d]: lr: %llx, fp: %llx\n", pid, i, frame.lr, frame.fp);
        }
 
        if(!frame.fp || !frame.lr){
            break;
        }
        if(access_process_vm_func(task, frame.fp, &frame, sizeof(struct user_frame), 0) != sizeof(frame)){
            break;
        }
    }
    mmput_func(mm);
}
 
// init
access_process_vm_func = (typeof(access_process_vm))kallsyms_lookup_name("access_process_vm");
get_task_mm_func = (typeof(get_task_mm))kallsyms_lookup_name("get_task_mm");
mmput_func = (typeof(mmput))kallsyms_lookup_name("mmput");
typedef int (*access_process_vm) (struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
access_process_vm access_process_vm_func;
typedef struct mm_struct *(*get_task_mm)(struct task_struct *task);
get_task_mm get_task_mm_func;
typedef void (*mmput) (struct mm_struct *mm);
mmput mmput_func;
 

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2025-8-5 17:05 被孤恒编辑 ,原因: 标题少了一部分
收藏
免费 220
支持
分享
最新回复 (126)
雪    币: 213
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
6666
2025-8-5 17:36
0
雪    币: 3644
活跃值: (5752)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-8-5 20:59
0
雪    币: 1867
活跃值: (4518)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
666
2025-8-5 22:00
0
雪    币: 260
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
1
2025-8-6 00:40
0
雪    币: 104
活跃值: (7074)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
太牛了!
2025-8-6 09:24
0
雪    币: 1391
活跃值: (6838)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
1
2025-8-6 09:48
0
雪    币: 1248
活跃值: (2202)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
可以的这帖子
2025-8-6 10:34
0
雪    币: 601
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
6
2025-8-6 10:49
0
雪    币: 511
活跃值: (2272)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
tql
2025-8-6 10:49
0
雪    币: 134
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
66666
2025-8-6 10:49
0
雪    币: 24
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
666
2025-8-6 10:49
0
雪    币: 157
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
文章写的挺好的, 简单易理解
1.使用inline_hook_syscalln hook  readlinkat 
2.获取当前进程的task_struct结构
3.获取当前task的寄存器信息, 帧指针fp, lr返回地址, pc当前执行地址
4.循环打印帧信息, 通过access_process_vm 获取下一帧数据
2025-8-6 12:07
0
雪    币: 209
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
2025-8-6 14:36
0
雪    币: 193
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
666
2025-8-6 15:49
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
666
2025-8-6 17:45
0
雪    币: 375
活跃值: (3176)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
17
666
2025-8-6 17:53
0
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
2025-8-6 18:25
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
666
2025-8-6 18:27
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
666
2025-8-6 18:29
0
雪    币: 293
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
66666
2025-8-6 20:31
0
雪    币: 110
活跃值: (1269)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
666
2025-8-6 20:44
0
雪    币: 413
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
666
2025-8-6 22:06
0
雪    币: 2412
活跃值: (4649)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
666
2025-8-6 22:58
0
雪    币: 1762
活跃值: (1240)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
25
感谢分享
2025-8-7 10:13
2
游客
登录 | 注册 方可回帖
返回