<?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://pages.0xjastrzab.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://pages.0xjastrzab.net/" rel="alternate" type="text/html" /><updated>2026-08-23T21:34:45+00:00</updated><id>https://pages.0xjastrzab.net/feed.xml</id><title type="html">0xJastrzab Pages</title><subtitle>Personal site and blog of bensaida011 — projects, write-ups, and things I&apos;m working on.</subtitle><entry><title type="html">Android Malware Detection using RNN and RNN-GRU models</title><link href="https://pages.0xjastrzab.net/android/machine-learning/malware-detection/neural-networks/2026/08/23/Android-Malware-Detection.html" rel="alternate" type="text/html" title="Android Malware Detection using RNN and RNN-GRU models" /><published>2026-08-23T21:30:00+00:00</published><updated>2026-08-23T21:30:00+00:00</updated><id>https://pages.0xjastrzab.net/android/machine-learning/malware-detection/neural-networks/2026/08/23/Android-Malware-Detection</id><content type="html" xml:base="https://pages.0xjastrzab.net/android/machine-learning/malware-detection/neural-networks/2026/08/23/Android-Malware-Detection.html"><![CDATA[<p>Blog Coming soon…</p>]]></content><author><name></name></author><category term="android" /><category term="machine-learning" /><category term="malware-detection" /><category term="neural-networks" /><summary type="html"><![CDATA[Blog Coming soon…]]></summary></entry><entry><title type="html">Reverse Engineering a BLE Gimbal Controller with Frida</title><link href="https://pages.0xjastrzab.net/reverse-engineering/ble/python/2026/08/23/Feiyu-Gimbal.html" rel="alternate" type="text/html" title="Reverse Engineering a BLE Gimbal Controller with Frida" /><published>2026-08-23T10:00:00+00:00</published><updated>2026-08-23T10:00:00+00:00</updated><id>https://pages.0xjastrzab.net/reverse-engineering/ble/python/2026/08/23/Feiyu-Gimbal</id><content type="html" xml:base="https://pages.0xjastrzab.net/reverse-engineering/ble/python/2026/08/23/Feiyu-Gimbal.html"><![CDATA[<h2 id="the-problem">The problem</h2>

<p>I recently got this device from a member of Hackerspace Trójmiasto. The Feiyu Scorp C gimbal only ships with a closed-source Android app, no public API, no documentation, no way to script it from a computer. The app talks to the gimbal over Bluetooth Low Energy (BLE), so in principle every command it can send is just a handful of bytes over a GATT characteristic. If I could capture those bytes, I could replay them from anything — a Python script, a Flask server, a Raspberry Pi.</p>

<p>This post walks through the process: hooking the Android app with Frida, capturing the raw BLE writes, and turning them into a small Python controller.</p>

<h2 id="step-1-find-the-gatt-characteristics">Step 1: Find the GATT characteristics</h2>

<p>Before touching the app, I paired the gimbal and used <code class="language-plaintext highlighter-rouge">nRF Connect</code> (Android) to poke around its GATT table. Most BLE peripherals expose a vendor-specific service with a <strong>write</strong> characteristic (commands go in) and a <strong>notify</strong> characteristic (status/telemetry comes out). For the Scorp C:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DEVICE_ADDRESS: 94:B5:55:D8:25:5A
WRITE_CHAR:     0000ff01-0000-1000-8000-00805f9b34fb
NOTIFY_CHAR:    0000ff02-0000-1000-8000-00805f9b34fb
</code></pre></div></div>

<p>Knowing the characteristics tells you <em>where</em> the commands land, but not <em>what bytes</em> to send. For that you need to watch the real app talk to the real device.</p>

<h2 id="step-2-hook-the-app-with-frida">Step 2: Hook the app with Frida</h2>

<p>Frida lets you inject JavaScript into a running process and intercept function calls — including the Android Bluetooth stack’s <code class="language-plaintext highlighter-rouge">writeCharacteristic</code> calls, before they ever leave the phone. Setup:</p>

<ul>
  <li>Rooted Android device (or emulator with root), USB debugging enabled</li>
  <li><code class="language-plaintext highlighter-rouge">frida-server</code> running on the device, matching the host <code class="language-plaintext highlighter-rouge">frida</code> version</li>
  <li>The Feiyu app installed and BLE-paired with the gimbal</li>
</ul>

<p>The hook itself targets <code class="language-plaintext highlighter-rouge">android.bluetooth.BluetoothGatt.writeCharacteristic</code>:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Java</span><span class="p">.</span><span class="nx">perform</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">BluetoothGatt</span> <span class="o">=</span> <span class="nx">Java</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="dl">"</span><span class="s2">android.bluetooth.BluetoothGatt</span><span class="dl">"</span><span class="p">);</span>

  <span class="nx">BluetoothGatt</span><span class="p">.</span><span class="nx">writeCharacteristic</span><span class="p">.</span><span class="nx">overload</span><span class="p">(</span>
    <span class="dl">"</span><span class="s2">android.bluetooth.BluetoothGattCharacteristic</span><span class="dl">"</span>
  <span class="p">).</span><span class="nx">implementation</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">characteristic</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">characteristic</span><span class="p">.</span><span class="nx">getValue</span><span class="p">();</span>
    <span class="kd">const</span> <span class="nx">hex</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">(</span><span class="dl">"</span><span class="s2">0</span><span class="dl">"</span> <span class="o">+</span> <span class="p">(</span><span class="nx">b</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">).</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)).</span><span class="nx">slice</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">);</span>
    <span class="p">}).</span><span class="nx">join</span><span class="p">(</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">);</span>

    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">[WRITE] </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">characteristic</span><span class="p">.</span><span class="nx">getUuid</span><span class="p">()</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">  </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">hex</span><span class="p">);</span>

    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">writeCharacteristic</span><span class="p">(</span><span class="nx">characteristic</span><span class="p">);</span>
  <span class="p">};</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Running this and then mashing every button in the app (joystick directions, mode switches, record/photo toggles) produces a log of every command the app is capable of sending:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[WRITE] 0000ff01-0000-1000-8000-00805f9b34fb  24 3c 00 01 0d 0a
[WRITE] 0000ff01-0000-1000-8000-00805f9b34fb  24 3c 00 02 0e 0a
[WRITE] 0000ff01-0000-1000-8000-00805f9b34fb  24 3c 00 10 1c 0a
...
</code></pre></div></div>

<p>Each distinct hex string, correlated with the button that was pressed while it was captured, becomes a documented command.</p>

<h2 id="step-3-figure-out-the-connection-sequence">Step 3: Figure out the connection sequence</h2>

<p>Devices like this usually aren’t stateless — sending a “move” command cold, without whatever handshake the app does on connect, is a common way to get silently ignored. Diffing the very first writes after each fresh pairing against the steady-state traffic showed a fixed 8-write connection sequence, followed by a repeating keep-alive packet every ~800ms. Skip either one and the gimbal stops responding within a few seconds even though the BLE link itself stays up.</p>

<p>That distinction — one-time setup vs. continuous heartbeat vs. one-shot command vs. held-down directional command — is the actual reverse-engineering result here. The bytes are trivial to copy; understanding when and how long to send them is the part that takes iteration.</p>

<h2 id="step-4-replay-from-python">Step 4: Replay from Python</h2>

<p>With the command table and connection sequence in hand, <code class="language-plaintext highlighter-rouge">bleak</code> (a cross-platform async BLE library) can drive the gimbal directly:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">bleak</span> <span class="kn">import</span> <span class="n">BleakClient</span>

<span class="n">DEVICE_ADDRESS</span> <span class="o">=</span> <span class="s">"94:B5:55:D8:25:5A"</span>
<span class="n">WRITE_CHAR</span> <span class="o">=</span> <span class="s">"0000ff01-0000-1000-8000-00805f9b34fb"</span>

<span class="n">CONNECTION_SEQUENCE</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">"24 3c 00 00 0c 0a"</span><span class="p">,</span>
    <span class="c1"># ... remaining captured handshake writes
</span><span class="p">]</span>

