Windows内核地址跑r3代码实现

本文探讨了一种针对Windows系统的内核内存篡改技术,通过修改页表项实现用户态读取内核内存。博客作者分享了修改内核页表的代码,并指出该方法仅适用于Win7 SP1,且在Win10 1903及更高版本中可能导致蓝屏。同时,文中提到在创建线程时不能直接使用内核地址,需要借助用户态地址作为中转。
摘要由CSDN通过智能技术生成

背景

最近在看雪上看到了一篇修改页属性的博客https://bbs.pediy.com/thread-266781.htm,之前的同事也提到过有人在r0跑dll,于是趁着这个机会把原作者的代码改了下,将申请的内核地址改成user可读,如下所示,这里有几点需要注意的

1、在win10 1903 中测试失败,蓝屏0x1a,会对pxe做检查

2、内核的pte基址是写死的

3、不支持大页

#include <Ntifs.h>
#include <ntimage.h>
#include <ntstrsafe.h>


//onlly working in win 7 sp1


#define KERNEL_CR3_OFFSET 0x28

PVOID g_UserRead = NULL;
PVOID MiGetXXXAddress(ULONG64 VirtualAddress, PVOID PteBase) {
	return ((VirtualAddress >> 9) & 0x7FFFFFFFF8) + (ULONG64)PteBase;
}

PVOID GetBase() {
	//PUCHAR BaseAddr = MmGetVirtualForPhysical;
	//return *(PUINT64)(BaseAddr + 0x22);
	//win 7
	return 0xFFFFF68000000000;
}
EXTERN_C PUCHAR PsGetProcessImageFileName( PEPROCESS Process);

VOID ProcessNotifyRoutine(_In_ HANDLE ParentId,_In_ HANDLE ProcessId,_In_ BOOLEAN Create){
	if (Create){
		PVOID tmpRead = (PVOID)((ULONG64)g_UserRead + 0x1000);
		PULONG64 PteBase = GetBase();
		PULONG64 Pte = (PULONG64)MiGetXXXAddress(g_UserRead, PteBase);
		PULONG64 Pde = (PULONG64)MiGetXXXAddress(Pte, PteBase);
		PULONG64 Ppe = (PULONG64)MiGetXXXAddress(Pde, PteBase);
		PULONG64 Pxe = (PULONG64)MiGetXXXAddress(Ppe, PteBase);


		ULONG PXEIndex = ((ULONG64)g_UserRead >> 0x27) & 0x1FF;
		ULONG PPEIndex = ((ULONG64)g_UserRead >> 0x1E) & 0x1FF;
		ULONG PDEIndex = ((ULONG64)g_UserRead >> 0x15) & 0x1FF;
		ULONG PTEIndex = ((ULONG64)g_UserRead >> 0xC) & 0x1FF;
		ULONG FuncOffset = (ULONG64)g_UserRead & 0xFFF;

		PVOID TmpAddress = MiGetXXXAddress(tmpRead, PteBase);
		ULONG64 TmpPTE = *(PULONG64)TmpAddress;

		
		//修正pte的值
		*(PULONG64)TmpAddress = *Pde;
		*(PULONG)((ULONG64)tmpRead + PTEIndex * 8) |= 4; //这里需要清楚g位
		__invlpg(tmpRead);
		//*(PULONG)((ULONG64)tmpRead + PTEIndex * 8) -= 0x100;
		//修正pde
		*(PULONG64)TmpAddress = *Ppe;
		*(PULONG)((ULONG64)tmpRead + PDEIndex * 8) |= 4;
		__invlpg(tmpRead);
		//修正ppe
		*(PULONG64)TmpAddress = *Pxe;
		*(PULONG)((ULONG64)tmpRead + PPEIndex * 8) |= 4;
		__invlpg(tmpRead);
		*(PULONG64)TmpAddress = TmpPTE;//还原




		NTSTATUS status = STATUS_SUCCESS;
		PEPROCESS Process;
		status = PsLookupProcessByProcessId(ProcessId, &Process);
		PUCHAR pFileName = PsGetProcessImageFileName(Process);
		char buf[] = "R3Worker.exe";
		if (strcmp(pFileName, buf) == 0){

			//DbgBreakPoint();
			PVOID pDirectoryTableBase = (ULONG64)Process + KERNEL_CR3_OFFSET;
			//修正pxe
			PVOID TmpAddress = MiGetXXXAddress(tmpRead, PteBase);
			ULONG64 TmpPTE = *(PULONG64)TmpAddress;
			*(PULONG64)TmpAddress = (*(PULONG64)pDirectoryTableBase) & (~0xFFF) | 0x063;
			*(PULONG)((ULONG64)tmpRead + PXEIndex * 8) |= 4;
			__invlpg(tmpRead);
			*(PULONG64)TmpAddress = TmpPTE;//还原
		}
		ObDereferenceObject(Process);
	}
}

VOID Unload(PDRIVER_OBJECT pDriverObj) {
	if(g_UserRead)
		ExFreePoolWithTag(g_UserRead, '123');
	PsSetCreateProcessNotifyRoutine(ProcessNotifyRoutine, TRUE);
	DbgPrint("See you! \n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath) {
	DriverObject->DriverUnload = Unload;

	//申请一块内存然后把它user的访问置位
	g_UserRead =  ExAllocatePoolWithTag(NonPagedPool,PAGE_SIZE * 2, '123');
	if (!g_UserRead)
		return STATUS_UNSUCCESSFUL;
	PsSetCreateProcessNotifyRoutine(ProcessNotifyRoutine, FALSE);
	DbgPrint("address  0x%llX   \n", g_UserRead);
	return STATUS_SUCCESS;
}

之后在r3往申请的内存写入shellcode,但是需要注意的是在启动线程的时候不能填写内核地址,CreateThread在内核中会校验先前模式,由于是r3来的,但是传入的是一个r0的地址,当然会出错,此时就需要一个地址用来做中转,如果要自己测试需要修改申请的内核地址

#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
#define _CRT_SECURE_NO_WARNINGS

int main(){


	unsigned char sc[] =
		"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
		"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
		"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
		"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
		"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
		"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
		"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
		"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
		"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
		"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
		"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
		"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
		"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
		"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
		"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
		"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
		"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
		"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
		"\x63\x2e\x65\x78\x65\x00";


	unsigned char relayBuffer[] = { "\x48\xB8\x00\xE0\x77\x02\x80\xFA\xFF\xFF\xFF\xE0\x00" };

	LPVOID relayAddress = VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	if (!relayAddress)
		return 0;


	PULONG64 address = 0xFFFFFA8002708000;
	__try{
		ULONG64 key = *address;
		memcpy(address, sc, 277); //sc拷贝到内核地址
		memcpy(relayAddress, relayBuffer,13);//中间跳 // mov rax xxxx  jmp rax
		memcpy((PUCHAR)relayAddress + 2, &address, 8);//修改地址为内核地址
		system("pause");
		CreateThread(NULL, NULL, relayAddress,NULL,NULL,NULL);//这里不能直接指定内核地址,有地址校验会报0xc000000d,参数错误,所以这里需要一r3地址
	}__except (EXCEPTION_EXECUTE_HANDLER) {
		printf("GetLastError %d \n", GetLastError());
	}
	system("pause");

	return 0;
}

shellcode是从msf生成的

执行之后的效果如下

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值