W

Wave

Guest
#1
Previous thread on programming: https://malwaretips.com/threads/theory-c-tutorial-native-windows-api-ntapi.63573/

Hello and welcome to my second programming thread (C programming instead of C++ this time). :)

[DISCLAIMER]
The code presented within this thread is NOT dangerous if used correctly however when dealing with kernel-mode components, if a crash occurs it’ll result in a BSoD crash and therefore data loss/corruption may occur. Therefore, to prevent issues such as a BSoD (Blue Screen of Death crash) affecting your main system and potentially causing any damage, please stick to a Virtual Machine if you do not already have experience on dealing with kernel-mode components for development.

I have intentionally set the font size to 3 because this thread is very long and with the normal font size it may actually be even more of a pain to go through... I highly recommend you zoom in once in your browser, and it will appear much nicer to read the text (as opposed to reading it without zooming in once or as it is by default).

If you find any false information/mistakes in this thread then please let me know so I can fix them.
[/DISCLAIMER]

In this thread you will be learning about monitoring process creation/termination from a device driver and how we can utilise this to develop some sort of Anti-Executable software. Of course the code presented within this thread cannot just be copy-pasted to re-create a working and fully functional product, that’s not how it works and that isn’t the purpose of this thread… The purpose of this thread is to give some examples for anyone who is interested in Anti-Exe development so they have a starting point to developing a reasonably decent Anti-Exe product which will actually monitor process creation/termination without using up too many system resources or cause too many problems, whilst providing some information to help them learn about what is actually going on. There will be multiple parts to this tutorial: the first part will evolve around theory (for both Anti-Exe and a bit about the process monitoring method we will be using) and then the second part will be the source code (which will be commented to an extent).

Throughout this tutorial I will be working with Visual Studio 2015 Community Edition and for the project template I will be using the template ‘Kernel Mode Driver, Empty (KMDF)’ (I’ll explain below how you can get this template in a spoiler). I will create a new source file with the filename “driver.c” (notice the extension will be *.c since I am writing this device driver in C as opposed to C++).

If you are running Windows Server 2008 R2, Windows Server 2016, Windows 7, 8, 8.1 or 10 then you will need to download and install WDK (Windows Driver Kit) from the official Microsoft website: https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit - Please note that you will need a 2015 edition of Visual Studio (the 2015 Community version is free and there is also the free Express edition as in previous editions of VS) installed prior to downloading and installing WDK.

If you are on Windows XP then you will need to download and install DDK (Driver Development Kit) which can be found from the following URL: https://winworldpc.com/download/3D077E95-18DA-11E4-99E5-7054D21A8599 - I apologise that this download is not from the official Microsoft website however since XP is no longer supported and focus from Microsoft has been placed on the newer OS versions, it seems they no longer even have the download page for DDK anymore! You will only need DDK if you are running Windows XP, if you are using a newer OS version such as Windows 7, 8/8.1 or 10 then you do not need DDK.

(If you are using Windows XP with DDK then you will not get the templates the same as you do on the latest Windows OS versions with WDK, you will need to execute commands via the console to have the programs compile everything and will also need to manually manage the sources file, etc. You can find more information about this via Google – hopefully no one here is still using XP for personal/business uses though).

Part 1 – Theory
Now since we are dealing with device drivers I should really talk about the OS rings (and the differences between kernel-mode (ring0) and user-mode (ring 3)) however I’ve done this numerous times in the past and therefore I will not make a new explanation but will copy-paste my previous descriptions encapsulated by a quote:

In Windows we have 4 different rings: ring 0; ring 1; ring 2; and ring 3.

