43 Commits
Author SHA1 Message Date
nbaertsch d39590e6b9 fix docs to match canonical code state 2026-06-21 13:57:33 -04:00
Noah Baertsch e64e9032a1 comment cleanup in comegon.zig 2026-06-21 13:53:24 -04:00
nbaertsch 9ba45408ad remove crash analysis doc 2026-06-21 13:51:05 -04:00
nbaertsch 9c1f2e8ac5 Revert "remove docs"
This reverts commit 3f33f9e704.
2026-06-21 13:50:28 -04:00
nbaertsch 3f33f9e704 remove docs 2026-06-21 13:50:10 -04:00
nbaertsch 0ab2137944 remove .scry from tracking 2026-06-21 13:49:51 -04:00
nbaertsch 0c6bf09820 restore readme 2026-06-21 13:49:25 -04:00
nbaertsch ce09c1edfa release 2026-06-21 13:46:13 -04:00
nbaertsch f983dc5fe4 support full untruncated return values in invoke() 2026-06-16 22:00:57 -04:00
nbaertschandCopilot 856c2465af Enrich docs with 'why' explanations for non-obvious steps
- Why RESERVED_METHODS = 7 (IUnknown + IDispatch slots)
- Why worker must be dead before dispatch (.text encrypt safety)
- Why re-marshal each cycle (IPID entries consumed, streams one-shot)
- Why +0x108 holds a pump event (BlockFn needs a handle for wait)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 18:37:18 -04:00
nbaertschandCopilot a1d97af623 Add detailed internals documentation (architecture, structures, pump lifecycle)
Three new docs for contributors and blog authors:
- docs/architecture.md: dispatch flow, thread model, CET compliance
- docs/com-structures.md: MIDL tables, stubs, format strings, fake objects
- docs/pump-lifecycle.md: step-by-step pump cycle walkthrough

All with mermaid diagrams for visual clarity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 18:33:20 -04:00
nbaertschandCopilot e109ffaa49 Update README for caller-is-STA architecture
Reflects the refactor that eliminated the dedicated STA thread.
Caller is now the STA, pumps directly via fiber, MTA thread exits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-11 13:14:13 -04:00
nbaertsch f0d68a1b3c Merge caller-sta-pump: single-thread pump architecture 2026-06-10 17:06:04 -04:00
nbaertschandCopilot 0d346f2c1b Refactor: caller-is-STA pump (eliminate dedicated STA thread)
Major architecture change — the calling thread IS now the STA:
- init() does CoInitializeEx(APARTMENTTHREADED) on caller
- CoIncrementMTAUsage pins MTA alive (no persistent MTA thread)
- Short-lived MTA thread unmarshals, exits completely
- pump() builds ModalLoop fiber + dispatches directly on caller
- Worker posts all messages, exits before dispatch begins
- fCoWaitCalled=1 ensures ModalLoop dispatches pre-queued messages

Removed:
- Dedicated STA thread + command loop
- StaCommand enum, command_event, completion_event
- sta_ready, unmarshal_done, pump_ready_event
- sta_handle, sta_main_fiber

Added:
- caller_fiber, mta_usage_cookie fields
- CoDecrementMTAUsage in deinit()
- Re-marshal/re-unmarshal per pump cycle (fresh IPIDs)
- NtWaitForSingleObject for non-pumping waits on STA

Result: single thread during sleep, system-only call stack.
Tested: 5000ms MsgWait sleep, exit 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-10 17:05:57 -04:00
nbaertschandCopilot f82b9a98f6 comegon: add MsgWait sleep via COM dispatch
Add MsgWaitForMultipleObjectsEx as a sleep primitive dispatched
through NdrStubCall2. This uses the win32u syscall path
(NtUserMsgWaitForMultipleObjectsEx) instead of ntdll
(NtDelayExecution), evading detectors that only monitor ntdll
sleep APIs.

New tests:
- Test 3: Single MsgWait dispatch (queue + pump)
- Test 4: Full encrypt-MsgWait-decrypt chain

