Skip to content

Library Injection

brkzlr edited this page Jul 17, 2026 · 5 revisions

Library injection loads your own compiled code into the attached process. Use it when a normal memory edit is not enough and you want code running inside the target itself.

Common reasons to use it:

  • Hooking functions from inside the process
  • Calling game or engine code from your own helper code
  • Logging internal state that is annoying to inspect from outside
  • Applying patches that need more logic than a few overwritten bytes

For simpler stuff like changing values, NOPing instructions or making a small code cave, Libpince Engine is usually easier than building an external library.

PINCE supports two injection paths:

  • .so injection: Linux-side dlopen, works with native Linux processes and WINE/Proton processes
  • DLL injection: Windows-side LoadLibraryW, only works with WINE/Proton processes

How to Access It

Open the Memory Viewer window and use the Tools menu:

  • Inject .so file: injects a Linux shared object into the target
  • Inject DLL file: injects a Windows DLL into a WINE/Proton target

Before Injecting Anything

Your code runs inside the target process. If your constructor, hook or DllMain crashes, the target probably crashes too.

Keep this in mind:

  • Match the loader architecture. A DLL must match the Windows PE bitness, while a .so must match the Linux-side inferior bitness. Under New-WoW64, this normally means a 32-bit DLL but a 64-bit .so
  • Avoid doing heavy work directly in a .so constructor or DllMain. Start a thread or do the minimum setup if you need anything complicated
  • Make sure the target can access the file path. PINCE can see the file from your desktop session, but the target may be inside a sandbox or a WINE prefix with different path rules
  • Do not inject random libraries you do not trust. This is code execution inside the target process

.so Injection

Use this for Linux shared objects.

Basic steps:

  1. Attach to a process.
  2. Open Tools -> Inject .so file.
  3. Select a .so file.
  4. PINCE calls dlopen in the target process.

If it succeeds, PINCE shows:

The shared object (.so) file has been injected

If your .so has a constructor like this, it runs when dlopen loads the library:

__attribute__((constructor))
static void init(void) {
    /* your setup */
}

If it fails, PINCE shows:

Failed to inject the shared object (.so) file

That failure is generic. It can mean dlopen could not be resolved, the file could not be loaded, the wrong architecture was used, a dependency was missing or the constructor failed.

.so Technical Details

PINCE first asks GDB to call one of these in the target:

  • dlopen(path, 2)
  • __libc_dlopen_mode(path, 2)

The 2 is RTLD_NOW, meaning symbols are resolved immediately.

If GDB cannot resolve those symbols, PINCE falls back to manual symbol lookup:

  • Reads /proc/<pid>/maps
  • Looks for libc, libc-musl, ld-musl or libdl
  • Resolves sandboxed mapped paths through /proc/<pid>/root
  • Parses the ELF .dynsym / .dynstr tables on disk
  • Calls the resolved dlopen address with a temporary interruption

This fallback handles 32-bit and 64-bit ELF files, little endian and big endian.

DLL Injection

Use this for Windows DLLs in WINE/Proton targets.

Basic steps:

  1. Attach to a WINE or Proton process
  2. Open Tools -> Inject DLL file
  3. Select a .dll file
  4. PINCE converts the Linux path to a WINE path and starts a bootstrap that calls LoadLibraryW

If you try this on a native Linux process, PINCE shows:

DLL injection is only supported for WINE/Proton processes

If setup succeeds, PINCE shows:

DLL injection thread started. The DLL may still fail to load due to bitness mismatch or missing dependencies.

This only confirms that PINCE started the injection path. It does not wait for LoadLibraryW to finish or verify a returned module handle. If the Windows loader succeeds, your DLL's DllMain runs with DLL_PROCESS_ATTACH like a normal DLL load.

DLL Technical Details

DLL injection is more involved than .so injection because GDB cannot resolve Windows PE exports by name.

PINCE first:

  1. Finds mapped .exe and DLL images in /proc/<pid>/maps
  2. Determines the Windows bitness from the main executable's PE header
  3. Parses the in-memory PE exports of kernel32.dll, kernelbase.dll and ntdll.dll
  4. Waits up to 10 seconds for a target thread to reach NtWaitForSingleObject or NtDelayExecution, providing a safe Windows-side context for setup calls

For pure 32-bit and 64-bit WINE processes, PINCE resolves LoadLibraryW, VirtualAlloc, VirtualFree, CreateThread and CloseHandle. It allocates one Windows-side buffer for the UTF-16LE path and bootstrap code, then starts the bootstrap with CreateThread. The bootstrap calls LoadLibraryW, closes its thread handle and frees the buffer.

For a 32-bit target under New-WoW64, the GDB inferior is 64-bit while the Windows program is 32-bit. PINCE uses the 64-bit NtAllocateVirtualMemory to allocate memory reachable by guest code, finds the 32-bit guest context with RtlWow64GetCurrentCpuArea, then redirects the guest EIP through a 32-bit LoadLibraryW helper before returning to the original code. This path does not create a Windows thread.

The path conversion is simple:

/home/user/mod.dll -> Z:\home\user\mod.dll

That assumes the WINE Z: drive maps to /, which is the default setup.

During DLL injection, PINCE sets an internal driving_inferior flag. This suppresses normal stop/run UI updates and blocks user pause/continue/step controls while PINCE is steering the target.

Common Failure Reasons

If injection fails, check these first:

  • The target is not attached anymore
  • The DLL does not match the Windows PE bitness or the .so does not match the Linux-side inferior bitness
  • The file exists for PINCE, but not for the target process namespace
  • A .so dependency is missing from the target environment
  • The .so constructor or DLL DllMain crashed during load
  • dlopen, LoadLibraryW, malloc or VirtualAlloc could not be resolved
  • The WINE Z: drive does not map to /
  • The WINE target did not hit NtWaitForSingleObject or NtDelayExecution within 10 seconds
  • The target is paused in a bad state for making function calls

For WINE/Proton DLL injection, also make sure kernel32.dll and ntdll.dll are mapped. They should be in normal WINE processes, but if they are missing or the export tables cannot be read, PINCE cannot call LoadLibraryW.

.so vs DLL

Use .so injection when your payload is Linux code. This is often useful even for WINE/Proton games if you want to hook Linux-side libraries, libc calls or WINE itself.

Use DLL injection when your payload is Windows code and should live in the Windows side of the WINE process. This is the path you usually want for Windows game mods or Windows hooks.

They are not interchangeable. A .so is loaded by Linux dlopen. A .dll is loaded by WINE's Windows loader through LoadLibraryW.

Clone this wiki locally