<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: iamsopotatoe</title>
    <description>The latest articles on DEV Community by iamsopotatoe (@iamsopotatoecoder).</description>
    <link>https://dev.to/iamsopotatoecoder</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3608549%2Fe9eaea80-529a-4e2d-a441-97b7a73c82f1.jpeg</url>
      <title>DEV Community: iamsopotatoe</title>
      <link>https://dev.to/iamsopotatoecoder</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iamsopotatoecoder"/>
    <language>en</language>
    <item>
      <title>TinyLoad v7 — VEH page-fault decryption and a fully encrypted overlay, what's new in TinyLoad v7.0, my open-source PE packer for Windows</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Sun, 31 May 2026 06:44:05 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/tinyload-v7-veh-page-fault-decryption-and-a-fully-encrypted-overlay-whats-new-in-tinyload-v70-2f18</link>
      <guid>https://dev.to/iamsopotatoecoder/tinyload-v7-veh-page-fault-decryption-and-a-fully-encrypted-overlay-whats-new-in-tinyload-v70-2f18</guid>
      <description>&lt;p&gt;TinyLoad v7 is out. if you haven't seen it before — it's a PE&lt;br&gt;
packer for Windows. one .cpp file, no dependencies, MIT.&lt;br&gt;
repo here.&lt;br&gt;
v6 killed the switch statement and split the opcode table. v7 goes&lt;br&gt;
after the two things that were still exploitable — a readable&lt;br&gt;
overlay and a dumpable memory image. both are gone now.&lt;/p&gt;

&lt;p&gt;VEH page-fault decryption&lt;br&gt;
this is the headline feature. instead of decrypting the payload&lt;br&gt;
into a normal RWX allocation and jumping to it, v7 maps every&lt;br&gt;
section as PAGE_NOACCESS after loading:&lt;br&gt;
cpp// after mapping sections, mark them inaccessible&lt;br&gt;
for each section:&lt;br&gt;
    VirtualProtect(page, 0x1000, PAGE_NOACCESS, &amp;amp;old);&lt;br&gt;
    vehPageXor(page, 0x1000, g_vehKey, (uintptr_t)page);&lt;br&gt;
a vectored exception handler catches every ACCESS_VIOLATION,&lt;br&gt;
checks if the fault address is inside the payload range, decrypts&lt;br&gt;
the faulting page, and marks it PAGE_EXECUTE_READWRITE:&lt;br&gt;
cppLONG CALLBACK vehHandler(EXCEPTION_POINTERS* ex) {&lt;br&gt;
    uintptr_t fault = ex-&amp;gt;ExceptionRecord-&amp;gt;ExceptionInformation[1];&lt;br&gt;
    uintptr_t pg = fault &amp;amp; ~(uintptr_t)0xFFF;&lt;br&gt;
    VirtualProtect((LPVOID)pg, 0x1000, PAGE_READWRITE, &amp;amp;old);&lt;br&gt;
    vehPageXor((BYTE*)pg, 0x1000, g_vehKey, pg);&lt;br&gt;
    VirtualProtect((LPVOID)pg, 0x1000, PAGE_EXECUTE_READWRITE, &amp;amp;old);&lt;br&gt;
    // add to LRU cache...&lt;br&gt;
    return EXCEPTION_CONTINUE_EXECUTION;&lt;br&gt;
}&lt;br&gt;
a background watchdog thread scans the LRU cache every 50ms and&lt;br&gt;
re-encrypts any page that hasn't been touched in 200ms, setting it&lt;br&gt;
back to PAGE_NOACCESS. the cache holds 256 slots with&lt;br&gt;
thread-safe eviction.&lt;br&gt;
the result is that at any point in time only the currently&lt;br&gt;
executing page (and recently accessed pages) are ever in plaintext&lt;br&gt;
in memory. a full memory dump gives you at most a few hot pages.&lt;br&gt;
the rest is encrypted noise.&lt;br&gt;
each page uses a unique key: g_vehKey ^ (addr &amp;gt;&amp;gt; 12) — so even&lt;br&gt;
if you recover one page's key you can't decrypt the others.&lt;/p&gt;

&lt;p&gt;fully encrypted overlay&lt;br&gt;
in v6 the overlay was still partially readable — the signature,&lt;br&gt;
metadata fields, and VM bytecode were sitting in the clear. v7&lt;br&gt;
encrypts everything with per-file stub-derived keys:&lt;br&gt;
[stub binary]&lt;br&gt;
[encrypted Tail struct]   — sig, origSz, packSz, flags, dispKey all XOR'ed&lt;br&gt;
[encrypted VM bytecode]   — full bytecode blob XOR'ed with stubKey stream&lt;br&gt;
[chunk 0][junk][chunk 1][junk][chunk 2][junk][chunk 3]  — payload split into 4&lt;br&gt;
[encrypted tail offset]   — 4-byte EOF pointer XOR'ed&lt;br&gt;
the stubKey is derived from a hash of the stub binary itself —&lt;br&gt;
so it's different for every build and can't be precomputed without&lt;br&gt;
the stub. even the 4-byte tail offset at the very end of the file&lt;br&gt;
is encrypted, so the overlay start address isn't readable from a&lt;br&gt;
hex dump.&lt;br&gt;
zero-filler interleaving pads the overlay at a 3:1 ratio giving&lt;br&gt;
roughly 6.73 bits/byte entropy, making it blend visually with&lt;br&gt;
normal PE sections rather than standing out as high-entropy packed&lt;br&gt;
data.&lt;/p&gt;

&lt;p&gt;canary integrity chain&lt;br&gt;
8 canary values are embedded in the VM bytecode. each one is&lt;br&gt;
checked in sequence — if any check fails, a corruption mask&lt;br&gt;
escalates from 1 bit to 8 bits and gets XORed into the decrypted&lt;br&gt;
plaintext. tampering with the overlay produces garbage output&lt;br&gt;
rather than a clean crash, which makes it harder to know if your&lt;br&gt;
modification worked.&lt;/p&gt;

&lt;p&gt;payload chunk splitting&lt;br&gt;
the payload is split into 4 chunks with random junk gaps of&lt;br&gt;
128–640 bytes between them. automated overlay scanners that look&lt;br&gt;
for a contiguous payload region won't find one.&lt;/p&gt;

&lt;p&gt;usage&lt;br&gt;
v7 adds a new --veh flag:&lt;br&gt;
TinyLoad.exe --i myapp.exe --vm --c --veh&lt;br&gt;
build from source:&lt;br&gt;
g++ -o TinyLoad.exe TinyLoad.cpp -static -O2 -s&lt;br&gt;
grab the binary from&lt;br&gt;
releases.&lt;/p&gt;

&lt;p&gt;what's left&lt;br&gt;
the main thing still missing is payload stub dependency — making&lt;br&gt;
the payload actively call back into the stub at runtime so a&lt;br&gt;
reconstructed dump is broken without the stub mapped. that's v8.&lt;br&gt;
if you find files it breaks on, open an issue. star helps a lot ❤️&lt;br&gt;
repo: github.com/iamsopotatoe-coder/TinyLoad&lt;br&gt;
blog: iamsopotatoe-coder.github.io/TinyLoad/#blog&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>security</category>
      <category>windows</category>
      <category>lowlevel</category>
    </item>
    <item>
      <title>TinyLoad v6 — split opcode tables, encrypted dispatch, and control flow flattening</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Tue, 26 May 2026 11:40:13 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/tinyload-v6-split-opcode-tables-encrypted-dispatch-and-control-flow-flattening-mkm</link>
      <guid>https://dev.to/iamsopotatoecoder/tinyload-v6-split-opcode-tables-encrypted-dispatch-and-control-flow-flattening-mkm</guid>
      <description>&lt;p&gt;TinyLoad v6 is out. if you haven't seen it before — it's a PE packer for Windows. one &lt;code&gt;.cpp&lt;/code&gt; file, no dependencies, MIT. &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;repo here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;v5 hardened the stub itself with encrypted strings, IAT wiping, and opmap obfuscation. v6 goes after the two biggest remaining fingerprints: the switch statement in the VM interpreter, and the single contiguous opcode table. both are gone.&lt;/p&gt;




&lt;h2&gt;
  
  
  the problem with a switch statement
&lt;/h2&gt;

&lt;p&gt;every version of TinyLoad up to v5 had a &lt;code&gt;vmRun&lt;/code&gt; function with a giant &lt;code&gt;switch(op)&lt;/code&gt; dispatching 28 opcode handlers. this is the most fingerprint-able thing in any custom VM — disassemblers recognise the pattern immediately, and once you have the handler layout you can reconstruct what each opcode does without ever running the code.&lt;/p&gt;

&lt;p&gt;v6 replaces it entirely with a &lt;strong&gt;computed-goto dispatch table&lt;/strong&gt; using GCC's &lt;code&gt;&amp;amp;&amp;amp;label&lt;/code&gt; extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;s_tbl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;  &lt;span class="c1"&gt;// file scope, filled at runtime&lt;/span&gt;

&lt;span class="c1"&gt;// pack time: read live label addresses, encrypt with random key&lt;/span&gt;
&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;dispKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;s_tbl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;s_tbl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;dispKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// store dispKey in tail&lt;/span&gt;

