0

My Windows driver use a PLOAD_IMAGE_NOTIFY_ROUTINE callback to get the image path for a specified process. I want to send this path (parameter FullImagePathName, which is a PUNICODE_STRING) to a usermode client, and display it. I think the first step would be to assign the copy the FullImagePathName to an other UNICODE_STRING which is available from my IOCTL function, then find a way to send it to the client. I already tried to play with some string copy functions with WCHAR and FullImagePath->Buffer, but no success. How can I proceed ?

void sSetNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) {


UNREFERENCED_PARAMETER(ImageInfo);
PEPROCESS process = NULL;
PUNICODE_STRING processName;
UNICODE_STRING lsass_prefix;
RtlInitUnicodeString(&lsass_prefix, L"\\Device\\HarddiskVolume4\\Windows\\System32\\lsass.exe");

PsLookupProcessByProcessId(ProcessId, &process);
SeLocateProcessImageName(process,&processName);

if (compare_punicode(processName, &lsass_prefix) == 0) {

    DbgPrint("Ok\n");
    DbgPrint("lsass launched %wZ\n", FullImageName);
    
}

//DbgPrint("%wZ (%d) loaded %wZ", processName, ProcessId, FullImageName);
 }

Here is my IOCTL function code :

 NTSTATUS
 DeviceControl(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp)
 {
      UNREFERENCED_PARAMETER(DeviceObject);

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status = STATUS_SUCCESS;
ULONG_PTR length = 0;

switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
case FIRST_DRIVER_IOCTL_TEST:

    if (lsass_string) {


        DbgPrint("[+] FIRST_DRIVER_IOCTL_TEST called\n");



        if (stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CHAR*))
        {
            status = STATUS_BUFFER_TOO_SMALL;
            DbgPrint("[!] STATUS_BUFFER_TOO_SMALL\n");
            break;
        }
        else {

            if (lsass_string == NULL)
            {
                status = STATUS_INVALID_PARAMETER;
                DbgPrint("[!] STATUS_INVALID_PARAMETER\n");
                break;
            }
            else {

                DbgPrint("sending to usermode\n");
                RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, lsass_string, strlen(Irp->AssociatedIrp.SystemBuffer));

            }

        }

    }
    break;

default:
    status = STATUS_INVALID_DEVICE_REQUEST;
    DbgPrint("[!] STATUS_INVALID_DEVICE_REQUEST\n");
    break;
}

Irp->IoStatus.Status = status;
Irp->IoStatus.Information = length;

IoCompleteRequest(Irp, IO_NO_INCREMENT);

return status;
}

Please note that "lsass_string" and "CHAR*" are for information only, and are not working code.

UVision
  • 11
  • 4
  • If you are running in a call from the client, then the client just needs to pass a buffer address that the kernel can write to. If the kernel wants to send something from an interrupt routine, then you would have to have previously set up a pinned buffer with something like MmProbeAndLockPages – stark Feb 12 '23 at 11:43
  • what type of data would be the most suitable to create a buffer that would be filled by the driver ? In my case, the logical consequence would be that the client code could easily read this buffer once it has received all the data. – UVision Feb 12 '23 at 13:35

0 Answers0