Ring 0 is the lowest ring available and the purpose of this ring is to hold the kernel (of the Operating System) in memory. Whereas, the third ring (ring 3) is used for user-mode on our systems (e.g. Windows kernel will be executed from within Ring 0, however our standard programs will be executing from within Ring 3). The reason we have different rings is for the protection of our system (memory, ports, and the safety of other resources in general), since it restricts what our standard programs can do on the system, but grants kernel-mode the highest privileges which it needs to control and operate the system correctly. The kernel must be executed from ring 0 because it needs to be able to access our hardware, it’s a layer to the hardware. However, our standard programs do not need such privileges and giving it to them can cause big problems, they are restricted by being executed in ring 3 which does not have access directly to hardware… Not to mention the fact that if Ring 3 software (user-mode software) was running from ring 0 then it can cause conflicts with the kernel.

Ring 0 needs to be protected because if a crash occurs it will result in a crash of the entire system and the reason for this is down to the kernel being executed there in memory. Thankfully, when a standard program crashes in user-mode (ring 3), our systems don’t need to be completely restarted and this is down to memory management – each piece of software executing from ring 3 has its own memory allocated to it, therefore when something goes wrong (the memory for that software messes up) only that memory is affected, thus only that program crashes. In ring 0 it’s a different story, you have access to all the memory on the system, complete control… Therefore, a crash will affect the entire system, and all the memory.

The purposes of ring 1 and 2 is actually for device drivers, they do not actually become loaded in ring 0 itself. The reason for ring 1 and 2 being used for device drivers is simply down to the fact that they may need to access microcontrollers and the such of hardware, and if they were loaded from ring 3 then they wouldn’t have the guaranteed control to do what they really need to do. However, device drivers in ring 1 and 2 have the most control next to the kernel in ring 0. Device drivers stay in ring 1 and 2 to grant them the privileges they require without giving them too much freedom and control which they don’t really need, and if they don’t need the same control as ring 0, then putting them in ring 0 can just cause problems. Ring 0 is for the kernel only, device drivers are to work from ring 1 and 2 (and work closely with the kernel of course), and then ring 3 is for user-mode.
Previously on older OS systems such as Windows 2000 or Windows XP most security products worked with kernel-patching techniques for its main protection features. Specifically, it would use SSDT (System Service Dispatch Table) hooking which involved replacing pointer addresses to a pointer address of the new function, which would redirect execution flow to allow the security product to intercept the calling of specific API functions to block activity occurring on the system. However back in the day these hooking methods were actually used to monitor process creation also, and thus once this action was intercepted the process would trace back to the PE on disk and this would have been scanned by the security product – however there are better, documented methods to monitor process creation now (since kernel-mode patching can also lead to instability and therefore it’s best to use documented methods where appropriate).

When Windows Vista was released Microsoft introduced a new feature within the OS called PatchGuard (and I am sure a lot of people are familiar with this due to previous posts I’ve written which you may/may not have read), however the purpose of PatchGuard is to prevent unsigned device drivers from being loaded and to prevent kernel-patching methods (via the feature within it under the name Kernel-Patch Protection (KPP)) for x64 systems (PatchGuard isn’t present on x86 versions of Windows). Since Windows Vista and on-wards, PatchGuard is built into all x64 versions of the Windows OS. If we went back to Windows 2000 or XP then PatchGuard wouldn’t exist for x64 systems and therefore you would find most security products setting kernel-mode hooks (e.g. SSDT hooks) for their protection features, however on x64 versions of Windows Vista and on-wards security products are forbidden from doing such activity ethically (since if they wanted to continue doing this they would need to exploit security features within the Windows OS and this would become patched up by Microsoft anyway, which would then break the security product after the patch update anyway).

Microsoft are not fans of kernel-patching techniques and they despise products doing such things and therefore they knew what they were doing when they introduced PatchGuard/KPP – they knew it would ruin a lot of features within certain software which relied on kernel-patching but also knew it would leave the customers of the OS with software which was more stable and allow them to be safer from malicious software (e.g. if security products cannot patch the kernel on x64 versions of Windows then neither can malware – however malware authors can bypass PatchGuard/KPP if they have the expertise to do so however it’s not a walk in the park to do after all the patches for the previous exploits and therefore it’s more rare than common for it to occur). As a result of Microsoft addressing the fact that a lot of software relied on kernel-patching techniques for its main functionality (e.g. security products used it for self-protection, dynamic protection analysis, process creation/termination monitoring, etc), they decided to introduce something called kernel-mode callbacks – these callbacks become registered and then they provide notifications to the register upon a certain event (e.g. if you register a callback for a specific activity then you can have your callback function invoked before/after the action has occurred on the system).

Thankfully Microsoft introduced kernel-mode callbacks as an alternate to kernel-mode patching (since they patched up the availability of kernel-patching without having to exploit Windows security) and also documented it, which means security software can use these kernel-mode callbacks for various things. Kernel-mode callbacks tend to be used for a wide variety of things, however two of the most common uses: process creation/termination monitoring, process protection and monitoring for modifications to the registry.

Now we’ve covered some basic theory about the OS rings and their purposes we can start to talk about Anti-Executable software and which methods we will use to monitor process creation/termination (and block it).

When it comes to the development of Anti-Executable software there are many approaches we can take and some approaches are better than others (in terms of security, speed and system resource usage). The first goal we need when it comes to developing our Anti-Exe software is to pick our method of monitoring process creation – of course we want our project to work as efficiently as possible and therefore we need to address multiple factors when it comes to the decision making of which techniques we will use. I will list some ideas of how process creation may be monitored (we will be using the first method I will mention below in this thread):

The first method we could approach would be to use the kernel-mode callback PsSetCreateProcessNotifyRoutineEx to monitor process creation/termination via a device driver. The way this method would work is we would register the callback and point it to invoke our callback function, so whenever a process is starting up/terminated our function ends up becoming invoked (the code within our function becomes executed) and we can filter out the parameters to find which process it was/the filename which is starting up/was terminated – as bonus addition to this, we can block process creation by altering the parameters which are passed to us. Using this kernel-mode callback is much more efficient and lighter on system resources than some other alternate methods we could use, not to mention it’s working from kernel-mode and thus it’s much more secure than other methods (for it to be disabled the attacker would need to have a kernel-mode component to unregister the callback for example, unlike with other methods where the attacker can stay executing code from within user-mode and disable the protection). This method is also most likely one of the easiest, even though it is one of the best methods – Microsoft took the trouble to document it which is an even bigger bonus to us and can help us if we run into any bugs/problems!

The second method we could approach would be to inject our own *.DLL (Dynamic Link Library)/code into all running user-mode processes and then within our injected code we can place hooks on specific functions to monitor process creation. This method is actually not a bad idea and if done properly would work well, however it can be bypassed much more easily than using the kernel-mode callback PsSetCreateProcessNotifyRoutine/Ex and if done incorrectly can cause instability (e.g. cause the target process to crash). There are many different functions which could be hooked to monitor process creation however this thread is not about API hooking and therefore I won’t go on about it any further. Without that being said, it’s not as secure as using a kernel-mode callback since malicious processes may actually attempt to remove the hooks (or evade them using system calls or implementing the function prologue manually), not to mention that an Anti-Cheat features of specific games may assume that the gamer is trying to hack the game (due to the security installed on the system is injecting into it and setting hooks).

The third method we could approach would be to either work with WMI (Windows Management Instruction) with Event Handlers for process creation/termination or to just regularly enumerate a list of running processes and compare which ones weren’t in the previous list (and then suspend them so you have time to perform further analysis and terminate the process if it shouldn’t be running). However, there are many flaws with this method. Firstly, it does not have the ability to execute our code before the process actually fully starts and starts executing its code and therefore if the process starts before we can perform our analysis it can have enough time to mess some stuff up (or even attack the Anti-Exe protection). Secondly, this method may use much more system resources (e.g. higher CPU usage to work) than the other two methods. Those are the two main problems for this method: security and system resource usage. However, I am sure there are many more flaws with this method and it just isn’t used in any commercial security products anymore for a reason (since I am sure some products in the past would have made the mistake of using this method on their start-up at some point and then later changed to a better method once they matured their product and upgraded their knowledge up).

