千家信息网

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

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章将为大家详细讲解有关Linux内核中如何添加新功能隐藏进程地址空间内存不被窃取,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。首先看怎样能获取其
千家信息网最后更新 2026年11月02日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安全错误 数据库的锁怎样保障安全 广东猪兼强互联网科技有限公司 互联网电商科技 互联网科技公司怎么样 中正互联网科技招聘 山东吉时兔互联网科技有限公司 中国最先进互联网科技公司 互联网教育科技公司vipkid 全球顶尖互联网科技公司 河南猎农互联网科技有限公司 佛山市拉法互联网科技有限公司 长沙互联网科技 皮皮虾互联网科技有限公司 从那以后互联网科技有限公司 广州智享互联网科技 米泉互联网科技专业怎么样 互联网科技对化妆品行业 互联网金融金融科技 杭州科技互联网公司有哪些 华人运通互联网科技公司 东风日产互联网科技公司叫什么 金融科技与互联网金融 善班互联网科技 互联网科技缩写 科技伦理 互联网公司 江阴互联网智能科技产品进口 济南有互联网科技公司吗 上海钢联属于互联网科技股吗 米泉互联网科技专业怎么样 科技互联网专业务虚会材料 优雇互联网科技有限公司
0