当前位置: 首页> 科技> 能源 > 店面设计装修网_深圳高端画册设计公司_中国2022年重大新闻_什么是网络营销

店面设计装修网_深圳高端画册设计公司_中国2022年重大新闻_什么是网络营销

时间:2025/8/27 12:46:19来源:https://blog.csdn.net/weixin_47763623/article/details/144497501 浏览次数:2次
店面设计装修网_深圳高端画册设计公司_中国2022年重大新闻_什么是网络营销

Linux网络数据包透明加解密

1. 概述

TCP协议本身不提供加密功能,通常需要通过像TLS这样的加密协议来保障通信安全。然而,TLS的实现通常需要修改应用程序代码,有时候这是很难做到的。本文提出一种透明的加解密方案,旨在无需修改应用程序即可自动加密和解密数据,确保传输安全。

我们将讨论两种实现透明加解密的方案:应用层加密和内核层加密,介绍它们的工作原理、优缺点、适用场景及实现细节。

2. 应用层加密方案

2.1 方案设计

在应用层加密方案中,我们的做法是通过拦截并重写sendrecv函数来实现数据的加解密。具体来说,我们用LD_PRELOAD来加载一个自定义的动态库,进而拦截这些函数调用。

工作流程:

  1. 当用户调用send函数时,我们先捕获到这个请求。
  2. 拦截器会对传输的数据进行加密处理。
  3. 加密后的数据通过原始send函数发送到TCP协议栈中去。
  4. 在接收端,拦截器会捕获到recv函数的调用,并对接收到的数据进行解密。
  5. 解密后的数据会返回给用户应用。

2.2 技术实现详解

(1)加密逻辑

对于加密部分,我们推荐使用强加密算法,比如AES,而不推荐使用一些不再安全的老算法(像RC4)。为了便于理解,下面给出了一个简单的异或加密的例子,主要是为了展示加解密的基本概念。请注意,这个方法仅适用于教学演示,不适合在生产环境中使用来保护数据安全。

void encrypt(char *data, size_t len) {for (size_t i = 0; i < len; ++i) {data[i] ^= 0xAA; // 简单的异或加密,纯粹为教学示范}
}
(2)send函数挂钩

为了拦截send函数,我们实现一个自定义so库, 通过LD_PRELOAD加载我们的动态库。这样, 应用程序每次调用send时,我们就能拦截到,进行加密处理。为了避免直接修改原始数据,我们会创建一个临时的缓冲区来存放加密后的数据。

#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>static ssize_t (*original_send)(int sockfd, const void *buf, size_t len, int flags);ssize_t send(int sockfd, const void *buf, size_t len, int flags) {char *encrypted_data = malloc(len);if (!encrypted_data) return -ENOMEM;memcpy(encrypted_data, buf, len);// 对数据加密encrypt(encrypted_data, len);// 调用原始 send 函数发送加密数据ssize_t ret = original_send(sockfd, encrypted_data, len, flags);free(encrypted_data);return ret;
}__attribute__((constructor))
static void init() {original_send = dlsym(RTLD_NEXT, "send");
}
(3)动态库加载

编译生成动态库后,通过LD_PRELOAD机制注入该库,以挂钩目标函数。

gcc -shared -fPIC -o libencrypt.so encrypt.c -ldl
export LD_PRELOAD=./libencrypt.so
(4)解密逻辑

接收端同样通过挂钩recv函数来实现解密。在接收到数据后,拷贝数据到临时缓冲区并进行解密,最后将解密后的数据传回用户空间。

