GitHub - cristeigabriela/bb: Benowin Blanc — Windows through a detective's lens.

10 min read Original article ↗

Benowin Blanc — Windows through a detective's lens.

(Name credits go to my beloved spouse. ꨄ︎)

A set of command-line tools that parse Windows SDK and PHNT headers via libclang and let you inspect what's actually in them: struct layouts, field offsets, enum values, constants, #define macros, and function declarations with full ABI breakdowns — the works.

Think of it as dt + x from WinDbg, but you don't need a debugger running, and it works against any SDK version, architecture, or PHNT release you throw at it.



Try bb viewer in your browser

bb-viewer — a vanilla TypeScript SPA built with Bun, powered by bb's JSON exports.

Browse 8,000+ functions, 5,000+ types, and 25,000+ constants from the Windows SDK and PHNT headers across all architectures (amd64, x86, arm64, arm) — with ABI layouts, memory visualizations, C expressions, and an interactive type graph. No install required.


bb-types

Struct and class layouts, right in your terminal

bb-types CLI output showing a struct layout with offsets, sizes, field names, and types

bb-consts

Constants, enums, and macro definitions

bb-consts CLI output showing enum values and constants with their numeric values

bb-types-tui

Interactive struct browser

bb-types-tui showing an interactive TUI with file tree, search bar, and struct display

bb-consts-tui

Interactive constant browser

bb-consts-tui showing an interactive TUI with file tree, search bar, and constant display



What is this?

Windows ships with thousands of C/C++ headers (the Windows SDK) that define every struct, enum, constant, macro, and function the OS exposes. Separately, the community-maintained PHNT (Process Hacker NT headers) documents internal structures and syscalls that Microsoft doesn't publish.

bb parses these headers with libclang and gives you fast, searchable, pretty-printed access to all of it — struct layouts, constant values, function ABIs with per-parameter register/stack locations, and more (hell, even TUIs!) — no debugger, no IDE, no digging through .h files by hand.

You might want this if you...

  • Reverse-engineer Windows internals;
  • Write kernel drivers or need to check struct layouts across architectures;
  • Want a quick dt-style lookup without spinning up WinDbg;
  • Need to see exactly which register or stack slot each function parameter lands in;
  • Need to export struct/constant/function definitions as JSON or SQLite for your own tooling;
  • Are just curious about what's inside those headers!

Quick start

Building

On a Windows host, you will need the following:

  • Visual Studio 2019/2022 Build Tools
  • LLVM + Clang (libclang.dll) version >=18.1
  • Rust 2024 edition
  • uv (Python package + project manager — winget install --id=astral-sh.uv -e). Plain Python >=3.10 also works as a fallback.

Afterwards, you may produce the binaries by invoking the following command:

.\update-submodules.ps1   # init + generate submodule data
cargo build --release

The project uses two submodules, managed by update-submodules.ps1:

Submodule Purpose Required for Setup
phnt PHNT NT header generation (phnt-single-header) --phnt flag .\update-submodules.ps1 phnt
sparse MSDN API metadata (sparse) — embeds both the SDK and driver datasets Enriched function views .\update-submodules.ps1 sparse

You can update them individually or all at once (.\update-submodules.ps1). Both support env var overrides for custom data:

Env var What it does
BB_PHNT_HEADER Use a custom phnt.h instead of generating from the submodule
BB_SPARSE_SDK_JSON Use a pre-generated sdk-api.json instead of running sparse in SDK mode (alias: BB_SPARSE_JSON)
BB_SPARSE_DRIVER_JSON Use a pre-generated driver-docs.json instead of running sparse in driver mode
BB_NO_CACHE Bypass the on-disk AST cache (parse from headers every time)

AST cache

After the first bb-funcs / bb-types / bb-consts invocation, the parsed translation unit is saved (via clang_saveTranslationUnit) under %LOCALAPPDATA%\bb\ast\<sha256>.ast. Subsequent runs with the same SDK

  • arch + mode load the AST directly and skip libclang's full re-parse. Typical numbers on this SDK / WDK combo:
