Windows special-folder paths

← recon index · docs/index

TL;DR

Resolve Windows special folder paths (Desktop, AppData, Startup, Program Files, …) via SHGetSpecialFolderPathW. Used by persistence/startup for StartUp-folder paths, by credentials/lsassdump for %SystemRoot%\System32\ntoskrnl.exe, and by any payload that needs a per-user / per-machine well-known path.

Primer

Windows uses CSIDL (Constant Special ID List) values to identify well-known folders abstractly. SHGetSpecialFolderPathW takes a CSIDL constant and returns the resolved filesystem path, handling per-user / per-machine differences and folder redirection in domain environments transparently.

The function is technically deprecated in favor of SHGetKnownFolderPath (Vista+, KNOWNFOLDERID enum), but the older API remains widely supported and avoids COM initialization overhead.

API → godoc

pkg.go.dev/github.com/oioio-space/maldev/recon/folder is the authoritative reference for every exported symbol. This page teaches the concepts; the godoc is the specification.

Examples

Simple — modern KNOWNFOLDERID

import (
    "github.com/oioio-space/maldev/recon/folder"
    "golang.org/x/sys/windows"
)

appdata, _   := folder.GetKnown(windows.FOLDERID_RoamingAppData, 0)
downloads, _ := folder.GetKnown(windows.FOLDERID_Downloads, 0)
system, _    := folder.GetKnown(windows.FOLDERID_System, 0)

// Force creation (KFF_CREATE) when staging a per-user drop directory:
stage, _ := folder.GetKnown(windows.FOLDERID_LocalAppData, windows.KF_FLAG_CREATE)

Simple — legacy CSIDL

appdata := folder.Get(folder.CSIDL_APPDATA, false)
startup := folder.Get(folder.CSIDL_STARTUP, false)
system  := folder.Get(folder.CSIDL_SYSTEM, false)

Composed — feed persistence

import (
    "path/filepath"

    "github.com/oioio-space/maldev/recon/folder"
)

implant := filepath.Join(
    folder.Get(folder.CSIDL_LOCAL_APPDATA, false),
    "Microsoft", "OneDrive", "Update", "winupdate.exe",
)

Advanced — resolve ntoskrnl path for kernel-driver work

ntos := filepath.Join(
    folder.Get(folder.CSIDL_SYSTEM, false),
    "ntoskrnl.exe",
)
// feeds credentials/lsassdump.DiscoverProtectionOffset(ntos, opener)

OPSEC & Detection

ArtefactWhere defenders look
SHGetSpecialFolderPathW callsUniversal Win32 API — invisible
Subsequent file writes to resolved pathsEDR file-write telemetry; flag depends on the path

D3FEND counters: none specific — primitive itself is universally legitimate.

Hardening: none — the call is invisible. Hardening is at the consumer (the writes the path drives).

MITRE ATT&CK

T-IDNameSub-coverageD3FEND counter
T1083File and Directory Discoveryfull

Limitations

  • CSIDL is the legacy path. Microsoft recommends KNOWNFOLDERID for new code. The package now ships both: use GetKnown for new callers; Get stays for backwards compatibility. KNOWNFOLDERID also exposes folders the legacy CSIDL set cannot resolve (FOLDERID_Downloads, third-party Shell extensions).
  • GetKnown returns API-allocated PWSTR. The wrapper frees it via CoTaskMemFree on every call — never returns a borrowed buffer the caller must clean up.
  • MAX_PATH cap on Get only. The legacy path truncates paths longer than 260 chars (Get); GetKnown is uncapped.
  • Some virtual folders return empty. CSIDL_NETWORK, CSIDL_PRINTERS, and similar non-filesystem virtual folders return empty strings.
  • Folder redirection is opaque. Domain-joined hosts with redirected user folders return the redirected (network) path, not the local cached one — operators relying on local-only paths must validate.

See also