A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #12987  by iSecure
 Wed May 02, 2012 11:04 am
Hello again =)

I want to understand how modern kernel-mode memory dumper and forensics tools works.

Found some explanation about user-mode process memory dump here: http://www.kernelmode.info/forum/viewto ... dump#p5887
Could someone provide me with similar information (what APIs or techniques used) for kernel-mode memory dump.

What mostly bothers me is:
1) how code which perform dumping can be sure that memory region it reads is valid (to prevent BSODs).
2) how code which perform dumping can be sure that memory region it reads will not change before it completes (is there some kernel-mode system wide memory lock which must be acquired?).

Any help will be appreciated. Thanks in advance.
 #12989  by EP_X0FF
 Wed May 02, 2012 12:40 pm
For a start look on Ms-Rem Extreme dumper (in attach). Forensic tools usually works with dumps of physical memory. For this approach see http://forum.sysinternals.com/physical- ... W=physdump (also it has link to win32dd).

old 5 years dated code from physdump responsible for dumping.
Code: Select all
NTSTATUS MmGenerateFullMemoryDump (IN PUNICODE_STRING OutputFileName)
{
	UNICODE_STRING PhysicalMemoryDevice;
	OBJECT_ATTRIBUTES MemoryAttributes, DumpFileAttributes;
	SYSTEM_BASIC_INFORMATION SystemBasicInfo;
	HANDLE MemoryHandle; 
	HANDLE DumpFileHandle;
	IO_STATUS_BLOCK IoStatusBlock;
	LARGE_INTEGER ViewBase;
	NTSTATUS Status;
	ULONG PageIndex;
	SIZE_T ViewSize;
	PUCHAR Buffer;
	PCHAR NullPage;
	PEPROCESS Process;
    ACCESS_MASK DesiredSectionAccess;
    ULONG ProtectMaskForAccess;
 	PSECTION Section;

    RtlInitUnicodeString(&PhysicalMemoryDevice, L"\\Device\\PhysicalMemory");

    InitializeObjectAttributes(&MemoryAttributes,
                               &PhysicalMemoryDevice,
                                OBJ_CASE_INSENSITIVE,
                               (HANDLE) NULL,
                               NULL);

    InitializeObjectAttributes(&DumpFileAttributes, 
				OutputFileName,
                                OBJ_KERNEL_HANDLE,
                                (HANDLE) NULL,
                                NULL);

    MemoryHandle = 0;
    Status = ObOpenObjectByName(&MemoryAttributes,
                                 MmSectionObjectType,
                                 KernelMode,
                                 NULL,
                                 SECTION_MAP_READ,
                                 NULL,
                                 &MemoryHandle);

    if ( !NT_SUCCESS(Status) )
    {
		return STATUS_ACCESS_DENIED;
    }

    Status = ZwQuerySystemInformation(SystemBasicInformation, &SystemBasicInfo, 
		sizeof(SYSTEM_BASIC_INFORMATION), 0);

    if ( !NT_SUCCESS(Status) )
	{
		goto Exit;
	}
		
	if ( SystemBasicInfo.PhysicalPageSize == PAGE_SIZE )
	{
		Status = ZwCreateFile(&DumpFileHandle,
			FILE_WRITE_ACCESS | SYNCHRONIZE,
			&DumpFileAttributes,
			&IoStatusBlock,
			NULL,
			FILE_ATTRIBUTE_NORMAL,
			0,
			FILE_SUPERSEDE,
			FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,
			NULL,
			0);

			if ( NT_SUCCESS(Status) ) 
			{
					 ViewBase.LowPart = 0;
					 ViewBase.HighPart = 0;
				     NullPage = (PCHAR)ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, 'pmuD');
					 if (NullPage)
					 {
						memzero(NullPage, PAGE_SIZE);
								 
 					 	Process = IoGetCurrentProcess();
						ProtectMaskForAccess = MiMakeProtectionMask(PAGE_READONLY);   
						ProtectMaskForAccess = ProtectMaskForAccess & 0x7;
						DesiredSectionAccess = MmMakeSectionAccess[ProtectMaskForAccess];
						
						Status = ObReferenceObjectByHandle(MemoryHandle,
								            DesiredSectionAccess,
									        MmSectionObjectType,
										    KernelMode,
											(PVOID *)&Section,
											NULL);
						if ( NT_SUCCESS(Status) )
						{
							for (PageIndex = 0; PageIndex < SystemBasicInfo.NumberOfPhysicalPages; PageIndex++)
							{
						        Buffer = NULL;
								ViewSize = PAGE_SIZE;
		
								Status = MmMapViewOfSection((PVOID)Section,
										Process,
										(PVOID *)&Buffer,
										0L,
										PAGE_SIZE,
										&ViewBase,
										&ViewSize,
										ViewUnmap,
										0,
										PAGE_READONLY);							

								if ( !NT_SUCCESS(Status) )
						        {
									if (PageIndex > 0)
									{
						                Status = ZwWriteFile(DumpFileHandle, 0, 0, 0, 
												&IoStatusBlock, NullPage, PAGE_SIZE, &ViewBase, 0);
									            ViewBase.LowPart += PAGE_SIZE;
								        continue;
									}
									break;
								}

							    Status = ZwWriteFile(DumpFileHandle, 0, 0, 0, 
											&IoStatusBlock, Buffer, PAGE_SIZE, &ViewBase, 0);

								if ( !NT_SUCCESS(Status) )
								{
									MmUnmapViewOfSection(Process, Buffer);
								    break;
								}
								MmUnmapViewOfSection(Process, Buffer);
								ViewBase.LowPart += PAGE_SIZE;
							}
							if (PageIndex == SystemBasicInfo.NumberOfPhysicalPages) Status = STATUS_SUCCESS;
							ObfDereferenceObject(Section);
						}					
						ExFreePoolWithTag(NullPage, 'pmuD');

					 }
					 //ExAllocatePoolWithTag
					 ZwClose(DumpFileHandle);

			} //ZwCreateFile
	}

