<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Articles tagged x86 at The Segfault Garden</title>
  <link rel="alternate" type="text/html"
        href="https://blog.segv.page/tags/x86/"/>
  <link rel="self" type="application/atom+xml"
        href="https://blog.segv.page/tags/x86/feed/"/>
  <updated>2026-06-02T01:40:41Z</updated>
  <id>urn:uuid:763a3ddc-a1df-4bad-b03e-86513dc3c50c</id>

  <author>
    <name>Lu</name>
    <uri>https://blog.segv.page</uri>
    <email>frgmntedflower@linux.com</email>
  </author>

  
    
  
    
  
    
  
    
  
    
  
    
  <entry>
    <title>CompanionCube32: DLL Injection, VTable Hooking, and a Custom Overlay</title>
    <link rel="alternate" type="text/html" href="https://blog.segv.page/blog/2026/06/01/CompanionCube32-DLL-injection-vtable-hooking-overlay/"/>
    <id>urn:uuid:e841b2e7-3a5d-4f8c-9b1d-6a2c8e4f0b3a</id>
    <updated>2026-06-01T02:00:00Z</updated>
    <category term="cpp"/><category term="win32"/><category term="x86"/>
    <content type="html">
      <![CDATA[<p>CompanionCube32 is a game overlay companion that injects into a running
process and renders custom UI on top of its output. It uses three techniques
that build on each other: DLL injection to get code running inside the target
process, virtual table hooking to intercept rendering calls, and a custom
overlay drawn via DirectX.</p>

<h2 id="dll-injection">DLL Injection</h2>

<p>The entry point is <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> targeting <code class="language-plaintext highlighter-rouge">LoadLibraryW</code>. The
injector process opens a handle to the target with <code class="language-plaintext highlighter-rouge">PROCESS_ALL_ACCESS</code>,
writes the DLL path into remote memory via <code class="language-plaintext highlighter-rouge">VirtualAllocEx</code>/<code class="language-plaintext highlighter-rouge">WriteProcessMemory</code>,
and spawns a remote thread that loads the DLL. Error handling is minimal but
effective — if <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> fails we fall back to <code class="language-plaintext highlighter-rouge">SetWindowsHookEx</code>
with a <code class="language-plaintext highlighter-rouge">WH_GETMESSAGE</code> hook that loads the DLL on the next message pump
iteration. This second path is slower but works on terminals where
<code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> is blocked.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">HANDLE</span> <span class="n">hThread</span> <span class="o">=</span> <span class="n">CreateRemoteThread</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span>
    <span class="p">(</span><span class="n">LPTHREAD_START_ROUTINE</span><span class="p">)</span><span class="n">LoadLibraryW</span><span class="p">,</span>
    <span class="n">pRemotePath</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hThread</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// fallback: WH_GETMESSAGE hook</span>
    <span class="n">SetWindowsHookEx</span><span class="p">(</span><span class="n">WH_GETMESSAGE</span><span class="p">,</span> <span class="n">hookProc</span><span class="p">,</span> <span class="n">hDll</span><span class="p">,</span> <span class="n">dwThreadId</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="vtable-hooking">VTable Hooking</h2>

<p>Once the DLL lives inside the target, we need to intercept the game’s
rendering pipeline. The approach is standard vtable patching: locate the
virtual function table of the <code class="language-plaintext highlighter-rouge">D3DDevice</code> or <code class="language-plaintext highlighter-rouge">SwapChain</code> object, change
<code class="language-plaintext highlighter-rouge">Protect</code> to <code class="language-plaintext highlighter-rouge">PAGE_READWRITE</code>, swap the <code class="language-plaintext highlighter-rouge">Present</code> or <code class="language-plaintext highlighter-rouge">DrawIndexed</code> entry
with our trampoline, and restore protection.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DWORD</span> <span class="n">oldProtect</span><span class="p">;</span>
<span class="n">VirtualProtect</span><span class="p">(</span><span class="n">vtable</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">),</span> <span class="n">PAGE_READWRITE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">oldProtect</span><span class="p">);</span>
<span class="n">originalPresent</span> <span class="o">=</span> <span class="p">(</span><span class="n">PresentFn</span><span class="p">)</span><span class="n">vtable</span><span class="p">[</span><span class="n">index</span><span class="p">];</span>
<span class="n">vtable</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">hookPresent</span><span class="p">;</span>
<span class="n">VirtualProtect</span><span class="p">(</span><span class="n">vtable</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">),</span> <span class="n">oldProtect</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">oldProtect</span><span class="p">);</span>
</code></pre></div></div>

<p>The hook function receives the device pointer and can chain back to the
original. We keep it minimal — store the device pointer, call through to
the original, then trigger our overlay draw.</p>

<h2 id="the-overlay">The Overlay</h2>

<p>Rendering happens through a separate DirectX 11 device and immediate
context created on a dedicated thread. The overlay uses its own swap chain
with <code class="language-plaintext highlighter-rouge">DXGI_SWAP_CHAIN_FLAG_OVERLAY</code> where supported, or renders into a
screen-space aligned quad via the hooked device. Text rendering goes
through <code class="language-plaintext highlighter-rouge">ID3D11DeviceContext::Draw</code> with a prebuilt font atlas stored as a
shader resource view.</p>

<p>The overlay thread synchronises with the hook via a <code class="language-plaintext highlighter-rouge">std::atomic&lt;bool&gt;</code>
that flips when the hooked Present returns, ensuring we never draw over a
partially swapped frame.</p>

<h2 id="results">Results</h2>

<p>The injector loads in under 10ms, the hook resolves the vtable in roughly
a microsecond, and the overlay runs at the target’s native framerate with
negligible overhead. The whole pipeline — inject, hook, render — fits in
about 600 lines of C++ and has held up across a handful of DirectX 11
titles.</p>

]]>
    </content>
  </entry>
    
  

</feed>
