Repository navigation
Description
Windows: Process.mainModule returns a system DLL in 17.24.1
Summary
On the tested Windows x64 environment, Process.mainModule identifies a system
DLL instead of the executable. An independent native oracle and Frida's explicit
name/address lookups agree on the executable base. The executable is present
elsewhere in Process.enumerateModules().
Environment
- Frida 17.24.1, official Node-API v8 Windows x64 addon; QuickJS runtime
- Node 24.22.0; Windows kernel release 10.0.26300, x64
- Original synthetic C++ fixture, LLVM-MinGW 20261006 / Clang 23.1.3
- Local-device attachment to a child created by the included controller
Reproduction
The attached source-only archive includes a small executable source, agent,
Node controller, dependency lockfile and build instructions. It does not depend
on a proprietary target. On Windows x64, from the extracted directory:
x86_64-w64-mingw32-clang++ -std=c++17 -O2 -Wall -Wextra -Werror -static fixture.cpp -lpsapi -o frida-identity-fixture.exe
npm ci
node reproduce.mjs > result.jsonBefore attachment, the fixture reports GetModuleHandleW(NULL) and the first
EnumProcessModules() handle. The controller attaches only to that child PID.
The agent compares Process.mainModule, explicit name/address lookups and an
in-agent GetModuleHandleW(NULL) with the pre-attachment executable base. The
fixture repeats the native observation during attachment. The controller then
unloads, detaches and checks normal child exit. No user-created Interceptor
hooks are installed and the script does not write target memory.
Expected
Process.mainModule.base equals the executable handle returned by
GetModuleHandleW(NULL); its name is frida-identity-fixture.exe.
Actual
In the original case with the Node executable, Frida package, target executable
and target working directory all on local Windows paths:
{
"reported_main": "win32u.dll",
"main_matches_native": false,
"named_matches_native": true,
"addressed_matches_native": true,
"native_api_matches_before": true,
"main_is_first_enumerated": true,
"native_base_enumeration_index": 23,
"native_identity_stable": true,
"native_enumeration_first_matches": true,
"exit_code": 0
}The same mismatch occurred in all 16 combinations of local Windows / WSL UNC
paths for those four factors, using identical binaries. The selected DLL was
win32u.dll or GDI32.dll; explicit lookups and native oracles agreed in every
case. All fixtures exited normally. The entirely local case demonstrates that
UNC placement is not necessary. These tests were launched through WSL interop;
an independent run on a machine without WSL has not been performed.
matrix-summary.json contains selected observations from those original runs.
standalone-result.json records a separate follow-up using the shortened,
path-free fixture and controller included in the attachment. It is an actual
execution result, not reconstructed expected output. DLL names and enumeration
indices can vary.
Suspected source explanation
The observed behavior matches this source chain:
- In 17.24.1 Windows module synchronization,
new modules are collected by iterating a hash-table snapshot of loaded handles. - The common module registry
appends registered modules to its enumeration array. - Main-module selection
uses the Windows collector,
which accepts the first enumerated entry, then caches that result.
The likely triggering change is
f8d4782, windows: Track dependency DLLs in module registry.
The 17.23.2 source
instead seeded the registry in native enumeration order. This is a source-based
regression hypothesis: no runtime bisection or upstream fix validation has been
performed, and no assertion about other released versions is intended.
Selecting the Windows main module by its native executable handle might avoid
the ordering dependency. Preserving native order while calculating registry
deltas might also work; neither correction has been implemented or tested here.
Impact / workaround
Scripts deriving offsets from Process.mainModule.base can calculate addresses
in the wrong image. Explicit name lookup validated against
GetModuleHandleW(NULL) worked in the tested cases. This r
4789
eport establishes an
identity/correctness failure; it does not claim an exploit or privilege bypass.
Activity