New flags:
- --msgwait-only: skip NtDelay tests, run only MsgWait tests

MsgWaitForMultipleObjectsEx(0, NULL, sleep_ms, 0, 0) is a
self-timed wait: no handles, no wake mask, timeout-only return.
Functionally equivalent to Sleep() but through win32k message
subsystem. All frames on sleeping stack are system DLLs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-07 12:20:11 -04:00
nbaertschandCopilot 692213dfdf feat(comegon): add --sleep-ms flag for configurable sleep duration
Default 2000ms. Usage: comegon.exe --sleep-ms 30000
Useful for Vigilator testing with longer sleep windows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-07 10:16:25 -04:00
nbaertschandCopilot 0fda26eb18 CLSID: A1FA A1FA A1FA — Hydra Dominatus
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 19:34:25 -04:00
nbaertschandCopilot 68bddc717b CLSID: use obvious dev CLSID, document stealth candidates
Replace arbitrary CLSID with intentionally obvious {C0E60000-C0E6-...}
for development. Document stealth CLSID candidate ranges in code comments
(shell32, propsys, explorerframe, msctf, actxprxy) with selection criteria.

Key findings from CLSID masquerade research:
- Registered CLSIDs break marshal (COM loads real DLL, not our factory)
- OLE range {000003xx} has special combase handling even when unregistered
- Unregistered CLSIDs adjacent to real system entries work and blend in
- Custom IMarshal is incompatible (would lose NdrStubCall2 dispatch)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 19:30:03 -04:00
nbaertschandCopilot cd5981a8fb Remove ReleaseSafe warning — all build modes stable
The 'Zig optimizer bug' was actually the STA fiber init race condition
(fixed in 257474a). ReleaseSafe and ReleaseFast now pass 30/30 each.
All 4 optimization modes verified: Debug, ReleaseSmall, ReleaseSafe,
ReleaseFast.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 18:19:24 -04:00
nbaertschandCopilot 80387db8b0 Add detection testing results to README
Tested COMegon sleep primitive against 4 beacon hunting tools
(Hunt-Sleeping-Beacons, pe-sieve, Moneta, Patriot) — all clean.
Includes disclaimer that host context affects detection independently.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 17:58:49 -04:00
nbaertschandCopilot 257474af15 fix: race condition in STA fiber init causing ~40% crash rate
Root cause: init() returned before the STA thread completed
ConvertThreadToFiber(), leaving g_ctx.sta_main_fiber as NULL.
When pump() ran immediately after init(), it queued SwitchToFiber(NULL),
which crashed inside kernelbase!SwitchToFiber+0x13 reading [0+0x10].

The crash was swallowed by COM's internal SEH in non-DEBUG builds,
manifesting as a hang instead of a crash (timeout at 15s vs normal 5s).

Fix: STA thread signals pump_ready_event after ConvertThreadToFiber(),
and init() waits on it before returning. This ensures sta_main_fiber
is valid before any caller can use it.

Also includes:
- Dynamic signature scanning (Layer 1) replacing hardcoded RVAs
- VEH crash handler with correct CONTEXT_AMD64 struct (DEBUG builds)
- debugSwitchToFiber wrapper for dispatch parameter validation (DEBUG)
- NtWaitForSingleObject in STA command loop (prevents premature pump)

