简体   繁体   English

如何在较新的 Linux 内核中挂钩 sys_clone

[英]How to hook sys_clone in newer Linux Kernel

I am trying to hook sys_clone , to bypass a method used by the tool unhide http://www.unhide-forensics.info/ .我试图挂钩sys_clone ,绕过工具unhide http://www.unhide-forensics.info/使用的方法。 The idea is to translate PIDs from real to fake, and fake to real, making the tool to see the hidden PIDs and the not hidden as continuous identifiers.这个想法是将 PID 从真实转换为假,将假转换为真实,使工具能够将隐藏的 PID 和未隐藏的 PID 视为连续标识符。 I mean, this tool forks continuously and checks if in all the loops there's some PID that is busy and it's not visible.我的意思是,这个工具不断地分叉并检查在所有循环中是否有一些 PID 很忙并且它不可见。 The idea es to cheat this tool by giving continuous PIDs.通过提供连续的PID来欺骗这个工具的想法。

The problem, is that when a program executes my hook handler for sys_clone , after returning, wait(&status) gives a signal of SEGVFAULT.问题在于,当程序为sys_clone执行我的钩子处理程序时,返回后,wait(&status) 会发出 SEGVFAULT 信号。

I'm following the same method that I'm using to hook all other syscalls, including sys_fork , sys_vfork and the others syscalls.我按照同样的方法,我使用挂钩所有其他系统调用,包括sys_forksys_vfork和其他系统调用。

sys_vfork and sys_fork uses the same function _do_fork() to create the new process: https://elixir.bootlin.com/linux/v4.15.1/source/kernel/fork.c#L2148 sys_vforksys_fork使用相同的功能_do_fork()来创建新的进程: https://elixir.bootlin.com/linux/v4.15.1/source/kernel/fork.c#L2148

https://elixir.bootlin.com/linux/v4.15.1/source/kernel/fork.c#L2111 https://elixir.bootlin.com/linux/v4.15.1/source/kernel/fork.c#L2111

https://elixir.bootlin.com/linux/v4.15.1/source/kernel/fork.c#L2123 https://elixir.bootlin.com/linux/v4.15.1/source/kernel/fork.c#L2123

EDITED : I created a minimal working example, reproducing the bug, so you can test it better, and sorry because I didn't before:编辑:我创建了一个最小的工作示例,重现了错误,所以你可以更好地测试它,抱歉,因为我以前没有:

#include <linux/module.h>
#include <linux/uaccess.h>

void **sys_call_table = NULL;
asmlinkage long (*sys_read)(long a1, long a2, long a3, long a4, long a5, long a6) = NULL;
asmlinkage long (*sys_clone)(long a1, long a2, long a3, long a4, long a5, long a6) = NULL;
asmlinkage long my_clone64(long a1, long a2, long a3, long a4, long a5, long a6);

char *sct_str;

module_param(sct_str, charp, 0);

inline void disable_wp(void) {
        asm("cli\n\tmov\t%cr0, %rax\n\tand\t$0xfffffffffffeffff, %rax\n\tmov\t%rax, %cr0\n\tsti");
}

inline void enable_wp(void) {
        asm("cli\n\tmov\t%cr0, %rax\n\tor\t$0x10000, %rax\n\tmov\t%rax, %cr0\n\tsti");
}

int init_module(void) {
        int ret = 0;
        mm_segment_t old_fs;

        kstrtoul(sct_str, 16, (unsigned long *) &sys_call_table);
        printk("%lx\n", sys_call_table);

        if (!sys_call_table) {
                return -1;
        }

        sys_read = sys_call_table[__NR_read];
        sys_clone = sys_call_table[__NR_clone];

        // hook sys_clone
        printk("be\n");
        disable_wp();
        sys_call_table[__NR_clone] = my_clone64;
        enable_wp();
        printk("af\n");

        // wait user's ENTER
        old_fs = get_fs();
        set_fs(KERNEL_DS);
        sys_read(0, (long)&ret, 1, 0, 0, 0);
        set_fs(old_fs);

        // restore sys_clone
        disable_wp();
        sys_call_table[__NR_clone] = sys_clone;
        enable_wp();

        return -1;
}

void cleanup_module(void) {
}


asmlinkage long my_clone64(long a1, long a2, long a3, long a4, long a5, long a6) {
        long ret = 0;
        printk("pid = %d\n", ret);
        ret = sys_clone(a1, a2, a3, a4, a5, a6);
        return ret;
}

MODULE_LICENSE("GPL");

Use this Makefile to compile:使用这个Makefile编译:

obj-m += so.o

KERNEL_HEADERS = /lib/modules/$(shell uname -r)/build

all:
        make V=1 -C $(KERNEL_HEADERS) M=$(PWD) modules

clean:
        make V=1 -C $(KERNEL_HEADERS) M=$(PWD) clean

Compile with make and to load do:使用make编译并加载 do:

