Accessing kernel memory from user mode (Windows) - windows

I'm writing a driver that needs to allocate a Non Paged pool of memory and this memory, for performance sake, must be directly accessible from a usermode program.
In the driver entry I've allocated some memory with these two type of methods:
pMdl = IoAllocateMdl(NULL,
4096,
FALSE,
FALSE,
NULL);
if(!pMdl) {
DbgPrintEx(DPFLTR_IHVVIDEO_ID, DPFLTR_INFO_LEVEL, "Error on IoAllocateMdl. Returning from driver early.\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(pMdl);
userMemory = (void *)MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmWriteCombined, NULL, FALSE, LowPagePriority);
and
userMemory = ExAllocatePoolWithTag(
NonPagedPool,
4096,
POOL_TAG);
Now I don't want to issue a DeviceIoControl every time I need to write/read from this memory, but instead I want to do something like this:
char* sharedMem;
.....
transactionResult = DeviceIoControl ( hDevice,
(DWORD) IOCTL_MMAP,
NULL,
0,
sharedMem,
sizeof(int),
&bRetur,
NULL
);
.....
sharedMem[0]='c';
Using a DeviceIoControl to get the address in kernel memory and then using it directly, like it were an mmap under Linux.
Is there some kind of way to do this in Windows?
I've done this:
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // Read/write access
TRUE,
"Global\\SharedMemory"); // Name of mapping object
lastError = GetLastError();
if (hMapFile == NULL)
{
printf("Could not create file mapping object (%d).\n" ,GetLastError());
return 1;
}
pBuf = (char*)MapViewOfFile(hMapFile, // Handle to map object
FILE_MAP_ALL_ACCESS, // Read/write permission
0,
0,
4096);
if (pBuf == NULL)
{
printf("Could not map view of file (%d).\n", GetLastError());
CloseHandle(hMapFile);
return 1;
}
pBuf[0] = 'c';
pBuf[1] = '\n';
CloseHandle(hMapFile);
And I've created the view in Kernel like this:
RtlInitUnicodeString(&name, L"\\BaseNamedObjects\\SharedMemory");
InitializeObjectAttributes(&oa, &name, 0, 0, NULL);
ZwCreateSection(&hsection, SECTION_ALL_ACCESS, &oa, &Li, PAGE_READWRITE, SEC_COMMIT, NULL);
ZwMapViewOfSection(hsection, NtCurrentProcess(),
&userMem, 0, MEM_WIDTH, NULL,
&j, ViewShare, 0, PAGE_READWRITE);
But in the kernel when I read the memory it's empty: how can it be?

I finally understood how this needs to work.
First I've created a structure like the following.
typedef struct _MEMORY_ENTRY
{
PVOID pBuffer;
} MEMORY_ENTRY, *PMEMORY_ENTRY;
This will be used to return the virtual address from the kernel space to the user space.
In the DriverEntry I used
userMem = ExAllocatePoolWithTag(NonPagedPool,
MEM_WIDTH,
POOL_TAG );
to set up the NonPaged memory.
Then I've created an IOCTL working in DIRECT_OUT mode that does the following snippet:
...
PMDL mdl = NULL;
PVOID buffer = NULL;
MEMORY_ENTRY returnedValue;
void* UserVirtualAddress = NULL;
...
buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); // Gets safely the pointer for the output in the IRP
mdl = IoAllocateMdl(userMem, MEM_WIDTH, FALSE, FALSE, NULL); // Allocate the memory descriptor list
MmBuildMdlForNonPagedPool(mdl); // This is needed when we're managing NonPaged memory
UserVirtualAddress = MmMapLockedPagesSpecifyCache(
mdl,
UserMode,
MmNonCached,
NULL,
FALSE,
NormalPagePriority); // Return the virtual address in the context of
// the user space program who called the IOCTL
returnedValue.pBuffer = UserVirtualAddress;
RtlCopyMemory(buffer,
&returnedValue,
sizeof(PVOID)); // I copy the virtual address in the structure that will
// be returned to the user mode program by the IRP
In the user mode program I just needed to to this
transactionResult = DeviceIoControl(
hDevice,
(DWORD) IOCTL_MMAP,
NULL,
0,
sharedMem,
sizeof(void*),
&bRetur,
NULL
);
In (MEMORY_ENTRY*)sharedMem->pBuffer we will find the memory area created and shared by the kernel space directly accessible by the kernel and by the user program.
I haven't wrote it but we need to remember to wrap the entire MmGetSystemAddressForMdlSafe(...)----->RtlCopyMemory(...) in a Try...Except block because we can encounter various problems here that may eventually cause a BugCheck so better be safe than sorry.
Anyway, if you're compiling this kind of code in a checked environment the Microsoft AutocodeReview will be pointing this out.
If someone needs more clarifications, or if I wrote something wrong just let me know and I will be happy to modify this post.