invocation cold warm
--mode user --name CreateFileW 10.3s 5.5s
--mode user --name __nonexistent 4.8s 3.8s
--mode kernel --name __nonexistent 2.4s 1.3s

The cache key hashes the synthetic header content, every clang argument, and the bb-sdk crate version — any change to header config, SDK install path, target arch, or a bb-sdk release invalidates automatically. Saved ASTs are ~80 MB; delete the bb/ast/ directory to nuke them, or set BB_NO_CACHE=1 to bypass per-invocation.

First commands

Inspect a struct layout:

Recurse into nested types:

bb-types --phnt --struct _PEB --depth 2

Search for constants by wildcard:

bb-consts --name GENERIC_*

Scope to a specific enum:

bb-consts --enum _MINIDUMP_TYPE

Use Enum::Constant syntax to search within enums:

bb-consts --name "_MINIDUMP_TYPE::*"

Target a different architecture from your host:

bb-types --arch arm64 --struct _CONTEXT

Inspect a function's ABI breakdown:

bb-funcs --name CreateFileW

List exported functions from a header:

bb-funcs --name "Create*" --filter fileapi.h --exported

Filter functions with SQL WHERE clauses:

bb-funcs --where "params > 3 AND return_type = 'BOOL'"
bb-funcs --where "name LIKE '%File%' AND is_exported = true"

Filter kernel/driver functions by IRQL constraint:

bb-funcs --mode kernel --name "Wdf*" --irql "<= DISPATCH_LEVEL"
bb-funcs --mode kernel --irql PASSIVE_LEVEL

Export as JSON or SQLite for your own tooling:

bb-types --arch arm64 --struct _CONTEXT --json
bb-consts --name "PROCESS_*" --json
bb-funcs --name "Nt*" --phnt --json

# or export to SQLite
bb-funcs --name "Create*" --sqlite funcs.db
bb-types --struct "_*" --sqlite types.db

JSON mode in bb-types performs full nested type expansion, producing all matched types alongside their deduplicated referenced_types — regardless of the --depth flag. SQLite exports mirror the same level of detail as JSON.

Typo? Both CLIs suggest close matches:

bb-types --struct _PBE
error: no structs matching '_PBE'

  did you mean?

    _ABC
    _PSP
    _PEB

The tools

CLI applications

Crate What it does
bb-types Inspect struct and class layouts
bb-consts Inspect constants, enums, and #define macros
bb-funcs Inspect function declarations with ABI parameter locations

TUI applications

Crate What it does
bb-types-tui Interactive struct browser
bb-consts-tui Interactive constant browser

Libraries

Crate What it does
bb-arch Architecture definitions, register sets, and ABI location types
bb-clang libclang abstractions for types, constants, and functions
bb-sparse Embedded Windows API metadata from MSDN (via sparse)
bb-sdk Windows SDK / PHNT header management
bb-sql SQL WHERE evaluator + SQLite export
bb-cli Shared CLI argument definitions
bb-tui Shared TUI framework on ratatui
bb-shared Small shared utilities

Web viewer

What it does
bb-viewer Web explorer for bb's JSON output — functions, types, constants, type graph

Supported headers

Windows SDK

Uses whatever version is available in your Developer Command Prompt environment.

User mode — Win32 + COM IDL surface (click to expand)

Winsock 2, networking (winhttp, wininet, iphlpapi, dhcpsapi, dns, ldap, snmp, ws2spi, http server, p2p alt, mprapi, rtmv2), security (acl, sddl, wincrypt, bcrypt, ncrypt, authz, wintrust, certenroll, cryptxml, slpublic, msdrm, winbio), shell/UI (shobjidl, shobjidl_core, commctrl, commdlg, uxtheme, dwmapi, magnification, prsht, interactioncontext, winwlx, highlevelmonitorconfigurationapi), GDI helpers (icm, fci), Media Foundation (mfapi, mfidl, mfreadwrite, mfmediaengine), Core Audio (mmdeviceapi, audioclient, audiopolicy), DirectShow (strmif, vfw), Direct3D 11/12 + DXGI 1.6 + d3dcompiler, UI Automation, Bluetooth, XInput, ETW + TraceLogging + TDH, BITS, Windows Update, MSI (msi, msiquery), Task Scheduler, DBGENG (debugger engine), AD (iads, adshlp, dsgetdc), PDH, WIA, WMP, COM+ (azroles, comsvcs, xpsobjectmodel, msinkaut, tom), virtdisk, fltuser (filter manager user side), peerdist, EAP (eapmethodpeerapis), WinUSB, projfs, WER, WMI consumers (wbemcli), Image helpers (traceloggingprovider, perflib), LM DFS, pathcch, traffic, sphelper (excluded — see notes), strsafe, intsafe, ntmsapi, mscat, drt, npapi, wdspxe / wdstpdi / wdsclientapi, roerrorapi, wtsapi32, lmaccess, setupapi, cfgmgr32, wlanapi + ras + rasdlg, powrprof, gpedit, oleauto, appmodel, hbaapi, propvarutil, propsys, traffic, mstcpip, windns, wincred, userenv, ktmw32, sapi, cfapi, diagnosticdataquery, clfsw32, usp10, winevt, ntstatus.h (full set of STATUS_* codes — reached via the WIN32_NO_STATUS dance that lets winnt.h's tiny inline subset coexist with ntstatus.h's ~2800 codes), …

Kernel mode — NT / WDF / NDIS / streaming (click to expand)

Built on the ntifs.h umbrella (which transitively pulls ntddk.h, wdm.h, in the right order so PEPROCESS / PETHREAD don't redefine). On top of that: ntstrsafe, wsk (Winsock Kernel), fltkernel (minifilter), aux_klib, usb + usbdi + usbdlib, WDF (wdf.h, wdfusb.h — KMDF version auto-discovered from Include/wdf/kmdf/<ver>/), NetAdapterCx (modern NDIS-on-WDF — also version-discovered from Include/<sdkver>/km/netcx/kmdf/adapter/<ver>/), ndis, kernel streaming (ks, ksmedia, portcls), HID (hidpi, hidclass, hidsdi, hidusage), swenum, pep_x, ndischimney. Plus a direct ntstatus.h include as a safety net so STATUS_* codes still flow when the WDK kernel-mode headers aren't installed — ntstatus.h lives in shared/ (always shipped with the plain SDK).

Excluded (with rationale)

Each is documented inline in crates/bb-sdk/src/winsdk/{user,kernel}.rs:

  • storport.hntddstor.h ships without include guards, so it re-runs all DEFINE_GUID(GUID_DEVINTERFACE_*, …) and clang flags redefinitions.
  • d3dkmthk.h / bdasup.h / fwpsk.h / ksproxy.h — drag in COM wtypes.h whose VARENUM / VT_EMPTY enumerators collide with the ones ks.h already declares.
  • video.h / miniport.h — miniport.h redefines _QUAD and _PROCESSOR_NUMBER against the kernel core types in scope.
  • dbghelp.h (kernel) — pulls minidumpapiset.h which needs user-mode-only types (VS_FIXEDFILEINFO, TIME_ZONE_INFORMATION).
  • wdbgexts.h — clang resolves <wdbgexts.h> from um/ before km/, and the user-mode header uses LPTR which isn't defined in the kernel chain.
  • bthsdpddi.h — implicit-int header bug.
  • dwrite.h / rtworkq.h — use C++ syntax (static_cast, untagged interface names) that won't parse in C mode.
  • sphelper.h — heavy C++ speech-API templates that balloon clang parse time from ~9 s to >4 min.
  • tapi.h chain (tapi3if, tspi, winfax) — C-mode parse errors in tapi.h itself, cascading to everything that pulls it.
  • webservices.h — two empty enums clang rejects in C mode.
  • wsman.h — needs WSMAN_API_VERSION_1_0 / _1_1 defined first; not wired up.
  • security.h / sspi.h — needs SECURITY_WIN32/SECURITY_KERNEL/SECURITY_MAC defined first.
  • p2p.h — not in modern SDK (10.0.26100.0).
  • winddi.h / rpcproxy.hHSEMAPHORE typedef conflict + anonymous-struct C-mode error.
  • resapi.h / iscsidsc.hPHANDLER_ROUTINE redef vs services API + missing ISDSC_STATUS definition.
  • imagehlp.h — struct redefs against dbghelp.h (which is the modern superset).
  • wudfddi.h — only in legacy wdf/umdf/1.x/ trees; modern UMDF 2.x uses wdf.h directly.
  • ntsecapi.hLSA_UNICODE_STRING / LSA_STRING collide with winternl.h's UNICODE_STRING / STRING.
  • udecx.h — not shipped in all installed WDKs.
bb-types  --mode kernel --winsdk --struct *DRIVER_OBJECT*
bb-funcs  --mode kernel --name NetAdapterCreate
bb-funcs  --mode kernel --name WdfCollectionAdd

All four mode combinations (--mode user, --mode kernel, --phnt, --phnt --mode kernel) parse with zero libclang errors and zero warnings against the SDK 10.0.26100.0 + WDK 1.35 + NetCx 2.5. Add --diagnostics to any invocation to see clang output yourself.

PHNT

The Process Hacker NT headers, embedded at compile time. Exposes internal NT structures and constants that the public SDK doesn't ship.

Supports version targeting from Win2000 through Win11 22H2:

bb-types --phnt win11 --struct _PEB
bb-consts --phnt --name "STATUS_*"

PHNT mode auto-inherits the full Windows SDK umbrellaphnt_synthetic_header starts from winsdk::sdk_header(mode) and only strips the two headers that fight phnt.h directly (winternl.h — phnt errors Do not mix Winternl.h and phnt.h; winusb.hshared/usb.h stubs PIRP=PVOID against phnt's real _IRP). Every coverage expansion in winsdk/ automatically applies to --phnt.

Header coverage

Measured against the documented MSDN free-function surface that bb-sparse embeds (sdk-api for user mode, windows-driver-docs-ddi for kernel) — see cargo test -p bb-tests sparse_coverage_dump -- --ignored --nocapture to run it yourself. Interface methods aren't counted because bb-funcs filters on EntityKind::FunctionDecl, and IDL vtables surface those as function-pointer fields inside IFoo::Vtbl structs.

mode funcs types consts enums free-func coverage
user 15,701 15,808 44,966 2,926 75.2%
kernel 4,884 3,298 19,893 735 62.4%
phnt user 17,424 17,338 48,073 3,167
phnt kern 4,885 3,721 20,472 776

Architecture support

All tools support cross-compilation via --arch — inspect layouts and ABIs for any target from any host:

Flag Target Notes
amd64 x86_64-pc-windows-msvc Default
x86 i686-pc-windows-msvc
arm64 aarch64-pc-windows-msvc
arm thumbv7-pc-windows-msvc
bb-types --arch arm64 --struct _CONTEXT

How it works

The flow is described below:

Diagram showing the bb crate dependency flow: bb-sdk feeds into bb-clang, which branches into bb-types, bb-funcs bb-consts (CLI frontends), each flowing down to bb-types-tui and bb-consts-tui (TUI frontends) Diagram showing the bb crate dependency flow: bb-sdk feeds into bb-clang, which branches into bb-types, bb-funcs bb-consts (CLI frontends), each flowing down to bb-types-tui and bb-consts-tui (TUI frontends)

We use bb-sdk to discover (or gather) the SDK environment, then we generate a SDK-specific "synthetic header" (also known as an Unsaved/CXUnsavedFile in the Clang-world) which will be passed through partial compilation with libclang.dll and in turn give us a TranslationUnit.

From the translation unit, we lift the AST entities into bb-clang serializable objects, and we use the information that we expose there to develop the tools.

For functions, bb-clang computes the full ABI layout: which register or stack slot each parameter occupies, per architecture and calling convention (cdecl, stdcall, fastcall). bb-funcs enriches this with MSDN metadata (DLL, lib, min Windows version, and — for kernel/WDF DDIs — IRQL constraints, KMDF/UMDF versions, etc.) from sparse and cross-references known constant values for each parameter. SQL WHERE clause filtering is supported via bb-sql.

For macros specifically, bb-consts does a two-pass resolution: first pass evaluates simple literals and variables, second pass substitutes known constant names into unresolved macro token streams before re-evaluating. This handles things like #define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF).