Thread Start-Address Validation (TSAV / L8)
Aether checks threads with stricter classification and cross-correlation against the L1–L5 findings. For every created thread in the target process Aether reads its Win32StartAddress via NtQueryInformationThread and, when access permits, also the live Rip / Eip via GetThreadContext / Wow64GetThreadContext. Each address is then graded as the following table, for more details read the blogpost:
| Verdict | Severity | Condition |
|---|---|---|
TSAV_SHELLCODE_PRIVATE | CRITICAL | Address lives in a MEM_PRIVATE + PAGE_EXECUTE_* region — classic CreateRemoteThread shellcode |
TSAV_SUSPENDED_RIP | CRITICAL | Suspended thread’s Rip disagrees with Win32StartAddress and resolves to a suspicious region — catches Win32StartAddress spoofing (EarlyBird / APC tricks) and SetThreadContext hijacks |
TSAV_HOLLOWED_HOST | HIGH | Address inside a MEM_IMAGE allocation that is not in the PEB module list (DLL hollowing / module stomping) |
TSAV_MODIFIED_HOST | HIGH | Address inside a MEM_IMAGE allocation that the L1–L5 pipeline already flagged as MODIFIED_CODE_*, MISSING_PEB, PRIVATE_RWX, DISK_MEM_DIFF, or HOOK_PROLOGUE |
TSAV_STAGED_PRIVATE_RW | HIGH | MEM_PRIVATE + PAGE_READWRITE — pre-VirtualProtect shellcode staging |
TSAV_MAPPED_NONPE | MEDIUM | MEM_MAPPED (pagefile-backed section) without a PE header — sRDI / pagefile reflective loader |
TSAV_SPOOF_TRAMPOLINE | MEDIUM | Address matches a denylisted trampoline (LoadLibraryA/W/ExA/W, WinExec, CreateProcessA/W, VirtualAlloc[Ex], RtlExitUserThread, RtlExitUserProcess, NtTerminateProcess, ShellExecuteA/W) |
What makes this stronger than the basic check “is start_address in any module” check:
VirtualQueryExcross-check — every address is queried forType/Protect/AllocationBasein a single O(1) call instead of a linear scan over the module list- Cross-correlation with L1–L5 — a thread whose start lands inside an already-flagged allocation is upgraded from “OK” to
TSAV_MODIFIED_HOST - PEB consistency — hollowed modules are detected even when the start address technically falls inside a “real” range
- Suspended-RIP probe —
Win32StartAddressis process-writable viaNtSetInformationThreadand is the spoofable field; the liveRipof a suspended thread is the one a loader can’t easily rewrite. We compare the two and flag any disagreement that resolves to a suspicious region - WoW64 aware — automatically switches to
Wow64GetThreadContextand readsEipfor 32-bit threads inside a 64-bit process - Access-rights ladder — falls back from
QUERY_INFORMATION | GET_CONTEXT→QUERY_INFORMATION→QUERY_LIMITED_INFORMATIONper thread, so partial-access scenarios still produce useful classifications