The Problem

If you are like me, you have found yourself digging through Microsoft specifications and RFC documentation trying to wrangle with a new exploit or tooling to get DA faster than your peers. The biggest problem one may encounter is the specifications are meant for developers; they will tell you the song and dance in its entirety, but it never goes beyond in any way that may be useful for you to weaponize. Fixing that may potentially lay with using the Windows 2003 Source code.

TThe specifications are written for developers. They lay out every field, every flag, every “SHOULD” and “MAY,” but never go beyond that in any way useful for weaponization. The MS-* specifications “Appendix B Product Behavior” sections offer implementation hints in footnotes, but these describe behavior across multiple Windows versions without specifying which one does what or are not deep enough to give you any assurance.

This gap matters for both sides. If you’re building offensive tooling and want it to blend in, you need to know the actual implementation that Windows does, not the specification/RFC-allowed superset. If you’re building detections, you need to know the actual implementation so you can distinguish it from everything else. It’s a nightmare for both blue and red teams to focus on the specifications in their entirety as they actually don’t describe what normal looks like, what normal is and what Windows will do. It just describes the shiny, gold standard.

Oh and nonetheless, the RFCs can send you down rabbit holes when you find notes such as “implementation-specific” as that will mean you have reached an unsolvable dead end that MS-* will not have the answers to many times. No worries though.

My solution to this problem was simple: read the Windows source code. Both Windows XP and Server 2003.

The Solution

The Windows Server 2003 source code leaked years ago alongside the Windows XP (SP1). While the codebase is two decades old, the core authentication protocols, Kerberos, NTLM, DCE/RPC, SPNEGO, in no way have they fundamentally changed. The flag values, the construction logic, the naming conventions, the hardcoded constants, the way the relevant SSPIs handle their respective protocols, and all sorts of other information; they’re overwhelmingly still there in modern Windows.

Where things have changed, it’s usually been extensions and upgrades that are additive in nature. As a result, not much of anything in that code base is no longer present as I will show in the later sections; the code base stands as a very underused source of truth for many things Windows.

This source code became the most useful reference for the Impacket-IoCs project. Over and over, when discussions stalled on “but why does Windows do it that way?” or “is this difference actually detectable?”, the answer was sitting in a .cxx file. I have answered many of my Kerberos and NTLM related doubts and IoCs by going to the source code and pinpointing exactly who’s who since the specifications’ jobs are not to tell you what Microsoft does; it’s to tell you the blueprint to develop.

Next follows a more in depth discussion of how the source code helped me improve my internal version of Impacket as well as better document its IoCs.

Practically Questions The Source Code Answered

The KDC Options And What Influences Their Setting

One of the first IoCs I picked up was that Impacket sets kdc-options in its AS-REQ to forwardable | renewable | proxiable. Windows sets something different that I could observe from my Windows Server 2003 but there was an issue; was this scenario specific or were there actual rules behind why one flag may appear but another may not? RFC 4120 lists every possible flag, and while MS-KILE does state that “the client does not set the PROXY or PROXIABLE option,” it never specifies the full default combination a real Windows client uses or the basis on which other flags would be set. The answer I would later find in ds/security/protocols/kerberos/client2/kerbtick.h:line 82:

define KERB_DEFAULT_TICKET_FLAGS (KERB_KDC_OPTIONS_forwardable | \
                                        KERB_KDC_OPTIONS_renewable | \
                                        KERB_KDC_OPTIONS_renewable_ok | \
                                        KERB_KDC_OPTIONS_name_canonicalize )

Windows sets forwardable, renewable, renewable-ok, and name-canonicalize. It does not set proxiable. Impacket sets proxiable but omits renewable-ok and name-canonicalize. Two flags present that shouldn’t be, two flags absent that should be. How did we determine this for certain? Because while MS-KILE makes no comment on what the KDC MUST do with these combinations, we can see the default flags that the client will ALWAYS set because we can see it in the source code. Now we know that my IoC 1 is robust as a standalone detection and if you’re a red teamer, you’ll change accordingly, and if you’re a blue teamer then you now know it’s an A tier detection.