&lt;span class="c1"&gt;// runtime: decrypt and jump&lt;/span&gt;
&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vmCode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;s_tbl&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;dispKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the label addresses are never plaintext in the packed binary. the packer reads them from its own running process, XORs them with a random key, and stores the result in the tail struct. the packed stub decrypts and recomputes the table at runtime. there's no static jump table to dump, no switch to fingerprint.&lt;/p&gt;




&lt;h2&gt;
  
  
  split opcode decoder
&lt;/h2&gt;

&lt;p&gt;v5 had a single 32-entry opmap, encrypted with one FNV-derived key. one key crack = full opcode table.&lt;/p&gt;

&lt;p&gt;v6 splits 28 opcodes across &lt;strong&gt;four independent 8-entry subtables&lt;/strong&gt;, each with its own key derived from different slices of the payload and VM bytecode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 4 subtables, each XOR-encrypted with independent key&lt;/span&gt;
&lt;span class="n"&gt;BYTE&lt;/span&gt; &lt;span class="n"&gt;sub0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sub1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sub2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sub3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// key for each subtable derived from different data slices&lt;/span&gt;
&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;k0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;packSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vmCode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0..7&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vmCodeSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0..7&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;k2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vmCodeSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;origSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vmCode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;8..15&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;k3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origSz&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;packSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vmCodeSz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;8..15&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sub0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;k0&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;sub1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;k1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;cracking one subtable key reveals at most 8 of 28 opcodes. the other 20 are behind three different keys derived from different data. opcodes are encoded as &lt;code&gt;(subtable_index &amp;lt;&amp;lt; 3) | slot&lt;/code&gt; — every packed file gets a completely different encoding across all four tables.&lt;/p&gt;




&lt;h2&gt;
  
  
  staged entry point and control flow noise
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tryRun&lt;/code&gt; and &lt;code&gt;runInMem&lt;/code&gt; are both broken into stages dispatched through function pointer tables — no linear flow to trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tryRun: s_chk → s_ld → s_prs → s_vm → s_dc → s_ex
runInMem: sp_hdr → sp_map → sp_reloc → sp_import → sp_go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;on top of that, &lt;code&gt;noiseDecrypt()&lt;/code&gt; fires at every stage transition and every 64 VM iterations — calling &lt;code&gt;sdec2&lt;/code&gt; on throwaway buffers with random keys. in a dynamic trace the real string decryption calls (the IAT hook lookups) are buried in identical-looking noise. you can't tell which call matters without executing every path.&lt;/p&gt;




&lt;h2&gt;
  
  
  other stuff in v6
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;full resource cloning&lt;/strong&gt; — switched from hardcoded &lt;code&gt;RT_ICON&lt;/code&gt;/&lt;code&gt;RT_VERSION&lt;/code&gt;/&lt;code&gt;RT_MANIFEST&lt;/code&gt; to &lt;code&gt;EnumResourceTypesA&lt;/code&gt;, so all resource types survive packing now&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LZ compressor fix&lt;/strong&gt; — found a hash-chain self-loop bug that was silently degrading match quality, compression improved ~2% across tested files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PE loader hardening&lt;/strong&gt; — SizeOfBlock underflow guard, reloc bounds validation, negative e_lfanew rejection, import thunk iteration cap, better error propagation throughout&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  current usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TinyLoad.exe --i myapp.exe --vm --c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;build from source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;g++ -o TinyLoad.exe TinyLoad.cpp -static -O2 -s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;grab the binary from &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad/releases" rel="noopener noreferrer"&gt;releases&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  what's left for v7
&lt;/h2&gt;

&lt;p&gt;the one criticism from the RE community that's still open — making a dump worthless. right now once the payload is decrypted in RAM it's self-contained. making it call back into the stub at runtime so a dump without the stub is broken is the hard problem v7 needs to solve.&lt;/p&gt;

&lt;p&gt;also thinking about more opaque predicate variety and bytecode encryption on top of the split subtables.&lt;/p&gt;

&lt;p&gt;if you find files it breaks on, open an issue. star helps a lot ❤️&lt;/p&gt;

