Analysis / API Logger

API Logger

The API Logger is an in-process inline-hook engine that records selected Win32 API calls made by the target and any children it spawns. The hook DLL (api_log.dll or api_log.x64.dll) is injected into the target at launch and streams events back to the SysAnalyzer or standalone UI via window-message IPC.

Beyond plain logging, the logger doubles as a lightweight automated-analysis aid: it auto-dumps WriteProcessMemory writes to disk, optionally captures freed memory regions, can spoof time/debugger checks, and can pause execution at a pattern of your choice for manual inspection.

Two ways to run it

ModeHow
EmbeddedTick Use Api Logger on the SysAnalyzer wizard. The Api Log tab streams entries during the analysis countdown.
StandaloneLaunch api_logger.exe directly, or via Wizard → Tools → External → Api Logger. Required to attach to an already-running PID, log shellcode injections, or use the Freeze At / payload-capture features.

The standalone UI auto-elevates on Vista+. The embedded mode and standalone mode share the same DLL, options panel, and IPC protocol — the only difference is which window receives the events.

Hook engine

The hook engine is MinHook. The API logger has cycled through three hook engines over its lifetime: the original OllyDbg-based hooker.lib, then the NTCore Hooking Engine (with diStorm as the disassembler), and now MinHook. The most recent move from NTCore to MinHook was made for x64 stability. Each migration is invisible to users of the logger but matters when reading the source — the wrapper in InstallHook() keeps the old call-site shape so existing hook installations weren't touched in the swap. Full attribution is on the License page.

Hooks are installed from DllMain at DLL_PROCESS_ATTACH. This is loader-lock territory, so installation order matters: engine-dependency hooks (VirtualAllocEx, VirtualFree, WriteProcessMemory) install first, then very-early-startup hooks (GetStartupInfoW, ExitProcess, ExitThread, used by the CRT during init), then the rest. The blocks are individually toggleable in the source for binary-search debugging if a target chokes on hook installation.

Injection

The injection mechanism depends on what's being launched:

ScenarioMechanism
New 32-bit processStartProcessWithDLLCreateProcess(CREATE_SUSPENDED), classic CreateRemoteThread of LoadLibraryA on the suspended process, then ResumeThread.
New 64-bit processx64Helper.exe /startwdll — same flow but driven by the 64-bit helper. Helper also pre-loads dependency DLLs (wininet, urlmon, advapi32) into the suspended target before injecting api_log.x64.dll, so the hook DLL's own DllMain can resolve hook targets via plain GetModuleHandle without hitting loader-lock from a fallback LoadLibrary.
Already-running process (any bitness)InjectDLL — bitness-matched CreateRemoteThread(LoadLibraryA) against the live PID.
Shellcode injection (32-bit only)InjectShellcode — allocates RWE memory in the target and starts a thread at the buffer. Two sub-modes: Inject Shellcode creates the thread; Inject Data writes the buffer without spawning a thread.

Spawn-following

The DLL hooks CreateProcessInternalW (rather than the older CreateProcessA approach) to catch every code path that creates a process — including paths that the public CreateProcess* functions don't go through. For each child, the same injection routine runs against the child, propagating logging across multi-stage loaders and protected launches.

Children whose name appears in the analysis-tool blocklist (sysanalyzer.exe, sniff_hit.exe, windump.exe, etc.) are not injected. This avoids the logger recursing into its own driver UI when the target spawns one of them.

Standalone UI

The standalone UI has the controls below. Settings are persisted under HKCU\Software\VB and VBA Program Settings\ApiLog\settings on form unload.

Standalone API Logger with options panel and live call log
Standalone API logger. Top: target inputs and the Freeze At / Ignore filter row. Middle: per-PID list and the toolbar (Inject & Log, Resume Logging, Parse, Find, Save, Clear). Right: the Options panel. Bottom: live call log with three columns — PID, formatted API call, and de-dup count.

Target inputs

FieldPurpose
ExecutablePath to the binary to launch, or pid:NNNN to attach to a running process. Drag-and-drop is supported. Auto-detects 32 vs 64-bit and picks the matching DLL accordingly.
ArgsCommand-line arguments passed to the launched binary. Drag-and-drop is supported.
Inject DLLPath to the hook DLL. Defaults to api_log.dll beside the EXE; switches to api_log.x64.dll automatically when the target is 64-bit. Drag-and-drop is supported.
PIDOpen the process picker (frmListProcess) to fill the executable field with pid:NNNN.
Browse buttons (...)File pickers for executable, DLL, and arguments file.

Inject DLL label

The label cycles between three modes when left-clicked:

Right-click the label to toggle DLL-name randomization. Caption goes red when active. With randomization on, the DLL is copied to %TEMP%\[random].dll before injection, defeating samples that fingerprint api_log.dll by name.

