Linux Rootkit 实验 | 00024 Rootkit 基本功能实现x隐藏端口
昨夜西风凋碧树。独上高楼,望尽天涯路。
实验说明
本次实验将初步实现 rootkit 的基本功能:
- 阻止其他内核模块加载
- 提供 root 后门
- 隐藏文件
- 隐藏进程
- 隐藏端口
- 隐藏内核模块
本次实验基于 0001 实验中学习的挂钩技术。
注:由于本次实验内容过多,故分为00020
到00025
六个实验报告分别讲解。
本节实现“隐藏端口”功能
实验环境
uname -a:
Linux kali 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux
GCC version:6.1.1
上述环境搭建于虚拟机,另外在没有特殊说明的情况下,均以 root 权限执行。
注:后面实验参考的是4.10.10的源码
实验过程
隐藏端口
用户态下隐藏端口信息,就是把/proc/
下端口相关信息过滤掉。具体来说,看下面一张表格:
网络类型 |
对应/proc |
内核源码文件 |
主要实现函数 |
TCP/IPv4 |
/proc/net/tcp |
net/ipv4/tcp_ipv4.c |
tcp4_seq_show |
TCP/IPv6 |
/proc/net/tcp6 |
net/ipv6/tcp_ipv6.c |
tcp6_seq_show |
UDP/IPv4 |
/proc/net/udp |
net/ipv4/udp.c |
udp4_seq_show |
UDP/IPv6 |
/proc/net/udp6 |
net/ipv6/udp.c |
udp6_seq_show |
可以看一下net/ipv4/tcp.c
和net/ipv4/tcp_ipv4.c
的开头注释,列举了 TCP/IP 协议栈的开发者们。
下面我们以表格第一行的IPv4
版本TCP
为例,做端口隐藏实验。
首先看一下cat /proc/net/tcp
:
我们再看一下tcp4_seq_show
:
可以看到,这个函数正是用来向用户层/proc
暴露网络信息的接口,它的意义也很好理解——每次写一行。我们再跟进看一下seq_file
:
是不是和之前见过的file_operations
很像!!!内核看来是有迹可循的!
tcp4_seq_show
就是seq_operations
中的show
函数。
老套路,我们钩掉它,就 OK。开始工作!
首先看一下假的show
:
思路是,先调用真的show
向seq
中写入一条记录,然后检查写入的内容中是否有:10000
这个字符串,如果有,就把seq->count
这个记录缓冲区已经使用的字节数减去一条记录的长度TMPSZ
相当于之前写入的无效了;如果没有,就正常放行。
下面是用来钩函数的宏:
这个“钩宏”和之前在隐藏文件实验一节中的宏很类似。Linux 中“一切皆文件”,所以先定义两个指针:
然后打开文件:
然后用另一个指针从file
结构体中获得取得文件处理函数结构体,这里有一些差异,因为之前是普通文件,这里是tcp_seq_afinfo
,也就是流文件:
这里的PDE_DATA
是从inode
节点获取对应数据的函数。
之后就是重点,替换:
上面开关写保护是 novice 师傅加的,可能是需要吧。有兴趣的可以自己去掉那两行试试行不行。
最后就是关闭文件了。这么看来,也很好理解。以后要写别的钩子,我们可以参照这个思路来。先找到相关结构体,包括回调函数的,然后打开文件,替换,关闭文件。
最后在入口出口函数中添加调用代码:
测试结果如下:
我们使用ncat -4 -l 10000
以IPv4
形式TCP
监听10000
端口,在没有加载模块时,可以查看到端口监听信息:
在加载模块后,发现端口已经被隐藏:
同样,此时执行netstat -tuln
也是看不到的:
卸载模块后,端口可以被看到:
实验问题
【问题一】
本实验中,端口号也是写死在代码里的。当然,端口号写死的影响不是很大,我们可以预设一些要使用的端口,让它们被过滤掉。但是,考虑改进这一设计,能否在运行时动态选择要隐藏的端口?
实验总结与思考
- 从内核源码中看到的
seq_file
让我学会了一种用 C 表达面向对象的编程技巧(这代码,太美了)