Exit:
	ZwClose(MemoryHandle);
	return Status;
}
Attachments
(67.64 KiB) Downloaded 81 times
 #13001  by EP_X0FF
 Thu May 03, 2012 6:28 am
GamingMasteR wrote:Ms-Rem code will BSOD in some cases.
Maybe you have anything you can share instead? :)
 #13003  by iSecure
 Thu May 03, 2012 9:27 am
Rapidly looked at the code, didn't found usage of any locks. Dumping all physical memory is quite 'heavy' operation (in terms of performance), so how could we be sure that content of dump reflects actual memory at the moment of starting the dumping routine, and that some memory didn't changed between points A and B, where A - moment of dump routine start, and B - moment of dump routine finish. In my opinion this is a problem, how it can be solved?

Also some of forensics tools (currently looking at Mandiant Memoryze) claim that they don't use system APIs to obtain physical memory dump, so how do they do it?
 #13006  by GamingMasteR
 Thu May 03, 2012 3:24 pm
Well, the main problem is that walking through physical memory page 0 to NumberOfPhysicalPages will count some memory portions reserved to other hardware devices, this will usually corrupt and crash the system. You will notice that if you test the code in x64 system with RAM > 4GB.

The most stable method I've tried is to get physical memory portions available from the MM by using MmPhysicalMemoryRange array, you can get it by calling MmGetPhysicalMemoryRanges wich works from w2k and later.
Then you can walk MmPhysicalMemoryRange array and read every page without falling into device-reserved pages.

Then you can use normally MmMapViewOfSection or MmMapMemoryDumpMdl :
Code: Select all
PVOID GetPhysicalPageMappedAddress(PFN_NUMBER PageFrameIndex)
{
	PFN_NUMBER MdlHack [ (sizeof (MDL) / sizeof (PFN_NUMBER)) + 1];
	PMDL TempMdl;
	PPFN_NUMBER PfnArray;

	RtlZeroMemory(&MdlHack, sizeof(MdlHack));
	TempMdl = (PMDL) &MdlHack;
	MmInitializeMdl(TempMdl, NULL, PageSize);
	PfnArray = MmGetMdlPfnArray ( TempMdl );
	PfnArray[0] = PageFrameIndex;
	TempMdl->ByteCount = PageSize;
	TempMdl->ByteOffset = 0;
	MmMapMemoryDumpMdl(TempMdl);
	TempMdl->MdlFlags |= ( MDL_PAGES_LOCKED | MDL_MAPPED_TO_SYSTEM_VA );

	return TempMdl->MappedSystemVa;
}

NTSTATUS MmReadPhysicalPage(PFN_NUMBER PageFrameIndex, PVOID Buffer)
{
	NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
	PVOID BaseAddress = NULL;

	BaseAddress = GetPhysicalPageMappedAddress(PageFrameIndex);
	if (BaseAddress != NULL && MmIsAddressValid(BaseAddress))
	{
		RtlCopyMemory(Buffer, BaseAddress, PageSize);
		ntStatus = STATUS_SUCCESS;
	}
	return ntStatus;
}
 #13106  by rossetoecioccolato
 Tue May 08, 2012 2:50 pm
At the following link you will find some code which illustrates the 3 most common ways of doing this: http://code.google.com/p/volatility/sou ... d%2Fdriver. The code is derived from an old version of win32dd at a time when this product was open source. Note that I am not recommending this code. Some control paths will crash your system when run on a computer with more than 4 GiB of memory. All of the control paths may (probably will) cause processor TLB corruption. However, it shows some common ways to acquire physical memory on Microsoft Windows systems.