Abstract

Intercepting and tracing process execution is a very useful mechanism for
implementing NT Task Manager-like applications and systems that require
manipulations of external processes. Notifying interested parties upon starting
of a new processes is a classic problem of developing process monitoring
systems and system-wide hooks. Win32 API provides set of great libraries (PSAPI
and ToolHelp [1]) that allow you to enumerate processes currently running in
the system. Although these APIs are extremely powerful they don't permit you to
get notifications when a new process starts or ends up. This article provides
an efficient and robust technique based on a documented interface for achieving
this goal.

Solution

Luckily, NT/2K provides a set of APIs, known as "Process Structure Routines"
[2] exported by NTOSKRNL. One of these APIs PsSetCreateProcessNotifyRoutine()
offers the ability to register system-wide callback function which is called by
OS each time when a new process starts, exits or is terminated. The
mentioned API can be employed as an easy to implement method for tracking down
processes simply by implementing a NT kernel-mode driver and a user mode Win32
control application. The role of the driver is to detect process execution and
notifiy the control program about these events.

Requirements

Resolve synchronization issues between the driver and the user mode application

Build an easy to use and extend OOP user-mode framework

Allow registering and un-registering of the callback as well as ability to
dynamically load and unload the kernel driver

How it works

The control application register the kernel mode driver under
HKLM\SYSTEM\CurrentControlSet\Services and dynamically loads it. The kernel
driver then creates a named event object that is used to signal the user-mode
application when new event has been fired (i.e. process starts or ends up). The
control application opens the same event object and creates a listening thread
that waits on this event. Next, the user mode application sends a request to
the driver to start monitoring. The driver invokes PsSetCreateProcessNotifyRoutine(),
which accepts two parameters. One of them specifies the entry point of a
caller-supplied callback routine, responsible for receiving all notifications
from Windows. Upon a notification, that takes place in the callback, the driver
signals that event in order to inform the user-mode application that something
has happened. The control application then gets the data for that particular
event from the driver and stores it in a special queue container for further
processing. If there is no need for detecting process execution anymore the
user mode application sends a request to the driver to stop monitoring. The
driver then deactivates the observing mechanism. Later the control mode
application can unload the driver and un-register it.

Design and implementation

NT Kernel mode driver (ProcObsrv)

The entry point DriverEntry() (ProcObsrv.c) performs the driver's
initialization only. The I/O manager calls this function when the driver is
loaded. Since PsSetCreateProcessNotifyRoutine() allows to
un-register the callback I implemented the actual process of registration and
un-registration in the driver's dispatch routine. This allows me dynamically to
start and stop the monitoring activities by using a single IOCTL (control code

IOCTL_PROCOBSRV_ACTIVATE_MONITORING

). Once the callback is
registered each time when a process starts or terminates the OS calls user
supplied ProcessCallback(). This function populates a buffer that
will be picked up by the user mode application. Next the driver signals the
named event object, thus the user-mode application that waits on it will be
informed that there is available information to be retrieved.

Control application (ConsCtl)

For the sake of simplicity I decided to provide a simple console application,
leaving the implementation of the fancy GUI stuff to you. Designing of an
application to be multithreaded allows that application to scale and be more
responsive. On the other hand, it is very important to take into account
several considerations related to synchronizing the access to information
provided by the publisher (i.e. kernel driver) and retrieved by the subscriber
(i.e. control application). The other important key point is that a detecting
system must be reliable, and makes sure that no events are missed out. To
simplify the design process, first I needed to assign the responsibilities
between different entities in the user mode application, responsible for
handling the driver. However it isn't difficult to do it by answering these
questions [5]:

What are the processes in the system

What are the roles in the framework

Who does what and how do they collaborate

Follows UML class diagram, that illustrates the relations between classes:

CApplicationScope implements a singleton and wraps up the main
interface to the framework. It exposes two public methods that start and stop
the monitoring process.

CProcessThreadMonitor is the thread that waits on the created by
the driver event to be signaled. As soon as a process has been created or ended
up, the driver signals this event object and CProcessThreadMonitor's
thread wakes up. Then the user mode application retrieves the data from the
driver. Next, the data is appended to queue container (CQueueContainer)
using its method Append().

CQueueContainer is a thread-safe queue controller that offers an
implementation of the Monitor/Condition variable pattern. The main purpose of
this class is to provide a thread-safe semaphore realization of a queue
container. This is how the method Append() works:

Since it is designed to notify when there is an element available in the queue,
it aggregates an instance of CRetreivalThread, which waits until
an element becomes available in the local storage. This is its pseudo
implementation:

Wait on m_evtElementAvailable
event object

Lock access to the STL deque object

Extract the data item

Unlock the deque

Process the data that has been retrieved from the queue

Here is the method invoked when something has been added to the queue:

// Implement specific behavior when kernel mode driver notifies // the user-mode app void
CQueueContainer::DoOnProcessCreatedTerminated() {
QUEUED_ITEM element; // Initially we have at least one element for processing
BOOL bRemoveFromQueue = TRUE;
while (bRemoveFromQueue) {
DWORD dwResult = ::WaitForSingleObject( m_mtxMonitor, INFINITE );
if (WAIT_OBJECT_0 == dwResult) {
// Is there anything in the queue
bRemoveFromQueue = (m_Queue.size() >0);
if (bRemoveFromQueue) {
// Get the element from the queue
element = m_Queue.front(); m_Queue.pop_front();
} // if else// Let's make sure that the event hasn't been // left in signaled state if there are no items // in the queue
::ResetEvent(m_evtElementAvailable);
}
// if ::ReleaseMutex(m_mtxMonitor);// Process it only if there is an element that has // been picked upif (bRemoveFromQueue)
m_pHandler->OnProcessEvent( &element, m_pvParam );
elsebreak;
} // while
}

CCustomThread - To help manage the complexity of maintaining raw
threads I encapsulated all thread's related activities in an abstract class. It
provides a pure virtual method Run(), that must be implemented by
any specific thread class (e.g. CRetrievalThread and CProcessThreadMonitor).
CCustomThread is designed to ensure that thread function returns
when you want the thread to terminate as the only way to make sure that all
thread's resources are cleaned up properly. It offers a means to shut any of
its instances down by signaling a named event m_hShutdownEvent.

CCallbackHandler is an abstract class that has been designed to
provide interface for performing user-supplied actions when process is created
or terminated. It exposes a pure virtual method OnProcessEvent(),
which must be implemented according to the specific requirements of the system.
In the sample code you will see a class CMyCallbackHandler, that
inherits from CCallbackHandler and implements OnProcessEvent()
method. One of the parameters pvParam of OnProcessEvent()
method allows you to pass any kind of data, that's why it is declared as PVOID.
In the sample code a pointer to an instance of CWhatheverYouWantToHold
is passed to the OnProcessEvent(). You might want to use this
parameter to pass just a handle to a window, that could be used for sending a
message to it within OnProcessEvent() implementation.

Compiling the sample code

You need to have installed MS Platform SDK on your machine. Provided sample
code of the user-mode application can be compiled for ANSI or UNICODE. In case
you would like to compile the driver you have to install Windows DDK as well.

Running the sample

However, it is not a problem if you don't have Windows DDK installed,
since the sample code contains a compiled debug version of ProcObsrv.sys kernel
driver as well as it source code. Just place control program along with the
driver in single directory and let it run.

For demonstration purposes, the user mode application dynamically installs the
driver and initiates process of monitoring. Next, you will see 10 instances of
notepad.exe launched and later on closed. Meanwhile you can peek at the console
window and see how the process monitor works. If you want you can start some
program and see how the console will display its process ID along with its
name.

Conclusion

This article demonstrated how you can employ a documented interface for
detecting NT/2K process execution. However it is by far not the only one
solution to this issue and certainly might miss some details, but I hope
you would find it helpful for some real scenarios.

Comments and Discussions

What if the processes create/end so quickly that there is no time for the control program to get every one of the notifications from the driver? For example:
1.the driver calls KeSetEvent;
2.the Wait* function in control program returns;
3.the driver calls KeSetEvent once again before the control program get process information;
4.the control program calls DeviceIoControl to get process information.
So the problem is, the control program only get one piece of process information even though there is two notifications in the real world.

hi all thnx for this post i run the source on windows 8.1 64bit the driver is working fine the problem is when any process is created the user mode application not shown any message about process creation or deletion plz help me to solve this issue i spend lot of time by can not solve this thnx

Hello all.
I downloaded the project and tried to compile it using VS2010 on my windows 7 sp1 64bit.
I added the additional include and lib directories but i am getting lots of errors!
here is the error log http://pastebin.com/vfYNYzQS[^]

