skip to content
secrary[dot]com

Make Your Dynamic Module Unfreeable (Anti-FreeLibrary)

/

When your product injects a module into a target process, the target process can call the FreeLibrary function to unload your module, assuming the reference count is one.

One way to remain injected is to hook the FreeLibrary function and check the arguments passed each time the target process calls it. However, there is an alternative method that does not require hooking.

When a process uses FreeLibrary to free a loaded module, it calls LdrUnloadDll, which is exported by ntdll:

kernel_ntdll

Inside the LdrUnloadDll function, it checks the ProcessStaticImport field of the LDR_DATA_TABLE_ENTRY structure to determine if the module is dynamically loaded.

This check occurs within the LdrpDecrementNodeLoadCountLockHeld function:

call_stack ProcessStaticImport_Ghidra

If the ProcessStaticImport field is set, LdrpDecrementNodeLoadCountLockHeld returns without freeing the loaded module.

struct

By setting the ProcessStaticImport field, FreeLibrary will be unable to unload our module:

poc

In this scenario, the module prints "Hello" every time it attaches to a process and "Bye!" when it detaches.

Note: There is an officially supported method to achieve similar results: calling GetModuleHandleExA with the GET_MODULE_HANDLE_EX_FLAG_PIN flag. This ensures that the module remains loaded until the process is terminated, regardless of how many times FreeLibrary is called.

Thanks to James Forshaw for the insights.