static ssize_t (*original_recv)(int sockfd, void *buf, size_t len, int flags);ssize_t recv(int sockfd, void *buf, size_t len, int flags) {ssize_t ret = original_recv(sockfd, buf, len, flags);if (ret > 0) {// 创建临时缓冲区以防覆盖原始数据char *decrypted_data = malloc(ret);if (!decrypted_data) return -ENOMEM;memcpy(decrypted_data, buf, ret);encrypt(decrypted_data, ret); // 解密memcpy(buf, decrypted_data, ret);free(decrypted_data);}return ret;
}__attribute__((constructor))
static void init() {original_recv = dlsym(RTLD_NEXT, "recv");
}

2.3 优缺点分析

优点:

  • 实现简单:无需修改内核代码,减少了系统级别的复杂度。
  • 灵活性高:支持不同加密算法的快速切换,适应多种需求。
  • 稳定性好:操作仅限于用户态,调试和维护更加简便。

缺点:

  • 应用范围有限:每个目标进程需单独配置LD_PRELOAD,增加了部署的复杂性。
  • 性能损耗:加密处理、动态库加载以及内存分配等操作会带来额外的性能开销。
  • 静态编译限制:静态编译的程序无法通过LD_PRELOAD挂钩,限制了该方案的适用场景。

3 内核层加密方案

3.1 方案设计

在内核态,通过挂钩sendmsgrecvmsg函数,透明地加解密数据。这种方法直接修改了内核网络协议栈的行为,对用户进程完全透明。

工作流程:

  1. 用户调用send,数据进入内核。
  2. 内核挂钩sendmsg,对数据进行加密。
  3. 数据进入网络层传输。
  4. 接收端挂钩recvmsg,对接收到的数据解密。
  5. 解密后的数据交还用户空间。

3.2 技术实现详解

(1)挂钩tcp_sendmsgtcp_recvmsg

通过kallsyms_lookup_name获取tcp_prot结构的地址, 替换其中的tcp_sendmsg为自定义函数。

tcp_prot 是 Linux 内核中用于管理 TCP 协议的结构体,主要用于实现和维护 TCP 协议的相关功能。它定义了 TCP 协议栈中的许多操作,包括连接管理、数据发送接收、拥塞控制等。

#include <linux/module.h>
#include "../../lib/klog.h"
#include <linux/kallsyms.h>
#include <linux/fs.h>
#include <net/sock.h>#define log_tag        "[net-crypt] "// 关闭写保护
unsigned long write_protect_disable(void)
{unsigned long cr0 = 0;unsigned long ret = 0;asm volatile("movq %%cr0, %%rax": "=a"(cr0));// 保存原始cr0的值ret = cr0;cr0 &= 0xfffffffffffeffff;asm volatile("movq %%rax, %%cr0":: "a"(cr0));// 返回原始cr0值return ret;
}// 开启写保护
void write_protect_enable(unsigned long oldval)
{asm volatile("movq %%rax, %%cr0":: "a"(oldval));
}// 保存proto结构地址
struct proto *orig_tcp_prot;
// 保存原来的
int (*orig_tcp_sendmsg)(struct sock *sk, struct msghdr *msg, size_t size);
int (*orig_tcp_recvmsg)(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len);static void __encrypt_buf(char *buf, int size)
{int i;for(i = 0; i < size; i++){buf[i] ^= 0xae;}
}// 我们的
static int my_tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
{void *buf = NULL;struct iov_iter from, to;if(strcmp(current->comm, "nc")){goto out;}klogw(log_tag "my_tcp_sendmsg IN, size=%d, msg_len=%d", (int)size, (int)msg_data_left(msg));buf = kmalloc(size, GFP_KERNEL);if(buf == NULL){goto out;}from = to = msg->msg_iter;_copy_from_iter_full(buf, size, &from);__encrypt_buf(buf, size);_copy_to_iter(buf, size, &to);out:if(buf){kfree(buf);}return orig_tcp_sendmsg(sk, msg, size);
}
static int my_tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len)
{int rv;void *buf = NULL;struct iov_iter from, to;from = to = msg->msg_iter;rv = orig_tcp_recvmsg(sk, msg, len, nonblock, flags, addr_len);if(rv <= 0){goto out;}if(strcmp(current->comm, "nc")){goto out;}klogw(log_tag "my_tcp_recvmsg IN, size=%d, msg_len=%d, rv=%d", (int)len, (int)msg_data_left(msg), rv);buf = kmalloc(rv, GFP_KERNEL);if(buf == NULL){goto out;}_copy_from_iter_full(buf, rv, &from);__encrypt_buf(buf, rv);_copy_to_iter(buf, rv, &to);out:if(buf){kfree(buf);}return rv;
}/** 模块初始化*/
static __init int main_init(void)
{int rv = -1;unsigned long cr0;// 1. 获取struct proto 结构地址orig_tcp_prot = (struct proto *)kallsyms_lookup_name("tcp_prot");// 2. 替换我们的orig_tcp_sendmsg = orig_tcp_prot->sendmsg;orig_tcp_recvmsg = orig_tcp_prot->recvmsg;cr0 = write_protect_disable();orig_tcp_prot->sendmsg = my_tcp_sendmsg;    orig_tcp_prot->recvmsg = my_tcp_recvmsg;    write_protect_enable(cr0);klogw(log_tag "kernel module load.");// 设置成功标识rv = 0x0;goto out;out:return rv;
}/** 模块清理*/
static __exit void main_exit(void)
{// 恢复原来的unsigned long cr0;cr0 = write_protect_disable();orig_tcp_prot->sendmsg = orig_tcp_sendmsg;    orig_tcp_prot->recvmsg = orig_tcp_recvmsg;    write_protect_enable(cr0);klogw(log_tag "kernel module unload.");
}module_init(main_init); 
module_exit(main_exit); 

