【发布时间】:2018-08-01 10:22:18
【问题描述】:
我正在尝试挂钩sys_clone,以绕过工具unhidehttp://www.unhide-forensics.info/ 使用的方法。这个想法是将 PID 从真实转换为虚假,将虚假转换为真实,使工具能够将隐藏的 PID 和未隐藏的 PID 视为连续标识符。
我的意思是,这个工具不断地分叉并检查在所有循环中是否有一些 PID 很忙并且不可见。
通过提供连续的 PID 来欺骗这个工具的想法。
问题在于,当程序执行我的sys_clone 的钩子处理程序时,返回后,wait(&status) 给出了 SEGVFAULT 的信号。
我使用的方法与我用来挂钩所有其他系统调用的方法相同,包括 sys_fork、sys_vfork 和其他系统调用。
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#L2123
已编辑: 我创建了一个最小的工作示例,重现了这个错误,所以你可以更好地测试它,很抱歉,因为我以前没有:
#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");
使用这个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
使用make 编译并加载:
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$
Operattion not permitted 是由于我使用返回码 (-1) 来自动卸载模块。
这是另一个会话的输出,当sys_clone 被钩住时。我运行/bin/ls:
diwou@diwou-VirtualBox:~$ ls
Violación de segmento (`core' generado)
解开sys_clone(按回车键)后,ls 命令再次起作用。
编辑:
如果您将my_clone64 替换为以下代码,则会发生相同的情况。但是,如果您将 call 更改为 jmp 它会起作用:
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"
);
这指向 Linux 内核中的某种实现......我说的对吗?
编辑:
有趣的是,当我在一个 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:~$
任何帮助将不胜感激。
谢谢。
【问题讨论】:
-
我试图通过向您解释您已经将 Linux 内核完全超出其设计规范来提供帮助,因此它崩溃并不奇怪,并且可能有更好的方法来做你要。您处于“XY problem”认知陷阱中。
-
你可能是对的。但我认为
clone更有可能对您在未向我们展示的代码中做错了什么特别敏感。无论如何,我不希望你再下降任何级别,我希望你上升几个级别,并首先解释你通过挂钩 clone 来实现的目标。 -
这个问题很难回答(尽管它很重要或存在本体论问题)。您对 Linux 内核进行了一些任意且未记录在问题中的更改。您发现了一个通常被描述为“分段错误”的问题。呈现的代码似乎有问题:C 类型是否正确?当现代内核使用
gs时,为什么要交换fs?你考虑过KPTI吗?你看过entry_64.S和common.c吗? -
我认为人们不赞成这个问题,因为它并不完全清楚你做了什么(或者更重要的是为什么)。您的问题太大了,无法总结为一个好的 SO 问题。这似乎不是一个很好的minimal reproducible example。没有人说过任何关于否决票的事情,因为他们反对白帽黑客。
-
@AbelRomeroPérez:在那次讨论中,我站在 PeterCordes 一边。你想挂钩系统调用。你说你知道你正在做一些不寻常的事情。好的。然后你应该准备好调试困难。我明白了,你已经完成了一些调试。好的。但是要询问 Stack Overflow,您需要更多 - 您需要 minimal reproducible example。删除所有不必要的代码并尽可能简化其余部分。在
my_clone64实现中将SYSCALL64()替换为sys_clone()是否有效?如果不是,则完全删除SYSCALL64。如果是,请检查您是否最终使用正确的参数调用sys_clone。以此类推。
标签: c assembly linux-kernel hook clone