借助crash工具理解linux系统的内存分配

渗透技巧 2年前 (2021) admin
893 0 0

背景

Linux下访问匿名页发生的神奇“化学反应”,我看完这篇文章感觉对linux的内存分配又多了一点点理解,因此也想推荐给你读下。

原文提出了一个问题:向mmap系统调用申请的内存”读时”没有导致物理可用内存减少,但是”随后写时”发现物理可用内存减少。

原作者借助”分析内核源码”来剖析问题原因,最终得到结论:第一次读匿名页后,然后写匿名页,先只读方式映射到0页,然后发生写时复制,分配物理页,虚拟页以可读可写的方式映射到此物理页。

我自己总结一下,按照时间顺序来看,当对”可读可写的vma”先读后写时,”对应的页表项”应该是:

  • 在第一次读之前不存在
  • 在第一次读之后,第一次写之前,映射到固定位置,此时页表属性是只读
  • 在第一次写之后,映射到实际分配的物理内存,此时页表属性是可读可写

可能这个结论你听着还是有点绕,并且我想你可能和我一样,也不想一下子就去研究”内核源码”来理解到底是怎么回事。

那怎么能搞清楚原文的问题和结论呢?我发现一个神器crash,实际操作一番后,我感觉文章结论其实很好理解。下面就和我一起玩一玩crash,理解mmap分配的内存属性到底是怎么回事,也验证一下原文的结论是否正确。

crash是一个可以用来调试linux内核崩溃的工具,也可以调试正在运行中的内核。它的安装和使用都很简单,理解本篇文章也不需要你之前接触过这个工具。

过程

准备源码

我们在”读内存之前”、”读内存后写内存前”、”写内存之后”这三个时刻分别调用getchar()停顿一下,方便我们用crash工具观察”页表项”

[root@instance-fj5pftdp tmp]# cat e.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

int main(){
  char* addr = mmap (NULL, 1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS , - 1 , 0);

  printf ("addr: %lu pid:%dn", addr, getpid()); // 在读内存之前:页表项不存在
  getchar();

  printf ("1:%s n", addr); // 在读内存之后:映射了页表,页表项属性为"只读"
  getchar();

  strcpy(addr, "AAAABBBB"); // 在写内存之后:页表项属性为"可读可写"

  printf ("2:%s n", addr);
  getchar();

  return 0;
}

上面的c程序编译运行后,我们就可以来分析”读内存之前”、”读内存后写内存前”、”写内存之后”这三个时刻的”页表项”是什么了。

使用crash分析”页表项”的变化

首先简单说一下crash的安装和使用。

如果你是centos系统,就可以用以下命令来安装

yum install crash  // 安装crash工具
debuginfo-install kernel-debuginfo-`uname -r`  // 安装crash工具需要的内核文件

安装好后,使用下面的命令就可以调试”运行中的内核了”。vmlinux是带调试符号的内核文件。

[root@instance-fj5pftdp ~]# crash /usr/lib/debug/usr/lib/modules/3.10.0-1160.11.1.el7.x86_64/vmlinux /dev/mem

下面来看一下”页表项”的变化:我在程序三次getchar()时,使用crashvtop命令查看”虚拟地址”对应的页表

借助crash工具理解linux系统的内存分配


crash> set 31880    // 31880是进程号
    PID: 31880
COMMAND: "a.out"
   TASK: ffff9396f6a7a100  [THREAD_INFO: ffff93983cbe4000]
    CPU: 1
  STATE: TASK_INTERRUPTIBLE
crash> px 139812061335552  // 十进制转成十六进制。139812061335552是mmap的返回值
$1 = 0x7f2888405000
crash> vtop 0x7f2888405000  // vtop指令用来查看"虚拟地址"对应的"物理地址"
VIRTUAL     PHYSICAL
7f2888405000  (not mapped)  // 这里可以看出来,此时没有映射到对应的"物理地址"

   PGD: 16abd67f0 => 16b7d7067
   PUD: 16b7d7510 => 170f95067
   PMD: 170f95210 => 160fd5067
   PTE: 160fd5028 => 0      // "页表项"为0,其中的P标志位为0,表示"页表项"不存在,没有映射到对应的"物理地址"

...

crash> vtop 0x7f2888405000
VIRTUAL     PHYSICAL
7f2888405000  117d65000
....
   PTE: 160fd5028 => 8000000117d65225
  PAGE: 117d65000

      PTE         PHYSICAL   FLAGS
8000000117d65225  117d65000  (PRESENT|USER|ACCESSED|NX)   // 此时"页表项"有了映射关系,说明此时只能读不能写

crash> vtop 0x7f2888405000
...

      PTE         PHYSICAL   FLAGS
80000003b0cf5867  3b0cf5000  (PRESENT|RW|USER|ACCESSED|DIRTY|NX)    // RW标志表明此时可以写
...

如上面指令结果中的注释:执行三次vtop指令时,页表项(PTE)中的”FLAGS”都不断变化,读写权限确实像”问题背景”中那样变化。可以看出来,结论是没有问题的

怎么样,crash验证这个结论是不是很简单?

总结

crash很好用,如果你想了解更多crash工具的应用场景,可以参考 解决Linux内核问题实用技巧之 – Crash工具结合/dev/mem任意修改内存


原文始发于微信公众号(leveryd):借助crash工具理解linux系统的内存分配

版权声明:admin 发表于 2021年12月15日 上午10:01。
转载请注明:借助crash工具理解linux系统的内存分配 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...