All in all, I am sure we can all agree that the kernel-mode callback PsSetCreateProcessNotifyRoutineEx for process creation/terminating is the best option out of all the three mentioned above.


I believe that this is sufficient information for us to continue onto the source code – please remember that if you have no experience with Windows Driver Development to stick to a Virtual Machine for testing and to be careful since if something goes wrong it will cause a system crash and this can result in potential data loss/corruption of course. The purpose of this thread isn’t to teach you the device driver development basics, but more on how someone could approach development for an Anti-Executable software at first (and maybe something they could use as a base example for their own implementation).


Part 2 – The source code
I will be using Visual Studio 2015 (Community Edition – registered) and I will create a new ‘Kernel-Mode Driver, Empty (KMDF)’ project (Visual C++ -> Windows Driver -> WDF). I will then create a new source file 'driver.c' under the 'Source Files' folder.

Code:
#include <ntddk.h>

/* I recommend adding a Driver Dispatch function to handle IRP messages or working with handing IOCTls to get info from user-mode and sending it back (e.g. if you have a GUI program you may want to let it know when a program starts up or when a program was blocked, etc) */

VOID PsCreateProcessNotifyEx_CB(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo);
VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pUniStr);

/*
This is our callback function
Once our callback has been registered everytime a process is created or terminated it'll result in our callback being involved BEFORE the process can fully continue execution, giving us time to perform analysis and block the execution before it continues.

If you are interested in developing a security product such as an AV, performing cloud analysis on untrusted processes trying to run (and so on) then you need to auto-block execution and work with IOCTLs to send back to a user-mode process to perform the scan (and if clean then manually re-execute the program) since you cannot just hold up in kernel-mode and wait for scan responses since this would break the structure of kernel-mode (as it would result in hanging up the entire system!).

If you are familiar with AV testing videos and you see an error alert dialog show up about how Windows cannot "find" a certain program, that is due to the use of this callback. When process execution is blocked it will result in a Windows error claiming the program cannot be found (or has been blocked). Like I said, many and most security products use this method for process monitoring (and blocking)!

So far the callback will check if the FileOpenNameAvailable is != 0 (so if it is true) and if it is then it will print out the ImageFileName which will give you the location of the PE on disk being ran.
*/

VOID PsCreateProcessNotifyEx_CB(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
       UNREFERENCED_PARAMETER(Process); // unreferenced
       UNREFERENCED_PARAMETER(ProcessId); // unreferenced

       if (CreateInfo) // if the info is available
       {
              if (CreateInfo->FileOpenNameAvailable) //check
              {
                     // print the program filepath which was opened (thats what ImageFileName is)
                     DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%wZ\n", CreateInfo->ImageFileName);

                     // To block process execution just change the parameter data in CreationStatus (CreateInfo->CreationStatus) to an NTSTATUS value such as STATUS_ACCESS_DENIED :)
              }
       }
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
       NTSTATUS NtRet = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)PsCreateProcessNotifyEx_CB, TRUE); // register the callback, TRUE as second parameter to disable and FALSE to enable

       DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Attention: The security guards have found and caught the Wave which was surfing within your kernel-mode memory!\n"); // print out (use programs like DbgView with kernel-mode monitoring to see this - you may need to make some registry tweaks to make it work!) - notice the little joke about my username?...
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pUniStr)
{
       UNREFERENCED_PARAMETER(pUniStr); // unreferenced
       pDriverObject->DriverUnload = DriverUnload; // assign the unload routine
       NTSTATUS NtRet = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)PsCreateProcessNotifyEx_CB, FALSE); // attempt to enable the callback and set the return status on registration to NtRet so we can check if it was successful or not

       if (NtRet == STATUS_ACCESS_DENIED) // access denied so callback failed to register
       {
              DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Callback failed to register: STATUS_ACCESS_DENIED 0\n"); // print out the fail
       }

       DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Attention: A wave has been seen surfing in your kernel-mode memory! 0\n"); // driver loaded notification

       return STATUS_SUCCESS; // return STATUS_SUCCESS as driver loaded fine
}