Related

How to get a serial number of a Windows disk?

I'm trying to get a serial number of a disk, using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
struct {
USHORT Reserved;
USHORT SerialNumberLength;
UCHAR SerialNumber[252];
} dsn;
DWORD nr;
memset(&dsn, '\0', sizeof dsn);
if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
printf("Serial number: %s\n", dsn.SerialNumber);
} else {
printf("No serial number, error %d.\n", (int)GetLastError());
}
}
However, GetLastError() returns ERROR_INVALID_FUNCTION.
The disk does exist, and it has a serial number, see this registry entry:
How can I retrieve the serial number from C code without using the registry?
we can use IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty (Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR)
and use SerialNumberOffset member of STORAGE_DEVICE_DESCRIPTOR
Specifies the byte offset from the beginning of the structure to a
NULL-terminated ASCII string that contains the device's serial number.
If the device has no serial number, this member is zero.
code can look like this:
ULONG GetSerial(HANDLE hFile)
{
static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
ULONG dwError;
do
{
dwError = ERROR_NO_SYSTEM_RESOURCES;
if (buf = LocalAlloc(0, size))
{
ULONG BytesReturned;
if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
{
if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
dwError = ERROR_MORE_DATA;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
dwError = NOERROR;
}
else
{
dwError = ERROR_NO_DATA;
}
}
}
else
{
dwError = ERROR_GEN_FAILURE;
}
}
else
{
dwError = GetLastError();
}
LocalFree(buf);
}
} while (dwError == ERROR_MORE_DATA);
return dwError;
}
also for open device we can use CreateFileW (L"\\\\.\\PhysicalDrive0", 0, 0, 0, OPEN_EXISTING, 0, 0); - in place dwDesiredAccess we can use 0 because IOCTL_STORAGE_QUERY_PROPERTY defined as
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - so FILE_ANY_ACCESS - accept any file access and FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING have sense only for file system devices (more general which use cache) - for disk devices - this is irrelevant
I have tried different approaches and figured out that sending IOCTL_STORAGE_QUERY_PROPERTY doesn't work as expected for different USB devices in both User and Kernel mode code. For some USB mass storages it doesn't return serial number. I'd assume that there are 2 correct ways to do that:
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER (a few AV products do that?)
create and send URB_CONTROL_DESCRIPTOR_REQUEST
Update 1.
I saw using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER in the one file system mini-filter driver that was used like the following:
FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,
...
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,....
I haven't tested this yet in KernelMode code, unfortunately, but trying to make it works in user mode code shows that this IOCTL mostly
is not supported by different devices, maybe this IOCTL is reserved for the future as a standard way to get the serial number and will be
required by USB standards later?
Also, "wmic diskdrive get name, serialnumber" returns in my case for USB Mass Storage incorrect serial number = "E" the same result as we would use IOCTL_STORAGE_QUERY_PROPERTY.
So, the correct way to get the serial number of USB mass storage is creating a USB request block in KernelMode code and using DeviceIoControl to the hub driver in the UserMode code.
USBVIEW (UserMode code) gets serial number by sending IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ioctl to the HUB driver which
returns USB_NODE_CONNECTION_INFORMATION_EX that contains USB_DEVICE_DESCRIPTOR. iSerialNumber member of USB_DEVICE_DESCRIPTOR is used later in the
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl request to the hub driver which finally gets serial number.
Another approach I see is maybe using some 3-rd party libraries like libusb to simplicate all these actions...
Update 2.
I took a look at USBSTOR disassembled code. USBSTOR_DeviceControl routine has the following code for the IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
So, it passes the IRP down the stack to the usbhub driver as was expected. So maybe this functionlaty is expected to be realized in
the usbhub driver sometime ? That would be great as for me...

DeviceIoControl error 1 incorrect function

