Click here to Skip to main content
15,394,479 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a driver written in pure C and a application in .NET framework which is capable of sending and receiving IOCTL commands. Currently the driver is able to send Process Creation, Image Load Notify and Thread Notify callbacks to the .NET application without issue but now I would like to be able to suspend or wait for the process creation until the application/user allows the process to start or if the user wishes to block the process then it simply does not run. I would imagine there must be some event that needs to be written within the driver or check within the process creation callback to handle this kind of functionality. I have seen kewaitforsingleobject and other methods but its still foggy to me how to send this notification to usermode and then reply back to the driver what should or should not happen. Any insight or advise would be one step closer to learning how this is achieved as I am getting more and more confused with the MSDN examples and partial code snippets and how it fits my issue.

What I have tried:

Not sure what to try :(

Maybe IOCTL block and IOCTL allow to receive a value from User Mode application and set a Boolean value within the driver to check for within the processcreation callback? still I don't know how to have the process creation wait until a response is gained from the users decision so the process does not start or vise versa. Is it possible to start the process suspended from the processcreation callback and have the user mode application resume the main thread of that process to allow the process to run or terminate the process if block is selected?
Posted
Updated 30-May-22 5:40am
v7

1 solution

Hello,

You could block the process creation by setting the CreationStatus member in the PS_CREATE_NOTIFY_INFO structure[^] to access denied in your callback.

I want to tell you that everything you are describing goes against Best Practices[^]. I would suggest that you look at other solutions.

There are better ways to prevent malware or unwanted processes from executing. You should consider blocking the process earlier. One of the first things that occurs during process creation is that the operating system maps the executable into memory. You should intercept it here.

You would want to filter IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION[^] and look for PAGE_EXECUTE page protection. Block it at the file layer before it gets mapped into memory.

You should abandon your idea to kill the process after it's already running. Consider blocking it at the file layer.
   
v2
Comments
Dale Seeley 10-May-22 22:40pm
   
Thank you Randor for bringing this to my attention I am not sure how I overlooked this with all the hours of research I've been doing but reading the PS_CREATE_NOTIFY_INFO link you have supplied is a structure and not a callback? how is this implemented into the driver if ProcessCreationNotifyEx is not the way and by that time the process is already loaded into memory? Will replacing my major function DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControl; to IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION make it possible to receive the callbacks at the file layer level? and to my original question how would I go about setting an event within all this and wait for response from my user mode application. I have heard inverted calls might be the answer but if there is a better way I am all ears.
Dale Seeley 10-May-22 23:38pm
   
Actually I can see now that within my PsSetCreateProcessNotifyRoutineEx I do have PS_CREATE_NOTIFY_INFO and going to that definition shows the exact structure that you have posted here. Is this the file layer that you are speaking of? here is the code I have in that area:

void OnProcessNotify(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);

if (CreateInfo) {
ProcId = HandleToUlong(ProcessId);
RtlCopyUnicodeString(&ImageP, CreateInfo->ImageFileName);
//DbgPrint("%d %wZ", ProcId, ImageP);
if (!CreateInfo->IsSubsystemProcess) {
CreateInfo->CreationStatus = STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY;
}
}
}

I was first wondering how this structure was called, a bit embarrassing seeing this now sorry Its painfully obvious I am not strong yet in C coding. So that brings me back to the MajorFunction IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION as I understand this is the callback that is called when file operations occur:

NTSTATUS FsRtlRegisterFileSystemFilterCallbacks(
[in] _DRIVER_OBJECT *FilterDriverObject,
[in] PFS_FILTER_CALLBACKS Callbacks
);

So I pass in The DriverObject and PFS_FILTER_CALLBACKS which I found to be:

typedef struct _FS_FILTER_CALLBACKS {
ULONG SizeOfFsFilterCallbacks;
ULONG Reserved;
PFS_FILTER_CALLBACK PreAcquireForSectionSynchronization;
PFS_FILTER_COMPLETION_CALLBACK PostAcquireForSectionSynchronization;
PFS_FILTER_CALLBACK PreReleaseForSectionSynchronization;
PFS_FILTER_COMPLETION_CALLBACK PostReleaseForSectionSynchronization;
PFS_FILTER_CALLBACK PreAcquireForCcFlush;
PFS_FILTER_COMPLETION_CALLBACK PostAcquireForCcFlush;
PFS_FILTER_CALLBACK PreReleaseForCcFlush;
PFS_FILTER_COMPLETION_CALLBACK PostReleaseForCcFlush;
PFS_FILTER_CALLBACK PreAcquireForModifiedPageWriter;
PFS_FILTER_COMPLETION_CALLBACK PostAcquireForModifiedPageWriter;
PFS_FILTER_CALLBACK PreReleaseForModifiedPageWriter;
PFS_FILTER_COMPLETION_CALLBACK PostReleaseForModifiedPageWriter;
PFS_FILTER_CALLBACK PreQueryOpen;
PFS_FILTER_COMPLETION_CALLBACK PostQueryOpen;
} FS_FILTER_CALLBACKS, *PFS_FILTER_CALLBACKS;

