我正在尝试挂钩sys_clone
,以绕过工具unhide
http://www.unhide-forensics.info/使用的方法。我们的想法是将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
EDITED : 我创建了一个最小的工作示例,重现了这个bug,所以你可以更好地测试它,抱歉因为我之前没有:
#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
进行编译并加载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$
Operattion not permitted
归因于我使用的返回码(-1
)来自动卸载模块。
这是其他会话的输出,当sys_clone
被挂钩时。我运行/bin/ls
:
diwou@diwou-VirtualBox:~$ ls
Violación de segmento (`core' generado)
取消挂钩sys_clone
后(按ENTER键),命令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,并在ls
中写入bash
ls
被追踪,并且有效。我可以看到ls
命令的输出。
但是如果我打开一个ssh会话,我加载LKM,并在bash
上写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:~$
而不进行跟踪,只是一个常见的ssh会话,输出是分段错误:
require 'dotenv/load'
require 'faraday'
class OverviewController < ApplicationController
def api_key
ENV["API_KEY"]
end
def url
"https://example.com"+api_key
end
def index
conn = Faraday.new(url, request: {open_timeout: 1, timeout: 1}) do |c|
c.response :json, :content_type => /\bjson$/
c.adapter Faraday.default_adapter
end
response = conn.get url
@hash = response.body['data']
end
end
任何帮助都将不胜感激。
感谢。
答案 0 :(得分:1)
我找到了答案。
只是普通挂钩的问题(也就是替换系统调用表中的系统调用处理程序)实际上(至少在内核4.15.1中),定义了一个名为ptregs_sys_clone
的额外符号,这最终会产生一些技巧call/jmp
到sys_clone
。并且嵌套调用不是一个选项,因为retaddr被检查来自一个或另一个路径......所以我找到的解决方案是替换sys_clone
中对ptregs_sys_clone
的引用。
代码如下。
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");
生成文件
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
祝你好运,感谢你的所有评论&amp;帮助