Skip to content

fix(injector): verify injection via module list, not truncated thread exit code#43

Merged
xxxnpno merged 1 commit into
masterfrom
fix/injector-hmodule-truncation
Jun 24, 2026
Merged

fix(injector): verify injection via module list, not truncated thread exit code#43
xxxnpno merged 1 commit into
masterfrom
fix/injector-hmodule-truncation

Conversation

@xxxnpno

@xxxnpno xxxnpno commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Problem

inject_dll() used GetExitCodeThread(...) != 0 as the success oracle. A thread exit code is a DWORD — only the low 32 bits of LoadLibraryW's HMODULE. A module whose load base is 4 GiB-aligned (low dword 0) makes a successful injection report failure (false negative; it can't false-positive a real failure).

Fix

After the remote thread completes, confirm vmhook.dll is actually mapped into the target by scanning its module list for the DLL base name via K32EnumProcessModulesEx + K32GetModuleBaseNameW.

  • Both are exported by kernel32 (the K32* psapi forwarders) → no psapi.lib; injector.exe stays statically self-contained (KERNEL32 + msvcrt only).
  • The legacy exit-code test is kept only as a fallback for the rare case enumeration is unavailable (e.g. access denied) → behaviour is never worse than before.
  • Buffer grows once if the target loaded modules between the two enum calls.

Validation

  • Builds clean under the project -Werror mingw flags; links kernel32-only.
  • End-to-end exercised by the CI injection-based integration test on the Windows JVM cells (this PR's run).

Trial-branch per policy so master stays green during validation.

🤖 Generated with Claude Code

… exit code

inject_dll() returned `GetExitCodeThread(...) != 0` as the success oracle, but a
thread exit code is a DWORD — only the LOW 32 bits of LoadLibraryW's HMODULE.
A module whose load base is 4 GiB-aligned (low dword == 0) makes a SUCCESSFUL
injection report failure (false negative; cannot false-positive a real failure).

Fix: after the remote thread completes, confirm vmhook.dll is actually mapped
into the target by scanning its module list for the DLL's base name via
K32EnumProcessModulesEx + K32GetModuleBaseNameW (both exported by kernel32 — the
K32* psapi forwarders — so no psapi.lib link; injector.exe stays statically
self-contained: KERNEL32 + msvcrt only). The legacy exit-code test is retained
only as a fallback for the rare case enumeration is unavailable (e.g. access
denied), so behaviour is never worse than before. Buffer grows once if the target
loaded modules between the two enum calls.

Builds clean under the project -Werror mingw flags; links kernel32-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@xxxnpno xxxnpno merged commit e6b71d5 into master Jun 24, 2026
55 checks passed
@xxxnpno xxxnpno deleted the fix/injector-hmodule-truncation branch June 24, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant