千家信息网

Linux内核中如何添加新功能隐藏进程地址空间内存不被窃取

发表于:2025-12-03 作者:千家信息网编辑
千家信息网最后更新 2025年12月03日,这篇文章将为大家详细讲解有关Linux内核中如何添加新功能隐藏进程地址空间内存不被窃取,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。首先看怎样能获取其
千家信息网最后更新 2025年12月03日Linux内核中如何添加新功能隐藏进程地址空间内存不被窃取

这篇文章将为大家详细讲解有关Linux内核中如何添加新功能隐藏进程地址空间内存不被窃取,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

首先看怎样能获取其它进程地址空间的内存,答案是ptrace毫无疑问了,其它比如使用crash工具,利用系统漏洞,插入模块等邪门方法不在本篇讨论范围之内。

上例子:test.c

 #define handle_error(msg) \     do { perror(msg); exit(EXIT_FAILURE); } while (0)  int main(void) {         char *p;          char const str[] = "Jeff Xie\n";          p = malloc(sizeof(str));         if (!p)                 handle_error("malloc");         printf("p:0x%llx\n", p);          memcpy(p, str, sizeof(str));         printf("str:%s\n", p);          sleep(10000);          return 0; }

地址: https://github.com/x-lugoo/hide-memory

上面例子test.c中只是非常单纯的malloc了一块区域(堆区),然后保存了一个字符串.

terminal 1:

#gcc test.c  #./a.out  p:0xd3d260 str:Jeff Xie

terminal 2:

#ps -C a.out   PID TTY          TIME CMD 19145 pts/4    00:00:00 a.out #cat /proc/19145/maps 00400000-00401000 r-xp      /home/jeff/a.out 00600000-00601000 r--p      /home/jeff/a.out 00601000-00602000 rw-p      /home/jeff/a.out 00d3d000-00d5e000 rw-p      [heap]

可以看到0xd3d260 在heap区域范围内,使用readmem就可以简单粗暴的读出了进程19145(a.out)的0xd3d260 向后十个字节的内容.

terminal 2:

#readmem 19145 0xd3d260 10 Jeff Xie

程序readmem使用ptrace功能实现,代码见:

https://github.com/x-lugoo/hide-memory/tree/main/ptrace

如果进程19145保存的不是一个普通的字符串,而是某位皇帝留下的千年宝藏的地址,或者里面的信息关系到整个公司的命脉,如果被nice值不高的人获取了,后果可想而知。

最近有人(前辈)在linux内核社区提交了一个patch,解决了这个问题,我把整个patch简化了一些。

原始patch:

https://lore.kernel.org/linux-fsdevel/20201203062949.5484-1-rppt@kernel.org/T/#t

被我简化后:

https://github.com/x-lugoo/hide-memory/blob/main/hidemem/0001-hidemem-Initialization-version.patc

此patch实现的原理:

新增一个系统调用memfd_hide, 当用户使用这个系统调用时,会返回一个fd, 进而使用mmap(...fd...),map一段内存,此段内存将是安全的,其它人不能通过ptrace获取。

--- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -362,6 +362,7 @@  438    common  pidfd_getfd     sys_pidfd_getfd  439    common  faccessat2      sys_faccessat2  440    common  process_madvise     sys_process_madvise +441    common  memfd_hide      sys_memfd_hide  SYSCALL_DEFINE1(memfd_hide, unsigned long, flags) {         struct file *file;         int fd, err;         fd = get_unused_fd_flags(flags & O_CLOEXEC);         file = hidemem_file_create(flags);         fd_install(fd, file);         return fd; }

当用户调用441号系统调用时,系统会返回一个fd,例如用户层这样调用:

#define __NR_memfd_hide 441 static int memfd_secret(unsigned long flags) {      return syscall(__NR_memfd_hide, flags); } fd = memfd_secret(0);

fd_install 做了以下操作,把fd和当前进程关联起来.

struct fdtable *fdt; struct task_struct {           ...           struct files_struct             *files; } fdt = current->files->fdt; fdt->fd[fd] = file;

hidemem_file_create 最终是返回了一个struct file, 但是做的一个很重要的动作是初始化一系列回调函数,让用户调用mmap和memcpy时,在发生page fault时进行合适的动作,比如调用alloc_page(gfp)申请一块内存.