I have created a device in kernel space and the access it in user space using CreateFile I am able to send ioctl to the driver and they are executed properly. The don't know how to trace what happens after WdfRequestComplete and upon return I end with error 1 (invalid function). Before this is flagged as dup please note there is a difference with this in that I write my driver ioctl and in that I am using synch io not asynch.
In user space:
fd = CreateFile(dev_path,
(FILE_GENERIC_READ | FILE_GENERIC_WRITE),
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL, OPEN_EXISTING, 0, NULL);
// ... error checking code here
DeviceIoControl(fd, // device handler
VIRTQC_CMD_MMAP, // command to send
&inputBuffer,
inputBufferLength,
&outputBuffer,
outputBufferLength,
&returnLength,
(LPOVERLAPPED)NULL); // overlapped structure not needed using sync io
and in Kernel space
status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &inputBuffer, NULL);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
inputVirtArg = (VirtioQCArg*)inputBuffer;
status = WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, &outputBuffer, NULL);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
outputVirtArg = (VirtioQCArg*)outputBuffer;
switch (IoControlCode)
{
case VIRTQC_CMD_MMAP:
if (PsGetCurrentThread() == irp->Tail.Overlay.Thread)
{
status = CreateAndMapMemory(device, &(inputVirtArg), &(outputVirtArg));
outputVirtArg->flag = (!NT_SUCCESS(status)) ? 0 : 1;
}
else
status = STATUS_UNSUCCESSFUL;
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
WdfRequestComplete(Request, status);
Update 1:
I have tried WdfRequestCompleteWithInformation(Request, status, OutputBufferLength); but same result.
Also, I notice that the address of inputBuffer and outputBuffer are the same.
Update 2:
I tried doing
temp = ExAllocatePoolWithTag(
NonPagedPool,
PAGE_SIZE,
MEMORY_TAG
);
// other code to
RtlCopyMemory((VirtioQCArg*)outputBuffer, temp, OutputBufferLength);
still get error 1
I had defined my ioctl cmds as enums in my linux driver (which works fine) and when implementing the driver in windows I used the same enum definition.
enum
{
// Module & Execution control (driver API)
VIRTIQC_SET = 200,
VIRTIQC_UNSET,
// more cmds.....
}
In windows defining control codes take a bit more. As explained here the CTL_CODE macro should be used to define new IOCTL control codes.
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
In my case I ended up with defining this:
#define VIRTQC_MAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC8, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define VIRTQC_UNMAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC9, METHOD_OUT_DIRECT, FILE_READ_DATA)
I know the function code less than 0x800 are reserved for MS but the device on my host requires this codes so I'm just providing what is being asked.

Error while reading other process memory

I'm using ReadProcessMemory to read a single byte out of a process i've created.
Since i'm attaching as a debugger, i'm reading addresses that are being executed now (or in the near past).
but i get a 299 error for ReadProcessMemory via GetLastError() on some addresses only (some works fine..)
On the cases i get an error, i call VirtualQueryEx, and the memInfo protect is 0x1, while the type & baseAddress are 0x0 (but the region size is some normal number), also VirtualQueryEx isn't failing..
if i call VirtualProtectEx for those cases, i get error 487 (Attempt to access invalid address).
i thought maybe the address i'm trying to read is paged out, thus all the errors, but it doesn't seem right since, as i've already mentioned, its an address that was executed recently.
ideas anyone?
You want to loop through all the memory, calling VirtualQueryEx() to make sure the memory is valid before calling ReadProcessMemory()
You need to make sure that MEMORY_BASIC_INFORMATION.State is MEM_COMMIT in most cases.
This whole operation can be easy to screw up, because you didn't supply any code I will provide a working solution that should work in 95% of situations. It's ok for bytesRead to be different than regionSize, you just need to handle that situation correctly. You don't need to take permissions in most cases using VirtualProtect because all valid memory should have read access.
int main()
{
DWORD procid = GetProcId("notepad.exe");
unsigned char* addr = 0;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
{
if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS)
{
char* buffer = new char[mbi.RegionSize]{ 0 };
SIZE_T bytesRead = 0;
if (ReadProcessMemory(hProc, addr, buffer, mbi.RegionSize, &bytesRead))
{
if (bytesRead)
{
//scan from buffer[0] to buffer[bytesRead]
}
else
{
//scan from buffer[0] to buffer[mbi.RegionSize]
}
}
delete[] buffer;
}
addr += mbi.RegionSize;
}
CloseHandle(hProc);
}
Reminder: This is just a PoC to teach you the concept.

WriteProcessMemory in debugged process

I try to write simple debugger. For simplicity, assume the debugger runs under Windows XP.
At first I create new process as follows:
CreateProcess(processName,
NULL,
NULL,
NULL,
false,
DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,
NULL,
NULL,
&startInfo,
&openedProcessInfo);
And when I try to read or write something in memory of debugging process there are some problems. For example:
DWORD oldProtect;
if(!VirtualProtectEx(hProcess, breakpointAddr, 1, PAGE_EXECUTE_READWRITE, &oldProtect)) {
printf("Error: %d\n", GetLastError());
}
SIZE_T bytesRead = 0;
SIZE_T bytesWritten = 0;
BYTE instruction;
BOOL isOk = ReadProcessMemory(hProcess, breakpointAddr, &instruction, 1, &bytesRead);
BYTE originalByte = instruction;
instruction = 0xCC;
if(isOk && bytesRead == 1) {
isOk = WriteProcessMemory(hProcess, breakpointAddr, &instruction, 1, &bytesWritten);
if(isOk) {
isOk = FlushInstructionCache(hProcess, breakpointAddr, 1);
}
}
if(!isOk) {
printf("Error: %d\n", GetLastError());
}
It works, but not everywhere. It works when the address to which I want to write(read) something, is located within executable module (.exe).
But when I try to write(read) something within DLL library (for example, read at address of function VirtualAlloc) VirtualProtectEx returns false and GetLastError = 487 (Attempt to access invalid address) and ReadProcessMemory also returns false and GetLastError = 299 (Only part of a ReadProcessMemory or WriteProcessMemory request was completed.)
Debug privileges are enabled but it has no effect.
Your code looks fine, if you're running as administrator than the most likely cause of the problem is that breakpointAddr is an invalid address. VirtualProtectEx giving you the "Attempt to access invalid address" error supports this conclusion.

