<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pastukhov Aleksey</title>
    <description>The latest articles on DEV Community by Pastukhov Aleksey (@steelfactor).</description>
    <link>https://dev.to/steelfactor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3874473%2F96013ba5-f022-4623-90ae-e5cbf4c964fb.png</url>
      <title>DEV Community: Pastukhov Aleksey</title>
      <link>https://dev.to/steelfactor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/steelfactor"/>
    <language>en</language>
    <item>
      <title>Deep inside the COM: Reading Windows ROT Without Asking Permission. Detective story</title>
      <dc:creator>Pastukhov Aleksey</dc:creator>
      <pubDate>Sun, 10 May 2026 17:22:52 +0000</pubDate>
      <link>https://dev.to/steelfactor/deep-inside-the-com-reading-windows-rot-without-asking-permission-story-about-reverse-1gkp</link>
      <guid>https://dev.to/steelfactor/deep-inside-the-com-reading-windows-rot-without-asking-permission-story-about-reverse-1gkp</guid>
      <description>&lt;p&gt;This is Part 4 of the "Inside the Running Object Table" series. Parts 1-3 covered the public COM API and rpcss internals. This one is about going further &amp;amp; getting it wrong several times before getting it right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## The goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GetRunningObjectTable()&lt;/code&gt; returns 15 entries on my machine. We wanted to read the same table &lt;strong&gt;without calling that function at all&lt;/strong&gt;, directly from rpcss memory. Without ole32 &amp;amp; ALPC. Raw &lt;code&gt;ReadProcessMemory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The motivation: the public API filters. AppContainer entries disappear. Security policy silently drops others. We wanted the unfiltered view.&lt;/p&gt;

&lt;p&gt;Simple idea. But suddenly aint simple path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Phase 1. Ghidra and the structure hunt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We opened &lt;code&gt;rpcss.dll&lt;/code&gt; in Ghidra, loaded the PDB from Microsoft's symbol server, and searched for &lt;code&gt;ROT&lt;/code&gt; in the Symbol Table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;CScmRotEntry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetAllowAnyClient&lt;/span&gt;
&lt;span class="n"&gt;CScmRotEntry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetProcessID&lt;/span&gt;
&lt;span class="n"&gt;CScmRotEntry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;
&lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aint &lt;code&gt;CROTEntry&lt;/code&gt;. &lt;strong&gt;&lt;code&gt;CScmRotEntry&lt;/code&gt;&lt;/strong&gt;. SCM: Service Control Manager. The ROT is wired into the service layer at a level the documentation never mentions.&lt;br&gt;
Me just reverse-engineered the full structure from constructors alone. Constructors are better than documentation - every field gets initialized with its exact offset. Two hours of reading decompiled C later, discovered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt;            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;vtable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// +0x00&lt;/span&gt;
    &lt;span class="n"&gt;longlong&lt;/span&gt;        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pNext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// +0x08  linked list&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwRefCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// +0x10&lt;/span&gt;
    &lt;span class="n"&gt;tagInterfaceData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pIFaceData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x18&lt;/span&gt;
    &lt;span class="n"&gt;_MnkEqBuf&lt;/span&gt;       &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pMnkEqBuf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// +0x20  moniker comparison buffer&lt;/span&gt;
    &lt;span class="n"&gt;CToken&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// +0x28  integrity level token&lt;/span&gt;
    &lt;span class="n"&gt;ushort&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pwszName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// +0x30  display name&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CScmRotEntry&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;  &lt;span class="n"&gt;dwMagic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// +0x50  = 0x746F7263 = "crot"&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;  &lt;span class="n"&gt;dwROTFlags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x68  bit 1 = AllowAnyClient&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic signature &lt;code&gt;"crot"&lt;/code&gt; at &lt;code&gt;+0x50&lt;/code&gt; was a gift. The hash table: 251 buckets, rolling hash &lt;code&gt;(hash * 3 ^ byte) % 0xFB&lt;/code&gt;.&lt;br&gt;
We also found &lt;code&gt;gpscmrot&lt;/code&gt; - the global pointer to the CScmRot object, at Ghidra address &lt;code&gt;0x180161270&lt;/code&gt;. RVA: &lt;code&gt;0x161270&lt;/code&gt;.&lt;br&gt;
Now had a map. Time to read the territory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Phase 2. The RVA that lied&lt;/strong&gt;&lt;br&gt;
With &lt;code&gt;SeDebugPrivilege&lt;/code&gt; and &lt;code&gt;ReadProcessMemory&lt;/code&gt;, opened the svchost hosting rpcss.dll and read the gpscmrot slot:&lt;br&gt;
First hit: &lt;code&gt;u"ROTFlags"&lt;/code&gt; at &lt;code&gt;0x180136070&lt;/code&gt;. One xref. Inside a very large function. But the Symbol Table itself gave us the real prize:&lt;br&gt;
rpcssBase + 0x161270 → 0x0000000000000000&lt;/p&gt;

&lt;p&gt;NULL. Every time. On a live system with 15 ROT entries visible via ole32.&lt;br&gt;
Dumped 64 bytes around the address. The slot was genuinely zero. &lt;code&gt;gpProcessList&lt;/code&gt; - also zero. &lt;code&gt;gScmMgot&lt;/code&gt; - zero.&lt;br&gt;
Spent time scanning every memory region in rpcss for the &lt;code&gt;"crot"&lt;/code&gt; magic. MEM_PRIVATE. MEM_MAPPED. All regions. Zero results. We searched for &lt;code&gt;L"Personal-Monikers"&lt;/code&gt; across every svchost process. Nothing.&lt;br&gt;
At this point the honest assessment was: &lt;strong&gt;we don't know where the data is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Phase 3. The LOAD/STORE revelation&lt;/strong&gt;&lt;br&gt;
Then came the constructor analysis of &lt;code&gt;CScmRot::Register&lt;/code&gt;. In Ghidra's &lt;br&gt;
write-site xref listing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1800ffe77: MOV qword ptr [gpscmrot], RSI   ← STORE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The instruction that &lt;strong&gt;writes&lt;/strong&gt; &lt;code&gt;gpscmrot&lt;/code&gt; is a STORE: &lt;code&gt;mov [mem], reg&lt;/code&gt;.&lt;br&gt;
Our behavioral scorer was built to find &lt;strong&gt;LOAD&lt;/strong&gt; instructions: &lt;code&gt;mov reg, [rip+disp]&lt;/code&gt;.&lt;br&gt;
We had been hunting in the right neighborhood but looking at the wrong side of the street. The constructor &lt;em&gt;writes&lt;/em&gt; to &lt;code&gt;gpscmrot&lt;/code&gt;. The functions that &lt;em&gt;use&lt;/em&gt; it read from it. The scorer finds readers, not writers.&lt;br&gt;
So when the scorer found a candidate at &lt;code&gt;0x160DE8&lt;/code&gt; instead of &lt;code&gt;0x161270&lt;/code&gt;, it was not wrong , it found a &lt;em&gt;different&lt;/em&gt; global, one that functions &lt;em&gt;read from&lt;/em&gt; with the same CScmRot usage pattern. Two different addresses. Both valid entry points into the same object graph.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;instruction VA:  0x1800ffe77
next RIP:        0x1800ffe7e
displacement:    0x000613f2
gpscmrot slot:   0x1800ffe7e + 0x613f2 = 0x180161270  ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The math was right all along. The object just happened to be NULL in the svchost we were reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Phase 4, Two svchosts, one ROT&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;rpcss.dll&lt;/code&gt; was loaded in &lt;strong&gt;two different svchost processes&lt;/strong&gt;.&lt;br&gt;
&lt;code&gt;EnumProcesses&lt;/code&gt; returns processes in an unspecified order. We were consistently landing in the svchost where rpcss was loaded but the ROT was not yet initialized, a secondary instance handling a different service role.&lt;br&gt;
The fix: check whether &lt;code&gt;gpscmrot&lt;/code&gt; is non-null before committing to a process.&lt;br&gt;
&lt;code&gt;&lt;br&gt;
DWORD_PTR slotAddr = rpcssBase + RVA_GPSCMROT;&lt;br&gt;
DWORD_PTR pScmRot  = 0;&lt;br&gt;
if (ReadPtr(hProc, slotAddr, &amp;amp;pScmRot) &amp;amp;&amp;amp; pScmRot != 0) {&lt;br&gt;
    return pid; // this is the active instance&lt;br&gt;
}&lt;br&gt;
// null → wrong svchost, keep looking&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
One loop change. Suddenly everything was non-null.&lt;/p&gt;
&lt;h2&gt;
  
  
  Phase 5  The behavioral scorer
&lt;/h2&gt;

&lt;p&gt;We built a parallel approach: find &lt;code&gt;gpscmrot&lt;/code&gt; dynamically from binary behavior, without trusting the hardcoded RVA.&lt;/p&gt;

&lt;p&gt;Any function managing CScmRot does all of these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load a global pointer via &lt;code&gt;mov reg, [rip+disp]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Call a mutex  &lt;code&gt;CMutexSem2::Request&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Access &lt;code&gt;[base + 0x30]&lt;/code&gt;  registration counter&lt;/li&gt;
&lt;li&gt;Take address of &lt;code&gt;[base + 0x38]&lt;/code&gt;  CScmRotHintTable&lt;/li&gt;
&lt;li&gt;Take address of &lt;code&gt;[base + 0x48]&lt;/code&gt;  CScmHashTable
Microsoft can rename &lt;code&gt;gpscmrot&lt;/code&gt;. They can strip PDB symbols. They cannot remove the mutex, the counter, and the hash table without breaking COM entirely.
We scored 2 points per pattern, +4 bonus for all four simultaneously.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[] Scoring 2048 functions...
[] Candidates: 407
[*] Best score: 13  slot: 0x00007FFC15460DE8
uses_lock    = true
uses_[+0x30] = true
uses_[+0x38] = true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Score 13. Maximum possible. Found without symbols, without hardcoded addresses, from behavior alone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CScmRot + 0x48  →  CScmHashTable
(CScmHashTable) →  bucket array (251 entries)
bucket[i]        →  CScmRotEntry
+0x50            →  "crot" magic (validate)
+0x20            →  _MnkEqBuf
_MnkEqBuf + 0x14 →  moniker name (uppercase wide string)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_MnkEqBuf&lt;/code&gt; buffer has 4 bytes size header, 12 bytes COM metadata, then the name. Final output with Office open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[ROT][bucket 000]  C:\USERS\USER\SOURCE\REPOS\IBULDOSER\IBULDOSER.SLN
[ROT][bucket 011]  !PERSONAL-MONIKERS::STORAGEPROVIDERSEARCHHANDLER
[ROT][bucket 020]  !VISUALSTUDIO.DTE.18.0:12600
[ROT][bucket 192]  C:\USERS\USER\APPDATA...[redacted].XLAM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[+] Total ROT entries found: 22&lt;br&gt;
ole32 had shown 15. We found 22. The 7 extra entries are what the public API silently discards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## What is the key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The LOAD/STORE distinction matters.&lt;/strong&gt; Ghidra shows where a variable is written. Your scanner needs where it is read. These can be different globals.&lt;br&gt;
&lt;strong&gt;NULL is not evidence of absence.&lt;/strong&gt; Two processes loaded rpcss.dll. Only one had an initialized ROT. Always validate the pointer before committing to a process.&lt;br&gt;
&lt;strong&gt;Behavior survives refactoring. Addresses don't.&lt;/strong&gt; The scorer found a valid CScmRot using four behavioral signals that cannot be removed without breaking Windows COM. Hardcoded RVAs last until the next update. Behavioral signatures last until the architecture changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The buffer is always 16 bytes ahead of the string.&lt;/strong&gt; A lesson in reading COM serialization formats the hard way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Phase 6 — Reading the table&lt;/strong&gt;&lt;br&gt;
With a valid &lt;code&gt;CScmRot*&lt;/code&gt;, the rest was mechanical:&lt;br&gt;
uses_[+0x48] = true`&lt;/p&gt;

&lt;p&gt;The write-site calculation confirmed &lt;code&gt;0x161270&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Code&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/ssteelfactor-oss/iBuldoser" rel="noopener noreferrer"&gt;github.com/ssteelfactor-oss/iBuldoser&lt;/a&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>c</category>
      <category>security</category>
      <category>reverseengineering</category>
    </item>
    <item>
      <title>Reverse Engineering rpcss.dll: Hunting for the ROT's Hidden Structure</title>
      <dc:creator>Pastukhov Aleksey</dc:creator>
      <pubDate>Wed, 06 May 2026 13:51:47 +0000</pubDate>
      <link>https://dev.to/steelfactor/reverse-engineering-rpcssdll-hunting-for-the-rots-hidden-structure-part-of-the-inside-the-2o36</link>
      <guid>https://dev.to/steelfactor/reverse-engineering-rpcssdll-hunting-for-the-rots-hidden-structure-part-of-the-inside-the-2o36</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part IV(?) of the "Inside the Running Object Table" series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I started this research, I assumed finding the internal layout of the Running Object Table would be straightforward. Microsoft documents IRunningObjectTable at the API level, surely the implementation couldn't be far behind. I was wrong. What followed was a Ghidra session that turned up something far more interesting than I expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does the ROT actually live?&lt;/strong&gt;&lt;br&gt;
First surprise: there is no rpcss.exe on disk.&lt;br&gt;
What Windows Task Manager shows as rpcss.exe is actually svchost.exe hosting rpcss.dll, classic service host pattern. The DLL lives at C:\Windows\System32\rpcss.dll and gets mapped into a dedicated svchost instance at boot. Every ROT operation your process makes through ole32.dll crosses an ALPC channel and lands in that DLL's code.&lt;br&gt;
So that's our target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading rpcss.dll into Ghidra&lt;/strong&gt;&lt;br&gt;
Standard procedure -drag, drop, let the auto-analyzer run. The interesting part comes when you load the PDB. Microsoft publishes symbol files for most system DLLs through their public symbol server, and rpcss.dll is no exception.&lt;br&gt;
Grab symchk.exe from your Windows SDK Debuggers folder and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;symchk&lt;/span&gt; &lt;span class="kd"&gt;C&lt;/span&gt;:\Windows\System32\rpcss.dll &lt;span class="se"&gt;^
&lt;/span&gt;    &lt;span class="na"&gt;/s &lt;/span&gt;&lt;span class="kd"&gt;srv&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kd"&gt;C&lt;/span&gt;:\Symbols&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kd"&gt;https&lt;/span&gt;://msdl.microsoft.com/download/symbols
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point Ghidra at the downloaded PDB and suddenly FUN_18006XXXX becomes something readable. This is where it gets interesting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First hit: CScmRotEntry&lt;/strong&gt;&lt;br&gt;
Searching the Symbol Table for ROT turns up the string u"ROTFlags" at address 180136070. Xref later, going inside a large function. But the real find is in the Symbol Table itself:&lt;br&gt;
CScmRotEntry::GetAllowAnyClient&lt;br&gt;
Not CROTEntry. Not CRunningObjectTableEntry. CScmRotEntry - SCM stands for Service Control Manager. This tells us something immediately, the ROT isn't just a COM subsystem. It's wired into the service infrastructure at a deeper level than the documentation suggests.&lt;br&gt;
The decompilation of GetAllowAnyClient reveals the first concrete field offset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x68&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// bit 1 = AllowAnyClient flag&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The real structure: LookupObjectInROT&lt;/strong&gt;&lt;br&gt;
Searching for CScmRot surfaces LookupObjectInROT - a function that takes ACTIVATION_PARAMS and walks the table looking for a matching object. From here we follow the call into CScmRot::GetObject, which is where the actual traversal happens.&lt;br&gt;
The decompilation reveals the first architectural surprise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;cplVar13&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longlong&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longlong&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;lpCriticalSection&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;uVar9&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ROT is not a &lt;strong&gt;linked list&lt;/strong&gt;. It's a &lt;strong&gt;hash table&lt;/strong&gt; with 251 buckets (0xFB).&lt;br&gt;
Each bucket is a pointer to a chain of CScmRotEntry objects. The hash function is a simple rolling hash over the moniker bytes:&lt;br&gt;
chash = ((hash * 3) ^ byte) % 0xFB;&lt;br&gt;
Aint cryptographic n complex. Fast and collision-tolerant for the expected workload of a few dozen registered objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Digging deeper: the class hierarchy&lt;/strong&gt;&lt;br&gt;
Listing all symbols with the CScmRot prefix turns up the full method table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CScmRot::EnumRunning
CScmRot::GetEntryFromScmReg
CScmRot::GetTimeOfLastChange
CScmRot::IsRunning
CScmRot::Register
CScmRot::Revoke
CScmRotEntry::CScmRotEntry
CScmRotEntry::GetProcessID
CScmRotEntry::IsValid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things stand out. First, CScmRotEntry::CScmRotEntry it's a constructor, meaning this is a proper class with its own lifecycle. Second, CScmRotEntry::GetProcessID -every ROT entry stores the PID of the registering process. That field doesn't appear anywhere in the public API documentation.&lt;br&gt;
Opening the constructor reveals something else entirely: CScmRotEntry is not a flat structure. It inherits from a base class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CScmRotEntry
    └── CScmRotMgotEntryBase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Mgot" itsa likely Marshaled Global Object Table. The base class constructor initializes most of the core fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;param_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;param_2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;_MnkEqBuf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;param_3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CToken&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;param_4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ushort&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;param_5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tagInterfaceData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;param_6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;param_7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;param_8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;undefined8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x08&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// pNext = NULL&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;undefined4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// ref count = 1&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tagInterfaceData&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ushort&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// display name&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;param_7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// flags&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_MnkEqBuf&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// moniker eq buffer&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CToken&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x28&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// integrity token&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param_4&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;LOCK&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;param_4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// manual AddRef&lt;/span&gt;
        &lt;span class="n"&gt;UNLOCK&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And CScmRotEntry adds its own fields on top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x746f7263&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// magic: "crot"&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x54&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_FILETIME&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x58&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;param_4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// last change time&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tagInterfaceData&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mh"&gt;0x68&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// ROTFlags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete structure map is being combining both constructors gives us the full layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;CScmRotMgotEntryBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt;            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;vtable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// +0x00&lt;/span&gt;
    &lt;span class="n"&gt;longlong&lt;/span&gt;         &lt;span class="n"&gt;pNext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// +0x08  next in bucket chain&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwRefCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x10  starts at 1&lt;/span&gt;
    &lt;span class="n"&gt;tagInterfaceData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pIFaceData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// +0x18  marshaled interface&lt;/span&gt;
    &lt;span class="n"&gt;_MnkEqBuf&lt;/span&gt;       &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pMnkEqBuf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x20  moniker comparison buffer&lt;/span&gt;
    &lt;span class="n"&gt;CToken&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// +0x28  integrity level token&lt;/span&gt;
    &lt;span class="n"&gt;ushort&lt;/span&gt;          &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pwszName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// +0x30  display name (wide string)&lt;/span&gt;
    &lt;span class="n"&gt;longlong&lt;/span&gt;         &lt;span class="n"&gt;unk_0x38&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// +0x38&lt;/span&gt;
    &lt;span class="n"&gt;BYTE&lt;/span&gt;             &lt;span class="n"&gt;bFlags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// +0x40  bit 0 = various flags&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwField_44&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x44&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwField_48&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x48&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwField_4c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x4c&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Derived class&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;CScmRotEntry&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CScmRotMgotEntryBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwMagic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// +0x50  0x746F7263 = "crot"&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwField_54&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x54&lt;/span&gt;
    &lt;span class="n"&gt;_FILETIME&lt;/span&gt;        &lt;span class="n"&gt;ftLastChange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// +0x58  registration timestamp&lt;/span&gt;
    &lt;span class="n"&gt;tagInterfaceData&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pIFaceData2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// +0x60  second interface data&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt;            &lt;span class="n"&gt;dwROTFlags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// +0x68  bit 1 = AllowAnyClient&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Three findings worth highlighting&lt;/strong&gt;&lt;br&gt;
The "crot" magic signature at +0x50. Every valid CScmRotEntry in memory carries the ASCII bytes c, r, o, t at this offset. This is almost certainly what CScmRotEntry::IsValid checks. For a memory scanner, this is a gift: you can locate entries by signature without knowing the head of the hash table.&lt;br&gt;
CToken uses manual reference counting. The increment at param_4 + 8 is wrapped in LOCK/UNLOCK - interlocked, not COM AddRef. This means tokens are shared across multiple ROT entries from the same process using a private refcount mechanism entirely separate from COM lifetime management.&lt;br&gt;
pwszName at +0x30 is the display name. This is the wide string you'd normally get from IMoniker::GetDisplayName. Reading it directly means a memory scanner doesn't need to deserialize tagInterfaceData or call any COM API to get human-readable moniker names — the string is right there in the entry.&lt;/p&gt;

&lt;p&gt;The security picture&lt;br&gt;
Walking CScmRot::GetObject surfaces three distinct access control mechanisms operating in sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;cRotEntryIsTrusted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plVar11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;RotMgotEntryIsAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plVar11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="n"&gt;CToken&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IsTokenILLower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plVar11&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;plVar13&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trust check, accessibility check, integrity level comparison in that order. When multiple entries match the same moniker, the one with the higher integrity level wins. A sandboxed process cannot shadow a medium-integrity registration. AppContainer entries get discarded entirely for out-of-container clients — we even found the log string in the code:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
"Disregarding ROT entry registered by AppContainer"&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
The ROT has a real access control model. It's just not documented anywhere near as clearly as the public API suggests.&lt;/p&gt;

&lt;p&gt;Code and tooling: github.com/ssteelfactor-oss&lt;/p&gt;

&lt;p&gt;[to be continued...]&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>microsoft</category>
      <category>security</category>
    </item>
    <item>
      <title>NetEnum: legitimate API scanning tool</title>
      <dc:creator>Pastukhov Aleksey</dc:creator>
      <pubDate>Sun, 19 Apr 2026 12:47:39 +0000</pubDate>
      <link>https://dev.to/steelfactor/netenum-legitimate-api-scanning-tool-17i8</link>
      <guid>https://dev.to/steelfactor/netenum-legitimate-api-scanning-tool-17i8</guid>
      <description>&lt;p&gt;NetEnum is a compact, purpose-built network and service enumeration tool, written in plain C, focused on one core idea: &lt;strong&gt;collecting intelligence through “legitimate” APIs and standard system interfaces&lt;/strong&gt; rather than noisy, signature-heavy probing. That design choice makes it especially useful in environments where you need &lt;strong&gt;reliable discovery&lt;/strong&gt; while keeping activity &lt;strong&gt;indistinguishable from normal administrative telemetry&lt;/strong&gt;—a practical advantage when EDR, IDS, and other detection layers are tuned to flag aggressive scanners.&lt;/p&gt;

&lt;h2&gt;
  
  
  What NetEnum is (and what it is not)
&lt;/h2&gt;

&lt;p&gt;Unlike classic scanners that spray packets, brute-force banners, or rely on exploit-style fingerprinting, NetEnum is intended to &lt;strong&gt;enumerate using the same mechanisms that real enterprise tooling uses&lt;/strong&gt;: vendor-supported endpoints, OS-provided management interfaces, and other approved channels. In other words, it aims to behave like routine operations—inventory, auditing, compliance checks—rather than “attack-like” reconnaissance.&lt;/p&gt;

&lt;p&gt;This makes NetEnum a good fit for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;internal asset inventory and continuous discovery,&lt;/li&gt;
&lt;li&gt;lab validation of monitoring baselines,&lt;/li&gt;
&lt;li&gt;security assessments where you want realistic operator behavior,&lt;/li&gt;
&lt;li&gt;blue team exercises that measure what can be learned from sanctioned interfaces alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why “legitimate API scanning” matters
&lt;/h2&gt;

&lt;p&gt;Modern corporate networks are full of valuable signals exposed through APIs: identity systems, management planes, cloud services, directory services, configuration endpoints, and monitoring stacks. Those systems exist specifically so administrators and automation can query them safely.&lt;/p&gt;

&lt;p&gt;NetEnum’s philosophy is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prefer official read paths&lt;/strong&gt; (documented endpoints, authenticated queries, normal tooling behaviors).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimize noisy traffic patterns&lt;/strong&gt; (avoid broad port blasting when high-confidence information is already available via an API).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce anomalous fingerprints&lt;/strong&gt; (keep requests close to typical management and inventory workflows).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lower visibility to EDR / IDS (in the practical sense)
&lt;/h2&gt;

&lt;p&gt;EDR and IDS commonly trigger on patterns associated with classic scanning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;high-rate connection attempts across many ports/hosts,&lt;/li&gt;
&lt;li&gt;unusual protocols or malformed probes,&lt;/li&gt;
&lt;li&gt;repeated banner grabs,&lt;/li&gt;
&lt;li&gt;tool-specific packet signatures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When enumeration is performed through &lt;strong&gt;standard API calls and normal management interfaces&lt;/strong&gt;, the observable footprint is often closer to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;legitimate admin scripts,&lt;/li&gt;
&lt;li&gt;IT automation,&lt;/li&gt;
&lt;li&gt;health checks and inventory tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That doesn’t mean “undetectable”—good monitoring can still catch unusual authentication patterns, abnormal query volumes, or access outside an expected role. But it does mean NetEnum is designed to &lt;strong&gt;avoid the obvious scanner-shaped telemetry&lt;/strong&gt; that many detection stacks prioritize.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for controlled, responsible use
&lt;/h2&gt;

&lt;p&gt;NetEnum is best used where you have permission and clear scope: your own infrastructure, sanctioned assessments, or test environments. The “legitimate API” approach is powerful precisely because it leverages trusted interfaces—so it should be paired with proper governance, logging, and access control. Used correctly, it becomes a high-signal inventory and visibility tool that complements (rather than replaces) traditional network scanning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If your goal is to understand an environment with &lt;strong&gt;less noise and fewer detection spikes&lt;/strong&gt;, NetEnum is built around a modern enumeration strategy: &lt;strong&gt;learn as much as possible from the same APIs and management planes that enterprises already rely on&lt;/strong&gt;. That yields actionable discovery while keeping collection closer to normal operational behavior—an advantage in networks where EDR/IDS is sensitive to classic recon patterns.&lt;/p&gt;

&lt;p&gt;If you want, paste your README (or tell me the key features/modules you want highlighted), and I’ll tailor the post to match NetEnum’s exact capabilities and include a short “How it works” section without revealing anything unsafe.&lt;/p&gt;

&lt;p&gt;Link to: &lt;a href="https://github.com/ssteelfactor-oss/NetEnum" rel="noopener noreferrer"&gt;https://github.com/ssteelfactor-oss/NetEnum&lt;/a&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>com</category>
      <category>c</category>
      <category>scanning</category>
    </item>
    <item>
      <title>Part 3: putting it al l together</title>
      <dc:creator>Pastukhov Aleksey</dc:creator>
      <pubDate>Mon, 13 Apr 2026 17:24:59 +0000</pubDate>
      <link>https://dev.to/steelfactor/part-3-putting-it-al-l-together-2k9d</link>
      <guid>https://dev.to/steelfactor/part-3-putting-it-al-l-together-2k9d</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;windows.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;objbase.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;CoInitializeEx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COINIT_APARTMENTTHREADED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;IRunningObjectTable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;IEnumMoniker&lt;/span&gt;        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;IBindCtx&lt;/span&gt;            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pCtx&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;IMoniker&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ULONG&lt;/span&gt;     &lt;span class="n"&gt;fetched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;GetRunningObjectTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;EnumRunning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;CreateBindCtx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pCtx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fetched&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;S_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;LPOLESTR&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUCCEEDED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;wprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;CoTaskMemFree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pCtx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pCtx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;CoUninitialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compile with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;cl&lt;/span&gt; &lt;span class="kd"&gt;rot_enum&lt;/span&gt;.c &lt;span class="kd"&gt;ole32&lt;/span&gt;.lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you'll actually see - on my comp the output looks like:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmdtiweic2psadq2ssec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmdtiweic2psadq2ssec.png" alt="Image description: Running Object Table as is"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>security</category>
      <category>com</category>
      <category>c</category>
    </item>
    <item>
      <title>Part 2: How It Works Under the Hood</title>
      <dc:creator>Pastukhov Aleksey</dc:creator>
      <pubDate>Sun, 12 Apr 2026 12:16:14 +0000</pubDate>
      <link>https://dev.to/steelfactor/part-2-how-it-works-under-the-hood-4a87</link>
      <guid>https://dev.to/steelfactor/part-2-how-it-works-under-the-hood-4a87</guid>
      <description>&lt;p&gt;Before diggin into, it's useful to consider a question that most COM tutorials never address: where does the Running Object Table actually reside?&lt;br&gt;
The ROT isnt a data structure inside the calling process, nor is it a kernel object. &lt;/p&gt;

&lt;p&gt;It exists as a global, session-scoped table inside rpcss.exe, the RPC Subsystem process, and is shared by all processes running in the same Windows session.&lt;br&gt;
When code calls GetRunningObjectTable() func, it does not receive a direct pointer to the table. Instead, it receives a proxy object that implements the IRunningObjectTable interface. Every method invoked on this proxy is marshaled across a local RPC channel (ALPC) to rpcss.exe. The actual mapping of monikers to live objects never leaves that system process.&lt;/p&gt;

&lt;p&gt;As a result, every ROT operation: Register, Revoke, IsRunning, GetObject, etc is an inter-process call. The overhead is insignificant for occasional use, but it must be taken into account when these functions are invoked inside tight loops or performance-critical code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API chain&lt;/strong&gt;&lt;br&gt;
The full enumeration path will be looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GetRunningObjectTable()
    └── IRunningObjectTable::EnumRunning()
            └── IEnumMoniker::Next()
                    └── IMoniker::GetDisplayName()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1: Get a handle to the ROT&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;HRESULT&lt;/span&gt; &lt;span class="n"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;IRunningObjectTable&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetRunningObjectTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entry point - GetRunningObjectTable(). 1-argument is reserved and must be zero. On success it gives you IRunningObjectTable, proxy to rpcss.exe. Always check SUCCEEDED(hr) before proceeding; if the RPC channel to rpcss is broken, it fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Get an enumerator&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;IEnumMoniker&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;EnumRunning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pROT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EnumRunning() returns IEnumMoniker, a standard COM enumerator over the monikers currently registered in the table. &lt;br&gt;
This is a snapshot: entries registered after this call won't appear, entries revoked before you iterate won't be missing from the snapshot either - enumerator captures state at the moment of the call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Iterate&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;IMoniker&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;ULONG&lt;/span&gt; &lt;span class="n"&gt;fetched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fetched&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;S_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// process pMon&lt;/span&gt;
    &lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next() func follows the standard COM enumerator contract: request items, get back how many were actually returned. &lt;br&gt;
Ask for one at a time it's simpler error handlingn. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Decode the moniker&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;IBindCtx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;CreateBindCtx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pCtx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;LPOLESTR&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lpVtbl&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GetDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pMon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;wprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;L"%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;CoTaskMemFree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GetDisplayName() requires a bind context. IBindCtx- even though we're only reading a name, not actually binding. The bind context carries parameters that affect how monikers resolve; for display purposes we pass a default one via CreateBindCtx(0, ...).&lt;br&gt;
Don't forget to free memory with callong CoTaskMemFree().&lt;/p&gt;

&lt;p&gt;To be continued&lt;/p&gt;

</description>
      <category>windows</category>
      <category>com</category>
      <category>security</category>
      <category>c</category>
    </item>
    <item>
      <title>Inside the Running Object Table: COM's Hidden Registry of Live Objects</title>
      <dc:creator>Pastukhov Aleksey</dc:creator>
      <pubDate>Sun, 12 Apr 2026 11:20:02 +0000</pubDate>
      <link>https://dev.to/steelfactor/inside-the-running-object-table-coms-hidden-registry-of-live-objects-3k64</link>
      <guid>https://dev.to/steelfactor/inside-the-running-object-table-coms-hidden-registry-of-live-objects-3k64</guid>
      <description>&lt;p&gt;&lt;strong&gt;Inside the Running Object Table: COM's Hidden Registry of Live Objects&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Introduction: Running Object Table, part I *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"...almost no developer ever looks at directly", by me.&lt;/p&gt;

&lt;p&gt;COM is a technology that many developers believe they understand until they encounter its less-documented internals. Most are familiar with the core concepts — interfaces, CoCreateInstance, and reference counting — yet the Component Object Model contains considerably more supporting infrastructure than its public API reveals. One of the least examined parts of this infrastructure is the Running Object Table, commonly abbreviated as ROT.&lt;/p&gt;

&lt;p&gt;The ROT is a system-wide registry that holds references to live COM objects. It does not store class information or factory objects; it contains only instantiated objects that have been explicitly registered as running and available for external access. In essence, it serves as the mechanism by which a COM server can declare that a particular object is active and can be located by name. A client that needs to connect to an already-running instance checks the ROT before creating a new object.&lt;/p&gt;

&lt;p&gt;This component plays a more important role than is commonly recognized. The ROT is the underlying mechanism used by GetObject() in VBScript, it enables Visual Studio to expose its automation model to external scripts, and it supports certain inter-process communication patterns in Windows without the need for explicit sockets or named pipes. At the same time, the ROT is an area that is frequently misunderstood and can be misused.&lt;br&gt;
Currently registered on a live Windows system, decoding of the monikers they expose, and analysis of what these findings indicate about the runtime behavior of COM.&lt;br&gt;
In C, without ATL, without MFC, without training wheels. Just enumerate what's actually registered on a live Windows system, decode the monikers i find, and talk about what the results reveal about COM's runtime model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwryt1s0vlimxjimonn6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwryt1s0vlimxjimonn6k.png" alt=" " width="659" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/steelfactor/part-2-how-it-works-under-the-hood-4a87"&gt;To be continued&lt;/a&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>com</category>
      <category>security</category>
      <category>c</category>
    </item>
  </channel>
</rss>
