• Windows内核地址跑r3代码实现


    背景

    最近在看雪上看到了一篇修改页属性的博客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生成的

    执行之后的效果如下

  • 相关阅读:
    Junit连接oracle数据库
    java判断字符串是否由数字组成
    Hibernate各种主键生成策略与配置详解
    一对多映射关系
    one-to-one 一对一映射关系(转 wq群)
    工厂模式
    struts2
    创建JUtil
    jdbc
    压缩数据
  • 原文地址:https://www.cnblogs.com/csnd/p/16675587.html
Copyright © 2020-2023  润新知