百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Linux内核如何私闯进程地址空间并修改进程内存

toyiye 2024-04-07 14:12 20 浏览 0 评论

进程地址空间的隔离是现代操作系统的一个显著特征。这也是区别于“古代”操作系统的显著特征。

进程地址空间隔离意味着进程P1无法以随意的方式访问进程P2的内存,除非这块内存被声明是共享的。

这非常容易理解,我举个例子。

我们知道,在原始野人社会,是没有家庭的观念的,所有的资源都是部落内共享的,所有的野人都可以以任意的方式在任意时间和任何其他野人交互。类似Dos这样的操作系统就是这样的,内存地址空间并没有隔离。进程可以随意访问其它进程的内存。

后来有了家庭的观念,家庭的资源被隔离,人们便不能私闯民宅了,人们无法以随意的方式进入别人的家用别人的东西,除非这是主人允许的。操作系统进入现代模式后,进程也有了类似家庭的概念。

但家庭的概念是虚拟的,人们只是遵守约定而不去破坏别人的家庭。房子作为一个物理基础设施,保护着家庭。在操作系统中,家庭类似于虚拟地址空间,而房子就是页表。

邻居不能闯入你的房子,但特权管理机构只要理由充分,就可以进入普通人家的房子,touch这家人的东西。对于操作系统而言,这就是内核可以做的事,内核可以访问任意进程的地址空间。

当然了,内核并不会无故私闯民宅,就像警察不会随意闯入别人家里一样。

但是,你可以让内核故意这么做,做点无赖的事情。

我们来试一下,先看一个程序:

  1. // test.c

  2. // gcc test.c -o test

  3. #include<stdio.h>

  4. #include<stdlib.h>

  5. #include<string.h>

  6. #include<unistd.h>

  7. #include<sys/mman.h>


  8. intmain

  9. {

  10. char* addr = mmap(, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1,0);

  11. strcpy(addr, "Zhejiang wenzhou pixie shi");


  12. printf("addr: %lu pid:%d\n", addr, getpid);


  13. printf("before:%s \n", addr);


  14. getchar;


  15. printf("after:%s\n", addr);


  16. return0;

  17. }

这个程序的输出非常简单,before和after都会输出 “Zhejiang wenzhou pixie shi”,但是我们想把这句话给改了,怎么办呢?显然,test进程如果自己不改它,那就没辙...但是可以让内核强制改啊,让内核私闯民宅就是了。

接下来我写一个内核模块:

  1. // test.c

  2. // make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` modules

  3. #include<linux/mm.h>

  4. #include<linux/sched.h>

  5. #include<linux/module.h>


  6. staticintpid =1;

  7. module_param(pid, int,0644);


  8. staticunsignedlong addr = 0;

  9. module_param(addr, long, 0644);


  10. // 根据一个进程的虚拟地址找到它的页表,相当于找到这家人的房子地址,然后闯入!

  11. staticpte_t* get_pte(struct task_struct *task, unsignedlong address)

  12. {

  13. pgd_t* pgd;

  14. pud_t* pud;

  15. pmd_t* pmd;

  16. pte_t* pte;

  17. struct mm_struct *mm = task->mm;


  18. pgd = pgd_offset(mm, address);

  19. if(pgd_none(*pgd) || pgd_bad(*pgd))

  20. return ;


  21. pud = pud_offset(pgd, address);

  22. if(pud_none(*pud) || pud_bad(*pud))

  23. return ;


  24. pmd = pmd_offset(pud, address);

  25. if(pmd_none(*pmd) || pmd_bad(*pmd))

  26. return ;


  27. pte = pte_offset_kernel(pmd, address);

  28. if(pte_none(*pte))

  29. return ;


  30. return pte;

  31. }


  32. staticinttest_init(void)

  33. {

  34. struct task_struct *task;

  35. pte_t* pte;

  36. struct page* page;


  37. // 找到这家人

  38. task = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);

  39. // 找到这家人住在哪里

  40. if(!(pte = get_pte(task, addr)))

  41. return-1;


  42. page = pte_page(*pte);

  43. // 强行闯入

  44. addr = page_address(page);

  45. // sdajgdoiewhgikwnsviwgvwgvw

  46. strcpy(addr, (char*)"rain flooding water will not get fat!");

  47. // 事了拂衣去,深藏功与名

  48. return0;

  49. }


  50. staticvoid test_exit(void)

  51. {

  52. }


  53. module_init(test_init);

  54. module_exit(test_exit);

  55. MODULE_LICENSE("GPL");