What The Etype List Contains

Another useful example came from AS-REQ encryption type lists. Impacket, as one of the first things I documented in Impacket-IoCs, often sent a single etype depending on whether you supplied an NT hash or a password. This was in contrast to Windows that appeared to be sending anywhere between 4-8 entries.

The source at ds/security/protocols/kerberos/client2/logonapi.cxx explains why we saw the range and variation within the same environment or even host:

if (PrimaryCredentials->Passwords != NULL) {
    KerbConvertKeysToCryptList(
        &RequestBody->encryption_type,
        PrimaryCredentials->Passwords
        );
} else {
    CDBuildIntegrityVect(&CryptTypeCount, CryptTypes);
    KerbConvertArrayToCryptList(
        &RequestBody->encryption_type,
        CryptTypes,
        CryptTypeCount,
        FALSE);
}

Windows builds the etype list from all available password-derived keys, or from the full set of supported crypt types if credential-specific keys are not available. That explains why a real Windows AS-REQ often has a longer etype list than tooling that sends only RC4 or only AES256.

From a detection perspective, this is useful because etype count, order, and composition can become part of a client fingerprint. Detection engineers can now look at the available creds for a given account and assert that if the AS-REQ deviated from that then create an alert as we know that would not be a Windows or Samba/SSSD client. From an offensive engineering perspective, it explains why simply making the request protocol-valid is not enough.

AS-REQ Requested Ticket Lifetimes

This may have been more obvious but again subtle. There are no rules or enforcements in RFC 4120 or MS-KILE as to what the date should be in AS-REQs. Impacket hardcodes till and rtime in the AS-REQ to now + timedelta(days=1). When observing real Windows traffic, I noticed dates around September 2037 being set. This obviously puzzled me as I wanted to know if this was algorithmic, random or if there was some logic that could allow us to use this as a detection or that it could be used in some way to track us during a red team operation. Because of ds/security/protocols/kerberos/client2/logonapi.cxx, line 1197, I was able to answer this question:

TempTime = KerbGlobalWillNeverTime;

KerbConvertLargeIntToGeneralizedTime(
    &RequestBody->endtime,
    NULL,
    &TempTime
    );

KerbConvertLargeIntToGeneralizedTime(
    &RequestBody->KERB_KDC_REQUEST_BODY_renew_until,
    NULL,
    &TempTime
    );

KerbGlobalWillNeverTime is 0x7FFFFFFFFFFFFFFF in FILETIME, which is the maximum signed 64-bit value. The function KerbConvertLargeIntToGeneralizedTime converts this FILETIME into a Kerberos GeneralizedTime string (an ASN.1 format like YYYYMMDDHHmmssZ). The resulting date lands around September 2037 because of how that conversion handles the maximum value. Windows asks for the longest possible ticket lifetime; the KDC clips it to policy (typically 10 hours / 7 days). But the request itself uses the sentinel value.

This is again incredibly useful from a red teaming perspective because you now know exactly what to set, but you also know what will always be there and that if it deviates from this then that device is not a Windows client :)

The NTLM Flags Pitfall

MS-NLMP documents the Type 1 negotiate flags, but again I was faced with the practical question of what Windows sends by default and what, if anything, determines the behavior to produce a reliable standard baseline.

Exploring the source code in ds/security/protocols/msv_sspi/ctxtcli.cxx, Windows builds its client negotiate flags like this:

Context->NegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE |
                          NTLMSSP_NEGOTIATE_OEM |
                          NTLMSSP_NEGOTIATE_NTLM |
                          ((NtLmGlobalLmProtocolSupported != 0)
                           ? NTLMSSP_NEGOTIATE_NTLM2 : 0 ) |
                          NTLMSSP_REQUEST_TARGET |
                          NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
                          NegotiateFlagsKeyStrength |
                          NTLMSSP_NEGOTIATE_VERSION;

That explains several packet-capture differences that otherwise look like they are random or potentially “highly” version specific. We can then confirm from the source code that Windows sets NEGOTIATE_OEM, NEGOTIATE_ALWAYS_SIGN, and NEGOTIATE_VERSION as a baseline default that’s always going to be sent over the wire from 2003 until Server 2025.

For defenders, this can be part of an NTLM client fingerprint, especially when combined with Type 3 structure, workstation handling, version fields, and AV-pair behavior. For offsec tooling developers, it is a reminder that “the server accepted it” and “Windows would have sent it” are different bars that are important distinctions when stealth is of utmost importance.

The SPN casing question

I had a recent discussion with a friend about SPN casing. When are service classes lowercase? When are computer names uppercase? Is casing random, inherited, canonicalized, or useful?

The source answers this across several subsystems.

In the LDAP client at ds/ds/src/ldap/client/bind.cxx:

#define LDAP_SERVICE_PREFIX L"ldap"

In the SMB redirector at base/fs/rdr2/rdbss/smb.mrx/sessetup.c:

UNICODE_STRING CifsServiceName = { 8, 10, L"cifs" };

In the DCOM remoting layer at com/ole32/com/dcomrem/security.cxx:

WCHAR* pwszSPNPrefix = L"host/";

Those service class strings are compile-time literals. The SPN builder in ds/security/base/lsa/security/driver/spn.cxx preserves what callers pass in. It does not magically uppercase the service class.

The hostname side has its own normalization. In ds/security/protocols/kerberos/client2/kerbutil.cxx:

Status = RtlDowncaseUnicodeString(
            &HostName,
            &HostName,
            FALSE
            );

That gives you a useful mental model in that the service class casing often comes from the calling subsystem, while hostnames are commonly downcased before the request is built.

There is one more important distinction. Some keen observers from the Impacket-IoCs project or others may have noticed that Windows will occasionally use both upper case and lower case HOST SPN. While some may determine it’s random behavior as ultimately the specifications (RFC 4120 and MS-KILE) make no statement on the matter, we are able to leverage the Windows source code for a definitive answer to allow red and blue teamers to make design decisions in their tools and detections grounded in Windows behavior.

The source code shows us the difference is due to the fact that during domain join, machine SPNs may be registered with uppercase HOST/ in ds/netapi/netlib/joinutl.c:

#define NETSETUPP_HOST_SPN_PREFIX L"HOST/"

So from that you can then determine that the operator may see lowercase SPNs in client requests (since the KDC will usually echo what’s given to it) and uppercase forms in directory-backed canonical names where the KDC is conferring with AD on the SPN to be built. The KDC and directory lookups are generally case-insensitive, but what comes after that in the form of what gets built and observed over the wire is what’s changing.

Something related to the above and worth mentioning for researchers is in ds/security/protocols/kerberos/server/getas.cxx and gettgs.cxx, the KDC branches on KERB_KDC_OPTIONS_name_canonicalize. If the client requested canonicalization, the KDC returns canonical directory-backed account naming. If not, it may duplicate the client-supplied name into the ticket. These client-supplied names can be mixed case or otherwise abnormal looking and so this branch behavior may warrant further research for those interested.

A Hitchhiker’s Guide To The Source Code

The rest of this post is a map. The goal is not to document every directory. The tree has hundreds of thousands of files and I have no interest or capability in reading every last one. The goal is to give security researchers enough orientation to hopefully encourage them to explore and read through the source code for aids relevant to what they are doing.

A High Level Orientation Day Of The Source Code

ds/: This is where my focus and reason for exploring the source code is: Directory Services. This is the place where Windows authentication, Active Directory, Kerberos, NTLM, Netlogon, LDAP, SAM, domain join, replication, DC locator, and account lookup behavior all live. Most of the gold for identity/authentication all things in Windows is found.

base/: Core OS services and subsystems. Security-relevant areas include the Service Control Manager, Event Log, Remote Registry, filesystem redirectors, kernel-adjacent components, and many service implementations.

com/: COM, DCOM, OLE, and RPC runtime code. If you care about WMI, DCOM activation, ORPC, SCM activation paths, RPC bind behavior, or authenticated RPC internals, or just that section of Windows then that would be found here.

admin/: Administrative services and tools. WMI and Task Scheduler are the two obvious security-relevant regions, but there are also management components worth searching when a protocol or admin surface stands out in your own research.

inetcore/ and inetsrv/: IIS and Internet services. Useful if your research touches HTTP services, IIS authentication, WebDAV-era behavior, SMTP/POP-era components, or server-side parsing surfaces from that generation.

net/: Networking components. This is useful for transport, legacy networking tools, name resolution, and network service glue. It is less identity-focused than ds/, but still worth searching when the behavior is network-stack-adjacent.

drivers/: Kernel drivers. This includes network and filesystem drivers. For user-mode auth protocol fingerprinting it is usually not the first stop, but for exploit research, attack surface review, and kernel boundary questions it matters.

public/sdk/inc/: Public and semi-public headers. Do not skip this directory. It contains constants, structures, flags, protocol IDs, and internal definitions that make the implementation files readable.

windows/, shell/, enduser/, multimedia/, printscan/, termsrv/: These are more situational. termsrv/ is clearly interesting for RDP/Terminal Services research. printscan/ is related to print subsystem surfaces. shell/ and enduser/ can matter for local privilege, file handling, UI-driven auth, and client behavior. Not much is interesting there or likely to be as relevant today.

A general rule of thumb can be: the more fundamental something is to the Windows/AD ecosystem as opposed to local operations, the less likely changes would be introduced since Microsoft has a history of strong commitments to backwards compatibility and interoperability across systems.

tools/ and sdktools/: Build and developer utilities that may interest some but definitely not for any security oriented workflows. Much of these would have likely changed and been moved about by now. A lot of it as well changed to allow the repository this was looked into to change.

Kerberos Client

ds/security/protocols/kerberos/client2/ is the Windows Kerberos SSPI provider. This is the code applications indirectly call when they use SSPI for Kerberos.

logonapi.cxx: AS-REQ construction. Default KDC options, etype list construction, pre-authentication data, requested lifetime, and credential-derived behavior live here. Start here when comparing a tool-generated AS-REQ to Windows.

kerbtick.cxx: TGS-REQ, AP-REQ, authenticators, GSS checksums, mutual authentication, referral chasing, delegation-related flows, and service ticket use. It is large, but it is one of the highest-value Kerberos client files.

kerbtick.h: Constants and structures that drive ticket requests. Small file, high value. This is where defaults like KERB_DEFAULT_TICKET_FLAGS live.

ctxtapi.cxx: SSPI entry points such as InitializeSecurityContext and AcceptSecurityContext. Useful for understanding target name parsing, context setup, SPN flow, and how application-facing SSPI calls become Kerberos operations.

ctxtmgr.cxx: Security context lifetime and state management. Useful when you care about context reuse, renew times, delegation state, and how SSPI context properties are carried.

kerbutil.cxx: Utility functions for hostname normalization, Kerberos OIDs, GSS helpers, DNS names, and shared conversions. Great file for “why did Windows format that string this way?”

userapi.cxx: User-mode Kerberos APIs, OID tables, wrap/unwrap behavior, and interaction with SPNEGO/SSPI surfaces.

tktcache.cxx: Ticket cache lookup, matching, expiry, and storage behavior. Useful for understanding when Windows reuses tickets versus asking the KDC again.

spncache.cxx: SPN-to-realm cache behavior. Useful for referral chasing and repeated service access.

kerbs4u.cxx: S4U2Self and S4U2Proxy client implementation. Important for constrained delegation research.

pkauth.cxx: PKINIT and smart-card/certificate pre-authentication. Worth exploring for certificate mapping, smart card behavior, and PKINIT packet construction.

krbaudit.cxx: Client-side Kerberos audit behavior, including logon GUID correlation.

kerbevt.mc and kerberos.mof: Event and WMI definitions. Useful for connecting implementation behavior to observable telemetry.

Kerberos’s KDC

ds/security/protocols/kerberos/server/ is the KDC. This is where the domain controller processes AS-REQs and TGS-REQs, resolves names, applies policy, builds tickets, and emits KDC-side events.

getas.cxx: AS-REQ handler. Pre-authentication validation, client account lookup, ticket construction, canonicalization choices, response encryption, and error paths.

gettgs.cxx: TGS-REQ handler. TGT validation, service principal lookup, cross-realm referrals, S4U, constrained delegation checks, service ticket construction, and canonicalization behavior.

tktutil.cxx: KDC name resolution and ticket utility logic. This is where principal names get resolved to directory accounts, including SPNs, UPNs, SAM-style names, krbtgt handling, cross-realm cases, and fallback paths.

pac.cxx: PAC construction. Group membership, SIDs, validation info, timestamps, and KDC PAC population logic. The file is old relative to modern PAC hardening, but it is still excellent for understanding the structure.

secdata.cxx and secdata.hxx: KDC security data and configuration. Domain keys, krbtgt data, trust data, and global service names.

refer.cxx: Cross-realm referral logic. Useful when investigating why the KDC issued a referral TGT instead of a service ticket.

transit.cxx: Kerberos transit path logic. Relevant for cross-realm and trust-path research.

pkserv.cxx: KDC-side PKINIT. Useful for certificate-based authentication research.

kpasswd.cxx: Kerberos password change service behavior.

events.cxx and kdcevent.mc: KDC event logging. Useful for defenders trying to understand which KDC errors should produce telemetry.

Kerberos Shared Code And Headers

ds/security/protocols/kerberos/common2/ contains code shared between Kerberos client and KDC.

names.cxx: Principal name construction and conversion helpers.

tickets.cxx: Ticket encoding, decoding, encryption, and checksum operations.

ds/security/protocols/kerberos/inc/kerbcomm.h: Shared constants such as krbtgt, kpasswd, size limits, and cross-component prototypes.

ds/security/protocols/kerberos/inc/krb5.h and krb5p.h: Kerberos constants: name types, etypes, flags, error codes, ASN.1 types, and protocol structures. This is pretty dry but can be helpful nonetheless for someone out there!

NTLM And MSV1_0

ds/security/protocols/msv_sspi/ contains the NTLM SSP and MSV1_0 authentication package code.

ctxtcli.cxx: The most important NTLM client file. Type 1 construction, Type 2 processing, Type 3 construction, version fields, workstation/domain handling, target info parsing, credential target construction, and session setup behavior.

ctxtsrv.cxx: Server-side NTLM. Type 1 processing, Type 2 challenge construction, TargetInfo population, Type 3 parsing, validation dispatch, and server-side policy behavior.

nlmain.c: MSV1_0 authentication package internals. NTLMv2 response construction, AV pair helpers, challenge response validation, pass-through authentication, workstation/domain logic, and many logon paths.

context.cxx: Session key derivation, TargetInfo construction, and security context helpers.

ntlm.cxx: Package initialization and NTLM SSP glue.

ntlmssp.h: NTLM engine version constants and revision values. Useful for understanding how VERSION fields are built. Nothing much beyond that so it isn’t something many people will explore.

ntlmcomn.h, ntlmsspi.h, and msp.h: These only contain internal structures, package definitions, helper macros, and shared NTLM declarations.

rng.cxx: Random number generation wrapper for NTLM nonces.

encrypt.cxx: Signing and sealing helpers.

public/sdk/inc/ntlmsp.h: NTLM flags, message structures, AV pair definitions, and public-facing constants.

LSA, SSPI, And Negotiate

ds/security/base/lsa/server/ is the Local Security Authority server. This is the broker between logon sessions, authentication packages, SSPI, SAM, policy, and auditing.

negotiat.cxx: The Negotiate/SPNEGO SSP. Mechanism selection, fallback selections across different authenticators, how SPNEGO messages look, the fields set/present and other relevant fun things.

negsupp.cxx: Support functions for Negotiate, including mech list construction and others.

spnego.c and spnego.h: ASN.1 encode/decode structures for SPNEGO tokens.

spmgr.cxx and spinit.cxx: Security package manager and package initialization. Useful for understanding how SSPs are loaded and represented inside LSA.

logons.cxx: Logon processing and LSA logon paths.

sesmgr.cxx: Logon session management.

credmgr.cxx: Credential Manager and cached credential behavior.

samhooks.cxx: LSA-to-SAM bridge for account lookups and authentication validation.

policy.cxx and audit.cxx-style paths where present: Policy and audit plumbing. This is pretty useful I imagine when connecting authentication behavior to Windows security events.

ds/security/base/lsa/security/driver/spn.cxx: SecMakeSPN and SPN construction logic used across Windows components.

SAM And Active Directory Internals

ds/ds/src/sam/server/ is the SAM server and account lookup layer.

usrlogon.c: Logon-time account lookup and validation behavior.

user.c: User object operations and directory search helpers.

dslayer.c: Directory/database access layer used by SAM.

gclookup.c: Global catalog lookup behavior.

ds/ds/src/ntdsa/src/ contains large parts of the Active Directory DSA.

cracknam.c: Name cracking. Useful when researching how Windows converts between UPNs, DNs, SAM names, GUIDs, SIDs, and display-style names.

mapspn.c: SPN mapping logic.

samcache.c: SAM cache behavior inside the DSA.

dsatools.c: Utility routines, including locale and sort-key behavior.

ds/ds/src/ntdsa/dblayer/ is the database layer.

dbisam.c, dbsyntax.c, dbinit.c: ESENT/JET indexing, syntax, and Unicode comparison behavior. These files help explain case-insensitive AD lookups and index behavior.

public/sdk/inc/ntdsapi.h: Directory service constants, including default locale comparison flags such as NORM_IGNORECASE. Very useful when investigating case sensitivity and matching.

LDAP

ds/ds/src/ldap/client/ contains the LDAP client.

bind.cxx: LDAP bind behavior and SPN construction among many other things.

ds/ds/src/ldap/server/ is worth searching when researching LDAP server-side parsing, bind handling, signing, sealing, and directory query behavior.

LDAP is especially useful for security researchers because so many AD tools interact with LDAP first and Kerberos/NTLM second. The source lets you bridge the gap super nicely in this regard between the Microsoft/RFC specifications as well as the behavioral subtleties that come with Windows.

Netlogon And Domain Join

ds/netapi/svcdlls/logonsrv/server/ contains the Netlogon service.

This section is where secure channel behavior, DC location, trust relationships, pass-through authentication, domain controller role behavior, and machine account interactions are all defined and implemented.

ds/netapi/netlib/joinutl.c: Domain join utilities. Machine account creation, default SPN registration, HOST/ SPN prefix behavior, and join-time account setup.

ds/netapi/netlib/ more broadly is worth searching for DC locator and network/domain helper behavior.

DCE/RPC Runtime

com/rpc/runtime/mtrt/ is the connection-oriented RPC runtime.

osfclnt.cxx: RPC client bind and request behavior. Presentation contexts, NDR32/NDR64 negotiation, authenticated binds, auth trailers, verification trailers, alter-context behavior, and request framing are all implemented/written in this file.

osfsvr.cxx: On the opposite end, we have the RPC server bind handling, presentation context negotiation, auth trailer validation, verification trailer processing, and server-side request security.

secclnt.cxx: Here is how RPC authentication has been integrated with SSPI. Authentication services, auth levels, signing/sealing, impersonation, and security context management.

osfpcket.cxx: PDU parsing and size calculations for bind, bind_ack, request, response, fault, alter_context, and auth packets. Nothing particularly useful here for anything more than the most extreme of researchers.

com/rpc/runtime/trans/: Transport providers. Useful when the behavior depends on named pipes, TCP, HTTP proxying, or other RPC transports and you would want to explore/discern between them.

public/sdk/inc/rpcdce.h: A dry file that only has RPC constants, authentication levels, authentication services, and protocol sequence strings.

SMB Redirector

base/fs/rdr2/rdbss/smb.mrx/ is the SMB1 redirector client.

sessetup.c: SMB session setup, CIFS SPN construction, SSPI integration, and authentication exchange behavior.

SMB2 and SMB3 are not in this codebase since they were defined/introduced in Windows Vista, but the authentication integration patterns are still useful and the core appears to be the same in a lot of ways. If you are exploring/learning about SMB deep enough to be reading my blog then this file is worth reading even if the transport has moved on.

Service Control Manager And Remote Registry

base/screg/sc/server/ contains the SCM service.

scopen.cxx: ROpenSCManagerW and machine-name handling.

create.cxx, start.cxx, control.cxx, delete.cxx, query.cxx: Service lifecycle operations. Useful for understanding SCMR semantics behind service creation, start, stop, query, and deletion.

scsec.cxx: SCM access checks and security descriptors.

base/screg/sc/client/ contains the SCM client side.

scbind.cxx: RPC binding behavior for SCM.

base/screg/winreg/ contains Remote Registry service code. Useful for WinReg RPC behavior, registry operation semantics, and remote administration surfaces.

DCOM And WMI

com/ole32/com/dcomrem/ contains DCOM remoting.

This is useful for SPN construction, authentication level and impersonation handling, security blanket negotiation, ORPC headers, and remote COM call behavior.

com/ole32/dcomss/objex/ contains the OXID resolver and object exporter infrastructure. This section comes into play for DCOM activation and pre-flight discovery function calls.

com/ole32/actprops/ contains DCOM activation properties. Useful for remote activation request serialization and activation metadata.

admin/wmi/wbem/ contains WMI. Security-relevant areas include provider hosting, service behavior, repository interaction, remote management, and WMI transport glue.

These sections are ultimately intimately linked especially when one weaponizes WMI/DCOM for lateral movement.

Event Logging And Auditing

base/eventlog/server/ contains the Event Log service. Not much to comment beyond its use for understanding event log storage, clearing, service behavior, and RPC-facing operations.

*.mc files throughout the tree define message/event IDs. Search for these when you want to connect implementation paths to Windows event text.

Kerberos-specific audit and event files such as krbaudit.cxx, events.cxx, kerbevt.mc, and kdcevent.mc are especially useful for blue team research. Keep in mind that while many of these exist, a lot more have been added since and so it’s helpful to keep that in mind.

The pattern I followed and recommend others do when researching Windows or low level behavior in general is to find the behavior, find the error path, find the event call, then find the .mc message definition to understand more about its context.

IIS And Internet Services

inetsrv/ and inetcore/ are the IIS and Internet-services portions of the source code.

For red team researchers, these are useful when authentication is handled by IIS or HTTP services. Within that, the directories are useful for older parsing surfaces, WebDAV-era behavior, ISAPI, SMTP/POP components, and service hardening history.

Keep in mind that many of these have been deprecated or strongly discouraged so it may not map as nicely to the modern ecosystem as it otherwise may have.

Terminal Services

termsrv/ contains Terminal Services/RDP components.

If you are researching remote logon, session creation, credential flow, or interactive logon over RDP-like paths, this directory is worth searching alongside LSA, Winlogon/GINA, and event logging.

Interactive Logon And Credential UI

ds/security/gina/msgina/ contains GINA, the pre-Credential Provider interactive logon UI.

This is old architecture, but still useful for understanding the historical flow from Ctrl+Alt+Del to credential collection to LSA logon calls.

The UI architecture has changed since Server 2003, but many downstream LSA and authentication-package concepts remain recognizable or in some ways unchanged from the brief analysis of the code I did.

Task Scheduler

admin/services/sched/ contains Task Scheduler.

This is useful for scheduled task creation, credential handling, job execution behavior, and administrative surfaces. For defenders, it helps connect protocol-level task creation to service-side behavior. For red teams, it explains what the service expects rather than only what the RPC interface expects and would allow to go through.

Headers Worth Keeping Open

Some files are not glamorous but may come of use to some of you out there:

public/sdk/inc/ntlmsp.h: NTLM flags and message layouts.

public/sdk/inc/rpcdce.h: RPC authn/authz constants and protocol sequence definitions.

public/sdk/inc/ntdsapi.h: Directory service constants and comparison flags.

ds/security/protocols/kerberos/inc/krb5.h and krb5p.h: Kerberos constants.

ds/security/protocols/kerberos/inc/kerbcomm.h: Shared Kerberos names and limits.

ds/security/protocols/msv_sspi/*.h: NTLM/MSV internal structures.

When reading implementation files, keep the relevant header beside them. Half the useful behavior is hidden behind a macro name until you resolve it.

Suggested Research Workflow

When I use this source tree for security research, I usually do it in this order:

  1. Start from a packet capture or event log difference or an ETW trace or a network detection.
  2. Identify the protocol field, flag, string, timestamp, error, or event ID that looks odd.
  3. Read the relevant RFC/MS-* specification for the observed behavior and then refer to the “Appendix B - Product Behavior” section to explore potential hints.
  4. If the RFC/MS-* specifications returned nothing that answered your exact question, move on to search the code base for the constant/string/value of concern via grep/rg etc.
  5. Read the caller, not just the definition as that’s where the answer would usually surface.
  6. Follow the branch that decides whether the field is included, omitted, canonicalized, copied or modified based on some logic in some way.
  7. Compare the finding against modern Windows captures and the specifications to tie in all three for a proper/definitive decision you can be confident in.
  8. Turn it into either a detection feature, a tool-quality improvement, or a note that the behavior is too variable to rely on.

The trick is not just to find a symbol. The trick is to keep walking until you find the decision.

What Makes A Good Detection Candidate

The source can tempt you into turning every difference into an IoC to detect or an improvement/modification to make to your tool, and you should resist that.

A good candidate for changing if you’re a red teamer or detecting if you’re a blue teamer, usually has several properties:

  1. It is produced by Windows in a stable/consistent way across versions you care about meaning either in your target or client’s environment.
  2. It appears before authentication succeeds, or in a place attackers cannot easily normalize without changing their implementation. Bonus points if it’s in an easy to access field, or one that does not require decoding/unpacking (think detecting ACSSI smb2/3 clientGUID).
  3. It combines well with other weak signals or is strong enough as a standalone one.
  4. It is explainable from source and observable in current telemetry as well as being consistent with the relevant specifications (MS-* and RFC).
  5. It survives common enterprise variation such as domain policy, crypto policy, OS build, locale, and application behavior.

Kerberos option bitmasks, etype list shape, NTLM negotiate/version behavior, SPNEGO mech ordering, RPC bind syntax negotiation, and SPN normalization can all be useful. Many more exist in Impacket-IoCs or other places but ultimately the best will be born out of your own native implementation and efforts of analyzing your own/target environments to best fine tune.

Closing Thought

The specifications tell you what’s possible while the source code tells you what’s happening. The specifications are a complete, perfect account of what MAY/SHOULD/MUST/NOT happen when dealing with a specific protocol but they often fail to tell us what does happen and what Windows/Microsoft is doing.

That gap is where the gold is. It is where you find the difference between protocol-valid and Windows-like, between a noisy IoC or a red herring and a potentially important behavior that is worth detecting or removing/coding out of your tool.

For red teams, I hope this blog post can act as a reference for building tools that look less like tools.

For blue teams, a reference for building detections that understand what normal Windows actually looks like.