Logging panel

ControlPurpose
Inject & LogStart: launch the target (or attach to PID), inject the DLL, and begin recording.
Stop Logging / Resume LoggingToggle: stop processing inbound messages without unhooking. The DLL keeps running in the target; new entries are dropped until resumed.
Ignore (Slow)Comma-separated substrings. Any incoming log line containing one is dropped. Saved across runs.
Re-ApplyRe-apply the Ignore filter against the currently-displayed log, removing matching rows. Useful after editing the filter mid-session.
FindPrompt for a substring; export every matching log line to a temp file in Notepad.
ClearWipe the call log.
SaveSave the call log to a chosen path.
ParseOpen the log in frmLogParser for higher-level extraction (URLs, paths, etc.).
Freeze AtSubstring trigger. When an inbound log line contains this substring, the calling thread inside the target is held inside the IPC SendMessage (which blocks) until the user clicks Continue. Lets you suspend execution at a specific call site for memory inspection. Empty = disabled.
ContinueRelease the thread held by the Freeze At trigger. Caption shows the held thread ID.

Log columns

Each entry has three columns: PID (hex), API call (formatted text), Count (number of consecutive duplicates collapsed into the same row).

Tabs

The right pane has two tabs:

Process menu (right-click on a PID)

ItemAction
SuspendNtSuspendProcess on the target.
ResumeNtResumeProcess on the target.
TerminateTerminateProcess on the target.
Update ConfigPush the current option-panel state into the running DLL by CreateRemoteThread'ing the DLL's ConfigHandlerThreadProc. The DLL re-queries every option from the UI by sending ***config:NAME messages and reading the return value (see Config protocol below).
Update AllPush the current options to every PID in the list.
Kill AllTerminate every PID in the list.
ClearEmpty the PID list (display only; processes keep running).

Options panel

The Api Startup Logging Options frame configures DLL behavior. Most of these flags live as int globals in the DLL; ConfigHandlerThreadProc queries each one from the UI on startup and again whenever Update Config is invoked.

OptionDLL behavior
Ignore Long SleepsSets noSleep. Sleep() longer than 3 seconds is logged and skipped instead of executed. Defeats time-based stalls in samples that Sleep(60000) to outlast a sandbox window. Sub-3-second sleeps pass through unchanged to avoid breaking legitimate timing.
Advance GetTickCountSets queryGetTick. Each GetTickCount call queries the UI for an override value (***config:getTickValue). The UI returns successive values incremented by 0x10000 per call, defeating tick-delta anti-debug checks.
Block OpenProcessSets blockOpenProcess. OpenProcess() is logged but returns NULL. Note that the analysis-tool list (sysanalyzer.exe, ollydbg.exe, windump.exe, sniff_hit.exe, api_logger.exe) is always protected from OpenProcess regardless of this flag.
Block NtSystemDebugCtlSets blockDebugControl. NtSystemDebugControl calls return zero without invoking the real function. Defeats samples that probe debug-control to detect kernel debuggers and loop forever on success.
Ignore ExitProcessSets ignoreExitProcess. ExitProcess is logged but ignored. Useful when you want to keep poking after the sample tries to terminate.
No Registry HooksSets noRegistry. Skips installing the advapi32 hooks entirely (RegCreateKeyA, RegOpenKey*, RegSetValue*, RegDeleteKey, etc.). Reduces noise when the registry surface isn't relevant.
No GetProcAddressSets noGetProc. Suppresses logging of GetProcAddress calls (which can fire thousands of times in startup). The hook itself remains installed because internal code uses Real_GetProcAddress.
Capture VirtualFreeUI-side option. On every VirtualFree log message, dump the freed region's contents to C:\virtualFree\[pid]_[addr]_[size].bin before the real VirtualFree runs. Catches packers that decrypt into a buffer, use it, then free it — the dump captures the unpacked stage.
Hook Lib Log Level0–3. Pushed to the DLL's logLevel global. Higher values get more verbose internal logging from the hook lib itself.
Not yet wired: the panel also shows Capture UrlDownload*, Capture send/recv bufs, and Advance Time Checks. These are placeholders for planned features and currently have no effect.

Config protocol

The DLL and UI talk via WM_COPYDATA. Two message classes are layered on the same channel:

Log messages

Format: [hex_pid],[hex_threadid],[message]. The UI parses the leading PID, optional thread ID, and treats the rest as a free-form log line. Older DLL builds omit the thread ID; the UI handles both shapes.

Config messages

Format: ***config:NAME or ***config:NAME:VALUE. The DLL sends these from ConfigHandlerThreadProc at install time, and again every time the UI fires Update Config. The UI returns the current value via SetWindowLong(GWL_USERRETVAL) through the subclass library — SendMessage returns that value to the DLL, which assigns it to the matching global.

Config keys recognized by the UI:

KeyReturns
noSleep1 if Ignore Long Sleeps is checked.
noRegistry1 if No Registry Hooks is checked.
noGetProc1 if No GetProcAddress is checked.
queryGetTick1 if Advance GetTickCount is checked.
blockOpenProcess1 if Block OpenProcess is checked.
blockDebugControl1 if Block NtSystemDebugCtl is checked.
ignoreExitProcess1 if Ignore ExitProcess is checked.
hooklibLogLevelThe chosen log level (0–3).
getTickValueAn incrementing tick value (GetTickCount() + n*0x10000) per call. Sent per hooked GetTickCount when queryGetTick is on.
handler:ADDROne-shot from the DLL on install: the address of ConfigHandlerThreadProc. The UI stashes it on the per-PID row so Update Config knows where to CreateRemoteThread later.

Hooked APIs

Resolved per DLL by the order shown below. Hooks marked capture have a side effect beyond logging.

kernel32.dll (resolved against kernelbase.dll on Vista+ when present)

APINotes
VirtualAllocExLogs allocation including returned base.
VirtualFreeAugmented with VirtualQuery region size since on Win8 x64 VirtualAlloc no longer flows through VirtualAllocEx. Capture: dumps freed region when option enabled.
WriteProcessMemoryCapture: automatically writes the buffer to wpm_[targetname]_mem_[addr].bin. This is one of the most useful hooks — it catches injected payloads before they run.
GetStartupInfoWCRT hits this almost immediately; hooked early to be safe.
ExitProcess, ExitThreadLogged. ExitProcess can be suppressed via option.
SleepLogged when > 3s; can be skipped via option.
GetTickCount, GetSystemTimeLogged; GetTickCount can return spoofed values.
IsDebuggerPresentAlways returns 0. Standard anti-debug bypass.
CloseHandleFrees internal handle bookkeeping.
CreateRemoteThreadIf targeting a tracked PID that wasn't yet injected, injects api_log first, then runs the real call.
OpenProcessLogs and (per option) blocks. Always blocks for analysis tools.
ReadProcessMemoryLogged.
CreateToolhelp32SnapshotLogged.
Process32First/NextHide: rewrites szExeFile to "xxxx..." and PID to 0xDEADBEEF for analysis tools (sniff_hit, sysanalyzer, windump, olly, api_log, vmware, vmnat, vmount, vmnet, vmtool, procmon, filemon, regmon, procexp, rootkitrevealer, windbg, wireshark, win_dump, tcpdump).
Module32First/NextSame hiding behavior on the module name.
DebugActiveProcessLogged.
CreateFileA, _lcreat, _lopenFile creation logged with full path.
WinExecLogged with command line.
DeleteFileA, DeleteFileWLogged.
CreateMutexALogged with name — useful for sample family identification.
CopyFileALogged with source and destination.
CreateProcessInternalWResolved by hand against kernelbase/kernel32. Catches every CreateProcess code path. Capture: auto-injects api_log into the spawned child.

ws2_32.dll

APINotes
socket, bind, listen, acceptServer-side socket setup.
connectOutbound connection — logs target host/port. Most useful single hook for C2 identification.
send, recvLogged.
closesocket, shutdownLogged.
gethostbyname, gethostbyaddrDNS resolution — logs the requested name/address.

urlmon.dll

APINotes
URLDownloadToFileA / WLogs URL and destination.
URLDownloadToCacheFileA / WSame for cache variant.

wininet.dll

APINotes
InternetGetConnectedStateLogged.
InternetConnectA / WLogged with host and port.
HttpOpenRequestA / WLogged with verb and path.

The HTTP send-request hooks (HttpSendRequestA / W) are present in the source but currently disabled. When enabled they auto-write the POST body to disk.

advapi32.dll (skipped if No Registry Hooks is set)

APINotes
RegCreateKeyA, RegCreateKeyExAKey creation.
RegOpenKeyA, RegOpenKeyExAKey open.
RegSetValueA, RegSetValueExAValue writes — persistence vector logging.
RegDeleteKeyA, RegDeleteValueADeletion.

The wide variants (RegCreateKeyW, RegSetValueExW, etc.) are not currently hooked. The RegEnum* and RegQueryValue* hooks are present in the source but commented out as too noisy.

ntdll.dll (resolved by hand)

APINotes
NtSystemDebugControlLogged; can be blocked via option.

Auto-saved artefacts

Independently of the user clicking Save, the DLL writes the following to disk during a run:

When the embedded mode is in use, the SysAnalyzer countdown additionally writes api.log to the desktop analysis folder at the end of the run (and on form unload).

Known limitations

Source

The DLL source (dll.cpp, main.h) and the API hook generator (which parses MSDN .h files to auto-build hook procedures) are under source/api_logger/ in the public source tree.

Help video