# 实验三: Attack xv6 ## 相关背景知识介绍 - 物理内存布局,基于QEMU的支持RISC-V系统的虚拟机的存储布局 - BOOT ROM QEMU提供的启动代码,实现系统初始化的启动 - CLINT (0x02000000): The Core Local Interruptor (CLINT) manages inter-processor interrupts and timers. - PLIC (0x0C000000): The Platform-Level Interrupt Controller, which handles interrupts across the system. - UART0 (0x10000000): The base address for the UART (Universal Asynchronous Receiver-Transmitter) interface, enabling basic serial I/O. - Virtio Disk (0x10001000): The virtio device base address, used for virtualized block device (disk) access.(虚拟磁盘) - Kernel Start (0x80000000): The boot ROM jumps here to load the OS kernel, marking the beginning of kernel space and unused physical memory. - 内核和物理存储使用 - 内核存储布局,内核使用`0x80000000`为起始地址的空间,包括代码,数据和页面分配`#define KERNBASE 0x80000000L` - RAM空间从KERNBASE到PHYSTOP`#define PHYSTOP (KERNBASE + 128*1024*1024)` - 将`trampoline`页映射到最高地址:`#define TRAMPOLINE (MAXVA - PGSIZE)` - #define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE),trampoline为上下文切换的一种特殊机制,特别实现了从用户态和内核态的切换,trampoline的代码(在用户和内核地址空间中)被映射到高到虚拟空间,这样从用户态进入到内核态时的状态切换会比较安全可控。 - 当用户态下触发一个系统调用或中断发生,trampoline的代码提供了一个最小化的固定的入口点实现从用户态到内核态的切换 - 将地址映射到一个高的地址空间(用户空间和内核空间都可以访问) - trampoline负责存储关键的寄存器值(如程序计数器和堆栈指针等)然后进入内核态,当从内核态返回时,则可以恢复寄存器的值返回到用户态 - 安全和隔离:限制入口点在特定的,受控的内存部分,可以阻止内核受到用户程序的可能的恶意代码或一些偶然性的修改 - 高效上下文切换 - #define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE) - trampoline.S映射到最高虚拟地址空间TRAMPOLINE,不同的进程用户页表都有这一项,指向同样的代码页 - TRAMPOLINE 是在操作系统中常用的一个概念,通常指向一段特定的代码,这段代码用来处理从用户空间到内核空间的上下文切换,例如在 trap 发生时进行转入内核的处理。具体来说,在 xv6 操作系统中,TRAMPOLINE 通常指向一段汇编代码,类似 trampoline.S 中的那段代码。 - 不可变的内核代码:TRAMPOLINE 所指向的汇编代码是内核的一部分,它是不可变的,所有进程都可以共享这段代码。在多个进程同时触发 trap 时,操作系统会为每个进程单独分配一个独立的内核栈和上下文,保证每个进程执行到 TRAMPOLINE 代码时不会发生冲突。 - 可重入 是指代码在多个执行流(例如多个线程或进程)之间执行时能够正确工作而不会导致状态错误或数据冲突。要确保代码可重入,应该避免全局或静态变量的使用,保证每次调用时的状态都是独立的,且能够正确处理中断或并发环境。 - CPU 执行代码:CPU 通过程序计数器(PC)跟踪当前执行的指令地址,指令缓存(I-cache)用来加速指令的获取过程。 - 指令缓存:CPU 会从内存中加载指令到 I-cache 中,缓存行的大小和指令的长度决定了每次加载多少条指令。通常缓存行大小为 32-128 字节,每次加载的指令数量取决于指令的大小。 - 栈指针(SP):栈指针管理函数调用和局部数据,它与指令的加载和执行过程无关。 ``` // one beyond the highest possible virtual address. // MAXVA is actually one bit less than the max allowed by // Sv39, to avoid having to sign-extend virtual addresses // that have the high bit set. #define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) ``` sbrk系统调用可以扩充程序进程空间的堆的大小, ## 任务前的一些背景条件说明 说明: 1、Makefile中的定义了编译选项:LAB_SYSCALL ifdef LAB LABUPPER = $(shell echo $(LAB) | tr a-z A-Z) XCFLAGS += -DSOL_$(LABUPPER) -DLAB_$(LABUPPER) endif 2、在kernel/vm.c文件的第272行左右,由于编译时用到了AB_SYSCALL选项,因此memset这一行没有满足预编译条件,相当于漏写了这一行语句 ``` #ifndef LAB_SYSCALL memset(mem, 0, sz); #endif ``` 3、在 kernel/kalloc.c文件中,同样的,将junk数据放入空闲的页里的操作页会omitted #ifndef LAB_SYSCALL if(r) memset((char*)r, 5, PGSIZE); // fill with junk #endif #ifndef LAB_SYSCALL // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); #endif 4、漏掉这个相关的memset语句带来的副作用为新分配的内存会保留之前的数据状态 ## 任务说明 创建user/secret.c文件, int main(int argc, char *argv[]) { if(argc != 2){ printf("Usage: secret the-secret\n"); exit(1); } char *end = sbrk(PGSIZE*32);//为程序分配额外的存储空间,PAGESIZE在很多系统中通常定义为一个常数值,值大小为4096字节 end = end + 9 * PGSIZE; //偏移9 * PGSIZE大小进行开始保存数据 strcpy(end, "my very very very secret pw is: "); strcpy(end+32, argv[1]); exit(0); }