Skip to content

Extending the Tool

Kriyos Arcane edited this page Jul 4, 2026 · 1 revision

Extending the Tool

For contributors and researchers.

How SIP GUIDs Work

Every file type that Windows verifies through WinVerifyTrust has a SIP GUID. The GUID maps to a DLL and a set of functions (verify, sign, hash, etc.) registered in:

HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDll*\{GUID}

The CryptSIPDllVerifyIndirectData subkey holds the verification function. This is what TrustMeBro redirects.

Finding New SIP GUIDs

  1. Open wintrust.dll in Ghidra or IDA
  2. Search for the builtin GUID table in .rdata. On Win11 24H2, it starts at offset 0x62410
  3. Each entry has a 16-byte GUID followed by function pointers
  4. Cross-reference the function pointers to find the handler DLL and function name
  5. Check CryptSIPDllIsMyFileType2 registrations in the registry for non-builtin SIPs (AppxSip, EsdSip, pwrshsip, wshext, MSISIP)

The full 19-GUID map is at docs/SIP_COMPLETE_MAP.md.

Adding a New GUID Alias

C++ (main.cpp + steal.h)

  1. Add the entry to ALL_STANDARD_SIPS[] in steal.h
  2. Add the alias to GUID_ALIASES[] in main.cpp
  3. Increment NUM_STANDARD_SIPS if adding to the standard set

Python (TrustMeBro.py)

  1. Add the entry to the SIPS dict
  2. Add the alias to the GUID_ALIASES dict

BOFs (tmb_bof.h)

  1. Add the entry to the GUID_ALIASES[] array in tmb_bof.h

How the BOF Shared Header Works

tmb_bof.h provides:

  • NT API resolution: resolves NtOpenKey, NtCreateKey, NtSetValueKey, NtDeleteKey, NtClose, NtQuerySystemInformation from ntdll exports via GetProcAddress. The tmb_init() function populates a global TMB_NTAPI struct.
  • FNV-1a hashing: fnv1a() function for string-free API resolution. Pre-computed hashes for NT functions are defined as constants.
  • Stack string macros: WSTR_INIT() builds wide strings on the stack character by character. Named macros like STR_WINTRUST(), STR_DBGUICONTINUE() provide common strings without .rdata literals.
  • GUID alias table: GUID_ALIASES[] array with tmb_resolve_alias() for case-insensitive lookup.
  • Registry helpers: tmb_reg_open(), tmb_reg_set_sz(), tmb_reg_delete(), tmb_reg_write_sip() wrap NT API calls with UNICODE_STRING handling.
  • Path builders: tmb_build_sip_path(), tmb_build_finalpolicy_path(), tmb_build_ismyfiletype_path(), tmb_build_formatobject_path() construct registry paths on the stack.
  • Output helpers: TMB_OK(), TMB_ERR(), TMB_WARN(), TMB_INFO(), TMB_NTERR() wrap BeaconPrintf with consistent formatting.

Adding a New BOF

  1. Create bofs/src/tmb_yourname.c
  2. Include tmb_bof.h
  3. Implement void go(char *args, int alen) as the entry point
  4. Use BeaconDataParse for argument parsing
  5. Call tmb_init() before any NT API call
  6. Use TMB_OK/TMB_ERR for output
  7. Compile: x86_64-w64-mingw32-gcc -o bofs/bin/tmb_yourname.o -c bofs/src/tmb_yourname.c -I bofs/include -Wall -Wno-unused-function
  8. Add the command to cna/tmb.cna and axscript/tmb.axscript

Experimental Branches

Publisher Spoof (experimental/publisher-spoof/)

Generates a self-signed cert with a chosen Common Name. Combined with FinalPolicy hijack, the UAC dialog shows the chosen publisher name.

What it needs to become production-ready:

  • Auto-enrollment of the cert in TrustedPublisher store (currently prints manual steps)
  • Auto-signing of the target PE with osslsigncode
  • Integration with the steal subcommand

Dual-SignerInfo (experimental/dual-signerinfo/)

Documentation only. Describes the parser divergence: kernel ci.dll reads SignerInfo[0], user-mode wintrust iterates all SignerInfo entries.

What it needs to become production-ready:

  • ASN.1 surgery code to inject a second SignerInfo into a PKCS#7 structure
  • Lab testing on Win11 24H2 to verify the kernel stride is still 0xF0
  • Verification that user-mode tools report the second (benign) signer as the primary

Clone this wiki locally