Windows
修改对方进程内存
步骤
1.获取对方进程的页目录表
2.让自己程序的Cr3=对方进程的页目录表
3.然后执行自己程序,访问对方进程的内存地址,就可以对对方的内存进行操作
4.再还原成自己程序的Cr3
以上步骤有几个值得思考的问题
问题1
1.为什么把自己进程的Cr3修改为对方进程的页目录表还是可以执行自己的代码?自己的虚拟地址按照cr3进行查表,查出来的物理地址不会变吗??
这是有要求的,自己的程序必须是驱动,这样才能运行在系统的高两G。
而高两G是享有一样的页表的,才能保证在进程间共享
问题2
2.CPU在切换线程的时候,CR3会改变,那咋整
有时候我们写的驱动程序不一定只有一个线程,那么假设我们有两个线程,一个线程去执行将自己的cr3修改为一个三环进程的cr3,当修改成功后,好巧不巧,cpu将进程切换了,正巧切换成自己程序的另一个线程,那么cr3可能就会被改回原来的cr3,此时线程继续执行,因为cr3已经不是对方进程的了,因此访问对方进程就会崩溃,系统蓝屏。
那如何解决这个问题?
提升IRQL?
别以为稳了,因为如果碰巧要访问的地址是分页内存,被交换到了硬盘上,那就直接寄了,因为IRQL等级高,不能切换线程去把这个分页地址从物理地址拿出来。就会蓝屏
方法1:将要访问的地址映射为一个内核地址,这样就可以操作了,这样就不会造成缺页异常
方法2:cpu如何切线程?cpu有一个硬件叫时钟的硬件,每一段时间就会给cpu一个信号,这个信号就叫做中断信号,这样cpu就知道过去了多久,如果我们把这个信号屏蔽了,这样cpu就不会切线程了
cli(屏蔽中断) sti(恢复中断)
这样cpu就不知道过了多久,也就不知道切线程了
这样改的是屏蔽当前核心的中断
几个小测试:
查到的资料说高两G内存的共享的,于是我们测试一下:
测试将一个驱动程序的cr3改为一个随便一个进程的,看驱动是否能够正常运行
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include "header.h" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); KdPrint(("Create!")); KdBreakPoint(); DriverObject->DriverUnload = DriverUnloadRoutine; NTSTATUS status; PDEVICE_OBJECT DeviceObject = NULL; UNICODE_STRING DeviceName; RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDevice"); status = IoCreateDevice( DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject );
if (!NT_SUCCESS(status)) { KdPrint(("Failed to create device: %X\n", status)); return status; } KdPrint(("Device created successfully\n"));
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\MyDevice_Link"); status = IoCreateSymbolicLink(&symbolicLink, &DeviceName); if (!NT_SUCCESS(status)) { KdPrint(("Failed to create device: %X\n", status)); return status; } KdPrint(("Device created successfully\n")); return STATUS_SUCCESS; } VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject) { if (DriverObject->DeviceObject != NULL) { UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\MyDevice_Link"); IoDeleteSymbolicLink(&symbolicLink); IoDeleteDevice(DriverObject->DeviceObject); }
DbgPrint("Driver unloaded\n"); }
|
在驱动下了个断点,查看寄存器之后,发现此时cr3是185000(驱动本身不是一个进程, 它是一个由操作系统管理的内核模式下运行的模块 在某种意义上“附属于”System
进程,因为它们由操作系统管理,并且在内核模式下运行,而System
进程是管理这些内核模式组件的关键部分。 )
然后我们手动修改它的cr3:
随便挑一个,改为0x3eed2020
发现是可以运行的!
为了验证是不是cr3随便一个值都行,我甚至将cr3改为0
结果果然崩溃了。
因此结论是:将驱动的cr3改为任意进程的cr3,都可以运行,没有问题的
2.测试两个驱动程序的页表是否相同
这个问题是我一开始学页表的时候想到的,现在看来有点蠢
本身驱动程序就是属于System这个进程的模块,而Cr3是每一个进程独有的,而不是每一个模块独有的,因此所有驱动程序的Cr3都是System这个进程的cr3,因此页表都是一样的
自己实现!Process 0 0的功能
这时候就可以去逆向内核了
查阅资料我们可以知道,内核程序有
ntoskrnl.exe(单核,不开PAE)
ntkrnlpa.exe(单核,开了PAE)
ntkrnlmp.exe (多核处理器内核,不开PAE)
ntkrpamp.exe (多核处理器内核,开了PAE)
如何查看我们当前是用哪个内核,可以用Windbg
我们可以查看程序的模块:
1 2 3
| 1: kd> lm start end module name 83c09000 84032000 nt (pdb symbols) c:\symbolslocal\ntkrpamp.pdb\49C89B090A944A63AFF6A937166259C22\ntkrpamp.pdb
|
nt是 Windows 操作系统的内核模块。 根据PDB我们可以知道,当前我使用的内核程序是ntkrpamp.exe
但是值得注意的是:现代版本的 Windows 已经合并了不同的内核版本,统一使用 ntoskrnl.exe
作为内核映像文件,并在系统启动时动态决定使用哪些功能和优化。
真是可恶啊,害我找半天ntkrpamp.exe,到处都没有翻到,原来你小子统一叫ntoskrnl.exe了,真淦
Windbg可以支持模糊搜索,x命令用于查找模块中包含特定名称的符号
通过查资料,我们可以知道一个API叫做KiSwapProcess这个函数,这个函数可以用来切进程,可以看看是否涉及进程遍历。
有时候遇到函数没有导出的,我们可以用找到nt模块基地址,计算出函数的偏移,然后再加上这个内核exe的ImageBase就可以在IDA找到对应的函数了
用IDA查看伪代码可以发现
最终往cr3写入的是一个叫做v4的变量
通过不断溯源,不断查看引用,最终我们发现,这个v4变量来源为:
来自一个PRKPROCESS结构的变量(我觉得叫PKPROCESS会好一点,P后面那个R我也不知道干啥的,也许是调用约定?),指向KPROCESS结构体
查看发现,KiSwapProcess拿的就是KPROCESS结构体里面的DirectoryTableBase
那么我们的问题变成,如何遍历所有进程的KPROCESS结构体
所以我们要思考,有哪些API会遍历所有进程对象
我们想到PsGetCurrentProcess
发现返回的是一个PEPROCESS的一个结构体,EPROCESS又是什么结构体?我们拿Windbg看一下
发现是一个包含的关系,也就是EPROCESS包含KPROCESS,而且KPROCESS还是在第一个,说明KPROCESS和EPROCESS是同地址
逆向PsGetCurrentProcess函数:
发现那这个结构体非常简单,就三行汇编代码,去fs寄存器地址加上124h的偏移就拿到了
那这个fs:124h又是何方神圣呢?我们再去逆向KeGetCurrentThread这个API函数
这下我们知道了,fs:124存的是ETHREAD结构体
我们捋一下思路,目前是ETHREAD->EPROCESS->DIRBASE
那如何获取全部的EPROCESS呢?
这里有一个链表结构,这个链表就可以遍历到所有进程
这个Link结构指向下一个EPROCESS的Link结构
fs:0是这个结构体:KPCR (Kernel Processor Control Region)
如何查看fs的虚拟地址?在之前的分段机制提到了,但是这里可以直接用Windbg的功能实现:
这样可以列出来具体KPCR 这个结构体的基地址
这样,我们就可以写驱动程序去遍历EPROCESS从而拿到DirBase了
下面是输出全部进程的信息,再指定输出某进程的DirBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| VOID Traversal_Eprocess() { ULONG KTHREAD = NULL; ULONG KPROCESS = NULL; ULONG EPROCESS = NULL; LIST_ENTRY* PActiveProcessLinks = nullptr;
KdBreakPoint();
__asm { mov eax,fs:[0x124] mov KTHREAD ,eax
mov eax,[eax+0x50] mov KPROCESS,eax }
EPROCESS = KPROCESS; PActiveProcessLinks = (LIST_ENTRY*)(EPROCESS + 0xb8);
while ((ULONG)(*(ULONG*)PActiveProcessLinks->Flink - 0xb8) != EPROCESS) { ULONG Temp_KPROCESS = (ULONG)(*(ULONG*)PActiveProcessLinks->Flink - 0xb8); ULONG DirBaseAddr = *(ULONG*)(Temp_KPROCESS + 0x18); CHAR* ImageFileName = (CHAR*)(Temp_KPROCESS + 0x16C); if (!strcmp(ImageFileName,"Solve.exe")) { Solve_DirBase = DirBaseAddr; } KdPrint(("进程%s的DirBase为0x%x\n", ImageFileName, DirBaseAddr)); PActiveProcessLinks = PActiveProcessLinks->Flink; }
KdPrint(("Solve.exe的DirBase为0x%x\n", Solve_DirBase)); }
|
PsGetNextProcess
我们也可以直接用这个函数,去获得所有的EPROCESS结构体
1 2 3 4
| PEPROCESS Process = NULL; while ((Process = PsGetNextProcess(Process)) != NULL) { }
|
虽然简单,但是还是有风险的,如果这个函数被HOOK了,那就无法正常获取了
通过Cr3写代码:
用户代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> using namespace std; int main() { char str[256] = { 'h','e','l' ,'l' ,'o' ,' ' ,'w' ,'o' ,'r' ,'l' ,'d' ,'\x00' }; cout <<hex<<"0x" << &str << endl; int a; cin >> a; int b = a * 8; cout << str << endl; system("pause"); return 0; }
|
驱动代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| #include "header.h"
ULONG Solve_DirBase = NULL;
VOID Traversal_Eprocess() {
ULONG KTHREAD = NULL; ULONG KPROCESS = NULL; ULONG EPROCESS = NULL; LIST_ENTRY* PActiveProcessLinks = nullptr;
KdBreakPoint();
__asm { mov eax,fs:[0x124] mov KTHREAD ,eax
mov eax,[eax+0x50] mov KPROCESS,eax }
EPROCESS = KPROCESS; PActiveProcessLinks = (LIST_ENTRY*)(EPROCESS + 0xb8);
while ((ULONG)(*(ULONG*)PActiveProcessLinks->Flink - 0xb8) != EPROCESS) { ULONG Temp_KPROCESS = (ULONG)(*(ULONG*)PActiveProcessLinks->Flink - 0xb8); ULONG DirBaseAddr = *(ULONG*)(Temp_KPROCESS + 0x18); CHAR* ImageFileName = (CHAR*)(Temp_KPROCESS + 0x16C); if (!strcmp(ImageFileName,"Solve.exe")) { Solve_DirBase = DirBaseAddr; } KdPrint(("进程%s的DirBase为0x%x\n", ImageFileName, DirBaseAddr)); PActiveProcessLinks = PActiveProcessLinks->Flink; }
KdPrint(("Solve.exe的DirBase为0x%x\n", Solve_DirBase)); }
VOID Open() { ULONG cr0 = __readcr0(); cr0 &= 0xFFFEFFFF; __writecr0(cr0); _disable(); }
VOID Edit_Memory() {
ULONG Old_cr3 = NULL; ULONG Ring3_Virtual = 0x1AFCB8; Open(); __asm { mov eax, cr3 mov Old_cr3, eax mov eax, Solve_DirBase mov cr3,eax } strcpy((char*)Ring3_Virtual, "123456");
__asm { mov eax, Old_cr3 mov cr3, eax } }
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); KdPrint(("Create!"));
DriverObject->DriverUnload = DriverUnloadRoutine; NTSTATUS status; PDEVICE_OBJECT DeviceObject = NULL; UNICODE_STRING DeviceName; RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDevice"); status = IoCreateDevice( DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject );
if (!NT_SUCCESS(status)) { KdPrint(("Failed to create device: %X\n", status)); return status; } KdPrint(("Device created successfully\n"));
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\MyDevice_Link"); status = IoCreateSymbolicLink(&symbolicLink, &DeviceName); if (!NT_SUCCESS(status)) { KdPrint(("Failed to create device: %X\n", status)); return status; } KdPrint(("Device created successfully\n"));
Traversal_Eprocess(); Edit_Memory(); return STATUS_SUCCESS; }
VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject) { if (DriverObject->DeviceObject != NULL) { UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\MyDevice_Link"); IoDeleteSymbolicLink(&symbolicLink); IoDeleteDevice(DriverObject->DeviceObject); }
DbgPrint("Driver unloaded\n"); }
|