How is this used to get notified at the file layer which you describe is better because it happens before the process or file is loaded into memory.
Dale Seeley 10-May-22 23:41pm
   
Should I be using a minifilter driver or Kernel driver or will it make much difference?
Randor 11-May-22 0:23am
   
[My recommendations]
If your goal is to block process creation I would recommend a minifilter driver. You should want to catch it *before* it executes from the file level.

Code samples are here: https://docs.microsoft.com/en-us/windows-hardware/drivers/samples/file-system-driver-samples

You would want to filter IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION and look for anything being mapped into memory with PAGE_EXECUTE.

[Your current code]
With your current kernel driver you can set the CreationStatus in the PS_CREATE_NOTIFY_INFO structure to block the process creation.

[Your question about event objects]
Your question about events... I would recommend creating an event in the 'Global Namespace' to signal between your malware driver/service.

https://docs.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces

[Events between driver/usermode]
https://github.com/Microsoft/Windows-driver-samples/tree/master/general/event

There is a code sample for just about every scenario. Just keep experimenting/exploring and asking questions.
Dale Seeley 11-May-22 0:38am
   
Thank you again for the quick response and time taken to help me out in my driver adventure. I agree with you that a minifilter driver would be best also now that I research it and take your advise but will my current code be sufficient or does my current code allow the process to load into memory?
Randor 11-May-22 0:48am
   
You need to learn how to read the documentation before asking for help. The answer to your question is right there in the docs.

"For a new process, the CreateProcessNotifyEx routine is called after the initial thread is created, but before the thread begins running."

Obviously the code has been mapped at that point in time.
Dale Seeley 22-May-22 17:17pm
   
Hello again Randor I am finally at the point where I would like to notify my usermode application of events happening within my driver. Its no surprise that I seem to be lost when trying to use the example from the Windows driver sample as it compiles but BSOD my computer because of a unhandled threading exception. I would like to use the IRP_BASED notify approach as it says there are 2 advantages to using that approach. If you can again point me in the right direction I would like to know what each side needs to make the notification work. I know to define a global event within the driver but because I am using .NET for the application side its difficult to understand the concept and how to receive the notification and have the driver wait until it receives the response from the usermode application.
Dale Seeley 11-May-22 0:01am
   
I found this code but its unclear where its called in the driver and if its to be used within a minifilter or Kernel driver

if( ( Data->Iopb->Parameters.AcquireForSectionSynchronization.PageProtection & PAGE_EXECUTE ) && ( Data->Iopb->Parameters.AcquireForSectionSynchronization.SyncType == SyncTypeCreateSection ) )
{
// this is a process execution which is about to start, determine here if the process is allowed to run, if not:
Data->IoStatus.Status = STATUS_ACCESS_DENIED; <-- this will deny the process execution ^_^
Data->IoStatus.Information = 0;
}
Randor 11-May-22 0:37am
   
That looks to be from a minifilter driver. That's exactly what I recommend to block process creation before it occurs. It would also potentially allow you to block malware DLL/libs from loading into a process.

What you would be doing here is catching it as it's being mapped into memory.
Dale Seeley 11-May-22 0:42am
   
awesome I will be creating a minifilter driver then to deal with process notifications and blocking. I will look at the link you have provided to try and learn how the events work and how to signal or notify my usermode application and send a response back to the driver. I hope to put this part of the project to bed soon as its quite in depth and has already taken me so long to understand lol. just to clarify I can define a event globally and then set that event within the callback to have the callback wait for a response as to block or allow?
Randor 11-May-22 0:53am
   
I'm not going to design your driver/kernel communication protocol for you. I will simply say that you can set/unset and share events.

Code sample is here:
https://github.com/Microsoft/Windows-driver-samples/tree/master/general/event
Draco2013 11-May-22 11:10am
   
OK I understand and thank you very much for this info I will mark this as accepted as I'm sure it will be everything I need. Now comes the part of actually doing it 😁
Randor 11-May-22 16:34pm
   
Thanks, but I don't answer questions for internet points.

Good luck with your project. What is your relationship with the original poster? Are you coworkers?
Dale Seeley 11-May-22 18:11pm
   
No just a friendly person like yourself who decided to help
Dale Seeley 12-May-22 1:20am
   
Hello Again Randor you suggested I keep researching, learning and experimenting so I am doing just that and making progress in my minifilter driver. here is my code so far:

#include <fltkernel.h>
#include <dontuse.h>

