1. kvm内存虚拟化
kvm虚拟机的内存虚拟化,使用了内存转换技术,过程如下:GVA -> GPA -> HVA -> HPA
通过qemu启动了一个8G内存的虚拟机,查看内存smaps,可以发现有个内存就是8G,这个就是guest所使用的物理内存。
利用这个信息就可以窥探虚拟机内存中特定的信息。
2. 虚拟地址转换为物理地址的原理
内核文档 pagemap.txt 中描述如下:
内存中每一个页对应一个64位,也就是8字节的字段。假设虚拟地址为0xfe0020,其转换过程如下:
虚拟地址0xfe0020,其高52位(0xfe0020>>12)为0xfe0,也就是其虚拟页号为0xfe0。那么该虚拟页的信息处于/proc/self/pagemap这个文件中偏移量为0xfe0*8的地方。从此处读取一个8字节的数据,先检查最高位 "Bit 63 page present",如果是1,那么说明该页处于物理内存中,那么该8字节的第0-54位就是物理页号。假设物理页号是0x40,那么实际的物理地址就是(0x40<<12)+0x20=0x40020。
(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
介绍完原理,下面实现由虚拟地址到物理地址的转换代码就很简单了:
/* gva2gpa.c*/
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PFN_MASK ((((uint64_t)1) << 55) - 1)
#define PFN_PRESENT_FLAG (((uint64_t)1) << 63)
int gva2gpa(unsigned long vir, unsigned long *phy)
{
int fd;
int page_size = getpagesize(); // 获取系统页大小, 通常为 4k
unsigned long vir_page_idx = vir / page_size;
unsigned long pfn_item_offset = vir_page_idx * sizeof(uint64_t); // 在pagemap记录文件中的偏移量
uint64_t pfn_item;
char *page_map_file = "/proc/self/pagemap"; // pagemap 文件路径
// 只读方式方式打开
fd = open(page_map_file, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open %s failed", page_map_file);
return -1;
}
// 定位到 offset 偏移位置
if ((off_t) - 1 == lseek(fd, pfn_item_offset, SEEK_SET)) {
fprintf(stderr, "lseek %s failed", page_map_file);
return -1;
}
// 读取对应项的值, 并判断读取位数
if (sizeof(uint64_t) != read(fd, &pfn_item, sizeof(uint64_t))) {
fprintf(stderr, "read %s failed", page_map_file);
return -1;
}
// 判断物理页是否在内存中 "Bit 63 page present"
if (0 == (pfn_item & PFN_PRESENT_FLAG)) {
fprintf(stderr, "page is not present");
return -1;
}
// 如果在内存上,物理页号加上偏移地址就是物理地址
// 对应项 bit0-54 位表示物理页号
*phy = (pfn_item & PFN_MASK) * page_size + vir % page_size;
return 0;
}
int main(int argc,char *argv[])
{
uint8_t *p_gva;
uint64_t ptr_mem;
p_gva = malloc(256);
strcpy(p_gva, "Where am I?");
printf("%s\n", p_gva);
if (gva2gpa((unsigned long)p_gva, &ptr_mem) == 0) {
printf("Physical address is: 0x%llx\n", ptr_mem);
}
getchar();
free(p_gva);
return 0;
}
在虚拟机上执行上面程序:
通过虚拟机物理地址获取物理机虚拟地址,即 gpa -> hva 。
在物理机上执行:
现在得到了物理机中的虚拟机地址,下面通过两种方法获取虚拟机内存中的信息:
- 一种方式是通过gdb提供的 x 指令:
- 另外一种方法是通过 qmp 指令
现在看到的 "Where am I?" 就是虚拟机内存中写入的信息。
原文链接:https://zhuanlan.zhihu.com/p/576412838