A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #32352  by AxtMueller
 Mon Dec 31, 2018 3:44 pm
Author: Axt Müller

If you are engaged in Windows driver development for many years, I guess you have a nightmare: how to read an unknown address in an absolutely safe way. We all know that, it is useless to test the validity of the address by MmIsAddressValid, even if this function return TRUE, the address may still be invalid. Read address in SEH (I mean put "memcpy" in "try...except") is only valid for user-mode address, only if you read an invalid address of user-mode, you can catch an exception. Some guys on the internet said: you can use IoAllocateMdl, MmProbeAndLockPages and MmGetSystemAddressForMdlSafe to read any kernel address, but this method still crashes system when you read some invalid addresses. Doubt my word? You can write a program to read address from 0x80000000 to 0xFFFFFFFF (32-bit), or from 0xFFFFF800`00000000 to 0xFFFFFFFF`00000000 (64-bit), I think your system will crash in 2 minutes. This problem has plagued me for many years, but I already got 3 good solutions now, let me share to all of you.

On Windows 2000, Windows XP and Windows 2003, you can use NtSystemDebugControl to read any address without crashing system. Code show as below:
Code: Select all
typedef struct _MEMORY_CHUNKS 
{
	PVOID Address;
	PVOID Data;
	ULONG Length;
}MEMORY_CHUNKS, *PMEMORY_CHUNKS;

const ULONG SysDbgCopyMemoryChunks_0 = 8

typedef NTSTATUS (_stdcall *NTSYSTEMDEBUGCONTROL)
(
	IN ULONG Command,
	IN PVOID InputBuffer,
	IN ULONG InputBufferLength,
	OUT PVOID OutputBuffer,
	IN ULONG OutputBufferLength,
	OUT PULONG ReturnLength
);

NTSYSTEMDEBUGCONTROL ZwSystemDebugControl = FUNCTION_ADDRESS;

NTSTATUS SafeReadKrnlAddr(PVOID TargetAddress, PVOID AllocatedBuffer, ULONG LengthYouWantToRead)
{
	MEMORY_CHUNKS mc;
	mc.Address = TargetAddress;
	mc.Length = LengthYouWantToRead;
	mc.Data = AllocatedBuffer;
	return ZwSystemDebugControl(SysDbgCopyMemoryChunks_0, &mc, sizeof(mc), NULL, 0, &NumberOfBytesRead);
}
But unfortunately, the above method is invalid since Windows Vista, So we need to use another method to read kernel memory after Windows Vista. We all knows that, we can get physical address of virtual address by MmGetPhysicalAddress, and we can map physical address by MmMapIoSpace. So the solution is clear now: get physical address of a virtual address, then map it to a new virtual address to read. Code show as below:
Code: Select all
BOOL SafeReadKrnlAddr(PVOID TargetAddress, PVOID AllocatedBuffer, ULONG LengthYouWantToRead)
{
	BOOL b = FALSE;
	PHYSICAL_ADDRESS PA;
	PA = MmGetPhysicalAddress(TargetAddress);
	if(PA.QuadPart)
	{
		NewVA = MmMapIoSpace(PA, LengthYouWantToRead, MmNonCached);
		if(NewVA)
		{
			memcpy(AllocatedBuffer, NewVA, LengthYouWantToRead);
			MmUnmapIoSpace(NewVA, LengthYouWantToRead);
			b = TRUE;
		}
	}
	return b;
}
After Windows 8.1, Microsoft finally provided a function that can read kernel memory safely: MmCopyMemory. This function is easy to use, the only thing to note is that you must use NonPagedPool memory as the buffer address. Code show as below:
Code: Select all
NTSTATUS SafeReadKrnlAddr(PVOID TargetAddress, PVOID AllocatedBuffer, ULONG LengthYouWantToRead)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	PVOID NonPagedBuffer = ExAllocatePool(NonPagedPool, LengthYouWantToRead);
	if(NonPagedBuffer)
	{
		SIZE_T NumberOfBytesTransferred = 0;
		MM_COPY_ADDRESS AddrToRead;
		AddrToRead.VirtualAddress = TargetAddress;
		status = MmCopyMemory(NonPagedBuffer, AddrToRead, LengthYouWantToRead, MM_COPY_MEMORY_VIRTUAL, &NumberOfBytesTransferred);
		if(STATUS_SUCCESS == status)
		{
			memcpy(AllocatedBuffer, NonPagedBuffer, NumberOfBytesTransferred);
		}
		ExFreePool(NonPagedBuffer);
	}
	return status;
}
In my project Windows Kernel Explorer (https://github.com/AxtMueller/Windows-Kernel-Explorer), I used Method 2 and Method 3 to read kernel memory for scanning kernel mode hooks. You can use my tool to scan hooks and let me know if it will crash the system when scanning hooks.
 #32354  by Brock
 Wed Jan 02, 2019 12:45 am
These methods as well as many others have been shared on this forum for some time now but for those less informed your examples may be informative, so thanks for this. As of 8.1 MmCopyMemory() is imho the best choice because it was designed to do exactly this and performs the underlying PTE validation, shadow range checking etc for you. Your method #2 using MmGetPhysicalAddress() would still need to deal with things like handling addresses in session space (Win32k, CDD, TSDDD, DXG), DMA etc. which has been mentioned here as well in the past. Lastly, your method #3 should allocate non-executable non-paged pool memory (NonPagedPoolNx) since you don't need your allocated memory to be executable, which otherwise would cause framework testing tools like HLK to fail.
 #32473  by AxtMueller
 Thu Jan 17, 2019 7:36 pm
Brock wrote: Wed Jan 02, 2019 12:45 am These methods as well as many others have been shared on this forum for some time now but for those less informed your examples may be informative, so thanks for this. As of 8.1 MmCopyMemory() is imho the best choice because it was designed to do exactly this and performs the underlying PTE validation, shadow range checking etc for you. Your method #2 using MmGetPhysicalAddress() would still need to deal with things like handling addresses in session space (Win32k, CDD, TSDDD, DXG), DMA etc. which has been mentioned here as well in the past. Lastly, your method #3 should allocate non-executable non-paged pool memory (NonPagedPoolNx) since you don't need your allocated memory to be executable, which otherwise would cause framework testing tools like HLK to fail.
yes, you're right, thanks for your suggestion.