Win32 API to tell whether a given binary (EXE or DLL) is x86, x64, or ia64

I am trying to find a programmatic way to tell if a binary is x86, x64, or ia64.
Platform: Windows.
Language: c/c++.
Background: Before trying to load a third-party dll, I need to find out its bitness.
Appreciate any pointers.
For EXEs
use GetBinaryType(...)
Here is same question for manged exe.
For DLLs (and EXEs)
Use the ImageNtHeader(...) to get the PE data of the file and then check the IMAGE_FILE_HEADER.Machine field.
Here is some code I found using Google Code Search
No Cleanup and NO error checking
// map the file to our address space
// first, create a file mapping object
hMap = CreateFileMapping(
hFile,
NULL, // security attrs
PAGE_READONLY, // protection flags
0, // max size - high DWORD
0, // max size - low DWORD
NULL ); // mapping name - not used
// next, map the file to our address space
void* mapAddr = MapViewOfFileEx(
hMap, // mapping object
FILE_MAP_READ, // desired access
0, // loc to map - hi DWORD
0, // loc to map - lo DWORD
0, // #bytes to map - 0=all
NULL ); // suggested map addr
peHdr = ImageNtHeader( mapAddr );
I open-sourced a project on Github that checks for VC++ redistributable DLLs specifically and there's a code snippet I created based off of the function in Shay's answer that successfully finds, loads, and inspects DLLs for x86 / x64 compatibility.
Full code snippet below:
/******************************************************************
Function Name: CheckProductUsingCurrentDirectory
Description: Queries the current working directory for a given binary.
Inputs: pszProductFolderToCheck - the product name to look up.
pBinaryArchitecture - the desired processor architecture
of the binary (x86, x64, etc..).
Results: true if the requested product is installed
false otherwise
******************************************************************/
bool CheckProductUsingCurrentDirectory(const LPCTSTR pszProductBinaryToCheck, Architecture pBinaryArchitecture){
bool bFoundRequestedProduct = false;
//Get the length of the buffer first
TCHAR currentDirectory[MAX_PATH];
DWORD currentDirectoryChars = GetCurrentDirectory(MAX_PATH, currentDirectory);
//exit if couldn't get current directory
if (currentDirectoryChars <= 0) return bFoundRequestedProduct;
TCHAR searchPath[MAX_PATH];
//exit if we couldn't combine the path to the requested binary
if (PathCombine(searchPath, currentDirectory, pszProductBinaryToCheck) == NULL) return bFoundRequestedProduct;
WIN32_FIND_DATA FindFileData;
HANDLE hFind= FindFirstFile(searchPath, &FindFileData);
//exit if the binary was not found
if (hFind == INVALID_HANDLE_VALUE) return bFoundRequestedProduct;
HANDLE hFile = CreateFile(searchPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if (hFile == INVALID_HANDLE_VALUE) goto cleanup;
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, pszProductBinaryToCheck);
if (hMapping == INVALID_HANDLE_VALUE) goto cleanup;
LPVOID addrHeader = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
if (addrHeader == NULL) goto cleanup; //couldn't memory map the file
PIMAGE_NT_HEADERS peHdr = ImageNtHeader(addrHeader);
if (peHdr == NULL) goto cleanup; //couldn't read the header
//Found the binary, AND its architecture matches. Success!
if (peHdr->FileHeader.Machine == pBinaryArchitecture){
bFoundRequestedProduct = true;
}
cleanup: //release all of our handles
FindClose(hFind);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
if (hMapping != INVALID_HANDLE_VALUE)
CloseHandle(hMapping);
return bFoundRequestedProduct;
}
This question and Shay's answer were helpful to me while I was creating this, so I thought I'd post the project here.
You can check the PE header yourself to read the IMAGE_FILE_MACHINE field. Here's a C# implementation that shouldn't be too hard to adapt to C++.

Resources