代码的主要功能是通过替换 TCP 协议栈中 sendmsgrecvmsg 函数来对通过 TCP 发送和接收的数据进行加密处理。下面是对代码的简单解释:

1. 禁用和启用写保护(write_protect_disablewrite_protect_enable

这两个函数是用来控制 CPU 的写保护寄存器 cr0 的状态,目的是为了能够修改内核中的只读数据结构。

  • write_protect_disable():通过修改 cr0 寄存器来禁用写保护,从而允许修改内核中的只读区域(比如 TCP 协议栈中的 tcp_prot 结构)。
  • write_protect_enable():恢复原来 cr0 寄存器的值,重新启用写保护。
2. 替换 sendmsgrecvmsg 函数(my_tcp_sendmsgmy_tcp_recvmsg

这两个函数是对 TCP 发送和接收数据的替代实现, 用于实现透明加解密:

  • 注意这里为了测试方便只处理了nc进程

  • my_tcp_sendmsg:当调用 sendmsg 时,首先检查当前进程的名称是否为 nc(netcat)。如果是,则会将发送的数据进行加密。加密后的数据会被重新复制到消息迭代器中,并继续调用原始的 sendmsg 函数将数据发送出去。

  • my_tcp_recvmsg:当调用 recvmsg 时,也会检查当前进程的名称是否为 nc。如果是,则会对接收到的数据进行加密处理后再返回。

3. 模块初始化(main_init

在模块加载时,会执行以下步骤:

  • 获取 tcp_prot 结构:通过 kallsyms_lookup_name 函数获取 tcp_prot 结构体的地址,这是一个包含 sendmsgrecvmsg 函数指针的结构体。

  • 替换函数指针:将 tcp_prot 中的 sendmsgrecvmsg 函数指针替换为自定义的 my_tcp_sendmsgmy_tcp_recvmsg 函数。这会使得所有通过 TCP 发送和接收的数据都会经过加密处理。

  • 禁用和启用写保护:修改 cr0 寄存器来禁用写保护,替换函数指针后,再恢复写保护。

3.3 优缺点分析

优点

  • 全局覆盖:所有TCP通信均被加密。
  • 完全透明:对应用程序无感知。

缺点

  • 开发复杂:需理解内核网络栈的细节。
  • 高风险:错误可能导致系统崩溃。
  • 维护难度大:内核版本升级可能导致挂钩代码失效。

4 应用场景对比与总结

在选择加密方案时,应用层加密和内核层加密各自有不同的优势和适用场景。以下是它们的对比:

特性应用层加密内核层加密
实现复杂度
全局适用性需逐个进程配置全局生效
性能影响用户态加密性能损耗较高内核态加密更高效
调试难度易于调试高,需内核调试工具支持
风险无内核崩溃风险潜在系统崩溃风险

总结与建议:

  1. 推荐场景

    • 应用层加密:适用于小规模部署或特定应用,尤其是在需要灵活性和易于调试的场景中。
    • 内核层加密:适合对全局网络安全性有严格要求的环境,能确保所有网络通信都受到加密保护。
  2. 开发建议

    • 如果是一般性的网络加密需求,优先选择应用层加密,因为它实现简便,且维护成本低。
    • 若项目需求必须在内核层进行加密,务必充分测试并做好风险评估,避免潜在的系统崩溃风险。

通过这两种方案,可以有效地提升基于TCP协议的数据传输安全性,并根据不同场景的需求选择最合适的加密方式。

关键字:店面设计装修网_深圳高端画册设计公司_中国2022年重大新闻_什么是网络营销

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: