Process Interception With Inverted Call Feedback

Hi There,

After buying all the windows development driver dev books, reading 30 pages of google search results I am completely lost. So posting this is my last option before I just off shore this task to India for a couple of grand. You guys are my last hope.

I am trying (and have been for 3+ weeks) to make a kernel driver capable of intercepting process executions, sending the file details to a windows service, getting a response that says whether they can be executed or not. (Typical Anti-Virus behaviour).

The code I have so far is capable of intercepting processes and printing their details to debug. It is also capable of accepting an IOCTL call “Hello kerneland” message and returning a “hello from kernal land” message to userland.

My next step requires using the Inverted Call model to send the process details to userland so I can get the response. I have read the OSR article 5-6 times now and I understand completely how this works in theory, but the example is using WDF and my code isn’t, so I don’t understand where anything fits (C isn’t my language of choice, im a Python dev that has inherited this project). My Windows userland service is written in VB.net (I am fairly comfortable in VB.net) and I can get information in and out using IOCTL calls from userland, this works fine. Can someone point me in the right direction?

Here is my code currently:

// Known Working - Read + Write - Slight bug with the write printing to much

#include <ntddk.h>
#include <stdio.h>
#define IOCTL_HELLO_WORLD CTL_CODE( FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)

PDEVICE_OBJECT pDeviceObject;
UNICODE_STRING dev,dos;

NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

VOID UnloadRoutine(
IN PDRIVER_OBJECT DriverObject
);

VOID CreateProcessNotifyEx(
inout PEPROCESS Process,
in HANDLE ProcessId,
__in_opt PPS_CREATE_NOTIFY_INFO CreateInfo
);

VOID CreateProcessNotifyEx(
__inout PEPROCESS Process,
in HANDLE ProcessId,
in_opt PPS_CREATE_NOTIFY_INFO CreateInfo

)
{
if (CreateInfo)
{
if(CreateInfo->FileOpenNameAvailable==TRUE)
{
DbgPrintEx(
DPFLTR_IHVDRIVER_ID,
DPFLTR_INFO_LEVEL,
“PID : 0x%X (%d) ImageName :%wZ CmdLine : %wZ \n”,
ProcessId,ProcessId,
CreateInfo->ImageFileName,
CreateInfo->CommandLine
);
}

//CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
CreateInfo->CreationStatus = STATUS_SUCCESS;
}

}

// IRP CREATE ROUTINE - Referenced In DriverEntry
NTSTATUS Create(PDEVICE_OBJECT DeviceObject,PIRP irp)
{
DbgPrint(“Create routine called.\n”);

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

IoCompleteRequest(irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IRP CLOSE ROUTINE - Referenced In DriverEntry
NTSTATUS Close(PDEVICE_OBJECT DeviceObject,PIRP irp)
{
DbgPrint(“Close routine called.\n”);

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

IoCompleteRequest(irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IOCTL - Handle I/O - Reference in DriverEntry
NTSTATUS IOCTL(PDEVICE_OBJECT DeviceObject,PIRP irp)
{
char local_buf[200];
int message_length;
PIO_STACK_LOCATION pIoStackLocation;

PVOID pBuf = irp->AssociatedIrp.SystemBuffer;
PCHAR welcome = “Hello from kerneland.”;

pIoStackLocation = IoGetCurrentIrpStackLocation(irp);

switch(pIoStackLocation->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_HELLO_WORLD:

message_length = _snprintf(local_buf,200,“%s”,pBuf);

DbgPrint(“IOCTL HELLOS.”);

DbgPrint(“Message received : %s”, pBuf);

//34
DbgPrint(“Input Message Length: %d”,message_length);

//30
DbgPrint(“Input Buffer Length: %d”,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);

RtlZeroMemory(pBuf,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);

RtlCopyMemory(pBuf, welcome, strlen(welcome) );

break;
}

// Finish the I/O operation by simply completing the packet and returning
// the same status as in the packet itself.
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = strlen(welcome);
IoCompleteRequest(irp,IO_NO_INCREMENT);

return STATUS_SUCCESS;
}

VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX) CreateProcessNotifyEx, TRUE);
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Was Unloaded\n”);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{

NTSTATUS status;

DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Entry\n”);

// Define Device
RtlInitUnicodeString(&dev,L"\Device\Driver_Test");
RtlInitUnicodeString(&dos,L"\DosDevices\Driver_Test");

// Create Devce
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating Device\n”);
IoCreateDevice(DriverObject,0,&dev,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObject);

// Sym Link Device
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating Sym Link\n”);
IoCreateSymbolicLink(&dos,&dev);

// Set Interface State
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Interface State\n”);
IoSetDeviceInterfaceState(RegistryPath, TRUE);

// Set A Create Routine
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Create Callback\n”);
DriverObject->MajorFunction[IRP_MJ_CREATE]=Create;

// Set control routine
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting IOCTL Callback\n”);
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=IOCTL;

// Set close routine
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Close Callback\n”);
DriverObject->MajorFunction[IRP_MJ_CLOSE]=Close;

// Set an unload routine for when the driver exits
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Unload Callback\n”);
DriverObject->DriverUnload = UnloadRoutine;

// Set Flags
pDeviceObject->Flags|=DO_DIRECT_IO;
pDeviceObject->Flags&=~DO_DEVICE_INITIALIZING;

// Hook process creation
status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)CreateProcessNotifyEx, FALSE);
if(!NT_SUCCESS(status))
{
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,“Failed to PsSetCreateProcessNotifyRoutineEx .status : 0x%X \n”,status);
}

// Print started
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Has Started\n”);

return STATUS_SUCCESS;

}

Thanks for your time.</stdio.h></ntddk.h>

IMO, it’s a little unfair asking a Python Dev to take on a driver project like this - there’s a huge difference between userland and kernel programming. If you really want to do this properly, get comfortable with C and then go on an OSR driver course (I’ll collect my commission later, Peter :-).

However, based on

http://www.osronline.com/article.cfm?id=94

(which is WDM based, not KMDF) your next step is to understand how your currently synchronous IOCTL() request can be split into two separate operations.

In your use case, there are two independent things happening: processes are being created (so your notify callback is being called), and your userland process wants to read some data by calling DeviceIoControl(handle, IOCTL_HELLO_WORLD, …).

Your driver has to reconcile these independent events by suitable queuing. If you get a notify callback when there is no read, you have to queue the notification (possibly holding up the process creation) until there is a read to inform userland about it.

If you get a read when there is no process notification queued, you will need to block the read until there is one. The right way to do this is to return STATUS_PENDING (the I/O manager takes care of the blocking) on the read (IOCTL).

You will also need to consider what happens if multiple IOCTLs are issued …

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-594240-
xxxxx@lists.osr.com] On Behalf Of xxxxx@zepko.com
Sent: 27 October 2015 11:40
To: Windows System Software Devs Interest List
Subject: [ntdev] Process Interception With Inverted Call Feedback

Hi There,

After buying all the windows development driver dev books, reading 30
pages of google search results I am completely lost. So posting this is
my last option before I just off shore this task to India for a couple
of grand. You guys are my last hope.

I am trying (and have been for 3+ weeks) to make a kernel driver
capable of intercepting process executions, sending the file details to
a windows service, getting a response that says whether they can be
executed or not. (Typical Anti-Virus behaviour).

The code I have so far is capable of intercepting processes and
printing their details to debug. It is also capable of accepting an
IOCTL call “Hello kerneland” message and returning a “hello from kernal
land” message to userland.

My next step requires using the Inverted Call model to send the process
details to userland so I can get the response. I have read the OSR
article 5-6 times now and I understand completely how this works in
theory, but the example is using WDF and my code isn’t, so I don’t
understand where anything fits (C isn’t my language of choice, im a
Python dev that has inherited this project). My Windows userland
service is written in VB.net (I am fairly comfortable in VB.net) and I
can get information in and out using IOCTL calls from userland, this
works fine. Can someone point me in the right direction?

Here is my code currently:

// Known Working - Read + Write - Slight bug with the write printing to
much

#include <ntddk.h>
> #include <stdio.h>
> #define IOCTL_HELLO_WORLD CTL_CODE( FILE_DEVICE_UNKNOWN, 0x900,
> METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)
>
>
> PDEVICE_OBJECT pDeviceObject;
> UNICODE_STRING dev,dos;
>
> NTSTATUS DriverEntry(
> IN PDRIVER_OBJECT DriverObject,
> IN PUNICODE_STRING RegistryPath
> );
>
> VOID UnloadRoutine(
> IN PDRIVER_OBJECT DriverObject
> );
>
> VOID CreateProcessNotifyEx(
> inout PEPROCESS Process,
>
in HANDLE ProcessId,
> __in_opt PPS_CREATE_NOTIFY_INFO CreateInfo );
>
>
> VOID CreateProcessNotifyEx(
>__inout PEPROCESS Process,
> in HANDLE ProcessId,
>
in_opt PPS_CREATE_NOTIFY_INFO CreateInfo
>
> )
> {
> if (CreateInfo)
> {
> if(CreateInfo->FileOpenNameAvailable==TRUE)
> {
> DbgPrintEx(
> DPFLTR_IHVDRIVER_ID,
> DPFLTR_INFO_LEVEL,
> “PID : 0x%X (%d) ImageName :%wZ CmdLine : %wZ \n”,
> ProcessId,ProcessId,
> CreateInfo->ImageFileName,
> CreateInfo->CommandLine
> );
> }
>
>
> //CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
> CreateInfo->CreationStatus = STATUS_SUCCESS;
> }
>
> }
>
> // IRP CREATE ROUTINE - Referenced In DriverEntry NTSTATUS
> Create(PDEVICE_OBJECT DeviceObject,PIRP irp) {
> DbgPrint(“Create routine called.\n”);
>
> irp->IoStatus.Status=STATUS_SUCCESS;
> irp->IoStatus.Information=0;
>
> IoCompleteRequest(irp,IO_NO_INCREMENT);
> return STATUS_SUCCESS;
> }
>
> // IRP CLOSE ROUTINE - Referenced In DriverEntry NTSTATUS
> Close(PDEVICE_OBJECT DeviceObject,PIRP irp) {
> DbgPrint(“Close routine called.\n”);
>
> irp->IoStatus.Status=STATUS_SUCCESS;
> irp->IoStatus.Information=0;
>
> IoCompleteRequest(irp,IO_NO_INCREMENT);
> return STATUS_SUCCESS;
> }
>
> // IOCTL - Handle I/O - Reference in DriverEntry NTSTATUS
> IOCTL(PDEVICE_OBJECT DeviceObject,PIRP irp) {
> char local_buf[200];
> int message_length;
> PIO_STACK_LOCATION pIoStackLocation;
>
>
> PVOID pBuf = irp->AssociatedIrp.SystemBuffer;
> PCHAR welcome = “Hello from kerneland.”;
>
> pIoStackLocation = IoGetCurrentIrpStackLocation(irp);
>
>
> switch(pIoStackLocation->Parameters.DeviceIoControl.IoControlCode)
> {
> case IOCTL_HELLO_WORLD:
>
> message_length = _snprintf(local_buf,200,“%s”,pBuf);
>
> DbgPrint(“IOCTL HELLOS.”);
>
> DbgPrint(“Message received : %s”, pBuf);
>
> //34
> DbgPrint(“Input Message Length: %d”,message_length);
>
> //30
> DbgPrint(“Input Buffer Length: %d”,pIoStackLocation-
> >Parameters.DeviceIoControl.InputBufferLength);
>
> RtlZeroMemory(pBuf,pIoStackLocation-
> >Parameters.DeviceIoControl.InputBufferLength);
>
> RtlCopyMemory(pBuf, welcome, strlen(welcome) );
>
>
> break;
> }
>
> // Finish the I/O operation by simply completing the packet and
> returning
> // the same status as in the packet itself.
> irp->IoStatus.Status = STATUS_SUCCESS;
> irp->IoStatus.Information = strlen(welcome);
> IoCompleteRequest(irp,IO_NO_INCREMENT);
>
> return STATUS_SUCCESS;
> }
>
>
> VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject) {
>
> PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)
> CreateProcessNotifyEx, TRUE);
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Was
> Unloaded\n”); }
>
> NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN
> PUNICODE_STRING RegistryPath) {
>
> NTSTATUS status;
>
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver
> Entry\n”);
>
> // Define Device
> RtlInitUnicodeString(&dev,L"\Device\Driver_Test");
> RtlInitUnicodeString(&dos,L"\DosDevices\Driver_Test");
>
> // Create Devce
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating
> Device\n”);
>
> IoCreateDevice(DriverObject,0,&dev,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECU
> RE_OPEN,FALSE,&pDeviceObject);
>
> // Sym Link Device
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating Sym
> Link\n”);
> IoCreateSymbolicLink(&dos,&dev);
>
> // Set Interface State
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting
> Interface State\n”);
> IoSetDeviceInterfaceState(RegistryPath, TRUE);
>
> // Set A Create Routine
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Create
> Callback\n”);
> DriverObject->MajorFunction[IRP_MJ_CREATE]=Create;
>
> // Set control routine
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting IOCTL
> Callback\n”);
> DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=IOCTL;
>
> // Set close routine
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Close
> Callback\n”);
> DriverObject->MajorFunction[IRP_MJ_CLOSE]=Close;
>
> // Set an unload routine for when the driver exits
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Unload
> Callback\n”);
> DriverObject->DriverUnload = UnloadRoutine;
>
> // Set Flags
> pDeviceObject->Flags|=DO_DIRECT_IO;
> pDeviceObject->Flags&=~DO_DEVICE_INITIALIZING;
>
> // Hook process creation
> status =
> PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)Cr
> eateProcessNotifyEx, FALSE);
> if(!NT_SUCCESS(status))
> {
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,“Failed to
> PsSetCreateProcessNotifyRoutineEx .status : 0x%X \n”,status);
> }
>
> // Print started
> DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Has
> Started\n”);
>
> return STATUS_SUCCESS;
>
> }
>
> Thanks for your time.
This email message has been delivered safely and archived online by Mimecast.

For more information please visit http://www.mimecast.com</stdio.h></ntddk.h>

xxxxx@zepko.com wrote:

I am trying (and have been for 3+ weeks) to make a kernel driver capable of intercepting process executions, sending the file details to a windows service, getting a response that says whether they can be executed or not. (Typical Anti-Virus behaviour).

The code I have so far is capable of intercepting processes and printing their details to debug. It is also capable of accepting an IOCTL call “Hello kerneland” message and returning a “hello from kernal land” message to userland.

My next step requires using the Inverted Call model to send the process details to userland so I can get the response. I have read the OSR article 5-6 times now and I understand completely how this works in theory, but the example is using WDF and my code isn’t,…

It should be. Seriously.

…so I don’t understand where anything fits (C isn’t my language of choice, im a Python dev that has inherited this project). My Windows userland service is written in VB.net (I am fairly comfortable in VB.net) and I can get information in and out using IOCTL calls from userland, this works fine. Can someone point me in the right direction?

The KEY difference between the ioctl code you have and an “inverted
call” ioctl is that you don’t complete the request right away. You
store the IRP in a queue in the driver, then call IoMarkIrpPending and
return STATUS_PENDING. The user-mode app will still be blocked, waiting
for a response (unless the app is using overlapped I/O). Later, when
you have something you need to send back, you pop the top pending
request from your queue and complete it. The app then continues to run,
and when it has done it’s thing, it calls DeviceIoControl to submit a
new request and waits.

Later, you’ll have to think about what happens if the driver needs to
return two things in quick succession, but the app hasn’t sent a new
empty request yet. That is commonly handled by having the app queue up
two or three empty requests. That, of course, requires overlapped I/O.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

You THINK!? That’s the understatement of the century.

You know… we SPECIFICALLY teach the Internals and Kernel Mode Drivers seminar to teach you how to do this stuff.

I disagree. There’s no reason on to complicate the simplicity of a simple software-only capture driver with WDF-ism.

So…

Mr. Boyce and Mr. Roberts have provided you some very good starting hints. Just, be careful of that old NT Insider article that Mr. Boyce cited. There are some nasty issues in it.

As Mr. Roberts said, you get the IOCTL from your app and you QUEUE it… you don’t complete it. Have your app send SEVERAL IOCTLs at once, each providing a discrete OutBuffer. As CreateProcess data arrives that you want to log, you store that data (I’ll talk more, below, about where you might store it… just go along with me for now). When you have “enough” data received of it, you attempt to remove an IOCTL from the queue of IOCTLs that your driver has received, and you complete it, thereby returning the data you’ve collected so far to your app.

The type of queue to use is called a CancelSafeQueue… see here:
https: or Google “Cancel-safe IRP Queues MSDN”

So, where do you collect the data. This depends on how “clever” you want to be. The easiest alternative is to collect the data in a buffer that you allocate in the driver, and when the buffer starts to get full, you dequeue a pending IRP, copy the data from your buffer to the IRP’s OutBuffer (Irp->AssociatedIrp.SystemBuffer will have the pointer to the OutBuffer), and then complete the Irp.

Alternatively, you could use a buffer from one of the pending IRPs directly.

In all cases, you DO have the synchronization issues to deal with that Mr. Roberts mentions.

Peter
OSR
@OSRDrivers</https:>

Hi all,

Thanks for the responses, I have spent a day getting my head around the documentation and trying to understand C, currently if I uncomment the three lines in DriverEntry it bluescreens instantly on start. The rest was taken from a copy of someone who looked like they had this working seen as there isn’t a single coding tutorial on this anywhere that I can find. Can someone take a look at this code and tell me where I am going wrong?

___________ Cancel.h_________________________
#ifndef __CANCEL_H
#define __CANCEL_H

#include <initguid.h>

//
// Since this driver is a legacy driver and gets installed as a service
// (without an INF file), we will define a class guid for use in
// IoCreateDeviceSecure function. This would allow the system to store
// Security, DeviceType, Characteristics and Exclusivity information of the
// deviceobject in the registery under
// HKLM\SYSTEM\CurrentControlSet\Control\Class\ClassGUID\Properties.
// This information can be overrided by an Administrators giving them the ability
// to control access to the device beyond what is initially allowed
// by the driver developer.
//

// {5D006E1A-2631-466c-B8A0-32FD498E4424} - generated using guidgen.exe
DEFINE_GUID (GUID_DEVCLASS_CANCEL_SAMPLE,
0x5d006e1a, 0x2631, 0x466c, 0xb8, 0xa0, 0x32, 0xfd, 0x49, 0x8e, 0x44, 0x24);

//
// GUID definition are required to be outside of header inclusion pragma to
// avoid error during precompiled headers.
//
#include <ntddk.h>
#include <wdmsec.h> // for IoCreateDeviceSecure
//#include <dontuse.h>

typedef struct _INPUT_DATA{

ULONG Data; //device data is stored here

} INPUT_DATA, *PINPUT_DATA;

typedef struct _DEVICE_EXTENSION{

// Irps waiting to be processed are queued here
LIST_ENTRY PendingIrpQueue;

// SpinLock to protect access to the queue
KSPIN_LOCK QueueLock;

// SpinLock to provide exclusive access to the port
KSPIN_LOCK DeviceLock;

// Pointer to current device IRP. Exclusive access to this
// field is also provided by the QueueLock.
PIRP CurrentIrp;

// Customtimer DPC object
KDPC PollingDpc;

// Time at which the device was last polled
LARGE_INTEGER LastPollTime;

// Polling timer object
KTIMER PollingTimer;

// Polling interval (retry interval)
LARGE_INTEGER PollingInterval;

PIO_CSQ CancelSafeQueue;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

typedef struct _FILE_CONTEXT{
//
// Lock to rundown threads that are dispatching I/Os on a file handle
// while the cleanup for that handle is in progress.
//
IO_REMOVE_LOCK FileRundownLock;
} FILE_CONTEXT, *PFILE_CONTEXT;

#endif



Driver.c

// Known Working - Read + Write - Slight bug with the write printing to much

#include <ntddk.h>
#include <stdio.h>
#include “cancel.h”
#define IOCTL_HELLO_WORLD CTL_CODE( FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)

PDEVICE_OBJECT pDeviceObject;
UNICODE_STRING dev,dos;

NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

VOID UnloadRoutine(
IN PDRIVER_OBJECT DriverObject
);

VOID CreateProcessNotifyEx(
inout PEPROCESS Process,
in HANDLE ProcessId,
in_opt PPS_CREATE_NOTIFY_INFO CreateInfo
);

VOID CreateProcessNotifyEx(
inout PEPROCESS Process,
in HANDLE ProcessId,
in_opt PPS_CREATE_NOTIFY_INFO CreateInfo

)
{

/Irp = IoCsqRemoveNextIrp(&deviceExtension->CSReadQueue, NULL);/
if (CreateInfo)
{
if(CreateInfo->FileOpenNameAvailable==TRUE)
{
DbgPrintEx(
DPFLTR_IHVDRIVER_ID,
DPFLTR_INFO_LEVEL,
“PID : 0x%X (%d) ImageName :%wZ CmdLine : %wZ \n”,
ProcessId,ProcessId,
CreateInfo->ImageFileName,
CreateInfo->CommandLine
);
}

//CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
CreateInfo->CreationStatus = STATUS_SUCCESS;
}

}

// IRP CREATE ROUTINE - Referenced In DriverEntry
NTSTATUS Create(PDEVICE_OBJECT DeviceObject,PIRP irp)
{
DbgPrint(“Create routine called.\n”);

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

IoCompleteRequest(irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

// IRP CLOSE ROUTINE - Referenced In DriverEntry
NTSTATUS Close(PDEVICE_OBJECT DeviceObject,PIRP irp)
{
DbgPrint(“Close routine called.\n”);

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

IoCompleteRequest(irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX) CreateProcessNotifyEx, TRUE);
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Was Unloaded\n”);
}

VOID CsqInsertIrp (PIO_CSQ Csq, PIRP irp)
{
PDEVICE_EXTENSION pDevExt;
pDevExt = (PDEVICE_EXTENSION)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, CancelSafeQueue);
InsertTailList(&pDevExt->PendingIrpQueue, &irp->Tail.Overlay.ListEntry);
}

VOID CsqRemoveIrp (PIO_CSQ Csq, PIRP irp)
{
PDEVICE_EXTENSION pDevExt;
pDevExt = (PDEVICE_EXTENSION)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, CancelSafeQueue);
RemoveTailList(&pDevExt->PendingIrpQueue);
}

VOID CsqAcquireLock (PIO_CSQ Csq, PKIRQL oldirql)
{
PDEVICE_EXTENSION pDevExt;
//given the add of csq, it returns the base of the struct containing the field extCsq
pDevExt = (PDEVICE_EXTENSION)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, CancelSafeQueue);
KeAcquireSpinLock(&pDevExt->QueueLock, oldirql);
}

VOID CsqReleaseLock (PIO_CSQ Csq, KIRQL newirql)
{
PDEVICE_EXTENSION pDevExt;
pDevExt = (PDEVICE_EXTENSION)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, CancelSafeQueue);
KeReleaseSpinLock(&pDevExt->QueueLock, newirql);
}

PIRP CsqPeekNextIrp (PIO_CSQ Csq, PIRP irp, PVOID peekCtx)
{
PIO_STACK_LOCATION stack;
PLIST_ENTRY headQueues, next;
PIRP nextIrp;
PDEVICE_EXTENSION pDevExt;
pDevExt = (PDEVICE_EXTENSION)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, CancelSafeQueue);
//get the irp queue as the had of the list or queued irps
headQueues = &pDevExt->PendingIrpQueue;
//if the irp sent by i/o mgr is NULL, get if from our list
//otherwise from the non_null irp we got
if (irp == NULL)
next = headQueues->Flink;
else
next = irp->Tail.Overlay.ListEntry.Flink;
//we just get an address of an istance, so find the irp
//with CONTAINING_RECORD macro (having IRP struct and next Add)
//and pass with a cycle irps to find the peekContext matching
//in file object (until next will not be our actually irp -we want the next-)
while (next != headQueues) {
nextIrp = (PIRP)CONTAINING_RECORD(next, IRP, Tail.Overlay.ListEntry);
stack = IoGetCurrentIrpStackLocation(nextIrp);
if (stack->FileObject == peekCtx)
return nextIrp;
else
next = next->Flink;
}
return NULL;
}

VOID CsqCompleteCanceledIrp (PIO_CSQ pCsq, PIRP irp)
{
//just return to the IOmgr the cancel-safe operation
IoCompleteRequest(irp, STATUS_CANCELLED);
}

// IOCTL - Handle I/O - Reference in DriverEntry
NTSTATUS IOCTL(PDEVICE_OBJECT DeviceObject,PIRP irp)
{
char local_buf[200];
int message_length;
PDEVICE_EXTENSION devExt;
PIO_STACK_LOCATION pIoStackLocation;

PVOID pBuf = irp->AssociatedIrp.SystemBuffer;
PCHAR welcome = “Hello from kerneland.”;

pIoStackLocation = IoGetCurrentIrpStackLocation(irp);

switch(pIoStackLocation->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_HELLO_WORLD:

message_length = _snprintf(local_buf,200,“%s”,pBuf);

DbgPrint(“IOCTL HELLOS.”);

DbgPrint(“Message received : %s”, pBuf);

//34
DbgPrint(“Input Message Length: %d”,message_length);

//30
DbgPrint(“Input Buffer Length: %d”,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);

RtlZeroMemory(pBuf,pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength);

RtlCopyMemory(pBuf, welcome, strlen(welcome) );

break;
}

// Finish the I/O operation by simply completing the packet and returning
// the same status as in the packet itself.
//irp->IoStatus.Status = STATUS_SUCCESS;
//irp->IoStatus.Information = strlen(welcome);
//IoCompleteRequest(irp,IO_NO_INCREMENT);

//Experiment
devExt = &pDeviceObject->DeviceExtension;
//Maybe put this after?
irp->IoStatus.Status = STATUS_PENDING;
irp->IoStatus.Information = 0;

KeEnterCriticalRegion();
CsqInsertIrp(&devExt->CancelSafeQueue, irp);
KeLeaveCriticalRegion();

return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{

NTSTATUS status;

// If I uncomment these it bluescreens on start
//PDEVICE_EXTENSION devExt;
//devExt = &pDeviceObject->DeviceExtension;
//IoCsqInitialize(devExt->CancelSafeQueue, CsqInsertIrp, CsqRemoveIrp, CsqPeekNextIrp,CsqAcquireLock, CsqReleaseLock, CsqCompleteCanceledIrp);

DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Entry\n”);

// Define Device
RtlInitUnicodeString(&dev,L"\Device\Driver_Test");
RtlInitUnicodeString(&dos,L"\DosDevices\Driver_Test");

// Create Devce
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating Device\n”);
IoCreateDevice(DriverObject,0,&dev,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObject);

// Sym Link Device
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating Sym Link\n”);
IoCreateSymbolicLink(&dos,&dev);

// Set Interface State
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Interface State\n”);
IoSetDeviceInterfaceState(RegistryPath, TRUE);

// Set A Create Routine
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Create Callback\n”);
DriverObject->MajorFunction[IRP_MJ_CREATE]=Create;

// Set control routine
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting IOCTL Callback\n”);
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=IOCTL;

// Set close routine
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Close Callback\n”);
DriverObject->MajorFunction[IRP_MJ_CLOSE]=Close;

// Set an unload routine for when the driver exits
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Setting Unload Callback\n”);
DriverObject->DriverUnload = UnloadRoutine;

// Set Flags
pDeviceObject->Flags|=DO_DIRECT_IO;
pDeviceObject->Flags&=~DO_DEVICE_INITIALIZING;

// Hook process creation
status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)CreateProcessNotifyEx, FALSE);
if(!NT_SUCCESS(status))
{
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,“Failed to PsSetCreateProcessNotifyRoutineEx .status : 0x%X \n”,status);
}

// Print started so Adam doesn’t freak out
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Has Started\n”);

return STATUS_SUCCESS;

}
__________________________________</stdio.h></ntddk.h></dontuse.h></wdmsec.h></ntddk.h></initguid.h>

xxxxx@zepko.com wrote:

Thanks for the responses, I have spent a day getting my head around the documentation and trying to understand C,

You aren’t really trying to learn C by starting with a kernel driver,
are you?

…currently if I uncomment the three lines in DriverEntry it bluescreens instantly on start. The rest was taken from a copy of someone who looked like they had this working seen as there isn’t a single coding tutorial on this anywhere that I can find. Can someone take a look at this code and tell me where I am going wrong?

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{

NTSTATUS status;

// If I uncomment these it bluescreens on start
//PDEVICE_EXTENSION devExt;
//devExt = &pDeviceObject->DeviceExtension;
//IoCsqInitialize(devExt->CancelSafeQueue, CsqInsertIrp, CsqRemoveIrp, CsqPeekNextIrp,CsqAcquireLock, CsqReleaseLock, CsqCompleteCanceledIrp);

Of course it does. I don’t understand how this could be a mystery to
you, assuming you have experience in SOME programming language.
DriverEntry is the VERY FIRST call that gets made to your driver. When
that call is made, what will be the contents of pDeviceObject?

DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Driver Entry\n”);

// Define Device
RtlInitUnicodeString(&dev,L"\Device\Driver_Test");
RtlInitUnicodeString(&dos,L"\DosDevices\Driver_Test");

// Create Devce
DbgPrintEx( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,“Creating Device\n”);
IoCreateDevice(DriverObject,0,&dev,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObject);

HERE is where pDeviceObject (and pDeviceObject->DeviceExtension) get
initialized.

As a side note, you have pDeviceObject, dev, and dos as globals. That
is TERRIBLE programming practice. Any time you find yourself creating a
global variable, you should question it. “dev” and “dos” should not be
global just because they are temporaries; they are only needed for a
short time in this one routine, so declare them in this one routine.

EVERY callback in a Windows driver gets handed the PDEVICE_OBJECT on
which it will be operating. In this particular case, this driver will
only create one device, so the difference is moot, but in the general
case, a single driver often handles multiple devices. You don’t have to
keep track of the device objects.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Adam Bradbury wrote:

So posting this is my last option before I just off shore this task
to India for a couple of grand.

What is your actual area of expertise?

> What is your actual area of expertise?

Well, I think the OP made it VERY clear

It is pretty obvious (at least to me) that the OP will be unable to handle this project within any more or less reasonable timeframe (if ever) if he works on it on his own, so that he needs some" external assistance".

However, if he decides " just off shore this task to India for a couple of grand.", there is a good chance he will outsource it to someone who has exactly the same (or, probably, even lower) level of understanding of kernel-level programming that the OP does, which means the whole thing is not going to progress any faster either. If he really wants the job done well and reasonably fast he needs to hire a good consultant who is going to charge considerably more than the OP expects…

Anton Bassov

anton bassov wrote:

However, if he decides " just off shore this task to India
for a couple of grand.", there is a good chance he will
outsource it to someone who has exactly the same (or,
probably, even lower) level of understanding of kernel-level
programming that the OP does, which means the whole thing
is not going to progress any faster either.

An excellent point, in fact, we may see someone named “James Smith” come on this group not long after … telling us his “doubts”, but otherwise asking the same exact question…

Thanks for the feedback, if not a little strongly worded in places. I have inherited this task as it falls within a product suite that I control and apart from webdevs I am the only “backend” programmer the company has access too.

Based on the fact that this driver is only going to be <1000 lines of code and most of you “experts” talk as if you could throw one together in a weekend, I am not averse to just paying for a consultant to build me one on day rates over a month. But before I have to fork over that kind of cash I thought I would attempt it with the help of the “community”.

You know kernel driver programming is complex, you know I come from Python, you know I have no C experience. So I am having to draw on your wisdom because there isn’t any written documentation that I can buy that explains how to string something like this together. If there is then I am open to it.

I will go and take your latest feed back on board and post my results at the end of today.

Thanks for everyone that’s helping.

On 30-Oct-2015 10:20, Adam.Bradbury wrote:

> I have inherited this task as it falls within a product suite that I
control …

Well this at least gives us the scope of the task:
it is a real product, not a quickie nor a student assignment.

Based on the fact that this driver is only going to be <1000 lines of
code and most of you “experts” talk as if you could throw one together
in a weekend, I am not averse to just paying for a consultant

> I will go and take your latest feed back on board and post my results
at the end of today.

Warning: you are now too close to what is allowed by the rules of this
list. So… if the owner won’t object - some thoughts:

Take your time, do the homework and clarify at least the following points:

* What Windows versions must be supported, and for how long time.
* Functional requirements:
What details the driver sends up to the usermode component (aka
“service”),
and how to handle cases when the driver cannot get these details
(yes, this is possible)
Any concrete performance and latency requirements?
* Any logging, tracing, diagnostic needed?
* How the driver will be installed (yes - the signing, certificates
and all that nuisance) - who will do this?
* Who will do the usermode component ? If not the same person, you
will need at least a mock-up for testing the kernel part.
* How the bugs and change requests will be handled. Ensure that the
consultant will be available for such requests.
Consider costs of possible downtime, impact on your company’s
reputation… etc, etc.

You don’t have to post all these details to the list, just have them
ready when talking to the consultant. As you have CS background, you
should be aware of the garbage in - garbage out law.

Best regards,
– pa

/* no, I am not looking for this task, thanks */

Hmmm…

Let me start by being sure we’re all on the same page here: As noted in Mr. Pavel A’s post, we *do not* solicit, offer, or otherwise discuss contract jobs on this forum. You’re not doing that, and you haven’t done that – But I want to make sure it does not come to that.

It may be “less than 1000 lines of code” (apparently you know this, I’m not sure how), and these calls may APPEAR to be simple, and marshalling code back from kernel mode may APPEAR to be simple, but – as they say – the devil is in the details. You don’t know what you don’t know. And the consequence of failure in kernel-mode is that you blue screen, or hang, the system on which you’re running.

I realize that the way everyone – myself included – programs in user-mode these days is to Google around for a sample, and collect JUST enough information to understand JUST enough to get something working, and then hack/edit/change/modify/test it into what you want it to do. Because of the unstated assumptions and complexities involved, this is absolutely, positively, a Very Bad Idea for Windows kernel-mode software development. It never – never – ends with a good result.

So, as somebody who has a bit of experience in the area you’ve been struggling with, I strongly suggest you take one of two approaches:

a) Contract it out. I recommend you NOT pay someone “on day rates” but rather that you get a fully fixed-price quote for your solution that includes support after the code has been written, and ALSO training for you so, once the code is done, you can maintain the solution in the future.

b) Learn how Windows works and how to write drivers before trying to extend the operating system through the interface provided by the I/O Manager. Take a class or two.

Trust me on this. This isn’t like learning to write a SQL left-join by spending an afternoon fooling with it until it FINALLY returns what you need, and “fuck it, it looks like it works so it’ll be good enough.”

In kernel-mode, getting the code written is the start of the battle. Then you have to get it to work properly, in all necessary circumstances. You have to be able to diagnose the issue when something goes wrong. You have to be able to identify the problem and then fix it when you find it.

It’s not rocket science, and (assuming you can just “pick up” enough C), you CAN learn how to do it with the right guidance in about a week. But “the right guidance” is really critical. Again, you don’t know the things that you don’t know. We do know what you don’t know.

Now… go forth and do what you will. But that’s my best and final advice on this topic.

Peter
OSR
@OSRDrivers

Cheers for all the feedback,

I am going to take a step back and go back to theory and take another run at this. If i have to hire it out, then I have to hire it out, I have about a month to play with, so there isnt a great deal of urgency.

Thanks for the help.

Good luck, Mr. Bradbury.

And thanks for the (unintentional) inspiration. I was looking for a topic for a new blog post, and you gave it to me!

https:</https:>

Peter
OSR
@OSRDrivers

Could you let me know if in the end you outsourced?
If so, to what company?


Alexandra from https://nearshoringpartner.ro/en/

Think the guy’s still around? Four years later?

Closed.

Peter