How send a struct from usermode to kernel mode?


Keywords:c++ 


Question: 

I want send a struct that contains a pid and a string that is correspondent to a directory. The pid are of 4 differents process then this will be sent 4x (since that pid > 0). I received all pid's with sucess on kernel mode, but directory comes wrong and with strange caracters. Also when the struct is sent on 2nd time this cause a BSOD in RtlInitUnicodeString().

Then i want know how fix this?

usermode:

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <Windows.h>
#include <tlhelp32.h>

struct data {
    DWORD pid;
    const char* path;
} mydata;

DWORD GetPID(const char* pname)
{

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(snapshot, &entry) == TRUE)
    {
        while (Process32Next(snapshot, &entry) == TRUE)
        {
            if (stricmp(entry.szExeFile, pname) == 0)
            {
                return entry.th32ProcessID;
            }
        }
    }

    CloseHandle(snapshot);

    return 0;

}

#define IOCTL_PID CTL_CODE(FILE_DEVICE_UNKNOWN,0x902,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)

int _tmain(int argc, _TCHAR* argv[])
{
    const char *names[4] = { "Proc01.exe", "Proc02.exe", "Proc03.exe", "Proc04.exe" };
    int i = 0;
    HANDLE hFile;
    DWORD dw, pid;
    BOOL bResult;

    while (i < sizeof(names) / sizeof(const char*)) {

        pid = GetPID(names[i]);

        if (pid > 0) {

            printf("\nApplication Pid: %d\n", pid);

            hFile = CreateFile("\\\\.\\test", GENERIC_ALL, 0, NULL, OPEN_EXISTING, 0, NULL);

            if (hFile == INVALID_HANDLE_VALUE)
            {
                printf("\nError: Unable to open the IOCTL Test driver. (%d)\n", GetLastError());
                //  return 1;
            }

            mydata.pid = pid;
            mydata.path = "\\??\\C:\\MyDirectory";

            bResult = DeviceIoControl(hFile, IOCTL_PID, &mydata, sizeof(mydata), NULL, 0, (LPDWORD)&mydata, NULL);

            if (!bResult)
            {
                printf("\nError: %d\n", GetLastError());

                CloseHandle(hFile);
                //  return 1;
            }
            else
                printf("\nSuccessfully sent control code to driver.\n");

            CloseHandle(hFile);
        }

        i++;
        Sleep(7000);
    }

    getch();

    return 0;
}

kernel mode:

#define IOCTL_PID CTL_CODE(FILE_DEVICE_UNKNOWN,0x902,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)

typedef struct MyData
{
    HANDLE pid;
    PCWSTR path;
};

NTSTATUS testPnP(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp)
{
    MyData *pData = (MyData*)Irp->AssociatedIrp.SystemBuffer;

    UNICODE_STRING MyPath;

    switch (irpSp->Parameters.DeviceIoControl.IoControlCode)
    {

    case IOCTL_PID:

        DbgPrint("Application PID: %d\n", pData->pid);

        RtlInitUnicodeString(&MyPath, pData->path);
        DbgPrint("UserMode string: %wZ\n", &MyPath);

        InitializeObjectAttributes(&obj,&MyPath,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,NULL,NULL);

        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp,IO_NO_INCREMENT);

        return STATUS_SUCCESS;

    default:
        DbgPrint("Unknown IOCTL code: %#x\n",irpSp->Parameters.DeviceIoControl.IoControlCode);

        Irp->IoStatus.Status=STATUS_INVALID_DEVICE_REQUEST;
        Irp->IoStatus.Information=0;

        IoCompleteRequest(Irp,IO_NO_INCREMENT);
        return STATUS_INVALID_DEVICE_REQUEST;

    }

    Irp->IoStatus.Status=STATUS_SUCCESS;
    Irp->IoStatus.Information=0;

    IoCompleteRequest(Irp,IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

EDITION:

I forgot of post the type of data for variable obj.

Then is here: OBJECT_ATTRIBUTES obj;


EDITION 2:

After the suggestion of struct to user mode, output still is a strange character "?".

/* UserMode */

struct data {
    DWORD pid;
    WCHAR path[200];
} mydata;

//...

mydata.path[0] = (WCHAR) L"\\??\\C:\\MyDirectory";


/* KernelMode */

typedef struct MyData
{
    HANDLE pid;
    WCHAR path[200];
};

//...

RtlInitUnicodeString(&MyPath, &pData->path[0]);
DbgPrint("Usermode string: %wZ\n", &MyPath);

1 Answer: 

The struct needs to be the same in both user and kernel mode. In user-mode you have a char* narrow string but in your driver you are treating it as a WCHAR* and when you do that the string tends to look Chinese. Process IDs are 32-bit in Win32 and pointer sized in the kernel and assuming you want to be able to use your driver on 64-bit systems you should probably just use UINT64 everywhere.

Your struct should look like this:

typedef struct {
  UINT64 pid;
  WCHAR path[ANYSIZE_ARRAY];
} mydata;

Allocate the struct in user-mode and pass the correct size in bytes to DeviceIoControl.

I would also recommend that you start out with METHOD_BUFFERED even though your current example does not return data from the driver.

If you are just starting out writing drivers I would recommend that you read the OSR Online articles starting with this one. Their mailing list is also useful.