<span class="k">async</span> <span class="k">def</span> <span class="nf">connect_and_init</span><span class="p">():</span>
    <span class="k">async</span> <span class="k">with</span> <span class="n">BleakClient</span><span class="p">(</span><span class="n">DEVICE_ADDRESS</span><span class="p">)</span> <span class="k">as</span> <span class="n">client</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">cmd_hex</span> <span class="ow">in</span> <span class="n">CONNECTION_SEQUENCE</span><span class="p">:</span>
            <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="n">write_gatt_char</span><span class="p">(</span><span class="n">WRITE_CHAR</span><span class="p">,</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">cmd_hex</span><span class="p">))</span>
            <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.03</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"Gimbal initialized."</span><span class="p">)</span>

<span class="n">asyncio</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">connect_and_init</span><span class="p">())</span>
</code></pre></div></div>

<p>From there it’s straightforward to wrap the write in a small Flask app with a web UI: a background asyncio loop owns the BLE connection and heartbeat, and Flask request handlers hand commands off to it with <code class="language-plaintext highlighter-rouge">asyncio.run_coroutine_threadsafe</code>.</p>

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

<ul>
  <li>BLE traffic capture doesn’t require rooting the protocol — you don’t need to know why <code class="language-plaintext highlighter-rouge">24 3c 00 10 1c 0a</code> means “tilt up,” you just need to capture it reliably and reproduce the timing around it.</li>
  <li>Frida’s Java hooks are the highest-leverage tool for this kind of work on Android — no APK decompilation needed if you can intercept the call at the framework boundary instead.</li>
  <li>Timing state (handshakes, heartbeats, hold-vs-toggle) is usually the hard part, not the payload bytes themselves.</li>
</ul>]]></content><author><name></name></author><category term="reverse-engineering" /><category term="ble" /><category term="python" /><category term="bluetooth-le" /><category term="frida" /><category term="android" /><category term="gatt" /><category term="python" /><category term="flask" /><summary type="html"><![CDATA[The problem]]></summary></entry><entry><title type="html">Hello World — About Me</title><link href="https://pages.0xjastrzab.net/introduction/2026/08/22/welcome.html" rel="alternate" type="text/html" title="Hello World — About Me" /><published>2026-08-22T21:41:07+00:00</published><updated>2026-08-22T21:41:07+00:00</updated><id>https://pages.0xjastrzab.net/introduction/2026/08/22/welcome</id><content type="html" xml:base="https://pages.0xjastrzab.net/introduction/2026/08/22/welcome.html"><![CDATA[<p>I’m a cybersecurity graduate from Coventry University with First Class Honours. My interests lie in penetration testing, reverse engineering, and building security tooling.</p>

<h2 id="what-i-work-with">What I Work With</h2>

<p><strong>Security:</strong> Burp Suite, Metasploit, Nmap, ffuf, SQLMap, Enum4Linux</p>

<p><strong>Reverse Engineering:</strong> Radare2, Cutter, Ghidra, GDB/GEF, pwntools, Frida</p>

<p><strong>Languages:</strong> Python, C++, Bash</p>

<p><strong>Other:</strong> Docker, FastAPI, Flask, OpenCV, TensorFlow, Git</p>

<h2 id="projects">Projects</h2>

<p><strong>Web Application &amp; Network Penetration Testing</strong> — Black-box pen test across 5 services with 7 findings scored using OWASP risk methodology. Exploited SSTI, RCE, and credential-based attacks.</p>

<p><strong>Reverse Engineering &amp; Binary Exploitation</strong> — Reverse-engineered binaries with Radare2 and Cutter. Developed ROP chain exploits and practised code cave injection.</p>

<p><strong>Android Malware Classification</strong> — ML pipeline using Androguard and TensorFlow to classify 15,000+ APK samples with 99% accuracy.</p>

<p><strong>BLE Gimbal Controller</strong> — Reverse-engineered a Bluetooth LE protocol with Frida and built a Flask REST API for real-time hardware control.</p>

<p>More write-ups coming soon. You can also find me on <a href="https://github.com/bensaida011">GitHub</a>.</p>]]></content><author><name></name></author><category term="introduction" /><summary type="html"><![CDATA[I’m a cybersecurity graduate from Coventry University with First Class Honours. My interests lie in penetration testing, reverse engineering, and building security tooling.]]></summary></entry></feed>