&lt;p&gt;repo: &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;github.com/iamsopotatoe-coder/TinyLoad&lt;/a&gt;&lt;br&gt;
blog: &lt;a href="https://iamsopotatoe-coder.github.io/TinyLoad/#blog" rel="noopener noreferrer"&gt;iamsopotatoe-coder.github.io/TinyLoad/#blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;don't use this to pack malware — legitimate use only.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>windows</category>
      <category>packer</category>
      <category>security</category>
    </item>
    <item>
      <title>TinyLoad v5 — encrypted strings, opmap obfuscation, and IAT wiping</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Tue, 19 May 2026 12:02:12 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/tinyload-v5-encrypted-strings-opmap-obfuscation-and-iat-wiping-26fd</link>
      <guid>https://dev.to/iamsopotatoecoder/tinyload-v5-encrypted-strings-opmap-obfuscation-and-iat-wiping-26fd</guid>
      <description>&lt;p&gt;TinyLoad v5 is out. if you haven't seen the project before — it's a PE packer for Windows. you give it an &lt;code&gt;.exe&lt;/code&gt;, it compresses and VM-encrypts it into a self-extracting stub that runs the original entirely in RAM. one &lt;code&gt;.cpp&lt;/code&gt; file, no dependencies, MIT. &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;repo here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;v4 added opaque predicates, anti-debug, and section scrambling. v5 is about hardening the stub itself — the code that actually runs when the packed exe launches. three main additions: &lt;strong&gt;encrypted API strings&lt;/strong&gt;, &lt;strong&gt;content-derived opmap obfuscation&lt;/strong&gt;, and &lt;strong&gt;IAT wiping post-load&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  quick recap
&lt;/h2&gt;

&lt;p&gt;when you run a TinyLoad packed exe, the stub spins up a custom 32-opcode VM interpreter, executes a decryption bytecode program against the payload, then manually maps the original PE into memory and runs it. every packed file gets a randomly shuffled opcode table so the bytecode looks different every time.&lt;/p&gt;

&lt;p&gt;v5 makes the stub itself harder to analyse statically.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. encrypted API strings
&lt;/h2&gt;

&lt;p&gt;in v4 the DLL and function names the stub needs — &lt;code&gt;kernel32.dll&lt;/code&gt;, &lt;code&gt;GetModuleHandleA&lt;/code&gt;, &lt;code&gt;VirtualAlloc&lt;/code&gt; etc — were sitting as plaintext strings in the binary. any static analysis tool would immediately see what APIs the stub calls just from strings view.&lt;/p&gt;

&lt;p&gt;v5 XOR-encrypts all of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BYTE&lt;/span&gt; &lt;span class="n"&gt;_ed_k32&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x3A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x3A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x3A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x6A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x3E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x30&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BYTE&lt;/span&gt; &lt;span class="n"&gt;_ed_gmha&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x5D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x4D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x58&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x5A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x2C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x07&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BYTE&lt;/span&gt; &lt;span class="n"&gt;_ed_gpa&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x06&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x09&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x2E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x3E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x3D&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;each one decrypts at runtime with a rolling XOR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;sdec2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;no readable strings in the binary anymore. strings view on the packed output is clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. opmap obfuscation via FNV hash
&lt;/h2&gt;

&lt;p&gt;this one directly addresses feedback from the r/ReverseEngineering community who pointed out that the opmap decode table was sitting plaintext right behind the stub — effectively a silver platter for static analysis.&lt;/p&gt;

&lt;p&gt;v5 derives a per-file XOR mask for the opmap using FNV-1a hash, seeded from the file's own content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;xorOpmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;opmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Tail&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vmCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x811C9DC5u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mh"&gt;0x01000193u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origSz&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;packSz&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vmCodeSz&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;vmLim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vmCodeSz&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vmCodeSz&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vmCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;vmLim&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vmCode&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;payLim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;packSz&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;packSz&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;payLim&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;NUM_OPS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;opmap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the hash feeds on origSz, packSz, vmCodeSz, and the first 32 bytes of both the VM bytecode and the payload. the resulting mask is different for every single packed file — you can't extract the opmap from one sample and apply it to another. the stub runs xorOpmap at unpack time to recover the real table before passing it to the VM.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. IAT wiping post-load
&lt;/h2&gt;

&lt;p&gt;after the stub manually maps the packed PE into memory and resolves all its imports, v5 zeroes out the import structures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// kill recovery&lt;/span&gt;
&lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OriginalFirstThunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OptionalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataDirectory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IMAGE_DIRECTORY_ENTRY_IMPORT&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;VirtualAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OptionalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataDirectory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;IMAGE_DIRECTORY_ENTRY_IMPORT&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;once imports are resolved the IAT entries aren't needed anymore. zeroing them means if someone dumps the process from memory after load, the import directory is gone — no DLL names, no function names, no reconstruction path from the dump alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. dead code and junk VM instructions
&lt;/h2&gt;

&lt;p&gt;v5 also adds dead code functions in the stub (&lt;code&gt;__attribute__((used))&lt;/code&gt; to force the compiler to keep them despite &lt;code&gt;-O2&lt;/code&gt;) and junk &lt;code&gt;NOP&lt;/code&gt; and self-mov instructions scattered through the VM bytecode to inflate and confuse disassembly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;NOP_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;              &lt;span class="c1"&gt;// junk&lt;/span&gt;
&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;MOV_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// junk — mov r6, r6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;minor individually but they add noise on top of everything else.&lt;/p&gt;