I was trying to get the code in the procObsrv.c file to work on 64 bit. So what i did was i installed WDK 7600.16385.1 on my win 7 64 bit. Then using win 7 build enviornment i built the procObsrv.c file in x64 free built enviornment. This gave me a procObsrv.sys file which was 64 bit compiled. I then digitally signed this file using dseo13b.exe. All this worked ok and the driver in successfully installed and started on 64 bit. But now

returns 0. I did a GetLastError which gives value 31 saying ERROR_GEN_FAILURE(A device attached to the system is not functioning.) which when mapped with NT CODES is STATUS_UNSUCCESSFUL as in http://support.microsoft.com/kb/113996. If you could help me in this please. How do i make the driver work successfully.

OutputBufferLenght = 12 and sizeof(PROCESS_CALLBACK_INFO = 24 that is why the if loop is not executing on 64 bit. How should this be resolved? this is a 64 bit problem as things work good on 32 bit..
Thanks for your help....

So callbackInfo size was 12 so the outputBufferLength = 12. The structure callbackInfo has DWORD datamember which for 64 bit had to be DWORD64. So in the threadMonitor.h file change structure as follows for 64 bit

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.

Yes, I said earlier, check this link :http://www.rohitab.com/discuss/topic/40632-how-can-i-compile-this-windows-project/?p=10094317
and that forum is a great place for things like this, there are very talented and smart guys there that can help you on driver developments and stuff like these
make sure you give that place a try.
Cheers/

The article is great I am new to this programming and have a question..
When a process is created, we get notification in the callback.Similarly when a process is terminated we get it in the callback function here.

What i want to do is, as soon as user closes the notepad.exe, i want to be notified and take a decision if i want it to be terminated or not...

You cannot do this using the approach described in the article. For 32-bit systems you can hook NtTerminateProcess and allow or block the operation. For 64-bit is a bit complicated as you are no longer allowed to modify system call table kernel structure.

I do not have Windows DDK installed...
i compliled the ConsCtl in visual studio 2005 and placed ProcObsrv.sys in the same folder.
10 notepads are opening up but the process information is not getting displayed.

Ever since the driver (ProcObsrv.sys) is using IoCreateNotificationEvent DDK API, I don't think (n I've tried) that it'd be able to run in non-administrator privilege, as stated in http://msdn.microsoft.com/en-us/library/aa490498.aspx

The driver creates a named event object in the \\BaseNamedObjects object directory. You can open a kernel-mode event named \\BaseNamedObjects\Xxx in user mode under the name Xxx. Note that security settings can prevent an application from opening the event. For more information, see the OpenEvent Fails in a Non-Administrator Account KB article. The \\BaseNamedObjects object directory is not created until the Microsoft Win32 subsystem initializes, so drivers that are loaded at boot time cannot create event objects in the \\BaseNamedObjects directory in their DriverEntry routines

even tho' in that article, also stated that it might be solved by using workaround here, http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q228785 , is there anybody around can implement this?

The code available for download is very old and quite outdated . I believe that you could sort this out by creating the event in your user mode application using CreateEvent Win32 API and then using a custom IOCTL you can pass the its handle to driver. Inside your IOCTL handler you just use ObReferenceObjectByHandle to obtain a kernel pointer to the user mode created event handle.

I am trying to use this process detection mechanism in a python script. To get this work I need a shared library(DLL) form this project.

I managed to rewrite the code in order to start an stop the monitoring-process(CApplicationScope is a global structure in my code). To do this I use the DLLMain-Function . I can compile the projcet and the driver. No problem so far.

Any time I load the DLL I end up in the following loop:
CustomThread.cpp: while (!GetIsActive()){}

It seems the thread will not be activated. But why? The untouched Project works fine on my Windows XP Professional machines. I am using Visual Studio 2008 Express Edition.

Is it not possible to build a shared library with this driver? Can anyone give me a hint how to make this work?

You cannot start a thread and expect it to be RUNNING from within the DllMain function. Your thread created within context of DllMain will be created, but suspended until the DllMain returns. That is probably why you are 'not active' during the DllMain.

It's using WMI. I've done it with COM. It's very reliable on XP but I'm yet to get it working on Vista. Have you tried your approach on Vista? I'll put the source code back up soon ( http://www.stetka.plus.com ). It's interesting to see your infoprocess site.

Not sure if it is a good way to do it, but i have recently been using a FileSystemWatcher class to monitor a directory. Configure it to watch for last access time and when a process gets run it triggers your event.

This only works if you know the directory and filename though obviously