[英]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_fork
, sys_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_vfork
和sys_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 bash
并read(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/jmp
到sys_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_clone
的ptregs_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.