&lt;h2&gt;
  
  
  current usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;TinyLoad&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt; &lt;span class="na"&gt;--i &lt;/span&gt;&lt;span class="kd"&gt;myapp&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt; &lt;span class="na"&gt;--vm --c
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;build from source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;g++ &lt;span class="nt"&gt;-o&lt;/span&gt; TinyLoad.exe TinyLoad.cpp &lt;span class="nt"&gt;-static&lt;/span&gt; &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or grab the binary from &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad/releases" rel="noopener noreferrer"&gt;releases&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;if you run into files it doesn't pack correctly, open an issue. and if you find it useful, a star helps a lot ❤️&lt;/p&gt;

&lt;p&gt;repo: &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;github.com/iamsopotatoe-coder/TinyLoad&lt;/a&gt;&lt;br&gt;
blog + changelogs: &lt;a href="https://iamsopotatoe-coder.github.io/TinyLoad/#blog" rel="noopener noreferrer"&gt;iamsopotatoe-coder.github.io/TinyLoad/#blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>security</category>
      <category>windows</category>
      <category>lowlevel</category>
    </item>
    <item>
      <title>i added opaque predicates, anti-debug, and section obfuscation to my PE packer published</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Fri, 15 May 2026 08:21:29 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/i-added-opaque-predicates-anti-debug-and-section-obfuscation-to-my-pe-packerpublished-17bd</link>
      <guid>https://dev.to/iamsopotatoecoder/i-added-opaque-predicates-anti-debug-and-section-obfuscation-to-my-pe-packerpublished-17bd</guid>
      <description>&lt;p&gt;TinyLoad v4 is out — here's what i added and why it actually matters: &lt;/p&gt;

&lt;p&gt;so TinyLoad v4 just dropped. if you don't know TinyLoad — it's my open-source PE packer for Windows. you throw an exe at it, it compresses and encrypts it with a custom VM, and spits out a self-extracting stub that runs the original entirely in RAM. no temp files, no installer, nothing written to disk. one &lt;code&gt;.cpp&lt;/code&gt; file, no dependencies. &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;here's the repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;v3 was already a decent jump because that's when the custom VM came in — randomised opcode shuffling so every packed file speaks a different instruction set. v4 is more focused. three specific additions that each solve a different analysis problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VM opaque predicates&lt;/li&gt;
&lt;li&gt;anti-debug checks&lt;/li&gt;
&lt;li&gt;PE section name obfuscation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;let me actually explain each one instead of just listing them.&lt;/p&gt;




&lt;h2&gt;
  
  
  opaque predicates — confusing static analysis
&lt;/h2&gt;

&lt;p&gt;when someone's trying to reverse a packed binary statically, they're usually building a control flow graph — figuring out which code actually runs and in what order. an opaque predicate is a branch where the outcome is predetermined (always taken, or never taken) but looks like it could go either way without executing anything.&lt;/p&gt;

&lt;p&gt;in v4 the VM bytecode that gets generated for decryption starts with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// load two constants into registers&lt;/span&gt;
&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;LDI_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;e64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x1337&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;LDI_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;e64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x1338&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// compare them — r8 will always be 1 since 0x1337 &amp;lt; 0x1338&lt;/span&gt;
&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;CMP_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// jump if nonzero — always jumps&lt;/span&gt;
&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;JNZ_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="n"&gt;eR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;opqPatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;e32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// this HLT is dead code — never reached&lt;/span&gt;
&lt;span class="n"&gt;eOp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;HLT_I&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// real decryption loop starts here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the halt never actually executes. but a static analyser that doesn't evaluate the constants just sees a branch with two possible destinations — one being an immediate halt before any decryption happens. it has to either run the code to know the truth, or assume both paths are possible.&lt;/p&gt;

&lt;p&gt;what makes this play nicely with the rest of TinyLoad is that opcodes are randomly shuffled at pack time. so &lt;code&gt;0x1337&lt;/code&gt; and &lt;code&gt;0x1338&lt;/code&gt; appear as completely different byte values in every packed file. no two samples look the same, which makes pattern matching across samples a lot harder too.&lt;/p&gt;




&lt;h2&gt;
  
  
  anti-debug — making dynamic analysis awkward
&lt;/h2&gt;

&lt;p&gt;static analysis not working? fine, let's just run it in a debugger. v4 adds two checks before the loader does anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;isDebugged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsDebuggerPresent&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;BOOL&lt;/span&gt; &lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;CheckRemoteDebuggerPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetCurrentProcess&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;IsDebuggerPresent&lt;/code&gt; catches the obvious stuff — x64dbg, WinDbg attached locally. &lt;code&gt;CheckRemoteDebuggerPresent&lt;/code&gt; goes a level deeper and catches kernel debuggers and remote sessions that the first check doesn't see.&lt;/p&gt;

