Dll Injection 



If you want to hook some APIs in another process (e.g. Notepad) or if you want to do system wide API hooking, you have to write a little DLL which does all the hooking work. This DLL then needs to be loaded into the target process(es) to do its work there. There's no official win32 API to inject a DLL into another process. Windows does offer some ways to inject DLLs, but they all come with their own share of problems.

Just to mention one, "SetWindowsHookEx()" is often (mis)used for injecting DLLs, but doing so has the following disadvantages: (1) It effects performance, because you have to set up a real message hook, which then gets called all the time, although you are not interested in the results at all. (2) It works only for processes which handle messages, not all processes do so. For example most console applications don't. (3) It works only if the target process is not blocked, frozen or crashed. (4) The DLL might be loaded into the target process later than expected. As a result you might miss some important API calls. (5) It doesn't work for system processes.

madCodeHook offers various injection related APIs, which work without any of the mentioned disadvantages.

madCodeHook APIs for injecting a DLL into one specific process

The "CreateProcessEx" function basically works exactly like the well known Windows API "CreateProcess". But it has one additional parameter that lets you define a DLL which you want to have injected into the to-be-started process. When called, "CreateProcessEx" starts the specified process, but patches it in such a way, that it behaves as if it had a "LoadLibrary" call right in the first line of it's source code.

A 64bit process can use this API to start both 32bit and 64bit processes. Unfortunately due to some limitations in the win32 API, this API doesn't allow you to create a 64bit process from within a 32bit process. The underlying win32 API "CreateProcess" itself supports starting a 64bit process, but the DLL injection part doesn't work, because a 32bit process only has limited access to a 64bit process. So if you want to use CreateProcessEx for starting a 64bit process, your own process must be 64bit, too.

The bitdepth of the hook DLL *always* needs to match the bitdepth of the target process. Otherwise CreateProcessExA/W will fail.

// start a process and inject your hook DLL into it
function CreateProcessExA/W (applicationName, commandLine : PAnsi/WideChar;
                             processAttr, threadAttr      : PSecurityAttributes;
                             inheritHandles               : bool;
                             creationFlags                : dword;
                             environment                  : pointer;
                             currentDirectory             : PAnsi/WideChar;
                             const startupInfo            : TStartupInfoA/W;
                             var processInfo              : TProcessInformation;
                             loadLibrary                  : PAnsi/WideChar     ) : boolean;

The function "InjectLibrary" is able to inject your DLL into one specific already running 32bit or 64bit process. You can inject 32bit DLLs into 32bit processes and 64bit DLLs into 64bit processes. Don't worry, you can't do anything wrong. madCodeHook will simply refuse to inject a DLL with a non-matching bitdepth.

If you call this API from inside a 32bit process, you cannot inject 64bit processes. If you call this API from inside a 64bit process, you can inject hook DLLs into both 32bit and 64bit processes. The bitdepth of the DLL and the target process must always match, though.

// inject a DLL into one specific process
function (Un)InjectLibraryA/W (libFileName   : PWide/AnsiChar;
                               processHandle : dword;
                               timeOut       : dword = 7000) : boolean;

madCodeHook APIs for system wide DLL injection

You can also inject your hook DLL system (or session) wide. This works only if you have administrator rights, though. System wide DLL injection generally consists of two separate parts:

(1) Injection into already running processes and
(2) automatic injection into newly created processes.

Automatic injection into newly created processes is handled by a little kernel mode driver. This driver is available as an external file (or rather 2 files, one for 32bit OSs and one for 64bit OSs). You need to configure this driver and sign it afterwards, otherwise it won't work. After you've done that, your program needs to "activate" the driver by using the following APIs, all of which need admin rights:

// (de)activate the injection driver

// option 1: dynamically load the driver without installing it
// the driver will be loaded and start running at once
// it will stay active only until the next reboot
// you may prefer this technique if you want your product to not require any
// installation and uninstallation procedures
// loading the same driver twice fails with ERROR_SERVICE_ALREADY_RUNNING
function LoadInjectionDriver (driverName, fileName32bit, fileName64bit: PWideChar) : bool; stdcall;

// option 2: permanently install your injection driver
// after installation it will automatically start running at once
// it will survive reboots, it will stay installed until you uninstall it
// for a clean uninstall call StopInjectionDriver first, then Uninstall
// installing the same driver twice fails succeeds and updates all parameters
function   InstallInjectionDriver (driverName, fileName32bit, fileName64bit, description: PWideChar) : bool; stdcall;
function UninstallInjectionDriver (driverName: PWideChar) : bool; stdcall;

// stopping the driver may not work, depending on how it was configured
// call Stop + Start to update an installed driver to a newer version
// call Stop + Load  to update a  loaded    driver to a newer version
function  StopInjectionDriver (driverName: PWideChar) : bool; stdcall;
function StartInjectionDriver (driverName: PWideChar) : bool; stdcall;

// checks whether the specific injection driver is installed
// returns true  if you used InstallInjectionDriver
// returns false if you used LoadInjectionDriver
function IsInjectionDriverInstalled (driverName: PWideChar) : bool; stdcall;

// checks whether the specific injection driver is running
// returns true if you used InstallInjectionDriver and
//              if the driver is running (not paused / stopped)
// returns true if you used LoadInjectionDriver
function IsInjectionDriverRunning (driverName: PWideChar) : bool; stdcall;

// Example:
success := LoadInjectionDriver('yourFancyDriverName', 'your32.sys', 'your64.sys');

Once the injection driver has been activated (see above), you can call the following APIs to inject your hook DLL system or session wide. If you call these APIs from within a 32bit process, you can only inject 32bit hook DLLs. If you call these APIs from within a 64bit process, you can inject both 32bit and 64bit DLLs.

// inject a DLL into all running processes

  // flags for injection/uninjection "session" parameter
  ALL_SESSIONS    : dword = dword(-1);
  CURRENT_SESSION : dword = dword(-2);

  // flags for injection/uninjection "options" parameter
  INJECT_PERMANENTLY      = $0002;  // shall the DLL stay after reboot?
  INJECT_METRO_APPS       = $0004;
  INJECT_ALLOW_THREAD     = $0010;  // allow a secondary thread to load the hook dll?

  // flags for inject approval callbacks (see below)
  TARGET_PROCESS_IS_64BIT         : dword = $00000001;
  TARGET_PROCESS_IS_SYSTEM        : dword = $00000002;
  TARGET_PROCESS_IS_ELEVATED      : dword = $00000004;
  TARGET_PROCESS_IS_PROTECTED     : dword = $00000008;
  TARGET_PROCESS_IS_NEWLY_CREATED : dword = $00000010;

  // callback approval definition for system/user wide dll injection
  TInjectApprovalCallbackRoutine = function (
    context     : pointer;    // you can use this for whatever purpose you like
    processId   : dword;      // injection target process ID
    parentId    : dword;      // which process started the target process?
    sessionId   : dword;      // to which session does the process belong?
    flags       : dword;      // see TARGET_PROCESS_IS_XXX flags (defined above)
    imagePath   : PWideChar;  // exe file name/path
    commandLine : PWideChar   // full command line
  ) : bool; stdcall;

// (un)injects a library to/from all processes of the specified session(s)
// driverName:      name of the driver to use
// libFileName:     full file path/name of the hook dll
// session:         session id into which you want the dll to be injected
//                  "-1" or "ALL_SESSIONS" means all sessions
//                  "-2" or "CURRENT_SESSION" means the current session
// options:         various options, see INJECT_xxx flags
// includeMask:     list of exe file name/path masks into which the hook dll shall be injected
//                  you can use multiple masks separated by a "|" char
//                  you can either use a full path, or a file name, only
//                  leaving this parameter empty means that all processes are "included"
//                  Example: "c:\program files\*.exe|calc.exe|*.scr"
// excludeMask:     list of exe file name/path masks which shall be excluded from dll injection
//                  the excludeMask has priority over the includeMask
//                  leaving this parameter empty means that no processes are "excluded"
// excludePIDs:     list of process IDs which shall not be touched
//                  when used, the list must be terminated with a "0" PID item
// callback:        will be called for each potential to-be-injected-into process
//                  callback decides whether to perform injection or not
//                  if callback doesn't reply within 5 sec timeout, a "true" reply is inferred
//                  caution: slow callback results in new processes starting delayed
// callbackContext: this pointer is forwarded to your callback function
//                  use for any purpose you like
function (Un)InjectLibraryA/W (driverName      : PAnsi/WideChar;
                               libFileName     : PAnsi/WideChar;
                               session         : dword;
                               options         : dword;
                               includeMask     : PAnsi/WideChar = nil;
                               excludeMask     : PAnsi/WideChar = nil;
                               excludePIDs     : TPCardinal     = nil;
                               callback        : TInjectApprovalCallbackRoutine = nil;
                               callbackContext : pointer        = nil;
                               timeOut         : dword          = 7000) : bool; stdcall; overload;

// uninjects every currently active system/session wide injection
function UninjectAllLibrariesA/W (driverName         : PAnsi/WideChar;
                                  excludePIDs        : TPCardinal = nil;
                                  timeOutPerUninject : dword      = 7000) : bool; stdcall;

// Example:
// we inject both a 32bit and a 64bit hook DLL
// let madCodeHook worry about choosing the right DLL for any given process
// this will only work if you call this from within a 64bit process
// you can't inject 64bit hook DLLs from within a 32bit process
InjectLibraryA('yourFancyDriverName', 'your32.dll', CURRENT_SESSION, false);
InjectLibraryA('yourFancyDriverName', 'your64.dll', CURRENT_SESSION, false);