diwou@diwou-VirtualBox:~/arpso$ sudo grep sys_call_table /proc/kallsyms
ffffffff9de00180 R sys_call_table
ffffffff9de01540 R ia32_sys_call_table
diwou@diwou-VirtualBox:~/arpso$ sudo insmod so.ko sct_str="ffffffff9de00180"
[NOW PRESS ENTER TO UNHOOK]
insmod: ERROR: could not insert module so.ko: Operation not permitted
diwou@diwou-VirtualBox:~/arpso$

The Operattion not permitted is due to the return code I use ( -1 ) to automatically unload the module. Operattion not permitted是由于我使用返回码 ( -1 ) 自动卸载模块。

Here is the output from the other session, when sys_clone is hooked.这是另一个会话的输出,当sys_clone被挂钩时。 And I run /bin/ls :我运行/bin/ls

diwou@diwou-VirtualBox:~$ ls
Violación de segmento (`core' generado)

After unhook sys_clone (by pressing ENTER) the command ls works again.解开sys_clone (按 ENTER)后,命令ls再次起作用。

EDIT: If you replace my_clone64 by the following code, it happens the same.编辑:如果你用下面的代码替换my_clone64 ,它会发生同样的情况。 But if you change call by jmp it works:但是,如果您通过jmp更改call ,它会起作用:

asm(
".globl my_clone64\n\t"
".type my_clone64, @function\n"
"my_clone64:\n\t"
"call sys_clone(%rip)\n\t"
// do something with %rax
"ret\n\t"
".size my_clone64, .-my_clone64\n\t"
);

That points to some kind of implementation in the Linux Kernel... am I right?这指向了 Linux 内核中的某种实现......我说得对吗?

EDIT: It's interesting that when I run strace -f bash in one ssh session, and read(0, waits for my input, then I load the LKM in another session, and I write ls in the bash being traced, and it works. I can see the output of the ls command. But if I open an ssh session, I load the LKM, and write ls on the bash without being traced, just a common ssh session, the output is segmentation fault:编辑:有趣的是,当我在一个 ssh 会话中运行strace -f bashread(0,等待我的输入,然后我在另一个会话中加载 LKM,并在被跟踪的bash写入ls时,它可以工作。我可以看到ls命令的输出,但是如果我打开一个ssh会话,我加载了LKM,在bash上写ls没有被追踪,只是一个普通的ssh会话,输出是segmentation fault:

clone(strace: Process 3233 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa5f68be9d0) = 3233
[...]
[pid  3233] stat("arpso", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
[pid  3233] open("arpso", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[pid  3233] fstat(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
[pid  3233] getdents(3, /* 15 entries */, 32768) = 480
[pid  3233] lstat("arpso/so.c", {st_mode=S_IFREG|0664, st_size=1575, ...}) = 0
[pid  3233] lstat("arpso/so.ko", {st_mode=S_IFREG|0664, st_size=5760, ...}) = 0
[pid  3233] lstat("arpso/modules.order", {st_mode=S_IFREG|0664, st_size=31, ...}) = 0
[pid  3233] lstat("arpso/Module.symvers", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
[pid  3233] lstat("arpso/so.o", {st_mode=S_IFREG|0664, st_size=5440, ...}) = 0
[pid  3233] lstat("arpso/so.mod.o", {st_mode=S_IFREG|0664, st_size=2528, ...}) = 0
[pid  3233] lstat("arpso/so.mod.c", {st_mode=S_IFREG|0664, st_size=542, ...}) = 0
[pid  3233] lstat("arpso/Makefile", {st_mode=S_IFREG|0664, st_size=177, ...}) = 0
[pid  3233] getdents(3, /* 0 entries */, 32768) = 0
[pid  3233] close(3)                    = 0
[...]
+++ exited with 0 +++
diwou@diwou-VirtualBox:~$ ls
Violación de segmento (`core' generado)
diwou@diwou-VirtualBox:~$

Any help would be appreciated.任何帮助,将不胜感激。

Thanks.谢谢。

I found the answer.我找到了答案。

Just the problem of common hooking (aka replace syscall handlers in syscall table) is that actually (in the kernel 4.15.1 at least), there's defined an extra symbol called ptregs_sys_clone , which makes some tricks to finally call/jmp to sys_clone .只是常见钩子的问题(又名替换 syscall 表中的 syscall 处理程序)实际上(至少在内核 4.15.1 中),定义了一个名为ptregs_sys_clone的额外符号,这使得一些技巧最终call/jmpsys_clone And nesting calls is not an option, since the retaddr is checked to go from one path or another... so the solution I found, is to replace the reference to sys_clone in ptregs_sys_clone .并且嵌套调用不是一种选择,因为 retaddr 被检查从一个路径或另一个路径......所以我找到的解决方案是替换sys_clone中对sys_cloneptregs_sys_clone

The code as follows.代码如下。

core.c核心文件

#include <linux/uaccess.h>

void **sys_call_table = NULL;
asmlinkage long (*sys_read)(long a1, long a2, long a3, long a4, long a5, long a6) = NULL;
asmlinkage long (*ptregs_sys_clone)(struct pt_regs *regs) = NULL;
asmlinkage long (*sys_clone)(long a1, long a2, long a3, long a4, long a5, long a6) = NULL;
asmlinkage long my_clone64(long a1, long a2, long a3, long a4, long a5, long a6);

extern char *sct_str;

void disable_wp(void) {
        asm("cli\n\tmov\t%cr0, %rax\n\tand\t$0xfffffffffffeffff, %rax\n\tmov\t%rax, %cr0\n\tsti");
}

void enable_wp(void) {
        asm("cli\n\tmov\t%cr0, %rax\n\tor\t$0x10000, %rax\n\tmov\t%rax, %cr0\n\tsti");
}

int patch_ptregs_syscall(void *addr, long newaddr, long *oldaddr) {
        int i = 0, ret = 0, *p = NULL;
        long vaddr = 0; // variable address

        p = addr + 3; // point to offset in: lea offset(%rip), %register
        //printk("value before patch = %lx\n", *p);
        vaddr = (long)*p + addr + 7; // offset + %rip + lea' size
        if (oldaddr) {
                *oldaddr = vaddr;
        }
        printk("address %lx, ofsset %lx\n", vaddr, *p);
        vaddr = newaddr - (long)addr - 7;
        printk("new address %lx, new offset %lx\n", newaddr, vaddr);
        disable_wp();
        ret = probe_kernel_write(p, &vaddr, sizeof(int));
        enable_wp();
        if (ret != 0) {
                return -2;
        }

        return 0;
}

int install_hooks(void) {
        int ret = 0;
        mm_segment_t old_fs;

        if (!sct_str) {
                return -2;
        }

        kstrtoul(sct_str, 16, (unsigned long *) &sys_call_table);
        printk("sct: %lx\n", sys_call_table);

        if (!sys_call_table) {
                return -2;
        }

        sys_read = sys_call_table[__NR_read];
        ptregs_sys_clone = sys_call_table[__NR_clone];

        // hook sys_clone
        patch_ptregs_syscall(ptregs_sys_clone, (long)my_clone64, (long *)&sys_clone);

        // wait user's ENTER
        old_fs = get_fs();
        set_fs(KERNEL_DS);
        sys_read(0, (long)&ret, 1, 0, 0, 0);
        set_fs(old_fs);

        // restore sys_clone
        patch_ptregs_syscall(ptregs_sys_clone, (long)sys_clone, NULL);

        return -1;
}

asmlinkage long my_clone64(long a1, long a2, long a3, long a4, long a5, long a6) {
        pid_t pid = 0;

        pid = sys_clone(a1, a2, a3, a4, a5, a6);
        printk("pid %d\n", pid);
        return pid;
}

main.c:主文件:

#include <linux/module.h>

extern int install_hooks(void);

char *sct_str;

module_param(sct_str, charp, 0);

int init_module(void) {
        return install_hooks();
}

void cleanup_module(void) {
}

MODULE_LICENSE("GPL");

Makefile生成文件

obj-m += so.o

so-objs := main.o core-asm.o

EXTRA_CFLAGS := -O0

KERNEL_HEADERS = /lib/modules/$(shell uname -r)/build

all:
        make V=1 -C $(KERNEL_HEADERS) M=$(PWD) core.s
        gcc -c core.s -o core-asm.o
        make V=1 -C $(KERNEL_HEADERS) M=$(PWD) modules

clean:
        make V=1 -C $(KERNEL_HEADERS) M=$(PWD) clean

Good luck, and thanks for all your comments & help.祝你好运,感谢您的所有评论和帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么在“ ./arch/h8300/kernel/process.c”中定义“ sys_clone”? - Why “sys_clone” is defined in “./arch/h8300/kernel/process.c”? 为什么我在 C 中调用 fork() 而是使用 sys_clone() 系统调用? 为什么不是 sys_fork() 系统调用? - Why I call fork() in C but make sys_clone() syscall instead? Why not sys_fork() syscall? 如何在linux内核的memcpy函数中添加一个钩子? - How to add a hook in memcpy function of linux kernel? 如何在linux内核中选择“sys”和“proc”文件 - How to choose between “sys' and ”proc" files in linux kernel 使用较新的编译器编译linux 2.6内核模块 - Compile a linux 2.6 kernel module with newer compiler 在linux内核模块中克隆一个文件 - Clone a file in linux kernel module 如何在 linux kernel 中实现 clone(2) 系统调用的另一个变体? - How to implement another variation of clone(2) syscall in linux kernel? 如何在Linux内核中注册UDP端口并为此端口范围创建钩子 - How to register UDP ports in Linux Kernel and creating hook for this port range 如何在Linux内核(sys.c)中创建可以由常规程序访问的结构类型? - How can I create a struct type in the linux kernel (sys.c) that can be accessed by regular programs? 尝试挂钩 linux kernel 系统调用 - Trying to hook a linux kernel system call
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM