Search code examples
c#dllloadlibraryeasyhook

Easyhook: How to hook a function from a DLL loaded with LoadLibrary


I have been playing with EasyHook for a while now and have been very successfull with statically linked DLLs. Now I tried to hook a function from a DLL that is dynamically loaded from the Host Application using the same approach as with the statically linked DLLs.

In this scenario, the hook failed to work. I got the following exception when trying to create the hook:

System.DllNotFoundException: The given library is not loaded into the current process.

The Exception is very correct in stating that the library is not yet loaded, but the Host/hooked Process is about to load it in a few ns/ms after it started (which totally doesn't matter).

The tutorials and the results from my searches on the internet only covered hooking a statically linked DLL. I haven't found anything about dynamically loaded DLLs. One solution that comes to mind: Hook LoadLibrary and GetProcAddress and wait for the right winapi call to do the desired replacement.

Is there any other/an easier way to hook functions from a dynamically loaded DLL?

There is one constraint: The external program cannot be changed to use the DLL in a static way.


To facilitate a possible solution, here are some snippets that show what I want to hook:

First, this is the DLL with the AddIntegers function that I want to replace (Code is in Delphi)

library Calculate;

function AddIntegers(_a, _b: integer): integer; stdcall;
begin
  Result := _a + _b;
end;

exports
   AddIntegers;

begin
end.

Second, this is the program using the above DLL using the exported AddIntegers function.

program HostConsole;
{$APPTYPE CONSOLE}

uses
  Winapi.Windows, System.SysUtils;

var
  n1, n2, sum: Int32;

  // Variables for DLL Loading
  h: HMODULE;
  AddIntegers: function(_a, _b: integer): integer; stdcall;

begin
  try
    // Load Library
    h := LoadLibrary('Calculate.dll');
    if h = 0 then
    begin;
      raise Exception.Create('Cannot load DLL');
    end;

    // Load function
    AddIntegers := GetProcAddress(h, 'AddIntegers');

    if not Assigned(AddIntegers) then
    begin
      raise Exception.Create('Cannot find function');
    end;

    Write('Enter first number: ');
    Readln(n1);

    Write('Enter second number: ');
    Readln(n2);

    // To the calculation
    sum := AddIntegers(n1, n2);

    Writeln('The sum is ', sum);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  // Unload Library
  FreeLibrary(h);

  Readln;

end.

Solution

  • It took me some time, but I finally figured it out: You can only hook something when it is there. As long as a module isn't loaded, there is no way for you to hook it.

    How do modules get loaded?

    As from the code in the question LoadLibrary() makes the module available. This means, in order to have the first point in time when a module becomes available, you need to hook LoadLibrary()!

    Hooking LoadLibrary()

    In case someone is looking for a way to call the function, here is one possible way:

    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
    delegate IntPtr LoadLibrary_Delegate(string lpFileName);
    
    
    [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern IntPtr LoadLibrary(string lpFileName);
    
    IntPtr LoadLibrary_Hook(string lpFileName)
    {
        IntPtr result = LoadLibrary(lpFileName);
    
        if (lpFileName == "<FILENAME HERE>")
        {
            // Apply hook
        }
        return result;
    }
    

    Now that you know when a library is loaded, you can hook its functions. You can also now hook any function that is statically loaded alongside the checked library.

    Hint: In my actual use case, the .dll is loaded right after startup and will be freed once the application terminates. If the library is loaded and unloaded several times, you should check for memory leaks. Yes, EasyHook might be unaware that a hooked library is unloaded.