fd = memfd_secret(0); p = mmap(NULL, 4096, prot, mode, fd, 0); memcpy(p, str, sizeof(str));

追随以下绿色标记 可以很好理清函数调用关系:

static struct file *hidemem_file_create(unsigned long flags) {         struct file *file = ERR_PTR(-ENOMEM);         struct inode *inode;         inode = alloc_anon_inode(hidemem_mnt->mnt_sb);         file = alloc_file_pseudo(inode, hidemem_mnt, "hidemem",                                  O_RDWR, &hidemem_fops);         inode->i_mapping->a_ops = &hidemem_aops;  static const struct file_operations hidemem_fops = {         .release        = hidemem_release,         .mmap           = hidemem_mmap, };  static int hidemem_mmap(struct file *file, struct vm_area_struct *vma) {         vma->vm_ops = &hidemem_vm_ops;         vma->vm_flags |= VM_LOCKED; }  static const struct vm_operations_struct hidemem_vm_ops = {         .fault = hidemem_fault, };  static vm_fault_t hidemem_fault(struct vm_fault *vmf) {         struct address_space *mapping = vmf->vma->vm_file->f_mapping;         vm_fault_t ret = 0;         struct page *page;         int err;          page = find_get_page(mapping, offset);         if (!page) {                 page = hidemem_alloc_page(vmf->gfp_mask);                 err = add_to_page_cache(page, mapping, offset, vmf->gfp_mask);         }         vmf->page = page; }
static struct page *hidemem_alloc_page(gfp_t gfp) {         return alloc_page(gfp); }

回到怎样隐藏进程空间的问题上:

当其它进程使用ptrace功能获取指定进程地址空间内容时,会调用到check_vma_flags(), 此时加上一个条件判断,如果此段vma(/proc/pid/maps中的每一列地址范围属于一个vma)属于hidemem, 直接返回错误.

 static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)  {      vm_flags_t vm_flags = vma->vm_flags; @@ -923,6 +925,9 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)      if (gup_flags & FOLL_ANON && !vma_is_anonymous(vma))          return -EFAULT;  +    if (vma_is_hidemem(vma)) +        return -EFAULT; +      if (write) {          if (!(vm_flags & VM_WRITE)) {              if (!(gup_flags & FOLL_FORCE))
static const struct vm_operations_struct hidemem_vm_ops = {         .fault = hidemem_fault, };  bool vma_is_hidemem(struct vm_area_struct *vma) {         return vma->vm_ops == &hidemem_vm_ops; }

加上vma_is_hidemem(vma)判断之后,此时如果使用readmem利用ptrace获取指定进程内存段的时候,会直接报错,以达到隐藏vma背后page内容的目的。

以上patch和测试代码都在:

https://github.com/x-lugoo/hide-memory

原始patch:

https://lore.kernel.org/linux-fsdevel/20201203062949.5484-1-rppt@kernel.org/T/#t

关于Linux内核中如何添加新功能隐藏进程地址空间内存不被窃取就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

进程 内存 地址 空间 内容 系统 用户 内核 范围 新功能 原始 代码 例子 函数 功能 动作 区域 字符 字符串 文章 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 数据库系统阶段冗余性 天津量化积分管理软件开发软件 服务器主板不识别百兆路由器 网络安全技术交流大会 软件开发公司哪家质量好 软件开发的4个阶段 数据库修改数据 安卓手机软件开发买卖 如何增加数据库访问权限 软件开发过程中一个错误 数据库实时导入solr 瓷砖软件开发定制 哪里有晨曦数据库引擎补丁 网络安全的论文摘要 河北智能软件开发服务咨询报价 天文数字底片数据库第一批 公安信息网络安全遵循 国家网络安全保卫部门职责 中控指纹F8数据库配置 金山区推广软件开发咨询热线 有特殊符号怎么插入到数据库 公司服务器及网络的管理 安庆网络安全培训班 铜仁天气预报软件开发 安全级别 数据库a1级 报考计算机网络技术好吗 未来之役服务器英文名字看不懂 深圳pdu服务器电源厂家直销 青浦区品牌软件开发收购价格 北京高性能服务器什么价位
0