How To Use madCodeHook 

www.madshi.net

On this page we'll work out together how to write a project which hooks an API system wide in both win9x and winNT families. Which API shall we hook? Hmmmm... Let's take a very system API, "TerminateProcess". We'll install a hook which protects our process from being terminated.

Before beginning with the work, let me say it loud and clear: I don't want madCodeHook to be used for illegal purposes! If you want to write a virus or a backdoor or whatever, please use something else. madCodeHook is used by several people for serious work and I don't want their work to be impacted negatively in any way. If you spread viruses which uses madCodeHook, some anti virus programs will condemn every single product, which uses madCodeHook. I don't want that to happen, so please use it only for serious and legal purpose. Thank you!

Okay, let's begin with the API hooking code. When hooking an API, we need to write some kind of hook callback function, which will later get called everytime someone tries to call the to-be-hooked API. Then we may need some way to call the original API from within our hook callback function. The most simple way to do so is to use a function variable. So let's have a look at a first easy chunk of code:

var OriginalTerminateProcess : function (processHandle, exitCode: dword) : bool; stdcall;

function TerminateProcessCallback(processHandle, exitCode: dword) : bool; stdcall;
begin
  result := OriginalTerminateProcess(processHandle, exitCode);
end;

Does that look alright to you? The "TerminateProcessCallback" function shall be our hook callback function. Inside the first version of our callback, we do nothing but call the original API. In order to enable us to handle all parameters as well as the result of the API correctly, we must use *exactly* the same function definition everywhere, which is also used by the original API - including the calling convention.

Now that we have a callback function and a "OriginalAPI" function variable, all we need to do is to install the hook. We do so by calling madCodeHook's HookAPI. But before doing that let's slightly rename our function variable. madCodeHook automatically builds up a full hooking queue. So in fact what we thought would be the original API function might just be another hook callback function. So let's rename "OriginalTerminateProcess" to "TerminateProcessNext".

var TerminateProcessNext : function (processHandle, exitCode: dword) : bool; stdcall;

function TerminateProcessCallback(processHandle, exitCode: dword) : bool; stdcall;
begin
  result := TerminateProcessNext(processHandle, exitCode);
end;

initialization
  HookAPI('kernel32.dll', 'TerminateProcess', @TerminateProcessCallback, @TerminateProcessNext);
end.

Okay, that should already work. Now let's fill our callback function, so that it actually does something. We want our own process to be protected from being terminated. So the first thing we have to do in our callback function is to check out which process is about to be terminated. Hmmmmm, the parameters give us a process handle. But which process hides behind that handle? Fortunately, madCodeHook exports two useful functions. With ProcessHandleToId we can convert the process handle to an ID, and with ProcessIdToFileName we can get the process' file name. If the file name is identical to our application's file name, we have to prevent the TerminateProcess call! Otherwise we let it pass:

function ThisIsOurProcess(processHandle: dword) : boolean;
var pid   : dword;
    arrCh : array [0..MAX_PATH] of char;
begin
  pid := ProcessHandleToId(processHandle);
  result := (pid <> 0) and ProcessIdToFileName(pid, arrCh) and
            (PosText('OurApplication.exe', arrCh) > 0);
end;

function TerminateProcessCallback(processHandle, exitCode: dword) : bool; stdcall;
begin
  if ThisIsOurProcess(processHandle) then begin
    result := false;
    SetLastError(ERROR_ACCESS_DENIED);
  end else
    result := TerminateProcessNext(processHandle, exitCode);
end;

This should actually work quite nicely. However, madCodeHook API hooks normally show effect only in the current process. That means, if another process calls TerminateProcess, we're lost. But the solution is quite easy. First, let's put all of our code into a little DLL like this:

library TPHook;

uses Windows, madRemote, madCodeHook, madStrings;

var TerminateProcessNext : function (processHandle, exitCode: dword) : bool; stdcall;

function ThisIsOurProcess(processHandle: dword) : boolean;
var pid   : dword;
    arrCh : array [0..MAX_PATH] of char;
begin
  pid := ProcessHandleToId(processHandle);
  result := (pid <> 0) and ProcessIdToFileName(pid, arrCh) and
            (PosText('OurApplication.exe', arrCh) > 0);
end;

function TerminateProcessCallback(processHandle, exitCode: dword) : bool; stdcall;
begin
  if ThisIsOurProcess(processHandle) then begin
    result := false;
    SetLastError(ERROR_ACCESS_DENIED);
  end else
    result := TerminateProcessNext(processHandle, exitCode);
end;

begin
  HookAPI('kernel32.dll', 'TerminateProcess', @TerminateProcessCallback, @TerminateProcessNext);
end.

And now in our application let's just inject this DLL into all processes system wide. That's very easy by using madCodeHook again:

program OurApplication;

uses Windows, madCodeHook;

begin
  InjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'TPHook.dll');
  MessageBox(0, 'You can''t call TerminateProcess on me!', 'Boasting...', MB_ICONINFORMATION);
  UninjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'TPHook.dll');
end.

Yeah, that was all you need to do already! Isn't that easy? However, when testing this stuff, please note that the win9x task manager doesn't use the TerminateProcess to terminate processes. So please use something else to test the hook. In the NT family the task manager uses TerminateProcess when you terminate a process on the "processes" tab, but not when you do it via the "applications" tab.