&lt;p&gt;if either one fires, the loader just returns false silently. no error, no crash, no messagebox. the packed exe runs and does absolutely nothing. that kind of silent failure is more disorienting than a visible error because it's not immediately obvious &lt;em&gt;why&lt;/em&gt; nothing happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  section obfuscation — breaking heuristic scanners
&lt;/h2&gt;

&lt;p&gt;PE packers often get detected not from what the code does but from what the file &lt;em&gt;looks like&lt;/em&gt; — and section names are one of the things scanners latch onto. if a packer consistently leaves behind a section called &lt;code&gt;.tinyld&lt;/code&gt; or whatever the compiler decided to name things, that's a trivial signature.&lt;/p&gt;

&lt;p&gt;after writing the packed output, v4 goes back into the PE headers and renames every section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;scrambleSections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IMAGE_NT_HEADERS64&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_NT_HEADERS64&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dos&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;e_lfanew&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;IMAGE_SECTION_HEADER&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IMAGE_FIRST_SECTION&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;".text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".rdata"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".bss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".idata"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;FileHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumberOfSections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;strncpy&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;sect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the payload itself lives in an overlay &lt;em&gt;after&lt;/em&gt; the sections, not inside any of them, so renaming changes nothing about how the exe actually runs. but from the outside the file looks like any other plain Windows binary — &lt;code&gt;.text&lt;/code&gt;, &lt;code&gt;.data&lt;/code&gt;, &lt;code&gt;.rdata&lt;/code&gt;. nothing to fingerprint.&lt;/p&gt;




&lt;h2&gt;
  
  
  putting it all together
&lt;/h2&gt;

&lt;p&gt;grab the binary from &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad/releases" rel="noopener noreferrer"&gt;releases&lt;/a&gt; or build it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;g++ &lt;span class="nt"&gt;-o&lt;/span&gt; TinyLoad.exe TinyLoad.cpp &lt;span class="nt"&gt;-static&lt;/span&gt; &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;usage hasn't changed much from v3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TinyLoad.exe --i myapp.exe --vm --c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--vm&lt;/code&gt; is the custom VM encryption (now with the opaque predicates and anti-debug baked in). &lt;code&gt;--c&lt;/code&gt; is LZ77 compression. compression runs first, then VM encryption goes on top — so the compressed stream's patterns get hidden too. you need at least one of the two flags.&lt;/p&gt;




&lt;p&gt;if you find files it breaks on, open an issue. and if you use it, a star really does help ❤️&lt;/p&gt;

&lt;p&gt;repo: &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;github.com/iamsopotatoe-coder/TinyLoad&lt;/a&gt;&lt;br&gt;
blog + changelogs: &lt;a href="https://iamsopotatoe-coder.github.io/TinyLoad/#blog" rel="noopener noreferrer"&gt;iamsopotatoe-coder.github.io/TinyLoad/#blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>security</category>
      <category>lowlevel</category>
      <category>windows</category>
    </item>
    <item>
      <title>How I load an exe directly into memory without touching disk — manual PE mapping</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Wed, 06 May 2026 12:15:13 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/how-i-load-an-exe-directly-into-memory-without-touching-disk-manual-pe-mapping-24le</link>
      <guid>https://dev.to/iamsopotatoecoder/how-i-load-an-exe-directly-into-memory-without-touching-disk-manual-pe-mapping-24le</guid>
      <description>&lt;p&gt;most people think running an exe means writing it to disk first. it doesn't. &lt;/p&gt;

&lt;p&gt;as part of building TinyLoad, a Windows PE packer, I had to write a PE loader that maps an executable directly into memory and runs it without ever creating a file. here's how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  what is a PE file
&lt;/h2&gt;

&lt;p&gt;PE (Portable Executable) is the format Windows uses for .exe and .dll files. it's basically a structured blob with a header describing how to load it, followed by sections containing code, data, resources etc.&lt;/p&gt;

&lt;p&gt;to run a PE file manually you have to do what the Windows loader does — but yourself, in memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  step 1: parse the headers
&lt;/h2&gt;

&lt;p&gt;every PE starts with a DOS header, then an NT header. the NT header tells you everything you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SizeOfImage&lt;/code&gt; — how much memory to allocate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ImageBase&lt;/code&gt; — where the linker expected the binary to live&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddressOfEntryPoint&lt;/code&gt; — where to jump to start execution&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SizeOfHeaders&lt;/code&gt; — how much of the front to copy as-is
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;IMAGE_DOS_HEADER&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_DOS_HEADER&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;IMAGE_NT_HEADERS64&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_NT_HEADERS64&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dos&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;e_lfanew&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  step 2: allocate memory and copy sections
&lt;/h2&gt;

