<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JM00NJ</title>
    <description>The latest articles on DEV Community by JM00NJ (@jm00nj).</description>
    <link>https://dev.to/jm00nj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4005327%2Fcdc71a8f-4480-456e-b794-da1d549753c0.png</url>
      <title>DEV Community: JM00NJ</title>
      <link>https://dev.to/jm00nj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jm00nj"/>
    <language>en</language>
    <item>
      <title>VESQER: A DPCM+RLE Hybrid Compressor in Pure x64 Assembly</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Wed, 01 Jul 2026 11:55:06 +0000</pubDate>
      <link>https://dev.to/jm00nj/vesqer-a-dpcmrle-hybrid-compressor-in-pure-x64-assembly-2gdh</link>
      <guid>https://dev.to/jm00nj/vesqer-a-dpcmrle-hybrid-compressor-in-pure-x64-assembly-2gdh</guid>
      <description>&lt;h2&gt;
  
  
  The Origin Story
&lt;/h2&gt;

&lt;p&gt;At 5 AM, while brainstorming ways to minimize the wire footprint of a custom ICMP-based C2 agent, an idea hit me: &lt;em&gt;"What if I just store the first byte, calculate the difference to the next one, and count how many times that same difference repeats?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The logic was dead simple. No dictionaries, no Huffman trees, no frequency tables — just subtraction and counting. Two primitive operations that could fit in a handful of x64 instructions.&lt;/p&gt;

&lt;p&gt;I felt like a genius... until I realized I had just accidentally reinvented &lt;strong&gt;Differential Pulse-Code Modulation (DPCM) combined with Run-Length Encoding (RLE)&lt;/strong&gt; — techniques formalized by telecom engineers in the 1950s-1970s.&lt;/p&gt;

&lt;p&gt;But here's the thing: writing it completely from scratch in &lt;strong&gt;pure x86-64 Assembly&lt;/strong&gt;, with zero libc, zero dependencies, and under 200 instructions — that part was entirely new. And it works &lt;em&gt;beautifully&lt;/em&gt; for its intended purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Why Compress C2 Traffic?
&lt;/h2&gt;

&lt;p&gt;In a covert channel scenario, every byte matters. Consider a typical &lt;code&gt;ls -la /root&lt;/code&gt; output: ~450 bytes. Over ICMP with 56-byte chunk fragmentation, that's &lt;strong&gt;9 packets&lt;/strong&gt; on the wire. Each packet is a detection opportunity — a chance for an IDS to correlate, flag, and alert.&lt;/p&gt;

&lt;p&gt;Now compress that 450 bytes down to ~250 bytes. You just dropped to &lt;strong&gt;5 packets&lt;/strong&gt;. Fewer packets means less traffic, smaller timing windows, and a reduced statistical footprint. The math is simple: &lt;strong&gt;less wire time = less detection surface&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But the compressor itself must be tiny. In a PIC (Position Independent Code) shellcode context, you can't afford to drag in zlib or LZ4. You need something that compiles to a few dozen instructions and requires zero heap allocation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Algorithm: DPCM + RLE Hybrid
&lt;/h2&gt;

&lt;p&gt;The VESQER algorithm operates in two conceptual layers that execute in a single pass:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Delta Encoding (DPCM):&lt;/strong&gt; Instead of storing raw byte values, we store the &lt;em&gt;difference&lt;/em&gt; (delta) between consecutive bytes. If the input is &lt;code&gt;A B C D&lt;/code&gt; (ASCII 65, 66, 67, 68), the deltas are all &lt;code&gt;+1&lt;/code&gt;. This transforms sequential patterns into repetitive values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — Run-Length Encoding (RLE):&lt;/strong&gt; Once the deltas are calculated, identical consecutive deltas are collapsed into a single &lt;code&gt;(count, delta)&lt;/code&gt; pair. Those four bytes &lt;code&gt;A B C D&lt;/code&gt; become: &lt;code&gt;Anchor: A&lt;/code&gt; + &lt;code&gt;(3, +1)&lt;/code&gt; — just 3 bytes instead of 4.&lt;/p&gt;

&lt;p&gt;The real power appears with longer sequences. &lt;code&gt;ABCDEFGHIJ&lt;/code&gt; (10 bytes) becomes &lt;code&gt;A&lt;/code&gt; + &lt;code&gt;(9, +1)&lt;/code&gt; — only 3 bytes. That's a &lt;strong&gt;70% reduction&lt;/strong&gt; from two simple operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Compression Format
&lt;/h3&gt;

&lt;p&gt;The output binary format is minimal and signature-free:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Anchor Byte] [Count₁] [Delta₁] [Count₂] [Delta₂] ... [CountN] [DeltaN]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anchor (1 byte):&lt;/strong&gt; The literal first byte of the input. This is the reference point for the entire stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count (1 byte, unsigned):&lt;/strong&gt; How many times to apply the associated delta. Range: 0–255.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delta (1 byte, signed via two's complement):&lt;/strong&gt; The difference to add for each step. Range: -128 to +127.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No magic bytes. No headers. No metadata. Just raw compressed data — exactly what you want when avoiding static signatures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk-Through: How It Actually Works
&lt;/h2&gt;

&lt;p&gt;Let's trace the algorithm with a concrete example. Given the input string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"AABCDFFF333A"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;bytes)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1 — Anchor Setup:&lt;/strong&gt;&lt;br&gt;
Read the first byte &lt;code&gt;A&lt;/code&gt; (0x41) and write it directly to the output. This is our anchor — the starting reference point. The compressor now peeks at the second byte &lt;code&gt;A&lt;/code&gt; and calculates the initial delta: &lt;code&gt;0x41 - 0x41 = 0&lt;/code&gt;. Active Delta is set to &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Main Compression Loop:&lt;/strong&gt;&lt;br&gt;
The compressor walks through the remaining bytes. For each byte, it calculates &lt;code&gt;current - previous&lt;/code&gt; and compares it to the Active Delta:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Position&lt;/th&gt;
&lt;th&gt;Char&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Previous&lt;/th&gt;
&lt;th&gt;Delta&lt;/th&gt;
&lt;th&gt;Active Delta&lt;/th&gt;
&lt;th&gt;Match?&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;0x41&lt;/td&gt;
&lt;td&gt;A (0x41)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Count++ → 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;0x42&lt;/td&gt;
&lt;td&gt;A (0x41)&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FLUSH&lt;/strong&gt; (1, 0) → New delta: +1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;0x43&lt;/td&gt;
&lt;td&gt;B (0x42)&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Count++ → 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;0x44&lt;/td&gt;
&lt;td&gt;C (0x43)&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Count++ → 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;td&gt;0x46&lt;/td&gt;
&lt;td&gt;D (0x44)&lt;/td&gt;
&lt;td&gt;+2&lt;/td&gt;
&lt;td&gt;+1&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FLUSH&lt;/strong&gt; (3, +1) → New delta: +2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;td&gt;0x46&lt;/td&gt;
&lt;td&gt;F (0x46)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;+2&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FLUSH&lt;/strong&gt; (1, +2) → New delta: 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;td&gt;0x46&lt;/td&gt;
&lt;td&gt;F (0x46)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Count++ → 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0x33&lt;/td&gt;
&lt;td&gt;F (0x46)&lt;/td&gt;
&lt;td&gt;-19&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FLUSH&lt;/strong&gt; (2, 0) → New delta: -19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0x33&lt;/td&gt;
&lt;td&gt;3 (0x33)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;-19&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FLUSH&lt;/strong&gt; (1, -19) → New delta: 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0x33&lt;/td&gt;
&lt;td&gt;3 (0x33)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Count++ → 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;0x41&lt;/td&gt;
&lt;td&gt;3 (0x33)&lt;/td&gt;
&lt;td&gt;+14&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FLUSH&lt;/strong&gt; (2, 0) → New delta: +14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;EOF&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FINAL FLUSH&lt;/strong&gt; (1, +14)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Compressed Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0x41] [01,00] [03,01] [01,02] [02,00] [01,ED] [02,00] [01,0E]
  A     1×(0)  3×(+1)  1×(+2)  2×(0)  1×(-19) 2×(0)   1×(+14)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;strong&gt;15 bytes&lt;/strong&gt; from 12. In this particular short example, the overhead of frequent delta changes actually &lt;em&gt;expands&lt;/em&gt; the data. This is expected — the algorithm's strength is with longer, more predictable sequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where It Dominates
&lt;/h3&gt;

&lt;p&gt;Consider a typical log line repeated across thousands of entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="err"&gt;"&lt;/span&gt;192.168.1.96  - - [10/Apr/2026:05:38:27 +0000] "GET /index.html..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The spaces, repeated digits, sequential timestamps, and identical structural characters create long delta runs. A 1KB log block can easily compress to 400-500 bytes — a &lt;strong&gt;50-60% reduction&lt;/strong&gt; with zero dictionary overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The x64 Assembly Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Register Architecture
&lt;/h3&gt;