Validated: 150/150 stability test passes (was ~60% before fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 16:00:21 -04:00
nbaertschandCopilot dff2e08168 feat: dynamic signature scanning for combase.dll internals
Replace hardcoded RVA table with runtime signature scanner that
resolves 4 internal combase.dll functions dynamically:
- ModalLoop (fiber proc) — 29-byte prologue + TEB access pattern
- CCliModalLoopCtor — unique 0x80010115 constant write + prologue verify
- NoOpReturn0 — int3-padded standalone xor eax,eax;ret
- PostCall — 26-byte prologue + unique flags test pattern

Removed 4 unused RVAs from runtime (BlockFn, PeekRPCAndDDEMessage,
pre_dispatch_setup, CStdStubBuffer_Invoke).

Version-gated fallback: if sig scan fails, falls back to known-good
RVAs only when combase.dll matches exact checksum + .text size.
Otherwise fails closed with clear error.

Scanner enforces unique-match (exactly 1 hit per pattern).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 13:00:11 -04:00
Noah Baertsch 63570a5b2b Update README.md 2026-06-05 12:17:10 -04:00
Noah Baertsch 91c59d20ca Correct HTML tags and enhance project description
Fix HTML tags in README and update project description.
2026-06-05 12:16:59 -04:00
Noah Baertsch 5943840381 Update README.md formatting and alignment
Fix formatting issues in README.md
2026-06-05 12:16:40 -04:00
Noah Baertsch e02e51a18b Revise README with project overview
Updated project description to clarify functionality.
2026-06-05 12:15:28 -04:00
Noah Baertsch 3b63e31596 Enhance README with image and project details
Added an image and expanded project description.
2026-06-05 12:15:10 -04:00
nbaertschandCopilot 51648abbb0 rename: Omegon → COMegon across all source files
- Renamed omegon.zig → comegon.zig
- Updated README.md display name, build commands, API examples
- Updated docs/ndrstubcall2-crash-analysis.md references
- Updated test harness print strings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 11:59:38 -04:00
nbaertschandCopilot b9011bcb51 docs: CET shadow stack verification — 5/5 stable with HVCI + /cetcompat
Two-step build instructions added for CET enforcement:
  zig build-obj → zig lld-link /cetcompat

Tested on Windows 11 x64 with HVCI enabled:
- Sleep mode: 5/5 pass (~4016ms)
- Proxy mode: 5/5 pass (~1015ms, result=0x0)
- Zero shadow stack violations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 11:49:56 -04:00
nbaertschandCopilot 23d2cd824e docs: NdrStubCall2 crash root cause analysis and fix writeup
Detailed technical writeup of the sporadic ~25% crash in NdrStubCall2
during sleep mode pump dispatch:

- Root cause: var_90 stack local corruption in rpcrt4!NdrStubCall2
- Fix: set MIDL_STUB_DESC.aXmitQuintuple = dispatch_table (one line)
- Full rpcrt4 disassembly with RVAs for future RE reference
- Failed approaches documented with explanations
- Verification: 150/150 across all optimization modes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 08:50:13 -04:00
nbaertschandCopilot 63306d1222 fix: eliminate NdrStubCall2 sporadic crash (150/150 all modes)
Root cause: NdrStubCall2 reads pStubDesc->aXmitQuintuple (+0x38) as a
dispatch table at RVA 0x17455. When NULL, it falls through to an OLE
extension result (var_90) which was unreliable — sporadically producing
wrong function pointers (e.g. 0x27F) depending on stack layout and
optimization level. Setting aXmitQuintuple to our dispatch_table gives
NdrStubCall2 a deterministic, correct dispatch path on every call.

Changes:
- Set stub_desc.aXmitQuintuple = dispatch_table (matches omega1)
- Use CStdStubBuffer_Invoke directly in vtable (no shim/trampoline)
- Remove invokeTrampoline naked function and invokeShim
- Remove csb_invoke_global export (no longer needed)
- Gate VEH crash handler behind DEBUG flag

Verified: 50/50 across ReleaseSafe, ReleaseSmall, and Debug (150/150).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-05 02:07:20 -04:00
nbaertschandCopilot 5fcdd2ffe1 chore: production hardening — debug output gating + dead code removal
- Add compile-time DEBUG flag (default false) with log() wrapper
- Convert 47 library print() calls to log() (test harness prints preserved)
- Remove dead pumpFiberProc and pumpExitTimerThread functions
- Trim VEH crash handler to minimal form, gate on DEBUG
- Remove unused struct fields (msg_posted, msg_pumped, worker_done, pump_ready)
- Remove unused EXCEPTION_POINTERS struct and extern VEH declarations
- Fix remaining print→log in psf_create_stub hex dump loop
- Document ReleaseSmall build requirement (ReleaseSafe has Zig optimizer bug)
- Update README status: hardening tasks complete

Both modes verified: sleep 4015ms (2×NtDelay 2s), proxy 1000ms (NtDelay 1s)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:58:39 -04:00
nbaertschandCopilot afacd437b8 docs: proxy mode now working in status
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:20:39 -04:00
nbaertschandCopilot b3a67c8a20 chore: fix gitignore for nested txt files
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:20:16 -04:00
nbaertschandCopilot 1293270555 feat: proxy mode invoke() fully working
SendReceive returns S_OK now that pDispatchTable=-1 fix is in place.
Both modes pass:
  pump()   — 2x NtDelay(2s) = 4000ms via PostCall batch dispatch
  invoke() — 1x NtDelay(1s) = 1016ms via SendReceive, returns STATUS_SUCCESS

Fixed executePumpCycle to use DeleteFiber on suspended pump fiber
(matching the sleep mode handler — no SetEvent+SwitchToFiber cleanup).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:19:58 -04:00
nbaertschandCopilot ce247b4c7f chore: add .gitignore, remove build artifacts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:05:02 -04:00
nbaertschandCopilot c7788028c1 refactor: rename execute() to pump()
Better reflects the COM message pump mechanism. The public API is
now init(), queue(), pump(), invoke(), deinit().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:04:46 -04:00
nbaertschandCopilot ce92dcc6f7 docs: fix status tracking, add Public API section
- Status section now reflects actual state (slot scaling works,
  proxy mode blocked on RPC_E_FULLSIC_REQUIRED)
- Added Public API section with usage examples for both modes
- Added table clarifying execute() vs invoke() (sleep vs proxy)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 20:03:01 -04:00
nbaertschandCopilot d4cfaf3e13 docs: expand Architecture section with full technical detail
Layered design, dispatch chain with full callstack, key internal
structures (CStdStubBuffer, CInterfaceStubHeader, MIDL_SERVER_INFO,
CSyncClientCall, CCliModalLoop), NDR format string layout, COM
channel setup flow, and sleep mode pump cycle walkthrough.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 11:01:13 -04:00
nbaertschandCopilot 0ac7da6c93 docs: add README
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 10:11:15 -04:00
nbaertschandCopilot 76a5789780 fix: use ModalLoop as direct fiber proc, DeleteFiber suspended
pumpFiberProc wrapper put user .text on the pump fiber stack,
violating the 'no user code on any callstack during sleep' rule.

Fix: ModalLoop is now the direct CreateFiber entry point (zero user
frames). After dispatch completes via SwitchToFiber(main), the
suspended pump fiber is destroyed with DeleteFiber — no need to
resume ModalLoop for cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 09:37:02 -04:00
nbaertschandCopilot 62f86b006c fix: pDispatchTable must be -1 for NDR dispatch path
CStdStubBuffer_Invoke checks CInterfaceStubHeader.pDispatchTable:
- When 0: skips NdrStubCall2, messages consumed but not dispatched
- When -1: uses MIDL_SERVER_INFO → NdrStubCall2 → our dispatch functions

Also fixed pump fiber to use pumpFiberProc wrapper (calls ModalLoop
then SwitchToFiber back to STA main) instead of raw ModalLoop as
direct fiber proc (which kills the thread on return).

Sleep mode fully working: 2x NtDelay(2s) dispatches in 4015ms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 04:26:37 -04:00
nbaertschandCopilot 5b148603b7 feat: import proven PoCs from FiberChain
- omega1.zig: Omega-1 PostCall + ModalLoop fiber pump (sleep mode reference)
  Full 6-call chain with .text encrypt/decrypt, zero user code on any stack
- sta_com_sleep.zig: Sync PoC Phase 1-5b (proxy mode reference)
  SendReceive path with return value capture, ModalLoop fiber pump

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 01:37:50 -04:00