&lt;p&gt;allocate a block of memory the size of the image, then copy the headers in. after that, iterate the section table and copy each section to its virtual address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VirtualAlloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OptionalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SizeOfImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MEM_COMMIT&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MEM_RESERVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PAGE_EXECUTE_READWRITE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OptionalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SizeOfHeaders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;IMAGE_SECTION_HEADER&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IMAGE_FIRST_SECTION&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;FileHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumberOfSections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;SizeOfRawData&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;VirtualAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;PointerToRawData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;sect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;SizeOfRawData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  step 3: fix relocations
&lt;/h2&gt;

&lt;p&gt;the linker assumed the binary would load at &lt;code&gt;ImageBase&lt;/code&gt;. if it lands somewhere else (which it usually does since ASLR), every absolute address in the binary is wrong by &lt;code&gt;delta = actual_base - preferred_base&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;the relocation table tells you exactly which addresses need fixing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OptionalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_BASE_RELOCATION&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;relDir&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;VirtualAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;VirtualAddress&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;WORD&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WORD&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SizeOfBlock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_BASE_RELOCATION&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WORD&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;IMAGE_REL_BASED_DIR64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;VirtualAddress&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFF&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_BASE_RELOCATION&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SizeOfBlock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  step 4: resolve imports
&lt;/h2&gt;

&lt;p&gt;the import directory tells you which DLLs the binary needs and which functions to load from each. iterate the import descriptors, load each DLL, resolve each function by name or ordinal, and write the function addresses into the import address table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_IMPORT_DESCRIPTOR&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;impDir&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;VirtualAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HMODULE&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LoadLibraryA&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;thunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_THUNK_DATA64&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;FirstThunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;orig&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_THUNK_DATA64&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OriginalFirstThunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressOfData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_SNAP_BY_ORDINAL64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;thunk&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;GetProcAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;orig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMAGE_IMPORT_BY_NAME&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;orig&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressOfData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;thunk&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;GetProcAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;thunk&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;orig&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;imp&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  step 5: jump to entry point
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;EntryPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WINAPI&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
&lt;span class="n"&gt;EntryPoint&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntryPoint&lt;/span&gt;&lt;span class="p"&gt;)((&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;OptionalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressOfEntryPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that's it. the exe runs directly from the allocated memory block, no file on disk, no Windows loader involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  where this is used in TinyLoad
&lt;/h2&gt;

&lt;p&gt;TinyLoad packs your exe with LZ77 compression and VM encryption. when the packed stub runs, it decrypts and decompresses the original exe in memory, then calls this loader directly. the original exe never exists as a file — it goes straight from encrypted bytes to running process.&lt;/p&gt;

&lt;p&gt;full source (single .cpp file, no deps): &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;https://github.com/iamsopotatoe-coder/TinyLoad&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>programming</category>
      <category>security</category>
      <category>packing</category>
    </item>
    <item>
      <title>I built a PE packer with a custom VM that randomizes its own instruction set every build — single .cpp file, no deps</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Tue, 05 May 2026 13:26:48 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/i-built-a-pe-packer-with-a-custom-vm-that-randomizes-its-own-instruction-set-every-build-single-2285</link>
      <guid>https://dev.to/iamsopotatoecoder/i-built-a-pe-packer-with-a-custom-vm-that-randomizes-its-own-instruction-set-every-build-single-2285</guid>
      <description>&lt;p&gt;PE packers are one of those things that sound complex until you actually build one. then they sound complex again but for different reasons.&lt;/p&gt;

&lt;p&gt;I built TinyLoad — a Windows PE packer in a single C++ file with no external dependencies. v3 just shipped and the main change is replacing rolling XOR with a custom virtual machine for decryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it does
&lt;/h2&gt;

&lt;p&gt;you give it an exe. it spits out a new exe that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;stores your original exe compressed and VM-encrypted inside itself&lt;/li&gt;
&lt;li&gt;at runtime, spins up a tiny VM interpreter, runs the decryption bytecode, and loads the result directly into RAM via manual PE mapping&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;the packed exe never writes anything to disk. the original binary just materializes in memory and runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  why a VM instead of XOR
&lt;/h2&gt;

&lt;p&gt;rolling XOR is trivially broken. an analyst sets a breakpoint on &lt;code&gt;VirtualAlloc&lt;/code&gt;, runs the stub, and dumps the decrypted payload from memory. takes about 30 seconds. the decryption loop is native x86 and immediately recognizable to any disassembler.&lt;/p&gt;

&lt;p&gt;the VM changes the problem completely. instead of decrypting with native x86 instructions, the stub runs a custom bytecode interpreter. the decryption logic is stored as VM bytecode — not x86. there's no native decryption loop to find.&lt;/p&gt;

&lt;p&gt;more importantly: the opcode table is randomly shuffled at pack time and baked into the binary. every packed file has a completely different instruction set. an analyst can't reuse their work across builds. they have to reverse the interpreter first, then figure out what the bytecode is doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  how the VM works
&lt;/h2&gt;

&lt;p&gt;20 opcodes: loads, moves, arithmetic, bitwise ops, memory read/write, jumps, halt. nothing exotic.&lt;/p&gt;

&lt;p&gt;at pack time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate a random permutation of the 20 opcode IDs&lt;/li&gt;
&lt;li&gt;emit the decryption program using the shuffled opcode table&lt;/li&gt;
&lt;li&gt;encrypt the payload with the matching key&lt;/li&gt;
&lt;li&gt;store: opmap + bytecode + encrypted payload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;at runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read the opmap from the binary&lt;/li&gt;
&lt;li&gt;run the interpreter against the bytecode&lt;/li&gt;
&lt;li&gt;the bytecode decrypts the payload byte by byte&lt;/li&gt;
&lt;li&gt;load and execute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the cipher is a 128-bit stream cipher using rotl/rotr key mixing. the keys are embedded as immediates directly inside the VM program — there are no separate key fields in the file format.&lt;/p&gt;

&lt;h2&gt;
  
  
  the compression
&lt;/h2&gt;

&lt;p&gt;custom LZ77 with hash-chain matching, 64KB sliding window, and lazy evaluation. runs on the raw PE first, then VM encryption goes on top. on a 3MB test exe it compressed down to ~878KB before encryption, so the final packed output was ~1.9MB including the ~1MB stub.&lt;/p&gt;

&lt;p&gt;PE files compress well because of their structure — repeated import patterns, padding sections, predictable headers.&lt;/p&gt;

&lt;h2&gt;
  
  
  single file, no dependencies
&lt;/h2&gt;

&lt;p&gt;the whole thing compiles with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;g++ &lt;span class="nt"&gt;-o&lt;/span&gt; TinyLoad.exe TinyLoad.cpp &lt;span class="nt"&gt;-static&lt;/span&gt; &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;no cmake, no vcpkg, no submodules. the LZ77, VM interpreter, bytecode emitter, PE loader, and resource cloner are all in one ~400 line .cpp file.&lt;/p&gt;

&lt;h2&gt;
  
  
  usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TinyLoad.exe --i myapp.exe --vm --c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--vm&lt;/code&gt; for VM encryption, &lt;code&gt;--c&lt;/code&gt; for LZ77 compression. you need at least one.&lt;/p&gt;

&lt;h2&gt;
  
  
  what's next
&lt;/h2&gt;

&lt;p&gt;v4 is going to be PE-aware compression — preprocessing the binary structure before LZ77 to get better ratios on code sections specifically. probably also a more complex key schedule inside the VM program.&lt;/p&gt;

&lt;p&gt;source: &lt;a href="https://github.com/iamsopotatoe-coder/TinyLoad" rel="noopener noreferrer"&gt;https://github.com/iamsopotatoe-coder/TinyLoad&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pepacker</category>
      <category>compression</category>
      <category>packer</category>
      <category>virtualmachine</category>
    </item>
    <item>
      <title>Why I Built My Own Windows Installer in C#</title>
      <dc:creator>iamsopotatoe</dc:creator>
      <pubDate>Wed, 12 Nov 2025 18:39:24 +0000</pubDate>
      <link>https://dev.to/iamsopotatoecoder/why-i-built-my-own-windows-installer-in-c-5ap9</link>
      <guid>https://dev.to/iamsopotatoecoder/why-i-built-my-own-windows-installer-in-c-5ap9</guid>
      <description>&lt;p&gt;Ive been working on C# projects lately, and I kept running into the same problem: making a simple installer was way too complicated. NSIS, Inno Setup, WiX etc.. were way to complicated.&lt;/p&gt;

&lt;p&gt;So I decided to build my own: Flex Installer.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;p&gt;Downloads your app from Dropbox (GitHub support coming soon)&lt;/p&gt;

&lt;p&gt;Installs it on Windows&lt;/p&gt;

&lt;p&gt;Adds an uninstaller entry in Windows Settings&lt;/p&gt;

&lt;p&gt;Fully customizable via a config file&lt;/p&gt;

&lt;p&gt;Its simple: customize the config file to your liking and add your dropbox link and done!&lt;/p&gt;

&lt;p&gt;Its open source on GitHub: &lt;a href="https://github.com/iamsopotatoe-coder/Flex-Installer" rel="noopener noreferrer"&gt;https://github.com/iamsopotatoe-coder/Flex-Installer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still in early development but it works.&lt;/p&gt;

&lt;p&gt;Id love to hear feedback, suggestions, or ideas for features from anyone!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>csharp</category>
      <category>resources</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