&lt;p&gt;The compressor is designed for maximum register economy — critical for shellcode deployments where every byte of machine code counts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;RSI&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Source&lt;/span&gt; &lt;span class="nv"&gt;pointer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;raw&lt;/span&gt; &lt;span class="nv"&gt;input&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;RDI&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Destination&lt;/span&gt; &lt;span class="nv"&gt;pointer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;compressed&lt;/span&gt; &lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;RCX&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Remaining&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="nv"&gt;counter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;R8B&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;RLE&lt;/span&gt; &lt;span class="nv"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;–&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;si&lt;/span&gt;&lt;span class="nv"&gt;ngle&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="nv"&gt;register&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;R9B&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Active&lt;/span&gt; &lt;span class="nv"&gt;Delta&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt; &lt;span class="nb"&gt;di&lt;/span&gt;&lt;span class="nv"&gt;fference&lt;/span&gt; &lt;span class="nv"&gt;being&lt;/span&gt; &lt;span class="nv"&gt;tracked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;AL&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Working&lt;/span&gt; &lt;span class="nv"&gt;register&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;current&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;delta&lt;/span&gt; &lt;span class="nv"&gt;calculation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;BL&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Previous&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;anchor&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;delta&lt;/span&gt; &lt;span class="nv"&gt;math&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;DL&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nv"&gt;Temporary&lt;/span&gt; &lt;span class="nv"&gt;save&lt;/span&gt; &lt;span class="nv"&gt;register&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;preserves&lt;/span&gt; &lt;span class="nv"&gt;current&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No heap. No stack frames. No memory allocation. Everything lives in registers during the hot loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Compressor Core
&lt;/h3&gt;

