Windows内核实验:跨进程内存读写

Windows内核实验:跨进程内存读写

一个进程访问的虚拟内存都是靠页的各种表来管理的嘛,他的根源就是cr3寄存器,所以理论上我们把某个进程的cr3换过来就可以实现读写这个进程

打开一个notepad

image-20230602212944955

用ce找到他的字符串内存地址为0xaab30

kd> !process 0 0 notepad.exe
Failed to get VadRoot
PROCESS 8165f350 SessionId: 0 Cid: 0688 Peb: 7ffd8000 ParentCid: 04a4
DirBase: 08bd0240 ObjectTable: e14bcae8 HandleCount: 46.
Image: notepad.exe

我们很自然的就会写出这样的代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void __declspec(naked) IdtEntry() {//裸函数不会帮我们生成栈帧,单纯一个call
__asm {
mov eax,cr3
mov ds:[0x8003f130],eax//8003f130 gdtr表里空闲项
mov eax,0x8bd0240 //08bd0240 notepad对应cr3
mov cr3,eax //后面的指令压根不会执行
mov eax,0x12345678
mov ds:[0xaab30],eax
mov eax,ds:[0x8003f130]
mov cr3,eax
iretd
}
}
void interrupt() {
__asm {
int 0x20
}
}
int main() {
if (0x401040 != IdtEntry) {
printf("IdtRntry address wrong");
system("pause");
exit(-1);
}
//eq 8003f500 0040ee00`00081040
interrupt();
system("pause");
}

但是其实mov cr3,eax 后面的指令压根不会执行,切换cr3后,我eip去取指令,但是现在已经是notepad的cr3了,自然不会取到我们程序的eip处指令,而是notepad的eip处指令,所以这段指令应该放到内核中每个进程都相同的地方,依旧选取gdt表里的空闲位置0x8003f140

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
char* Address = (char *)0x8003f140;
size_t i;
void EditNotepad();
void __declspec(naked) IdtEntry() {//裸函数不会帮我们生成栈帧,单纯一个call
for ( i = 0; i < 0x40; i++) {
Address[i] = ((char*)EditNotepad)[i];
}
__asm {
push 0x8003f140
ret//也可以用一个寄存器 jmp 寄存器来实现地址无关
}
}
void __declspec(naked) EditNotepad() {
__asm {
mov eax, cr3
mov ds : [0x8003f130] , eax//8003f130 gdtr表里空闲项
mov eax, 0x8bd0240 //08bd0240 notepad对应cr3
mov cr3, eax
mov eax, 0x12345678
mov ds : [0xaab30] , eax
mov eax, ds : [0x8003f130]
mov cr3, eax
iretd
}
}
void interrupt() {
__asm {
int 0x20
}
}
int main() {
if (0x401040 != IdtEntry) {
printf("IdtRntry address wrong");
system("pause");
exit(-1);
}
//eq 8003f500 0040ee00`00081040
interrupt();
system("pause");
}

image-20230602220050054

还可以用我们之前的做系统调用的方法去实现一个系统调用或中断来做