Bypassing executables

Bypassing executables

Post by emildotche » Thu, 16 Feb 2006 08:10:37


I've been researching this topic for a while, I found many posts from
people trying to resolve similar problems but I am yet to find a
solution that truly works (e.g. is not just an idea.)

I need to prevent specific programs from executing *after* there is a
request for their execution. I don't need this for the purposes of
controlling access to executables, so a solution that is based on
access rights and simply prevents the user from being able to start a
program is not applicable.

What I need is to intercept the process creation sequence, and if
necessary interfere with it so that the process terminates cleanly, as
if the first thing it did is to call ExitProcess.

I can think of two general ways this can be accomplished:

1) Somehow kill the process after it was created but before it could
manage to do anything.

2) Somehow make the system start another executable instead of the
requested one. That is, when the user attempts to run program A, run
program B instead.

Some people have suggested that 1) can be done in the
PsSetLoadImageNotifyRoutine callback, by calling ZwTerminateProcess if
needed. I tried to implement this strategy, but ZwTerminateProcess
returns INVALID_HANDLE (I am calling it with the handle passed to the
callback function).

I also read this post: #9e362165ce82969d

It suggests replacing the man thread function with ExitProcess. While I
haven't tried it, thread functions and ExitProcess have different
arguments and I would think that swapping one for the other is at least
not portable.

My preference would be to get 2) to work. One reason is that when the
requested program should not run, what I really want is to create the
implession that it ran and returned whatever exit code I choose. Being
able to quietly substitute my own executable puts me in control, so I
can do whatever I choose.

Has anyone done something similar? While I do want to find a working
solution, I'd appreciate any ideas or suggestions, even if they're not
tested in practice.


Bypassing executables

Post by James Brow » Thu, 16 Feb 2006 19:18:53

lt; XXXX@XXXXX.COM > wrote in message
news: XXXX@XXXXX.COM ...

PsSetLoadImageNotify doesn't seem like the right way. It gets called when a
DLL/EXE is mapped into memory, not when a process has been created. As such
I wouldn't have thought the appropriate kernel-process structures would be
in the right state when this is called. Have you looked at the
PsSetCreateProcessNotifyRoutine function? seems more logical to me.

There is also an interesting registry location:

HKLM\Software\Microsof\Windows NT\CurrentVersion\Image File Execution

You can put a specific executable-name under this registry-path, and set the
debugger to be used when the exe loads. Using this key it is possible to
intercept the loading of a specific executable with a 'non-debugger' - i.e.
a program of your choosing, when gets executed *instead* of the real exe. It
is the choice of the 'debugger' what it does next - it can load+debug the
real executable or it could simply do nothing+exit.

Lots of information on google about this registry-location. The best thing
about this, is that it is entirely user-mode and requires no kernel-hacking.


Microsoft MVP - Windows SDK
Free Win32 Source and Tutorials


Bypassing executables

Post by emildotche » Fri, 17 Feb 2006 04:07:40

Hi James,

It is my understanding that PsSetCreateProcessNotifyRoutine is called
before the image is mapped into memory. In other words, at the time the
process is created and passed to the callback of
PsSetCreateProcessNotifyRoutine, it is not known which executable will
be mapped "into" that process (well, maybe the OS "knows" but this
information is not passed to the callback.) So when the CreateProcess
callback is called, I don't have any means to determine whether I want
the process to be terminated, or not. On the other hand, the LoadImage
callback is passed both info about the executable as well as the
process which was created for it. Please do correct me if I am wrong.

I'll look up the info on the registry location you mentioned, it does
seem as promising alternative.


Bypassing executables

Post by emildotche » Fri, 17 Feb 2006 05:01:37

Sorry I was too quick with that first reply. :)

The ImageFileExecutionOptions registry location will not work for me
because it would require registration of all executables I am
interested in, and the thing is I don't know them beforehand. I
apologize for not providing this info earlier, but what I am trying to
resolve is this:

At some point in time, my own program begins execution and is given the
path to another executable to start. What I need to do is monitor the
process tree created by that executable, and for each child process do
one of the following:

1. Allow a child process to execute, or

2. Make the parent process believe that the child process ran and
exited normally with exit code of my choosing.

Note, I don't know what programs will be executed, and in turn, I don't
know what programs the executed programs will execute, etc. So I can't
register them beforehand in the ImageFileExecutionOptions.

At first I thought I can use the debugging API, which would notify me
when the "debugee" creates processes, but reading through the docs I
couldn't find anything that would allow me to interfere and not allow a
child process to run, while making the parent process believe the child
did run.

Once again, thanks very much for the help.

Bypassing executables

Post by James Brow » Fri, 17 Feb 2006 07:28:59

ok...I'm not too familiar with the PsSet_xxx kernel routines. I advise going
to and subscribing to the private nt-developer forums they
have there - this type of question has been answered in the past.


Microsoft MVP - Windows SDK
Free Win32 Source and Tutorials

Bypassing executables

Post by Carl Woodw » Fri, 17 Feb 2006 16:48:34

> Some people have suggested that 1) can be done in the

ZwTerminateProcess by no means terminates the process cleanly, this is a
particular problem if a thread has begun executing in the process already.
That said, if you choose to call ZwTerminateProcess in your image notify
routine before ntdll is mapped (i.e. the second invokation) then no thread
will have executed (the first code to execute is the APC that eventually
calls ntdll!LdrpInitializeProcess) then ZwTerminateProcess might work for
you. I have noticed that performing certain operations in the load image
notify routine can deadlock the machine, you need to be really carefull
about what you do in there. One example is that ZwAllocateVirtualMemory can
deadlock forever waiting on a fast mutex. You may have similar problems with
ZwTerminateProcess because you are trying to tear down a process whilst the
creating thread is still inside of ZwCreateProcessEx (because at that time
the process address space has been created, as has the EPROCESS) but the
section object for the primary executable and ntdll have not yet been
mapped, this is as far as I know part of ZwCreateProcessEx's functionality).

The reason you are getting STATUS_INVALID_HANDLE is because you are trying
to terminate using the ProcessId from the load image callback routine, this
isn't a handle (although the docs say it is). To get around this, you must
open the process first:

CLIENT_ID ClientId = {0};
OBJECT_ATTRIBUTES ObjectAttributes = {0};
HANDLE ProcessHandle = NULL;

ClientId.UniqueProcess = ThreadContext->ProcessId;
InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, 0,
Status = ZwOpenProcess(&ProcessHandle, PROCESS_ALL_ACCESS,
&ObjectAttributes, &ClientId);
if (!NT_SUCCESS(Status))

Status = ZwTerminateProcess(ProcessHandle, 0);
if (!NT_SUCCESS(Status))

Status = ZwClose(ProcessHandle);
if (!NT_SUCCESS(Status))

Finally, I have observed that the load image notify routine can be called at
both PASSIVE_LEVEL and APC_LEVEL, therefore calling ZwOpenProcess and indeed
any ZwXxxxXxxxxx routine is not safe at any IRQL other than PASSIVE_LEVEL. I
suggest you create a system thread or work item to terminate the process,
this might keep the system up and running, although you may have to wait
until the process is fully set up