SysAnalyzer is a 32-bit Visual Basic 6 application. The Win32 API surface a 32-bit process can use against a 64-bit target is restricted — EnumProcessModules, VirtualQueryEx, ReadProcessMemory, and CreateRemoteThread all behave differently or fail outright across the bitness boundary. To handle 64-bit targets transparently, SysAnalyzer ships a small native 64-bit helper executable that runs as a child process and feeds results back through stdout.
x64Helper.exe is a self-contained 64-bit command-line tool. It is invoked by SysAnalyzer through the Cx64 wrapper class, which captures stdout via the GetCommandOutput helper (15- or 25-second timeout) and parses CSV lines. Most users never see it directly, but it can be run from a shell for ad-hoc 64-bit inspection.
The helper enables SeDebugPrivilege on startup and runs a 13-second internal watchdog thread to prevent indefinite hangs during malware-triggered edge cases.
x64Helper.exe /command [args...]
Forward slashes and dashes are interchangeable (-inject behaves the same as /inject). All commands write CSV or status lines to stdout; the calling code looks for Error: as the failure prefix.
| Command | Args | Action |
|---|---|---|
| /inject | decimal_pid dll_path | Inject the specified DLL into a running process via CreateRemoteThread(LoadLibraryA). Pre-loads the API-logger dependency DLLs first (advapi32, ole32, oleaut32, wininet, urlmon, ws2_32) so the injected DLL's DllMain can resolve hook targets without calling LoadLibrary under loader lock. |
| /startwdll | exe_path dll_path [args...] | Launch a process suspended, pre-load the API-logger dependency DLLs, inject the given DLL, then resume. The exe is launched with STARTF_USESHOWWINDOW + SW_SHOWNORMAL + CREATE_NEW_CONSOLE so the sample's windows are visible regardless of whether SysAnalyzer launched the helper hidden. Optional args are folded into the child's command line, with quoting added for any args containing spaces. |
| /loadlib | file_path [export [cdecl]] | LoadLibrary the given DLL into the helper process itself, optionally calling a named export. Without cdecl the export is invoked as __stdcall. Used by SysAnalyzer for "launch a 64-bit DLL directly" from the wizard's Executable field. |
| /dlls | decimal_pid | Enumerate modules loaded into the target. Output is CSV: base,size,path, one module per line. Hex base/size, decimal-formatted size suffix. |
| /dumpprocess | decimal_pid out_file_path | Dump the main module image of the target. Resolves the base via EnumProcessModulesEx, then writes SizeOfImage bytes to disk in 10-MB chunks (avoids large-allocation hangs). |
| /dumpmodule | decimal_pid hex_base hex_size out_file_path | Dump an arbitrary memory range. Same chunking behavior. Refuses to overwrite an existing file. |
| /memmap | decimal_pid [out_file_path | -c] (or pid: -32, -64, -1) | Walk the address space via VirtualQueryEx and emit CSV: va,AllocationBase,Size,AllocationProtect,Type,Protect,State,ModuleFileName. -c prints only the allocation count. Negative pid values run a system-wide scan: -32 for 32-bit processes only, -64 for 64-bit, -1 for all. Capped at 25,000 allocations per process to avoid runaway loops. |
| /procs | [32 | 64 | strMatch] | List running processes. With 32 or 64, filter by bitness. With any other string, treat as a case-insensitive substring match against the process name. |
GetCommandOutput path doesn't trigger this since IsDebuggerPresent returns false in the helper's own process.The integration points are in Cx64.cls:
| Method | Helper command | Caller in SysAnalyzer |
|---|---|---|
| x64StartWithDll | /startwdll | API Logger launching a 64-bit target with api_log.x64.dll. |
| x64Inject | /inject | API Logger attaching to a running 64-bit PID. |
| x64LoadLib | /loadlib | Wizard launching a 64-bit DLL as the analysis target. |
| GetMemoryMap | /memmap | RWE injection scanner, deep memory scanner, and Memory Map right-click against a 64-bit process. |
| GetProcessModules | /dlls | Per-process Analyze dumping unknown DLLs in 64-bit processes. |
| DumpMemory | /dumpmodule | Per-DLL memory dump in Analyze; right-click Dump Module; RWE region dumps. |
| DumpProcess | /dumpprocess | Right-click Dump on a 64-bit process row. |
Each method first verifies the target is genuinely 64-bit (via IsWow64Process) and that the helper exists on disk; if either check fails, the wrapper returns an error string in out_injLog instead of invoking the helper.
A 32-bit process accessing C:\Windows\System32 is silently redirected to C:\Windows\SysWOW64. This breaks anything that needs to find x64Helper.exe, hand it a path under System32, or read 64-bit DLL files for PE analysis.
Cx64 wraps every file access in a DisableRedir / RevertRedir pair around Wow64DisableWow64FsRedirection / Wow64RevertWow64FsRedirection. The pair is reference-counted — nested calls are coalesced — so callers don't have to track whether they're inside an outer disable scope.
If your code calls into Cx64 from a routine that itself touched the file system under disabled redirection, this is safe. Calling raw VB Open, Dir, or FileCopy outside that scope from elsewhere is not safe and may quietly resolve the wrong file.
Cx64 looks for x64Helper.exe in App.Path and walks up to four parent folders. The IDE-vs-installed layout is:
x64Helper.exe sits beside sysanalyzer.exe.x64Helper.exe sits two levels above the VB6 project folder.The path is resolved once at class init via GetShortPathName — the helper command line uses the 8.3 short form to avoid quoting issues with long paths and spaces.
The wrapper enables SeDebugPrivilege in the SysAnalyzer process at Cx64.Class_Initialize. Without it, OpenProcess against protected processes (anti-malware, system services) returns access-denied. The helper itself enables it again on its own startup.
The current state is exposed as Cx64.isSeDebugEnabled for diagnostic purposes; frmListProcess shows it in the window caption.
| Symptom | Cause |
|---|---|
Error: failed to open process | Insufficient privilege (SeDebug not granted) or target is a protected process. |
Error: dll file not found | Path resolved against the wrong WoW64 view, or genuinely missing. Confirm via where in a 64-bit shell. |
Error: Watchdog timeout | The helper's 13-second timer expired. The helper aborts; the wrapper sees error output. Most often hit during massive memory-map scans on processes with tens of thousands of allocations. |
preload [dll]: LoadLibraryA returned NULL in target | The dependency DLL doesn't exist on this Windows SKU (typically urlmon on stripped-down Server installs). Non-fatal — the corresponding hooks just won't install. Other hooks proceed normally. |
| Wrapper returns true but no data | The 15-second GetCommandOutput timeout fired before the helper finished. Re-run with the helper directly to see full output. |
The helper source is a single C++ file (main.cpp) under source/x64Helper/. The VB6 wrapper is source/sysanalyzer/Cx64.cls.