// I know, very little code for nice functionality... It's not as much as you were expecting, right? :D
Before I end this thread I need to point out that you MUST unregister the callback in the DriverUnload routine function (or make sure it is unregistered before the driver becomes unloaded) because if it is not it will result in a BSoD crash. Why will it crash if I forget to unregister the callback? The answer to this is simple – if the callback is still registered and you unload your driver then the notification function will be pointed to a pointer address of your own function in memory which no longer exists, and therefore when a new process is created/terminated and it goes through the callback it will end up trying to access a function which no longer exists in memory (the callback function we created), thus causing a Blue Screen of Death crash (since like I said, any crashes in kernel-mode results in a system-wide crash).

As well as this, you need to add /integritycheck to the linker settings – if you do not enable \integritycheck then the callback will fail to become registered, period. You can do this by going to: Project -> (project_name) Properties -> Popup dialog shows -> Linker -> Command Line -> In the “Additional Options” rich textbox control you need to add “/integritycheck” (without the double “” of course) and then press OK. Make sure that the ‘Configuration’ and ‘Platform’ is set correctly before you alter the settings otherwise you’ll need to re-do the steps later on.

I hope this thread has helped someone and good luck with any security software development anyone may be attempting to do!

Stay safe,
Wave. :)
 
W

Wave

Guest
#10
Here's some more information I've written for another thread which is still related to Anti-Executable development:
There are some interesting functions which are exported by a Windows Dynamic Link Library (*.DLL) called csrsrv.dll; csrss.exe imports this module and utilises some functions exported by this library. The two interesting functions which become exported by this library would be: CsrCreateProcess and CsrTerminateProcess.

If you are able to obtain a handle to csrss.exe and then utilise this handle to inject code into it (e.g. you can perform stealth code injection or you can work with standard DLL injection techniques to get your code running within the address space of csrss.exe on its own thread) then you can place a hook on those two interesting functions (CsrTerminateProcess and CsrTerminateProcess) which will allow you to log when a new process is created or when a process becomes terminated; through these hooks you will be able to essentially monitor all user-mode process execution attempts (at the least), and when a new process is created and your CsrTerminateProcess hook callback function becomes invoked, you will be greeted with access to a working and usable HANDLE to the newly starting up process, which you can use for whatever you want (terminate the process if you do not want to allow the execution, inject into it to control it, etc).

In a way, hooking CsrCreateProcess and CsrTerminateProcess would create similar functionality in user-mode of using the kernel-mode callback PsSetCreateProcessNotifyRoutine/Ex. The good thing about this method is that you will only need to inject into csrss.exe and hook CsrCreateProcess/CsrTerminateProcess once, you won’t need to inject into any other programs except csrss.exe for it to work.

However, this option may be good mainly for Windows Vista and Windows 7 only, and not for Windows 8/8.1 or Windows 10 – on Windows Vista and Windows 7, csrss.exe is not a protected process, and therefore you can obtain a handle to the SYSTEM process as long as you have enabled debugging rights, however on newer OS editions of Windows, csrss.exe will be a protected process and you will be unable to obtain a handle to it from user-mode. Of course, if you are developing software for Windows Vista and Windows 7 and you do not have experience with device driver development to use kernel-mode callbacks then this can be a good alternate from user-mode, however of course using a kernel-mode callback for process monitoring would be much more secure.
Source: Windows processes which are significantly important
------------------

As well as this, you can detect process execution through the usage of user-mode hooks if you are unable to utilise a kernel-mode driver for the kernel-mode callback. Of course, you could hook a Win32 API like CreateProcess but it would not catch all process execution attempts coming from user-mode (without tricks to bypass hooks) because not all programs will call this function for process execution (e.g. explorer.exe will use ShellExecuteW instead) - of course you could hook both CreateProcess and Shell functions but this won't work very well either. As a better method when working via hooking, you can either try hooking ZwSuspendThread (and then perform filters to make sure it's a new process start-up), RtlCreateUserProcess (for XP support), NtCreateUserProcess (better idea than RtlCreateUserProcess but only exists since Windows Vista therefore this would remove XP support) or NtCreateSection (probably the best method suggested in this chunk of text - every process start-up from user-mode should trigger this hook).

In case you do decide to hook NtCreateSection for process monitoring, you can filter out a process start-up (to differentiate between a normal file operation or a process creation attempt) via comparing the value of the parameters for the allocation attributes and the page protection - if the allocation attributes is for SEC_IMAGE and the page protection is for PAGE_EXECUTE then this indicates it is a process start-up. You can filter it out like the following pseudo-code (I believe - more testing needs to be done but I did test it with explorer.exe):
Code:
NTSTATUS NTAPI NtCreateSection_CB(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle)
{
    if (AllocationAttributes == SEC_IMAGE)
    {
        if (SectionPageProtection == PAGE_EXECUTE)
        {
            // new process startup
        }
    }
    return NtCreateSection_Tramp(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle);
}
There are many other functions which can be hooked such as CreateProcessInternalA/W routines (however this won't be triggered that much on newer OS versions of Windows but would have worked well on older OS versions); the reason I specifically like this function is because it is very easy to inject a DLL into the newly starting up process, you won't even need to manual map it when you inject from a callback of a hook to a function like that. Except it's not very secure or good in terms of reliability.

That being said, user-mode hooking for anti-executable development really isn't the best idea because more sophisticated malware will be able to bypass the monitoring straight from user-mode via a direct NTAPI system call, thus meaning they won't need any kernel-mode execution to bypass the anti-executable protection. The CsrCreateProcess hook from csrss.exe should be sufficient enough for a decent and more secure user-mode based anti-executable software, though (since Windows will automatically notify csrss.exe of the process creation anyway, meaning the hook will become triggered, unless another program injected into csrss.exe and un-detoured the function which is highly unlikely).

Hope this new information may be found helpful by someone out there, and happy developing!

Stay safe,
Wave. ;)
 

Andy Ful

Level 33
Content Creator
Verified
Joined
Dec 23, 2014
Messages
2,261
Operating System
Windows 10
Antivirus
Windows Defender
#11
...

Code:
#include <ntddk.h>

/* I recommend adding a Driver Dispatch function to handle IRP messages or working with handing IOCTls to get info from user-mode and sending it back (e.g. if you have a GUI program you may want to let it know when a program starts up or when a program was blocked, etc) */

VOID PsCreateProcessNotifyEx_CB(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo);
VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pUniStr);

/*
This is our callback function
Once our callback has been registered everytime a process is created or terminated it'll result in our callback being involved BEFORE the process can fully continue execution, giving us time to perform analysis and block the execution before it continues.

If you are interested in developing a security product such as an AV, performing cloud analysis on untrusted processes trying to run (and so on) then you need to auto-block execution and work with IOCTLs to send back to a user-mode process to perform the scan (and if clean then manually re-execute the program) since you cannot just hold up in kernel-mode and wait for scan responses since this would break the structure of kernel-mode (as it would result in hanging up the entire system!).

If you are familiar with AV testing videos and you see an error alert dialog show up about how Windows cannot "find" a certain program, that is due to the use of this callback. When process execution is blocked it will result in a Windows error claiming the program cannot be found (or has been blocked). Like I said, many and most security products use this method for process monitoring (and blocking)!

So far the callback will check if the FileOpenNameAvailable is != 0 (so if it is true) and if it is then it will print out the ImageFileName which will give you the location of the PE on disk being ran.
*/

VOID PsCreateProcessNotifyEx_CB(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
       UNREFERENCED_PARAMETER(Process); // unreferenced
       UNREFERENCED_PARAMETER(ProcessId); // unreferenced

       if (CreateInfo) // if the info is available
       {
              if (CreateInfo->FileOpenNameAvailable) //check
              {
                     // print the program filepath which was opened (thats what ImageFileName is)
                     DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%wZ\n", CreateInfo->ImageFileName);

                     // To block process execution just change the parameter data in CreationStatus (CreateInfo->CreationStatus) to an NTSTATUS value such as STATUS_ACCESS_DENIED :)
              }
       }
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
       NTSTATUS NtRet = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)PsCreateProcessNotifyEx_CB, TRUE); // register the callback, TRUE as second parameter to disable and FALSE to enable

       DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Attention: The security guards have found and caught the Wave which was surfing within your kernel-mode memory!\n"); // print out (use programs like DbgView with kernel-mode monitoring to see this - you may need to make some registry tweaks to make it work!) - notice the little joke about my username?...
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pUniStr)
{
       UNREFERENCED_PARAMETER(pUniStr); // unreferenced
       pDriverObject->DriverUnload = DriverUnload; // assign the unload routine
       NTSTATUS NtRet = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)PsCreateProcessNotifyEx_CB, FALSE); // attempt to enable the callback and set the return status on registration to NtRet so we can check if it was successful or not

       if (NtRet == STATUS_ACCESS_DENIED) // access denied so callback failed to register
       {
              DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Callback failed to register: STATUS_ACCESS_DENIED 0\n"); // print out the fail
       }

       DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Attention: A wave has been seen surfing in your kernel-mode memory! 0\n"); // driver loaded notification

       return STATUS_SUCCESS; // return STATUS_SUCCESS as driver loaded fine
}
...
Wave. :)
Great post. Thanks. :)
Will this driver work with scripts (like *.js or *.vbs) ?
 
W

Wave

Guest
#12
Great post. Thanks. :)
Will this driver work with scripts (like *.js or *.vbs) ?
The callback will be invoked for process execution therefore no matter what format is being used, if a new process start-up occurs, then the callback will become invoked, passing in the information for the new process start-up request. When the callback becomes invoked the process is still in it's early stages of execution and therefore has not actually properly started yet (which is why this method allows to block the execution after filtering).

That being said, there are other kernel-mode callbacks for monitoring things like when a DLL is mapped into memory by a running process (e.g. PsSetLoadImageNotifyRoutine - I believe this kernel-mode callback can be a method of preventing DLL injection into a process (e.g. if the self-protection for the process was bypassed) from kernel-mode, like a LdrLoadDll hook equivalent for kernel-mode).

However to use PsSetCreateProcessNotifyRoutine/Ex you need to remember to add /integritycheck via the linker settings, otherwise the registration will fail with STATUS_ACCESS_DENIED. :)
 
W

Wave

Guest
#17
If I try to run a file, the Smartscreen may check it first, and next comes the driver?
Actually this is a very interesting question and right now I am not sure however I am definitely going to test this out to check and then I'll let you know. I would assume that the driver would intercept first, only because this method is commonly used by typical popular AV software and they successfully block detected threats upon execution before the SmartScreen alert (I believe?).
 

Andy Ful

Level 33
Content Creator
Verified
Joined
Dec 23, 2014
Messages
2,261
Operating System
Windows 10
Antivirus
Windows Defender
#19
I have just tested 'NoVirusThanks EXE Radar Pro' and 'Excubits Bouncer' - in both cases 'SmartScreen App on the Run' is triggered as the first (that is good). I think that it is not a problem for an anti-exe to be after the SmartScreen. I only pray, that there is no way to trigger a malware file before the SmartScreen check.:)