I am writing some kernel side code for Windows7 to access shared memory created in user mode, as suggested here.
The shared memory is created in user space with name:
"MySharedMem"
Opening the shared memory in user space works.
Opening the same shared memory in kernel mode calling ZwOpenSection fails returning:
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
The kernel code is:
NTSTATUS CModule1::OpenShared()
{
SIZE_T vs = 256;
WCHAR stringBuffer[] = L"\\BaseNamedObjects\\MySharedMem";
UNICODE_STRING sectionName;
RtlInitUnicodeString(§ionName,stringBuffer);
OBJECT_ATTRIBUTES myAttributes;
InitializeObjectAttributes(&myAttributes,§ionName,0,NULL,NULL);
NTSTATUS status0 = ZwOpenSection(§ionHandle_,SECTION_MAP_READ|SECTION_MAP_WRITE,&myAttributes);
NTSTATUS status = ZwMapViewOfSection(§ionHandle_, ZwCurrentProcess(), (PVOID *)&pSharedData_, 0, 0, NULL, &vs, ViewShare, 0, PAGE_READWRITE);
return status;
}
I tried several names (L"\\MySharedMem" or L"MySharedMem") but I got other errors as STATUS_OBJECT_PATH_INVALID or STATUS_OBJECT_PATH_NOT_FOUND. Also creating the shared memory as "Global\\MySharedMem" does not work.What am I doing wrong?
I tried to create the shared memory in kernel mode, I get success on ZwCreateSection and ZwMapViewOfSection but i get access violation when I access the pSharedData_ pointer to test the buffer:
NTSTATUS CModule1::MapUserSection()
{
typedef struct SHARED_SECTION {DWORD i; };
NTSTATUS status = STATUS_SUCCESS;
ULONG Attributes=OBJ_KERNEL_HANDLE | OBJ_FORCE_ACCESS_CHECK;
OBJECT_ATTRIBUTES objectAttributes;
LARGE_INTEGER MaxSize;
SIZE_T ViewSize=sizeof(SHARED_SECTION);
MaxSize.QuadPart=sizeof(SHARED_SECTION);
WCHAR stringBuffer[] = L"\\MySm2";
UNICODE_STRING sectionName;
RtlInitUnicodeString(§ionName,stringBuffer);
InitializeObjectAttributes(&objectAttributes,§ionName,Attributes,NULL,NULL);
status= ZwCreateSection(§ionHandle_,SECTION_ALL_ACCESS,&objectAttributes,&MaxSize,PAGE_READWRITE,SEC_COMMIT,NULL);
status = ZwMapViewOfSection(sectionHandle_, ZwCurrentProcess(), (PVOID *)&pSharedData_, 0, 0, NULL, &ViewSize, ViewShare, 0, PAGE_READWRITE);
//To test the buffer
RtlFillMemory(pSharedData_, '1',ViewSize);
return status;
}
Everything fails...
Concerning CreateFileMapping:
Creating a file mapping object in the global namespace from a session other than session zero requires the SeCreateGlobalPrivilege privilege.
From KB191840:
[T]he object is always mapped in the user address space (below 0x80000000) of a process (regardless of whether the object is created in kernel mode or user mode) the address is valid only if it is accessed in the context of the process.
The KB continues:
This method is not recommended and is used least by low-level device drivers because, as explained earlier, the scope of the address is limited to the process in which the object is mapped, and it cannot be accessed in a DPC or ISR. [Emphasis Mine]
The fix is either:
Create the file mapping in kernel mode. (Suggested by the KB article.)
Use IOCTL
Related
I've been trying to call a sample driver. I have written DriverEntry method, where I initialize both the driver name and symbolic ling pointing to the driver.
// UNICODE_STRING DriverName, SymbolName; // Driver registry paths
...
// Driver Entrypoint
NTSTATUS
DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {
Q_UNUSED(pRegistryPath);
DbgPrintEx(0, 0, "Driver Loaded\n");
// The PsSetLoadImageNotifyRoutine routine registers a driver-supplied
// callback that is subsequently notified whenever
// an image is loaded (or mapped into memory).
PsSetLoadImageNotifyRoutine(ImageLoadCallback);
// initialize driver name
RtlInitUnicodeString(&DriverName, L"\\Device\\Explorer");
// initialize symbolic link
RtlInitUnicodeString(&SymbolName, L"\\DosDevices\\Explorer");
IoCreateDevice(pDriverObject, 0, &SymbolName, FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);
IoCreateSymbolicLink(&DriverName, &SymbolName);
pDriverObject->MajorFunction[IRP_MJ_CREATE] = CreateCall;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = CloseCall;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControl;
pDriverObject->DriverUnload = UnloadDriver;
pDeviceObject->Flags |= DO_DIRECT_IO;
pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
When I load the driver up (using OSR Driver Loader, could be done using cmd also, by registering the driver as a new service), I get expected output in DebugView (sysinternals tool allowing to see kernel debug logs)
Now I needed to make sure that both the device and symlink are present in Windows Object Directories. To do that, I use WinObj (another tool from sysinternals), here is the output
What confuses me here, is that the symbolic link is in Device folder, instead of GLOBAL??.
Symbolic link in Device
Device in GLOBAL??
Now, finally, calling the driver itself. I use c++ for that purpose and this is my code,
class Test
{
public:
HANDLE hDriver; // Handle to driver
// Initializer
Test::Test(LPCSTR RegistryPath)
{
LPCSTR path = "\\\\.\\Explorer";
hDriver = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (hDriver == INVALID_HANDLE_VALUE)
{
// Handle the error.
char result = GetLastError();
bool zadek = false;
}
}
The problem is that I can't get a valid handle for the driver. The value of hDriver is always either 0x00000000000000a0 or 0xffffffff, no matter the path I use. I'm using createFileA because I want to access system memory.
Is there some blatant mistake I made?
I should say it is over 8-9 year since last time I written a device driver, but what comes off the top of my head are:
You say you get 0xa0 for hDriver which is a valid handle value.
Right now, you can only use device IO control, because you only have callback for IRP_MJ_DEVICE_CONTROL.
Try L"\\??\\Explorer" or L"\\GLOBAL??\\Explorer" for symbolic link.
You need to use DriverName for IoCreateDevice.
You are passing incorrect arguments to IoCreateSymbolicLink.
So your code should become like this:
...
// initialize driver name
RtlInitUnicodeString(&DriverName, L"\\Device\\Explorer");
// initialize symbolic link
RtlInitUnicodeString(&SymbolName, L"\\??\\Explorer");
IoCreateDevice(pDriverObject, 0, &DriverName, FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);
IoCreateSymbolicLink(&SymbolName, &DriverName);
...
I am trying to use CreateFileMapping for the first time and it is giving me this error when I use GetLastError():
ERROR_INVALID_HANDLE: The handle is invalid.
Here is my code:
// create the name of our file-mapping object
nTry++; // Ensures a unique string is used in case user closes and reopens
wsprintfA(szName, FS6IPC_MSGNAME1 ":%X:%X", GetCurrentProcessId(), nTry);
// stuff the name into a global atom
m_atom = GlobalAddAtomA(szName);
if (m_atom == 0)
{ *pdwResult = ERR_ATOM;
return FALSE;
}
// create the file-mapping object
m_hMap = CreateFileMappingA(
(HANDLE)0xFFFFFFFF, // use system paging file
NULL, // security
PAGE_READWRITE, // protection
0, MAX_SIZE+256, // size
szName); //
EDIT:
The first issue was resolved, but now my program crashes somewhere else.
#define FS6IPC_MESSAGE_SUCCESS 1
#define FS6IPC_MESSAGE_FAILURE 0
// IPC message types
#define FS6IPC_READSTATEDATA_ID 1
#define FS6IPC_WRITESTATEDATA_ID 2
// read request structure
typedef struct tagFS6IPC_READSTATEDATA_HDR
{
DWORD dwId; // FS6IPC_READSTATEDATA_ID
DWORD dwOffset; // state table offset
DWORD nBytes; // number of bytes of state data to read
void* pDest; // destination buffer for data (client use only)
} FS6IPC_READSTATEDATA_HDR;
// write request structure
typedef struct tagFS6IPC_WRITESTATEDATA_HDR
{
DWORD dwId; // FS6IPC_WRITESTATEDATA_ID
DWORD dwOffset; // state table offset
DWORD nBytes; // number of bytes of state data to write
} FS6IPC_WRITESTATEDATA_HDR;
while (*pdw)
{ switch (*pdw)
{ case FS6IPC_READSTATEDATA_ID:
pHdrR = (FS6IPC_READSTATEDATA_HDR *) pdw;
m_pNext += sizeof(FS6IPC_READSTATEDATA_HDR);
if (pHdrR->pDest && pHdrR->nBytes)
CopyMemory(pHdrR->pDest, m_pNext, pHdrR->nBytes);
m_pNext += pHdrR->nBytes; // Debugger says the issue is here
break;
case FS6IPC_WRITESTATEDATA_ID:
// This is a write, so there's no returned data to store
pHdrW = (FS6IPC_WRITESTATEDATA_HDR *) pdw;
m_pNext += sizeof(FS6IPC_WRITESTATEDATA_HDR) + pHdrW->nBytes;
break;
default:
// Error! So terminate the scan
*pdw = 0;
break;
}
pdw = (DWORD *) m_pNext;
}
I'm guessing you're running on a 64-bit system, on which HANDLEs are 64 bits. The OS is quite right—the handle value 0x00000000FFFFFFFF is an invalid handle value for your process.
What exactly are you trying to do? If you want to create a file mapping backed by an actual file, pass in the handle for that file. If you want to a create a file mapping backed by the paging file instead, pass in INVALID_HANDLE_VALUE. INVALID_HANDLE_VALUE happens to be (HANDLE)-1, which is 0xFFFFFFFF on 32-bit systems but 0xFFFFFFFFFFFFFFFF on 64-bit systems, but that doesn't really matter since you should just use the symbolic value INVALID_HANDLE_VALUE in any case.
If your application is crashing when you pass in INVALID_HANDLE_VALUE, it's not because the call to CreateFileMapping is failing, it's for some other reason, and you should debug that.
I have a program that allocates a buffer whose pointer is passed to a kernel driver through a custom IOCTL. In the driver I get an Mdl and lock the pages of the user-program buffer with "MmGetSystemAddressForMdlSafe" and then use the Mdl to fill the user program buffer.
If in the user program the buffer was a normal array, the driver always works as it should.
(WORD buffer[256], where word is an unsigned short)
If the user program buffer was instead allocated with the new keyword (WORD *buffer = new WORD[256]) or the malloc keyword (WORD *buffer=(WORD*) malloc(sizeof(*buffer)*256))) from time to time I get a BSOD and the error is "page fault in non paged area".
WHY?
Thanks!
EDIT(additional details):
In the driver I use MmGetSystemAddressForMdlSafe this way:
PVOID p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
Irp is a PIRP that I receive as the second parameter when I handle the IRP_MJ_DEVICE_CONTROL MajorFunction.
After I've checked that p_buffer is not null, I use that pointer to write user buffer:
READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256)
IOCTL definition:
#define IOCTL_TEST_READPORT CTL_CODE(FILE_DEVICE_TEST, \
TEST_IOCTL_INDEX + 0, \
METHOD_OUT_DIRECT, \
FILE_ANY_ACCESS)
Driver function that handles IRP_MJ_DEVICE_CONTROL:
NTSTATUS TESTDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IrpStack;
ULONG input_buffer_size;
ULONG output_buffer_size;
ULONG control_code;
PVOID p_buffer;
NTSTATUS nt_status;
struct port current_port;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
switch (IrpStack->MajorFunction)
{
case IRP_MJ_DEVICE_CONTROL:
control_code = IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (control_code)
{
case IOCTL_TEST_READPORT:
p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
input_buffer_size = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
if (!p_buffer)
{
nt_status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (input_buffer_size)
{
memcpy (¤t_port, Irp->AssociatedIrp.SystemBuffer, input_buffer_size);
switch (current_port.size)
{
case 1:
current_port.value = (ULONG)READ_PORT_UCHAR((PUCHAR)(USHORT)current_port.address);
memcpy (p_buffer, ¤t_port.value, sizeof(current_port.value));
Irp->IoStatus.Information = sizeof(current_port.value);
break;
case 0xF0:
READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256);
Irp->IoStatus.Information = sizeof(current_port.value);
break;
case 2:
current_port.value = (ULONG)READ_PORT_USHORT((PUSHORT)(USHORT)current_port.address);
memcpy (p_buffer, ¤t_port.value, sizeof(current_port.value));
Irp->IoStatus.Information = sizeof(current_port.value);
break;
}
}
else
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
case IRP_MJ_CREATE:
KdPrint(("IRP_MJ_CREATE"));
break;
case IRP_MJ_CLOSE:
KdPrint(("IRP_MJ_CLOSE"));
break;
default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
break;
}
nt_status = Irp->IoStatus.Status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return nt_status;
}
The relevant case is case 0xF0: inside case IOCTL_TEST_READPORT:
From my understanding you are misunderstanding the purpose of MmGetSystemAddressForMdlSafe. Per this document here, you use this function to get virtual addresses that are described by a MDL (Memory Descriptor List).
. If the driver must use virtual addresses to access the pages described by the MDL, it must map those pages into the system address space using MmGetSystemAddressForMdlSafe
that same document also says this:
To use virtual addresses to access the buffer described by the MDL, the driver calls MmGetSystemAddressForMdlSafe to map the buffer into system space.
MmGetSystemAddressForMdlSafe:
Maps the physical pages described by an MDL into system space and returns a virtual address for the MDL. The returned virtual address can be used at any IRQL and in any process context.
If you look at the MSDN documentation of MmGetSystemAddressForMdlSafe then you will see this following line:
The MmGetSystemAddressForMdlSafe macro returns a nonpaged system-space virtual address for the buffer that the specified MDL describes.
It says that this functions returns a non-paged virtual address for a buffer that is described by an MDL.
The definition of MDL is the following:
A memory descriptor list (MDL) describes a list of pages in physical memory.
This is a description of pages in physical memory, not virtual memory. Your buffer allocated by new will have virtual addresses already, trying to use MmGetSystemAddressForMdlSafe on it, is wrong. You should use that function to get a virtual address from an MDL, not a MDL from a virtual address range.
Now, moving on to an explanation for the page fault in non-paged area:
Now if you think about it, it is likely that your buffer allocated by new or malloc is already in a paged memory area (in fact, seeing that it's in user land, it's extremely likely), meaning that trying to get a virtual address to this buffer (which is already wrong because it's not an MDL), will cause a page fault in non-paged area, because the memory of the buffer is in a paged area, whereas you're mapping it to a non-paged area in the kernel, and non-paged areas can not cause a page fault. (most likely will have to do with wrong IRQL levels)
I'm struggling with some debug asserts when my test program exits. The purpose is to get the device path to a disk drive through using the Setup API. This I'm doing. I'm following the rule described here for SetupDiGetDeviceInterfaceDetail i.e. calling SetupDiGetDeviceInterfaceDetail() to determine the size needed for the structure, and allocating memory for the structure and calling SetupDiGetDeviceInterfaceDetail() again.
This process works and I'm getting the data that I need. What is not working correctly is, when the program exits, or I delete the memory directly, I get a debug assertion. The assert window has the file where the problem was found, dbgdel.cpp, on line 52. The problem: "Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)".
I'm not sure what the problem is. If I remove all of the code I'm using and new up an SP_DEVICE_INTERFACE_DETAIL_DATA object without all of the Setup API calls, the call to delete works. Basically, here's what I'm doing:
HDEVINFO hDevs = SetupDiGetClassDevs(&DiskClassGuid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
SP_DEVICE_INTERFACE_DATA devInterfaceData = {sizeof(SP_DEVICE_INTERFACE_DATA)};
DWORD size(0);
SetupDiEnumDeviceInterfaces(hDevs, NULL, &DiskClassGuid, 0, &devInterfaceData);
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetails(NULL);
SetupDiGetDeviceInterfaceDetail(hDevs, &devInterfaceData, pDetails, 0, &size, NULL);
pDetails = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(new BYTE[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + size]);
// zero allocated memory
pDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(hDevs, &devInterfaceData, pDetails, size, NULL, NULL);
delete[] pDetails;
When the program exits, or the delete[] is called, the assert that mentioned earlier shows up. Please explain to me what I'm doing wrong.
Thanks,
Andy
UPDATE:
Forgot to add the definition of SP_DEVICE_INTERFACE_DETAIL_DATA. That can be found here. However, it looks like this:
typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
DWORD cbSize;
TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;
I have a driver code which works good on 32 bit. On 64 bit i compiled it and also digitally signed it. The driver loads but fails to work properly. The main functionality of the driver to to register process creation and termination to my program in call back. So i have two IOCTL working. The code is as follows..
NTSTATUS DispatchIoctl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
PPROCESS_CALLBACK_INFO pProcCallbackInfo;
//
// These IOCTL handlers are the set and get interfaces between
// the driver and the user mode app
//
switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_PROCOBSRV_ACTIVATE_MONITORING:
{
ntStatus = ActivateMonitoringHanlder( Irp );
break;
}
case IOCTL_PROCOBSRV_GET_PROCINFO:
{
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(PROCESS_CALLBACK_INFO))
{
pProcCallbackInfo = Irp->AssociatedIrp.SystemBuffer;
pProcCallbackInfo->hParentId = extension->hParentId;
pProcCallbackInfo->hProcessId = extension->hProcessId;
pProcCallbackInfo->bCreate = extension->bCreate;
ntStatus = STATUS_SUCCESS;
}
break;
}
default:
break;
}
Irp->IoStatus.Status = ntStatus;
//
// Set number of bytes to copy back to user-mode
//
if(ntStatus == STATUS_SUCCESS)
Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
else
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
when i call
bReturnCode = ::DeviceIoControl(
hDriverFile,
IOCTL_PROCOBSRV_ACTIVATE_MONITORING,
&activateInfo,
sizeof(activateInfo),
NULL,
0,
&dwBytesReturned,
NULL
);
The code succeeds but when i call
bReturnCode = ::DeviceIoControl(
m_hDriverFile,
IOCTL_PROCOBSRV_GET_PROCINFO,
0,
0,
&callbackInfo, sizeof(callbackInfo),
&dwBytesReturned,
&ov
);
getLastError returns 31. Can anyone help me with this. Is it a problem of IOCTL structure with 64 bit? Please help me find a solution to this. Thanks..
If you don't have driver debugging experience, try to diagnose this problem using trace. Add KdPrint lines to your code in all places where it is necessary, for example:
case IOCTL_PROCOBSRV_GET_PROCINFO:
{
KdPrint(("IOCTL_PROCOBSRV_GET_PROCINFO. OutputBufferLength = %d\n", irpStack->Parameters.DeviceIoControl.OutputBufferLength));
KdPrint(("sizeof(PROCESS_CALLBACK_INFO) = %d\n", sizeof(PROCESS_CALLBACK_INFO)));
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(PROCESS_CALLBACK_INFO))
{
pProcCallbackInfo = Irp->AssociatedIrp.SystemBuffer;
pProcCallbackInfo->hParentId = extension->hParentId;
pProcCallbackInfo->hProcessId = extension->hProcessId;
pProcCallbackInfo->bCreate = extension->bCreate;
KdPrint(("IOCTL_PROCOBSRV_GET_PROCINFO. STATUS_SUCCESS\n"));
ntStatus = STATUS_SUCCESS;
}
break;
}
This is just a sample, add KdPrint lines to all places to understand what happens. Build the driver in checked configuration, install it and run your program. See KdPrint output in DbgView program, with Caprure Kernel option enabled.
You can use DbgPrint instead of KdPrint, in this case it will work also in free driver configuration.
Edit:
How is PROCESS_CALLBACK_INFO defined? What is client code that calls the driver? Is client compiled as 32 or 64 bit? Passing a structure between client and driver, ensure that it doesn't contain bitness-dependent fields (has the same size when compiled both in 32 and 64 bit), and structure padding is the same.
Not too much information here, but you could always check that the size of your own defined structures are the same in the compiled user-mode client and the driver when running 64-bit. There might be packing/alignment issues that can be fixed by using #pragma pack (or whatever your compiler supports) for your structs.
As a general rule also try to set the status code to a more specific value on detected problems, such as e.g. STATUS_BUFFER_TOO_SMALL if you detect that a passed buffer is too small. I suspect that this isn't the actual issue here when using the IOCTL_PROCOBSRV_GET_PROCINFO IOCTL as you are getting back Win32 error 31, but it helps clients to troubleshoot their problems in general.
Update: As the differences actually seem to mismatch judging from your comments, try to surround the struct definitions with packing and then make sure to recompile both the client and driver. Example if you are using the Visual C++ compiler:
#pragma pack(push, 8) // Save current packing and set to 8-byte
typedef struct _PROCESS_CALLBACK_INFO
{
// Whatever ...
} PROCESS_CALLBACK_INFO;
#pragma pack(pop) // Restore previous packing
I found the answer. Thanks to the debugging. As mention earlier the outputBufferLength was less than the structure length due to which the driver was failing.
The outputBufferLength depands on the size of the struct you pass while calling the following function..
bReturnCode = ::DeviceIoControl(
m_hDriverFile,
IOCTL_PROCOBSRV_GET_PROCINFO,
0,
0,
&callbackInfo, sizeof(callbackInfo),
&dwBytesReturned,
&ov
);
So callbackInfo size was 12 so the outputBufferLength = 12. The structure callbackInfo has DWORD datamember which for 64 bit had to be DWORD64. When i changed the datatype of the member in the structure and then called the DeviceIOControl function , the driver worked great and the outputBufferLength was = 24 as expected. Thanks for all your help.