来来来,我们来试一下:

  1. [root@10 page_replace]# ./test

  2. addr: 140338535763968pid:9912

  3. before:Zhejiangwenzhou pixie shi

此时,我们加载内核模块test.ko

  1. [root@10 test]# insmod test.ko pid=9912addr=140338535763968

  2. [root@10 test]#

在test进程拍入回车:

  1. [root@10 page_replace]# ./test

  2. addr: 140338535763968pid:9912

  3. before:Zhejiangwenzhou pixie shi


  4. after:rain flooding water will not get fat!

  5. [root@10 page_replace]#

仔细看上面那个内核模块的 get_pte函数,这个函数要想写对,你必须对你想蹂躏的进程所在的机器的MMU有一定的了解,比如是32位系统还是64位系统,是3级页表还是4级页表或者5级?这...

Linux的可玩性在于你可以自己动手,又可以让人代劳。比如,获取一个进程的虚拟地址的页表项指示的物理页面,就可以直接得到。

有这样的API吗?有啊,别忘了一切皆文件,恰好在proc文件系统中,就有这么一个文件:

  • /proc/$pid/pagemap

读取这个文件,得到的就是进程虚拟地址的页表项,下图截自内核Doc:Documentation/vm/pagemap.txt

虚拟地址空间是每进程的,而物理地址空间则是所有进程共享的。换句话说,物理地址是全局的。

现在,根据Documentation/vm/pagemap.txt的解释,写一个程序,获取任意进程任意虚拟地址的全局物理地址:

  1. // getphys.c

  2. // gcc getphys -o getphys

  3. #include<fcntl.h>

  4. #include<stdio.h>

  5. #include<stdlib.h>


  6. intmain(intargc, char**argv)

  7. {

  8. intfd;

  9. intpid;

  10. unsignedlong pte;

  11. unsignedlong addr;

  12. unsignedlong phy_addr;

  13. char procbuf[64] = {0};


  14. pid = atoi(argv[1]);

  15. addr = atol(argv[2]);


  16. sprintf(procbuf, "/proc/%d/pagemap", pid);


  17. fd = open(procbuf, O_RDONLY);

  18. size_toffset = (addr/4096) * sizeof(unsignedlong);

  19. lseek(fd, offset, SEEK_SET);


  20. read(fd, &pte, sizeof(unsignedlong));


  21. phy_addr = (pte & ((((unsignedlong)1) <<55) -1))*4096+ addr%4096;

  22. printf("phy addr:%lu\n", phy_addr);


  23. return0;

  24. }

随后,我们修改内核模块:

  1. #include<linux/module.h>


  2. staticunsignedlong addr = 0;

  3. module_param(addr, long, 0644);


  4. staticinttest_init(void)

  5. {

  6. strcpy(phys_to_virt(addr), (char*)"rain flooding water will not get fat!");

  7. return0;

  8. }


  9. staticvoid test_exit(void)

  10. {

  11. }


  12. module_init(test_init);

  13. module_exit(test_exit);


  14. MODULE_LICENSE("GPL");

先运行test,然后根据test的输出作为getphys的输入,再根据getphys的输出作为内核模块test.ko的输入,就成了。还记得吗?这不就是管道连接多个程序的风格吗?

输入一个物理地址,然后把它改了,仅此而已。通过虚拟地址获取页表的操作已经由用户态的pagemap文件的读取并解析代劳了。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码