&lt;p&gt;The heart of the algorithm — the main loop — is remarkably compact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;_compress_loop:&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;_end&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;       &lt;span class="c1"&gt;; Read current byte&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;              &lt;span class="c1"&gt;; Save it (we'll need the original)&lt;/span&gt;
    &lt;span class="nf"&gt;sub&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;              &lt;span class="c1"&gt;; Calculate delta: current - previous&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;r9b&lt;/span&gt;             &lt;span class="c1"&gt;; Does it match our Active Delta?&lt;/span&gt;
    &lt;span class="nf"&gt;jne&lt;/span&gt; &lt;span class="nv"&gt;_flush&lt;/span&gt;              &lt;span class="c1"&gt;; No → flush the current run&lt;/span&gt;

&lt;span class="nl"&gt;_inc_r8b:&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;                 &lt;span class="c1"&gt;; Yes → extend the run&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;                 &lt;span class="c1"&gt;; Advance source pointer&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;                 &lt;span class="c1"&gt;; Decrement remaining counter&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;_end&lt;/span&gt;                 &lt;span class="c1"&gt;; If zero → final flush and exit&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;              &lt;span class="c1"&gt;; Update "previous" byte&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;            &lt;span class="c1"&gt;; Overflow guard (max 1-byte count)&lt;/span&gt;
    &lt;span class="nf"&gt;jne&lt;/span&gt; &lt;span class="nv"&gt;_compress_loop&lt;/span&gt;      &lt;span class="c1"&gt;; Continue scanning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hot path — when deltas match — executes in just &lt;strong&gt;8 instructions per byte&lt;/strong&gt;. No branches taken, no memory writes, pure register operations. Branch prediction loves this because &lt;code&gt;jne _flush&lt;/code&gt; is "not taken" for the majority of iterations in sequential data.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Flush Mechanism
&lt;/h3&gt;

&lt;p&gt;When a delta mismatch occurs, the current run is written to the output and a new sequence begins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;_flush:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;     &lt;span class="c1"&gt;; Write RLE count to output&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;r9b&lt;/span&gt;     &lt;span class="c1"&gt;; Write Active Delta to output&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;

    &lt;span class="c1"&gt;; Start tracking the new sequence&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;r9b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;             &lt;span class="c1"&gt;; New Active Delta = the mismatched delta&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;              &lt;span class="c1"&gt;; Reset count to 1 (this byte starts a new run)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;              &lt;span class="c1"&gt;; Update previous byte&lt;/span&gt;

    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;_end&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;_compress_loop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 255 Overflow Guard
&lt;/h3&gt;

&lt;p&gt;A single byte can only represent counts up to 255. When a run exceeds this limit, a "mini-flush" writes the current state and resets the counter — without changing the Active Delta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;_flush_255:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;     &lt;span class="c1"&gt;; Write 255 to output&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;r9b&lt;/span&gt;     &lt;span class="c1"&gt;; Write current delta&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;r8b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;              &lt;span class="c1"&gt;; Reset counter, KEEP the same delta&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;_compress_loop&lt;/span&gt;      &lt;span class="c1"&gt;; Continue — same series, new count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures seamless handling of runs longer than 255 bytes — they simply produce multiple &lt;code&gt;(255, delta)&lt;/code&gt; pairs followed by the remainder.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decompressor
&lt;/h2&gt;

&lt;p&gt;Decompression is the inverse operation and is intentionally simpler — this is critical because in a C2 context, the decompressor runs on the client side where code complexity is less constrained, but in the agent-side PIC context, simplicity means fewer instructions and smaller shellcode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;_decompress_loop:&lt;/span&gt;
    &lt;span class="nf"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;_print_and_exit&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;      &lt;span class="c1"&gt;; Read Count&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;

    &lt;span class="nf"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;_print_and_exit&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;      &lt;span class="c1"&gt;; Read Delta&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;

    &lt;span class="nf"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;             &lt;span class="c1"&gt;; Count == 0? Skip (edge case)&lt;/span&gt;
    &lt;span class="nf"&gt;jz&lt;/span&gt; &lt;span class="nv"&gt;_decompress_loop&lt;/span&gt;

&lt;span class="nl"&gt;_write_loop:&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;              &lt;span class="c1"&gt;; Apply delta: previous + delta = current&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;      &lt;span class="c1"&gt;; Write reconstructed byte&lt;/span&gt;
    &lt;span class="nf"&gt;inc&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;dl&lt;/span&gt;
    &lt;span class="nf"&gt;jnz&lt;/span&gt; &lt;span class="nv"&gt;_write_loop&lt;/span&gt;         &lt;span class="c1"&gt;; Repeat for the full count&lt;/span&gt;

    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;_decompress_loop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key detail is in &lt;code&gt;_write_loop&lt;/code&gt;: the delta is applied &lt;strong&gt;cumulatively&lt;/strong&gt;. Each iteration adds the delta to the running byte value. This is what reconstructs sequences like &lt;code&gt;A → B → C → D&lt;/code&gt; from a single &lt;code&gt;(3, +1)&lt;/code&gt; pair — not by writing &lt;code&gt;B&lt;/code&gt; three times, but by computing &lt;code&gt;A+1=B&lt;/code&gt;, &lt;code&gt;B+1=C&lt;/code&gt;, &lt;code&gt;C+1=D&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance: Compression Scenarios
&lt;/h2&gt;

&lt;p&gt;Here's what to expect with real-world data on a 1GB file scale:&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Server Log Files (access.log)
&lt;/h3&gt;

&lt;p&gt;Repeated IP addresses, timestamps with sequential seconds, identical HTTP status codes, recurring URL paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Compression: ~40-55% reduction → ~450-600 MB output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Delta encoding crushes timestamp sequences (incrementing seconds produce delta=1 runs). Repeated structural elements (spaces, brackets, quotes) create long zero-delta runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Random / Encrypted Binary Data
&lt;/h3&gt;

&lt;p&gt;Uniform byte distribution, no patterns, no sequential relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Compression: 0% — data EXPANDS by ~50-80% → ~1.5-1.8 GB output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every delta change triggers a flush, producing 2 bytes (count + delta) for almost every input byte, plus the anchor overhead. This is mathematically unavoidable — Shannon's entropy theorem guarantees no lossless algorithm can compress truly random data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Database Exports (CSV / SQL Dumps)
&lt;/h3&gt;

&lt;p&gt;Sequential IDs (1, 2, 3...), repeated column names, consistent delimiters, similar numeric values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Compression: ~30-45% reduction → ~550-700 MB output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Auto-incrementing IDs produce perfect delta=1 runs. Repeated commas, quotes, and fixed-width fields compress well. Variable-length string fields (names, addresses) reduce overall effectiveness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versus Traditional Algorithms
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Log File&lt;/th&gt;
&lt;th&gt;Random&lt;/th&gt;
&lt;th&gt;DB Dump&lt;/th&gt;
&lt;th&gt;Dictionary?&lt;/th&gt;
&lt;th&gt;Decode Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;VESQER (DPCM+RLE)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~50%&lt;/td&gt;
&lt;td&gt;Expands&lt;/td&gt;
&lt;td&gt;~40%&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Trivial (~15 instructions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gzip (LZ77+Huffman)&lt;/td&gt;
&lt;td&gt;~80%&lt;/td&gt;
&lt;td&gt;~0%&lt;/td&gt;
&lt;td&gt;~70%&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Heavy (sliding window + tree)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LZ4&lt;/td&gt;
&lt;td&gt;~65%&lt;/td&gt;
&lt;td&gt;~0%&lt;/td&gt;
&lt;td&gt;~55%&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Medium (hash table lookups)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zstd&lt;/td&gt;
&lt;td&gt;~85%&lt;/td&gt;
&lt;td&gt;~0%&lt;/td&gt;
&lt;td&gt;~75%&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Heavy (FSE + match finder)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;VESQER doesn't compete with dictionary-based algorithms on raw compression ratio. It was never designed to. Its advantages are elsewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero memory overhead:&lt;/strong&gt; No hash tables, no sliding windows, no frequency tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiny code footprint:&lt;/strong&gt; The entire compressor fits in ~100 instructions. Critical for PIC shellcode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trivial decompression:&lt;/strong&gt; ~15 instructions to decompress. The client-side decoder is negligible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No signatures:&lt;/strong&gt; No magic bytes, no headers, no identifiable structure. The output is indistinguishable from random noise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  C2 Integration: The Real Purpose
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/JM00NJ/ICMP-Ghost-A-Fileless-x64-Assembly-C2-Agent" rel="noopener noreferrer"&gt;Ghost-C2&lt;/a&gt; project, VESQER sits in the data pipeline between command execution and network transmission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Command Output (RAM) → VESQER Compress → Rolling XOR → ICMP Chunking → Wire
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compression step achieves two things simultaneously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Reduced Wire Footprint:&lt;/strong&gt; A &lt;code&gt;ls -la&lt;/code&gt; output that would normally require 9 ICMP packets now fits in 5. Fewer packets = fewer detection opportunities = lower IDS correlation probability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Double-Layer Obfuscation:&lt;/strong&gt; Compressed data already has high entropy — it looks like random noise. When Rolling XOR is applied on top, the result is indistinguishable from encrypted random data. A DPI engine analyzing the payload sees no ASCII patterns, no structural markers, nothing to fingerprint.&lt;/p&gt;

&lt;p&gt;The PIC-integrated version of the compressor is called via a simple function convention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="c1"&gt;; RSI = Source buffer (raw command output)&lt;/span&gt;
&lt;span class="c1"&gt;; RDI = Destination buffer (compressed output)&lt;/span&gt;
&lt;span class="c1"&gt;; RCX = Input length&lt;/span&gt;
&lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;_vesqer_compress&lt;/span&gt;
&lt;span class="c1"&gt;; RAX = Compressed length (new size for chunking)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All registers are preserved via push/pop, making it a clean drop-in function within the agent's execution pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Accidental Reinvention" Reflection
&lt;/h2&gt;

&lt;p&gt;The irony isn't lost on me. DPCM was formalized in the 1950s for voice digitization. RLE dates back to the 1960s. Combining predictive coding with run-length encoding has been done in countless forms — JPEG uses a variant, video codecs use it, even fax machines used it.&lt;/p&gt;

&lt;p&gt;But none of those implementations were written in pure x86-64 Assembly, with zero dependencies, optimized for shellcode deployment inside a fileless C2 agent running inside a hijacked &lt;code&gt;cron&lt;/code&gt; process.&lt;/p&gt;

&lt;p&gt;Sometimes reinventing the wheel is the whole point — when you need that wheel to be invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Source Code &amp;amp; Implementation
&lt;/h2&gt;

&lt;p&gt;The complete standalone compressor and decompressor are available as open-source tools:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VESQER Compressor Repository:&lt;/strong&gt; &lt;a href="https://github.com/JM00NJ/dpcm-rle-hybrid-x64-compressor" rel="noopener noreferrer"&gt;dpcm-rle-hybrid-x64-compressor on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ghost-C2 (Integrated Version):&lt;/strong&gt; &lt;a href="https://github.com/JM00NJ/ICMP-Ghost-A-Fileless-x64-Assembly-C2-Agent" rel="noopener noreferrer"&gt;Ghost-C2 on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Legal Disclaimer
&lt;/h2&gt;

&lt;p&gt;This project is created for &lt;strong&gt;educational purposes and security research only&lt;/strong&gt;. The compression algorithm itself is a general-purpose tool, but its integration with C2 frameworks is discussed purely in the context of authorized penetration testing and red team operations. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MITRE ATT&amp;amp;CK:&lt;/strong&gt; &lt;a href="https://attack.mitre.org/techniques/T1048/" rel="noopener noreferrer"&gt;T1048&lt;/a&gt; · &lt;a href="https://attack.mitre.org/techniques/T1027/" rel="noopener noreferrer"&gt;T1027&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>assembly</category>
      <category>computerscience</category>
      <category>programming</category>
    </item>
    <item>
      <title>memfd_create: Anonymous RAM Files and Volatile Storage in x64 Assembly</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Wed, 01 Jul 2026 11:47:48 +0000</pubDate>
      <link>https://dev.to/jm00nj/memfdcreate-anonymous-ram-files-and-volatile-storage-in-x64-assembly-34m1</link>
      <guid>https://dev.to/jm00nj/memfdcreate-anonymous-ram-files-and-volatile-storage-in-x64-assembly-34m1</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;"In high-performance computing and modern system auditing, reducing disk I/O overhead is critical for operational efficiency. This article examines the memfd_create system call—a mechanism designed to create anonymous, volatile files that reside exclusively in RAM, providing a secure and fast alternative to traditional disk-based temporary storage."&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Problem with Physical Storage
&lt;/h3&gt;

&lt;p&gt;Traditional temporary files (usually stored in /tmp) require disk interaction, which introduces latency and leaves a physical footprint on the storage medium. In scenarios requiring high volatility and data privacy, physical disk traces are undesirable. memfd_create solves this by providing a file descriptor that points to an anonymous file in the RAM-backed tmpfs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Technical Anatomy of memfd_create
&lt;/h3&gt;

&lt;p&gt;Introduced in Linux Kernel 3.17, memfd_create (Syscall 319 on x64) behaves like a regular file but lacks a permanent location on the global file system. It is invisible to standard directory listing commands like ls.&lt;/p&gt;

&lt;p&gt;Key Characteristics:&lt;/p&gt;

&lt;p&gt;Volatility: The data is lost immediately when the last file descriptor is closed or the process terminates.&lt;/p&gt;

&lt;p&gt;Sealing: Using fcntl, the file can be "sealed" (MFD_ALLOW_SEALING), making it immutable and preventing further modifications—a vital feature for integrity-sensitive operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl3uiyje2z8ylj0zs9byu.png" alt=" " width="799" height="436"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3. Implementation in x64 Assembly
&lt;/h3&gt;

&lt;p&gt;Implementing memfd_create at the assembly level allows for a microscopic footprint and direct control over memory allocation. Below is the technical implementation of creating an anonymous file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;
&lt;span class="nf"&gt;section&lt;/span&gt; &lt;span class="nv"&gt;.rodata&lt;/span&gt;

    &lt;span class="nf"&gt;mem_name&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="s"&gt;"system_audit_log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;; Null-terminated internal label&lt;/span&gt;

&lt;span class="c1"&gt;; memfd_create(const char *name, unsigned int flags)&lt;/span&gt;

&lt;span class="c1"&gt;; RAX: 319, RDI: name_ptr, RSI: flags&lt;/span&gt;



    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;319&lt;/span&gt;            &lt;span class="c1"&gt;; sys_memfd_create&lt;/span&gt;

   &lt;span class="nf"&gt;lea&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;rel&lt;/span&gt; &lt;span class="nv"&gt;mem_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;; Pointer to a string label (visible only in /proc/pid/fd/)&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;              &lt;span class="c1"&gt;; MFD_CLOEXEC (Close-on-exec flag)&lt;/span&gt;

    &lt;span class="nf"&gt;syscall&lt;/span&gt;                 &lt;span class="c1"&gt;; Execute&lt;/span&gt;

    &lt;span class="c1"&gt;; Result: RAX contains the file descriptor &lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Advanced Use Cases: Observability and Logic Isolation
&lt;/h3&gt;

&lt;p&gt;From a system research perspective, memfd_create is an essential tool for System Integrity Analysis:&lt;/p&gt;

&lt;p&gt;Dynamic Payload Analysis: Safely executing and analyzing code blocks in a memory-resident environment without altering the host's disk state.&lt;/p&gt;

&lt;p&gt;Inter-Process Communication (IPC): Sharing large data structures between processes via file descriptors without the overhead of disk synchronization.&lt;/p&gt;

&lt;p&gt;Forensic Resilience: Evaluating how modern forensic tools detect memory-only artifacts, a crucial part of Runtime Security Research.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Defense and Monitoring Perspective
&lt;/h3&gt;

&lt;p&gt;While memfd_create offers significant performance benefits, its "invisible" nature requires specific auditing strategies. Blue Team researchers should monitor:&lt;/p&gt;

&lt;p&gt;File Descriptor Auditing: Inspecting /proc/[pid]/fd/ for links starting with memfd:.&lt;/p&gt;

&lt;p&gt;Syscall Hooking: Utilizing eBPF or Auditd to track sys_memfd_create calls, especially those originating from unauthorized or non-service processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;memfd_create represents a significant evolution in Linux memory management. By enabling memory-resident storage and execution, it provides researchers with a powerful tool for building high-performance, low-impact system utilities. Understanding these low-level volatile mechanisms is key to mastering both System Architecture and Modern Security Auditing.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Legal Disclaimer
&lt;/h2&gt;

&lt;p&gt;This project is created for educational purposes and security research only. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool. Operating this tool on networks you do not own is strictly prohibited.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MITRE ATT&amp;amp;CK:&lt;/strong&gt; &lt;a href="https://attack.mitre.org/techniques/T1055/009/" rel="noopener noreferrer"&gt;T1055.009&lt;/a&gt; · &lt;a href="https://attack.mitre.org/techniques/T1027/" rel="noopener noreferrer"&gt;T1027&lt;/a&gt; · &lt;a href="https://attack.mitre.org/techniques/T1564/" rel="noopener noreferrer"&gt;T1564&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>cybersecurity</category>
      <category>assembly</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Linux x64 Assembly: Syscalls, Registers, and the .bss Segment</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Wed, 01 Jul 2026 11:43:48 +0000</pubDate>
      <link>https://dev.to/jm00nj/linux-x64-assembly-syscalls-registers-and-the-bss-segment-49n5</link>
      <guid>https://dev.to/jm00nj/linux-x64-assembly-syscalls-registers-and-the-bss-segment-49n5</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;If you are messing around with Assembly, the most direct way to talk to the processor is through the Syscall (System Call) mechanism. Opening a file, reading from a keyboard, or printing text to a terminal... it all starts with a formal request to the Kernel. Today, we are going behind the scenes to examine the open, read, and exit calls, how registers manage data flow, and why the .bss segment is your best friend for memory management.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Register Dance: Who Goes Where?
&lt;/h3&gt;

&lt;p&gt;In the Linux x64 architecture, before making a syscall, you must tell the Kernel exactly what you want. There is a strict protocol for this:&lt;/p&gt;

&lt;p&gt;RAX: Holds the Syscall ID. (e.g., 0 for read, 2 for open, 60 for exit).&lt;/p&gt;

&lt;p&gt;RDI: The 1st Parameter (Usually the File Descriptor).&lt;/p&gt;

&lt;p&gt;RSI: The 2nd Parameter (Usually the memory address for a buffer).&lt;/p&gt;

&lt;p&gt;RDX: The 3rd Parameter (Usually the size of the data in bytes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding File Descriptors (FD)
&lt;/h2&gt;

&lt;p&gt;To the Kernel, there is no difference between a keyboard, a terminal, or a .txt file; they are all just numbers:&lt;/p&gt;

&lt;p&gt;0 (stdin): Standard Input (Keyboard).&lt;/p&gt;

&lt;p&gt;1 (stdout): Standard Output (Terminal screen).&lt;/p&gt;

&lt;p&gt;2 (stderr): Standard Error.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Essential Syscalls: Open, Read, and Exit
&lt;/h3&gt;

&lt;p&gt;To read data from a file and then shut down the program, we follow this logical sequence:&lt;/p&gt;

&lt;h4&gt;
  
  
  A. sys_open (RAX = 2)
&lt;/h4&gt;

&lt;p&gt;Before you can touch a file, you must "open" it.&lt;/p&gt;

&lt;p&gt;RDI: The address of the filename string (e.g., filename db "data.txt", 0).&lt;/p&gt;

&lt;p&gt;RSI: Flags. 0 = Read-only, 1 = Write-only.&lt;/p&gt;

&lt;p&gt;RDX: Mode (File permissions, usually used when creating a file).&lt;/p&gt;

&lt;h4&gt;
  
  
  B. sys_read (RAX = 0)
&lt;/h4&gt;

&lt;p&gt;Once the file is opened, the Kernel returns a File Descriptor (FD) inside the RAX register. Now we can read:&lt;/p&gt;

&lt;p&gt;RDI: The FD number returned by the previous open call.&lt;/p&gt;

&lt;p&gt;RSI: The address of where the data should be written (Buffer address).&lt;/p&gt;

&lt;p&gt;RDX: How many bytes to read.&lt;/p&gt;

&lt;h4&gt;
  
  
  C. sys_write (RAX = 1)
&lt;/h4&gt;

&lt;p&gt;Now that the data is safely parked in our .bss buffer, we want to actually see it. We ask the Kernel to take that data and push it to the terminal screen.&lt;/p&gt;

&lt;p&gt;RDI: 1 (The File Descriptor for Standard Output / Terminal).&lt;/p&gt;

&lt;p&gt;RSI: The address of the data we want to print (our .bss buffer).&lt;/p&gt;

&lt;p&gt;RDX: How many bytes to print.&lt;/p&gt;

&lt;h4&gt;
  
  
  D. sys_close (RAX = 3)
&lt;/h4&gt;

&lt;p&gt;A true systems programmer always cleans up after themselves. Leaving File Descriptors open causes memory/resource leaks. Since we overwrote our RDI register during the sys_write step, we need to make sure we saved our original file FD somewhere safe (like R8) so we can close it properly.&lt;br&gt;
RDI: The File Descriptor of the file we want to close (saved earlier).&lt;/p&gt;
&lt;h4&gt;
  
  
  E. sys_exit (RAX = 60)
&lt;/h4&gt;

&lt;p&gt;When the task is done, you must tell the OS "I'm leaving."&lt;/p&gt;

&lt;p&gt;RDI: The status code (0 usually means "everything is fine").&lt;/p&gt;
&lt;h3&gt;
  
  
  3. The .bss Segment: The Data "Parking Lot"
&lt;/h3&gt;

&lt;p&gt;If your program receives external data (like a keyboard entry or file content), you need a place to store it. This is where the .bss (Block Started by Symbol) segment comes in.&lt;/p&gt;

&lt;p&gt;Why use .bss instead of .data?&lt;/p&gt;

&lt;p&gt;The .data section is for pre-defined (initialized) data (e.g., msg db "Hello"). This increases the actual size of your binary file.&lt;/p&gt;

&lt;p&gt;The .bss section simply "reserves" space. It doesn't increase the file size on disk; it only ensures that when the program runs, the RAM reserves that much space for you.&lt;/p&gt;

&lt;p&gt;How Data is Written to .bss&lt;/p&gt;

&lt;p&gt;When you execute a sys_read syscall, you provide the address of a label defined in .bss to the RSI register. The Kernel then takes the bytes it reads from the file/keyboard and lays them out in memory starting from that specific address.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Implementation: A Minimalist File Reader
&lt;/h3&gt;

&lt;p&gt;The following code opens a file, reads its content into a .bss buffer, and exits gracefully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nf"&gt;section&lt;/span&gt; &lt;span class="nv"&gt;.data&lt;/span&gt;
    &lt;span class="nf"&gt;filename&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="s"&gt;"test.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;   &lt;span class="c1"&gt;; Null-terminated string for the filename&lt;/span&gt;

&lt;span class="nf"&gt;section&lt;/span&gt; &lt;span class="nv"&gt;.bss&lt;/span&gt;
    &lt;span class="nf"&gt;buffer&lt;/span&gt; &lt;span class="nv"&gt;resb&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;              &lt;span class="c1"&gt;; Reserve 64 bytes for our incoming data&lt;/span&gt;

&lt;span class="nf"&gt;section&lt;/span&gt; &lt;span class="nv"&gt;.text&lt;/span&gt;
    &lt;span class="nf"&gt;global&lt;/span&gt; &lt;span class="nv"&gt;_start&lt;/span&gt;

&lt;span class="nl"&gt;_start:&lt;/span&gt;
    &lt;span class="c1"&gt;; 1. OPEN the file (sys_open)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                  &lt;span class="c1"&gt;; syscall: sys_open&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;filename&lt;/span&gt;           &lt;span class="c1"&gt;; 1st param: address of the filename&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                  &lt;span class="c1"&gt;; 2nd param: O_RDONLY flag (read only)&lt;/span&gt;
    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;                &lt;span class="c1"&gt;; 3rd param: mode (0, clear garbage data)&lt;/span&gt;
    &lt;span class="nf"&gt;syscall&lt;/span&gt;                     &lt;span class="c1"&gt;; Execute. RAX now contains the FD&lt;/span&gt;

    &lt;span class="nf"&gt;push&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;                    &lt;span class="c1"&gt;; PRO TIP: Save the FD to the stack instead of a register!&lt;/span&gt;

    &lt;span class="c1"&gt;; 2. READ the data (sys_read)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;                &lt;span class="c1"&gt;; 1st param: Move FD from RAX to RDI&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                  &lt;span class="c1"&gt;; syscall: sys_read&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;             &lt;span class="c1"&gt;; 2nd param: destination buffer in .bss&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;                 &lt;span class="c1"&gt;; 3rd param: number of bytes to read&lt;/span&gt;
    &lt;span class="nf"&gt;syscall&lt;/span&gt;                     &lt;span class="c1"&gt;; Execute. The data is now in the buffer&lt;/span&gt;

    &lt;span class="c1"&gt;; 3. WRITE the data to terminal (sys_write)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                  &lt;span class="c1"&gt;; syscall: sys_write&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                  &lt;span class="c1"&gt;; 1st param: File Descriptor 1 (stdout)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;             &lt;span class="c1"&gt;; 2nd param: source buffer in .bss&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;                 &lt;span class="c1"&gt;; 3rd param: number of bytes to write&lt;/span&gt;
    &lt;span class="nf"&gt;syscall&lt;/span&gt;                     &lt;span class="c1"&gt;; Execute. Data prints to the screen&lt;/span&gt;

    &lt;span class="c1"&gt;; 4. CLOSE the file (sys_close)&lt;/span&gt;
    &lt;span class="nf"&gt;pop&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;                     &lt;span class="c1"&gt;; 1st param: Pop the saved FD directly into RDI!&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;                  &lt;span class="c1"&gt;; syscall: sys_close&lt;/span&gt;
    &lt;span class="nf"&gt;syscall&lt;/span&gt;                     &lt;span class="c1"&gt;; Execute. File is successfully closed!&lt;/span&gt;

    &lt;span class="c1"&gt;; 5. EXIT the program (sys_exit)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;                 &lt;span class="c1"&gt;; syscall: sys_exit&lt;/span&gt;
    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;                &lt;span class="c1"&gt;; 1st param: exit code 0 (success)&lt;/span&gt;
    &lt;span class="nf"&gt;syscall&lt;/span&gt;                     &lt;span class="c1"&gt;; Execute. Program terminates cleanly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In Assembly, everything is about addressing and register management. Whether data comes from the terminal (FD 0) or a file is just a parameter change for the Kernel. By mastering the .bss section and syscall registers, you gain total control over how your program interacts with the system memory and hardware.&lt;/p&gt;

&lt;p&gt;In the low-level world, every byte is under your command. Keep coding!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>assembly</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>RFC 1071 Checksum Explained: x64 Assembly Implementation</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Sun, 28 Jun 2026 16:59:48 +0000</pubDate>
      <link>https://dev.to/jm00nj/rfc-1071-checksum-explained-x64-assembly-implementation-1ebj</link>
      <guid>https://dev.to/jm00nj/rfc-1071-checksum-explained-x64-assembly-implementation-1ebj</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;As the development of my ICMP-based Network Communication Project continues at full throttle, today I want to talk about the most "diplomatic" part of the operation: the Checksum. If you don't stamp this seal correctly on the packet you're sending, the Target host's operating system treats your packet as a "Malformed data" and dumps it in the trash before it even gets through the door.&lt;/p&gt;

&lt;p&gt;So, how exactly is this "seal" calculated in a low-level language? Let's examine it step-by-step through the very algorithm I wrote and currently use in my project.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ The Heart of the Algorithm: perform_checksum
&lt;/h2&gt;

&lt;p&gt;The ICMP protocol uses a 16-bit One's Complement sum to ensure data integrity. This means you have to add up the entire packet in 16-bit (2-byte) chunks.&lt;/p&gt;

&lt;p&gt;Here is what this mathematical operation looks like in the x64 Assembly realm:&lt;/p&gt;

&lt;p&gt;Note: In this context, rdi represents the starting address of our data buffer, r14 is the starting offset, and r15 is the ending offset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;
&lt;span class="nl"&gt;perform_checksum:&lt;/span&gt;

    &lt;span class="c1"&gt;; RFC 1071 standard 16-bit one's complement sum algorithm&lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;                &lt;span class="c1"&gt;; Clear eax (Accumulator for the sum)&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;r14&lt;/span&gt;               &lt;span class="c1"&gt;; r10 = Current offset&lt;/span&gt;

&lt;span class="nl"&gt;.loop:&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nv"&gt;r11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;r15&lt;/span&gt;               &lt;span class="c1"&gt;; r11 = End offset&lt;/span&gt;

    &lt;span class="nf"&gt;sub&lt;/span&gt; &lt;span class="nv"&gt;r11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;                &lt;span class="c1"&gt;; Remaining bytes to process&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nv"&gt;r11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                  &lt;span class="c1"&gt;; Check if only 1 byte is left (odd length)&lt;/span&gt;

    &lt;span class="nf"&gt;jle&lt;/span&gt; &lt;span class="nv"&gt;.last&lt;/span&gt;                       &lt;span class="c1"&gt;; If &amp;lt;= 1 byte left, jump to final block&lt;/span&gt;



    &lt;span class="nf"&gt;movzx&lt;/span&gt; &lt;span class="nb"&gt;r12d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;word&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;; Read 2 bytes (1 word) zero-extended&lt;/span&gt;

    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;r12d&lt;/span&gt;              &lt;span class="c1"&gt;; Add to accumulator&lt;/span&gt;

    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;                   &lt;span class="c1"&gt;; Move offset forward by 2 bytes&lt;/span&gt;

    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.loop&lt;/span&gt;                   &lt;span class="c1"&gt;; Repeat &lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧩 Part 1: Gathering the Pieces
&lt;/h2&gt;

&lt;p&gt;We are essentially telling the CPU: "Fetch me a 16-bit (word) chunk from memory, add it to the eax register, and move to the next 2 bytes." This loop runs smoothly until we hit the end of the packet.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Part 2: The "Odd Byte" Paradox
&lt;/h2&gt;

&lt;p&gt;If the total length of the packet is an odd number (e.g., 11 bytes), the very last byte won't have a pair to form a 16-bit word. In this scenario, our algorithm elegantly dives into the .final block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;.last:&lt;/span&gt;
    &lt;span class="nf"&gt;je&lt;/span&gt; &lt;span class="nv"&gt;.final&lt;/span&gt;                   &lt;span class="c1"&gt;; If exactly 1 byte left, handle it&lt;/span&gt;
    &lt;span class="nf"&gt;jmp&lt;/span&gt; &lt;span class="nv"&gt;.wrap&lt;/span&gt;                   &lt;span class="c1"&gt;; If 0 bytes left, finalize calculation&lt;/span&gt;
&lt;span class="nl"&gt;.final:&lt;/span&gt;
    &lt;span class="nf"&gt;movzx&lt;/span&gt; &lt;span class="nb"&gt;r12d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;; Read the last remaining single byte&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;r12d&lt;/span&gt;               &lt;span class="c1"&gt;; Add it to the accumulator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔄 Part 3: The Wrap and Carry
&lt;/h2&gt;

&lt;p&gt;Mathematically, this continuous addition might exceed a 16-bit boundary. This is where the most critical aspect of RFC 1071 comes into play: Adding the overflowing bits (the carry) back into the main sum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;.wrap:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;r11d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;               &lt;span class="c1"&gt;; Copy sum to r11d&lt;/span&gt;
    &lt;span class="nf"&gt;shr&lt;/span&gt; &lt;span class="nb"&gt;r11d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;                &lt;span class="c1"&gt;; Shift right to isolate the carry bits&lt;/span&gt;
    &lt;span class="nf"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;eax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;             &lt;span class="c1"&gt;; Mask eax to keep only the lower 16 bits&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;r11w&lt;/span&gt;                &lt;span class="c1"&gt;; Add the carry bits back to the sum&lt;/span&gt;
    &lt;span class="nf"&gt;adc&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                   &lt;span class="c1"&gt;; Add any final carry (add with carry)&lt;/span&gt;
    &lt;span class="nf"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;                      &lt;span class="c1"&gt;; One's complement (invert bits) for final checksum&lt;/span&gt;
    &lt;span class="nf"&gt;ret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎯 Why not ax?
&lt;/h2&gt;

&lt;p&gt;The not instruction at the very end is the final requirement of the One's Complement logic. By inverting the bits (0 -&amp;gt; 1, 1 -&amp;gt; 0), we ensure that when the receiving end takes our packet and performs the exact same addition, the result will be 0xFFFF. If it is, the data is clean, and our seal is valid!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Writing this algorithm in Assembly is a fantastic exercise to truly understand how data is laid out in memory and how the CPU crunches bytes. Thanks to this algorithm, our custom ICMP packets can bypass kernel-level drops and roam the network like "official documents".&lt;/p&gt;

&lt;p&gt;When I integrate dynamic targeting and fileless execution (memfd_create) into my Distributed management architecture, this checksum engine will remain the most reliable gear in the machine.&lt;/p&gt;

&lt;p&gt;Stay Coded!&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/cwe-290/" rel="noopener noreferrer"&gt;CWE-290 at Layer 3: IP Source Spoofing and uRPF Failure in Enterprise Wireless Infrastructure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/etherleak-reloaded/" rel="noopener noreferrer"&gt;EtherLeak: IP Total Length Over-read via Ethernet Frame Padding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/ghost-leak/" rel="noopener noreferrer"&gt;Ghost Leak — Pre-Auth Buffer Over-read via TTL=0 + IP Total Length in ArubaOS 8.13.2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>architecture</category>
      <category>programming</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Building a Low-Level ICMP Sniffer in x64 Assembly (Raw Sockets)</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Sun, 28 Jun 2026 16:53:51 +0000</pubDate>
      <link>https://dev.to/jm00nj/building-a-low-level-icmp-sniffer-in-x64-assembly-raw-sockets-5128</link>
      <guid>https://dev.to/jm00nj/building-a-low-level-icmp-sniffer-in-x64-assembly-raw-sockets-5128</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;In the realm of network security and packet analysis, tools like Python (Scapy) or C are the usual go-tos. However, when we want to strip away all abstraction layers from the OS network stack and talk directly to the processor, resources become incredibly scarce. Finding modern, zero-dependency networking tools written in x64 Assembly on the internet is almost impossible today.&lt;/p&gt;

&lt;p&gt;In this post, we will explore the architecture and design decisions behind my x64 Assembly-based ICMP Sniffer project, completely rejecting standard C libraries (libc) and relying purely on direct Linux system calls (syscalls).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept: Why Assembly?
&lt;/h2&gt;

&lt;p&gt;Our goal isn't just to catch ICMP (ping) packets on the network. We want to manually manage memory, register allocations, and data type conversions (integer-to-string) at the CPU cycle level. This approach provides a flawless foundation for understanding how hardware behaves during System security auditing and low-level software analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does It Work? (Technical Deep Dive)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The architecture of the tool is divided into three main phases:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Raw Socket Foundation To capture raw, unprocessed packets passing through the network interface card (NIC), the application uses sys_socket (syscall 41) with AF_INET and SOCK_RAW parameters. Our target here is strictly the IPPROTO_ICMP protocol. This tells the operating system to filter out all TCP/UDP traffic and hand us only the ICMP packets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Packet Observation and Header Stripping Incoming packets are read into a memory buffer using sys_recvfrom. Since we are using Raw Sockets, the data arrives in its absolute raw form. To reach the actual payload, we must manually bypass the protocol headers:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;IPv4 Header: 20 Bytes&lt;/p&gt;

&lt;p&gt;ICMP Header: 8 Bytes&lt;/p&gt;

&lt;p&gt;Therefore, by utilizing the lea rsi, [sniffed_data + 28] instruction in our Assembly code, we strip away this 28-byte "noise" and dive straight into the heart of the data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Custom Integer-to-ASCII Engine This is the most complex and educational part of the project. The captured IP address (e.g., 192.168.1.29) resides in memory as raw binary (hexadecimal). To print this to the terminal, we must convert it into a human-readable ASCII string.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since we aren't using any external printf or itoa functions, I designed the engine as follows:&lt;/p&gt;

&lt;p&gt;Each octet (8-bit IP segment) fetched from the network address is divided by 10 using the div instruction.&lt;/p&gt;

&lt;p&gt;We mathematically add 48 (0x30) to the remainders to convert them into ASCII characters.&lt;/p&gt;

&lt;p&gt;These converted characters are written into a 16-byte memory buffer in reverse order (from end to start).&lt;/p&gt;

&lt;p&gt;Using logical brakes via conditional jumps (je, jg), dot (.) characters are strategically inserted only between the octets to prevent malformed strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion and Source Code
&lt;/h3&gt;

&lt;p&gt;This tool proves how we can filter not just the "existence" of ICMP packets, but the actual payloads hidden inside them (like Non-standard data structures or remote management signals) at the kernel level. Writing our own string conversion engine using nothing but Linux Syscalls, without relying on any external libraries, has been a fantastic exercise in pushing the limits of low-level system programming.&lt;/p&gt;

&lt;p&gt;For security researchers, Blue Team members, and exploit development enthusiasts who want to test the tool or review the code, the full source is available on my GitHub profile:&lt;/p&gt;

&lt;p&gt;🔗 GitHub Repo: &lt;a href="https://github.com/JM00NJ/asm-icmp-sniffer" rel="noopener noreferrer"&gt;JM00NJ/asm-icmp-sniffer&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Legal Disclaimer
&lt;/h2&gt;

&lt;p&gt;This project is created for educational purposes and security research only. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool. Operating this tool on networks you do not own is strictly prohibited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/assembly-httpserver/" rel="noopener noreferrer"&gt;Defying Python: Building a Bare-Metal HTTP Server in x86_64 Assembly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/algorithmforprinting-ip_addresses/" rel="noopener noreferrer"&gt;Solving IP Endianness in x64 Assembly: A Single-Pass Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/network-fingerprinting/" rel="noopener noreferrer"&gt;Network Fingerprinting: Analyzing Default ICMP Structures and Payload Mimicry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>security</category>
      <category>learning</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Linux Process Evasion: ptrace &amp; prctl</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Sun, 28 Jun 2026 16:49:03 +0000</pubDate>
      <link>https://dev.to/jm00nj/linux-process-evasion-ptrace-prctl-4ae</link>
      <guid>https://dev.to/jm00nj/linux-process-evasion-ptrace-prctl-4ae</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;In cybersecurity research and Red Team simulations, developing custom tools requires a deep understanding of host-based evasion. When an agent lands on a target system, modern Blue Teams and Endpoint Detection and Response (EDR) solutions will attempt to attach a disassembler or a debugger to analyze the suspicious process.&lt;/p&gt;

&lt;p&gt;How do these processes defend themselves against analysis? In this article, we will explore the technical details of how the Linux kernel's own mechanisms—ptrace and prctl—can be utilized for process self-defense, strictly using pure x64 Assembly.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Preventing Debuggers with ptrace
&lt;/h3&gt;

&lt;p&gt;In Linux environments, tools like gdb or strace rely on the ptrace (Process Trace) system call to inspect the internal state and system calls of a process. However, the Linux kernel enforces a golden rule: A process can only be traced by one tracer at a time.&lt;/p&gt;

&lt;p&gt;This architectural constraint can be leveraged for defensive research. If an application sends a PTRACE_TRACEME command to itself the moment it starts, it effectively blocks any external analyst or tool from attaching to it. If an attempt is made, the operating system simply returns an EPERM (Operation not permitted) error.&lt;/p&gt;

&lt;p&gt;Here is how this logic is implemented in pure x64 Assembly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;_anti_debug:&lt;/span&gt;

    &lt;span class="c1"&gt;; Dynamically calculating Syscall 101 to evade static analysis&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;91&lt;/span&gt;         

    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;         &lt;span class="c1"&gt;; rax = 101 (sys_ptrace)&lt;/span&gt;



    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;        &lt;span class="c1"&gt;; arg1 = 0 (PTRACE_TRACEME)&lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;        &lt;span class="c1"&gt;; arg2 = 0&lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;        &lt;span class="c1"&gt;; arg3 = 0&lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;r10&lt;/span&gt;        &lt;span class="c1"&gt;; arg4 = 0&lt;/span&gt;

    &lt;span class="nf"&gt;syscall&lt;/span&gt;



    &lt;span class="c1"&gt;; Result check (If already being traced, rax returns negative)&lt;/span&gt;

    &lt;span class="nf"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;       

    &lt;span class="nf"&gt;js&lt;/span&gt; &lt;span class="nv"&gt;_exit&lt;/span&gt;            &lt;span class="c1"&gt;; If negative, debugger detected! Terminate the process.&lt;/span&gt;

    &lt;span class="nf"&gt;ret&lt;/span&gt;



&lt;span class="nl"&gt;_exit:&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;         &lt;span class="c1"&gt;; sys_exit&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;          &lt;span class="c1"&gt;; Exit with error code&lt;/span&gt;

    &lt;span class="nf"&gt;syscall&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;A key detail in the code above is that we avoid writing the sys_ptrace syscall number (101) directly into the code. Instead, we calculate it dynamically at runtime (91 + 10). This simple operation, known as Syscall Obfuscation, is a highly effective technique for bypassing static signature scans, such as YARA rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Preventing Memory Dumps with prctl
&lt;/h3&gt;

&lt;p&gt;We successfully blocked the debugger, but what if an incident responder takes a core dump of the process memory? When a RAM image is captured, all dynamically resolved strings, IP addresses, or command outputs become completely visible to the investigator.&lt;/p&gt;

&lt;p&gt;To mitigate this, we can turn to prctl (syscall 157), the process control mechanism of the Linux kernel. By passing the PR_SET_DUMPABLE argument with a value of 0 to the prctl function, we give the kernel a strict directive: "Forbid the creation of core dumps for this process at the OS level."&lt;/p&gt;

&lt;p&gt;The implementation is quite minimal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;
&lt;span class="nl"&gt;_disable_memory_dump:&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;157&lt;/span&gt;        &lt;span class="c1"&gt;; sys_prctl&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;          &lt;span class="c1"&gt;; arg1 = 4 (PR_SET_DUMPABLE)&lt;/span&gt;

    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;          &lt;span class="c1"&gt;; arg2 = 0 (SUID_DUMP_DISABLE)&lt;/span&gt;

    &lt;span class="nf"&gt;syscall&lt;/span&gt;

    &lt;span class="nf"&gt;ret&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Once this function is executed, even users without root privileges are restricted from accessing the memory of your process. When combined with a process that runs in the background (daemonized via fork) and masquerades as a legitimate system service (like systemd-resolved), this technique creates a significant blind spot for analysts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operational Security (OPSEC) and Conclusion
&lt;/h2&gt;

&lt;p&gt;Developing a tool that secures itself by communicating directly with the kernel, without even relying on the standard libc library, elevates the code from a standard script to a work of low-level engineering.&lt;/p&gt;

&lt;p&gt;Naturally, modern and advanced EDR solutions operating at the Kernel level (via eBPF) have the capability to intercept system calls. However, understanding and utilizing the ptrace and prctl combination is an excellent baseline technique for evading manual analysis by Incident Response teams and traditional antivirus software.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Legal Disclaimer
&lt;/h2&gt;

&lt;p&gt;This project is created for educational purposes and security research only. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool. Operating this tool on networks you do not own is strictly prohibited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/algorithmforprinting-ip_addresses/" rel="noopener noreferrer"&gt;Solving IP Endianness in x64 Assembly: A Single-Pass Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/compressdpcm-rle/" rel="noopener noreferrer"&gt;VESQER: A DPCM+RLE Hybrid Compressor in Pure x64 Assembly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/icmp_sniffer/" rel="noopener noreferrer"&gt;Building a Low-Level ICMP Sniffer in x64 Assembly (Raw Sockets)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Linux sends ICMP Echo with 56-byte payload + 8-byte header = 64 bytes total. Windows uses 32 bytes. NDR systems fingerprint hosts by these defaults — before any exploit lands. This post shows how to mimic any OS at the raw socket level.</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Sat, 27 Jun 2026 12:58:28 +0000</pubDate>
      <link>https://dev.to/jm00nj/linux-sends-icmp-echo-with-56-byte-payload-8-byte-header-64-bytes-total-windows-uses-32-bytes-1jhe</link>
      <guid>https://dev.to/jm00nj/linux-sends-icmp-echo-with-56-byte-payload-8-byte-header-64-bytes-total-windows-uses-32-bytes-1jhe</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636" class="crayons-story__hidden-navigation-link"&gt;Network Fingerprinting: Analyzing Default ICMP Structures and Payload Mimicry&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/jm00nj" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4005327%2Fcdc71a8f-4480-456e-b794-da1d549753c0.png" alt="jm00nj profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jm00nj" class="crayons-story__secondary fw-medium m:hidden"&gt;
              JM00NJ
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                JM00NJ
                
              
              &lt;div id="story-author-preview-content-4005878" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jm00nj" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4005327%2Fcdc71a8f-4480-456e-b794-da1d549753c0.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;JM00NJ&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 27&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636" id="article-link-4005878"&gt;
          Network Fingerprinting: Analyzing Default ICMP Structures and Payload Mimicry
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cybersecurity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cybersecurity&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/assembly"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;assembly&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/linux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;linux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/network"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;network&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>cybersecurity</category>
      <category>linux</category>
      <category>networking</category>
      <category>security</category>
    </item>
    <item>
      <title>Network Fingerprinting: Analyzing Default ICMP Structures and Payload Mimicry</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Sat, 27 Jun 2026 12:46:31 +0000</pubDate>
      <link>https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636</link>
      <guid>https://dev.to/jm00nj/network-fingerprinting-analyzing-default-icmp-structures-and-payload-mimicry-4636</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;"In advanced network observability, understanding the default behavior of various operating systems is vital for traffic profiling. This article explores the structural differences in ICMP Echo Requests across different OS environments and analyzes how 'Traffic Mimicry' can be used to evaluate the accuracy of Network Intrusion Detection Systems (NIDS)."&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Anatomy of an ICMP Signature
&lt;/h3&gt;

&lt;p&gt;A standard ICMP Echo Request is not just a simple signal; it carries a specific "fingerprint" based on the operating system that generated it. These fingerprints consist of:&lt;/p&gt;

&lt;p&gt;Total Packet Size&lt;/p&gt;

&lt;p&gt;TTL (Time to Live) values&lt;/p&gt;

&lt;p&gt;Default Payload Content&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cross-Platform Discrepancies (Linux vs. Windows)
&lt;/h3&gt;

&lt;p&gt;When a system sends a "ping," the default data size ($D$) and the total packet length ($L$) vary significantly between architectures.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Linux (Typical)&lt;/th&gt;
&lt;th&gt;Windows (Typical)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Size ($D$)&lt;/td&gt;
&lt;td&gt;56 Bytes&lt;/td&gt;
&lt;td&gt;32 Bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ICMP Header ($H$)&lt;/td&gt;
&lt;td&gt;8 Bytes&lt;/td&gt;
&lt;td&gt;8 Bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total ICMP Length ($L$)&lt;/td&gt;
&lt;td&gt;64 Bytes&lt;/td&gt;
&lt;td&gt;40 Bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default Payload&lt;/td&gt;
&lt;td&gt;Timestamp + Data&lt;/td&gt;
&lt;td&gt;abcdefg...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Linux Signature&lt;br&gt;
In most Linux distributions, the ping utility sends 56 bytes of data. When combined with the 8-byte ICMP header, it totals 64 bytes. A key characteristic of Linux ICMP traffic is that the first few bytes of the payload are often occupied by a high-resolution timestamp, used to calculate RTT (Round Trip Time) with microsecond precision.&lt;/p&gt;

&lt;p&gt;The Windows Signature&lt;br&gt;
Windows systems default to a 32-byte data payload. The payload content is static and follows a predictable alphabetical sequence: abcdefghijklmnopqrstuvwabcdefghi. This static nature makes Windows ICMP traffic easily identifiable during deep packet inspection (DPI).&lt;/p&gt;
&lt;h3&gt;
  
  
  3. The Concept of Traffic Mimicry
&lt;/h3&gt;

&lt;p&gt;Traffic Mimicry is a research method used to test the resilience of network filters. By aligning custom communication protocols with the default signatures of a specific OS, researchers can evaluate whether a security appliance is biased toward certain traffic patterns.&lt;/p&gt;

&lt;p&gt;For example, when developing a Remote Management Interface in x64 Assembly, ensuring the payload size ($D$) is exactly 32 bytes or 56 bytes allows the traffic to blend into the "Ambient Noise" of a corporate network.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Implementation: Engineering a Mimicry-Aligned Packet Structure
&lt;/h3&gt;

&lt;p&gt;To evaluate the resilience of network monitoring tools, we must construct a packet architecture that adheres strictly to the structural expectations of a standard Linux environment. Below is the assembly-level definition of an ICMP Echo Reply, designed with Structural Alignment in mind.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;
&lt;span class="c1"&gt;; --- [MIMICRY UPDATE] UPDATED PACKET ARCHITECTURE ---&lt;/span&gt;

&lt;span class="c1"&gt;; This structure strictly aligns with the 64-byte Linux ICMP Echo signature&lt;/span&gt;

&lt;span class="nl"&gt;icmp_packet:&lt;/span&gt;

    &lt;span class="nf"&gt;type&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                &lt;span class="c1"&gt;; ICMP Type 0 (Echo Reply)&lt;/span&gt;

    &lt;span class="nf"&gt;code&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                

    &lt;span class="nf"&gt;checksum&lt;/span&gt; &lt;span class="nv"&gt;dw&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;            &lt;span class="c1"&gt;; Checksum placeholder&lt;/span&gt;

    &lt;span class="nf"&gt;identifier&lt;/span&gt; &lt;span class="nv"&gt;dw&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;          &lt;span class="c1"&gt;; Process Identifier&lt;/span&gt;

    &lt;span class="nf"&gt;sequence&lt;/span&gt; &lt;span class="nv"&gt;dw&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;            &lt;span class="c1"&gt;; Internal signaling sequence&lt;/span&gt;



    &lt;span class="c1"&gt;; --- MIMICRY PADDING (24 BYTES) ---&lt;/span&gt;

    &lt;span class="c1"&gt;; Emulates the default timestamp behavior of modern network stacks&lt;/span&gt;

    &lt;span class="nf"&gt;mimicry_ts&lt;/span&gt; &lt;span class="nv"&gt;dq&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;          &lt;span class="c1"&gt;; 8-byte Dynamic Timestamp (Cycle-accurate timing)&lt;/span&gt;



    &lt;span class="c1"&gt;; 16-byte Sequential Padding: Replicates standard OS data patterns&lt;/span&gt;

    &lt;span class="nf"&gt;mimicry_seq&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x17&lt;/span&gt;

                &lt;span class="kd"&gt;db&lt;/span&gt; &lt;span class="mh"&gt;0x18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x1F&lt;/span&gt;



    &lt;span class="c1"&gt;; --- DATA TRANSMISSION AREA ---&lt;/span&gt;

    &lt;span class="nf"&gt;payload&lt;/span&gt; &lt;span class="nv"&gt;times&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;    &lt;span class="c1"&gt;; 32-byte payload chunk to hit the 64-byte total&lt;/span&gt;

    &lt;span class="no"&gt;payload_len&lt;/span&gt;&lt;span class="kd"&gt; equ&lt;/span&gt; &lt;span class="kc"&gt;$&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;icmp_packet&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Analysis of the Structural Logic&lt;br&gt;
Timestamp Emulation (mimicry_ts): Standard Linux ping requests embed an 8-byte timestamp to calculate RTT. By reserving this space and populating it with high-precision timing data (via RDTSC), our custom communication layer avoids the "Empty Payload" signature that often triggers NIDS anomalies.&lt;/p&gt;

&lt;p&gt;Sequential Byte Padding (mimicry_seq): Many network filters look for entropy in the payload. By utilizing a fixed, sequential padding (0x10 to 0x1F), we replicate the predictable data structures of kernel-level protocol implementations.&lt;/p&gt;

&lt;p&gt;The 64-Byte Structural Boundary: By dedicating 24 bytes to structural emulation and allocating exactly 32 bytes for the data payload, the internal data segment equals the 56-byte Linux standard. When combined with the 8-byte ICMP header, the total packet size is precisely 64 bytes (8 + 24 + 32 = 64). This ensures that the traffic volume remains strictly within the expected "Ambient Noise" threshold.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Defensive Implications: Anomaly Detection
&lt;/h3&gt;

&lt;p&gt;From a Blue Team perspective, identifying "Mimicry" requires looking beyond packet size. Advanced detection strategies include:&lt;/p&gt;

&lt;p&gt;Entropy Analysis: Monitoring the randomness of the payload.&lt;/p&gt;

&lt;p&gt;TTL Consistency: Checking if the TTL value matches the expected OS signature.&lt;/p&gt;

&lt;p&gt;Frequency Analysis: Analyzing if the ICMP requests follow the standard interval pattern of a human-initiated ping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding the "Default State" of network protocols is the first step in advanced system auditing. Mimicry is not just about blending in; it is a critical tool for identifying the limitations of signature-based detection. By mastering the low-level construction of ICMP packets, researchers can develop more robust and observable communication frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Legal Disclaimer
&lt;/h2&gt;

&lt;p&gt;This project is created for educational purposes and security research only. Unauthorized access to computer systems is illegal. The author is not responsible for any misuse of this tool. Operating this tool on networks you do not own is strictly prohibited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/icmp_sniffer/" rel="noopener noreferrer"&gt;Building a Low-Level ICMP Sniffer in x64 Assembly (Raw Sockets)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/algorithmforprinting-ip_addresses/" rel="noopener noreferrer"&gt;Solving IP Endianness in x64 Assembly: A Single-Pass Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/assembly-httpserver/" rel="noopener noreferrer"&gt;Defying Python: Building a Bare-Metal HTTP Server in x86_64 Assembly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>assembly</category>
      <category>linux</category>
      <category>network</category>
    </item>
    <item>
      <title>Solving IP Endianness in x64 Assembly: A Single-Pass Algorithm</title>
      <dc:creator>JM00NJ</dc:creator>
      <pubDate>Sat, 27 Jun 2026 12:34:20 +0000</pubDate>
      <link>https://dev.to/jm00nj/solving-ip-endianness-in-x64-assembly-a-single-pass-algorithm-3n9b</link>
      <guid>https://dev.to/jm00nj/solving-ip-endianness-in-x64-assembly-a-single-pass-algorithm-3n9b</guid>
      <description>&lt;h2&gt;
  
  
  Research Context
&lt;/h2&gt;

&lt;p&gt;When doing low-level network programming in Assembly, you experience firsthand the immense chaos running behind the scenes of operations we solve with a single line in high-level languages (Python, C, etc.). While developing the Nested-ICMP-Communication Analysis project, specifically an Encapsulated ICMP framework, I hit exactly this kind of wall: extracting an IP address from a packet header and printing it to the screen in the correct format.&lt;/p&gt;

&lt;p&gt;Sounds simple, right? However, when x86 architecture and network protocols are involved, seeing 5.1.168.192 instead of 192.168.1.5 on your terminal is extremely common.&lt;/p&gt;

&lt;p&gt;So why does this happen, and what kind of algorithm did I develop to overcome this issue during the debugging process? Let's dive into the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Endianness Problem in Network Headers
&lt;/h3&gt;

&lt;p&gt;When you capture a packet coming over the network and read the source/destination IP address inside the sockaddr_in structure, the data arrives in Network Byte Order (Big-Endian) format. This means the most significant byte is stored at the lowest memory address.&lt;/p&gt;

&lt;p&gt;However, the x86/x64 processor architectures we use rely on Little-Endian (Host Byte Order). When the processor pulls this 4-byte IP data into a register, the reading direction is effectively reversed for our purposes.&lt;/p&gt;

&lt;p&gt;The result? A packet that arrives as 192.168.1.5 appears scrambled if we try to naively print it from memory. The inet_ntoa() function in high-level languages handles this conversion in the background. But if you are writing a custom sniffer in pure Assembly, you must do this conversion byte by byte yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging Hell: The Problems Encountered
&lt;/h3&gt;

&lt;p&gt;While writing this conversion, I encountered a few critical issues that cost me hours in GDB (GNU Debugger):&lt;/p&gt;

&lt;p&gt;Register Clashes: While separating each octet (byte) of the IP address and converting it to an ASCII character (string), you must use the AX register for division operations (DIV). If you don't carefully manage your remainders (AH) and quotients (AL), the numbers of the IP address get completely corrupted.&lt;/p&gt;

&lt;p&gt;The Dot (.) Separator: It's not enough to just convert the numbers; a . (0x2E / 46 in decimal) character must be inserted exactly between each octet, but not at the very end.&lt;/p&gt;

&lt;p&gt;Performance Loss (The Reversing Trap): In standard logic, you parse the IP, convert it to a string, and realize the string is backwards. Then, you write a second loop to reverse that string. This creates unnecessary memory read/write cycles and bloats the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: A Single-Pass Backward Build Algorithm
&lt;/h3&gt;

&lt;p&gt;To solve the problem, instead of creating the string and then reversing it, I designed a more optimized algorithm.&lt;/p&gt;

&lt;p&gt;The logic is simple but highly effective: Read the IP bytes backwards, and write the ASCII string backwards. By starting at the end of the IP address within the sockaddr_in structure (offset 7 down to 4) and writing from the end of a 15-byte output buffer (addr_ip) down to index 0, the string naturally formats itself correctly from left to right.&lt;/p&gt;

&lt;p&gt;Here is the exact critical loop from my engine:&lt;/p&gt;

&lt;h3&gt;
  
  
  ASSEMBLY CODE:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;
&lt;span class="c1"&gt;; IP ADDRESS TO STRING ALGORITHM (EXTRACT REVERSE BYTE-BY-BYTE AND CONVERT TO ASCII)&lt;/span&gt;

    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rdx&lt;/span&gt;                &lt;span class="c1"&gt;; Clear rdx&lt;/span&gt;
    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;rbx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;rbx&lt;/span&gt;                &lt;span class="c1"&gt;; Clear rbx&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;                  &lt;span class="c1"&gt;; Start index for reading IP from sockaddr_in (sin_addr offset)&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;                 &lt;span class="c1"&gt;; Start index for writing to the addr_ip buffer (backwards)&lt;/span&gt;
&lt;span class="nl"&gt;_loopforip:&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;                  &lt;span class="c1"&gt;; Divisor for base-10 conversion&lt;/span&gt;
    &lt;span class="nf"&gt;movzx&lt;/span&gt; &lt;span class="nb"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;incoming_addr&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;; Fetch one octet from IP address&lt;/span&gt;
&lt;span class="nl"&gt;_divloop:&lt;/span&gt;
    &lt;span class="nf"&gt;div&lt;/span&gt; &lt;span class="nb"&gt;bl&lt;/span&gt;                      &lt;span class="c1"&gt;; Divide AX by 10; AL = quotient, AH = remainder&lt;/span&gt;
    &lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="nb"&gt;ah&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;                  &lt;span class="c1"&gt;; Convert remainder to ASCII character&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;addr_ip&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;ah&lt;/span&gt;       &lt;span class="c1"&gt;; Store ASCII character in the output buffer&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;                     &lt;span class="c1"&gt;; Move buffer pointer backward&lt;/span&gt;
    &lt;span class="nf"&gt;xor&lt;/span&gt; &lt;span class="nb"&gt;ah&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;ah&lt;/span&gt;                  &lt;span class="c1"&gt;; Clear AH for the next division cycle&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                   &lt;span class="c1"&gt;; Check if quotient is zero&lt;/span&gt;
    &lt;span class="nf"&gt;jg&lt;/span&gt; &lt;span class="nv"&gt;_divloop&lt;/span&gt;                 &lt;span class="c1"&gt;; If not zero, continue extracting digits&lt;/span&gt;

    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;                  &lt;span class="c1"&gt;; Check if this is the last octet (first IP block)&lt;/span&gt;
    &lt;span class="nf"&gt;je&lt;/span&gt; &lt;span class="nv"&gt;_contiune&lt;/span&gt;                &lt;span class="c1"&gt;; If last octet, skip adding the dot separator&lt;/span&gt;
    &lt;span class="nf"&gt;mov&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;addr_ip&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;rdi&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;46&lt;/span&gt;  &lt;span class="c1"&gt;; Insert '.' (dot) character&lt;/span&gt;
&lt;span class="nl"&gt;_contiune:&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rdi&lt;/span&gt;                     &lt;span class="c1"&gt;; Move buffer pointer backward for the next octet&lt;/span&gt;
    &lt;span class="nf"&gt;dec&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;                     &lt;span class="c1"&gt;; Move to the next IP octet in sockaddr_in&lt;/span&gt;
    &lt;span class="nf"&gt;cmp&lt;/span&gt; &lt;span class="nb"&gt;rcx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;                  &lt;span class="c1"&gt;; Check if all 4 octets have been processed&lt;/span&gt;
    &lt;span class="nf"&gt;jg&lt;/span&gt; &lt;span class="nv"&gt;_loopforip&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This single-pass method successfully converts the raw network bytes into a human-readable ASCII string using minimal CPU cycles, entirely avoiding an extra "string reversing" loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion and Open Source
&lt;/h3&gt;

&lt;p&gt;Network programming in Assembly might seem tedious at first, but it is a unique experience for understanding the true mechanics underlying these systems. Especially when working on tunneling architectures aimed at Evaluating IDS detection resilience, having this level of byte-control is absolutely vital.&lt;/p&gt;

&lt;p&gt;You can find this algorithm in action, along with the complete source code of the asm-icmp-sniffer, on my GitHub profile:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JM00NJ/asm-icmp-sniffer" rel="noopener noreferrer"&gt;JM00NJ/asm-icmp-sniffer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disclaimer: This article and the associated source code are intended for educational purposes and authorized security research only. Understanding low-level network protocols is essential for building better defense mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/icmp_sniffer/" rel="noopener noreferrer"&gt;Building a Low-Level ICMP Sniffer in x64 Assembly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/syscall-bss/" rel="noopener noreferrer"&gt;Linux x64 Assembly: Syscalls, Registers, and the .bss Segment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://netacoding.com/posts/anti-analysis/" rel="noopener noreferrer"&gt;Linux Process Evasion: ptrace &amp;amp; prctl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>assembly</category>
      <category>linux</category>
      <category>lowlevel</category>
      <category>security</category>
    </item>
  </channel>
</rss>