PFLT_FILTER FilterHandle = NULL;
NTSTATUS MiniBitUnload(FLT_FILTER_UNLOAD_FLAGS Flags);
FLT_PREOP_CALLBACK_STATUS MiniDriverPreOperation(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_POSTOP_CALLBACK_STATUS MiniDriverPostOperation(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);

const FLT_OPERATION_REGISTRATION Callbacks[] = {
{IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, 0, MiniDriverPreOperation, MiniDriverPostOperation},
{IRP_MJ_OPERATION_END}
};

const FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION),
FLT_REGISTRATION_VERSION,
0,
NULL,
Callbacks,
MiniBitUnload,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};

NTSTATUS MiniBitUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
KdPrint(("Driver Unload \r\n"));
FltUnregisterFilter(FilterHandle);
return STATUS_SUCCESS;
};

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath) {
NTSTATUS status;
status = FltRegisterFilter(DriverObject, &FilterRegistration, &FilterHandle);

if (NT_SUCCESS(status)) {
status = FltStartFiltering(FilterHandle);

if (!NT_SUCCESS(status)){
FltUnregisterFilter(FilterHandle);
}
}

}

FLT_PREOP_CALLBACK_STATUS MiniDriverPreOperation(PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID* CompletionContext) {

if ((Data->Iopb->Parameters.AcquireForSectionSynchronization.PageProtection & PAGE_EXECUTE) && (Data->Iopb->Parameters.AcquireForSectionSynchronization.SyncType == SyncTypeCreateSection))
{
PFLT_FILE_NAME_INFORMATION FileNameInfo;
NTSTATUS status;
WCHAR Name[200] = { 0 };

status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInfo);

if (NT_SUCCESS(status)) {

status = FltParseFileNameInformation(FileNameInfo);

if (NT_SUCCESS(status)) {

if (FileNameInfo->Name.MaximumLength < 260) {

// I will check here to determine if the process should be allowed or not to run
//Data->IoStatus.Status = STATUS_ACCESS_DENIED;
//Data->IoStatus.Information = 0;

RtlCopyMemory(Name, FileNameInfo->Name.Buffer, FileNameInfo->Name.MaximumLength);
KdPrint(("Information: %ws \r\n", Name));
}
}

FltReleaseFileNameInformation(FileNameInfo);
}
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

FLT_POSTOP_CALLBACK_STATUS MiniDriverPostOperation(PFLT_CALLBACK_DATA Data,
PCFLT_RELATED_OBJECTS FltObjects,
PVOID* CompletionContext){
KdPrint(("Post Create Is Running \r\n"));
return FLT_POSTOP_FINISHED_PROCESSING;
}

This is a
Randor 12-May-22 2:18am
   
The error message is telling you exactly what is wrong. It's telling you the function parameters do not match. Look closely at the error message. Do you see your callback function in the error message has only three parameters. The callback signature is on the MSDN site:

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/nc-fltkernel-pflt_post_operation_callback

That's what causes your C4113 warning.
Dale Seeley 12-May-22 2:12am
   
My question now is... Communication with my application, can you still use DeviceIOControl or is there a better method? Also the file paths come up like this:

Information: \Device\HarddiskVolume6\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2203.10.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe

is there a way to get the drive letter to form a proper path?
Randor 12-May-22 2:36am
   
From kernelmode you can use the FltQueryInformationFile function to get most info about the file and reassemble the file path.

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/nf-fltkernel-fltqueryinformationfile

Edit: There are a dozen ways to do this, you can use this one too
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/nf-fltkernel-fltparsefilenameinformation
Dale Seeley 14-May-22 0:31am
   
Currently I am using fltparsefilenameinformation but it still only gives me the path like so:
Information: \Device\HarddiskVolume6\Windows\System32\wbem\wmiutils.dll

Where I would like to change is \Device\HarddiskVolume6\ to display a Drive Letter Instead.

Dale Seeley 14-May-22 1:15am
   
Getting the dosName would be preferred
Dale Seeley 14-May-22 1:56am
   
I am noticing and maybe its a realization if correct... Are you not able to get drive letter within the pre operation and only in the post operation? because its not actually loaded yet? if that is the case how can I stop this in the file layer before its loaded into memory and still determine the drive or dos path?
Randor 14-May-22 16:27pm
   
You want the drive letter? Have you tried IoVolumeDeviceToDosName?

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-iovolumedevicetodosname
Dale Seeley 15-May-22 21:51pm
   
thank you after some hair pulling I finally got the filepath correct! thank you Randor
Randor 16-May-22 1:45am
   
Happy to see you solved it, congratulations.
Draco2013 12-May-22 11:09am
   
Thank you I will try to fix the file path with fltQueryinformation and DeviceIoControl should still do the trick for communicating between the filter driver and application?
Draco2013 12-May-22 11:15am
   
Ahhh ok I'm missing the flags in my function parameter thank you
Randor 14-May-22 16:49pm
   
Yep, for some reason I can see deleted posts here. Your warning was a missing parameter in your callback.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900