<?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="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-08-28T12:51:59+00:00</updated><id>/feed.xml</id><title type="html">./amnesia.sh</title><subtitle>Malware analyst, reverse engineer, and occasional CTFer.</subtitle><entry><title type="html">BrunnerCTF - Secret Storage</title><link href="/ctf/2026/08/28/secret-storage.html" rel="alternate" type="text/html" title="BrunnerCTF - Secret Storage" /><published>2026-08-28T12:45:00+00:00</published><updated>2026-08-28T12:45:00+00:00</updated><id>/ctf/2026/08/28/secret-storage</id><content type="html" xml:base="/ctf/2026/08/28/secret-storage.html"><![CDATA[<p>Last weekend I played in <a href="https://ctf.brunnerne.dk/">BrunnerCTF</a>. The challenges looked to be on the easier side, with most challenges having over 100 solves, so I decided to try a challenge in a category other than reversing for once.</p>

<h2 id="challenge-overview">Challenge overview</h2>

<blockquote>
  <p>BrunnerCorp needed a new secret vault for their various needs, but license fees are too expensive nowadays! Thankfully, the intern had a ChatGPT Plus subscription, which is all they needed. Unfortunately the pentesters found the source code on a public GitHub along with an <em>encrypted vault export</em>, but that shouldn’t be a problem… right? Oh and they phished one of the users. Here’s their creds:</p>

  <p><code class="language-plaintext highlighter-rouge">maya.chen@brunnercorp.tld:Odense2026!</code></p>
</blockquote>

<p>We’re given a web application for some kind of “secrets manager” application, along with an exported secrets vault. One of the exported secrets is, of course, the encrypted flag.</p>

<p><img src="/assets/images/brunner/secret_storage_page.png" alt="" /></p>

<p>My first instinct was to look at the <code class="language-plaintext highlighter-rouge">encrypt</code> and <code class="language-plaintext highlighter-rouge">decrypt</code> helper functions that are used for the bulk of the cryptography in this challenge. Unusually for a crypto challenge, though, the algorithm is pretty standard.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">encrypt</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">key</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">associated_data</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">nonce</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">urandom</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
    <span class="n">ciphertext</span> <span class="o">=</span> <span class="n">AESGCM</span><span class="p">(</span><span class="n">key</span><span class="p">).</span><span class="n">encrypt</span><span class="p">(</span><span class="n">nonce</span><span class="p">,</span> <span class="n">value</span><span class="p">.</span><span class="n">encode</span><span class="p">(),</span> <span class="n">associated_data</span><span class="p">.</span><span class="n">encode</span><span class="p">())</span>
    <span class="k">return</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64encode</span><span class="p">(</span><span class="n">nonce</span> <span class="o">+</span> <span class="n">ciphertext</span><span class="p">).</span><span class="n">decode</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">token</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">key</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span> <span class="n">associated_data</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">raw</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="n">token</span><span class="p">.</span><span class="n">encode</span><span class="p">())</span>
        <span class="k">return</span> <span class="p">(</span>
            <span class="n">AESGCM</span><span class="p">(</span><span class="n">key</span><span class="p">).</span><span class="n">decrypt</span><span class="p">(</span><span class="n">raw</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">raw</span><span class="p">[</span><span class="mi">12</span><span class="p">:],</span> <span class="n">associated_data</span><span class="p">.</span><span class="n">encode</span><span class="p">()).</span><span class="n">decode</span><span class="p">()</span>
        <span class="p">)</span>
    <span class="k">except</span> <span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="n">InvalidTag</span><span class="p">,</span> <span class="nb">UnicodeDecodeError</span><span class="p">)</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"ciphertext authentication failed"</span><span class="p">)</span> <span class="k">from</span> <span class="n">exc</span>
</code></pre></div></div>

<p>The encryption uses AES-GCM with a random nonce, and the tag is validated on decryption. AES-GCM is a standard and secure encryption algorithm, so we can’t solve the challenge by breaking it. Instead, we’ll have to look for some kind of implementation mistake that we can use to trick the server into decrypting the flag for us.</p>

<p>The most promising-looking function for this is <code class="language-plaintext highlighter-rouge">import_vault</code>. We have an exported vault, so it would make sense for us to be able to import the vault and have the server decrypt the flag with its key.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="n">api</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/api/v1/vault/import"</span><span class="p">)</span>
<span class="o">@</span><span class="n">role_required</span><span class="p">(</span><span class="s">"admin"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">import_vault</span><span class="p">():</span>
    <span class="n">body</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">get_json</span><span class="p">(</span><span class="n">silent</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> <span class="ow">or</span> <span class="p">{}</span>
    <span class="k">if</span> <span class="n">body</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"format"</span><span class="p">)</span> <span class="o">!=</span> <span class="s">"vault-export-v1"</span> <span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span>
        <span class="n">body</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"secrets"</span><span class="p">),</span> <span class="nb">list</span>
    <span class="p">):</span>
<span class="c1"># [...]
</span></code></pre></div></div>

<p>However, there are two problems with this:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">import_vault</code> can only be called by an admin, which we aren’t.</li>
  <li><code class="language-plaintext highlighter-rouge">import_vault</code> expects us to supply our own key for the vault decryption, rather than using the key known to the server.</li>
</ul>

<h2 id="getting-admin">Getting admin</h2>

<p>When we log into the site, a cookie called <code class="language-plaintext highlighter-rouge">secret_storage_session</code> is used to save our login session. The cookie data is a long base64 string, presumably encrypted.</p>

<p>Looking at the <code class="language-plaintext highlighter-rouge">open_session</code> function of the <code class="language-plaintext highlighter-rouge">EncryptedSessionInterface</code> class, we can see how the session cookie is used. The cookie is encrypted and decrypted using the same <code class="language-plaintext highlighter-rouge">encrypt</code> and <code class="language-plaintext highlighter-rouge">decrypt</code> helper functions that are used for the secrets, with the hard-coded AAD string <code class="language-plaintext highlighter-rouge">secret/session/v1</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">open_session</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
	<span class="n">token</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">cookies</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"SESSION_COOKIE_NAME"</span><span class="p">])</span>
	<span class="k">if</span> <span class="ow">not</span> <span class="n">token</span><span class="p">:</span>
		<span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">session_class</span><span class="p">()</span>
	<span class="k">try</span><span class="p">:</span>
		<span class="n">payload</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">key</span><span class="p">,</span> <span class="s">"secret/session/v1"</span><span class="p">)</span>
		<span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">session_class</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span>
	<span class="k">except</span> <span class="p">(</span><span class="nb">ValueError</span><span class="p">,</span> <span class="n">json</span><span class="p">.</span><span class="n">JSONDecodeError</span><span class="p">):</span>
		<span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">session_class</span><span class="p">()</span>
</code></pre></div></div>

<p>This is a pretty standard way for login sessions to work. The server encrypts the user’s session information using some kind of authenticated encryption scheme and sends back the ciphertext. Then, each time the user accesses the site, the cookie is sent to the server, where it’s decrypted on the server side. Since the user doesn’t know the key, the server knows the cookie could only have been issued by the server itself.</p>

<p>So, what key is being used in our <code class="language-plaintext highlighter-rouge">EncryptedSessionInterface</code> instance? It turns out to be the same <code class="language-plaintext highlighter-rouge">SECRET_ENCRYPTION_KEY</code> that’s being used to encrypt and decrypt the secrets:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create_app</span><span class="p">(</span><span class="n">config_class</span><span class="o">=</span><span class="n">Config</span><span class="p">):</span>
    <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
    <span class="n">app</span><span class="p">.</span><span class="n">config</span><span class="p">.</span><span class="n">from_object</span><span class="p">(</span><span class="n">config_class</span><span class="p">)</span>
    <span class="n">app</span><span class="p">.</span><span class="n">session_interface</span> <span class="o">=</span> <span class="n">EncryptedSessionInterface</span><span class="p">(</span>
        <span class="n">app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"SECRET_ENCRYPTION_KEY"</span><span class="p">]</span>
    <span class="p">)</span>
    <span class="n">app</span><span class="p">.</span><span class="n">teardown_appcontext</span><span class="p">(</span><span class="n">close_db</span><span class="p">)</span>
    <span class="n">app</span><span class="p">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>

    <span class="k">with</span> <span class="n">app</span><span class="p">.</span><span class="n">app_context</span><span class="p">():</span>
        <span class="n">init_db</span><span class="p">()</span>

    <span class="k">return</span> <span class="n">app</span>
</code></pre></div></div>

<p>Since we have the ability to create new secrets, we can take advantage of this to get the server to encrypt an admin session cookie for us. This is how secrets are encrypted in the <code class="language-plaintext highlighter-rouge">create_secret</code> function.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ciphertext</span> <span class="o">=</span> <span class="n">encrypt</span><span class="p">(</span>
	<span class="n">body</span><span class="p">[</span><span class="s">"value"</span><span class="p">],</span>
	<span class="n">current_app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"SECRET_ENCRYPTION_KEY"</span><span class="p">],</span>
	<span class="sa">f</span><span class="s">"secret/</span><span class="si">{</span><span class="n">body</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<p>The AAD string for the encrypted secret is the string <code class="language-plaintext highlighter-rouge">secret/</code> followed by the name of the secret. If we name a secret <code class="language-plaintext highlighter-rouge">session/v1</code>, then we can encrypt the text of the secret with the <code class="language-plaintext highlighter-rouge">secret/session/v1</code> AAD string that is used to validate the session cookie.</p>

<p>To get an idea of the format of the session cookie, I started up a local instance of the challenge using a known <code class="language-plaintext highlighter-rouge">SECRET_ENCRYPTION_KEY</code> of all zeroes, then decrypted the cookie for my own session as <code class="language-plaintext highlighter-rouge">maya.chen</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{"user_id":3,"role":"editor"}
</code></pre></div></div>

<p>In <code class="language-plaintext highlighter-rouge">init_db</code>, the admin account’s information is inserted before the accounts of the three normal users, so we’ll want to set our <code class="language-plaintext highlighter-rouge">user_id</code> to <code class="language-plaintext highlighter-rouge">1</code> . (The role information for each user is actually checked against the roles in the database, not the role in the session cookie JSON, so simply setting <code class="language-plaintext highlighter-rouge">"role":"admin"</code> in the cookie without changing our user ID doesn’t help us.)</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">init_db</span><span class="p">():</span>
    <span class="n">db_path</span> <span class="o">=</span> <span class="n">current_app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"DB_PATH"</span><span class="p">]</span>
    <span class="n">db_path</span><span class="p">.</span><span class="n">parent</span><span class="p">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">db</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">()</span>
    <span class="n">db</span><span class="p">.</span><span class="n">executescript</span><span class="p">(</span><span class="n">SCHEMA</span><span class="p">)</span>
    <span class="n">db</span><span class="p">.</span><span class="n">commit</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">current_app</span><span class="p">.</span><span class="n">config</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"BOOTSTRAP_ADMIN_PASSWORD"</span><span class="p">):</span>
        <span class="n">email</span> <span class="o">=</span> <span class="n">current_app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"BOOTSTRAP_ADMIN_EMAIL"</span><span class="p">].</span><span class="n">lower</span><span class="p">()</span>
        <span class="n">db</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span>
            <span class="s">"INSERT OR IGNORE INTO users(email,password_hash,role) VALUES(?,?,?)"</span><span class="p">,</span>
            <span class="p">(</span>
                <span class="n">email</span><span class="p">,</span>
                <span class="n">generate_password_hash</span><span class="p">(</span><span class="n">current_app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"BOOTSTRAP_ADMIN_PASSWORD"</span><span class="p">]),</span>
                <span class="s">"admin"</span><span class="p">,</span>
            <span class="p">),</span>
        <span class="p">)</span>
        <span class="n">db</span><span class="p">.</span><span class="n">commit</span><span class="p">()</span>
    <span class="n">seed_data</span><span class="p">()</span>
</code></pre></div></div>

<p>We can then create the secret:</p>

<p><img src="/assets/images/brunner/create_secret.png" alt="" /></p>

<p>Exporting the vault, we obtain the ciphertext fof the session cookie.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
	</span><span class="nl">"ciphertext"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fUzmLWhoCazUgGLUkaA0YvBW0G70YCQ0t9ZgiyGUDK-4t_46vbiXzszt6vS0SUnNrKUpAXgXl9M="</span><span class="p">,</span><span class="w">
	</span><span class="nl">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-08-22 17:31:29"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
	</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
	</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"session/v1"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"owner_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w">
	</span><span class="nl">"updated_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-08-22 17:31:29"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Setting the text of the <code class="language-plaintext highlighter-rouge">secret_storage_session</code> cookie to the exported ciphertext, we’re now logged in as admin.</p>

<h2 id="decrypting-the-flag">Decrypting the flag</h2>

<h3 id="import_vault"><code class="language-plaintext highlighter-rouge">import_vault</code></h3>

<p>We can now call <code class="language-plaintext highlighter-rouge">import_vault</code>, but we still need to figure out how to use it to decrypt our flag. <code class="language-plaintext highlighter-rouge">import_vault</code> expects us to pass in the key to the imported vault, which of course we don’t have.</p>

<p>Specifically, <code class="language-plaintext highlighter-rouge">import_vault</code> does the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">nonce</span><span class="p">,</span> <span class="n">encrypted</span> <span class="o">=</span> <span class="n">raw</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">raw</span><span class="p">[</span><span class="mi">12</span><span class="p">:]</span>
<span class="n">value</span> <span class="o">=</span> <span class="p">(</span>
	<span class="n">AESGCM</span><span class="p">(</span><span class="n">source_key</span><span class="p">)</span>
	<span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">nonce</span><span class="p">,</span> <span class="n">encrypted</span><span class="p">,</span> <span class="sa">f</span><span class="s">"secret/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">"</span><span class="p">.</span><span class="n">encode</span><span class="p">())</span>
	<span class="p">.</span><span class="n">decode</span><span class="p">()</span>
<span class="p">)</span>

<span class="n">destination</span> <span class="o">=</span> <span class="n">AESGCM</span><span class="p">(</span><span class="n">current_app</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s">"SECRET_ENCRYPTION_KEY"</span><span class="p">]).</span><span class="n">encrypt</span><span class="p">(</span>
	<span class="n">nonce</span><span class="p">,</span> <span class="n">value</span><span class="p">.</span><span class="n">encode</span><span class="p">(),</span> <span class="sa">f</span><span class="s">"secret/</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">"</span><span class="p">.</span><span class="n">encode</span><span class="p">()</span>
<span class="p">)</span>
</code></pre></div></div>

<ul>
  <li>The entry read from the vault is split into the prepended nonce and the actual ciphertext.</li>
  <li>The ciphertext is decrypted using the nonce and the user-supplied key.</li>
  <li>The ciphertext is re-encrypted using the nonce and the new key.</li>
</ul>

<p>The issue here is that the nonce that is being used when the secret is re-imported into the vault is the same nonce that was used to encrypt the flag originally. (In retrospect, I should have noticed this a <em>lot</em> sooner since this is basically the only way to screw up AES-GCM.) We can use this to encrypt a plaintext of our choice using the same key and nonce as the flag, which gets us the keystream that was used to encrypt the flag originally.</p>

<h3 id="nonce-reuse">Nonce reuse</h3>

<p>AES-GCM is a <a href="https://en.wikipedia.org/wiki/Stream_cipher">stream cipher</a>. It uses the key and nonce to generate a keystream, then XORs the keystream with the plaintext to produce the ciphertext.</p>

<p>The keystream that gets generated for a given key and nonce is always the same, which means that the same key and nonce should never be used to encrypt more than one plaintext. If an attacker knows one plaintext-ciphertext pair, they can use that to decrypt other ciphertexts encrypted with the same plaintext:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Message ^ Keystream = Ciphertext
KnownPlaintext ^ Keystream = EncryptedKnownPlaintext

Ciphertext ^ EncryptedKnownPlaintext = (Message ^ Keystream) ^ (KnownPlaintext ^ Keystream) = Message ^ KnownPlaintext
Message = KnownPlaintext ^ Ciphertext ^ EncryptedKnownPlaintext
</code></pre></div></div>

<p>This is exactly what we can use the <code class="language-plaintext highlighter-rouge">import_vault</code> function to do. We can create our own fake secrets, then add that secret to an export JSON file with the same nonce prepended to it as the flag. Then, we can export the vault with the new secret added, and it will be encrypted with the flag’s keystream.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>EncFlag = Keystream ^ Flag
FakeEncSecret = Keystream ^ FakeSecret

EncFlag ^ FakeEncSecret = (Keystream ^ Flag) ^ (Keystream ^ FakeSecret) = Flag ^ FakeSecret

Flag = EncFlag ^ FakeSecret ^ FakeEncSecret
</code></pre></div></div>

<h3 id="the-solve-script">The solve script</h3>

<p>First we need to create the fake secret to import. (I used a string of <code class="language-plaintext highlighter-rouge">a</code>’s, but any string would work). The fake secret will be decrypted using a key of our choice before it gets re-encrypted by the challenge’s key, so we can use an arbitrary key to encrypt it (I used a key of all 1s). The AAD is also validated by the challenge instance, so we have to make sure to supply the correct AAD string of <code class="language-plaintext highlighter-rouge">secret/Flag</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">from</span> <span class="nn">cryptography.hazmat.primitives.ciphers.aead</span> <span class="kn">import</span> <span class="n">AESGCM</span>

<span class="n">enc_flag</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="s">"w0nG9y0JrT1WmR8Zo_wj0P-e6kMfsytriqclB0PY5cgzp4d00ADBFe1YQyRskKJAOYlBa9nqMZh4xtTX6IGwX6QbXw=="</span><span class="p">)</span>
<span class="n">nonce</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">tag</span> <span class="o">=</span> <span class="n">enc_flag</span><span class="p">[:</span><span class="mi">12</span><span class="p">],</span> <span class="n">enc_flag</span><span class="p">[</span><span class="mi">12</span><span class="p">:</span><span class="o">-</span><span class="mi">16</span><span class="p">],</span> <span class="n">enc_flag</span><span class="p">[</span><span class="o">-</span><span class="mi">16</span><span class="p">:]</span>

<span class="n">aad</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'secret/Flag'</span>

<span class="n">fake_secret</span> <span class="o">=</span> <span class="n">AESGCM</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x01</span><span class="s">'</span><span class="o">*</span><span class="mi">32</span><span class="p">).</span><span class="n">encrypt</span><span class="p">(</span><span class="n">nonce</span><span class="p">,</span> <span class="sa">b</span><span class="s">'a'</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">),</span> <span class="n">aad</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64encode</span><span class="p">(</span><span class="n">nonce</span> <span class="o">+</span> <span class="n">fake_secret</span><span class="p">))</span>
</code></pre></div></div>

<p>This prints out the string <code class="language-plaintext highlighter-rouge">w0nG9y0JrT1WmR8ZiaMR5F_0H2HZbSUoY3j0c_xr4B6ZzE1EJa27eLFPCKlH0UE9IJleqprAUsLh-4TK2EmBVaGiCA==</code>, which we can add to the vault export file which we’re going to supply to the challenge.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"format"</span><span class="p">:</span><span class="w"> </span><span class="s2">"vault-export-v1"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"secrets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"ciphertext"</span><span class="p">:</span><span class="w"> </span><span class="s2">"w0nG9y0JrT1WmR8ZiaMR5F_0H2HZbSUoY3j0c_xr4B6ZzE1EJa27eLFPCKlH0UE9IJleqprAUsLh-4TK2EmBVaGiCA=="</span><span class="p">,</span><span class="w">
            </span><span class="nl">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-07-30 08:14:02"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The CTF flag for the challenge"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Flag"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"owner_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
            </span><span class="nl">"updated_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-07-30 08:14:02"</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Then, we can import and re-export the vault, which gets us the fake secret encrypted with the flag’s keystream.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
	</span><span class="nl">"ciphertext"</span><span class="p">:</span><span class="w"> </span><span class="s2">"w0nG9y0JrT1WmR8ZoO833_Ca-Vkcpz5Vn653OWOI29pm94JKgBX_A7gMfSM8n_AeZ9dd5cwdKampUdk8_mGLorTifA=="</span><span class="p">,</span><span class="w">
	</span><span class="nl">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-08-22 19:42:46"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The CTF flag for the challenge"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w">
	</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Flag"</span><span class="p">,</span><span class="w">
	</span><span class="nl">"owner_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
	</span><span class="nl">"updated_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-08-22 19:42:46"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Now we just have to XOR the original exported flag with the recovered keystream and the string of <code class="language-plaintext highlighter-rouge">a</code>’s that make up the fake secret.</p>

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

<span class="n">orig</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="s">'w0nG9y0JrT1WmR8Zo_wj0P-e6kMfsytriqclB0PY5cgzp4d00ADBFe1YQyRskKJAOYlBa9nqMZh4xtTX6IGwX6QbXw=='</span><span class="p">)[</span><span class="mi">12</span><span class="p">:</span><span class="o">-</span><span class="mi">16</span><span class="p">]</span>
<span class="n">returned</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="s">'w0nG9y0JrT1WmR8ZoO833_Ca-Vkcpz5Vn653OWOI29pm94JKgBX_A7gMfSM8n_AeZ9dd5cwdKampUdk8_mGLorTifA=='</span><span class="p">)[</span><span class="mi">12</span><span class="p">:</span><span class="o">-</span><span class="mi">16</span><span class="p">]</span>

<span class="k">print</span><span class="p">(</span><span class="n">xor</span><span class="p">(</span><span class="n">xor</span><span class="p">(</span><span class="n">orig</span><span class="p">,</span> <span class="n">returned</span><span class="p">),</span> <span class="sa">b</span><span class="s">'a'</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">orig</span><span class="p">)))</span>
</code></pre></div></div>

<p>This gets us the flag: <code class="language-plaintext highlighter-rouge">brunner{but_th3_A1_s41d_1t_w45_f1n3???}</code></p>

<p>Overall, I thought this was a pretty good challenge. Most CTF crypto challenges tend to be pretty academic, but the vulnerabilities in this challenge are plausible implementation errors that I could easily imagine showing up in a real application.</p>]]></content><author><name></name></author><category term="ctf" /><summary type="html"><![CDATA[Exploiting AES-GCM implementation errors]]></summary></entry><entry><title type="html">Source code strings in the AUR malware</title><link href="/malware/2026/06/15/aur-debug-info.html" rel="alternate" type="text/html" title="Source code strings in the AUR malware" /><published>2026-06-15T22:30:00+00:00</published><updated>2026-06-15T22:30:00+00:00</updated><id>/malware/2026/06/15/aur-debug-info</id><content type="html" xml:base="/malware/2026/06/15/aur-debug-info.html"><![CDATA[<p>A few days ago, hundreds of AUR packages were <a href="https://lists.archlinux.org/archives/list/aur-general@lists.archlinux.org/thread/FGXPCB3ZVCJIV7FX323SBAX2JHYB7ZS4/">compromised</a>, with a malicious npm package called <code class="language-plaintext highlighter-rouge">atomic-lockfile</code> being added to the dependencies. As reported by <a href="https://ioctl.fail/preliminary-analysis-of-aur-malware/">ioctl.fail</a>, the package runs a malicious ELF executable called <code class="language-plaintext highlighter-rouge">deps</code>.</p>

<p>The focus of this blog post is an embedded eBPF ELF binary that <code class="language-plaintext highlighter-rouge">deps</code> loads using a call to <code class="language-plaintext highlighter-rouge">bpf_object__open_mem</code>. When I looked at this binary, I immediately noticed that the strings contained a great deal of debug information. Specifically, I saw what looked like full lines of source code, complete with comments:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>20 20 20 20 20 20 20 20 20 20 20 63 2d 3e 70 72  |           c-&gt;pr|
65 76 5f 6f 66 66 20 20 3d 20 63 2d 3e 6f 66 66  |ev_off  = c-&gt;off|
3b 00 20 20 20 20 20 20 20 20 20 20 20 20 20 20  |;.              |
20 20 63 2d 3e 70 72 65 76 5f 72 6c 65 6e 20 3d  |  c-&gt;prev_rlen =|
20 6d 65 72 67 65 64 3b 00 20 20 20 20 20 20 20  | merged;.       |
20 20 20 20 20 20 20 20 20 63 2d 3e 6f 66 66 20  |         c-&gt;off |
20 20 20 20 20 2b 3d 20 6e 65 78 74 5f 72 6c 65  |     += next_rle|
6e 3b 20 2f 2f 20 62 6f 74 74 6f 6d 20 61 64 64  |n; // bottom add|
73 20 72 6c 65 6e 20 e2 86 92 20 74 6f 74 61 6c  |s rlen â.. total|
</code></pre></div></div>

<p>I decided to investigate how these strings might have gotten there and how much of the source code was actually present, which turned out to be a bit of a rabbit hole. This writeup is focused on the debug info itself and doesn’t really go into the malicious functionality of the code.</p>

<h2 id="initial-investigations">Initial investigations</h2>

<p>At first glance, if we print out all the strings from this region of the code in order, it really does look like the malware is embedding its full source code. For instance, this almost looks like a reasonable implementation of a function to convert a string to an integer.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">__attribute__</span><span class="p">((</span><span class="n">noinline</span><span class="p">))</span> <span class="n">u32</span> <span class="n">name_to_pid</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="n">u8</span> <span class="n">c</span> <span class="o">=</span> <span class="p">(</span><span class="n">u8</span><span class="p">)</span><span class="n">name</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">c</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">&lt;</span> <span class="sc">'0'</span> <span class="o">||</span> <span class="n">c</span> <span class="o">&gt;</span> <span class="sc">'9'</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">n</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">+</span> <span class="p">(</span><span class="n">c</span> <span class="o">-</span> <span class="sc">'0'</span><span class="p">);</span>
</code></pre></div></div>

<p>However, what we’re seeing clearly isn’t the exact source code. Some lines are missing, like the initialization of the loop and the opening bracket after the function name.</p>

<p>There are also lines that occasionally appear out of order: the line <code class="language-plaintext highlighter-rouge">(void *)(c-&gt;buf + c-&gt;off + 16)) &lt; 0 || rlen == 0)</code> here should come after <code class="language-plaintext highlighter-rouge">if (bpf_probe_read_user(&amp;rlen, sizeof(rlen),</code>, not before.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">u16</span> <span class="n">rlen</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
                            <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="n">c</span><span class="o">-&gt;</span><span class="n">buf</span> <span class="o">+</span> <span class="n">c</span><span class="o">-&gt;</span><span class="n">off</span> <span class="o">+</span> <span class="mi">16</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">rlen</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="n">bpf_probe_read_user</span><span class="p">(</span><span class="o">&amp;</span><span class="n">rlen</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">rlen</span><span class="p">),</span>
    <span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
</code></pre></div></div>

<p>In order to ensure the lines were correctly ordered and get an idea of how many lines were missing, I would need to figure out how to parse the file format where the debug strings were stored.</p>

<h3 id="dwarf">DWARF</h3>

<p>I initially assumed this was DWARF info and went on a wild goose chase trying to use various DWARF parsing tools to read it. It turns out it isn’t DWARF, but I’ll document what I looked for anyway in the hopes that it’s helpful to somebody.</p>

<p>The thing that threw me off initially is that DWARF <em>does</em> allow source embedding. In DWARF 5, the <code class="language-plaintext highlighter-rouge">DW_LNCT_source</code> field was introduced, which stores the full source code of the binary as a string. Clang <a href="https://reviews.llvm.org/D42765#change-Lg43B1hAXkki">supports it</a> with the <code class="language-plaintext highlighter-rouge">-gembed-source</code> flag.</p>

<p>When I compiled a test binary with <code class="language-plaintext highlighter-rouge">-gembed-source</code>, though, the format didn’t match. The AUR malware stores each line as its own null-terminated string, but the DWARF info stored the source code as a single string with newlines included. Additionally, while <code class="language-plaintext highlighter-rouge">llvm-dwarfdump</code> didn’t show any source information for the malware, it did for the test binary:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file_names[  0]:  
          name:  .debug_line_str[0x0000002a] = "embedded_source.c"  
     dir_index: 0  
  md5_checksum: 22f3183556b123a4d647cadec6ef7675  
        source:  .debug_line_str[0x0000003c] = "#include &lt;stdio.h&gt;\n\nint main(int argc, char** argv) {\n    pri  
ntf(\"test embedded source\\n\");\n    return 0;\n}\n"
</code></pre></div></div>

<p>Evidently, the malware strings had nothing to do with the DWARF feature. (In retrospect, it’s unlikely a malware developer would ever go out of their way to compile with <code class="language-plaintext highlighter-rouge">-gembed-source</code>, a compiler flag most people have never heard of.)</p>

<h3 id="btf">BTF</h3>

<p>After googling a bunch of variations of “ebpf debug info”, I eventually caught on that BPF has its own debug info format called <a href="https://docs.ebpf.io/concepts/btf/">BTF</a>. I compiled a debug build of a test BPF binary (<code class="language-plaintext highlighter-rouge">clang -target bpf -g -c test.c -o test.o</code>), and sure enough, source code strings appeared in the binary.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>000001d0: 7374 2e63 0069 6e74 2066 756e 6328 2920  st.c.int func()    
000001e0: 7b00 2020 2020 7265 7475 726e 2030 3b00  {.    return 0;.
</code></pre></div></div>

<p>Unfortunately, I wasn’t able to find a tool to display these strings. <code class="language-plaintext highlighter-rouge">bpftool btf dump</code> prints out a lot of type info, but it ignores the source code lines entirely.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[1] PTR '(anon)' type_id=3
[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[3] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=1
[4] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)

// [...]

[98] DATASEC 'license' size=0 vlen=1
	type_id=96 offset=0 size=4 (VAR 'LICENSE')
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">llvm-objdump</code> understands the BTF format in theory, but <code class="language-plaintext highlighter-rouge">llvm-objdump -S</code> ignores the embedded source strings. Instead, it searches for the source file on disk and complains about not being able to find it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0000000000000000 &lt;walk_dirent&gt;:  
llvm-objdump: warning: 'bpf.elf': failed to find source /cloud/scales/agent/../ebpf/scales.bpf.c
</code></pre></div></div>

<h2 id="the-btf-format">The BTF format</h2>

<p>Eventually I gave up on finding a tool and decided to analyze the file format to see if I could write a parser script myself. Luckily, the Linux kernel’s <a href="https://docs.kernel.org/bpf/btf.html">documentation</a> of the format is pretty good. (All of the struct definitions referenced here are taken from this documentation.)</p>
<h3 id="btf-and-btfext"><code class="language-plaintext highlighter-rouge">.BTF</code> and <code class="language-plaintext highlighter-rouge">.BTF.ext</code></h3>

<p>The BTF information is split up into two sections of the ELF: <code class="language-plaintext highlighter-rouge">.BTF</code> and <code class="language-plaintext highlighter-rouge">.BTF.ext</code>. The table of source code strings is stored in <code class="language-plaintext highlighter-rouge">.BTF</code>, and the corresponding line numbers are stored in <code class="language-plaintext highlighter-rouge">.BTF.ext</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">.BTF</code> section starts with the following header:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">btf_header</span> <span class="p">{</span>
    <span class="n">__u16</span>   <span class="n">magic</span><span class="p">;</span>
    <span class="n">__u8</span>    <span class="n">version</span><span class="p">;</span>
    <span class="n">__u8</span>    <span class="n">flags</span><span class="p">;</span>
    <span class="n">__u32</span>   <span class="n">hdr_len</span><span class="p">;</span>

    <span class="cm">/* All offsets are in bytes relative to the end of this header */</span>
    <span class="n">__u32</span>   <span class="n">type_off</span><span class="p">;</span>       <span class="cm">/* offset of type section       */</span>
    <span class="n">__u32</span>   <span class="n">type_len</span><span class="p">;</span>       <span class="cm">/* length of type section       */</span>
    <span class="n">__u32</span>   <span class="n">str_off</span><span class="p">;</span>        <span class="cm">/* offset of string section     */</span>
    <span class="n">__u32</span>   <span class="n">str_len</span><span class="p">;</span>        <span class="cm">/* length of string section     */</span>
<span class="p">};</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">str_off</code> and <code class="language-plaintext highlighter-rouge">str_len</code> tell us the offset and length of the string table, which is just an array of null-terminated strings. This table stores the source code lines as well as any other strings that are relevant to the debug info, such as filenames, function names, and type names.</p>

<p>The<code class="language-plaintext highlighter-rouge">.BTF.ext</code> function isn’t nearly as straightforward. It starts with this header:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">btf_ext_header</span> <span class="p">{</span>
    <span class="n">__u16</span>   <span class="n">magic</span><span class="p">;</span>
    <span class="n">__u8</span>    <span class="n">version</span><span class="p">;</span>
    <span class="n">__u8</span>    <span class="n">flags</span><span class="p">;</span>
    <span class="n">__u32</span>   <span class="n">hdr_len</span><span class="p">;</span>

    <span class="cm">/* All offsets are in bytes relative to the end of this header */</span>
    <span class="n">__u32</span>   <span class="n">func_info_off</span><span class="p">;</span>
    <span class="n">__u32</span>   <span class="n">func_info_len</span><span class="p">;</span>
    <span class="n">__u32</span>   <span class="n">line_info_off</span><span class="p">;</span>
    <span class="n">__u32</span>   <span class="n">line_info_len</span><span class="p">;</span>

    <span class="cm">/* optional part of .BTF.ext header */</span>
    <span class="n">__u32</span>   <span class="n">core_relo_off</span><span class="p">;</span>
    <span class="n">__u32</span>   <span class="n">core_relo_len</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The relevant fields here are <code class="language-plaintext highlighter-rouge">line_info_off</code> and <code class="language-plaintext highlighter-rouge">line_info_len</code>, which point to the part of the debug info that stores the source line numbers.</p>

<p>The Linux kernel docs give us the following definition for <code class="language-plaintext highlighter-rouge">line_info</code>:</p>

<blockquote>
  <p>The line_info is organized as below.:</p>

  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>line_info_rec_size              /* __u32 value */
btf_ext_info_sec for section #1 /* line_info for section #1 */
btf_ext_info_sec for section #2 /* line_info for section #2 */
...
</code></pre></div>  </div>
</blockquote>

<p>Unfortunately, the <code class="language-plaintext highlighter-rouge">btf_ext_info_sec</code> struct is more mysterious, and it’s not obvious what it has to do with line numbers.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">btf_ext_info_sec</span> <span class="p">{</span>
   <span class="n">__u32</span>   <span class="n">sec_name_off</span><span class="p">;</span> <span class="cm">/* offset to section name */</span>
   <span class="n">__u32</span>   <span class="n">num_info</span><span class="p">;</span>
   <span class="cm">/* Followed by num_info * record_size number of bytes */</span>
   <span class="n">__u8</span>    <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The example disassembly makes this a little clearer. We can see that the information for each line number is stored as a set of four values, and that an array of these structures appear after the metadata at the start of the <code class="language-plaintext highlighter-rouge">line_info</code> section.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.long   16                      # LineInfo
.long   1                       # LineInfo section string offset=1
.long   2
.long   .Ltmp0
.long   7
.long   33
.long   7182                    # Line 7 Col 14
.long   .Ltmp3
.long   7
.long   58
.long   8206                    # Line 8 Col 14
</code></pre></div></div>

<p>As it turns out, the structures are <code class="language-plaintext highlighter-rouge">bpf_line_info</code> structures, which are defined in the following way:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">bpf_line_info</span> <span class="p">{</span>
    <span class="n">__u32</span>   <span class="n">insn_off</span><span class="p">;</span> <span class="cm">/* [0, insn_cnt - 1] */</span>
    <span class="n">__u32</span>   <span class="n">file_name_off</span><span class="p">;</span> <span class="cm">/* offset to string table for the filename */</span>
    <span class="n">__u32</span>   <span class="n">line_off</span><span class="p">;</span> <span class="cm">/* offset to string table for the source line */</span>
    <span class="n">__u32</span>   <span class="n">line_col</span><span class="p">;</span> <span class="cm">/* line number and column number */</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The only non-obvious field here is <code class="language-plaintext highlighter-rouge">line_col</code>, which stores the column in the low 10 bits and the line number in the high 12 bits.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define BPF_LINE_INFO_LINE_NUM(line_col)        ((line_col) &gt;&gt; 10)
#define BPF_LINE_INFO_LINE_COL(line_col)        ((line_col) &amp; 0x3ff)
</span></code></pre></div></div>

<p>I think what’s going on here is that the <code class="language-plaintext highlighter-rouge">btf_ext_info_sec</code> structure can store arbitrary structures in the <code class="language-plaintext highlighter-rouge">data</code> field, and in this case it happens to store <code class="language-plaintext highlighter-rouge">bpf_line_info</code> structures. In this case, <code class="language-plaintext highlighter-rouge">line_info_rec_size</code> is <code class="language-plaintext highlighter-rouge">sizeof(bpf_line_info) = 16</code>, so a <code class="language-plaintext highlighter-rouge">btf_ext_info_sec</code> struct stores <code class="language-plaintext highlighter-rouge">16 * num_info</code> bytes in its <code class="language-plaintext highlighter-rouge">data</code> field.</p>

<p>This is the example <code class="language-plaintext highlighter-rouge">line_info</code> with every value annotated according to its field:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> .long   16                      # line_info_rec_size
 .long   1                       # btf_ext_info_sec.sec_name_off
 .long   2                       # btf_ext_info_sec.num_info
 .long   .Ltmp0                  # 1st line info - bpf_line_info.insn_off
 .long   7                       # 1st line info - bpf_line_info.file_name_off
 .long   33                      # 1st line info - bpf_line_info.line_off
 .long   7182                    # 1st line info - bpf_line_info.line_col
 .long   .Ltmp3                  # 2nd line info - bpf_line_info.insn_off
 .long   7                       # 2nd line info - bpf_line_info.file_name_off
 .long   58                      # 2nd line info - bpf_line_info.line_off
 .long   8206                    # 2nd line info - bpf_line_info.line_col
</code></pre></div></div>

<p>In this example there’s only one <code class="language-plaintext highlighter-rouge">btf_ext_info_sec</code> structure, but the malware had one for each function. I guess that’s what a “section” means in this context.</p>

<h3 id="parser-script">Parser script</h3>

<p>Ultimately, to figure out which source lines go where, the only things we need to extract are the line number and string table offset. This script reads the <code class="language-plaintext highlighter-rouge">line_off</code> value from each of the <code class="language-plaintext highlighter-rouge">bpf_line_info</code> structures and retrieves the null-terminated string at that offset in the string table. It then matches that to the line number extracted from the <code class="language-plaintext highlighter-rouge">line_col</code> value of the <code class="language-plaintext highlighter-rouge">bpf_line_info</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">ctypes</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">lief</span>
<span class="kn">import</span> <span class="nn">io</span>

<span class="k">class</span> <span class="nc">btf_header</span><span class="p">(</span><span class="n">Structure</span><span class="p">):</span>
    <span class="n">_fields_</span> <span class="o">=</span> <span class="p">[</span>
        <span class="p">(</span><span class="s">'magic'</span><span class="p">,</span> <span class="n">c_uint16</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'version'</span><span class="p">,</span> <span class="n">c_uint8</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'flags'</span><span class="p">,</span> <span class="n">c_uint8</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'hdr_len'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'type_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'type_len'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'str_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'str_len'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">)</span>
    <span class="p">]</span>

<span class="k">class</span> <span class="nc">btf_ext_header</span><span class="p">(</span><span class="n">Structure</span><span class="p">):</span>
    <span class="n">_fields_</span> <span class="o">=</span> <span class="p">[</span>
        <span class="p">(</span><span class="s">'magic'</span><span class="p">,</span> <span class="n">c_uint16</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'version'</span><span class="p">,</span> <span class="n">c_uint8</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'flags'</span><span class="p">,</span> <span class="n">c_uint8</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'func_info_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'func_info_len'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'line_info_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'line_info_len'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'core_relo_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'core_relo_len'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">)</span>
    <span class="p">]</span>

<span class="k">class</span> <span class="nc">bpf_line_info</span><span class="p">(</span><span class="n">Structure</span><span class="p">):</span>
    <span class="n">_fields_</span> <span class="o">=</span> <span class="p">[</span>
        <span class="p">(</span><span class="s">'insn_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'file_name_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'line_off'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">),</span>
        <span class="p">(</span><span class="s">'line_col'</span><span class="p">,</span> <span class="n">c_uint32</span><span class="p">)</span>
    <span class="p">]</span>

<span class="n">elf</span><span class="p">:</span> <span class="n">lief</span><span class="p">.</span><span class="n">ELF</span><span class="p">.</span><span class="n">Binary</span> <span class="o">=</span> <span class="n">lief</span><span class="p">.</span><span class="n">ELF</span><span class="p">.</span><span class="n">parse</span><span class="p">(</span><span class="s">'bpf.elf'</span><span class="p">)</span>

<span class="k">for</span> <span class="n">section</span> <span class="ow">in</span> <span class="n">elf</span><span class="p">.</span><span class="n">sections</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">section</span><span class="p">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">'.BTF'</span><span class="p">:</span>
        <span class="n">btf</span> <span class="o">=</span> <span class="n">section</span><span class="p">.</span><span class="n">content</span><span class="p">.</span><span class="n">tobytes</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">section</span><span class="p">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">'.BTF.ext'</span><span class="p">:</span>
        <span class="n">btf_ext</span> <span class="o">=</span> <span class="n">section</span><span class="p">.</span><span class="n">content</span><span class="p">.</span><span class="n">tobytes</span><span class="p">()</span>

<span class="n">header</span> <span class="o">=</span> <span class="n">btf_header</span><span class="p">.</span><span class="n">from_buffer_copy</span><span class="p">(</span><span class="n">btf</span><span class="p">)</span>

<span class="n">type_offset</span> <span class="o">=</span> <span class="n">header</span><span class="p">.</span><span class="n">type_off</span> <span class="o">+</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">btf_header</span><span class="p">)</span>
<span class="n">str_offset</span> <span class="o">=</span> <span class="n">header</span><span class="p">.</span><span class="n">str_off</span> <span class="o">+</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">btf_header</span><span class="p">)</span>

<span class="n">string_table</span> <span class="o">=</span> <span class="n">btf</span><span class="p">[</span><span class="n">str_offset</span><span class="p">:</span><span class="n">str_offset</span> <span class="o">+</span> <span class="n">header</span><span class="p">.</span><span class="n">str_len</span><span class="p">]</span>

<span class="n">ext_header</span> <span class="o">=</span> <span class="n">btf_ext_header</span><span class="p">.</span><span class="n">from_buffer_copy</span><span class="p">(</span><span class="n">btf_ext</span><span class="p">)</span>
<span class="n">line_info_offset</span> <span class="o">=</span> <span class="n">ext_header</span><span class="p">.</span><span class="n">line_info_off</span> <span class="o">+</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">btf_ext_header</span><span class="p">)</span>

<span class="c1"># this should really go from line_info_offset - line_info_offset + ext_header.line_info_len, but the value of line_info_len is wrong for some reason
</span><span class="n">line_stream</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">btf_ext</span><span class="p">[</span><span class="n">line_info_offset</span><span class="p">:])</span>

<span class="n">unknown</span> <span class="o">=</span> <span class="n">line_stream</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>

<span class="n">lines</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">max_line</span> <span class="o">=</span> <span class="mi">0</span>

<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">unknown2</span> <span class="o">=</span> <span class="n">line_stream</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
    <span class="n">num_infos</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">line_stream</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span> <span class="s">'little'</span><span class="p">)</span>
    
    <span class="c1"># this is a hack to stop reading once we hit the end of line_info, since line_info_len is wrong
</span>    <span class="k">if</span> <span class="n">num_infos</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">break</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="n">num_infos</span><span class="p">):</span>
        <span class="n">buf</span> <span class="o">=</span> <span class="n">line_stream</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">sizeof</span><span class="p">(</span><span class="n">bpf_line_info</span><span class="p">))</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">bpf_line_info</span><span class="p">):</span>
            <span class="k">break</span>

        <span class="n">info</span> <span class="o">=</span> <span class="n">bpf_line_info</span><span class="p">.</span><span class="n">from_buffer_copy</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
        <span class="n">line_no</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">line_col</span> <span class="o">&gt;&gt;</span> <span class="mi">10</span>
        <span class="k">if</span> <span class="n">line_no</span> <span class="o">&gt;</span> <span class="n">max_line</span><span class="p">:</span>
            <span class="n">max_line</span> <span class="o">=</span> <span class="n">line_no</span>

        <span class="c1"># read a null terminated string from the string table
</span>        <span class="n">line_str</span> <span class="o">=</span> <span class="n">string_table</span><span class="p">[</span><span class="n">info</span><span class="p">.</span><span class="n">line_off</span><span class="p">:</span><span class="n">string_table</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">line_off</span><span class="p">)]</span>
        <span class="n">lines</span><span class="p">[</span><span class="n">line_no</span><span class="p">]</span> <span class="o">=</span> <span class="n">line_str</span>
        <span class="k">print</span><span class="p">(</span><span class="n">line_str</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="n">max_line</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="n">lines</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</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">'/* missing line */'</span><span class="p">)</span>
</code></pre></div></div>

<p>The full output of the script can be found <a href="/assets/aur_debug_lines.txt">here</a>.</p>

<h2 id="the-reassembled-source-lines">The reassembled source lines</h2>

<p>Unfortunately, the debug info doesn’t contain as much of the source code as I would’ve hoped. In a couple of places, there are gaps consisting of more than a hundred lines, suggesting that entire functions may be missing debug info. However, it may still be useful for analysis, especially when compared against the disassembly or decompilation of the eBPF bytecode. Some functions are nearly complete, except for the lines that contain things like whitespace or single curly braces.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">net_exit_openat</span><span class="p">(</span><span class="k">struct</span> <span class="n">sys_exit_ctx</span> <span class="o">*</span><span class="n">ctx</span><span class="p">)</span>
<span class="cm">/* missing line */</span>
    <span class="n">u64</span> <span class="n">id</span> <span class="o">=</span> <span class="n">bpf_get_current_pid_tgid</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">bpf_map_lookup_elem</span><span class="p">(</span><span class="o">&amp;</span><span class="n">net_open_temp</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">id</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">bpf_map_delete_elem</span><span class="p">(</span><span class="o">&amp;</span><span class="n">net_open_temp</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">id</span><span class="p">);</span>
    <span class="kt">long</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">ret</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">u32</span> <span class="n">tgid</span> <span class="o">=</span> <span class="p">(</span><span class="n">u32</span><span class="p">)(</span><span class="n">id</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">);</span>
    <span class="n">u64</span> <span class="n">key</span>  <span class="o">=</span> <span class="p">((</span><span class="n">u64</span><span class="p">)</span><span class="n">tgid</span> <span class="o">&lt;&lt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">u32</span><span class="p">)</span><span class="n">fd</span><span class="p">;</span>
    <span class="n">u8</span> <span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">bpf_map_update_elem</span><span class="p">(</span><span class="o">&amp;</span><span class="n">net_fds</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">key</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="n">BPF_ANY</span><span class="p">);</span>
<span class="cm">/* missing line */</span>
<span class="err">}</span>
</code></pre></div></div>

<p>I plan on doing some follow-up posts describing what the eBPF binary actually does, along with some analysis of the main malware executable.</p>]]></content><author><name></name></author><category term="malware" /><summary type="html"><![CDATA[Analyzing debug info in eBPF binaries]]></summary></entry><entry><title type="html">crackmes.one CTF 2026 - crackme9</title><link href="/ctf/2026/02/25/crackme9.html" rel="alternate" type="text/html" title="crackmes.one CTF 2026 - crackme9" /><published>2026-02-25T12:44:00+00:00</published><updated>2026-02-25T12:44:00+00:00</updated><id>/ctf/2026/02/25/crackme9</id><content type="html" xml:base="/ctf/2026/02/25/crackme9.html"><![CDATA[<p>This year I participated in the crackmes.one CTF, a reverse engineering similar to Flare-On. <a href="https://github.com/crackmesone/ctf-2026-challenges-public/tree/main/Fatmike/Handout">Crackme9</a> was one of the harder challenges, with 43 solves by the end of the CTF.</p>

<p><img src="/assets/images/crackmesone/crackme9.png" alt="" /></p>

<p>As usual, we’re given a prompt to enter a serial number. Tracing through the function that checks the serial, we can see a call to a function at the address <code class="language-plaintext highlighter-rouge">0x40a000</code>. This address is not part of the <code class="language-plaintext highlighter-rouge">.text</code> section of the PE file; instead, it occurs at the start of a nonstandard section called <code class="language-plaintext highlighter-rouge">.pc</code>. Looking at a hex dump of the <code class="language-plaintext highlighter-rouge">.pc</code> section, it was obviously encrypted.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>99 ae 95 d7 80 31 f7 5c 3b c9 38 6e 9a 11 56 06  .....1.\;.8n..V.
16 81 d5 0c eb 59 b4 fa fe da 10 89 de 78 32 6b  .....Y.......x2k
87 bb fb e6 09 19 77 a9 84 33 4a dc 91 7b 8b 5a  ......w..3J..{.Z
6d 5c 4a 94 ed ee cd 5e d8 5e f5 c2 8b bc 35 15  m\J....^.^....5.
39 7c 1b 08 bd 76 29 5d 66 13 0c 14 c1 f3 93 c6  9|...v)]f.......
98 71 b1 26 83 bf c7 20 b9 a6 8b 91 cc 05 7f 20  .q.&amp;... ....... 
b7 74 0c 6d ea 66 b3 34 aa 03 18 25 23 4f 45 26  .t.m.f.4...%#OE&amp;
84 4a 2e 47 db 99 9d 97 6c 37 3a d3 84 ac 17 67  .J.G....l7:....g
</code></pre></div></div>
<h2 id="initial-deobfuscation-steps">Initial deobfuscation steps</h2>

<h3 id="api-resolution">API resolution</h3>

<p>I started off by setting breakpoints on commonly used APIs like <code class="language-plaintext highlighter-rouge">VirtualAlloc</code> and <code class="language-plaintext highlighter-rouge">VirtualProtect</code>. Tracing through the calls, I found that the APIs were being resolved through some kind of indirect lookup function.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0040112</span><span class="n">c</span>    <span class="kt">int32_t</span> <span class="n">do_VirtualProtect</span><span class="p">()</span>

<span class="mo">0040112</span><span class="n">f</span>        <span class="n">sub_401367</span><span class="p">()</span>
<span class="mo">0040113</span><span class="n">c</span>        <span class="n">jump</span><span class="p">(</span><span class="n">sub_401ad3</span><span class="p">(</span><span class="o">&amp;</span><span class="n">data_40d1a8</span><span class="p">))</span>
</code></pre></div></div>

<p>As usual for this type of challenge, the APIs were being called based on their hash, in this case <code class="language-plaintext highlighter-rouge">0x10066f2f</code> for <code class="language-plaintext highlighter-rouge">VirtualProtect</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00401</span><span class="n">ad3</span>    <span class="kt">int32_t</span> <span class="kr">__fastcall</span> <span class="n">sub_401ad3</span><span class="p">(</span><span class="kt">int32_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>

<span class="mo">00401</span><span class="n">ae6</span>        <span class="k">if</span> <span class="p">(</span><span class="n">arg1</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">aeb</span>            <span class="k">return</span> <span class="n">arg1</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span>
<span class="mo">00401</span><span class="n">aeb</span>        
<span class="mo">00401</span><span class="n">b20</span>        <span class="kt">void</span><span class="o">*</span> <span class="n">var_14</span>
<span class="mo">00401</span><span class="n">b20</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="n">sub_401175</span><span class="p">(</span><span class="n">sub_401ca2</span><span class="p">(</span><span class="o">*</span><span class="n">arg1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">var_14</span><span class="p">,</span> <span class="mh">0x10066f2f</span><span class="p">))</span>
<span class="mo">00401</span><span class="n">b26</span>        <span class="k">return</span> <span class="n">arg1</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span>
</code></pre></div></div>

<p>Looking at the hash function at <code class="language-plaintext highlighter-rouge">sub_401ba9</code>, it turned out to just be unmodified CRC32:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00401</span><span class="n">ba9</span>    <span class="kt">uint32_t</span> <span class="kr">__stdcall</span> <span class="n">sub_401ba9</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">arg1</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">arg2</span><span class="p">)</span>

<span class="mo">00401</span><span class="n">bad</span>        <span class="kt">int32_t</span> <span class="n">esi</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00401</span><span class="n">baf</span>        <span class="kt">uint32_t</span> <span class="n">edx</span> <span class="o">=</span> <span class="mh">0xffffffff</span>
<span class="mo">00401</span><span class="n">bb3</span>        <span class="kt">char</span><span class="o">*</span> <span class="n">edi</span> <span class="o">=</span> <span class="n">arg1</span>
<span class="mo">00401</span><span class="n">bb3</span>        
<span class="mo">00401</span><span class="n">bb9</span>        <span class="k">if</span> <span class="p">(</span><span class="n">arg2</span> <span class="n">u</span><span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">bbb</span>            <span class="kt">int32_t</span> <span class="n">ebx</span>
<span class="mo">00401</span><span class="n">bbb</span>            <span class="kt">int32_t</span> <span class="n">var_10_1</span> <span class="o">=</span> <span class="n">ebx</span>
<span class="mo">00401</span><span class="n">bbb</span>            
<span class="mo">00401</span><span class="n">be6</span>            <span class="k">do</span>
<span class="mo">00401</span><span class="n">bbc</span>                <span class="n">ebx</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">edi</span><span class="p">[</span><span class="n">esi</span><span class="p">]</span>
<span class="mo">00401</span><span class="n">bc1</span>                <span class="kt">int32_t</span> <span class="n">i_1</span> <span class="o">=</span> <span class="mi">8</span>
<span class="mo">00401</span><span class="n">bdd</span>                <span class="kt">int32_t</span> <span class="n">i</span>
<span class="mo">00401</span><span class="n">bdd</span>                
<span class="mo">00401</span><span class="n">bdd</span>                <span class="k">do</span>
<span class="mo">00401</span><span class="n">bc7</span>                    <span class="kt">uint32_t</span> <span class="n">ecx_2</span> <span class="o">=</span> <span class="n">edx</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mi">1</span>
<span class="mo">00401</span><span class="n">bc9</span>                    <span class="kt">char</span> <span class="n">eax_2</span> <span class="o">=</span> <span class="n">ebx</span><span class="p">.</span><span class="n">b</span> <span class="o">^</span> <span class="n">edx</span><span class="p">.</span><span class="n">b</span>
<span class="mo">00401</span><span class="n">bcd</span>                    <span class="n">edx</span> <span class="o">=</span> <span class="n">ecx_2</span> <span class="o">^</span> <span class="mh">0xedb88320</span>
<span class="mo">00401</span><span class="n">bcd</span>                    
<span class="mo">00401</span><span class="n">bd5</span>                    <span class="k">if</span> <span class="p">((</span><span class="n">eax_2</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">bd5</span>                        <span class="n">edx</span> <span class="o">=</span> <span class="n">ecx_2</span>
<span class="mo">00401</span><span class="n">bd5</span>                    
<span class="mo">00401</span><span class="n">bd8</span>                    <span class="n">ebx</span><span class="p">.</span><span class="n">b</span> <span class="n">u</span><span class="o">&gt;&gt;=</span> <span class="mi">1</span>
<span class="mo">00401</span><span class="n">bda</span>                    <span class="n">i</span> <span class="o">=</span> <span class="n">i_1</span>
<span class="mo">00401</span><span class="n">bda</span>                    <span class="n">i_1</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="mo">00401</span><span class="n">bdd</span>                <span class="k">while</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="mo">00401</span><span class="n">bdf</span>                <span class="n">edi</span> <span class="o">=</span> <span class="n">arg1</span>
<span class="mo">00401</span><span class="n">be2</span>                <span class="n">esi</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="mo">00401</span><span class="n">be6</span>            <span class="k">while</span> <span class="p">(</span><span class="n">esi</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">arg2</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">be6</span>        
<span class="mo">00401</span><span class="n">bf0</span>        <span class="k">return</span> <span class="n">not</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">edx</span><span class="p">)</span>
</code></pre></div></div>

<p>Since CRC32 is such a widely used algorithm, we can use <a href="https://github.com/OALabs/hashdb">HashDB</a> to look up the API names that go with each checksum. In fact, there’s a <a href="https://github.com/cxiao/hashdb_bn">Binary Ninja plugin</a> that pulls data from HashDB to define an enum mapping all the API names to their checksums.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="n">hashdb_strings_crc32</span> <span class="o">:</span> <span class="kt">uint32_t</span>
<span class="p">{</span>
    <span class="n">FlushInstructionCache</span> <span class="o">=</span> <span class="mh">0xe9258e7a</span><span class="p">,</span>
    <span class="n">RtlMoveMemory</span> <span class="o">=</span> <span class="mh">0x1c0cd35c</span><span class="p">,</span>
    <span class="n">GlobalUnWire</span> <span class="o">=</span> <span class="mh">0x2c82f5fb</span><span class="p">,</span>
    <span class="n">MapUserPhysicalPages</span> <span class="o">=</span> <span class="mh">0xafd472a5</span><span class="p">,</span>
    <span class="c1">// [...]</span>
    <span class="n">RtlUniform</span> <span class="o">=</span> <span class="mh">0xd09d867b</span><span class="p">,</span>
    <span class="n">RtlNewSecurityObjectEx</span> <span class="o">=</span> <span class="mh">0x8733c5dd</span><span class="p">,</span>
    <span class="n">WinSqmSetEscalationInfo</span> <span class="o">=</span> <span class="mh">0x648930f1</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Normally I’d write a script to rename all the API hashing functions to their respective API names, but in this case there weren’t that many of them so I just renamed them all by hand.</p>

<h3 id="breakpoints">Breakpoints</h3>

<p>When left to run under a debugger, the binary runs until it hits an int3 breakpoint at <code class="language-plaintext highlighter-rouge">0x4037c6</code>. I’d seen things like this before where exceptions are used to obfuscate control flow, but the binary seemed to be “getting stuck”, never progressing past the breakpoint location.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">004037</span><span class="n">c2</span>    <span class="kt">void</span> <span class="kr">__fastcall</span> <span class="n">do_breakpoint</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>

<span class="mo">004037</span><span class="n">c2</span>  <span class="n">c6410101</span>           <span class="n">mov</span>     <span class="n">byte</span> <span class="p">[</span><span class="n">ecx</span><span class="o">+</span><span class="mh">0x1</span><span class="p">],</span> <span class="mh">0x1</span>
<span class="mo">004037</span><span class="n">c6</span>  <span class="n">cc</span>                 <span class="n">int3</span>    
</code></pre></div></div>

<p>When I was resolving the API hashes, I saw that <code class="language-plaintext highlighter-rouge">KiUserExceptionDispatcher</code> was one of the hashed APIs, so I looked at how that was being used. It looked like the binary was supposed to overwrite the entry point of <code class="language-plaintext highlighter-rouge">KiUserExceptionDispatcher</code> with an exception handler of its own. However, for whatever reason the overwrite function wasn’t being called, so the real <code class="language-plaintext highlighter-rouge">KiUserExceptionDispatcher</code> was being called instead.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00402</span><span class="n">ce0</span>    <span class="kt">void</span> <span class="kr">__fastcall</span> <span class="n">overwrite_KiUserExceptionDispatcher</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">hook</span><span class="p">)</span>

<span class="mo">00402</span><span class="n">ce6</span>        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">hook</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00402</span><span class="n">ce6</span>            <span class="k">return</span> 
<span class="mo">00402</span><span class="n">ce6</span>        
<span class="mo">00402</span><span class="n">cef</span>        <span class="kt">int32_t</span> <span class="n">KiUserExceptionDispatcher</span>
<span class="mo">00402</span><span class="n">cef</span>        <span class="n">int80_t</span> <span class="n">st0_1</span>
<span class="mo">00402</span><span class="n">cef</span>        <span class="n">st0_1</span><span class="p">,</span> <span class="n">KiUserExceptionDispatcher</span> <span class="o">=</span>
<span class="mo">00402</span><span class="n">cef</span>            <span class="n">get_KiUserExceptionDispatcher</span><span class="p">(</span><span class="o">&amp;</span><span class="n">kernel32_addr</span><span class="p">,</span> <span class="n">get_kernel32_baseaddr</span><span class="p">())</span>
<span class="mo">00402</span><span class="n">cfd</span>        <span class="n">_memcpy_s</span><span class="p">(</span><span class="n">KiUserExceptionDispatcher</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">hook</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mi">6</span><span class="p">)</span>
<span class="mo">00402</span><span class="n">d05</span>        <span class="o">*</span><span class="n">hook</span> <span class="o">=</span> <span class="mi">0</span>
</code></pre></div></div>

<p>It turned out the <code class="language-plaintext highlighter-rouge">KiUserExceptionDispatcher</code> overwrite was only called if an anti-debug check was passed. The check used <code class="language-plaintext highlighter-rouge">NtQueryInformationProcess</code>, which I had thought could be bypassed with ScyllaHide, but for whatever reason that didn’t work in this case. Ultimately, I just patched out the check entirely.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00402</span><span class="n">b6d</span>    <span class="kt">int32_t</span> <span class="kr">__fastcall</span> <span class="n">check_ProcessDebugPort</span><span class="p">(</span><span class="kt">int32_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>

<span class="mo">00402</span><span class="n">b77</span>        <span class="k">if</span> <span class="p">(</span><span class="n">sub_402bc7</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00402</span><span class="n">b7c</span>            <span class="k">return</span> <span class="mi">1</span>
<span class="mo">00402</span><span class="n">b7c</span>        
<span class="mo">00402</span><span class="n">b88</span>        <span class="kt">int32_t</span><span class="o">*</span> <span class="n">ProcessInformation</span> <span class="o">=</span> <span class="n">arg1</span>
<span class="mo">00402</span><span class="n">b89</span>        <span class="kt">int32_t</span> <span class="n">esi</span>
<span class="mo">00402</span><span class="n">b89</span>        <span class="kt">int32_t</span> <span class="n">var_c</span> <span class="o">=</span> <span class="n">esi</span>
<span class="mo">00402</span><span class="n">b8a</span>        <span class="n">ProcessInformation</span> <span class="o">=</span> <span class="n">nullptr</span>
<span class="mo">00402</span><span class="n">b91</span>        <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mo">00402</span><span class="n">b98</span>        <span class="n">HANDLE</span> <span class="n">ProcessHandle</span> <span class="o">=</span> <span class="n">do_GetCurrentProcess</span><span class="p">()</span>
<span class="mo">00402</span><span class="n">b9f</span>        <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mo">00402</span><span class="n">bb1</span>        <span class="n">NTSTATUS</span> <span class="n">result</span> <span class="o">=</span> <span class="n">do_NtQueryInformationProcess</span><span class="p">(</span><span class="n">ProcessHandle</span><span class="p">,</span> 
<span class="mo">00402</span><span class="n">bb1</span>            <span class="n">ProcessInformationClass</span><span class="o">:</span> <span class="n">ProcessDebugPort</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ProcessInformation</span><span class="p">,</span> 
<span class="mo">00402</span><span class="n">bb1</span>            <span class="n">ProcessInformationLength</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">ReturnLength</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">)</span>
<span class="mo">00402</span><span class="n">bb1</span>        
<span class="mo">00402</span><span class="n">bb9</span>        <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">!=</span> <span class="n">STATUS_SUCCESS</span><span class="p">)</span>
<span class="mo">00402</span><span class="n">bc3</span>            <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00402</span><span class="n">bc6</span>            <span class="k">return</span> <span class="n">result</span>
<span class="mo">00402</span><span class="n">bc6</span>        
<span class="mo">00402</span><span class="n">bbe</span>        <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">ProcessInformation</span> <span class="o">!=</span> <span class="n">result</span>
<span class="mo">00402</span><span class="n">bc2</span>        <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>At that point, the custom exception handler was being called, but the binary still crashed soon after with mysterious access violation exceptions. It seemed like it was trying to decrypt and run the <code class="language-plaintext highlighter-rouge">.pc</code> section, but the decryption was incorrect.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mf">00402e1</span><span class="n">c</span>    <span class="kt">void</span> <span class="n">decrypt_and_ntcontinue</span><span class="p">(</span><span class="n">PCONTEXT</span> <span class="n">arg1</span><span class="p">)</span> <span class="n">__noreturn</span>

<span class="mf">00402e20</span>        <span class="n">context_record</span> <span class="o">=</span> <span class="n">arg1</span>
<span class="mf">00402e28</span>        <span class="n">exception</span> <span class="o">=</span> <span class="n">__return_addr</span>
<span class="mf">00402e2</span><span class="n">d</span>        <span class="n">get_cipher_ctx</span><span class="p">()</span>
<span class="mf">00402e40</span>        <span class="n">decrypt_from_exception</span><span class="p">(</span><span class="n">ctx</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">cipher_ctx</span><span class="p">,</span> <span class="n">exception</span><span class="o">:</span> <span class="n">exception</span><span class="p">,</span> 
<span class="mf">00402e40</span>            <span class="n">context</span><span class="o">:</span> <span class="n">context_record</span><span class="p">)</span>
<span class="mf">00402e45</span>        <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mf">00402e54</span>        <span class="n">do_NtContinue</span><span class="p">(</span><span class="n">ContextRecord</span><span class="o">:</span> <span class="n">context_record</span><span class="p">,</span> <span class="n">TestAlert</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="mf">00402e59</span>        <span class="n">breakpoint</span>
</code></pre></div></div>
<h2 id="the-pc-section">The .pc section</h2>

<h3 id="decrypting-the-code">Decrypting the code</h3>

<p>At some point in the reversing process I got pretty stuck, and I was just kind of clicking around the decompilation for a while looking for something comprehensible. I eventually found something I recognized at <code class="language-plaintext highlighter-rouge">sub_401e67</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mf">00401e67</span>    <span class="kt">uint32_t</span><span class="o">*</span> <span class="n">__thiscall</span> <span class="n">sub_401e67</span><span class="p">(</span><span class="kt">uint32_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">arg2</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">arg3</span><span class="p">)</span>

<span class="mf">00401e72</span>        <span class="kt">int32_t</span> <span class="n">var_14</span> <span class="o">=</span> <span class="mh">0xb979379e</span>
<span class="mf">00401e7</span><span class="n">c</span>        <span class="kt">int32_t</span> <span class="n">var_10</span> <span class="o">=</span> <span class="mh">0x157c4a7f</span>
<span class="mf">00401e83</span>        <span class="kt">int32_t</span> <span class="n">var_c</span> <span class="o">=</span> <span class="mh">0x60c09cf3</span>
<span class="mf">00401e8</span><span class="n">a</span>        <span class="kt">int32_t</span> <span class="n">var_8</span> <span class="o">=</span> <span class="mh">0x34c8ed5c</span>
<span class="mf">00401e96</span>        <span class="o">*</span><span class="n">arg1</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_14</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">ea1</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_10</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">ead</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_c</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">ebd</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_8</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">ec5</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="n">arg2</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">ed1</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mi">4</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">edd</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mi">8</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">ee9</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mh">0xc</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">ef5</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mh">0x10</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">f01</span>        <span class="n">arg1</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mh">0x14</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">f0d</span>        <span class="n">arg1</span><span class="p">[</span><span class="mh">0xa</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mh">0x18</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">f1d</span>        <span class="n">arg1</span><span class="p">[</span><span class="mh">0xb</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="mh">0x1c</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">f20</span>        <span class="n">arg1</span><span class="p">[</span><span class="mh">0xc</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00401</span><span class="n">f27</span>        <span class="n">arg1</span><span class="p">[</span><span class="mh">0xd</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00401</span><span class="n">f33</span>        <span class="n">arg1</span><span class="p">[</span><span class="mh">0xe</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="n">arg3</span><span class="p">)</span>
<span class="mo">00401</span><span class="n">f42</span>        <span class="n">arg1</span><span class="p">[</span><span class="mh">0xf</span><span class="p">]</span> <span class="o">=</span> <span class="n">bytes_to_int</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arg3</span><span class="p">[</span><span class="mi">4</span><span class="p">])</span>
<span class="mo">00401</span><span class="n">f4a</span>        <span class="k">return</span> <span class="n">arg1</span>
</code></pre></div></div>

<p>This function takes a 32-byte array and an 8-byte array, and uses them alongside a 16-byte constant to initialize an array of 64 bytes. If you’ve reversed enough cryptography code before, you’ll likely recognize this as the initialization of a ChaCha20 matrix. Normally the constant used in ChaCha20 is the string <code class="language-plaintext highlighter-rouge">expand 32-byte k</code>, but I guess the challenge author thought that would be too easy to search for, so here it’s replaced by the custom constant <code class="language-plaintext highlighter-rouge">0xb979379e</code>, <code class="language-plaintext highlighter-rouge">0x157c4a7f</code>, <code class="language-plaintext highlighter-rouge">0x60c09cf3</code>, <code class="language-plaintext highlighter-rouge">0x34c8ed5c</code>.</p>

<p>Looking at where the ChaCha20 matrix is initialized, the nonce is hard-coded as <code class="language-plaintext highlighter-rouge">0a 0b 0c 0d 0e 0f 10 11</code>, but the key is retrieved from a global variable.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0040405</span><span class="mi">9</span>    <span class="kt">char</span><span class="o">*</span> <span class="n">__thiscall</span> <span class="n">do_chacha_with_hprov_key</span><span class="p">(</span><span class="k">struct</span> <span class="n">cipher_ctx</span><span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">ciphertext</span><span class="p">)</span>

<span class="mo">0040406</span><span class="mi">9</span>        <span class="kt">char</span><span class="o">*</span> <span class="n">ciphertext_buf</span> <span class="o">=</span> <span class="n">do_malloc</span><span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">)</span>
<span class="mo">00404077</span>        <span class="n">memcpy</span><span class="p">(</span><span class="n">dest</span><span class="o">:</span> <span class="n">ciphertext_buf</span><span class="p">,</span> <span class="n">src</span><span class="o">:</span> <span class="n">ciphertext</span><span class="p">,</span> <span class="n">count</span><span class="o">:</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">)</span>
<span class="mo">004040</span><span class="mi">87</span>        <span class="kt">int32_t</span> <span class="n">counter_1</span> <span class="o">=</span> <span class="n">ciphertext</span> <span class="o">-</span> <span class="o">*</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">pc_ptr</span>
<span class="mo">004040</span><span class="mi">8</span><span class="n">c</span>        <span class="kt">int64_t</span> <span class="n">nonce</span>
<span class="mo">004040</span><span class="mi">8</span><span class="n">c</span>        <span class="n">nonce</span><span class="p">.</span><span class="n">d</span> <span class="o">=</span> <span class="mh">0xd0c0b0a</span>
<span class="mo">004040</span><span class="mi">93</span>        <span class="n">nonce</span><span class="o">:</span><span class="mi">4</span><span class="p">.</span><span class="n">d</span> <span class="o">=</span> <span class="mh">0x11100f0e</span>
<span class="mo">004040</span><span class="mi">9</span><span class="n">a</span>        <span class="n">get_crypt_context</span><span class="p">()</span>
<span class="mo">004040</span><span class="n">ad</span>        <span class="k">struct</span> <span class="n">matrix</span> <span class="n">matrix</span>
<span class="mo">004040</span><span class="n">ad</span>        <span class="n">init_chacha_matrix_with_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">matrix</span><span class="p">,</span> <span class="n">key</span><span class="o">:</span> <span class="n">get_key_from_hprov</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hprov</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">nonce</span><span class="p">,</span> 
<span class="mo">004040</span><span class="n">ad</span>            <span class="n">counter0</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">counter1</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">004040</span><span class="n">ba</span>        <span class="n">chacha_rotated_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">matrix</span><span class="p">,</span> <span class="n">counter_1</span><span class="p">,</span> <span class="n">counter_2</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">004040</span><span class="n">c8</span>        <span class="n">do_chacha_encrypt</span><span class="p">(</span><span class="o">&amp;</span><span class="n">matrix</span><span class="p">,</span> <span class="n">ciphertext</span><span class="o">:</span> <span class="n">ciphertext_buf</span><span class="p">,</span> <span class="n">len</span><span class="o">:</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mo">004040</span><span class="n">d3</span>        <span class="k">return</span> <span class="n">ciphertext_buf</span>
</code></pre></div></div>

<p>I found that the key was being stored alongside an <code class="language-plaintext highlighter-rouge">HCRYPTPROV</code> handle obtained from <code class="language-plaintext highlighter-rouge">CryptAcquireContextA</code>, and that other Crypto API functions were being called to generate the key itself.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">004033</span><span class="mi">88</span>    <span class="n">BOOL</span> <span class="n">__convention</span><span class="p">(</span><span class="s">"regparm"</span><span class="p">)</span> <span class="n">hash_something_for_chacha_key</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">arg1</span><span class="p">,</span> <span class="kt">int32_t</span><span class="o">*</span> <span class="n">arg2</span> <span class="err">@</span> <span class="n">ebp</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">arg3</span> <span class="err">@</span> <span class="n">esi</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">arg4</span> <span class="err">@</span> <span class="n">edi</span><span class="p">)</span>

<span class="mo">004033</span><span class="mi">88</span>        <span class="o">*</span><span class="mh">0xff45c6</span> <span class="o">=</span> <span class="n">arg1</span>
<span class="mo">004033</span><span class="mi">95</span>        <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">004033</span><span class="n">a5</span>        <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mo">004033</span><span class="n">aa</span>        <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x40d1a0</span>
<span class="mo">004033</span><span class="n">b2</span>        <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span>
<span class="mo">004033</span><span class="n">c5</span>        <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">5</span><span class="p">]</span>
<span class="mo">004033</span><span class="n">c8</span>        <span class="n">BOOL</span> <span class="n">result</span> <span class="o">=</span> <span class="n">do_CryptCreateHash</span><span class="p">(</span><span class="n">hProv</span><span class="o">:</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">4</span><span class="p">],</span> <span class="n">Algid</span><span class="o">:</span> <span class="mh">0x800c</span><span class="p">,</span> <span class="n">hKey</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> 
<span class="mo">004033</span><span class="n">c8</span>            <span class="n">dwFlags</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">phHash</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">])</span>
<span class="mo">004033</span><span class="n">c8</span>        
<span class="mo">004033</span><span class="n">cf</span>        <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">004033</span><span class="n">de</span>            <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mf">004033e3</span>            <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">9</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x40d1a0</span>
<span class="mf">004033e9</span>            <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="mo">004033</span><span class="n">f7</span>            <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">9</span><span class="p">]</span>
<span class="mo">004033</span><span class="n">f7</span>            
<span class="mo">00403401</span>            <span class="k">if</span> <span class="p">(</span><span class="n">do_CryptHashData</span><span class="p">(</span><span class="n">hHash</span><span class="o">:</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">8</span><span class="p">],</span> <span class="n">pbData</span><span class="o">:</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">7</span><span class="p">],</span> <span class="n">dwDataLen</span><span class="o">:</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">6</span><span class="p">],</span> 
<span class="mo">00403401</span>                    <span class="n">dwFlags</span><span class="o">:</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="mo">0040340</span><span class="n">c</span>                <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xa</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x20</span>
<span class="mo">00403420</span>                <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mo">00403425</span>                <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xc</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x40d1a0</span>
<span class="mo">0040342</span><span class="n">b</span>                <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xb</span><span class="p">]</span> <span class="o">=</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="mo">00403440</span>                <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xc</span><span class="p">]</span>
<span class="mo">00403443</span>                <span class="n">do_CryptGetHashParam</span><span class="p">(</span><span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xb</span><span class="p">],</span> <span class="mi">2</span><span class="p">,</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xa</span><span class="p">],</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00403443</span>            
<span class="mo">00403456</span>            <span class="n">get_dll_baseaddr</span><span class="p">()</span>
<span class="mo">0040345</span><span class="n">b</span>            <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xe</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x40d1a0</span>
<span class="mo">00403461</span>            <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xd</span><span class="p">]</span> <span class="o">=</span> <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="mo">00403467</span>            <span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xe</span><span class="p">]</span>
<span class="mo">0040346</span><span class="n">a</span>            <span class="n">result</span> <span class="o">=</span> <span class="n">do_CryptDestroyHash</span><span class="p">(</span><span class="n">arg2</span><span class="p">[</span><span class="o">-</span><span class="mh">0xd</span><span class="p">])</span>
<span class="mo">0040346</span><span class="n">a</span>        
<span class="mo">00403473</span>        <span class="o">*</span><span class="n">arg2</span>
<span class="mo">00403474</span>        <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>Setting a breakpoint on <code class="language-plaintext highlighter-rouge">CryptHashData</code>, it turned out the ChaCha20 key was the SHA-256 hash of the <code class="language-plaintext highlighter-rouge">.text</code> section of the executable. This is an anti-debug measure, as setting software breakpoints changes the contents of <code class="language-plaintext highlighter-rouge">.text</code>.</p>

<p>When I tried dumping the <code class="language-plaintext highlighter-rouge">.text</code> section from the binary and hashing it, the resulting hash didn’t result in a valid decryption. Looking more closely in x64dbg at the data that was being hashed, I found that the last byte of the section had been changed from <code class="language-plaintext highlighter-rouge">0x00</code> to <code class="language-plaintext highlighter-rouge">0x90</code>. Making the same change to my copy of the <code class="language-plaintext highlighter-rouge">.text</code> section, I got the correct key <code class="language-plaintext highlighter-rouge">F630AA38D57297375D645559C334FD50D55CA1D177D2655A042351CF69244BF2</code>. (For the actual decryption step, I used <a href="https://github.com/Demonslay335/CryptoTester">CryptoTester</a>, which supports custom-constant ChaCha20 out of the box.)</p>
<h3 id="running-the-code">Running the code</h3>

<h4 id="single-stepping">Single stepping</h4>

<p>Now that I had the right key, I could see how the program tried to run the decrypted code. The program obtains the ChaCha20 key with a call to <code class="language-plaintext highlighter-rouge">CryptGetHashParam</code>, so I set a breakpoint on <code class="language-plaintext highlighter-rouge">CryptGetHashParam</code>’s return to patch in the correct hash. To make things easier, I eventually figured out that I could set the breakpoint to run a command to write to the hash address, so that I wouldn’t have to manually do the patch on every run. The syntax to do this in x64dbg is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>set 0x40d268,#F630AA38D57297375D645559C334FD50D55CA1D177D2655A042351CF69244BF2#
</code></pre></div></div>

<p>However, even with the correct key, the program didn’t decrypt and run the <code class="language-plaintext highlighter-rouge">.pc</code> section all at once. Instead, it only decrypted it 16 bytes at a time, and wrote only those few instructions to the buffer it had allocated for running shellcode.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">004040</span><span class="n">ad</span>        <span class="n">init_chacha_matrix_with_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">matrix</span><span class="p">,</span> <span class="n">key</span><span class="o">:</span> <span class="n">get_key_from_hprov</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hprov</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">nonce</span><span class="p">,</span> 
<span class="mo">004040</span><span class="n">ad</span>            <span class="n">counter0</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">counter1</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">004040</span><span class="n">ba</span>        <span class="n">chacha_rotated_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">matrix</span><span class="p">,</span> <span class="n">counter_1</span><span class="p">,</span> <span class="n">counter_2</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">004040</span><span class="n">c8</span>        <span class="n">do_chacha_encrypt</span><span class="p">(</span><span class="o">&amp;</span><span class="n">matrix</span><span class="p">,</span> <span class="n">ciphertext</span><span class="o">:</span> <span class="n">ciphertext_buf</span><span class="p">,</span> <span class="n">len</span><span class="o">:</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mo">004040</span><span class="n">d3</span>        <span class="k">return</span> <span class="n">ciphertext_buf</span>
</code></pre></div></div>

<p>Moreover, the program seemed to only be running one instruction at a time before allocating a new shellcode buffer with <code class="language-plaintext highlighter-rouge">VirtualAlloc</code>. For example, out of the first 16 bytes that are decrypted, only the instruction <code class="language-plaintext highlighter-rouge">mov eax,ecx</code> is run at first.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3E4C0006 | 8BC1                     | mov eax,ecx                             |
3E4C0008 | C741 04 90B16E0A         | mov dword ptr ds:[ecx+4],A6EB190        |
3E4C000F | C700 00000000            | mov dword ptr ds:[eax],0                |
3E4C0015 | 0000                     | add byte ptr ds:[eax],al                |
3E4C0017 | 0000                     | add byte ptr ds:[eax],al                |
3E4C0019 | 0000                     | add byte ptr ds:[eax],al                |
</code></pre></div></div>

<p>Then, a new buffer is allocated to run the next instruction <code class="language-plaintext highlighter-rouge">mov dword ptr ds:[ecx+4],A6EB190</code>, and so on.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>17100002 | C741 04 90B16E0A         | mov dword ptr ds:[ecx+4],A6EB190        |
17100009 | C741 08 336C4720         | mov dword ptr ds:[ecx+8],20476C33       |
17100010 | 0000                     | add byte ptr ds:[eax],al                |
17100012 | 0000                     | add byte ptr ds:[eax],al                |
17100014 | 0000                     | add byte ptr ds:[eax],al                |
</code></pre></div></div>

<p>In order to run a single instruction at a time before stopping execution, the program uses the same mechanisms that debuggers use to perform single steps. The CPU has a flag called the <em>trap flag</em> that tells it to execute a single instruction before raising an exception of type <code class="language-plaintext highlighter-rouge">EXCEPTION_SINGLE_STEP</code>. (You can see an example of how a debugger might use this flag <a href="https://www.timdbg.com/posts/writing-a-debugger-from-scratch-part-2/">here</a>.) Sure enough, the trap flag is set before each run of the decrypted shellcode.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">004037</span><span class="n">fa</span>    <span class="kt">uint32_t</span> <span class="kr">__stdcall</span> <span class="n">set_context_flags</span><span class="p">(</span><span class="n">CONTEXT</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>

<span class="mo">00403</span><span class="mi">804</span>        <span class="o">*</span><span class="p">(</span><span class="n">arg1</span><span class="o">-&gt;</span><span class="n">ContextFlags</span> <span class="o">+</span> <span class="mh">0x18</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00403</span><span class="mi">809</span>        <span class="o">*</span><span class="p">(</span><span class="n">arg1</span><span class="o">-&gt;</span><span class="n">ContextFlags</span> <span class="o">+</span> <span class="mh">0x14</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00403</span><span class="mi">80</span><span class="n">e</span>        <span class="o">*</span><span class="p">(</span><span class="n">arg1</span><span class="o">-&gt;</span><span class="n">ContextFlags</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00403</span><span class="mi">811</span>        <span class="kt">uint32_t</span> <span class="n">ContextFlags</span> <span class="o">=</span> <span class="n">arg1</span><span class="o">-&gt;</span><span class="n">ContextFlags</span>
<span class="mo">00403</span><span class="mi">813</span>        <span class="o">*</span><span class="p">(</span><span class="n">ContextFlags</span> <span class="o">+</span> <span class="mh">0xc0</span><span class="p">)</span> <span class="o">|=</span> <span class="mh">0x100</span>
<span class="mo">00403</span><span class="mi">81</span><span class="n">e</span>        <span class="k">return</span> <span class="n">ContextFlags</span>
</code></pre></div></div>

<h4 id="control-flow-obfuscation">Control flow obfuscation</h4>

<p>Even after decryption, the decompilation of the <code class="language-plaintext highlighter-rouge">.pc</code> section didn’t make much sense. The code was full of <code class="language-plaintext highlighter-rouge">int3</code> breakpoints, which were usually followed by nonsensical disassembly.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0040a0cb  mov     eax, dword [ebp-0x8]
0040a0ce  and     eax, 0x80000003
0040a0d3  int3    
0040a0d4  fisttp  dword [eax-0x7d], st0
0040a0d7  enter   0x40fc, 0x85
0040a0db  ror     ah, 0x38
</code></pre></div></div>

<p>Upon closer inspection in x64dbg, several bytes were being skipped each time the program executed an int3 breakpoint. For instance, after the breakpoint at <code class="language-plaintext highlighter-rouge">0x40a035</code> was executed, the next instruction to be run was the <code class="language-plaintext highlighter-rouge">push dword ptr [ebp+8]</code> instruction at <code class="language-plaintext highlighter-rouge">0x40a037</code>, with the address <code class="language-plaintext highlighter-rouge">0x40a036</code> being skipped over entirely.</p>

<p>Looking at the exception handler code again, there were different functions for retrieving the next <code class="language-plaintext highlighter-rouge">.pc</code> instruction depending on whether the exception was an <code class="language-plaintext highlighter-rouge">int3</code> breakpoint or a single step.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00404562</span>    <span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">decrypt_from_exception</span><span class="p">(</span><span class="k">struct</span> <span class="n">cipher_ctx</span><span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="kt">int32_t</span><span class="o">*</span> <span class="n">exception</span><span class="p">,</span> <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">context</span><span class="p">)</span>

<span class="mo">0040456</span><span class="mi">9</span>        <span class="k">if</span> <span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">field_8</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040456</span><span class="mi">9</span>            <span class="k">return</span> 
<span class="mo">0040456</span><span class="mi">9</span>        
<span class="mo">00404574</span>        <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">exception</span> <span class="o">==</span> <span class="n">STATUS_BREAKPOINT</span><span class="p">)</span>
<span class="mo">0040457</span><span class="mi">9</span>            <span class="n">chacha_breakpoint</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
<span class="mo">00404574</span>        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">exception</span> <span class="o">==</span> <span class="n">STATUS_SINGLE_STEP</span><span class="p">)</span>
<span class="mo">004045</span><span class="mi">8</span><span class="n">b</span>            <span class="n">chacha_single_step</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
<span class="mo">004045</span><span class="mi">86</span>        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">exception</span> <span class="o">==</span> <span class="n">STATUS_GUARD_PAGE_VIOLATION</span><span class="p">)</span>
<span class="mo">004045</span><span class="mi">9</span><span class="n">a</span>            <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">context_1</span> <span class="o">=</span> <span class="n">context</span>
<span class="mo">004045</span><span class="mi">9</span><span class="n">e</span>            <span class="n">chacha_guard_page_violation</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">exception</span><span class="p">)</span>
</code></pre></div></div>

<p>Unlike the single-step handler, which just set <code class="language-plaintext highlighter-rouge">context-&gt;Eip</code> to the next instruction before returning, the breakpoint handler called another function to determine the next instruction pointer before returning.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">004042</span><span class="n">ec</span>    <span class="kt">int32_t</span> <span class="n">__thiscall</span> <span class="n">chacha_breakpoint</span><span class="p">(</span><span class="k">struct</span> <span class="n">cipher_ctx</span><span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">context</span><span class="p">)</span>

<span class="mo">004042</span><span class="n">fe</span>        <span class="k">if</span> <span class="p">(</span><span class="n">sub_4037b2</span><span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">field_24</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040431</span><span class="mi">8</span>            <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">exception_context</span> <span class="o">=</span> <span class="n">context</span>
<span class="mo">0040431</span><span class="mi">8</span>            
<span class="mo">0040432</span><span class="mi">8</span>            <span class="k">if</span> <span class="p">(</span><span class="n">check_context_eip_in_range</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">eip</span><span class="o">:</span> <span class="n">exception_context</span><span class="o">-&gt;</span><span class="n">Eip</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040432</span><span class="n">d</span>                <span class="n">breakpoint_set_eip</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">exception_context</span><span class="p">)</span>
<span class="mo">00404332</span>                <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">context_1</span> <span class="o">=</span> <span class="n">context</span>
<span class="mo">00404335</span>                <span class="kt">uint32_t</span> <span class="n">Eip</span> <span class="o">=</span> <span class="n">context_1</span><span class="o">-&gt;</span><span class="n">Eip</span>
<span class="mo">0040433</span><span class="n">b</span>                <span class="kt">uint32_t</span> <span class="n">Eip_1</span> <span class="o">=</span> <span class="n">Eip</span>
<span class="mo">0040433</span><span class="n">f</span>                <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">Eip</span> <span class="o">=</span> <span class="n">Eip</span>
<span class="mo">00404342</span>                <span class="n">chacha_and_flush_cache</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">context</span><span class="o">:</span> <span class="n">context_1</span><span class="p">)</span>
<span class="mo">004042</span><span class="n">fe</span>        <span class="k">else</span>
<span class="mo">00404304</span>            <span class="n">flush_instruction_cache</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">context</span><span class="p">)</span>
<span class="mo">0040430</span><span class="mi">9</span>            <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">field_24</span>
<span class="mo">00404310</span>            <span class="n">set_context_flags</span><span class="p">(</span><span class="o">&amp;</span><span class="n">context</span><span class="p">)</span>
<span class="mo">00404310</span>        
<span class="mo">0040434</span><span class="n">d</span>        <span class="k">return</span> <span class="mh">0xffffffff</span>
</code></pre></div></div>

<p>Looking at the <code class="language-plaintext highlighter-rouge">breakpoint_set_eip</code> function at <code class="language-plaintext highlighter-rouge">0x4045f3</code>, it was retrieving a value from a map, then setting the next instruction to one of two locations pulled from that map.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">004045</span><span class="n">f3</span>    <span class="k">struct</span> <span class="n">map_val</span><span class="o">*</span> <span class="n">__thiscall</span> <span class="n">breakpoint_set_eip</span><span class="p">(</span><span class="k">struct</span> <span class="n">cipher_ctx</span><span class="o">*</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">exception_context</span><span class="p">)</span>

<span class="mo">004045</span><span class="n">f7</span>        <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">exception_context_1</span> <span class="o">=</span> <span class="n">exception_context</span>
<span class="mo">0040460</span><span class="mi">8</span>        <span class="k">struct</span> <span class="n">map_val</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span>
<span class="mo">0040460</span><span class="mi">8</span>            <span class="n">get_map_data</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">key</span><span class="o">:</span> <span class="p">(</span><span class="n">exception_context_1</span><span class="o">-&gt;</span><span class="n">Eip</span><span class="p">).</span><span class="n">w</span> <span class="o">-</span> <span class="p">(</span><span class="n">ctx</span><span class="o">-&gt;</span><span class="n">baseaddr</span><span class="p">).</span><span class="n">w</span><span class="p">)</span>
<span class="mo">0040460</span><span class="mi">8</span>        
<span class="mo">00404611</span>        <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00404622</span>            <span class="kt">int32_t</span> <span class="n">offset</span>
<span class="mo">00404622</span>            
<span class="mo">00404622</span>            <span class="k">if</span> <span class="p">(</span><span class="n">check_eflags</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">exception_context_1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040462</span><span class="n">c</span>                <span class="n">offset</span> <span class="o">=</span> <span class="n">result</span><span class="o">-&gt;</span><span class="n">offset_2</span>
<span class="mo">00404622</span>            <span class="k">else</span>
<span class="mo">00404627</span>                <span class="n">offset</span> <span class="o">=</span> <span class="n">result</span><span class="o">-&gt;</span><span class="n">offset_1</span> <span class="o">+</span> <span class="n">result</span><span class="o">-&gt;</span><span class="n">offset_2</span>
<span class="mo">00404627</span>            
<span class="mo">0040463</span><span class="n">c</span>            <span class="n">flush_process_cache</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">exception_context</span><span class="p">,</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">exception_context_1</span><span class="o">-&gt;</span><span class="n">Eip</span><span class="p">)</span>
<span class="mo">00404641</span>            <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">00404611</span>        <span class="k">else</span>
<span class="mo">00404613</span>            <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">00404613</span>        
<span class="mo">00404647</span>        <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>Dumping the map data from the debugger, it contained a long list of structures of four integer values. The first integer was the map key, corresponding to the address of the <code class="language-plaintext highlighter-rouge">.pc</code> instruction currently being executed (minus the base address of <code class="language-plaintext highlighter-rouge">0x400000</code>). The third and fourth integers were the two choices of addresses to jump to.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00 A0 00 00 01 00 00 00 2F 00 00 00 02 00 00 00 
01 A0 00 00 12 00 00 00 49 00 00 00 02 00 00 00 
02 A0 00 00 0A 00 00 00 72 00 00 00 02 00 00 00 
03 A0 00 00 0A 00 00 00 43 00 00 00 02 00 00 00 
04 A0 00 00 05 00 00 00 5A 00 00 00 02 00 00 00 
</code></pre></div></div>

<p>The second integer, which was always a value between 1 and 18, was used for deciding which of the two offsets to jump to. Depending on its value, different combinations of the flags in the <code class="language-plaintext highlighter-rouge">eflags</code> register were checked, and the decision for where to jump was made based on the value of those flags.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00404166</span>    <span class="kt">uint32_t</span> <span class="kr">__stdcall</span> <span class="n">check_eflags</span><span class="p">(</span><span class="k">struct</span> <span class="n">map_val</span><span class="o">*</span> <span class="n">arg1</span><span class="p">,</span> <span class="n">CONTEXT</span><span class="o">*</span> <span class="n">arg2</span><span class="p">)</span>

<span class="mo">00404172</span>        <span class="kt">uint32_t</span> <span class="n">EFlags</span> <span class="o">=</span> <span class="n">arg2</span><span class="o">-&gt;</span><span class="n">EFlags</span>
<span class="mo">0040417</span><span class="mi">8</span>        <span class="kt">uint32_t</span> <span class="n">choice</span> <span class="o">=</span> <span class="n">arg1</span><span class="o">-&gt;</span><span class="n">field_4</span> <span class="o">-</span> <span class="mi">1</span>
<span class="mo">0040417</span><span class="mi">8</span>        
<span class="mo">0040417</span><span class="n">c</span>        <span class="k">if</span> <span class="p">(</span><span class="n">choice</span> <span class="n">u</span><span class="o">&gt;</span> <span class="mh">0x11</span><span class="p">)</span>
<span class="mo">0040423</span><span class="mi">8</span>            <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040417</span><span class="n">c</span>        <span class="k">else</span>
<span class="mo">004041</span><span class="mi">82</span>            <span class="k">switch</span> <span class="p">(</span><span class="n">choice</span><span class="p">)</span>
<span class="mo">004041</span><span class="n">ea</span>                <span class="k">case</span> <span class="mi">0</span>
<span class="mo">004041</span><span class="n">ea</span>                    <span class="n">EFlags</span> <span class="n">u</span><span class="o">&gt;&gt;=</span> <span class="mi">6</span>  <span class="c1">// ~ZF</span>
<span class="mo">004041</span><span class="n">ed</span>                    <span class="k">goto</span> <span class="n">get_negated_flag</span>
<span class="mo">00404234</span>                <span class="k">case</span> <span class="mi">1</span>
<span class="mo">00404234</span>                    <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">004041</span><span class="mi">89</span>                <span class="k">case</span> <span class="mi">2</span>
<span class="mo">004041</span><span class="mi">8</span><span class="n">c</span>                    <span class="n">EFlags</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="n">EFlags</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mi">7</span><span class="p">).</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mi">1</span>  <span class="c1">// SF</span>
<span class="mo">004041</span><span class="mi">8</span><span class="n">f</span>                    <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">EFlags</span><span class="p">.</span><span class="n">b</span>

<span class="c1">// [...]</span>

<span class="mo">004041</span><span class="n">a2</span>                <span class="k">case</span> <span class="mh">0x10</span>
<span class="mo">004041</span><span class="n">a5</span>                    <span class="k">if</span> <span class="p">((</span><span class="mi">1</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">EFlags</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">).</span><span class="n">b</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>  <span class="c1">// ZF</span>
<span class="mo">004041</span><span class="n">b7</span>                        <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">004041</span><span class="n">a5</span>                    <span class="k">else</span>
<span class="mo">004041</span><span class="n">af</span>                        <span class="c1">// ~ (OF ^ SF)</span>
<span class="mo">004041</span><span class="n">af</span>                        <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="n">EFlags</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mh">0xb</span><span class="p">).</span><span class="n">b</span> <span class="o">^</span> <span class="p">(</span><span class="n">EFlags</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mi">7</span><span class="p">).</span><span class="n">b</span>
<span class="mo">004041</span><span class="n">af</span>                        
<span class="mo">004041</span><span class="n">b3</span>                        <span class="k">if</span> <span class="p">((</span><span class="mi">1</span> <span class="o">&amp;</span> <span class="n">choice</span><span class="p">.</span><span class="n">b</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">004041</span><span class="n">b7</span>                            <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">004041</span><span class="n">b3</span>                        <span class="k">else</span>
<span class="mo">004041</span><span class="n">b7</span>                            <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">004041</span><span class="mi">8</span><span class="n">c</span>                <span class="k">case</span> <span class="mh">0x11</span>
<span class="mo">004041</span><span class="mi">8</span><span class="n">c</span>                    <span class="n">EFlags</span><span class="p">.</span><span class="n">b</span> <span class="o">&amp;=</span> <span class="mi">1</span>
<span class="mo">004041</span><span class="mi">8</span><span class="n">f</span>                    <span class="n">choice</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">EFlags</span><span class="p">.</span><span class="n">b</span>
<span class="mo">004041</span><span class="mi">8</span><span class="n">f</span>        
<span class="mo">0040423</span><span class="n">b</span>        <span class="k">return</span> <span class="n">choice</span>
</code></pre></div></div>

<p>The combinations of flags that were being checked were, in fact, the exact combinations of flags that are checked in ordinary x86 branch instructions. For example, in the switch case above, case 0 corresponds to the condition <code class="language-plaintext highlighter-rouge">ZF = 0</code>, which is used in the conditional branch instruction <code class="language-plaintext highlighter-rouge">JNZ</code>. Similarly, case 0x10 corresponds to the condition <code class="language-plaintext highlighter-rouge">ZF = 0</code> and <code class="language-plaintext highlighter-rouge">SF = OF</code>, which is the conditional <code class="language-plaintext highlighter-rouge">JG</code>.</p>

<p>Essentially, every single branch instruction in the <code class="language-plaintext highlighter-rouge">.pc</code> section had been replaced with a breakpoint, and the branch logic was being handled in the exception handler. Since this is a one-to-one replacement of one assembly instruction with another, the most logical way to deal with it would’ve been to patch the original branch instructions back in, except that the branch instructions were longer than the breakpoint instructions replacing them.</p>
<h2 id="reversing-the-check-algorithm">Reversing the check algorithm</h2>

<h3 id="emulating-with-unicorn">Emulating with Unicorn</h3>

<p>Since I couldn’t think of a good way to patch the binary in a way that would get me a reasonable-looking decompilation, I decided to try to emulate the <code class="language-plaintext highlighter-rouge">.pc</code> section in Unicorn, adding a hook to intercept the breakpoint instructions and resolve the branches. I had to hook a couple of external functions like <code class="language-plaintext highlighter-rouge">strlen</code>, but luckily there weren’t too many of those, so I didn’t have to do much extra work other than the usual boilerplate that comes with setting up Unicorn.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">unicorn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">unicorn.x86_const</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">capstone</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">struct</span>

<span class="k">def</span> <span class="nf">to_int</span><span class="p">(</span><span class="n">b</span><span class="p">):</span>
    <span class="k">return</span> <span class="nb">hex</span><span class="p">(</span><span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span>

<span class="n">jumps</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span><span class="p">:</span> <span class="s">'jnz'</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span> <span class="s">'jmp'</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="s">'js'</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="s">'jae'</span><span class="p">,</span> <span class="mi">4</span><span class="p">:</span> <span class="s">'jle'</span><span class="p">,</span> <span class="mi">5</span><span class="p">:</span> <span class="s">'ja'</span><span class="p">,</span> <span class="mi">6</span><span class="p">:</span> <span class="s">'jge'</span><span class="p">,</span> <span class="mi">7</span><span class="p">:</span> <span class="s">'jp'</span><span class="p">,</span> <span class="mi">8</span><span class="p">:</span> <span class="s">'jo'</span><span class="p">,</span> <span class="mi">9</span><span class="p">:</span> <span class="s">'jbe'</span><span class="p">,</span> <span class="mi">10</span><span class="p">:</span> <span class="s">'jecxz'</span><span class="p">,</span> <span class="mi">11</span><span class="p">:</span> <span class="s">'jnp'</span><span class="p">,</span> <span class="mi">12</span><span class="p">:</span> <span class="s">'jz'</span><span class="p">,</span> <span class="mi">13</span><span class="p">:</span> <span class="s">'jl'</span><span class="p">,</span> <span class="mi">14</span><span class="p">:</span> <span class="s">'jns'</span><span class="p">,</span> <span class="mi">15</span><span class="p">:</span> <span class="s">'jno'</span><span class="p">,</span> <span class="mi">16</span><span class="p">:</span> <span class="s">'jg'</span><span class="p">,</span> <span class="mi">17</span><span class="p">:</span> <span class="s">'jb'</span> <span class="p">}</span>

<span class="k">def</span> <span class="nf">get_bit</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">bit</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">val</span> <span class="o">&gt;&gt;</span> <span class="n">bit</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">1</span>

<span class="k">def</span> <span class="nf">get_branch_cond</span><span class="p">(</span><span class="n">cond</span><span class="p">,</span> <span class="n">flags</span><span class="p">,</span> <span class="n">ecx</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># jnz
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span> <span class="c1"># jmp
</span>        <span class="k">return</span> <span class="bp">True</span>
    <span class="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span> <span class="c1"># js
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span>
    <span class="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span> <span class="c1"># jae
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</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="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">4</span><span class="p">:</span> <span class="c1"># jle
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="p">(</span><span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">!=</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0x7</span><span class="p">))</span>
    <span class="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">5</span><span class="p">:</span> <span class="c1"># ja
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</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="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">6</span><span class="p">:</span> <span class="c1"># jge
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span> <span class="o">==</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0xb</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">7</span><span class="p">:</span> <span class="c1"># jp
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span>
    <span class="k">elif</span> <span class="n">cond</span> <span class="o">==</span> <span class="mi">8</span><span class="p">:</span> <span class="c1"># jo
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">9</span><span class="p">):</span> <span class="c1"># jbe
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</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="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">10</span><span class="p">):</span> <span class="c1"># jecxz
</span>        <span class="k">return</span> <span class="n">ecx</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">11</span><span class="p">):</span> <span class="c1"># jnp
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">12</span><span class="p">):</span> <span class="c1"># jz
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">13</span><span class="p">):</span> <span class="c1"># jl
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">!=</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0x7</span><span class="p">)</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">14</span><span class="p">):</span> <span class="c1"># jns
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">15</span><span class="p">):</span> <span class="c1"># jno
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">16</span><span class="p">):</span> <span class="c1"># jg
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="p">(</span><span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0xb</span><span class="p">)</span> <span class="o">==</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</span><span class="p">,</span> <span class="mh">0x7</span><span class="p">))</span>
    <span class="k">elif</span><span class="p">(</span><span class="n">cond</span> <span class="o">==</span> <span class="mi">17</span><span class="p">):</span> <span class="c1"># jb
</span>        <span class="k">return</span> <span class="n">get_bit</span><span class="p">(</span><span class="n">flags</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="k">else</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"invalid condition"</span><span class="p">,</span> <span class="n">cond</span><span class="p">)</span>

<span class="n">offsets</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'data.bin'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">).</span><span class="n">read</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">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">f</span><span class="p">),</span> <span class="mh">0x10</span><span class="p">):</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">f</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="mh">0x10</span><span class="p">]</span>
    <span class="n">key</span><span class="p">,</span> <span class="n">cond</span><span class="p">,</span> <span class="n">jump</span><span class="p">,</span> <span class="n">base</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'&lt;4i'</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
    <span class="n">offsets</span><span class="p">[</span><span class="n">key</span> <span class="o">+</span> <span class="mh">0x400000</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">cond</span><span class="p">,</span> <span class="n">jump</span><span class="p">,</span> <span class="n">base</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">emu_push</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">,</span> <span class="n">val</span><span class="p">):</span>
    <span class="n">esp</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_ESP</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">esp</span> <span class="o">-</span> <span class="mi">4</span><span class="p">,</span> <span class="n">val</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_ESP</span><span class="p">,</span> <span class="n">esp</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">emu_pop</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">):</span>
    <span class="n">esp</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_ESP</span><span class="p">)</span>
    <span class="n">val</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">esp</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_ESP</span><span class="p">,</span> <span class="n">esp</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">emu_return</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">):</span>
    <span class="n">ret_addr</span> <span class="o">=</span> <span class="n">emu_pop</span><span class="p">(</span><span class="n">mu</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">,</span> <span class="n">ret_addr</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_strlen</span><span class="p">(</span><span class="n">uc</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="s">'Skipping strlen function'</span><span class="p">)</span>
    <span class="n">uc</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EAX</span><span class="p">,</span> <span class="mh">0x13</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_crypt</span><span class="p">(</span><span class="n">uc</span><span class="p">):</span>
    <span class="c1">#print('Skipping crypto setup')
</span>    <span class="n">uc</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EAX</span><span class="p">,</span> <span class="n">HPROV</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_hash</span><span class="p">(</span><span class="n">uc</span><span class="p">):</span>
    <span class="c1">#print('Skipping hash function')
</span>    <span class="n">uc</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EAX</span><span class="p">,</span> <span class="n">MEM</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_pass</span><span class="p">(</span><span class="n">uc</span><span class="p">):</span>
    <span class="k">pass</span>

<span class="k">def</span> <span class="nf">hook_mem_write</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">,</span> <span class="n">access</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">user_data</span><span class="p">):</span>
        <span class="n">data_bytes</span> <span class="o">=</span> <span class="n">value</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'[</span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">))</span><span class="si">}</span><span class="s">] Address </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s">: Wrote </span><span class="si">{</span><span class="n">data_bytes</span><span class="p">.</span><span class="nb">hex</span><span class="p">()</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_mem_read</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">,</span> <span class="n">access</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">user_data</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">address</span> <span class="o">&gt;=</span> <span class="n">MEM</span> <span class="ow">and</span> <span class="n">address</span> <span class="o">&lt;</span> <span class="n">MEM</span> <span class="o">+</span> <span class="mh">0x10</span><span class="p">:</span>
        <span class="n">data_bytes</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">address</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'[</span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">))</span><span class="si">}</span><span class="s">] Address </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s">: Read </span><span class="si">{</span><span class="n">data_bytes</span><span class="p">.</span><span class="nb">hex</span><span class="p">()</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_code</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">user_data</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">md</span><span class="p">.</span><span class="n">disasm</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">address</span><span class="p">,</span> <span class="n">size</span><span class="p">),</span> <span class="n">address</span><span class="p">):</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"0x%x:</span><span class="se">\t</span><span class="s">%s</span><span class="se">\t</span><span class="s">%s"</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">address</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">mnemonic</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">op_str</span><span class="p">))</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="se">\t</span><span class="s">eax:'</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EAX</span><span class="p">)))</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="se">\t</span><span class="s">ebx:'</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EBX</span><span class="p">)))</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="se">\t</span><span class="s">ecx:'</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_ECX</span><span class="p">)))</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="se">\t</span><span class="s">edx:'</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EDX</span><span class="p">)))</span>
        <span class="n">ebp</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EBP</span><span class="p">)</span>
        <span class="n">esp</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_ESP</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="se">\t</span><span class="s">ebp: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">  ebp - 4: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span> <span class="o">-</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">    ebp - 8: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span> <span class="o">-</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">    ebp - c: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span> <span class="o">-</span> <span class="mh">0xc</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">    ebp - 10: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span> <span class="o">-</span> <span class="mh">0x10</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">    ebp - 0x14: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span> <span class="o">-</span> <span class="mh">0x14</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</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">'</span><span class="se">\t</span><span class="s">stack: +0: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">esp</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">    +4: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">esp</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">    +8:  </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">esp</span> <span class="o">+</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s">     +c: </span><span class="si">{</span><span class="n">to_int</span><span class="p">(</span><span class="n">mu</span><span class="p">.</span><span class="n">mem_read</span><span class="p">(</span><span class="n">ebp</span> <span class="o">+</span> <span class="mh">0xc</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span><span class="si">}</span><span class="s"> '</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">i</span><span class="p">.</span><span class="n">mnemonic</span> <span class="o">==</span> <span class="s">'call'</span><span class="p">:</span> <span class="c1"># skip external function calls
</span>            <span class="n">target</span> <span class="o">=</span> <span class="n">i</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">imm</span>
            <span class="k">if</span> <span class="n">target</span> <span class="ow">in</span> <span class="n">hooks</span><span class="p">:</span>
                <span class="n">hooks</span><span class="p">[</span><span class="n">target</span><span class="p">](</span><span class="n">mu</span><span class="p">)</span>
                <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">address</span> <span class="o">+</span> <span class="n">i</span><span class="p">.</span><span class="n">size</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">hook_interrupt</span><span class="p">(</span><span class="n">mu</span><span class="p">:</span> <span class="n">Uc</span><span class="p">,</span> <span class="n">intno</span><span class="p">,</span> <span class="n">user_data</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">intno</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
        <span class="n">eip</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">cond</span><span class="p">,</span> <span class="n">jump</span><span class="p">,</span> <span class="n">base</span> <span class="o">=</span> <span class="n">offsets</span><span class="p">[</span><span class="n">eip</span><span class="p">]</span>
        <span class="n">not_taken_addr</span> <span class="o">=</span> <span class="n">eip</span> <span class="o">+</span> <span class="n">base</span>
        <span class="n">taken_addr</span> <span class="o">=</span> <span class="n">eip</span> <span class="o">+</span> <span class="n">base</span> <span class="o">+</span> <span class="n">jump</span>
        <span class="c1">#print(hex(eip), hex(not_taken_addr), hex(taken_addr))
</span>
        <span class="n">ecx</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_ECX</span><span class="p">)</span>
        <span class="n">flags</span> <span class="o">=</span> <span class="n">mu</span><span class="p">.</span><span class="n">reg_read</span><span class="p">(</span><span class="n">UC_X86_REG_EFLAGS</span><span class="p">)</span>

        <span class="n">should_branch</span> <span class="o">=</span> <span class="n">get_branch_cond</span><span class="p">(</span><span class="n">cond</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">flags</span><span class="p">,</span> <span class="n">ecx</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">should_branch</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'Branch </span><span class="si">{</span><span class="n">jumps</span><span class="p">[</span><span class="n">cond</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s"> taken: </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">taken_addr</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">,</span> <span class="n">taken_addr</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="sa">f</span><span class="s">'Branch </span><span class="si">{</span><span class="n">jumps</span><span class="p">[</span><span class="n">cond</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s"> not taken: </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">not_taken_addr</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EIP</span><span class="p">,</span> <span class="n">not_taken_addr</span><span class="p">)</span>


<span class="n">sc</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'shellcode.bin'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>

<span class="n">md</span> <span class="o">=</span> <span class="n">Cs</span><span class="p">(</span><span class="n">CS_ARCH_X86</span><span class="p">,</span> <span class="n">CS_MODE_32</span><span class="p">)</span>
<span class="n">md</span><span class="p">.</span><span class="n">detail</span> <span class="o">=</span> <span class="bp">True</span>

<span class="n">hooks</span> <span class="o">=</span> <span class="p">{</span>
    <span class="mh">0x409731</span><span class="p">:</span> <span class="n">hook_strlen</span><span class="p">,</span>
    <span class="mh">0x403477</span><span class="p">:</span> <span class="n">hook_crypt</span><span class="p">,</span>
    <span class="mh">0x40a4b5</span><span class="p">:</span> <span class="n">hook_hash</span>
<span class="p">}</span>

<span class="n">BASE</span> <span class="o">=</span> <span class="mh">0x40a000</span>
<span class="n">BASE_SIZE</span> <span class="o">=</span> <span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span>
<span class="n">STACK_ADDR</span> <span class="o">=</span> <span class="mh">0x40000000</span>
<span class="n">STACK_SIZE</span> <span class="o">=</span> <span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span>

<span class="n">MEM</span> <span class="o">=</span> <span class="mh">0x60000000</span>
<span class="n">MEM_SIZE</span> <span class="o">=</span> <span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span>

<span class="n">HPROV</span> <span class="o">=</span> <span class="n">MEM</span> <span class="o">+</span> <span class="mh">0x100</span>
<span class="n">CONSTS</span> <span class="o">=</span> <span class="n">MEM</span> <span class="o">+</span> <span class="mh">0x2000</span>

<span class="k">def</span> <span class="nf">emu</span><span class="p">(</span><span class="n">start</span><span class="p">):</span>
    <span class="n">mu</span> <span class="o">=</span> <span class="n">Uc</span><span class="p">(</span><span class="n">UC_ARCH_X86</span><span class="p">,</span> <span class="n">UC_MODE_32</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">mem_map</span><span class="p">(</span><span class="n">STACK_ADDR</span><span class="p">,</span> <span class="n">STACK_SIZE</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_ESP</span><span class="p">,</span> <span class="n">STACK_ADDR</span> <span class="o">+</span> <span class="n">STACK_SIZE</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_EBP</span><span class="p">,</span> <span class="n">STACK_ADDR</span> <span class="o">+</span> <span class="n">STACK_SIZE</span> <span class="o">//</span> <span class="mi">2</span> <span class="o">-</span> <span class="mh">0x1000</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">mem_map</span><span class="p">(</span><span class="n">MEM</span><span class="p">,</span> <span class="n">MEM_SIZE</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">MEM</span><span class="p">,</span> <span class="sa">b</span><span class="s">'ABCD-4567-89ab-cdef'</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">STACK_ADDR</span> <span class="o">+</span> <span class="n">STACK_SIZE</span> <span class="o">//</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="n">MEM</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span>

    <span class="n">KEY</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s">'F630AA38D57297375D645559C334FD50D55CA1D177D2655A042351CF69244BF2'</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">HPROV</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="n">KEY</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">HPROV</span> <span class="o">+</span> <span class="mh">0x24</span><span class="p">,</span> <span class="p">(</span><span class="n">MEM</span><span class="o">+</span><span class="mh">0x1000</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">HPROV</span> <span class="o">+</span> <span class="mh">0x28</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x00\x8a\x00\x00</span><span class="s">'</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">CONSTS</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="s">'47bb5d8690b16e0a336c472093768a1cfbbdfe59'</span><span class="p">))</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">reg_write</span><span class="p">(</span><span class="n">UC_X86_REG_ECX</span><span class="p">,</span> <span class="n">CONSTS</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">mem_map</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="n">BASE_SIZE</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">mem_write</span><span class="p">(</span><span class="n">BASE</span><span class="p">,</span> <span class="n">sc</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">hook_add</span><span class="p">(</span><span class="n">UC_HOOK_CODE</span><span class="p">,</span> <span class="n">hook_code</span><span class="p">)</span>
    <span class="n">mu</span><span class="p">.</span><span class="n">hook_add</span><span class="p">(</span><span class="n">UC_HOOK_INTR</span><span class="p">,</span> <span class="n">hook_interrupt</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">hook_add</span><span class="p">(</span><span class="n">UC_HOOK_MEM_READ</span><span class="p">,</span> <span class="n">hook_mem_read</span><span class="p">)</span>

    <span class="n">mu</span><span class="p">.</span><span class="n">emu_start</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">BASE</span><span class="o">+</span><span class="n">BASE_SIZE</span><span class="p">)</span>

<span class="n">emu</span><span class="p">(</span><span class="mh">0x40a025</span><span class="p">)</span>
</code></pre></div></div>

<p>Unfortunately, this approach turned out to be largely unhelpful. I could tell that the serial was supposed to be 19 characters long (consistent with something of the format <code class="language-plaintext highlighter-rouge">XXXX-XXXX-XXXX-XXXX</code>), and that the characters were being hashed in groups of four, but the hash algorithm didn’t match up with anything I recognized.</p>

<p>I did notice that the <code class="language-plaintext highlighter-rouge">.pc</code> section started with a function that loaded five random-looking values, though, so I figured those were the target values for the hash.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0040</span><span class="n">a000</span>    <span class="kt">int32_t</span><span class="o">*</span> <span class="n">__convention</span><span class="p">(</span><span class="s">"fastcall"</span><span class="p">)</span> <span class="n">sub_40a000</span><span class="p">(</span><span class="kt">int32_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>

<span class="mo">0040</span><span class="n">a000</span>  <span class="n">c70147bb5d86</span>       <span class="n">mov</span>     <span class="n">dword</span> <span class="p">[</span><span class="n">ecx</span><span class="p">],</span> <span class="mh">0x865dbb47</span>  <span class="p">{</span><span class="mh">0x865dbb47</span><span class="p">}</span>
<span class="mo">0040</span><span class="n">a006</span>  <span class="mi">8</span><span class="n">bc1</span>               <span class="n">mov</span>     <span class="n">eax</span><span class="p">,</span> <span class="n">ecx</span>
<span class="mo">0040</span><span class="n">a008</span>  <span class="n">c7410490b16e0a</span>     <span class="n">mov</span>     <span class="n">dword</span> <span class="p">[</span><span class="n">ecx</span><span class="o">+</span><span class="mh">0x4</span><span class="p">],</span> <span class="mh">0xa6eb190</span>
<span class="mo">0040</span><span class="n">a00f</span>  <span class="n">c74108336c4720</span>     <span class="n">mov</span>     <span class="n">dword</span> <span class="p">[</span><span class="n">ecx</span><span class="o">+</span><span class="mh">0x8</span><span class="p">],</span> <span class="mh">0x20476c33</span>
<span class="mo">0040</span><span class="n">a016</span>  <span class="n">c7410c93768a1c</span>     <span class="n">mov</span>     <span class="n">dword</span> <span class="p">[</span><span class="n">ecx</span><span class="o">+</span><span class="mh">0xc</span><span class="p">],</span> <span class="mh">0x1c8a7693</span>
<span class="mo">0040</span><span class="n">a01d</span>  <span class="n">c74110fbbdfe59</span>     <span class="n">mov</span>     <span class="n">dword</span> <span class="p">[</span><span class="n">ecx</span><span class="o">+</span><span class="mh">0x10</span><span class="p">],</span> <span class="mh">0x59febdfb</span>
<span class="mo">0040</span><span class="n">a024</span>  <span class="n">c3</span>                 <span class="n">retn</span>     <span class="p">{</span><span class="n">__return_addr</span><span class="p">}</span>
</code></pre></div></div>
<h3 id="fixing-the-decompilation">Fixing the decompilation</h3>

<p>After a while I gave up on emulation and came back to the idea of trying to decompile the code again. I couldn’t figure out a way to patch in the branch instructions without overwriting too many other instructions, so instead I decided to see if I could modify the Binary Ninja IL somehow.</p>

<p>In Binary Ninja, the code to be decompiled is lifted through several different forms of IL as it’s being processed. In theory, there are APIs to edit IL instructions at any stage of the analysis process. Since the modification I wanted to make was supposed to be equivalent to a simple binary patch, I thought it would make the most sense to modify the lowest level of IL, called <em>Lifted IL</em>. However, when I looked through the <a href="https://docs.binary.ninja/dev/bnil-modifying.html">documentation</a> on how to do so, I found that it explicitly advised against it:</p>

<blockquote>
  <p><strong>You probably do not want to modify Lifted IL with a Workflow</strong>. Instead, consider modifying the Architecture directly (<a href="https://github.com/Vector35/binaryninja-api/tree/dev/arch">most of which are Open Source on our GitHub</a>) or making your Activity modify Low Level IL.</p>
</blockquote>

<p>“Modifying the Architecture directly” sounded promising, so I looked at how the <a href="https://github.com/Vector35/binaryninja-api/tree/e2e420c91147f2a83cf59b37c973f57e209ef67a/arch/x86">x86 architecture plugin</a> generated IL instructions.</p>

<p>The <a href="https://github.com/Vector35/binaryninja-api/blob/e2e420c91147f2a83cf59b37c973f57e209ef67a/arch/x86/il.cpp#L1856">code</a> for how breakpoints were handled turned out to be very straightforward: for each INT3 breakpoint instruction, a single IL breakpoint instruction is emitted. To fix it, I would need to modify that line to emit an IL branch instruction instead.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="n">XED_ICLASS_INT3</span><span class="p">:</span>
	<span class="n">il</span><span class="p">.</span><span class="n">AddInstruction</span><span class="p">(</span><span class="n">il</span><span class="p">.</span><span class="n">Breakpoint</span><span class="p">());</span>
	<span class="k">break</span><span class="p">;</span>
</code></pre></div></div>

<p>Conveniently, the <a href="https://github.com/Vector35/binaryninja-api/blob/e2e420c91147f2a83cf59b37c973f57e209ef67a/arch/x86/il.cpp#L1860">code</a> for handling branch instructions is right next to the breakpoint code, and it includes a convenient switch case listing all the ways to construct an IL conditional branch from its corresponding x86 branch.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="n">XED_ICLASS_JO</span><span class="p">:</span>
	<span class="n">ConditionalJump</span><span class="p">(</span><span class="n">arch</span><span class="p">,</span> <span class="n">il</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">FlagCondition</span><span class="p">(</span><span class="n">LLFC_O</span><span class="p">),</span> <span class="n">addrSize</span><span class="p">,</span> <span class="n">branchDestination</span><span class="p">,</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">instLen</span><span class="p">);</span>
	<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

<span class="k">case</span> <span class="n">XED_ICLASS_JNO</span><span class="p">:</span>
	<span class="n">ConditionalJump</span><span class="p">(</span><span class="n">arch</span><span class="p">,</span> <span class="n">il</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">FlagCondition</span><span class="p">(</span><span class="n">LLFC_NO</span><span class="p">),</span> <span class="n">addrSize</span><span class="p">,</span> <span class="n">branchDestination</span><span class="p">,</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">instLen</span><span class="p">);</span>
	<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
	
<span class="c1">// [...]</span>

<span class="k">case</span> <span class="n">XED_ICLASS_JECXZ</span><span class="p">:</span>
	<span class="n">ConditionalJump</span><span class="p">(</span><span class="n">arch</span><span class="p">,</span> <span class="n">il</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">CompareEqual</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">Register</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">XED_REG_ECX</span><span class="p">),</span> <span class="n">il</span><span class="p">.</span><span class="n">Const</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)),</span> <span class="n">addrSize</span><span class="p">,</span> <span class="n">branchDestination</span><span class="p">,</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">instLen</span><span class="p">);</span>
	<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

<span class="k">case</span> <span class="n">XED_ICLASS_JRCXZ</span><span class="p">:</span>
	<span class="n">ConditionalJump</span><span class="p">(</span><span class="n">arch</span><span class="p">,</span> <span class="n">il</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">CompareEqual</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">Register</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">XED_REG_RCX</span><span class="p">),</span> <span class="n">il</span><span class="p">.</span><span class="n">Const</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">0</span><span class="p">)),</span> <span class="n">addrSize</span><span class="p">,</span> <span class="n">branchDestination</span><span class="p">,</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">instLen</span><span class="p">);</span>
	<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</code></pre></div></div>

<p>To generate the branch instructions, I started by adding a hard-coded map of all the breakpoint addresses in the <code class="language-plaintext highlighter-rouge">.pc</code> section and their corresponding branch types, as well as an enum containing all the conditions.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;cstdint&gt;</span><span class="cp">
</span>
<span class="k">struct</span> <span class="nc">CrackmeCond</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">cond</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">not_taken</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">taken</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">CRACKME_SC_START</span> <span class="o">=</span> <span class="mh">0x40a000</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">CRACKME_SC_END</span> <span class="o">=</span> <span class="mh">0x40a5ff</span><span class="p">;</span>

<span class="k">enum</span> <span class="k">class</span> <span class="nc">JumpCond</span><span class="o">:</span> <span class="kt">int</span> <span class="p">{</span>
    <span class="n">JNZ</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
    <span class="n">JMP</span><span class="p">,</span>
    <span class="n">JS</span><span class="p">,</span>
    <span class="n">JAE</span><span class="p">,</span>
    <span class="n">JLE</span><span class="p">,</span>
    <span class="n">JA</span><span class="p">,</span>
    <span class="n">JGE</span><span class="p">,</span>
    <span class="n">JP</span><span class="p">,</span>
    <span class="n">JO</span><span class="p">,</span>
    <span class="n">JBE</span><span class="p">,</span>
    <span class="n">JECXZ</span><span class="p">,</span>
    <span class="n">JNP</span><span class="p">,</span>
    <span class="n">JZ</span><span class="p">,</span>
    <span class="n">JL</span><span class="p">,</span>
    <span class="n">JNS</span><span class="p">,</span>
    <span class="n">JNO</span><span class="p">,</span>
    <span class="n">JG</span><span class="p">,</span>
    <span class="n">JB</span>
<span class="p">};</span>

<span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">CrackmeCond</span><span class="o">&gt;</span> <span class="n">conds</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">{</span> <span class="mh">0x40a000</span><span class="p">,</span> <span class="p">{</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">49</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a001</span><span class="p">,</span> <span class="p">{</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">75</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a002</span><span class="p">,</span> <span class="p">{</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">116</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a003</span><span class="p">,</span> <span class="p">{</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">69</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a004</span><span class="p">,</span> <span class="p">{</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">92</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a005</span><span class="p">,</span> <span class="p">{</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">34</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a006</span><span class="p">,</span> <span class="p">{</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">161</span> <span class="p">}</span> <span class="p">},</span>
    <span class="c1">// [...]</span>
    <span class="p">{</span> <span class="mh">0x40a5fd</span><span class="p">,</span> <span class="p">{</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">130</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a5fe</span><span class="p">,</span> <span class="p">{</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">52</span> <span class="p">}</span> <span class="p">},</span>
    <span class="p">{</span> <span class="mh">0x40a5ff</span><span class="p">,</span> <span class="p">{</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">46</span> <span class="p">}</span> <span class="p">},</span>
<span class="p">};</span>
</code></pre></div></div>

<p>I then modified the breakpoint handling code to add a switch case modeled after Binary Ninja’s own code for handling x86 conditional branch instructions, calling <code class="language-plaintext highlighter-rouge">ConditionalJump</code> with the arguments I had parsed out of the map whenever a breakpoint fell within the range of addresses in the <code class="language-plaintext highlighter-rouge">.pc</code> section.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">case</span> <span class="n">XED_ICLASS_INT3</span><span class="p">:</span>
	<span class="k">if</span><span class="p">(</span><span class="n">addr</span> <span class="o">&gt;=</span> <span class="n">CRACKME_SC_START</span> <span class="o">&amp;&amp;</span> <span class="n">addr</span> <span class="o">&lt;</span> <span class="n">CRACKME_SC_END</span><span class="p">)</span>
	<span class="p">{</span>
		<span class="k">const</span> <span class="k">auto</span> <span class="n">cond_struct</span> <span class="o">=</span> <span class="n">conds</span><span class="p">[</span><span class="n">addr</span><span class="p">];</span>
		<span class="k">const</span> <span class="k">auto</span> <span class="n">cond</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">JumpCond</span><span class="o">&gt;</span><span class="p">(</span><span class="n">cond_struct</span><span class="p">.</span><span class="n">cond</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
		<span class="k">const</span> <span class="k">auto</span> <span class="n">taken_addr</span> <span class="o">=</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">cond_struct</span><span class="p">.</span><span class="n">taken</span><span class="p">;</span>
		<span class="k">const</span> <span class="k">auto</span> <span class="n">not_taken_addr</span> <span class="o">=</span> <span class="n">addr</span> <span class="o">+</span> <span class="n">cond_struct</span><span class="p">.</span><span class="n">not_taken</span><span class="p">;</span>

		<span class="k">switch</span><span class="p">(</span><span class="n">cond</span><span class="p">)</span> 
		<span class="p">{</span>
			<span class="k">case</span> <span class="n">JumpCond</span><span class="o">::</span><span class="n">JMP</span><span class="p">:</span>
				<span class="n">il</span><span class="p">.</span><span class="n">AddInstruction</span><span class="p">(</span><span class="n">il</span><span class="p">.</span><span class="n">Jump</span><span class="p">(</span><span class="n">il</span><span class="p">.</span><span class="n">ConstPointer</span><span class="p">(</span><span class="n">addrSize</span><span class="p">,</span> <span class="n">taken_addr</span><span class="p">)));</span>
				<span class="k">break</span><span class="p">;</span>

			<span class="k">case</span> <span class="n">JumpCond</span><span class="o">::</span><span class="n">JO</span><span class="p">:</span>
				<span class="n">ConditionalJump</span><span class="p">(</span><span class="n">arch</span><span class="p">,</span> <span class="n">il</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">FlagCondition</span><span class="p">(</span><span class="n">LLFC_O</span><span class="p">),</span> <span class="n">addrSize</span><span class="p">,</span> <span class="n">taken_addr</span><span class="p">,</span> <span class="n">not_taken_addr</span><span class="p">);</span>
				<span class="k">break</span><span class="p">;</span>

				<span class="c1">// [...]</span>

			<span class="k">case</span> <span class="n">JumpCond</span><span class="o">::</span><span class="n">JECXZ</span><span class="p">:</span>
				<span class="n">ConditionalJump</span><span class="p">(</span><span class="n">arch</span><span class="p">,</span> <span class="n">il</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">CompareEqual</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">il</span><span class="p">.</span><span class="n">Register</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">XED_REG_ECX</span><span class="p">),</span> <span class="n">il</span><span class="p">.</span><span class="n">Const</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">)),</span> <span class="n">addrSize</span><span class="p">,</span> <span class="n">taken_addr</span><span class="p">,</span> <span class="n">not_taken_addr</span><span class="p">);</span>
				<span class="k">break</span><span class="p">;</span>

			<span class="nl">default:</span>
				<span class="n">il</span><span class="p">.</span><span class="n">AddInstruction</span><span class="p">(</span><span class="n">il</span><span class="p">.</span><span class="n">Breakpoint</span><span class="p">());</span>	
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">else</span>
	<span class="p">{</span>
		<span class="n">il</span><span class="p">.</span><span class="n">AddInstruction</span><span class="p">(</span><span class="n">il</span><span class="p">.</span><span class="n">Breakpoint</span><span class="p">());</span>	
	<span class="p">}</span>

	<span class="k">break</span><span class="p">;</span>
</code></pre></div></div>

<p>After making this modification, the LLIL breakpoint instructions were replaced with LLIL branches, which repaired the control flow of the binary.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>6 @ 0040a02e  [ebp - 0x14 {var_18}].d = ecx
7 @ 0040a031  [ebp + 8 {serial}].d
8 @ 0040a035  breakpoint
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>6 @ 0040a02e  [ebp - 0x14 {var_18}].d = ecx
7 @ 0040a035  if ([ebp + 8 {serial}].d == 0) then 8 @ 0x40a045 else 10 @ 0x40a037
</code></pre></div></div>

<p>This worked far better than I expected it to, resulting in coherent decompilation! It wasn’t perfect by any means, but it was good enough for me to reimplement the algorithm. The trace I’d printed out from Unicorn turned out to be helpful after all, as it gave me values to test against in the steps where the decompilation was ambiguous.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0040</span><span class="n">a025</span>    <span class="kt">int32_t</span> <span class="kr">__stdcall</span> <span class="n">do_check</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">arg1</span> <span class="err">@</span> <span class="n">ecx</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">serial</span><span class="p">)</span>

<span class="mo">0040</span><span class="n">a025</span>        <span class="kt">int32_t</span> <span class="n">ebp</span>
<span class="mo">0040</span><span class="n">a025</span>        <span class="kt">int32_t</span> <span class="n">var_4</span> <span class="o">=</span> <span class="n">ebp</span>
<span class="mo">0040</span><span class="n">a026</span>        <span class="k">struct</span> <span class="n">struct_1</span><span class="o">*</span> <span class="n">ebp_1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">var_4</span>
<span class="mo">0040</span><span class="n">a02b</span>        <span class="kt">void</span><span class="o">*</span> <span class="n">entry_ebx</span>
<span class="mo">0040</span><span class="n">a02b</span>        <span class="kt">void</span><span class="o">*</span> <span class="n">var_28</span> <span class="o">=</span> <span class="n">entry_ebx</span>
<span class="mo">0040</span><span class="n">a02c</span>        <span class="kt">int32_t</span> <span class="n">entry_esi</span>
<span class="mo">0040</span><span class="n">a02c</span>        <span class="kt">int32_t</span> <span class="n">var_2c</span> <span class="o">=</span> <span class="n">entry_esi</span>
<span class="mo">0040</span><span class="n">a02d</span>        <span class="kt">int32_t</span> <span class="n">edi</span>
<span class="mo">0040</span><span class="n">a02d</span>        <span class="kt">int32_t</span> <span class="n">var_30</span> <span class="o">=</span> <span class="n">edi</span>
<span class="mo">0040</span><span class="n">a02d</span>        <span class="kt">int32_t</span><span class="o">*</span> <span class="n">esp_1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">var_30</span>
<span class="mo">0040</span><span class="n">a02e</span>        <span class="kt">int32_t</span> <span class="n">var_18</span> <span class="o">=</span> <span class="n">arg1</span>
<span class="mo">0040</span><span class="n">a035</span>        <span class="kt">int32_t</span> <span class="n">result</span>
<span class="mo">0040</span><span class="n">a035</span>        
<span class="mo">0040</span><span class="n">a035</span>        <span class="k">if</span> <span class="p">(</span><span class="n">serial</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a045</span>            <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040</span><span class="n">a035</span>        <span class="k">else</span>
<span class="mo">0040</span><span class="n">a03f</span>            <span class="n">esp_1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">var_30</span>
<span class="mo">0040</span><span class="n">a03f</span>            
<span class="mo">0040</span><span class="n">a043</span>            <span class="k">if</span> <span class="p">(</span><span class="mh">0x409731</span><span class="p">(</span><span class="n">serial</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x13</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a04c</span>                <span class="kt">int32_t</span> <span class="n">var_8_1</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040</span><span class="n">a053</span>                <span class="kt">int32_t</span> <span class="n">var_c_1</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040</span><span class="n">a05a</span>                <span class="kt">int32_t</span> <span class="n">var_14_1</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040</span><span class="n">a061</span>                <span class="kt">int32_t</span> <span class="n">var_10_1</span> <span class="o">=</span> <span class="mh">0xcafebabe</span>
<span class="mo">0040</span><span class="n">a061</span>                
<span class="mo">0040</span><span class="n">a06f</span>                <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a06f</span>                    <span class="n">sub_40a4b5</span><span class="p">(</span><span class="mh">0x403477</span><span class="p">())</span>
<span class="mo">0040</span><span class="n">a06f</span>                    
<span class="mo">0040</span><span class="n">a080</span>                    <span class="k">if</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a08a</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a080</span>                    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a0ae</span>                        <span class="o">*</span><span class="p">(</span><span class="n">esp_1</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">acc</span> <span class="o">+</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">))</span>
<span class="mo">0040</span><span class="n">a0af</span>                        <span class="o">*</span><span class="p">(</span><span class="n">esp_1</span> <span class="o">-</span> <span class="mi">8</span><span class="p">)</span> <span class="o">=</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">i</span>
<span class="mo">0040</span><span class="n">a0ba</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">i</span> <span class="o">=</span> <span class="n">do_char_hash</span><span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_c</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a0c1</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a0ce</span>                        <span class="kt">int32_t</span> <span class="n">done_yet</span> <span class="o">=</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&amp;</span> <span class="mh">0x80000003</span>
<span class="mo">0040</span><span class="n">a0ce</span>                        
<span class="mo">0040</span><span class="n">a0d3</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">done_yet</span> <span class="n">s</span><span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a0d9</span>                            <span class="n">done_yet</span> <span class="o">=</span> <span class="p">((</span><span class="n">done_yet</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="mh">0xfffffffc</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a0d9</span>                        
<span class="mo">0040</span><span class="n">a0dc</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">done_yet</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a0e6</span>                            <span class="c1">// go to compare-and-multiply</span>
<span class="mo">0040</span><span class="n">a0e6</span>                            <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">2</span>
<span class="mo">0040</span><span class="n">a0dc</span>                        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">!=</span> <span class="mh">0x13</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a10f</span>                            <span class="c1">// if not all chars hashed, resume loop</span>
<span class="mo">0040</span><span class="n">a10f</span>                            <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a0f3</span>                        <span class="k">else</span>
<span class="mo">0040</span><span class="n">a0fe</span>                            <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">3</span>
<span class="mo">0040</span><span class="n">a09a</span>                    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span>  <span class="c1">// state 3: check</span>
<span class="mo">0040</span><span class="n">a178</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a178</span>                            <span class="k">break</span>
<span class="mo">0040</span><span class="n">a178</span>                        
<span class="mo">0040</span><span class="n">a17a</span>                        <span class="o">*</span><span class="p">(</span><span class="n">esp_1</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)</span> <span class="o">=</span> <span class="mi">4</span>
<span class="mo">0040</span><span class="n">a186</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_0</span> <span class="o">=</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_c</span><span class="p">[</span><span class="o">*</span><span class="p">(</span><span class="n">esp_1</span> <span class="o">-</span> <span class="mi">4</span><span class="p">)]</span>
<span class="mo">0040</span><span class="n">a199</span>                        <span class="kt">int32_t</span> <span class="n">check_result</span> <span class="o">=</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_10</span> <span class="o">|</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">i</span> <span class="o">^</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_0</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a19b</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_10</span> <span class="o">=</span> <span class="n">check_result</span>
<span class="mo">0040</span><span class="n">a19b</span>                        
<span class="mo">0040</span><span class="n">a19e</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">check_result</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a1a9</span>                            <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">5</span>  <span class="c1">// wrong</span>
<span class="mo">0040</span><span class="n">a19e</span>                        <span class="k">else</span>
<span class="mo">0040</span><span class="n">a1a0</span>                            <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">4</span>  <span class="c1">// right</span>
<span class="mo">0040</span><span class="n">a11f</span>                    <span class="k">else</span>  <span class="c1">// state 2: get compare val and multiply</span>
<span class="mo">0040</span><span class="n">a124</span>                        <span class="kt">void</span><span class="o">*</span> <span class="n">eax_11</span>
<span class="mo">0040</span><span class="n">a124</span>                        <span class="kt">int32_t</span> <span class="n">edx_1</span>
<span class="mo">0040</span><span class="n">a124</span>                        <span class="n">edx_1</span><span class="o">:</span><span class="n">eax_11</span> <span class="o">=</span> <span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a12a</span>                        <span class="n">unimplemented</span>  <span class="p">{</span><span class="n">sar</span> <span class="n">eax</span><span class="p">,</span> <span class="mh">0x2</span><span class="p">}</span>
<span class="mo">0040</span><span class="n">a12e</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_8</span> <span class="o">=</span> <span class="p">((</span><span class="n">eax_11</span> <span class="o">+</span> <span class="p">(</span><span class="n">edx_1</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">))</span> <span class="n">s</span><span class="o">&gt;&gt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a13a</span>                        <span class="c1">// save compare_val into ebp_1 - 0x1c?</span>
<span class="mo">0040</span><span class="n">a13a</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_4</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_c</span> <span class="o">+</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_8</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">))</span>
<span class="mo">0040</span><span class="n">a151</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_10</span> <span class="o">|=</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">i</span> <span class="o">^</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_4</span>
<span class="mo">0040</span><span class="n">a168</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">i</span> <span class="o">=</span> <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">*</span> <span class="mh">0x112233</span> <span class="o">-</span> <span class="mh">0x35014542</span>
<span class="mo">0040</span><span class="n">a16b</span>                        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a16b</span>                
<span class="mo">0040</span><span class="n">a1b6</span>                <span class="k">if</span> <span class="p">(</span><span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">serial_char</span> <span class="o">!=</span> <span class="mi">4</span><span class="p">)</span>
<span class="mo">0040</span><span class="n">a1be</span>                    <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040</span><span class="n">a1b6</span>                <span class="k">else</span>
<span class="mo">0040</span><span class="n">a1b8</span>                    <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">0040</span><span class="n">a043</span>            <span class="k">else</span>
<span class="mo">0040</span><span class="n">a045</span>                <span class="n">result</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0040</span><span class="n">a045</span>        
<span class="mo">0040</span><span class="n">a1c7</span>        <span class="o">*</span><span class="n">esp_1</span>
<span class="mo">0040</span><span class="n">a1c7</span>        <span class="n">esp_1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="mo">0040</span><span class="n">a1c8</span>        <span class="n">esp_1</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="mo">0040</span><span class="n">a1ca</span>        <span class="n">ebp_1</span><span class="o">-&gt;</span><span class="n">field_20</span>
<span class="mo">0040</span><span class="n">a1cb</span>        <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<h3 id="reimplementation">Reimplementation</h3>

<p>The hash was a weird ad-hoc algorithm that made different decisions about how to process each character depending on certain ranges of values that it fell into. This means that the initial approach I took with Unicorn would never have worked very well on its own, as the flow of execution is completely different depending on whether each of the characters in the serial are letters, numbers, special characters and so on. There’s even a special case that specifically handles the character <code class="language-plaintext highlighter-rouge">0x30</code>.</p>

<p>The presence of all the weird conditionals also means that the hash can’t be cracked using constraint solvers. (My initial reimplementation of the algorithm was in Python with the intent of solving it with z3, which resulted in confusing error statements like <code class="language-plaintext highlighter-rouge">Symbolic expressions cannot be cast to concrete Boolean values.</code>) Luckily, only 4 characters are hashed at a time, so I rewrote my implementation in C and bruteforced each group of characters.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span>
<span class="kt">uint32_t</span> <span class="nf">hash_round</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">acc</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">c</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">((</span><span class="n">acc</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</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">acc</span> <span class="o">^</span> <span class="n">c</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mh">0x80000000</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span><span class="p">((</span><span class="n">c</span> <span class="o">&gt;=</span> <span class="mh">0x61</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">c</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="mh">0x61</span> <span class="o">+</span> <span class="mi">26</span><span class="p">)))</span> <span class="p">{</span>
                <span class="n">acc</span> <span class="o">-=</span> <span class="n">c</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">else</span> <span class="k">if</span><span class="p">((</span><span class="n">c</span> <span class="o">&lt;</span> <span class="mh">0x41</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">c</span> <span class="o">&gt;</span> <span class="p">(</span><span class="mh">0x41</span> <span class="o">+</span> <span class="mi">26</span><span class="p">)))</span> <span class="p">{</span>
                <span class="n">acc</span> <span class="o">*=</span> <span class="mi">9</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">else</span> <span class="p">{</span>
                <span class="n">acc</span> <span class="o">+=</span> <span class="n">c</span><span class="p">;</span>
                <span class="k">if</span><span class="p">((</span><span class="n">acc</span> <span class="o">&amp;</span> <span class="mh">0x100</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">acc</span> <span class="o">^=</span> <span class="mh">0x13371337</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">&gt;</span> <span class="mh">0x60</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">acc</span> <span class="o">=</span> <span class="p">(</span><span class="n">c</span> <span class="o">*</span> <span class="mh">0x21</span><span class="p">)</span> <span class="o">^</span> <span class="p">(</span><span class="n">acc</span> <span class="o">+</span> <span class="mh">0xdeadbeef</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">acc</span> <span class="o">+=</span> <span class="mh">0xdeadbeef</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">&gt;=</span> <span class="mh">0x40</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">acc</span> <span class="o">=</span> <span class="p">((</span><span class="n">acc</span> <span class="o">&gt;&gt;</span> <span class="mi">5</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">acc</span> <span class="o">&lt;&lt;</span> <span class="mh">0x1b</span><span class="p">))</span> <span class="o">^</span> <span class="mh">0x87654321</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="k">else</span> <span class="p">{</span>
                <span class="n">acc</span> <span class="o">=</span> <span class="p">((</span><span class="n">acc</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">acc</span> <span class="o">&gt;&gt;</span> <span class="mh">0x1d</span><span class="p">))</span> <span class="o">+</span> <span class="mh">0x12345678</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="k">if</span><span class="p">((</span><span class="n">c</span> <span class="o">&amp;</span> <span class="mi">2</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">acc</span> <span class="o">-=</span> <span class="p">(</span><span class="n">c</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">);</span>
            <span class="k">if</span><span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="mh">0x30</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">acc</span> <span class="o">|=</span> <span class="mh">0xf0f0f0f0</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">acc</span> <span class="o">^=</span> <span class="mh">0x55aa55aa</span><span class="p">;</span>
            <span class="n">acc</span> <span class="o">+=</span> <span class="n">c</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">uint32_t</span> <span class="n">mod_5</span> <span class="o">=</span> <span class="p">(</span><span class="n">c</span> <span class="o">%</span> <span class="mi">5</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span>
    <span class="k">for</span><span class="p">(</span><span class="kt">uint32_t</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">mod_5</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">acc</span> <span class="o">&amp;</span> <span class="mh">0x80000000</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">acc</span> <span class="o">&lt;&lt;=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">acc</span> <span class="o">=</span> <span class="p">(</span><span class="n">acc</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x4c11db7</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="kt">uint32_t</span> <span class="n">mask</span> <span class="o">=</span> <span class="n">i</span> <span class="o">&amp;</span> <span class="mh">0x80000001</span><span class="p">;</span>
        <span class="k">if</span><span class="p">(</span><span class="n">mask</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">acc</span> <span class="o">+=</span> <span class="n">i</span> <span class="o">*</span> <span class="mh">0xa</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">acc</span> <span class="o">^=</span> <span class="n">c</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">uint32_t</span> <span class="n">count</span> <span class="o">=</span> <span class="n">acc</span><span class="p">;</span>
    <span class="n">count</span> <span class="o">^=</span> <span class="p">(</span><span class="n">count</span> <span class="o">&gt;&gt;</span> <span class="mh">0x10</span><span class="p">);</span>
    <span class="n">count</span> <span class="o">^=</span> <span class="p">(</span><span class="n">count</span> <span class="o">&gt;&gt;</span> <span class="mh">0x8</span><span class="p">);</span>

    <span class="k">if</span><span class="p">((</span><span class="n">count</span> <span class="o">&amp;</span> <span class="mh">0xf</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">acc</span> <span class="o">=</span> <span class="o">~</span><span class="n">acc</span><span class="p">;</span>
    <span class="p">}</span>

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

<span class="kt">uint32_t</span> <span class="nf">hash</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">pos</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">acc</span> <span class="o">=</span> <span class="mh">0xcafebabe</span> <span class="o">+</span> <span class="mh">0x112233</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">pos</span><span class="p">;</span>
    <span class="k">for</span><span class="p">(</span><span class="kt">uint32_t</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">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">acc</span> <span class="o">=</span> <span class="n">hash_round</span><span class="p">(</span><span class="n">acc</span><span class="p">,</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>

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

<span class="kt">int</span> <span class="nf">check_val</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">expected</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">pos</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span><span class="p">(</span><span class="kt">char</span> <span class="n">i</span> <span class="o">=</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x7f</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">//printf("trying %x\n", i);</span>
        <span class="k">for</span><span class="p">(</span><span class="kt">char</span> <span class="n">j</span> <span class="o">=</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mh">0x7f</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">for</span><span class="p">(</span><span class="kt">char</span> <span class="n">k</span> <span class="o">=</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="mh">0x7f</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">for</span><span class="p">(</span><span class="kt">char</span> <span class="n">l</span> <span class="o">=</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">l</span> <span class="o">&lt;</span> <span class="mh">0x7f</span><span class="p">;</span> <span class="n">l</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kt">char</span> <span class="n">to_try</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">l</span><span class="p">};</span>
                    <span class="kt">uint32_t</span> <span class="n">acc</span> <span class="o">=</span> <span class="n">hash</span><span class="p">(</span><span class="n">to_try</span><span class="p">,</span> <span class="n">pos</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span>
                    <span class="k">if</span><span class="p">(</span><span class="n">acc</span> <span class="o">==</span> <span class="n">expected</span><span class="p">)</span> <span class="p">{</span>
                        <span class="n">printf</span><span class="p">(</span><span class="s">"%c%c%c%c"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">l</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="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">compares</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mh">0x865dbb47</span><span class="p">,</span> <span class="mh">0xa6eb190</span><span class="p">,</span> <span class="mh">0x20476c33</span><span class="p">,</span> <span class="mh">0x1c8a7693</span><span class="p">,</span> <span class="mh">0x59febdfb</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">4</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">check_val</span><span class="p">(</span><span class="n">compares</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">i</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="o">-</span><span class="mi">1</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">check_val</span><span class="p">(</span><span class="n">compares</span><span class="p">[</span><span class="mi">4</span><span class="p">],</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</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>This gets us the serial: <code class="language-plaintext highlighter-rouge">D3FE-A7ED-BAAD-C0D3</code>. Entering it into the crackme, we finally have our flag!</p>

<p><img src="/assets/images/crackmesone/crackme9_flag.png" alt="" /></p>

<p><code class="language-plaintext highlighter-rouge">CMO{byp4ss3d_f4tm1k3_2o26}</code></p>

<p>Note: After the CTF, I found out that the control flow obfuscation came from an obfuscator called <a href="https://github.com/Fatmike-GH/Nanomites">Nanomites</a>, the source code of which was on the challenge author’s GitHub long before the CTF started. I’ll have to remember to check for that next time.</p>]]></content><author><name></name></author><category term="ctf" /><summary type="html"><![CDATA[Reversing a challenge with breakpoint-based obfuscation]]></summary></entry><entry><title type="html">Experimenting with Binary Ninja IL rewriting</title><link href="/ctf/2026/01/25/obfuscators-riddle.html" rel="alternate" type="text/html" title="Experimenting with Binary Ninja IL rewriting" /><published>2026-01-25T14:10:00+00:00</published><updated>2026-01-25T14:10:00+00:00</updated><id>/ctf/2026/01/25/obfuscators-riddle</id><content type="html" xml:base="/ctf/2026/01/25/obfuscators-riddle.html"><![CDATA[<p>Every year after Flare-On ends, I tell myself that I should really take the time to learn more about deobfuscation. One of the projects I’ve been meaning to do for a while is to figure out how to use Binary Ninja’s <a href="https://docs.binary.ninja/dev/bnil-modifying.html">IL modification API</a>, so I decided to pick out a challenge from crackmes.one and see if I could improve the decompilation by modifying ILs.</p>

<p>The crackme I used was <a href="https://crackmes.one/crackme/684917e72b84be7ea77437c1">The Obfuscator’s Riddle</a>, an obfuscated binary of medium difficulty. My goal was to solve the crackme entirely statically by using IL modification to produce a clean decompilation. Unfortunately, I didn’t succeed at doing this, but there are so few writeups on IL modification that it’s still probably helpful to document everything that I tried. Also, I want to come back to this project at some point, and if I write it all down in a blog post I’ll at least have a prayer of remembering what I was doing.</p>

<p>I did manage to deobfuscate the binary, but it was through binary patching, not IL modification. The patching script and the solution to the crackme are included at the end ofthe writeup.</p>

<h2 id="challenge-overview">Challenge Overview</h2>

<p>As usual for these types of challenges, we’re prompted for a password:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">140001081</span>        <span class="n">write_console</span><span class="p">(</span><span class="s">"Enter password: "</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mi">140001091</span>        <span class="n">int128_t</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">o</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="mi">140001099</span>        <span class="kt">uint32_t</span> <span class="n">numberOfCharsRead</span><span class="p">[</span><span class="mh">0x6</span><span class="p">]</span>
<span class="mi">140001099</span>        <span class="n">numberOfCharsRead</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="mi">1400010</span><span class="n">ad</span>        <span class="kt">uint64_t</span> <span class="n">rbx</span> <span class="o">=</span> <span class="mh">0x10</span>
<span class="mi">1400010</span><span class="n">c1</span>        <span class="n">ReadConsoleA</span><span class="p">(</span><span class="n">hConsoleInput</span><span class="o">:</span> <span class="n">GetStdHandle</span><span class="p">(</span><span class="n">nStdHandle</span><span class="o">:</span> <span class="n">STD_INPUT_HANDLE</span><span class="p">),</span> 
<span class="mi">1400010</span><span class="n">c1</span>            <span class="n">lpBuffer</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">buffer</span><span class="p">,</span> <span class="n">nNumberOfCharsToRead</span><span class="o">:</span> <span class="mh">0x10</span><span class="p">,</span> 
<span class="mi">1400010</span><span class="n">c1</span>            <span class="n">lpNumberOfCharsRead</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">numberOfCharsRead</span><span class="p">,</span> <span class="n">pInputControl</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">)</span>
</code></pre></div></div>

<p>However, after the password prompt we’re faced with a long switch statement that has 0xef different cases. At the end of each case, the variable <code class="language-plaintext highlighter-rouge">rax_2</code> is set to a different value between 0 and 0xef, followed by a jump to a “dispatcher” block at <code class="language-plaintext highlighter-rouge">0x31b8</code>. Weirdly, Binary Ninja didn’t decompile the switch case at first, but rebasing to a base address of 0 fixed it.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>            <span class="k">switch</span> <span class="p">(</span><span class="n">rcx_5</span><span class="p">)</span>
<span class="mo">000011</span><span class="n">ca</span>                <span class="k">case</span> <span class="mi">1</span>
<span class="mo">000011</span><span class="n">ca</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xbe</span>
<span class="mo">000011</span><span class="n">cc</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="mo">0000211</span><span class="n">b</span>                <span class="k">case</span> <span class="mi">3</span>
<span class="mo">0000211</span><span class="n">b</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xb5</span>
<span class="mo">0000211</span><span class="n">d</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="mo">00001</span><span class="n">d9c</span>                <span class="k">case</span> <span class="mi">4</span>
<span class="mo">00001</span><span class="n">d9c</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xe7</span>
<span class="mo">00001</span><span class="n">d9e</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="mo">00002</span><span class="n">d8e</span>                <span class="k">case</span> <span class="mi">5</span>
<span class="mo">00002</span><span class="n">d8e</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0x2b</span>
<span class="mo">00002</span><span class="n">d90</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="mf">000015e8</span>                <span class="k">case</span> <span class="mi">6</span>
<span class="mf">000015e8</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0x15</span>
<span class="mo">000015</span><span class="n">ea</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
</code></pre></div></div>

<p>This is a classic case of control flow flattening. <code class="language-plaintext highlighter-rouge">rax_2</code> stores a state variable that’s used as an offset into a jump table, and the dispatcher at <code class="language-plaintext highlighter-rouge">0x31b8</code> jumps to the address stored at <code class="language-plaintext highlighter-rouge">[jumptable base address] + 4 * rax_2</code>. Since the decompiler doesn’t understand how the state variable is used by the dispatcher to calculate where to jump next, it can’t reconstruct the control flow graph of the binary, leading to a “flattened” view where every basic block ends with a jump to the dispatcher. (If you’re unfamiliar with control flow flattening, there are a lot of detailed writeups on different forms of it, like this one from  <a href="https://github.com/obfuscator-llvm/obfuscator/wiki/Control-Flow-Flattening">OLLVM</a> or this one from <a href="https://tigress.cs.arizona.edu/transformPage/docs/flatten/index.html">Tigress</a>.)</p>

<h2 id="binary-ninja-il-modification">Binary Ninja IL Modification</h2>

<h3 id="patching-with-jump-instructions">Patching with JUMP instructions</h3>

<p>My first thought was to look through the MLIL for jumps to the dispatcher block, then replace them with direct jumps to the target address that would be calculated within the dispatcher block based on the state variable. Finding the jumps was easy, as the jumps to the dispatcher block followed a predictable pattern of setting the <code class="language-plaintext highlighter-rouge">al</code> register to the next state value, followed by a jump to the dispatcher:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>70 @ 000011b5  rax_2.al = 0x2c
71 @ 000011b7  goto 1026 @ 0x31b8
</code></pre></div></div>

<p>This made it relatively easy to write a function to check whether a set of two MLIL instructions corresponded to a CFF-obfuscated jump.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">):</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">operands</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="n">second</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="n">DISPATCHER_LOC</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">!=</span> <span class="n">MediumLevelILSetVarField</span> <span class="ow">or</span> <span class="nb">type</span><span class="p">(</span><span class="n">second</span><span class="p">)</span> <span class="o">!=</span> <span class="n">MediumLevelILGoto</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">!=</span> <span class="n">Variable</span> <span class="ow">or</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="o">!=</span> <span class="n">MediumLevelILConst</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>
</code></pre></div></div>

<p>I then tried searching through the MLIL to find all instances of the CFF pattern, using the <code class="language-plaintext highlighter-rouge">replace_expr</code> function to replace the jumps to the dispatcher with jumps to the target address. (Like a lot of the other IL modification functions, the instruction to be replaced by <code class="language-plaintext highlighter-rouge">replace_expr</code> is specified by its “expression index”. Notably, the expression index of an instruction is <em>not</em> the index where the instruction appears in the IL: the first MLIL instruction in the crackme binary has expression index 4, not 0. The expression indices 0 through 3 are actually assigned to the the operands of the first instruction, which are themselves expressions.)</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">block</span> <span class="ow">in</span> <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">basic_blocks</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="nb">len</span><span class="p">(</span><span class="n">block</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
        <span class="n">first</span> <span class="o">=</span> <span class="n">block</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">second</span> <span class="o">=</span> <span class="n">block</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="k">if</span> <span class="n">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">):</span>
            <span class="n">idx</span> <span class="o">=</span> <span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
            <span class="n">jump_target</span> <span class="o">=</span> <span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="n">bv</span><span class="p">.</span><span class="n">read_int</span><span class="p">(</span><span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">idx</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
            <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">'Found jump offset </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">idx</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value</span><span class="p">)</span><span class="si">}</span><span class="s"> at address </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

            <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">replace_expr</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">expr_index</span><span class="p">,</span> <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">jump</span><span class="p">(</span><span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">const</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">jump_target</span><span class="p">)))</span>
            <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">'Replaced jump at </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s"> with jump to </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">jump_target</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
</code></pre></div></div>

<p>I then added the modification function as a step in the analysis workflow so that it would be called whenever a function was analyzed. Most of the examples of MLIL modification suggest inserting the modification step right after <code class="language-plaintext highlighter-rouge">core.function.generateMediumLevelIL</code>, but when I tried to do this, I got an exception in <code class="language-plaintext highlighter-rouge">core.function.analyzeIndirectBranches</code> step with the message <code class="language-plaintext highlighter-rouge">invalid access to LLIL instruction</code>. I’m not sure why this happened, but I’m guessing it’s because the new MLIL instructions inserted into the function aren’t actually backed by any underlying LLIL instructions. Inserting the modification function after <code class="language-plaintext highlighter-rouge">core.function.analyzeIndirectBranches</code> fixed the issue.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">wf</span> <span class="o">=</span> <span class="n">Workflow</span><span class="p">(</span><span class="s">"core.function.metaAnalysis"</span><span class="p">).</span><span class="n">clone</span><span class="p">(</span><span class="s">"core.function.metaAnalysis"</span><span class="p">)</span>

<span class="n">wf</span><span class="p">.</span><span class="n">register_activity</span><span class="p">(</span><span class="n">Activity</span><span class="p">(</span>
    <span class="n">configuration</span><span class="o">=</span><span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">({</span>
        <span class="s">"name"</span><span class="p">:</span> <span class="s">"extension.fix_cff"</span><span class="p">,</span>
        <span class="s">"title"</span><span class="p">:</span> <span class="s">"Fix CFF"</span><span class="p">,</span>
        <span class="s">"description"</span><span class="p">:</span> <span class="s">"Replace control flow flattening instructions with direct jumps."</span><span class="p">,</span>
        <span class="s">"eligibility"</span><span class="p">:</span> <span class="p">{</span>
            <span class="s">"auto"</span><span class="p">:</span> <span class="p">{</span>
                <span class="s">"default"</span><span class="p">:</span> <span class="bp">True</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}),</span>
    <span class="n">action</span><span class="o">=</span><span class="n">fix_cff</span>
<span class="p">))</span>

<span class="n">wf</span><span class="p">.</span><span class="n">insert_after</span><span class="p">(</span><span class="s">"core.function.analyzeIndirectBranches"</span><span class="p">,</span> <span class="p">[</span>
    <span class="s">"extension.fix_cff"</span>
<span class="p">])</span>

<span class="n">wf</span><span class="p">.</span><span class="n">register</span><span class="p">()</span>
</code></pre></div></div>

<p>In general, it seems like IL modification is somewhat experimental, with very little error handling when things go wrong. I ran into some difficult-to-diagnose errors, especially when I tried modifying LLIL rather than MLIL - sometimes Binary Ninja would segfault, and other times the analysis workflow appeared to get stuck in an infinite loop of analyzing and reanalyzing. My guess is a lot of these problems have to do with the fact that my IL modifications resulted in major changes to the control flow, and that IL modification might work better if I used it to clean up data-level obfuscation (e.g., opaque constants) as opposed to control flow obfuscation.</p>

<p>This was the working script that I ended up with:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DISPATCHER_LOC</span> <span class="o">=</span> <span class="mi">380</span>
<span class="n">JUMPTABLE_ADDR</span> <span class="o">=</span> <span class="mh">0x418c</span>

<span class="k">def</span> <span class="nf">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">):</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">operands</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="n">second</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="n">DISPATCHER_LOC</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">!=</span> <span class="n">MediumLevelILSetVarField</span> <span class="ow">or</span> <span class="nb">type</span><span class="p">(</span><span class="n">second</span><span class="p">)</span> <span class="o">!=</span> <span class="n">MediumLevelILGoto</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">!=</span> <span class="n">Variable</span> <span class="ow">or</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="o">!=</span> <span class="n">MediumLevelILConst</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>
 
<span class="k">def</span> <span class="nf">fix_cff</span><span class="p">(</span><span class="n">analysis_context</span><span class="p">:</span> <span class="n">AnalysisContext</span><span class="p">):</span>
    <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">"Starting CFF replacement for function </span><span class="si">{</span><span class="n">analysis_context</span><span class="p">.</span><span class="n">function</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">block</span> <span class="ow">in</span> <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">basic_blocks</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="nb">len</span><span class="p">(</span><span class="n">block</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
            <span class="n">first</span> <span class="o">=</span> <span class="n">block</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
            <span class="n">second</span> <span class="o">=</span> <span class="n">block</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="k">if</span> <span class="n">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">):</span>
                <span class="n">idx</span> <span class="o">=</span> <span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
                <span class="n">jump_target</span> <span class="o">=</span> <span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="n">bv</span><span class="p">.</span><span class="n">read_int</span><span class="p">(</span><span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">idx</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
                <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">'Found jump offset </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">idx</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value</span><span class="p">)</span><span class="si">}</span><span class="s"> at address </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

                <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">replace_expr</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">expr_index</span><span class="p">,</span> <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">jump</span><span class="p">(</span><span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">const</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">jump_target</span><span class="p">)))</span>
                <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">'Replaced jump at </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s"> with jump to </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">jump_target</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

    <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">finalize</span><span class="p">()</span>
    <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span><span class="p">.</span><span class="n">generate_ssa_form</span><span class="p">()</span>


<span class="n">wf</span> <span class="o">=</span> <span class="n">Workflow</span><span class="p">(</span><span class="s">"core.function.metaAnalysis"</span><span class="p">).</span><span class="n">clone</span><span class="p">(</span><span class="s">"core.function.metaAnalysis"</span><span class="p">)</span>

<span class="n">wf</span><span class="p">.</span><span class="n">register_activity</span><span class="p">(</span><span class="n">Activity</span><span class="p">(</span>
    <span class="n">configuration</span><span class="o">=</span><span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">({</span>
        <span class="s">"name"</span><span class="p">:</span> <span class="s">"extension.fix_cff"</span><span class="p">,</span>
        <span class="s">"title"</span><span class="p">:</span> <span class="s">"Fix CFF"</span><span class="p">,</span>
        <span class="s">"description"</span><span class="p">:</span> <span class="s">"Replace control flow flattening instructions with direct jumps."</span><span class="p">,</span>
        <span class="s">"eligibility"</span><span class="p">:</span> <span class="p">{</span>
            <span class="s">"auto"</span><span class="p">:</span> <span class="p">{</span>
                <span class="s">"default"</span><span class="p">:</span> <span class="bp">True</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}),</span>
    <span class="n">action</span><span class="o">=</span><span class="n">fix_cff</span>
<span class="p">))</span>

<span class="n">wf</span><span class="p">.</span><span class="n">insert_after</span><span class="p">(</span><span class="s">"core.function.analyzeIndirectBranches"</span><span class="p">,</span> <span class="p">[</span>
    <span class="s">"extension.fix_cff"</span>
<span class="p">])</span>

<span class="n">wf</span><span class="p">.</span><span class="n">register</span><span class="p">()</span>
</code></pre></div></div>

<p>While this script did, in fact, successfully replace the jumps to the dispatcher with jumps to the target address, it didn’t produce an unflattened decompilation. The long switch case was still there, and it was full of jumps to hard-coded addresses, e.g, <code class="language-plaintext highlighter-rouge">jump(0x2209)</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>            <span class="k">switch</span> <span class="p">(</span><span class="n">jump_table_14000418c</span><span class="p">[</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">rcx_5</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)])</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                <span class="k">case</span> <span class="mh">0xffffcff2</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                    <span class="k">continue</span>
<span class="mo">000011</span><span class="n">a0</span>                <span class="k">case</span> <span class="mh">0xffffd014</span>
<span class="mo">000011</span><span class="n">a0</span>                    <span class="n">rax_2</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">000031</span><span class="n">b8</span>                <span class="n">label_31b8</span><span class="p">:</span>
<span class="mo">000031</span><span class="n">b8</span>                    <span class="n">rcx_4</span> <span class="o">=</span> <span class="n">rbp_1</span>
<span class="mo">000031</span><span class="n">ba</span>                    <span class="n">rdx_1</span> <span class="o">=</span> <span class="n">r14_1</span>
<span class="mo">000031</span><span class="n">bd</span>                    <span class="k">continue</span>
<span class="mo">000031</span><span class="n">bd</span>                    <span class="k">continue</span>
<span class="mo">000011</span><span class="n">a7</span>                <span class="k">case</span> <span class="mh">0xffffd01b</span>
<span class="mo">000011</span><span class="n">a7</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xe8</span>
<span class="mo">00003214</span>                    <span class="n">jump</span><span class="p">(</span><span class="mh">0x2209</span><span class="p">)</span>
<span class="mo">000011</span><span class="n">ae</span>                <span class="k">case</span> <span class="mh">0xffffd022</span>
<span class="mo">000011</span><span class="n">ae</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0x33</span>
<span class="mo">00003214</span>                    <span class="n">jump</span><span class="p">(</span><span class="mh">0x14db</span><span class="p">)</span>
<span class="mo">000011</span><span class="n">b5</span>                <span class="k">case</span> <span class="mh">0xffffd029</span>
</code></pre></div></div>

<p>As it turns out, the primary instructions used in MLIL control flow are not <code class="language-plaintext highlighter-rouge">JUMP</code> instructions, but <code class="language-plaintext highlighter-rouge">GOTO</code> instructions. A <code class="language-plaintext highlighter-rouge">JUMP</code> instruction targets a specific memory address, whereas a <code class="language-plaintext highlighter-rouge">GOTO</code> targets an MLIL expression index. Since the documentation on IL modification mentioned that adding new <code class="language-plaintext highlighter-rouge">GOTO</code> expressions can be more complicated than adding other types of IL instructiions, I had hoped that it would be possible to get a good result by using a <code class="language-plaintext highlighter-rouge">JUMP</code> in place of a <code class="language-plaintext highlighter-rouge">GOTO</code>, but apparently not.</p>

<h3 id="patching-with-goto-instructions">Patching with GOTO instructions</h3>

<h4 id="labels">Labels</h4>

<p>The Binary Ninja documentation gives the following explanation of how to add new <code class="language-plaintext highlighter-rouge">GOTO</code> IL instructions:</p>

<blockquote>
  <p>When you are trying to insert *LIL_GOTO, *LIL_IF, and *LIL_JUMP_TO instructions, you will need to specify the IL destination as a *LevelILLabel. Your destination must be a properly marked label, which is actually rather tricky to obtain. This is because there is currently no way to get a label for already-emitted IL Instructions, so if you want to modify the control flow of a function, you will need to do a <a href="https://docs.binary.ninja/dev/bnil-modifying.html#adding-instructions-and-replacing-multiple-instructions-copy-transformation">Copy Transformation</a> as described above. This may change in future versions.</p>
</blockquote>

<p>I was a little confused about what exactly this meant, but I eventually came across <a href="https://github.com/Vector35/binaryninja-api/issues/7189">this GitHub issue</a> that clarified things slightly:</p>

<blockquote>
  <p>The general idea is that you need to mark labels at the start of basic blocks and have the goto target those labels. If the goto is emitted before the label though, what do you do? In that case, make the label ahead of time and mark it when you get to the block.</p>
</blockquote>

<p>Unfortunately, there aren’t many examples that demonstrate the correct use of labels, but after some experimenting I was eventually able to figure out the following:</p>

<ul>
  <li>IL basic blocks are constructed by emitting one instruction at a time. It’s not possible to insert new IL instructions at arbitrary locations in a basic block, which is why any modification more complicated than replacing one IL instruction with one other IL instruction requires a completely new basic block to be constructed.</li>
  <li>Any IL instruction that can be targeted by a direct branch (<code class="language-plaintext highlighter-rouge">GOTO</code>, <code class="language-plaintext highlighter-rouge">JUMP_TO</code>) or an indirect branch (<code class="language-plaintext highlighter-rouge">IF</code>) needs to have a “label” attached to it. When a branch instruction is constructed, the label needs to be passed to it to specify the destination of the branch.</li>
  <li>Labels need to be “emitted” just like instructions. You can’t attach a label to an arbitrary IL instruction, the label has to be attached to the instruction <em>at the same time the instruction is being emitted.</em></li>
  <li>It’s possible to construct a label without immediately specifying the location that the label points to. The <code class="language-plaintext highlighter-rouge">mark_label()</code> function associates a label with a particular IL location, and it can be called anytime after a label is created. When <code class="language-plaintext highlighter-rouge">mark_label()</code> is called on a label, the label points to the next IL instruction to be emitted.</li>
  <li>What happens if the branch instruction comes before the branch target in the basic block? Since the target instruction hasn’t been emitted yet, there’s no label that can be used to construct the branch instruction. In this case, you need to create a new label without marking it, then keep track of that label and call <code class="language-plaintext highlighter-rouge">mark_label()</code> on it later when the target instruction is emitted.</li>
</ul>

<p>According to the documentation, when copying an IL function, we’re supposed to be able to use the function <code class="language-plaintext highlighter-rouge">MediumLevelILFunction.get_label_for_source_instruction()</code> to obtain a label that corresponds to the start of a basic block. In practice, however, this function often returned <code class="language-plaintext highlighter-rouge">None</code> without giving me much indication as to why. I ultimately didn’t end up using <code class="language-plaintext highlighter-rouge">get_label_for_source_instruction()</code> in my script.</p>

<h4 id="writing-the-replacement-script">Writing the replacement script</h4>

<p>An MLIL <code class="language-plaintext highlighter-rouge">GOTO</code> expression takes a label for a basic block as its target, but a jump table is just a list of memory addresses. In order to figure out the equivalent <code class="language-plaintext highlighter-rouge">GOTO</code> instruction to construct for each jump in the jump table, I needed to figure out which MLIL instructions were present at each of the targeted addresses. Since the dispatcher is represented in MLIL as a long switch case targeting each location in the jump table, I figured I could construct a map from addresses to MLIL just by finding the target address of each case in the switch statement.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">blocks</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">dispatcher</span> <span class="o">=</span> <span class="n">old_func</span><span class="p">.</span><span class="n">get_basic_block_at</span><span class="p">(</span><span class="n">DISPATCHER_LOC</span><span class="p">)</span>
<span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">dispatcher</span><span class="p">.</span><span class="n">outgoing_edges</span><span class="p">:</span>
    <span class="n">insn</span> <span class="o">=</span> <span class="n">e</span><span class="p">.</span><span class="n">target</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">blocks</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">expr_index</span>
</code></pre></div></div>

<p>However, in my initial attempt at writing the script I ran into an issue where some of the jump target addresses weren’t the addresses of any MLIL instructions. This has to do with how addresses get assigned to more complex MLIL instructions like <code class="language-plaintext highlighter-rouge">if</code>/<code class="language-plaintext highlighter-rouge">then</code>/<code class="language-plaintext highlighter-rouge">else</code>. For instance, the following MLIL instruction is assigned an address of <code class="language-plaintext highlighter-rouge">0x12a9</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1542 @ 000012a9  if (rdx_4 == i_23) then 1650 @ 0x2db2 else 1652 @ 0x12af
</code></pre></div></div>

<p>However, looking at the disassembly that was lifted into this MLIL instruction, it actually begins at <code class="language-plaintext highlighter-rouge">0x12a7</code>. If a jump table were to jump there, it would jump to <code class="language-plaintext highlighter-rouge">0x12a7</code>, not <code class="language-plaintext highlighter-rouge">0x12a9</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>000012a7  cmp     edx, ecx
000012a9  je      0x2dad
</code></pre></div></div>

<p>The issue here is that an <code class="language-plaintext highlighter-rouge">if</code>/<code class="language-plaintext highlighter-rouge">then</code>/<code class="language-plaintext highlighter-rouge">else</code> MLIL instruction is lifted from two instructions: a comparison and a branch. The comparison comes before the branch, but it’s the address of the branch instruction that’s considered to be the “address” of the MLIL instruction. More generally, MLIL instructions can be constructed out of complicated trees of expressions and sub-expressions, and the underlying disassembly behind those instructions can come from completely different locations in memory. As a result of this, it doesn’t necessarily make sense to talk about “the address” of an MLIL instruction, so the approach I took to figure out jump targets probably wasn’t a very good one.</p>

<p>The hacky workaround I used in my script was to iterate through the operands of each of the MLIL instructions and associate their addresses with the MLIL instruction as well. In the case of the comparison above, for example, its first operand is the <code class="language-plaintext highlighter-rouge">MediumLevelILCmpE</code> expression <code class="language-plaintext highlighter-rouge">rdx_4 == i_23</code>. Since it’s lifted from the <code class="language-plaintext highlighter-rouge">cmp edx, ecx</code> instruction, its address is <code class="language-plaintext highlighter-rouge">0x12a7</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mlil_addresses</span> <span class="o">=</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="nb">len</span><span class="p">(</span><span class="n">old_func</span><span class="p">)):</span>
    <span class="n">insn</span> <span class="o">=</span> <span class="n">old_func</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mlil_addresses</span><span class="p">:</span>
        <span class="n">mlil_addresses</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span>
    <span class="k">for</span> <span class="n">op</span> <span class="ow">in</span> <span class="n">insn</span><span class="p">.</span><span class="n">operands</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">op</span><span class="p">.</span><span class="n">address</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mlil_addresses</span><span class="p">:</span>
                <span class="n">mlil_addresses</span><span class="p">[</span><span class="n">op</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="k">pass</span>
</code></pre></div></div>

<p>Having (mostly) associated the MLIL instructions with their addresses, it was then possible to construct a new IL function. The jumps to the dispatcher were replaced with GOTO instructions that pointed to the jump target, and all other MLIL instructions were copied over unchanged. While copying each instruction, I maintained a dictionary called <code class="language-plaintext highlighter-rouge">labels</code> that associated each label with the memory address it targeted.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">dispatcher</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">expr_index</span><span class="p">):</span>
    <span class="n">idx</span> <span class="o">=</span> <span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
    <span class="n">jump_target</span> <span class="o">=</span> <span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="n">bv</span><span class="p">.</span><span class="n">read_int</span><span class="p">(</span><span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">idx</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">jump_target</span> <span class="ow">in</span> <span class="n">mlil_addresses</span><span class="p">:</span>
        <span class="n">jump_target</span> <span class="o">=</span> <span class="n">mlil_addresses</span><span class="p">[</span><span class="n">jump_target</span><span class="p">]</span>

    <span class="k">if</span> <span class="n">jump_target</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">labels</span><span class="p">:</span>
        <span class="n">label</span> <span class="o">=</span> <span class="n">MediumLevelILLabel</span><span class="p">()</span>
        <span class="n">labels</span><span class="p">[</span><span class="n">jump_target</span><span class="p">]</span> <span class="o">=</span> <span class="n">label</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">label</span> <span class="o">=</span> <span class="n">labels</span><span class="p">[</span><span class="n">jump_target</span><span class="p">]</span>

    <span class="n">new_expr</span> <span class="o">=</span> <span class="n">new_func</span><span class="p">.</span><span class="n">goto</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">loc</span><span class="o">=</span><span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">))</span>
    <span class="n">new_func</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_expr</span><span class="p">,</span> <span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">))</span>
    <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">'Replaced jump at </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s"> with jump to </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">jump_target</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">emit_insn</span><span class="p">(</span><span class="n">new_func</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">labels</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="n">log_warn</span><span class="p">(</span><span class="sa">f</span><span class="s">'Could not emit instruction: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">. Emitting NOP instead.'</span><span class="p">)</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_func</span><span class="p">.</span><span class="n">nop</span><span class="p">(</span><span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">)),</span> <span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">))</span>
</code></pre></div></div>

<p>To copy MLIL instructions, I defined a function called <code class="language-plaintext highlighter-rouge">emit_insn</code>. Before copying each instruction, the function checked to see if its address was associated with an existing label that needed to be marked. If so, it marked the label, otherwise it created a new label for the instruction and marked it immediately. In this way, all of the newly created <code class="language-plaintext highlighter-rouge">GOTO</code> instructions ended up with correctly marked labels once the new MLIL was done being emitted.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">emit_insn</span><span class="p">(</span><span class="n">new_func</span><span class="p">,</span> <span class="n">insn</span><span class="p">,</span> <span class="n">labels</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span> <span class="ow">in</span> <span class="n">labels</span><span class="p">:</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">mark_label</span><span class="p">(</span><span class="n">labels</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">])</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">label</span> <span class="o">=</span> <span class="n">MediumLevelILLabel</span><span class="p">()</span>
        <span class="n">labels</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">label</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">mark_label</span><span class="p">(</span><span class="n">labels</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">])</span>
    
    <span class="n">new_func</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">insn</span><span class="p">.</span><span class="n">copy_to</span><span class="p">(</span><span class="n">new_func</span><span class="p">),</span> <span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">insn</span><span class="p">))</span>
</code></pre></div></div>

<p>The full script:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DISPATCHER_LOC</span> <span class="o">=</span> <span class="mi">380</span>
<span class="n">JUMPTABLE_ADDR</span> <span class="o">=</span> <span class="mh">0x418c</span>

<span class="c1"># Look for the following pattern:
# state.al = [next state]
# goto 380 @ 0x31b8
</span><span class="k">def</span> <span class="nf">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">dispatcher</span><span class="p">):</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">3</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">operands</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="n">second</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="n">DISPATCHER_LOC</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">)</span> <span class="o">!=</span> <span class="n">MediumLevelILSetVarField</span> <span class="ow">or</span> <span class="nb">type</span><span class="p">(</span><span class="n">second</span><span class="p">)</span> <span class="o">!=</span> <span class="n">MediumLevelILGoto</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">!=</span> <span class="n">Variable</span> <span class="ow">or</span> <span class="nb">type</span><span class="p">(</span><span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="o">!=</span> <span class="n">MediumLevelILConst</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>

<span class="k">def</span> <span class="nf">emit_insn</span><span class="p">(</span><span class="n">new_func</span><span class="p">,</span> <span class="n">insn</span><span class="p">,</span> <span class="n">labels</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span> <span class="ow">in</span> <span class="n">labels</span><span class="p">:</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">mark_label</span><span class="p">(</span><span class="n">labels</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">])</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">label</span> <span class="o">=</span> <span class="n">MediumLevelILLabel</span><span class="p">()</span>
        <span class="n">labels</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">label</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">mark_label</span><span class="p">(</span><span class="n">labels</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">])</span>
    
    <span class="n">new_func</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">insn</span><span class="p">.</span><span class="n">copy_to</span><span class="p">(</span><span class="n">new_func</span><span class="p">),</span> <span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">insn</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">fix_cff</span><span class="p">(</span><span class="n">analysis_context</span><span class="p">:</span> <span class="n">AnalysisContext</span><span class="p">):</span>
    <span class="n">labels</span> <span class="o">=</span> <span class="p">{}</span>
    
    <span class="n">old_func</span> <span class="o">=</span> <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span>
    <span class="n">new_func</span> <span class="o">=</span> <span class="n">MediumLevelILFunction</span><span class="p">(</span><span class="n">old_func</span><span class="p">.</span><span class="n">arch</span><span class="p">,</span> <span class="n">low_level_il</span><span class="o">=</span><span class="n">analysis_context</span><span class="p">.</span><span class="n">llil</span><span class="p">)</span>

    <span class="n">blocks</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="n">dispatcher</span> <span class="o">=</span> <span class="n">old_func</span><span class="p">.</span><span class="n">get_basic_block_at</span><span class="p">(</span><span class="n">DISPATCHER_LOC</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">dispatcher</span><span class="p">.</span><span class="n">outgoing_edges</span><span class="p">:</span>
        <span class="n">insn</span> <span class="o">=</span> <span class="n">e</span><span class="p">.</span><span class="n">target</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">blocks</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">expr_index</span>

    <span class="c1"># Deal with instructions like if/then/else where the operands are at a different memory address than the MLIL instruction itself
</span>    <span class="n">mlil_addresses</span> <span class="o">=</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="nb">len</span><span class="p">(</span><span class="n">old_func</span><span class="p">)):</span>
        <span class="n">insn</span> <span class="o">=</span> <span class="n">old_func</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mlil_addresses</span><span class="p">:</span>
            <span class="n">mlil_addresses</span><span class="p">[</span><span class="n">insn</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span>
        <span class="k">for</span> <span class="n">op</span> <span class="ow">in</span> <span class="n">insn</span><span class="p">.</span><span class="n">operands</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">op</span><span class="p">.</span><span class="n">address</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">mlil_addresses</span><span class="p">:</span>
                    <span class="n">mlil_addresses</span><span class="p">[</span><span class="n">op</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">address</span>
            <span class="k">except</span><span class="p">:</span>
                <span class="k">pass</span>

    <span class="n">new_func</span><span class="p">.</span><span class="n">prepare_to_copy_function</span><span class="p">(</span><span class="n">old_func</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">old_block</span> <span class="ow">in</span> <span class="n">old_func</span><span class="p">.</span><span class="n">basic_blocks</span><span class="p">:</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">prepare_to_copy_block</span><span class="p">(</span><span class="n">old_block</span><span class="p">)</span>

        <span class="n">first_insn</span> <span class="o">=</span> <span class="n">old_block</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">new_func</span><span class="p">.</span><span class="n">set_current_address</span><span class="p">(</span><span class="n">first_insn</span><span class="p">.</span><span class="n">address</span><span class="p">,</span> <span class="n">old_block</span><span class="p">.</span><span class="n">arch</span><span class="p">)</span>
        <span class="n">emit_insn</span><span class="p">(</span><span class="n">new_func</span><span class="p">,</span> <span class="n">first_insn</span><span class="p">,</span> <span class="n">labels</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">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">old_block</span><span class="p">)):</span>
            <span class="n">first</span><span class="p">:</span> <span class="n">MediumLevelILInstruction</span> <span class="o">=</span> <span class="n">old_block</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">second</span><span class="p">:</span> <span class="n">MediumLevelILInstruction</span> <span class="o">=</span> <span class="n">old_block</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>  
            <span class="n">new_func</span><span class="p">.</span><span class="n">set_current_address</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">address</span><span class="p">,</span> <span class="n">old_block</span><span class="p">.</span><span class="n">arch</span><span class="p">)</span>

            <span class="k">if</span> <span class="n">is_cff</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">dispatcher</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">expr_index</span><span class="p">):</span>
                <span class="n">idx</span> <span class="o">=</span> <span class="n">first</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
                <span class="n">jump_target</span> <span class="o">=</span> <span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="n">bv</span><span class="p">.</span><span class="n">read_int</span><span class="p">(</span><span class="n">JUMPTABLE_ADDR</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">*</span> <span class="p">(</span><span class="n">idx</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">value</span> <span class="o">-</span> <span class="mi">1</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">jump_target</span> <span class="ow">in</span> <span class="n">mlil_addresses</span><span class="p">:</span>
                    <span class="n">jump_target</span> <span class="o">=</span> <span class="n">mlil_addresses</span><span class="p">[</span><span class="n">jump_target</span><span class="p">]</span>

                <span class="k">if</span> <span class="n">jump_target</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">labels</span><span class="p">:</span>
                    <span class="n">label</span> <span class="o">=</span> <span class="n">MediumLevelILLabel</span><span class="p">()</span>
                    <span class="n">labels</span><span class="p">[</span><span class="n">jump_target</span><span class="p">]</span> <span class="o">=</span> <span class="n">label</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">label</span> <span class="o">=</span> <span class="n">labels</span><span class="p">[</span><span class="n">jump_target</span><span class="p">]</span>

                <span class="n">new_expr</span> <span class="o">=</span> <span class="n">new_func</span><span class="p">.</span><span class="n">goto</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">loc</span><span class="o">=</span><span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">))</span>
                <span class="n">new_func</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_expr</span><span class="p">,</span> <span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">))</span>
                <span class="n">log_info</span><span class="p">(</span><span class="sa">f</span><span class="s">'Replaced jump at </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">second</span><span class="p">.</span><span class="n">address</span><span class="p">)</span><span class="si">}</span><span class="s"> with jump to </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">jump_target</span><span class="p">)</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="n">emit_insn</span><span class="p">(</span><span class="n">new_func</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">labels</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="n">log_warn</span><span class="p">(</span><span class="sa">f</span><span class="s">'Could not emit instruction: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">. Emitting NOP instead.'</span><span class="p">)</span>
                    <span class="n">new_func</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">new_func</span><span class="p">.</span><span class="n">nop</span><span class="p">(</span><span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">)),</span> <span class="n">ILSourceLocation</span><span class="p">.</span><span class="n">from_instruction</span><span class="p">(</span><span class="n">second</span><span class="p">))</span>

    <span class="n">new_func</span><span class="p">.</span><span class="n">finalize</span><span class="p">()</span>
    <span class="n">new_func</span><span class="p">.</span><span class="n">generate_ssa_form</span><span class="p">()</span>

    <span class="n">analysis_context</span><span class="p">.</span><span class="n">mlil</span> <span class="o">=</span> <span class="n">new_func</span>

<span class="n">wf</span> <span class="o">=</span> <span class="n">Workflow</span><span class="p">(</span><span class="s">"core.function.metaAnalysis"</span><span class="p">).</span><span class="n">clone</span><span class="p">(</span><span class="s">"core.function.metaAnalysis"</span><span class="p">)</span>

<span class="n">wf</span><span class="p">.</span><span class="n">register_activity</span><span class="p">(</span><span class="n">Activity</span><span class="p">(</span>
    <span class="n">configuration</span><span class="o">=</span><span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">({</span>
        <span class="s">"name"</span><span class="p">:</span> <span class="s">"extension.fix_cff"</span><span class="p">,</span>
        <span class="s">"title"</span><span class="p">:</span> <span class="s">"Fix CFF"</span><span class="p">,</span>
        <span class="s">"description"</span><span class="p">:</span> <span class="s">"Replace control flow flattening instructions with direct jumps."</span><span class="p">,</span>
        <span class="s">"eligibility"</span><span class="p">:</span> <span class="p">{</span>
            <span class="s">"auto"</span><span class="p">:</span> <span class="p">{</span>
                <span class="s">"default"</span><span class="p">:</span> <span class="bp">True</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}),</span>
    <span class="n">action</span><span class="o">=</span><span class="n">fix_cff</span>
<span class="p">))</span>

<span class="n">wf</span><span class="p">.</span><span class="n">insert_after</span><span class="p">(</span><span class="s">"core.function.analyzeIndirectBranches"</span><span class="p">,</span> <span class="p">[</span>
    <span class="s">"extension.fix_cff"</span><span class="p">,</span>
<span class="p">])</span>

<span class="n">wf</span><span class="p">.</span><span class="n">register</span><span class="p">()</span>
</code></pre></div></div>

<p>Unfortunately, this script wasn’t any more successful than the previous one at getting me an unflattened binary. As a reminder, this is what the decompilation looked like before running the script:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>            <span class="k">switch</span> <span class="p">(</span><span class="n">rcx_5</span><span class="p">)</span>
<span class="mo">000011</span><span class="n">ca</span>                <span class="k">case</span> <span class="mi">1</span>
<span class="mo">000011</span><span class="n">ca</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xbe</span>
<span class="mo">000011</span><span class="n">cc</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="mo">0000211</span><span class="n">b</span>                <span class="k">case</span> <span class="mi">3</span>
<span class="mo">0000211</span><span class="n">b</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xb5</span>
<span class="mo">0000211</span><span class="n">d</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="mo">00001</span><span class="n">d9c</span>                <span class="k">case</span> <span class="mi">4</span>
<span class="mo">00001</span><span class="n">d9c</span>                    <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xe7</span>
<span class="mo">00001</span><span class="n">d9e</span>                    <span class="k">goto</span> <span class="n">label_31b8</span>
<span class="c1">// [...]</span>
</code></pre></div></div>

<p>And this is what it looked like after:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>            <span class="k">switch</span> <span class="p">(</span><span class="n">jump_table_14000418c</span><span class="p">[</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">rcx_5</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)])</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                <span class="k">case</span> <span class="mh">0xffffcff2</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                    <span class="k">continue</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                <span class="k">case</span> <span class="mh">0xffffd014</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                    <span class="k">goto</span> <span class="n">label_11a0</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                <span class="k">case</span> <span class="mh">0xffffd01b</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                    <span class="k">goto</span> <span class="n">label_11a7</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                <span class="k">case</span> <span class="mh">0xffffd022</span>
<span class="mo">000011</span><span class="mi">9</span><span class="n">d</span>                    <span class="k">goto</span> <span class="n">label_11ae</span>
<span class="c1">// [...]</span>
</code></pre></div></div>

<p>The long switch case is still there, even though all the GOTO instructions have been changed. There were a few CFF blocks that my script missed, so it’s possible the decompilation would’ve worked better if I’d managed to detect and rewrite them. However, it’s also possible that swapping out individual MLIL instructions doesn’t do much to rewrite the control flow graph, and I would’ve had to do a much more substantial rewrite of the function in order to get rid of the dispatcher block entirely. Additionally, I’m not sure exactly where in the analysis process Binary Ninja tries to simplify the control flow graph, so maybe the script runs too late in the workflow to be able to make a difference (although, one of the official <a href="https://github.com/Vector35/binaryninja-api/blob/2f1d09c6b714a9f2664eab0a9dbef79e3cbd8919/python/examples/wf_unflatten.py">examples</a> of the API performs unflattening at the MLIL level, so that’s unlikely).</p>

<p>At this point, my script had gotten kind of hacky and unmanageable, so I decided to stop trying to solve the crackme this way. I still think IL rewriting has a lot of promise as a deobfuscation technique, but it’s probably easier to use in situations that don’t involve drastic modification to the control flow. In addition, as the script got to be longer and more complicated, it probably would’ve been better to switch to using the C++ or Rust APIs. I think the C++ API is the most supported way of doing this, so maybe I’ll try that next time.</p>

<h2 id="binary-patching">Binary Patching</h2>

<p>I did eventually manage to get a nice-looking decompilation, but it was with binary patching using keystone and capstone. This is a much more well-documented topic than IL modification, so I’m not going to go through the script line by line, but I’ll still provide it here for reference. Essentially, it replaces jumps to the dispatcher with jumps to the real target address, and it replaces the <code class="language-plaintext highlighter-rouge">cmove</code>/<code class="language-plaintext highlighter-rouge">cmovne</code>/<code class="language-plaintext highlighter-rouge">cmovb</code> instructions that set the state with the corresponding conditional jump instructions.</p>

<p>I looked for jumps to the dispatcher by using capstone to search for the pattern <code class="language-plaintext highlighter-rouge">mov al, ??; jmp 0x31b8</code>, which worked <em>almost</em> all of the time but wasn’t quite perfect. The manual patches in the script were to deal with dispatcher jumps that didn’t quite fit the pattern. For example, <code class="language-plaintext highlighter-rouge">mov al, 0x9f; xor edi, edi; jmp     0x31b8</code> wasn’t detected as a jump to be unflattened because of the extra <code class="language-plaintext highlighter-rouge">xor edi, edi</code> in the middle. There’s probably a better way to do this than trying to match exact sequences of instructions, which will likely be the topic of a future blog post once I look into it more.</p>

<p>The script:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">pefile</span>
<span class="kn">from</span> <span class="nn">capstone</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">capstone.x86</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">keystone</span> <span class="kn">import</span> <span class="o">*</span>

<span class="n">baseaddr</span> <span class="o">=</span> <span class="mh">0x0</span>
<span class="n">dispatcher_rva</span> <span class="o">=</span> <span class="n">baseaddr</span> <span class="o">+</span> <span class="mh">0x31b8</span>
<span class="n">jumptable_rva</span> <span class="o">=</span> <span class="n">baseaddr</span> <span class="o">+</span> <span class="mh">0x418c</span>
<span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="p">.</span><span class="n">PE</span><span class="p">(</span><span class="s">'crackme.exe'</span><span class="p">)</span>
<span class="n">mapped</span> <span class="o">=</span>  <span class="nb">bytearray</span><span class="p">(</span><span class="n">pe</span><span class="p">.</span><span class="n">get_memory_mapped_image</span><span class="p">())</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">mapped</span><span class="p">[</span><span class="mh">0x1000</span><span class="p">:</span><span class="mh">0x3250</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">get_jump_target</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
    <span class="n">jump_offset</span> <span class="o">=</span> <span class="n">jumptable_rva</span> <span class="o">+</span> <span class="p">(</span><span class="n">idx</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">-</span> <span class="n">baseaddr</span>
    <span class="n">target</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'&lt;i'</span><span class="p">,</span> <span class="n">mapped</span><span class="p">[</span><span class="n">jump_offset</span><span class="p">:</span><span class="n">jump_offset</span><span class="o">+</span><span class="mi">4</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">jumptable_rva</span>
    <span class="k">return</span> <span class="n">target</span>

<span class="k">def</span> <span class="nf">get_cond_targets</span><span class="p">(</span><span class="n">cmov</span><span class="p">,</span> <span class="n">first_op</span><span class="p">,</span> <span class="n">second_op</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">first_op</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">reg</span> <span class="o">==</span> <span class="n">X86_REG_EAX</span><span class="p">:</span>
        <span class="n">eax_target</span> <span class="o">=</span> <span class="n">get_jump_target</span><span class="p">(</span><span class="n">first_op</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">imm</span><span class="p">)</span>
        <span class="n">other_target</span> <span class="o">=</span> <span class="n">get_jump_target</span><span class="p">(</span><span class="n">second_op</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">imm</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">eax_target</span> <span class="o">=</span> <span class="n">get_jump_target</span><span class="p">(</span><span class="n">second_op</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">imm</span><span class="p">)</span>
        <span class="n">other_target</span> <span class="o">=</span> <span class="n">get_jump_target</span><span class="p">(</span><span class="n">first_op</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">imm</span><span class="p">)</span>

    <span class="k">return</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">cond_mappings</span><span class="p">[</span><span class="n">cmov</span><span class="p">.</span><span class="n">mnemonic</span><span class="p">]</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">other_target</span><span class="p">)</span><span class="si">}</span><span class="s">; jmp </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">eax_target</span><span class="p">)</span><span class="si">}</span><span class="s">'</span>

<span class="n">md</span> <span class="o">=</span> <span class="n">Cs</span><span class="p">(</span><span class="n">CS_ARCH_X86</span><span class="p">,</span> <span class="n">CS_MODE_64</span><span class="p">)</span>
<span class="n">md</span><span class="p">.</span><span class="n">detail</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">md</span><span class="p">.</span><span class="n">skipdata</span> <span class="o">=</span> <span class="bp">True</span>

<span class="n">ks</span> <span class="o">=</span> <span class="n">Ks</span><span class="p">(</span><span class="n">KS_ARCH_X86</span><span class="p">,</span> <span class="n">KS_MODE_64</span><span class="p">)</span>

<span class="n">cond_mappings</span> <span class="o">=</span> <span class="p">{</span><span class="s">'cmove'</span><span class="p">:</span> <span class="s">'je'</span><span class="p">,</span> <span class="s">'cmovne'</span><span class="p">:</span> <span class="s">'jne'</span><span class="p">,</span> <span class="s">'cmovb'</span><span class="p">:</span> <span class="s">'jb'</span><span class="p">}</span>

<span class="c1"># disassemble the entire .text section
</span><span class="n">instructions</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">md</span><span class="p">.</span><span class="n">disasm</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">baseaddr</span><span class="o">+</span><span class="mh">0x1000</span><span class="p">):</span>
    <span class="n">instructions</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

<span class="c1"># find all the jumps to the dispatcher
</span><span class="n">dispatcher_jmps</span> <span class="o">=</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="nb">len</span><span class="p">(</span><span class="n">instructions</span><span class="p">)):</span>
    <span class="n">insn</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">insn</span><span class="p">.</span><span class="n">mnemonic</span> <span class="o">==</span> <span class="s">'jmp'</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">op</span> <span class="ow">in</span> <span class="n">insn</span><span class="p">.</span><span class="n">operands</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">op</span><span class="p">.</span><span class="nb">type</span> <span class="o">==</span> <span class="n">X86_OP_IMM</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">op</span><span class="p">.</span><span class="n">imm</span> <span class="o">==</span> <span class="n">dispatcher_rva</span><span class="p">:</span>
                    <span class="n">dispatcher_jmps</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

<span class="n">compare_locs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">jmp_loc</span> <span class="ow">in</span> <span class="n">dispatcher_jmps</span><span class="p">:</span>
    <span class="n">insn</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">jmp_loc</span><span class="p">]</span>
    <span class="n">prev</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">jmp_loc</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
    <span class="n">equiv</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="c1"># Patch direct jumps to dispatcher:
</span>    <span class="c1"># 00002c30  b0d1               mov     al, 0xd1
</span>    <span class="c1"># 00002c32  e981050000         jmp     0x31b8
</span>    <span class="k">if</span> <span class="n">prev</span><span class="p">.</span><span class="n">mnemonic</span> <span class="o">==</span> <span class="s">'mov'</span><span class="p">:</span>
        <span class="n">patch_len</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">prev</span><span class="p">.</span><span class="n">size</span>
        <span class="n">patch_loc</span> <span class="o">=</span> <span class="n">prev</span><span class="p">.</span><span class="n">address</span>
        <span class="n">target</span> <span class="o">=</span> <span class="n">get_jump_target</span><span class="p">(</span><span class="n">prev</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">imm</span><span class="p">)</span>
        <span class="n">equiv</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'jmp </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">target</span><span class="p">)</span><span class="si">}</span><span class="s">'</span>

    <span class="c1"># Patch conditional jumps to dispatcher:
</span>    <span class="c1"># 00002c1e  b936000000         mov     ecx, 0x36
</span>    <span class="c1"># 00002c23  b8c8000000         mov     eax, 0xc8
</span>    <span class="c1"># 00002c28  0f45c1             cmovne  eax, ecx  {0x36}
</span>    <span class="c1"># 00002c2b  e988050000         jmp     0x31b8
</span>    <span class="k">elif</span> <span class="s">'cmov'</span> <span class="ow">in</span> <span class="n">prev</span><span class="p">.</span><span class="n">mnemonic</span><span class="p">:</span>
        <span class="n">compare_locs</span><span class="p">[</span><span class="n">prev</span><span class="p">.</span><span class="n">address</span><span class="p">]</span> <span class="o">=</span> <span class="n">jmp_loc</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">first_op</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">jmp_loc</span> <span class="o">-</span> <span class="mi">3</span><span class="p">]</span>
        <span class="n">second_op</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">jmp_loc</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span>
        <span class="n">patch_len</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">prev</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">first_op</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">second_op</span><span class="p">.</span><span class="n">size</span>
        <span class="n">patch_loc</span> <span class="o">=</span> <span class="n">first_op</span><span class="p">.</span><span class="n">address</span>

        <span class="n">equiv</span> <span class="o">=</span> <span class="n">get_cond_targets</span><span class="p">(</span><span class="n">prev</span><span class="p">,</span> <span class="n">first_op</span><span class="p">,</span> <span class="n">second_op</span><span class="p">)</span>
        
    <span class="k">if</span> <span class="n">equiv</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">patch_opcodes</span> <span class="o">=</span> <span class="n">ks</span><span class="p">.</span><span class="n">asm</span><span class="p">(</span><span class="n">equiv</span><span class="p">,</span> <span class="n">as_bytes</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">addr</span><span class="o">=</span><span class="n">patch_loc</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">padded</span> <span class="o">=</span> <span class="n">patch_opcodes</span> <span class="o">+</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x90</span><span class="s">'</span> <span class="o">*</span> <span class="p">(</span><span class="n">patch_len</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">patch_opcodes</span><span class="p">))</span>
        <span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="n">patch_loc</span> <span class="o">-</span> <span class="n">baseaddr</span><span class="p">,</span> <span class="n">padded</span><span class="p">)</span>

<span class="c1"># Find the conditional mov instructions reached by a jump, ex:
# 00001267  483b5c2438         cmp     rbx, qword [rsp+0x38 {var_e0}]
# 0000126c  b999000000         mov     ecx, 0x99
# 00001271  b8cd000000         mov     eax, 0xcd
# 00001276  e96e0e0000         jmp     0x20e9
# [...]
# 000020e9  0f42c1             cmovb   eax, ecx
# 000020ec  e9c7100000         jmp     0x31b8
</span><span class="n">compare_jumps</span> <span class="o">=</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="nb">len</span><span class="p">(</span><span class="n">instructions</span><span class="p">)):</span>
    <span class="n">insn</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="k">if</span> <span class="n">insn</span><span class="p">.</span><span class="n">mnemonic</span> <span class="o">==</span> <span class="s">'jmp'</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">op</span> <span class="ow">in</span> <span class="n">insn</span><span class="p">.</span><span class="n">operands</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">op</span><span class="p">.</span><span class="nb">type</span> <span class="o">==</span> <span class="n">X86_OP_IMM</span><span class="p">:</span>
                <span class="k">if</span> <span class="n">op</span><span class="p">.</span><span class="n">imm</span> <span class="ow">in</span> <span class="n">compare_locs</span><span class="p">:</span>
                    <span class="n">compare_jumps</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">compare_locs</span><span class="p">[</span><span class="n">op</span><span class="p">.</span><span class="n">imm</span><span class="p">]</span>

<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">compare_loc</span> <span class="ow">in</span> <span class="n">compare_jumps</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
    <span class="n">insn</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="n">first_op</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span>
    <span class="n">second_op</span> <span class="o">=</span> <span class="n">instructions</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">patch_len</span> <span class="o">=</span> <span class="n">insn</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">first_op</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">second_op</span><span class="p">.</span><span class="n">size</span>
    <span class="n">patch_loc</span> <span class="o">=</span> <span class="n">first_op</span><span class="p">.</span><span class="n">address</span>

    <span class="n">cmov</span> <span class="o">=</span> <span class="n">instructions</span><span class="p">[</span><span class="n">compare_loc</span><span class="p">]</span>
    <span class="n">equiv</span> <span class="o">=</span> <span class="n">get_cond_targets</span><span class="p">(</span><span class="n">cmov</span><span class="p">,</span> <span class="n">first_op</span><span class="p">,</span> <span class="n">second_op</span><span class="p">)</span>
    <span class="n">patch_opcodes</span> <span class="o">=</span> <span class="n">ks</span><span class="p">.</span><span class="n">asm</span><span class="p">(</span><span class="n">equiv</span><span class="p">,</span> <span class="n">as_bytes</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">addr</span><span class="o">=</span><span class="n">patch_loc</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>

    <span class="n">padded</span> <span class="o">=</span> <span class="n">patch_opcodes</span> <span class="o">+</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x90</span><span class="s">'</span> <span class="o">*</span> <span class="p">(</span><span class="n">patch_len</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">patch_opcodes</span><span class="p">))</span>
    <span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="n">patch_loc</span> <span class="o">-</span> <span class="n">baseaddr</span><span class="p">,</span> <span class="n">padded</span><span class="p">)</span>

<span class="c1"># Patch the last few cases where the instruction pattern is different from the others
# Ex., an extra instruction between the mov and the jump to the dispatcher:
# 000024cf  b09f               mov     al, 0x9f
# 000024d1  31ff               xor     edi, edi  {0x0}
# 000024d3  e9e00c0000         jmp     0x31b8
</span><span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="mh">0x1272</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\xe9\x6a\x03\x00\x00</span><span class="s">'</span><span class="p">)</span>
<span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="mh">0x1381</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\xe9\x48\x0d\x00\x00</span><span class="s">'</span><span class="p">)</span>
<span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="mh">0x1518</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\xe9\x89\x0e\x00\x00</span><span class="s">'</span><span class="p">)</span>
<span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="mh">0x24d3</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\xe9\x16\xf6\xff\xff</span><span class="s">'</span><span class="p">)</span>
<span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="mh">0x28eb</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\xe9\xf2\xeb\xff\xff</span><span class="s">'</span><span class="p">)</span>

<span class="c1"># Insert a mov ebp, ecx instruction after the XOR loop, since we're not reaching the mov in the dispatcher
</span><span class="n">pe</span><span class="p">.</span><span class="n">set_bytes_at_rva</span><span class="p">(</span><span class="mh">0x28e6</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x89\xcd</span><span class="s">'</span><span class="p">)</span>

<span class="n">pe</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">'patched_cs.exe'</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="solving-the-crackme">Solving The Crackme</h2>

<p>With the control flow flattening removed, we obtain the following decompilation:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1400014</span><span class="n">f1</span>        <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span>
<span class="mi">1400014</span><span class="n">f1</span>            <span class="n">input</span> <span class="o">=</span> <span class="n">var_58</span>
<span class="mi">1400014</span><span class="n">f6</span>            <span class="kt">int32_t</span> <span class="n">state</span> <span class="o">=</span> <span class="o">-</span><span class="mh">0x5a4c3827</span>
<span class="mi">1400014</span><span class="n">fc</span>            <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0x78</span>
<span class="mi">1400014</span><span class="n">fe</span>            <span class="kt">int64_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mi">140001505</span>            <span class="kt">int64_t</span> <span class="n">var_e8_1</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mi">14000281</span><span class="n">c</span>            <span class="n">BOOL</span> <span class="n">input_char</span>
<span class="mi">14000281</span><span class="n">c</span>            <span class="kt">int32_t</span> <span class="n">or_chars</span>
<span class="mi">14000281</span><span class="n">c</span>            <span class="n">input_char</span><span class="p">,</span> <span class="n">or_chars</span> <span class="o">=</span> <span class="n">IsDebuggerPresent</span><span class="p">()</span>
<span class="mi">14000281</span><span class="n">c</span>            
<span class="mi">140002824</span>            <span class="k">if</span> <span class="p">(</span><span class="n">input_char</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mi">140001</span><span class="n">b95</span>                <span class="k">if</span> <span class="p">(</span><span class="n">rcx_3</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mi">14000126</span><span class="n">c</span>                    <span class="k">for</span> <span class="p">(;</span> <span class="n">i</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">rcx_3</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="mi">1400024</span><span class="n">b4</span>                        <span class="kt">uint8_t</span> <span class="n">xored</span>
<span class="mi">1400024</span><span class="n">b4</span>                        
<span class="mi">1400024</span><span class="n">b4</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="n">u</span><span class="o">&lt;</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mi">1400024</span><span class="n">ba</span>                            <span class="n">input_char</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">input</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="mf">1400017e8</span>                            <span class="kt">uint64_t</span> <span class="n">substituted</span>
<span class="mf">1400017e8</span>                            <span class="n">substituted</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">sbox</span><span class="p">[</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">input_char</span><span class="p">.</span><span class="n">b</span><span class="p">)]</span>
<span class="mi">1400012</span><span class="n">d7</span>                            <span class="n">xored</span> <span class="o">=</span> <span class="p">(</span><span class="n">state</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">).</span><span class="n">b</span> <span class="o">^</span> <span class="n">substituted</span><span class="p">.</span><span class="n">b</span>
<span class="mi">1400012</span><span class="n">d7</span>                        
<span class="mi">1400012</span><span class="n">be</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="mh">0x10</span> <span class="o">||</span> <span class="n">i</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mi">1400031</span><span class="n">f3</span>                            <span class="n">fail</span><span class="p">()</span>
<span class="mi">1400031</span><span class="n">f3</span>                            <span class="n">noreturn</span>
<span class="mi">1400031</span><span class="n">f3</span>                        
<span class="mi">1400012</span><span class="n">c4</span>                        <span class="n">xor_buf</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">xored</span>
<span class="mi">14000264</span><span class="n">b</span>                        <span class="n">state</span> <span class="o">=</span> <span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">xored</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span> <span class="o">^</span> <span class="n">state</span><span class="p">)</span> <span class="o">*</span> <span class="mh">0xc2b2ae3d</span> <span class="o">+</span> <span class="mh">0x1f2e3d4c</span>
<span class="mi">14000264</span><span class="n">b</span>                    
<span class="mi">14000140</span><span class="n">f</span>                    <span class="k">while</span> <span class="p">(</span><span class="n">i_1</span> <span class="n">u</span><span class="o">&lt;</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mi">1400028</span><span class="n">ca</span>                        <span class="k">if</span> <span class="p">(</span><span class="n">i_1</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="mh">0x10</span><span class="p">)</span>
<span class="mi">140003200</span>                            <span class="n">fail</span><span class="p">()</span>
<span class="mi">140003200</span>                            <span class="n">noreturn</span>
<span class="mi">140003200</span>                        
<span class="mi">1400028</span><span class="n">d7</span>                        <span class="n">or_chars</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">target</span><span class="p">[</span><span class="n">i_1</span><span class="p">]</span>
<span class="mi">1400028</span><span class="n">db</span>                        <span class="n">or_chars</span><span class="p">.</span><span class="n">b</span> <span class="o">^=</span> <span class="n">xor_buf</span><span class="p">[</span><span class="n">i_1</span><span class="p">]</span>
<span class="mf">1400028e0</span>                        <span class="n">or_chars</span><span class="p">.</span><span class="n">b</span> <span class="o">|=</span> <span class="n">or_chars_1</span><span class="p">.</span><span class="n">b</span>
<span class="mf">1400028e3</span>                        <span class="n">i_1</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="mf">1400028e6</span>                        <span class="n">or_chars_1</span> <span class="o">=</span> <span class="n">or_chars</span>
<span class="mf">1400028e6</span>                    
<span class="mi">140002878</span>                    <span class="n">input_char</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">or_chars_1</span><span class="p">.</span><span class="n">b</span> <span class="o">==</span> <span class="mi">0</span>
<span class="mi">140002878</span>                    
<span class="mi">140002</span><span class="n">c1e</span>                    <span class="k">if</span> <span class="p">((</span><span class="n">input_char</span><span class="p">.</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mi">14000137</span><span class="n">f</span>                        <span class="n">input_char</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0xe0</span>
<span class="mi">14000137</span><span class="n">f</span>                        
<span class="mf">1400012e6</span>                        <span class="k">for</span> <span class="p">(;</span> <span class="n">i_2</span> <span class="n">u</span><span class="o">&lt;</span> <span class="mh">0x20</span><span class="p">;</span> <span class="n">i_2</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span>
<span class="mi">140002</span><span class="n">a9f</span>                            <span class="k">if</span> <span class="p">(</span><span class="n">i_2</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="mh">0x20</span><span class="p">)</span>
<span class="mi">140003214</span>                            <span class="n">label_140003214</span><span class="o">:</span>
<span class="mi">140003214</span>                                <span class="n">fail</span><span class="p">()</span>
<span class="mi">140003214</span>                                <span class="n">noreturn</span>
<span class="mi">140003214</span>                            
<span class="mi">140002</span><span class="n">ab1</span>                            <span class="n">console_msg</span><span class="p">[</span><span class="n">i_2</span><span class="p">]</span> <span class="o">=</span> <span class="n">access_granted</span><span class="p">[</span><span class="n">i_2</span><span class="p">]</span> <span class="o">^</span> <span class="mh">0xcd</span>
<span class="mi">140002</span><span class="n">ab1</span>                        
<span class="mi">14000278</span><span class="n">b</span>                        <span class="n">write_console</span><span class="p">(</span><span class="n">check_alnum</span><span class="p">(</span><span class="o">&amp;</span><span class="n">console_msg</span><span class="p">,</span> <span class="mh">0x20</span><span class="p">),</span> <span class="mh">0x20</span><span class="p">)</span>
<span class="mi">140002790</span>                        <span class="k">continue</span>
<span class="mi">140002790</span>                
<span class="mi">1400024</span><span class="n">cf</span>                <span class="n">input_char</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="mh">0x9f</span>
<span class="mi">1400024</span><span class="n">cf</span>                
<span class="mi">1400020</span><span class="n">df</span>                <span class="k">for</span> <span class="p">(</span><span class="n">i_2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i_2</span> <span class="n">u</span><span class="o">&lt;</span> <span class="mh">0xe</span><span class="p">;</span> <span class="n">i_2</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span>
<span class="mi">140001</span><span class="n">f85</span>                    <span class="k">if</span> <span class="p">(</span><span class="n">i_2</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="mh">0xe</span><span class="p">)</span>
<span class="mi">140001</span><span class="n">f85</span>                        <span class="k">goto</span> <span class="n">label_140003214</span>
<span class="mi">140001</span><span class="n">f85</span>                    
<span class="mi">140001</span><span class="n">f97</span>                    <span class="n">console_msg</span><span class="p">[</span><span class="n">i_2</span><span class="p">]</span> <span class="o">=</span> <span class="n">access_denied</span><span class="p">[</span><span class="n">i_2</span><span class="p">]</span> <span class="o">^</span> <span class="mh">0xcd</span>
<span class="mi">140001</span><span class="n">f97</span>                
<span class="mi">1400017</span><span class="n">c9</span>                <span class="n">write_console</span><span class="p">(</span><span class="n">check_alnum</span><span class="p">(</span><span class="o">&amp;</span><span class="n">console_msg</span><span class="p">,</span> <span class="mh">0xe</span><span class="p">),</span> <span class="mh">0xe</span><span class="p">)</span>
</code></pre></div></div>

<p>After all that, it turns out the validation function is very simple. First, there’s a debugger check, which we don’t care about since we’re solving everything statically. Then, each character of the input is encrypted using a custom algorithm that uses a substitution box followed by an XOR with a running keystream generated by an LCG. The encrypted input is XORed with the target value <code class="language-plaintext highlighter-rouge">8b4bb25cd9a29e4a009aac337f6d359d</code>, and each byte of the result is ORed together and compared to 0, a way of checking if all bytes in the XORed result are 0. If the comparison to the target value succeeds, the string <code class="language-plaintext highlighter-rouge">Congratulations, Access granted</code> is XOR decoded with the key <code class="language-plaintext highlighter-rouge">0xcd</code> and printed to the console.</p>

<p>The encryption algorithm is the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">MASK32</span> <span class="o">=</span> <span class="mh">0xffff_ffff</span>

<span class="n">sbox</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="s">"""51c25966c4e9e36c1e1a19cab4db17717241fa3b3739c626a8e8f99502da556f
702449b957d9de69a4150386bd625d6553fdfe6d892dd56bf7ec21a1602c22f4
18c5cb807f4c52768442f5932b48835bb18e0fba081661f8bf0d05dc1f9e9b0e
df38e1c3b7999d7e5aeabba340ac3eab00f0b690964acf63bc27ce3faf752ff1
cc58aa44f6740c28871bed2e68ad4b8c01097d46368288d4549a50a2d37847d2
6e0a7c8def35a9dda025d710e6f3b23381d0ee731cfb8556ffc1113cd177310b
6406290713d82a98325cc9794f9f8fb5e4b36aeb91b8ae7b43cdfc5f8b30e7d6
c05e4da6453d7a141d679c2092e5f2344e94a7c7be97c8a5e023b0048ae23a12"""</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">encrypt</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="n">state</span> <span class="o">=</span> <span class="mh">0xa5b3c7d9</span>
    <span class="n">xored</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="n">substituted</span> <span class="o">=</span> <span class="n">sbox</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> 
        <span class="n">xored_char</span> <span class="o">=</span> <span class="p">((</span><span class="n">state</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">)</span> <span class="o">^</span> <span class="n">substituted</span>
        <span class="n">xored</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">xored_char</span><span class="p">)</span>
        <span class="n">state</span> <span class="o">=</span> <span class="p">((((</span><span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span> <span class="o">^</span> <span class="n">state</span><span class="p">)</span> <span class="o">*</span> <span class="mh">0xc2b2ae3d</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK32</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x1f2e3d4c</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK32</span>

    <span class="k">return</span> <span class="n">xored</span>
</code></pre></div></div>

<p>Inverting the algorithm is straightforward. The generation of the keystream from the LCG is the same for encryption and decryption, so we just need to run the ciphertext through an inverse S-box and apply the XOR with the keystream again.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">inv_sbox</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="o">*</span><span class="mh">0x100</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="nb">len</span><span class="p">(</span><span class="n">sbox</span><span class="p">)):</span>
    <span class="n">inv_sbox</span><span class="p">[</span><span class="n">sbox</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span> <span class="o">=</span> <span class="n">i</span>

<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="n">state</span> <span class="o">=</span> <span class="mh">0xa5b3c7d9</span>
    <span class="n">xored</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="n">xored_char</span> <span class="o">=</span> <span class="p">((</span><span class="n">state</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">)</span> <span class="o">^</span> <span class="n">i</span>
        <span class="n">xored</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">inv_sbox</span><span class="p">[</span><span class="n">xored_char</span><span class="p">])</span>
        <span class="n">state</span> <span class="o">=</span> <span class="p">((((</span><span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span> <span class="o">^</span> <span class="n">state</span><span class="p">)</span> <span class="o">*</span> <span class="mh">0xc2b2ae3d</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK32</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x1f2e3d4c</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">MASK32</span>

    <span class="k">return</span> <span class="n">xored</span>
</code></pre></div></div>

<p>Applying the decryption function to the target sequence of bytes <code class="language-plaintext highlighter-rouge">8b4bb25cd9a29e4a009aac337f6d359d</code>, we obtain the password: <code class="language-plaintext highlighter-rouge">ElementaryMyDr!!</code>.</p>]]></content><author><name></name></author><category term="ctf" /><summary type="html"><![CDATA[Trying (and failing) to solve a crackme with the IL modification API]]></summary></entry><entry><title type="html">Flare-On 12 writeup: 09 - 10000</title><link href="/flareon/2025/11/30/flare12-10000.html" rel="alternate" type="text/html" title="Flare-On 12 writeup: 09 - 10000" /><published>2025-11-30T15:39:00+00:00</published><updated>2025-11-30T15:39:00+00:00</updated><id>/flareon/2025/11/30/flare12-10000</id><content type="html" xml:base="/flareon/2025/11/30/flare12-10000.html"><![CDATA[<p><img src="/assets/images/flareon12/score.png" alt="" /></p>

<h2 id="introduction">Introduction</h2>

<p>This year, I finished Flare-On for the fourth time. I’m hoping the rumors about this possibly being the last Flare-On aren’t true, since I fell just short of my goal of top 50 and I want a second chance. I’m not sure if this is the hardest Flare-On challenge I’ve ever done (last year’s challenge <code class="language-plaintext highlighter-rouge">serpentine</code> was pretty insane too) but it’s easily the hardest one I’ve ever done a writeup of.</p>

<p>This challenge has a lot of different steps to it, many of which take a lot of time to compute and are easy to do wrong. For the most part, I handled this by writing a lot of different scripts to perform each intermediate step and saving the results to a JSON file. This was very messy and not at all optimal, but it did manage to get me the flag in the end. I’d be interested to know how other people did or didn’t optimize their solve scripts for this challenge.</p>

<h3 id="initial-observations">Initial Observations</h3>

<p>Te <code class="language-plaintext highlighter-rouge">main</code> function begins with a check of a license file called <code class="language-plaintext highlighter-rouge">license.bin</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">main</span><span class="p">()</span>
    <span class="n">sub_140010090</span><span class="p">()</span>
    <span class="n">sub_140082940</span><span class="p">(</span>
        <span class="n">f_print_to_stdout</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stdout_ostream</span><span class="p">,</span> <span class="n">string_val</span><span class="o">:</span> <span class="s">"checking license file..."</span><span class="p">),</span> 
        <span class="n">sub_1400c58c0</span><span class="p">)</span>
    <span class="kt">void</span> <span class="n">ifstream</span>
    <span class="n">int512_t</span> <span class="n">zmm1</span> <span class="o">=</span> <span class="n">f_get_file</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ifstream</span><span class="p">,</span> <span class="s">"license.bin"</span><span class="p">,</span> <span class="n">sub_1400c7d60</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span>
</code></pre></div></div>

<p>We can see that the license file has to be exactly 34000 bytes long, and that its SHA256 hash is used as a key to decrypt the flag.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">license_length</span> <span class="o">==</span> <span class="mi">340000</span><span class="p">)</span>
    <span class="n">f_do_smth_ifstream</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ifstream</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">zmm1</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">sub_1400c9ed0</span><span class="p">(</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">license_length</span><span class="p">))</span>
    <span class="n">f_read_file</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ifstream</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">license_length</span><span class="p">))</span>
    <span class="kt">void</span> <span class="n">hash_maybe</span>
    <span class="n">f_sha256_license_file</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="mh">0x29810</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">hash_maybe</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ifstream</span><span class="p">,</span> <span class="mh">0x100000</span><span class="p">)</span>
</code></pre></div></div>

<p>Additionally, there’s a validation function that’s run on the license file to check whether decryption of the flag should be performed. There are two conditions that need to be met: the loop performing the validation code must reach 10000 iterations, and a buffer must contain a specific expected value. I’ll refer to this buffer as the “checksum buffer” from here on out.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">counter</span> <span class="n">s</span><span class="o">&gt;</span> <span class="mi">9999</span><span class="p">)</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">_Buf1</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">check_buf</span><span class="p">,</span> <span class="n">_Buf2</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">expected_buf</span><span class="p">,</span> <span class="n">_Size</span><span class="o">:</span> <span class="mh">0x9c40</span><span class="p">)</span>
                <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
            <span class="n">sub_140082940</span><span class="p">(</span>
                <span class="n">f_print_to_stdout</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stdout_ostream</span><span class="p">,</span> 
                    <span class="n">string_val</span><span class="o">:</span> <span class="s">"license valid!"</span><span class="p">),</span> 
</code></pre></div></div>

<h3 id="10000-executables">10000 Executables</h3>

<p>The challenge binary contains 10000 resources starting with the string <code class="language-plaintext highlighter-rouge">M8Z</code>. Each of them looks <em>kind of</em> like a PE file, containing a mangled version of the string <code class="language-plaintext highlighter-rouge">This program cannot be run in DOS mode</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000: 4d 38 5a 90 38 03 66 02 04 09 71 ff 81 b8 c2 91  M8Z.8.f...q.....
00000010: 01 40 c2 15 c6 80 0b 1c 0e 1f ba f8 00 b4 09 cd  .@..............
00000020: 21 b8 01 4c 80 54 68 01 69 73 20 70 72 6f 67 cc  !..L.Th.is prog.
00000030: 61 6d f0 63 e8 6e e3 e9 74 dc 62 65 e7 f9 75 e7  am.c.n..t.be..u.
00000040: a3 69 0e 06 44 4f 53 80 6d 6f 64 65 2e 71 0d 29  .i..DOS.mode.q.)
</code></pre></div></div>

<p>Googling the string <code class="language-plaintext highlighter-rouge">M8Z</code>, I found that the resources were in fact PE files compressed with <a href="https://github.com/herrcore/aplib-ripper">aPlib</a>. This explained the 10000-iteration loop in the license validation code: the license file would somehow be checked using functions from all 10000 of these DLLs.</p>

<p>Sure enough, <code class="language-plaintext highlighter-rouge">sub_140001482</code> was responsible for retrieving the DLL at a given index and calling its entry point, passing in the checksum buffer as an argument to the entry function.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">FindResourceA</span><span class="p">(</span><span class="n">hModule</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">lpName</span><span class="o">:</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">resource_index</span><span class="p">),</span> <span class="n">lpType</span><span class="o">:</span> <span class="mh">0xa</span><span class="p">)</span>
<span class="kt">char</span><span class="o">*</span> <span class="n">var_68_1</span> <span class="o">=</span>
    <span class="n">LockResource</span><span class="p">(</span><span class="n">hResData</span><span class="o">:</span> <span class="n">LoadResource</span><span class="p">(</span><span class="n">hModule</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">hResInfo</span><span class="p">))</span>
<span class="kt">uint32_t</span> <span class="n">compressed_size</span> <span class="o">=</span> <span class="n">SizeofResource</span><span class="p">(</span><span class="n">hModule</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">hResInfo</span><span class="p">)</span>
<span class="kt">int32_t</span> <span class="n">decompressed_size</span> <span class="o">=</span> <span class="n">sub_140002690</span><span class="p">(</span><span class="n">var_68_1</span><span class="p">,</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">compressed_size</span><span class="p">))</span>
<span class="kt">void</span> <span class="n">var_1c8</span>
<span class="n">sub_1400c0c30</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_1c8</span><span class="p">,</span> <span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">decompressed_size</span><span class="p">))</span>
<span class="k">struct</span> <span class="n">pe_headers</span><span class="o">*</span> <span class="n">pe_file</span> <span class="o">=</span> <span class="n">sub_1400297d0</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_1c8</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="kt">int32_t</span> <span class="n">var_1d0_1</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">f_decompress_pe</span><span class="p">(</span><span class="n">var_68_1</span><span class="p">,</span> <span class="n">pe_file</span><span class="p">,</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">compressed_size</span><span class="p">),</span> 
    <span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">decompressed_size</span><span class="p">),</span> <span class="n">nullptr</span><span class="p">)</span>

<span class="c1">//[...]</span>

<span class="kt">int32_t</span> <span class="n">var_cc_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">dll_addr</span>
    <span class="o">+</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">rax_21</span><span class="o">-&gt;</span><span class="n">OptionalHeader</span><span class="p">.</span><span class="n">AddressOfEntryPoint</span><span class="p">))(</span><span class="o">&amp;</span><span class="n">check_buf</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="the-license-file-format">The License File Format</h3>

<p>My first step was to get an idea of how the file format of the license file was structured so that I could generate a test license file to use in a debugger. I found that on each run of the loop, the validation function reads two values from the license. The first field of the license file is a 16-bit integer that must be between 0 and 9999:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint16_t</span> <span class="n">license_index</span> <span class="o">=</span> <span class="n">license_buf</span><span class="o">-&gt;</span><span class="n">index</span>

<span class="k">if</span> <span class="p">(</span><span class="n">license_index</span> <span class="n">u</span><span class="o">&gt;</span> <span class="mi">9999</span><span class="p">)</span>
    <span class="n">sub_140082940</span><span class="p">(</span>
        <span class="n">f_print_to_stdout</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stdout_ostream</span><span class="p">,</span> 
            <span class="n">string_val</span><span class="o">:</span> <span class="s">"invalid license file"</span><span class="p">),</span> 
        <span class="n">sub_1400c58c0</span><span class="p">)</span>
    <span class="n">rbx_1</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">break</span>
</code></pre></div></div>

<p>The second field is a sequence of 32 bytes that gets passed to a function called <code class="language-plaintext highlighter-rouge">check</code>. If the check function succeeds, a value gets written to the checksum buffer, but if it fails, the license is invalid.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">f_get_func_by_name</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_78</span><span class="p">,</span> <span class="s">"_Z5checkPh"</span><span class="p">,</span> <span class="n">rsi</span><span class="p">)</span>
<span class="kt">char</span> <span class="n">check_result</span> <span class="o">=</span>  <span class="c1">// check function called here</span>
    <span class="p">(</span><span class="o">*</span><span class="n">f_get_next_export_addr</span><span class="p">(</span><span class="n">rbx_2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">var_78</span><span class="p">))(</span><span class="n">license_data</span><span class="p">)</span> <span class="o">^</span> <span class="mi">1</span>
<span class="n">sub_1400b4c10</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_78</span><span class="p">)</span>

<span class="k">if</span> <span class="p">(</span><span class="n">check_result</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
    <span class="n">sub_140082940</span><span class="p">(</span>
        <span class="n">f_print_to_stdout</span><span class="p">(</span><span class="o">&amp;</span><span class="n">stdout_ostream</span><span class="p">,</span> 
            <span class="n">string_val</span><span class="o">:</span> <span class="s">"invalid license file"</span><span class="p">),</span> 
        <span class="n">sub_1400c58c0</span><span class="p">)</span>
    <span class="n">rbx_1</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">break</span>

<span class="n">license_buf</span> <span class="o">=</span> <span class="n">license_data</span> <span class="o">+</span> <span class="mh">0x20</span>
<span class="n">f_write_to_check_buf</span><span class="p">(</span><span class="n">counter</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>

<p>After figuring this out, the license length of 34000 made sense. Each of the 10000 DLLs had a 2-byte index and a 32-byte data sequence associated with it. The index field had to refer to which of the DLLs to run, and the data field was an input that the executable would validate.</p>

<h2 id="the-executable-contents">The Executable Contents</h2>

<h3 id="the-transformation-functions">The Transformation Functions</h3>

<p>Each DLL contains many functions with names starting with <code class="language-plaintext highlighter-rouge">f</code> followed by a series of numbers. Many of these functions are almost identical except for some constant values that they reference, and it turns out they fall into 3 main categories.</p>

<p>The first is a substitution of each byte of the input:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">f73179180583603935578</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>
    <span class="o">*</span><span class="n">arg1</span> <span class="o">^=</span> <span class="o">*</span><span class="n">data_786020</span>
    <span class="kt">uint8_t</span> <span class="n">sbox</span><span class="p">[</span><span class="mh">0x100</span><span class="p">]</span>
    <span class="n">__builtin_memcpy</span><span class="p">(</span><span class="n">dest</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">sbox</span><span class="p">,</span> 
        <span class="n">src</span><span class="o">:</span> <span class="s">"</span><span class="se">\x80\x99\x10\xb8\x5b\x23\x97\xf8\x12\x18\x65\x67\xb5\x02\xc3\x8e\x44\xd7\x01</span><span class="s">\x"</span>
    <span class="s">"d6</span><span class="se">\x7a\x28\x15\x63\x76\x98\x13\x05\x34\x60\xec\x61\xfa\xb4\x4d\xe4\xf2\xb3\xe1\xab</span><span class="s">"</span>
    <span class="s">"ea</span><span class="se">\xb2\xd0\x94\x30\x4f\x0d\x33\xfc\x4a\x5c\xb0\x4c\xd1\x2f\x41\xee\x62\x31\x7b\x54</span><span class="s">"</span>
    <span class="s">"91</span><span class="se">\xf6\xdc\xf7\x1a\xe3\xaa\x71\x3a\x08\xce\xaf\x3b\x86\x82\x3c\x56\x35\xc8\x29\x57</span><span class="s">"</span>
    <span class="s">"3d</span><span class="se">\x43\x64\xff\x9f\xbe\xbd\xa9\x45\xd9\x7f\x3e\xe2\xf5\xb6\x88\x87\xe0\x81\x03\xe9</span><span class="s">"</span>
    <span class="s">"25</span><span class="se">\xcb\xdf\x9d\xdb\xf0\x48\xd5\x49\x68\xc2\x6f\x2c\xbf\x47\x1e\xed\x24\x5e\xe8\x16</span><span class="s">"</span>
    <span class="s">"c9</span><span class="se">\x9b\x09\xd3</span><span class="s">\x"</span>
        <span class="n">count</span><span class="o">:</span> <span class="mh">0x100</span><span class="p">)</span>
    <span class="kt">uint64_t</span> <span class="n">result</span> <span class="o">=</span> <span class="mh">0x27f36e9c748f9017</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int32_t</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="n">s</span><span class="o">&lt;=</span> <span class="mh">0x1f</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">result</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">sbox</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">arg1</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)]))])</span>
        <span class="n">arg1</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)]</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">b</span>
    
    <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>The second is a “shuffle” function that permutes the bytes of the input:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">f49843883851507363229</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>
    <span class="o">*</span><span class="n">arg1</span> <span class="o">^=</span> <span class="o">*</span><span class="n">data_786020</span>
    <span class="kt">uint8_t</span> <span class="n">shuffle</span><span class="p">[</span><span class="mh">0x20</span><span class="p">]</span>
    <span class="n">__builtin_memcpy</span><span class="p">(</span><span class="n">dest</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">shuffle</span><span class="p">,</span> 
        <span class="n">src</span><span class="o">:</span> <span class="s">"</span><span class="se">\x18\x03\x09\x1e\x08\x0b\x07\x1f\x1a\x17\x1b\x1c\x19\x1d\x0c\x13\x15\x11\x02</span><span class="s">\x"</span>
    <span class="s">"0d</span><span class="se">\x0f\x16\x05\x14\x0e\x01\x00\x04\x10\x06\x12\x0a</span><span class="s">"</span><span class="p">,</span> 
        <span class="n">count</span><span class="o">:</span> <span class="mh">0x20</span><span class="p">)</span>
    <span class="kt">uint64_t</span> <span class="n">result</span> <span class="o">=</span> <span class="mh">0x1405160f0d021115</span>
    <span class="kt">int64_t</span> <span class="n">var_58</span>
    <span class="n">__builtin_memset</span><span class="p">(</span><span class="n">dest</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">var_58</span><span class="p">,</span> <span class="n">ch</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">count</span><span class="o">:</span> <span class="mh">0x20</span><span class="p">)</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int32_t</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="n">s</span><span class="o">&lt;=</span> <span class="mh">0x1f</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">result</span> <span class="o">=</span> <span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="o">*</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_58</span> <span class="o">+</span> <span class="n">result</span><span class="p">)</span> <span class="o">=</span> <span class="n">arg1</span><span class="p">[</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">shuffle</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)])]</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int32_t</span> <span class="n">i_1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i_1</span> <span class="n">s</span><span class="o">&lt;=</span> <span class="mh">0x1f</span><span class="p">;</span> <span class="n">i_1</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="o">&amp;</span><span class="n">var_58</span> <span class="o">+</span> <span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i_1</span><span class="p">)))</span>
        <span class="n">arg1</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i_1</span><span class="p">)]</span> <span class="o">=</span> <span class="n">result</span><span class="p">.</span><span class="n">b</span>
    
    <span class="k">return</span> <span class="n">result</span>
</code></pre></div></div>

<p>The third type of function does something more complicated. First it ORs the first bit of the input with 1, while keeping track of what the original value of the bit was. Then, it does something involving a 32-byte constant value, and then the result is XORed with the original first bit again in order to ensure the relationship between input and output remains 1-to-1.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">f92961177136248183669</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>
    <span class="o">*</span><span class="n">arg1</span> <span class="o">^=</span> <span class="o">*</span><span class="n">data_786020</span>
    <span class="kt">char</span> <span class="n">rax_6</span> <span class="o">=</span> <span class="o">*</span><span class="n">arg1</span> <span class="o">&amp;</span> <span class="mi">1</span>
    <span class="o">*</span><span class="n">arg1</span> <span class="o">|=</span> <span class="mi">1</span>
    <span class="kt">uint8_t</span> <span class="n">exp</span><span class="p">[</span><span class="mh">0x20</span><span class="p">]</span>
    <span class="n">__builtin_memcpy</span><span class="p">(</span><span class="n">dest</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">exp</span><span class="p">,</span> 
        <span class="n">src</span><span class="o">:</span> <span class="s">"</span><span class="se">\x5f\xc3\x2c\xb6\x7b\xe3\x24\x44\x19\xfd\x97\x84\x57\x82\xbc\xc1\x80\xad\x21</span><span class="s">\x"</span>
    <span class="s">"43</span><span class="se">\x21\x83\xd1\x6e\xb5\xd0\xe6\xc5\x88\x17\xfb</span><span class="s">"</span><span class="p">,</span> 
        <span class="n">count</span><span class="o">:</span> <span class="mh">0x1f</span><span class="p">)</span>
    <span class="kt">uint8_t</span> <span class="n">result</span><span class="p">[</span><span class="mh">0x20</span><span class="p">]</span>
    <span class="n">__builtin_memcpy</span><span class="p">(</span><span class="n">dest</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">result</span><span class="p">,</span> 
        <span class="n">src</span><span class="o">:</span> <span class="s">"</span><span class="se">\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00</span><span class="s">\x"</span>
    <span class="s">"00</span><span class="se">\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00</span><span class="s">"</span><span class="p">,</span> 

<span class="c1">// [...]</span>

            <span class="k">if</span> <span class="p">((</span><span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">exp</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i_1</span><span class="p">)])</span> <span class="n">s</span><span class="o">&gt;&gt;</span> <span class="n">j</span><span class="p">.</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
                <span class="kt">int32_t</span> <span class="n">var_18_1</span> <span class="o">=</span> <span class="mi">0</span>
                
                <span class="k">for</span> <span class="p">(</span><span class="kt">int32_t</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="n">s</span><span class="o">&lt;=</span> <span class="mh">0x1f</span><span class="p">;</span> <span class="n">k</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">int32_t</span> <span class="n">var_20_1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">var_20_1</span> <span class="n">s</span><span class="o">&lt;=</span> <span class="n">k</span><span class="p">;</span> <span class="n">var_20_1</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span>
                        <span class="n">var_18_1</span> <span class="o">+=</span> <span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">input</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="n">var_20_1</span><span class="p">)])</span>
                            <span class="o">*</span> <span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">var_20_1</span><span class="p">)])</span>

<span class="c1">// [...]</span>

    <span class="o">*</span><span class="n">arg1</span> <span class="o">=</span> <span class="o">*</span><span class="n">arg1</span> <span class="o">^</span> <span class="n">rax_6</span> <span class="o">^</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">arg1</span>

</code></pre></div></div>

<p>To figure out what the mathematical operation was, I first noticed that 1) each bit of the 32-byte constant was checked to see if it was 1 or 0, and 2) if the bit was 1, a multiplication was performed and saved in an accumulator value. I was already familiar with the <a href="https://en.wikipedia.org/wiki/Exponentiation_by_squaring">repeated-squaring method of modular exponentiation</a>, so I guessed that was probably what was happening here. That also explained the OR with the low bit of the base: since the modulus is a power of 2, performing the modular exponentation on an even number would almost certainly have a result of 0. In order to ensure the exponentation operation could be inverted, the exponentation had to be performed on an odd number.</p>

<p>For all three types of functions, the user input is first XORed with a 32-bit integer pulled from the checksum buffer. The index into the array is equal to the numeric index of the DLL.</p>

<p>For example, from <code class="language-plaintext highlighter-rouge">1756.dll</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">f92027960352701313593</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>
    <span class="o">*</span><span class="n">arg1</span> <span class="o">^=</span> <span class="n">xor_data</span><span class="p">[</span><span class="mi">1756</span><span class="p">]</span>
    <span class="kt">int64_t</span> <span class="n">var_38</span>
</code></pre></div></div>

<p>And from <code class="language-plaintext highlighter-rouge">3463.dll</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">f88730141134197849752</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>
    <span class="o">*</span><span class="n">arg1</span> <span class="o">^=</span> <span class="n">xor_data</span><span class="p">[</span><span class="mi">3463</span><span class="p">]</span>
    <span class="kt">int64_t</span> <span class="n">var_118</span>
</code></pre></div></div>

<h3 id="the-check-function">The Check Function</h3>

<p>The numbered functions are called in a function called <code class="language-plaintext highlighter-rouge">check</code>. Each executable imports numbered functions from other executables and calls them in addition to its own.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">check</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">arg1</span><span class="p">)</span>
    <span class="n">f38236877289593244403</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f32422423688401106395</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f14945866699034032513</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f42785923630423812381</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f01804799128886574724</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f63971385288077461058</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f32043095044683465902</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f35008263310466305120</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f39008422158736727341</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span> <span class="c1">// Import</span>
    <span class="n">f77204538293621348673</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f63086630871216239504</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f62306332011382063629</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f16456010376118152083</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span>
    <span class="n">f09449763057560931840</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span> <span class="c1">// Import</span>
    <span class="n">f52479682207617065503</span><span class="p">(</span><span class="n">arg1</span><span class="p">)</span> <span class="c1">// Import</span>
</code></pre></div></div>

<p>After all of these transformations are called on the input to <code class="language-plaintext highlighter-rouge">check</code>, we get to the actual checking step. First, the 32-byte transformed input gets XORed with the values in an array of 16 128-bit integers. Binary Ninja had a hard time with the decompilation here for some reson, but this loop is just XORing one array with another.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">compare_val</span> <span class="o">=</span> <span class="mh">0xdc37c0e304978087</span>
<span class="kt">int64_t</span> <span class="n">var_80</span> <span class="o">=</span> <span class="mi">0</span>
<span class="kt">int64_t</span> <span class="n">var_90</span> <span class="o">=</span> <span class="mh">0x594b7f91f11228e5</span>
<span class="n">int128_t</span> <span class="n">xor_vals</span><span class="p">[</span><span class="mh">0x10</span><span class="p">]</span>
<span class="n">xor_vals</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">q</span> <span class="o">=</span> <span class="mh">0x264f1c2a310e43aa</span>
<span class="n">xor_vals</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">xor_vals</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">q</span> <span class="o">=</span> <span class="mh">0x6f62577ddb8f7c8</span>
<span class="c1">// [...]</span>
<span class="n">xor_vals</span><span class="p">[</span><span class="mh">0xe</span><span class="p">]</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">xor_vals</span><span class="p">[</span><span class="mh">0xf</span><span class="p">].</span><span class="n">q</span> <span class="o">=</span> <span class="mh">0xf3a55fbbc4837e78</span>
<span class="n">xor_vals</span><span class="p">[</span><span class="mh">0xf</span><span class="p">]</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">int128_t</span> <span class="n">_Buf1</span><span class="p">[</span><span class="mh">0x10</span><span class="p">]</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int32_t</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="n">s</span><span class="o">&lt;=</span> <span class="mh">0xf</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="kt">int64_t</span><span class="o">*</span> <span class="n">rax_595</span> <span class="o">=</span> <span class="p">(</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x530</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">_Buf1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">-</span> <span class="mh">0x170</span>
    <span class="kt">uint32_t</span> <span class="n">rax_598</span> <span class="o">=</span> <span class="n">i</span> <span class="n">s</span><span class="o">&gt;&gt;</span> <span class="mh">0x1f</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mh">0x1e</span>
    <span class="kt">int64_t</span> <span class="n">rdx_6</span> <span class="o">=</span> <span class="n">rax_595</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="kt">int64_t</span><span class="o">*</span> <span class="n">rcx_595</span> <span class="o">=</span> <span class="p">(</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x530</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">_Buf1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">-</span> <span class="mh">0x170</span>
    <span class="c1">// XOR the input data with value from XOR table, and write</span>
    <span class="c1">// it back to the XOR table. Each input value gets xored</span>
    <span class="c1">// with 4 table values.</span>
    <span class="o">*</span><span class="n">rcx_595</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">arg1</span> <span class="o">+</span> <span class="p">(</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(((</span><span class="n">i</span> <span class="o">+</span> <span class="n">rax_598</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">-</span> <span class="n">rax_598</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">3</span><span class="p">))</span> <span class="o">^</span> <span class="o">*</span><span class="n">rax_595</span>
    <span class="n">rcx_595</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">rdx_6</span>
    <span class="kt">int64_t</span><span class="o">*</span> <span class="n">rax_611</span> <span class="o">=</span> <span class="p">(</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x530</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">_Buf1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">-</span> <span class="mh">0x170</span>
    <span class="n">rax_611</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
    <span class="o">*</span><span class="n">rax_611</span>
    <span class="n">bool</span> <span class="n">c_2</span> <span class="o">=</span> <span class="n">unimplemented</span>  <span class="p">{</span><span class="n">sbb</span> <span class="n">rax</span><span class="p">,</span> <span class="n">qword</span> <span class="p">[</span><span class="n">rbp</span><span class="o">+</span><span class="mh">0x548</span><span class="p">]}</span>
    
    <span class="c1">// All the XORs need to be less than compare_val</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">not</span><span class="p">(</span><span class="n">c_2</span><span class="p">))</span>
        <span class="k">return</span> <span class="mi">0</span>
</code></pre></div></div>

<p>Then we have a really long unrolled loop with a lot of modular additions and multiplications:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// [...]</span>
<span class="kt">int64_t</span> <span class="n">rax_686</span> <span class="o">=</span> <span class="n">out_33</span><span class="p">.</span><span class="n">q</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">rax_686</span> <span class="o">+</span> <span class="n">out_22</span><span class="p">.</span><span class="n">q</span>
<span class="kt">int64_t</span> <span class="n">var_5c0_34</span> <span class="o">=</span> <span class="n">adc</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">out_33</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span><span class="p">,</span> <span class="n">out_22</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span><span class="p">,</span> <span class="n">rax_686</span> <span class="o">+</span> <span class="n">out_22</span><span class="p">.</span><span class="n">q</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">rax_686</span><span class="p">)</span>
<span class="n">modulus</span> <span class="o">=</span> <span class="n">compare_val</span>
<span class="kt">int64_t</span> <span class="n">var_5d0_35</span> <span class="o">=</span> <span class="n">var_80</span>
<span class="n">int128_t</span> <span class="n">out_34</span> <span class="o">=</span> <span class="n">modulo_int128</span><span class="p">(</span><span class="o">&amp;</span><span class="n">val</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">modulus</span><span class="p">,</span> <span class="n">out</span><span class="o">:</span> <span class="n">out_33</span><span class="p">)</span>
<span class="c1">// [...]</span>
</code></pre></div></div>

<p>And then, finally, the result of that is another array of 16 128-bit integers, which is compared against a target value. If they match, we’ve passed the check.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// [...]</span>
<span class="n">_Buf1</span><span class="p">[</span><span class="mh">0xd</span><span class="p">]</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">_Buf1</span><span class="p">[</span><span class="mh">0xe</span><span class="p">].</span><span class="n">q</span> <span class="o">=</span> <span class="mh">0xa5b7c08151fface8</span>
<span class="n">_Buf1</span><span class="p">[</span><span class="mh">0xe</span><span class="p">]</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">_Buf1</span><span class="p">[</span><span class="mh">0xf</span><span class="p">].</span><span class="n">q</span> <span class="o">=</span> <span class="mh">0xc7b8d0a6d71a6e00</span>
<span class="n">_Buf1</span><span class="p">[</span><span class="mh">0xf</span><span class="p">]</span><span class="o">:</span><span class="mi">8</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="mi">0</span>
<span class="kt">int32_t</span> <span class="n">rax_884</span>
<span class="n">rax_884</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">memcmp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">_Buf1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">_Buf2</span><span class="p">,</span> <span class="n">_Size</span><span class="o">:</span> <span class="mh">0x100</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</code></pre></div></div>

<p>I guessed pretty early on that the operations that were happening in the unrolled loop could be some kind of operations on a matrix, with the array of 16 values being treated as a 4x4 matrix. (It helped that I’d seen a few people complaining that the challenge was too hard because there was math in it.) This was confirmed when I looked at which indices of the array were being multiplied together at each step and realized that it was consistent with the calculation of a determinant.</p>

<p>The <code class="language-plaintext highlighter-rouge">check</code> function returns failure if the determinant it calulates is 0, which told me that whatever step came next required the 4x4 matrix to be invertible. Rather than manually reverse all the math, I tried patching in a bunch of test values for the matrix in x64dbg and checked to see what the final result was. For example, passing in the identity matrix as an input gave me the identity matrix as an output, and passing in a diagonal matrix other than the identity as an input gave me a different diagonal matrix as an output. After trying out a couple different common operations that might produce this result, it turned out that the operation was modular exponentiation, with each <code class="language-plaintext highlighter-rouge">check</code> function using a different prime modulus.</p>

<p>(This kind of educated guessing is almost always how I approach reverse engineering mathematical functions. Often, something that looks like a very complicated algorithm turns out to be something simple like multiplication or exponentation over big integers, so it’s a good idea to test for that first. It’s often possible to make a very good guess about what a function does just by looking at what happens when one of the inputs is 0 or 1. In this case, I was already on the lookout for modular exponentiation because it was one of the three types of transformation functions called before <code class="language-plaintext highlighter-rouge">check</code>.)</p>

<h2 id="the-dependency-graph">The Dependency Graph</h2>

<h3 id="the-checksum-buffer">The Checksum Buffer</h3>

<p>After each of the DLLs is finished running, the checksum buffer is written to. The checksum buffer is an array of 10000 32-bit integers: one for each of the DLLs. The program iterates through each of the currently loaded DLLs, then adds the current value of the counter to the index associated with each DLL before freeing the memory associated with the DLL.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int64_t</span> <span class="n">f_write_to_check_buf</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">counter</span><span class="p">)</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">var_10</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">resources_vec</span>
    <span class="kt">int64_t</span> <span class="n">resources_start</span> <span class="o">=</span> <span class="n">f_get_vec_start</span><span class="p">(</span><span class="o">&amp;</span><span class="n">resources_vec</span><span class="p">)</span>
    <span class="kt">int64_t</span> <span class="n">resources_end</span> <span class="o">=</span> <span class="n">f_get_vec_end</span><span class="p">(</span><span class="n">var_10</span><span class="p">)</span>
    
    <span class="k">while</span> <span class="p">(</span><span class="n">f_compare_args</span><span class="p">(</span><span class="o">&amp;</span><span class="n">resources_start</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">resources_end</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">struct</span> <span class="n">resource_struct</span><span class="o">*</span> <span class="n">rax_4</span> <span class="o">=</span> <span class="o">*</span><span class="n">f_dereference_ptr</span><span class="p">(</span><span class="o">&amp;</span><span class="n">resources_start</span><span class="p">)</span>
        <span class="o">*</span><span class="p">((</span><span class="n">sx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">rax_4</span><span class="o">-&gt;</span><span class="n">index</span><span class="p">.</span><span class="n">w</span><span class="p">))</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="o">&amp;</span><span class="n">check_buf</span><span class="p">)</span> <span class="o">+=</span> <span class="n">counter</span>
        <span class="n">VirtualFree</span><span class="p">(</span><span class="n">lpAddress</span><span class="o">:</span> <span class="n">rax_4</span><span class="o">-&gt;</span><span class="n">addr</span><span class="p">,</span> <span class="n">dwSize</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">dwFreeType</span><span class="o">:</span> <span class="n">MEM_RELEASE</span><span class="p">)</span>
        <span class="n">sub_140020560</span><span class="p">(</span><span class="o">&amp;</span><span class="n">resources_start</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">sub_1400af7b0</span><span class="p">(</span><span class="o">&amp;</span><span class="n">resources_vec</span><span class="p">)</span>
</code></pre></div></div>

<p>If only a single DLL was loaded each time, it would be easy to determine the order in which the DLLs were supposed to run based on the expected final value of the checksum buffer. For example, if index 5402 contained the number 1, we would know <code class="language-plaintext highlighter-rouge">5402.dll</code> was supposed to run when the counter equaled 1.</p>

<p>However, more than one DLL is loaded each time: the DLLs depend on each other, and when a DLL is loaded, its dependencies need to be loaded (and the dependencies of those dependencies, etc.). That means that the value contained in each index is the sum of all counter values where the corresponding DLL was loaded as a dependency somewhere along the chain.</p>

<h3 id="constructing-the-import-graph">Constructing the Import Graph</h3>

<p>I started by traversing the imports section of each of the DLLs to determine the direct imports of each DLL, and wrote the result to a big JSON file.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_imports</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
    <span class="n">imports</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">filepath</span> <span class="o">=</span> <span class="n">exe_paths</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
    <span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="p">.</span><span class="n">PE</span><span class="p">(</span><span class="s">'exes/'</span> <span class="o">+</span> <span class="n">filepath</span><span class="p">)</span>
    <span class="n">pe</span><span class="p">.</span><span class="n">parse_data_directories</span><span class="p">(</span><span class="n">directories</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>  <span class="c1"># DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']
</span>    <span class="k">for</span> <span class="n">iid</span> <span class="ow">in</span> <span class="n">pe</span><span class="p">.</span><span class="n">DIRECTORY_ENTRY_IMPORT</span><span class="p">:</span>
        <span class="n">dll_name</span> <span class="o">=</span> <span class="n">iid</span><span class="p">.</span><span class="n">dll</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'ascii'</span><span class="p">)</span>
        <span class="n">num</span> <span class="o">=</span> <span class="n">dll_name</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">num</span><span class="p">.</span><span class="n">isdecimal</span><span class="p">():</span>
            <span class="n">imports</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">num</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">imports</span>

<span class="n">imports</span> <span class="o">=</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">10000</span><span class="p">):</span>
    <span class="n">imports</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_imports</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

<span class="n">j</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">imports</span><span class="p">)</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'imports.json'</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">j</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>

<p>I then recursively traversed the parsed list of imports for each of the DLLs, giving me a full list of the imports that would be loaded for each DLL (i.e., a list that included not just direct dependencies, but also dependencies of those dependencies, and so on), and wrote that to an even bigger JSON file. (I’m sure there are much nicer ways to create and store large graphs in Python, but to be honest, I was well into “extremely cursed CTF code” territory at this point and I didn’t feel like learning about any of those.)</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">visited</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_imports_recursive</span><span class="p">(</span><span class="n">idx</span><span class="p">,</span> <span class="n">visited</span><span class="p">):</span>
    <span class="n">imports</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">imports_json</span><span class="p">[</span><span class="n">idx</span><span class="p">]:</span>
        <span class="k">if</span> <span class="n">num</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">visited</span><span class="p">:</span>
            <span class="n">imports</span> <span class="o">+=</span> <span class="n">get_imports_recursive</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="n">visited</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">'already visited'</span><span class="p">,</span> <span class="n">num</span><span class="p">)</span>
    <span class="n">imports</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span>
    <span class="n">visited</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">imports</span>

<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'imports_chain.jsonl'</span><span class="p">,</span> <span class="s">'w'</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">10000</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="s">'Getting imports for:'</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
    <span class="n">imps_i</span> <span class="o">=</span> <span class="n">get_imports_recursive</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="nb">set</span><span class="p">())</span>
    <span class="n">j</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">({</span><span class="n">i</span><span class="p">:</span> <span class="n">imps_i</span><span class="p">})</span>
    <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">j</span> <span class="o">+</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
</code></pre></div></div>

<p>One thing that I noticed during this process was that there were no circular dependencies, i.e., there are no DLLs <code class="language-plaintext highlighter-rouge">A.dll</code> and <code class="language-plaintext highlighter-rouge">B.dll</code> such that <code class="language-plaintext highlighter-rouge">A.dll</code> imports <code class="language-plaintext highlighter-rouge">B.dll</code> and <code class="language-plaintext highlighter-rouge">B.dll</code> imports <code class="language-plaintext highlighter-rouge">A.dll</code>. That meant that the chain of imports had to eventually end in DLLs that aren’t loaded by anything other than themselves. It’s easy to figure out where those DLLs fall in the run order: if <code class="language-plaintext highlighter-rouge">A.dll</code> is only loaded once and the checksum buffer contains the value <code class="language-plaintext highlighter-rouge">x</code> at index <code class="language-plaintext highlighter-rouge">A</code>, then it’s supposed to be run when the counter is at value <code class="language-plaintext highlighter-rouge">x</code>.</p>

<p>Now consider a slightly more complicated case: Suppose <code class="language-plaintext highlighter-rouge">A.dll</code> and <code class="language-plaintext highlighter-rouge">B.dll</code> aren’t loaded by any other DLLs, and that their corresponding checksum buffer values are <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> respectively. Now suppose <code class="language-plaintext highlighter-rouge">C.dll</code> is a DLL loaded only by <code class="language-plaintext highlighter-rouge">A.dll</code> and <code class="language-plaintext highlighter-rouge">B.dll</code>, and the value in its checksum buffer is <code class="language-plaintext highlighter-rouge">z</code>. Then the value in the checksum buffer is equal to the sum of the counter values when <code class="language-plaintext highlighter-rouge">A.dll</code>, <code class="language-plaintext highlighter-rouge">B.dll</code>, and <code class="language-plaintext highlighter-rouge">C.dll</code> are run, since those are all the times when <code class="language-plaintext highlighter-rouge">C.dll</code> is loaded. That means that C must be run when the counter is at <code class="language-plaintext highlighter-rouge">z - x - y</code>.</p>

<p>Note that the dependency graph I initially constructed tracks the DLLs that each DLL <em>imports</em>, but what we actually need for this calculation is a graph of the DLLs that each DLL is <em>imported by</em>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">imported_by</span> <span class="o">=</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">10000</span><span class="p">):</span>
    <span class="n">imported_by</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">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'imports_chain.jsonl'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">.</span><span class="n">split</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">data</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">data</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
        <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">v</span><span class="p">:</span>
            <span class="n">imported_by</span><span class="p">[</span><span class="n">val</span><span class="p">].</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">k</span><span class="p">))</span>
</code></pre></div></div>

<p>We can then recursively walk the “imported by” graph until we get to a DLL that isn’t imported by anything, and calculate the counter value for each of the DLLs.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">done</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">def</span> <span class="nf">walk_graph</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">idx</span> <span class="ow">in</span> <span class="n">done</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">done</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
    <span class="n">counter_val</span> <span class="o">=</span> <span class="n">targets</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
    <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">imported_by</span><span class="p">[</span><span class="n">idx</span><span class="p">]:</span>
        <span class="n">counter_val</span> <span class="o">-=</span> <span class="n">walk_graph</span><span class="p">(</span><span class="n">num</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">'Got:'</span><span class="p">,</span> <span class="n">idx</span><span class="p">,</span> <span class="n">counter_val</span><span class="p">)</span>
    <span class="n">done</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">counter_val</span>
    <span class="k">return</span> <span class="n">counter_val</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">10000</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">i</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">done</span><span class="p">:</span>
        <span class="n">walk_graph</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
</code></pre></div></div>

<p>As expected, the values obtained from the script were each of the numbers 0 through 9999, which told me I had the right order for the DLLs.</p>

<h2 id="passing-the-check">Passing the Check</h2>

<h3 id="inverting-the-matrix-exponentiation">Inverting the Matrix Exponentiation</h3>

<p>First, I needed to extract the target matrix to compare to, along with the modulus, the exponent, and the table of XOR values that are used to generate the matrix to exponentiate. For something like this I would normally use a disassembler like capstone, but since there are so many functions to process, it would’ve taken a long time. Luckily, there was very little variation in the disassembly of the functions, so it was possible to extract everything using regular expressions. The constant values were always loaded using the register <code class="language-plaintext highlighter-rouge">rax</code>, and they were always loaded in the same order: the modulus, then the exponent, then the XOR table, then the target matrix.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pefile</span>
<span class="kn">import</span> <span class="nn">glob</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">re</span>

<span class="c1"># 4881c4c0000000     add     rsp, 0xc0            |   4883c450           add     rsp, 0x50  | 415f               pop     r15 {__saved_r15}
# 5d                 pop     rbp {__saved_rbp}
# c3                 retn     {__return_addr}
</span><span class="n">cleanup_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'(\x48\x81\xc4....|\x48\x83\xc4.|\x41\x5f)\x5d\xc3'</span>

<span class="c1"># 55                 push    rbp {__saved_rbp}
# 4889e5             mov     rbp, rsp {__saved_rbp}  | 4881ec10010000     sub     rsp, 0x110 | 4157               push    r15 {__saved_r15}
</span><span class="n">start_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x55(\x48\x89\xe5|\x48\x81\xec....|\x41\x57)'</span>

<span class="n">function_expr</span> <span class="o">=</span> <span class="n">start_expr</span> <span class="o">+</span> <span class="sa">rb</span><span class="s">'.*?'</span> <span class="o">+</span> <span class="n">cleanup_expr</span>

<span class="k">def</span> <span class="nf">build_matrix</span><span class="p">(</span><span class="n">l</span><span class="p">):</span>
    <span class="n">m</span> <span class="o">=</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">4</span><span class="p">):</span>
        <span class="n">m</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</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">4</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
            <span class="n">m</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">l</span><span class="p">[</span><span class="mi">4</span><span class="o">*</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">m</span>

<span class="c1"># 48b8????????????????   mov     rax, ????????????????    
# ba00000000         mov     edx, 0x0   
</span><span class="n">mov_rax_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x48\xb8(?P&lt;constval&gt;.{8})'</span> <span class="o">+</span> <span class="sa">rb</span><span class="s">'(\xba\x00\x00\x00\x00|\x48\x89)'</span>

<span class="k">def</span> <span class="nf">extract_matrix</span><span class="p">(</span><span class="n">mapped</span><span class="p">,</span> <span class="n">startaddr</span><span class="p">,</span> <span class="n">endaddr</span><span class="p">):</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">mapped</span><span class="p">[</span><span class="n">startaddr</span><span class="p">:</span><span class="n">endaddr</span><span class="p">]</span>
    <span class="n">consts</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">mov_rax_expr</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">):</span>
        <span class="n">constval</span> <span class="o">=</span> <span class="n">i</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="s">"constval"</span><span class="p">)</span>
        <span class="n">consts</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">constval</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">consts</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">34</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'extraction failed'</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">consts</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">e</span> <span class="o">=</span> <span class="n">consts</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
        <span class="n">xors</span> <span class="o">=</span> <span class="n">consts</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="mi">18</span><span class="p">]</span>
        <span class="n">matrix</span> <span class="o">=</span> <span class="n">consts</span><span class="p">[</span><span class="mi">18</span><span class="p">:</span><span class="mi">34</span><span class="p">]</span>
        <span class="k">return</span> <span class="n">n</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">xors</span><span class="p">,</span> <span class="n">build_matrix</span><span class="p">(</span><span class="n">matrix</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">try_extract</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">out</span><span class="p">):</span>
    <span class="n">r</span> <span class="o">=</span> <span class="p">{}</span>

    <span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="p">.</span><span class="n">PE</span><span class="p">(</span><span class="n">filepath</span><span class="p">)</span>
    <span class="n">mapped</span> <span class="o">=</span> <span class="n">pe</span><span class="p">.</span><span class="n">get_memory_mapped_image</span><span class="p">()</span>

    <span class="n">d</span> <span class="o">=</span> <span class="p">[</span><span class="n">pefile</span><span class="p">.</span><span class="n">DIRECTORY_ENTRY</span><span class="p">[</span><span class="s">"IMAGE_DIRECTORY_ENTRY_EXPORT"</span><span class="p">]]</span>
    <span class="n">pe</span><span class="p">.</span><span class="n">parse_data_directories</span><span class="p">(</span><span class="n">directories</span><span class="o">=</span><span class="n">d</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">sym</span> <span class="ow">in</span> <span class="n">pe</span><span class="p">.</span><span class="n">DIRECTORY_ENTRY_EXPORT</span><span class="p">.</span><span class="n">symbols</span><span class="p">:</span>
        <span class="n">r</span><span class="p">[</span><span class="n">sym</span><span class="p">.</span><span class="n">name</span><span class="p">.</span><span class="n">decode</span><span class="p">()]</span> <span class="o">=</span> <span class="n">sym</span><span class="p">.</span><span class="n">address</span>

    <span class="c1"># Find each function by matching the function prologue and epilogue
</span>    <span class="n">functions</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">function_expr</span><span class="p">,</span> <span class="n">mapped</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">):</span>
        <span class="n">functions</span><span class="p">[</span><span class="n">i</span><span class="p">.</span><span class="n">start</span><span class="p">()]</span> <span class="o">=</span> <span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">start</span><span class="p">(),</span> <span class="n">i</span><span class="p">.</span><span class="n">end</span><span class="p">())</span>

    <span class="c1"># Get the name of each function
</span>    <span class="n">ranges</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
        <span class="k">if</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">functions</span><span class="p">:</span>
            <span class="n">ranges</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">functions</span><span class="p">[</span><span class="n">v</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">'fail:'</span><span class="p">,</span> <span class="n">filepath</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>

    <span class="k">try</span><span class="p">:</span>
        <span class="n">n</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">xors</span><span class="p">,</span> <span class="n">matrix</span> <span class="o">=</span> <span class="n">extract_matrix</span><span class="p">(</span><span class="n">mapped</span><span class="p">,</span> <span class="n">ranges</span><span class="p">[</span><span class="s">'_Z5checkPh'</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">ranges</span><span class="p">[</span><span class="s">'_Z5checkPh'</span><span class="p">][</span><span class="mi">1</span><span class="p">])</span>
    <span class="k">except</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'fail:'</span><span class="p">,</span> <span class="n">filepath</span><span class="p">)</span>
    <span class="n">j</span> <span class="o">=</span> <span class="p">{</span><span class="n">filepath</span><span class="p">:</span> <span class="p">{</span><span class="s">'n'</span><span class="p">:</span> <span class="n">n</span><span class="p">,</span> <span class="s">'e'</span><span class="p">:</span> <span class="n">e</span><span class="p">,</span> <span class="s">'x'</span><span class="p">:</span> <span class="n">xors</span><span class="p">,</span> <span class="s">'m'</span><span class="p">:</span> <span class="n">matrix</span><span class="p">}}</span>
    <span class="n">out</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">j</span><span class="p">)</span> <span class="o">+</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>

<span class="n">out</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'matrices.json'</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span>

<span class="n">g</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="s">'exes/exe_0x*.exe'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">filepath</span> <span class="ow">in</span> <span class="n">g</span><span class="p">:</span>
    <span class="n">try_extract</span><span class="p">(</span><span class="n">filepath</span><span class="p">,</span> <span class="n">out</span><span class="p">)</span>
</code></pre></div></div>

<p>Once I had the target matrix <code class="language-plaintext highlighter-rouge">mat</code>, modulus <code class="language-plaintext highlighter-rouge">n</code>, and exponent <code class="language-plaintext highlighter-rouge">e</code>, I needed to invert the exponentiation operation. As it turned out, the way I ended up doing this was needlessly complicated, so I’ll cover both my way of solving it and the way that was given in the official writeup.</p>

<h4 id="my-solve-script">My solve script</h4>

<p>My first thought was that there’s one case where it’s obvious how to approach the problem: if <code class="language-plaintext highlighter-rouge">mat</code> is diagonal, then we can find the <code class="language-plaintext highlighter-rouge">e</code>th root of <code class="language-plaintext highlighter-rouge">mat</code> just by taking the <code class="language-plaintext highlighter-rouge">e</code>th root of each entry. My linear algebra knowledge is a little rusty at this point, but I at least remembered enough to know that many matrices can be <a href="https://en.wikipedia.org/wiki/Diagonalizable_matrix">diagonalized</a>. From the Wikipedia page:</p>

<blockquote>
  <p>In linear algebra, a square matrix <code class="language-plaintext highlighter-rouge">A</code>  is called <strong>diagonalizable</strong> or <strong>non-defective</strong> if it is similar to a diagonal matrix. That is, if there exists an invertible matrix <code class="language-plaintext highlighter-rouge">P</code> and a diagonal matrix <code class="language-plaintext highlighter-rouge">D</code> such that <code class="language-plaintext highlighter-rouge">P**-1 * A * P = D</code>. This is equivalent to <code class="language-plaintext highlighter-rouge">A = P * D * P**-1</code>.</p>
</blockquote>

<p>Note that if <code class="language-plaintext highlighter-rouge">A = P * D * P**-1</code>, then <code class="language-plaintext highlighter-rouge">A**e =  (P * D * P**-1)**e = P * D**e * P**-1</code>. That means that if <code class="language-plaintext highlighter-rouge">A</code> is diagonalizable, then the problem of finding the <code class="language-plaintext highlighter-rouge">e</code>th root of <code class="language-plaintext highlighter-rouge">A</code> reduces to the easier problem of finding the <code class="language-plaintext highlighter-rouge">e</code>th root of <code class="language-plaintext highlighter-rouge">D</code>.</p>

<p>Unfortunately, not all the given matrices were diagonalizable. However, sometimes a matrix can be diagonalizable over one field but not another. For example, the matrix <code class="language-plaintext highlighter-rouge">[[0, -1], [1, 0]]</code> is not diagonalizable over the real numbers, but is diagonalizable over the complex numbers:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>([[0, i], [-i, 0]])**-1  * [[0, -1], [1, 0]] * [[0, i], [-i, 0]] = [[i, 0], [0, -i]]
</code></pre></div></div>

<p>The case we’re looking at is a little different, as the elements of the matrices we’re given are not real numbers, but members of a finite field. However, it’s possible to extend other fields in a way that’s similar to going from the real numbers to the complex numbers, in a process called taking the <a href="https://en.wikipedia.org/wiki/Algebraic_closure">algebraic closure</a>. It turned out that even though the given matrices weren’t diagonalizable over the given finite fields, they <em>were</em> diagonalizable over the algebraic closures of those fields.</p>

<p>The final Sage script I ended up with is as follows:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">json</span>

<span class="k">def</span> <span class="nf">get_eth_root</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">mat</span><span class="p">):</span>
    <span class="n">A</span> <span class="o">=</span> <span class="n">Matrix</span><span class="p">(</span><span class="n">GF</span><span class="p">(</span><span class="n">n</span><span class="p">).</span><span class="n">algebraic_closure</span><span class="p">(),</span> <span class="n">mat</span><span class="p">)</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="n">A</span><span class="p">.</span><span class="n">is_diagonalizable</span><span class="p">():</span>
        <span class="k">return</span> <span class="bp">None</span>

    <span class="p">(</span><span class="n">D</span><span class="p">,</span><span class="n">P</span><span class="p">)</span><span class="o">=</span><span class="n">A</span><span class="p">.</span><span class="n">right_eigenmatrix</span><span class="p">()</span>

    <span class="n">prod</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">inverse</span><span class="p">()</span> <span class="o">*</span> <span class="n">A</span> <span class="o">*</span> <span class="n">P</span>

    <span class="n">x0</span> <span class="o">=</span> <span class="n">prod</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">].</span><span class="n">nth_root</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
    <span class="n">x1</span> <span class="o">=</span> <span class="n">prod</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">].</span><span class="n">nth_root</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
    <span class="n">x2</span> <span class="o">=</span> <span class="n">prod</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">2</span><span class="p">].</span><span class="n">nth_root</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
    <span class="n">x3</span> <span class="o">=</span> <span class="n">prod</span><span class="p">[</span><span class="mi">3</span><span class="p">][</span><span class="mi">3</span><span class="p">].</span><span class="n">nth_root</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>

    <span class="n">X</span> <span class="o">=</span> <span class="n">Matrix</span><span class="p">(</span><span class="n">GF</span><span class="p">(</span><span class="n">n</span><span class="p">).</span><span class="n">algebraic_closure</span><span class="p">(),</span> <span class="p">[</span>
    <span class="p">[</span><span class="n">x0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
    <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="n">x1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
    <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
    <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x3</span><span class="p">]</span>
    <span class="p">])</span>

    <span class="n">X2</span> <span class="o">=</span> <span class="n">P</span> <span class="o">*</span> <span class="n">X</span> <span class="o">*</span> <span class="n">P</span><span class="p">.</span><span class="n">inverse</span><span class="p">()</span>

    <span class="n">vals</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">X2</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">row</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">vals</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)))</span>
            <span class="k">except</span><span class="p">:</span>
                <span class="k">return</span> <span class="bp">None</span>
    <span class="k">return</span> <span class="n">vals</span>

<span class="n">lines</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'matrices.json'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>

<span class="n">out</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'roots.json'</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">.</span><span class="n">split</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">j</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">filename</span><span class="p">,</span> <span class="n">data</span> <span class="ow">in</span> <span class="n">j</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
        <span class="n">root_mat</span> <span class="o">=</span> <span class="n">get_eth_root</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="s">'n'</span><span class="p">],</span> <span class="n">data</span><span class="p">[</span><span class="s">'e'</span><span class="p">],</span> <span class="n">data</span><span class="p">[</span><span class="s">'m'</span><span class="p">])</span>
        <span class="n">res</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">({</span><span class="n">filename</span><span class="p">:</span> <span class="n">root_mat</span><span class="p">})</span>
        <span class="n">out</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">res</span> <span class="o">+</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
</code></pre></div></div>

<h4 id="the-simpler-way-to-solve-it">The simpler way to solve it</h4>

<p>My solution was overly complicated because I got way too hung up on the idea of diagonalizing the matrices, when I really should’ve realized that the matrices didn’t need to be diagonalized at all. In fact, finding the <code class="language-plaintext highlighter-rouge">e</code>th root of a matrix isn’t any harder, or fundamentally any different, from finding the <code class="language-plaintext highlighter-rouge">e</code>th root of one of its elements.</p>

<p>To review, we can find the <code class="language-plaintext highlighter-rouge">e</code>th root of a number <code class="language-plaintext highlighter-rouge">m</code> modulo a prime number <code class="language-plaintext highlighter-rouge">p</code> by taking advantage of <a href="https://en.wikipedia.org/wiki/Fermat's_little_theorem">Fermat’s little theorem</a>, which states that <code class="language-plaintext highlighter-rouge">m**(p-1) = 1</code> mod <code class="language-plaintext highlighter-rouge">p</code>. Then if we can find an integer <code class="language-plaintext highlighter-rouge">d</code> such that <code class="language-plaintext highlighter-rouge">d * e = k * (p-1) + 1</code>, for some integer <code class="language-plaintext highlighter-rouge">k</code>, then <code class="language-plaintext highlighter-rouge">m**(d * e) = m</code> mod <code class="language-plaintext highlighter-rouge">p</code>, so <code class="language-plaintext highlighter-rouge">(m**d)**e = m</code> mod <code class="language-plaintext highlighter-rouge">p</code>. Thus <code class="language-plaintext highlighter-rouge">m**d</code> is the desired <code class="language-plaintext highlighter-rouge">e</code>th root of <code class="language-plaintext highlighter-rouge">m</code> mod <code class="language-plaintext highlighter-rouge">p</code>.</p>

<p>Finding the <code class="language-plaintext highlighter-rouge">e</code>th root of a 4x4 matrix modulo <code class="language-plaintext highlighter-rouge">p</code> is a very similar process. The set of 4x4 matrices modulo <code class="language-plaintext highlighter-rouge">p</code> forms a group called the <a href="https://en.wikipedia.org/wiki/General_linear_group">general linear group</a> of 4x4 matrices over the integers mod <code class="language-plaintext highlighter-rouge">p</code>. Let <code class="language-plaintext highlighter-rouge">g</code> be the order of this group. Then by <a href="https://en.wikipedia.org/wiki/Lagrange%27s_theorem_(group_theory)">Lagrange’s theorem</a>, for a matrix <code class="language-plaintext highlighter-rouge">A</code> in this group, <code class="language-plaintext highlighter-rouge">A ** g = I</code>, where <code class="language-plaintext highlighter-rouge">I</code> is the identity matrix. Then if we can find an integer <code class="language-plaintext highlighter-rouge">d</code> such that <code class="language-plaintext highlighter-rouge">d * e = k * g + 1</code> for some integer <code class="language-plaintext highlighter-rouge">k</code>, then <code class="language-plaintext highlighter-rouge">A**(d * e) = A</code>, so <code class="language-plaintext highlighter-rouge">(A**d)**e = A</code>. Thus <code class="language-plaintext highlighter-rouge">A**d</code> is the desired <code class="language-plaintext highlighter-rouge">e</code>th root of <code class="language-plaintext highlighter-rouge">A</code>. The only extra step we have to do here is to find the order of the group, but that’s a well-known problem with lots of <a href="https://math.stackexchange.com/questions/34271/order-of-general-and-special-linear-groups-over-finite-fields">writeups</a> available.</p>

<p>See the <a href="https://security.googlecloudcommunity.com/community-blog-42/flare-on-12-challenge-solutions-6061">official writeup</a>, as well as unofficial writeups from <a href="https://jro.sg/CTFs/Flare-On%2012/10000.html#a-matrix-problem">jro</a> and <a href="https://gist.github.com/superfashi/563425ee96d505c0263373230335e41a">SuperFashi</a>, for more explanations of this.</p>

<h3 id="inverting-the-transformation-functions">Inverting the Transformation Functions</h3>

<h4 id="detecting-the-function-types">Detecting the Function Types</h4>

<p>After calculating the 32-byte transformed input that produced the correct matrix, the next step was to invert each of the transformation functions in order to determine the input from the license file that would be required.</p>

<p>For any given transformation, I needed a way to determine if it was an sbox, a shuffle, or an exponentiation. I would also need to extract the relevant operand from each one (the sbox, the indices of the shuffle, or the exponent). Again, I used binary regular expressions to extract the data.</p>

<p>For example, all three transformation types use <code class="language-plaintext highlighter-rouge">rax</code> and <code class="language-plaintext highlighter-rouge">rdx</code> to initialize immediate values, so the following regex could be used to extract the operand:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># 48b8????????????????  mov     rax, ????????????????
# 48ba????????????????  mov     rdx, ????????????????
mov_expr = rb'\x48\xb8(?P&lt;rax&gt;.{8})' + rb'\x48\xba(?P&lt;rdx&gt;.{8})'
</code></pre></div></div>

<p>For the sbox operation, a total of 256 operand bytes would be extracted from this regex, and for the other two transformations, at total of 32 bytes would be extracted. Additionally, the exponentiation operation is the only one of the three types that uses multiplication, which could be detected with another regular expression:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># 0fb6c0             movzx   eax, al
# 0fafc2             imul    eax, edx
mul_expr = rb'\x0f\xb6\xc0\x0f\xaf\xc2'
</code></pre></div></div>

<p>This on its own is enough to distinguish the three types of functions, but since I knew it would be an absolute nightmare to debug any false positives, I added a couple of extra verification functions to check whether the sbox really contained all the values between 0 and 256 and whether the shuffle order really contained all the values between 0 and 32. I ended up with the following extraction function:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 4881c4c0000000     add     rsp, 0xc0            |   4883c450           add     rsp, 0x50  | 415f               pop     r15 {__saved_r15}
# 5d                 pop     rbp {__saved_rbp}
# c3                 retn     {__return_addr}
</span><span class="n">cleanup_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'(\x48\x81\xc4....|\x48\x83\xc4.|\x41\x5f)\x5d\xc3'</span>

<span class="c1"># 55                 push    rbp {__saved_rbp}
# 4889e5             mov     rbp, rsp {__saved_rbp}  | 4881ec10010000     sub     rsp, 0x110 | 4157               push    r15 {__saved_r15}
</span><span class="n">start_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x55(\x48\x89\xe5|\x48\x81\xec....|\x41\x57)'</span>

<span class="n">function_expr</span> <span class="o">=</span> <span class="n">start_expr</span> <span class="o">+</span> <span class="sa">rb</span><span class="s">'.*?'</span> <span class="o">+</span> <span class="n">cleanup_expr</span>

<span class="c1"># 48b8????????????????  mov     rax, ????????????????
# 48ba????????????????  mov     rdx, ????????????????
</span><span class="n">mov_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x48\xb8(?P&lt;rax&gt;.{8})'</span> <span class="o">+</span> <span class="sa">rb</span><span class="s">'\x48\xba(?P&lt;rdx&gt;.{8})'</span>

<span class="c1"># 0fb6c0             movzx   eax, al
# 0fafc2             imul    eax, edx
</span><span class="n">mul_expr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x0f\xb6\xc0\x0f\xaf\xc2'</span>

<span class="n">mul_mov</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'H\x89E\xa0H\x89U\xa8'</span> <span class="o">+</span> <span class="n">mov_expr</span> <span class="o">+</span> <span class="sa">rb</span><span class="s">'H\x89E\xafH\x89U\xb7'</span>

<span class="n">shuffle_mov</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'H</span><span class="se">\x89</span><span class="s">E</span><span class="se">\xd0</span><span class="s">H</span><span class="se">\x89</span><span class="s">U</span><span class="se">\xd8</span><span class="s">'</span> <span class="o">+</span> <span class="n">mov_expr</span> <span class="o">+</span> <span class="sa">b</span><span class="s">'H</span><span class="se">\x89</span><span class="s">E</span><span class="se">\xe0</span><span class="s">H</span><span class="se">\x89</span><span class="s">U</span><span class="se">\xe8</span><span class="s">'</span>

<span class="k">def</span> <span class="nf">verify_sbox</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="n">s</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mh">0x100</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>
        <span class="n">s</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x100</span>

<span class="k">def</span> <span class="nf">verify_shuffle</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="n">s</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mh">0x20</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>
        <span class="n">s</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x20</span>

<span class="k">def</span> <span class="nf">detect_type</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="n">constant_bytes</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span>
    <span class="n">insn_type</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">mov_expr</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">):</span>
        <span class="n">constant_bytes</span> <span class="o">+=</span> <span class="n">i</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="s">"rax"</span><span class="p">)</span>
        <span class="n">constant_bytes</span> <span class="o">+=</span> <span class="n">i</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="s">"rdx"</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">constant_bytes</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x100</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">verify_sbox</span><span class="p">(</span><span class="n">constant_bytes</span><span class="p">):</span>
            <span class="n">insn_type</span> <span class="o">=</span> <span class="s">'sbox'</span>
    <span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">constant_bytes</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x20</span><span class="p">:</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="n">mul_expr</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="n">mul_mov</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">insn_type</span> <span class="o">=</span> <span class="s">'modexp'</span>
        <span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="n">shuffle_mov</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">verify_shuffle</span><span class="p">(</span><span class="n">constant_bytes</span><span class="p">):</span>
                <span class="n">insn_type</span> <span class="o">=</span> <span class="s">'shuffle'</span>
    <span class="k">if</span> <span class="n">insn_type</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="n">constant_bytes</span><span class="p">.</span><span class="nb">hex</span><span class="p">(),</span> <span class="nb">len</span><span class="p">(</span><span class="n">constant_bytes</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
        
    <span class="k">return</span> <span class="n">insn_type</span><span class="p">,</span> <span class="n">constant_bytes</span>
</code></pre></div></div>

<p>Running this function on all the exports (other than <code class="language-plaintext highlighter-rouge">check</code>) of all of the DLLs, I compiled a list of every function type and operand in every DLL.</p>

<h4 id="creating-the-license">Creating the License</h4>

<p>For each <code class="language-plaintext highlighter-rouge">check</code> function, I then had to extract the order of function calls. I again used regular expressions here, which turned out to be a little bit messy because both direct and indirect function calls were used:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 48 89 c1               mov     rcx, rax
# e8 d2 ce fb ff         call    f41689142231683650251
</span><span class="n">mov_rax_rcx</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x48\x89\xc1\xe8(?P&lt;direct&gt;....)'</span>

<span class="c1">#48 8b 05 6a 54 01 00     mov     rax, qword [rel f89290994951878036061]
#ff d0                    call    rax
</span><span class="n">mov_rax_addr</span> <span class="o">=</span> <span class="sa">rb</span><span class="s">'\x48\x8b.(?P&lt;indirect&gt;...).\xff\xd0'</span>

<span class="n">call_regex</span> <span class="o">=</span> <span class="n">mov_rax_rcx</span> <span class="o">+</span> <span class="sa">rb</span><span class="s">'|'</span> <span class="o">+</span> <span class="n">mov_rax_addr</span>
</code></pre></div></div>

<p>The other tricky part of this is that there are two different places to check for the name of the function that’s being called: each function is either one of the DLL’s <em>imports</em>, or one of its <em>exports</em>. It turned out that the direct calls are always used for exports and the indirect calls are always used for exports, so that at least made things a little easier to distinguish.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">re</span><span class="p">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">call_regex</span><span class="p">,</span> <span class="n">check_func</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">DOTALL</span> <span class="o">|</span> <span class="n">re</span><span class="p">.</span><span class="n">MULTILINE</span><span class="p">):</span>
    <span class="n">direct</span> <span class="o">=</span> <span class="n">i</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="s">'direct'</span><span class="p">)</span>
    <span class="n">indirect</span> <span class="o">=</span> <span class="n">i</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="s">'indirect'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">indirect</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">relative_addr</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'&lt;i'</span><span class="p">,</span> <span class="n">indirect</span><span class="o">+</span><span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)[</span><span class="mi">0</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">start</span><span class="p">()</span> <span class="o">+</span> <span class="n">check_start</span> <span class="o">+</span> <span class="n">relative_addr</span> <span class="o">+</span> <span class="mi">7</span>
        <span class="k">if</span> <span class="n">offset</span> <span class="ow">in</span> <span class="n">imports</span><span class="p">:</span>
            <span class="n">call_seq</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">imports</span><span class="p">[</span><span class="n">offset</span><span class="p">])</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">relative_addr</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'&lt;i'</span><span class="p">,</span> <span class="n">direct</span><span class="p">)[</span><span class="mi">0</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">start</span><span class="p">()</span> <span class="o">+</span> <span class="n">check_start</span> <span class="o">+</span> <span class="n">relative_addr</span> <span class="o">+</span> <span class="mi">8</span>
        <span class="k">if</span> <span class="n">offset</span> <span class="ow">in</span> <span class="n">exports</span><span class="p">:</span>
            <span class="n">call_seq</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">exports</span><span class="p">[</span><span class="n">offset</span><span class="p">])</span>
</code></pre></div></div>

<p>I then wrote the corresponding inverse functions for each transformation operation. The one issue I ran into with this is that the initial value I parsed for the exponent of the modular exponentiation operation was wrong: the exponent is actually only 31 bytes, not 32, so a byte had to be removed from the middle of the value that I parsed:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">undo_modexp</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">e</span><span class="p">):</span>
    <span class="c1">#print('modexp')
</span>    <span class="n">e</span> <span class="o">=</span> <span class="n">e</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">15</span><span class="p">]</span> <span class="o">+</span> <span class="n">e</span><span class="p">[</span><span class="mi">16</span><span class="p">:]</span> <span class="c1"># the exponent has 1 unused byte for some reason
</span>    <span class="n">xor_val</span> <span class="o">=</span> <span class="n">enc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&amp;</span> <span class="mi">1</span>
    <span class="n">enc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">enc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">|</span> <span class="mi">1</span>
    <span class="n">enc</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>
    <span class="n">e</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>
    <span class="n">d</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">255</span><span class="p">))</span>
    <span class="n">res</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">256</span><span class="p">))</span>
    <span class="n">res</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="s">'little'</span><span class="p">))</span>
    <span class="n">res</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">res</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">^</span> <span class="n">xor_val</span> <span class="o">^</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">res</span>
</code></pre></div></div>

<p>At that point, I was able to call the inverses of each of the transformation functions to generate the expected initial input that should be in the license file. I made sure to do this in the order in which the DLLs were called and kept the state of the checksum buffer updated so that the XOR with the value from the buffer would be correct.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">check_calls</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
    <span class="n">opcode</span><span class="p">,</span> <span class="n">hexval</span><span class="p">,</span> <span class="n">source</span> <span class="o">=</span> <span class="n">func_types</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
    <span class="n">operand</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">hexval</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">opcode</span> <span class="o">==</span> <span class="s">'shuffle'</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">undo_shuffle</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">operand</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">opcode</span> <span class="o">==</span> <span class="s">'modexp'</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">undo_modexp</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">operand</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">opcode</span> <span class="o">==</span> <span class="s">'sbox'</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">undo_sbox</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">operand</span><span class="p">)</span>
    <span class="c1">#print(data.hex())
</span>
    <span class="c1"># undo the XOR with the checksum buffer value
</span>    <span class="n">first_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">4</span><span class="p">],</span> <span class="s">'little'</span><span class="p">)</span>
    <span class="n">first_int</span> <span class="o">^=</span> <span class="n">counter_table</span><span class="p">[</span><span class="n">source</span><span class="p">]</span>
    <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">first_int</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span>

<span class="c1"># update the checksum buffer
</span><span class="k">for</span> <span class="n">imp</span> <span class="ow">in</span> <span class="n">imports</span><span class="p">[</span><span class="n">idx</span><span class="p">]:</span>
    <span class="n">counter_table</span><span class="p">[</span><span class="n">imp</span><span class="p">]</span> <span class="o">+=</span> <span class="n">counter</span>

<span class="n">license_part</span> <span class="o">=</span> <span class="n">idx</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s">'little'</span><span class="p">)</span> <span class="o">+</span> <span class="nb">bytes</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c1">#print(license_part.hex())
</span><span class="n">license</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">license_part</span><span class="p">)</span>
</code></pre></div></div>

<p>I now had a license file to check, but, I couldn’t just let the validation code run, as the DLL loading was slow enough the validation function took several minutes per DLL. Luckily, the only thing that’s used in the decryption of the flag is the SHA256 hash of the license file, so I just patched out the license check so that the hashing and decryption ran immediately. This <em>finally</em> gets us the flag:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Its_l1ke_10000_spooO0o0O0oOo0o0O0O0OoOoOOO00o0o0Ooons@flare-on.com
</code></pre></div></div>]]></content><author><name></name></author><category term="flareon" /><summary type="html"><![CDATA[Reversing 10000 executables]]></summary></entry><entry><title type="html">Flare-On 11 writeup: 07 - fullspeed</title><link href="/flareon/2024/12/08/fullspeed.html" rel="alternate" type="text/html" title="Flare-On 11 writeup: 07 - fullspeed" /><published>2024-12-08T21:00:00+00:00</published><updated>2024-12-08T21:00:00+00:00</updated><id>/flareon/2024/12/08/fullspeed</id><content type="html" xml:base="/flareon/2024/12/08/fullspeed.html"><![CDATA[<h2 id="challenge-overview">Challenge overview</h2>

<p>We are given a binary and a pcap containing what looks like random data. In this type of challenge, this usually means that the pcap contains some kind of custom protocol with encrypted messages, and we have to decrypt the messages to find the flag somewhere inside.</p>

<h3 id="initial-guesses-and-network-setup">Initial guesses and network setup</h3>

<p>Before I even looked at the binary, I looked at the pcap itself to see if I could tell anything about the protocol.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000  0a 6c 55 90 73 da 49 75  4e 9a d9 84 6a 72 95 47   .lU.s.Iu N...jr.G
00000010  45 e4 f2 92 12 13 ec cd  a4 b1 42 2e 2f dd 64 6f   E....... ..B./.do
00000020  c7 e2 83 89 c7 c2 e5 1a  59 1e 01 47 e2 eb e7 ae   ........ Y..G....
00000030  26 40 22 da f8 c7 67 6a  1b 27 20 91 7b 82 99 9d   &amp;@"...gj .' .{...
00000040  42 cd 18 78 d3 1b c5 7b  6d b1 7b 97 05 c7 ff 24   B..x...{ m.{....$
00000050  04 cb bf 13 cb db 8c 09  66 21 63 40 45 29 39 22   ........ f!c@E)9"
    00000000  a0 d2 eb a8 17 e3 8b 03  cd 06 32 27 bd 32 e3 53   ........ ..2'.2.S
    00000010  88 08 18 89 3a b0 23 78  d7 db 3c 71 c5 c7 25 c6   ....:.#x ..&lt;q..%.
    00000020  bb a0 93 4b 5d 5e 2d 3c  a6 fa 89 ff bb 37 4c 31   ...K]^-&lt; .....7L1
    00000030  96 a3 5e af 2a 5e 0b 43  00 21 de 36 1a a5 8f 80   ..^.*^.C .!.6....
    00000040  15 98 1f fd 0d 98 24 b5  0a f2 3b 5c cf 16 fa 4e   ......$. ..;\...N
    00000050  32 34 83 60 2d 07 54 53  4d 2e 7a 8a af 81 74 dc   24.`-.TS M.z...t.
    00000060  f2 72 d5 4c 31 86 0f                               .r.L1..
00000060  3f bd 43 da 3e e3 25                               ?.C.&gt;.%
    00000067  86 df d7                                           ...
00000067  c5 0c ea 1c 4a a0 64 c3  5a 7f 6e 3a b0 25 84 41   ....J.d. Z.n:.%.A
00000077  ac 15 85 c3 62 56 de a8  3c ac 93 00 7a 0c 3a 29   ....bV.. &lt;...z.:)
00000087  86 4f 8e 28 5f fa 79 c8  eb 43 97 6d 5b 58 7f 8f   .O.(_.y. .C.m[X..
00000097  35 e6 99 54 71 16                                  5..Tq.
    0000006A  fc b1 d2 cd bb a9 79 c9  89 99 8c                  ......y. ...
	[...]
</code></pre></div></div>

<p>Notice that there are two large blocks of data sent at the start of the communication, followed by many exchanges of variable-length data. Communication over a network often consists of a key exchange followed by messages encrypted by a symmetric cipher derived from a shared secret, and this communication appeared to fit that pattern. This means that to solve the challenge, we have to 1) determine the asymmetric algorithm used in the key exchange, 2) determine which symmetric cipher is being used and how the symmetric key is derived from the shared secret obtained through the key exchange, and 3) find a weakness in either the key exchange algorithm or the symmetric cipher that allows us to decrypt the communications and get the flag.</p>

<p>Before I really got started with the reversing, I set up a simple server to receive network communications from the malware. I used a second VM running REMnux on the same internal network as my analysis VM, configured my analysis VM to use it as a gateway, and used <code class="language-plaintext highlighter-rouge">accept-all-ips</code> to ensure that it intercepted all traffic.</p>

<p>By intercepting socket-related system calls I found that the challenge binary did the following:</p>

<ul>
  <li>Sent two seemingly randomly generated sequences of 0x30 bytes each. This was presumably the challenge binary’s public key.</li>
  <li>Received two sequences of 0x30 bytes, presumably the server’s public key. I didn’t know the format of the key yet, so I couldn’t generate my own, but echoing the challenge binary’s key back to it seemed to pass whatever validation checks it was doing.</li>
  <li>Received more data indefinitely, probably waiting for an encrypted command.</li>
</ul>

<p>Based on this, I wrote a script to act as a simple server:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">socket</span>
    
<span class="n">HOST</span> <span class="o">=</span> <span class="s">'0.0.0.0'</span>
<span class="n">PORT</span> <span class="o">=</span> <span class="mi">31337</span>
    
<span class="k">with</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span> <span class="k">as</span> <span class="n">s</span><span class="p">:</span>
	<span class="n">s</span><span class="p">.</span><span class="n">bind</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">))</span>
	<span class="n">s</span><span class="p">.</span><span class="n">listen</span><span class="p">()</span>
	<span class="n">conn</span><span class="p">,</span> <span class="n">addr</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">accept</span><span class="p">()</span>
	<span class="k">with</span> <span class="n">conn</span><span class="p">:</span>
		<span class="n">data1</span> <span class="o">=</span> <span class="n">conn</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mh">0x30</span><span class="p">)</span>
		<span class="n">data2</span> <span class="o">=</span> <span class="n">conn</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mh">0x30</span><span class="p">)</span>
		<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"got data: </span><span class="si">{</span><span class="n">data1</span><span class="p">.</span><span class="nb">hex</span><span class="p">()</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">data2</span><span class="p">.</span><span class="nb">hex</span><span class="p">()</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
		<span class="n">conn</span><span class="p">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">data1</span><span class="p">)</span>
		<span class="k">print</span><span class="p">(</span><span class="s">"sent data1"</span><span class="p">)</span>
		<span class="n">conn</span><span class="p">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">data2</span><span class="p">)</span>
		<span class="k">print</span><span class="p">(</span><span class="s">"sent data2"</span><span class="p">)</span>
		<span class="n">conn</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">'a'</span><span class="o">*</span><span class="mh">0x100</span><span class="p">)</span>
</code></pre></div></div>

<p>With the server running, the key exchange was able to run to completion, so it was time to start reversing it.</p>

<h2 id="part-1-net-aot-reversing">Part 1: .NET AOT reversing</h2>

<p>The main difficulty in reversing this binary is that it’s an “ahead of time” (AOT) compiled .NET application. Normally, a .NET binary would contain instructions in the form of IL bytecode that can be decompiled by tools like dnSpy, but it’s also possible to compile the bytecode to native code in advance. This is of course much more difficult to decompile, and the experience of reversing a .NET AOT binary is more like reversing C++ than a typical .NET binary.</p>

<h3 id="the-hydrated-section">The <code class="language-plaintext highlighter-rouge">hydrated</code> section</h3>

<p>I started off by looking for references to interesting strings, but none of the strings in the binary appeared to be referenced by anything. A closer look revealed that the binary was full of pointers to ununitialized memory in the <code class="language-plaintext highlighter-rouge">hydrated</code> section:</p>

<p><img src="/assets/images/flareon11/hydrated.png" alt="" /></p>

<p>From <a href="https://migeel.sk/blog/2023/09/15/reverse-engineering-natively-compiled-dotnet-apps/">this writeup</a>, I found out that .NET AOT binaries store class structures, including strings, in a compressed or “dehydrated” form as a way of saving space. These structures are then unpacked at runtime. Luckily for us, these structures are unpacked all at once at the start of program execution, so we can easily dump the entire <code class="language-plaintext highlighter-rouge">hydrated</code> section from memory. At that point, we can load the new <code class="language-plaintext highlighter-rouge">hydrated</code> section into memory in the decompiler.</p>

<p>Binary Ninja gives us the option to load any file at any virtual address (<code class="language-plaintext highlighter-rouge">Analysis &gt; Load File at Address...</code>), but unfortunately, strings in the new section don’t appear in Binary Ninja’s <code class="language-plaintext highlighter-rouge">Strings</code> view. I worked around this by patching the PE file itself. I appended the dumped <code class="language-plaintext highlighter-rouge">hydrated</code> section to the end of the PE file, then edited the sections in PE-bear so that the data at the physical address of the patch was loaded at the virtual address of the <code class="language-plaintext highlighter-rouge">hydrated</code> section.</p>

<h3 id="recovering-structures">Recovering structures</h3>

<p>Looking at the <code class="language-plaintext highlighter-rouge">hydrated</code> section, I found that every string was stored as a pointer and an integer, followed by the string itself.</p>

<p><img src="/assets/images/flareon11/dotnet_strings.png" alt="" /></p>

<p>It wasn’t hard to guess that the structure of the strings was something like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">DotNetString</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">vtable</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">len</span><span class="p">;</span>
    <span class="kt">wchar_t</span> <span class="n">string</span><span class="p">[</span><span class="n">len</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Unfortunately, this meant that Binary Ninja didn’t display the contents of the strings in the HLIL view - the strings were simply displayed as a pointer to the method table of the <code class="language-plaintext highlighter-rouge">String</code> class. I worked around this with a hacky plugin that renamed string structures with the first few characters of the string contents.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">build_dotnet_string</span><span class="p">(</span><span class="n">baseaddr</span><span class="p">):</span>
	<span class="n">dn_var</span> <span class="o">=</span> <span class="n">bv</span><span class="p">.</span><span class="n">define_user_data_var</span><span class="p">(</span><span class="n">addr</span><span class="o">=</span><span class="n">baseaddr</span><span class="p">,</span> <span class="n">var_type</span><span class="o">=</span><span class="n">bv</span><span class="p">.</span><span class="n">types</span><span class="p">[</span><span class="s">'DotNetString'</span><span class="p">])</span>		
	<span class="n">a</span> <span class="o">=</span> <span class="n">dn_var</span><span class="p">.</span><span class="n">value</span><span class="p">[</span><span class="s">'vtable'</span><span class="p">]</span>
	<span class="n">l</span> <span class="o">=</span> <span class="n">dn_var</span><span class="p">.</span><span class="n">value</span><span class="p">[</span><span class="s">'len'</span><span class="p">]</span>
	<span class="n">string_var</span> <span class="o">=</span> <span class="n">bv</span><span class="p">.</span><span class="n">define_user_data_var</span><span class="p">(</span><span class="n">addr</span><span class="o">=</span><span class="n">baseaddr</span><span class="o">+</span><span class="mh">0xc</span><span class="p">,</span> <span class="n">var_type</span><span class="o">=</span><span class="n">Type</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="nb">type</span><span class="o">=</span><span class="n">bv</span><span class="p">.</span><span class="n">types</span><span class="p">[</span><span class="s">'WCHAR'</span><span class="p">],</span> <span class="n">count</span><span class="o">=</span><span class="n">l</span><span class="p">))</span>
	<span class="n">dn_var</span><span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">string_var</span><span class="p">.</span><span class="n">value</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">' '</span><span class="p">,</span> <span class="s">'_'</span><span class="p">)[</span><span class="mi">0</span><span class="p">:</span><span class="mh">0x20</span><span class="p">]</span>
</code></pre></div></div>

<p>There were also a few error logging and exeception handling functions that were called frequently with string arguments, so I searched for cross-references to those functions and renamed any strings that were passed to those functions.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">find_strings</span><span class="p">(</span><span class="n">starting_point</span><span class="p">,</span> <span class="n">which_args</span><span class="p">):</span>
	<span class="n">func</span> <span class="o">=</span> <span class="n">bv</span><span class="p">.</span><span class="n">get_functions_at</span><span class="p">(</span><span class="n">starting_point</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
	<span class="k">for</span> <span class="n">site</span> <span class="ow">in</span> <span class="n">func</span><span class="p">.</span><span class="n">caller_sites</span><span class="p">:</span>
		<span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">which_args</span><span class="p">:</span>
			<span class="n">baseaddr</span> <span class="o">=</span> <span class="n">site</span><span class="p">.</span><span class="n">hlil</span><span class="p">.</span><span class="n">operands</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="n">arg</span><span class="p">].</span><span class="n">value</span><span class="p">.</span><span class="n">value</span>
			<span class="n">build_dotnet_string</span><span class="p">(</span><span class="n">baseaddr</span><span class="p">)</span>
			<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Built string at </span><span class="si">{</span><span class="nb">hex</span><span class="p">(</span><span class="n">baseaddr</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Even with the strings identified, reversing the binary was still pretty difficult because most functions were called indirectly through method tables. I used <a href="https://github.com/Washi1337/ghidra-nativeaot">Washi’s Ghidra plugins for AOT binaries</a> as a reference for how these method tables were structured. The tables start with the following header:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">MethodTable</span>
<span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">flags</span><span class="p">;</span>
    <span class="kt">int32_t</span> <span class="n">base_size</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">addr</span><span class="p">;</span>
    <span class="kt">int16_t</span> <span class="n">slot_count</span><span class="p">;</span>
    <span class="kt">int16_t</span> <span class="n">interface_count</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">hash_code</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The most important field for us is <code class="language-plaintext highlighter-rouge">slot_count</code>, which stores the number of function pointers that appear directly after the method table header. Once we know <code class="language-plaintext highlighter-rouge">slot_count</code>, we can define a table of all methods associated with a particular class, and we can identify indirect calls to these methods every time the class is used. This is an example of a decompiled method table struct and its associated function slots:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">MethodTable</span> <span class="n">EC_FieldElement</span> <span class="o">=</span> 
<span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">flags</span> <span class="o">=</span> <span class="mh">0x50200000</span>
    <span class="kt">int32_t</span> <span class="n">base_size</span> <span class="o">=</span> <span class="mh">0x28</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">addr</span> <span class="o">=</span> <span class="mh">0x14015b7d0</span>
    <span class="kt">int16_t</span> <span class="n">slot_count</span> <span class="o">=</span> <span class="mh">0x19</span>
    <span class="kt">int16_t</span> <span class="n">interface_count</span> <span class="o">=</span> <span class="mh">0x0</span>
    <span class="kt">uint32_t</span> <span class="n">hash_code</span> <span class="o">=</span> <span class="mh">0x6d50bb04</span>
<span class="p">}</span>
<span class="k">struct</span> <span class="n">field_element_functions</span> <span class="n">EC_FieldElement_slots</span> <span class="o">=</span> 
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func01</span> <span class="o">=</span> <span class="n">sub_140075c60</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func02</span> <span class="o">=</span> <span class="n">sub_140076590</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func03</span> <span class="o">=</span> <span class="n">sub_140076600</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func04</span> <span class="o">=</span> <span class="n">sub_140075df0</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func05</span> <span class="o">=</span> <span class="n">sub_140075e00</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func06</span> <span class="o">=</span> <span class="n">sub_140075e10</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func07</span> <span class="o">=</span> <span class="n">sub_140075e90</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func08</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_FpFieldElement__Multiply</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func09</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_FpFieldElement__Divide</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func0a</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_FpFieldElement__Negate</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func0b</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_FpFieldElement__Square</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func0c</span> <span class="o">=</span> <span class="n">sub_140076210</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func0d</span> <span class="o">=</span> <span class="n">sub_140075bb0</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func0e</span> <span class="o">=</span> <span class="n">sub_140075bd0</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func0f</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_ECFieldElement__get_IsZero</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func10</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_FpFieldElement__MultiplyMinusProduct</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func11</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_ECFieldElement__Equals_0</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func12</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_ECFieldElement__GetEncodedLength</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func13</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_ECFieldElement__EncodeTo</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func14</span> <span class="o">=</span> <span class="n">sub_140076280</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func15</span> <span class="o">=</span> <span class="n">sub_1400762d0</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func16</span> <span class="o">=</span> <span class="n">BouncyCastle_Cryptography_Org_BouncyCastle_Math_EC_FpFieldElement__ModMult</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func17</span> <span class="o">=</span> <span class="n">sub_140076320</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">func18</span> <span class="o">=</span> <span class="n">sub_140076560</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If we can identify even one function in this table, either by using signatures or by finding references within the function to meaningful strings, we can identify the class it’s associated with. In this example, several functions have been identified by their signature as belonging to the <code class="language-plaintext highlighter-rouge">EC_ECFieldElement</code> class, so we can use cross-references to this class to find code that’s relevant to elliptic curve cryptography.</p>

<h3 id="creating-signatures">Creating signatures</h3>

<p>By default, Binary Ninja doesn’t have any signatures associated with .NET AOT. However, we can use the Signature Matcher tool to create signatures for any library we want. <a href="https://harfanglab.io/insidethelab/reverse-engineering-ida-pro-aot-net/">This writeup by HarfangLab</a> provides a pretty detailed guide on how to compile a .NET AOT binary containing the most common system functions you’d want to create signatures for. They are also kind enough to provide us with the <a href="https://harfanglab.io/medias/2024/01/Program.txt">source code</a> that they used to compile this binary.</p>

<p>I used .NET 8 to compile the signature binary, as the challenge binary contained the string <code class="language-plaintext highlighter-rouge">.NET 8.0</code>. I also added in some functions from the unit tests of BouncyCastle related to ECC cryptography and ChaCha20, since it was clear from the strings in the challenge binary that these algorithms were being used. Other than that, I followed the HarfangLab writeup exactly. This resulted in a few hundred functions being identified, including many of the functions that were most relevant to the cryptography.</p>

<h2 id="part-2-finding-the-algorithm-parameters">Part 2: Finding the algorithm parameters</h2>

<p>Knowing that ECC was being used, I started looking for the elliptic curve parameters. I would need to find the curve parameters <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>, the prime modulus <code class="language-plaintext highlighter-rouge">p</code>, and the generator point <code class="language-plaintext highlighter-rouge">G</code>. (For an explanation of what each of these terms are, see my <a href="/malware/2023/03/18/ecc.html">previous writeup</a> on the subject.)</p>

<h3 id="integers">Integers</h3>

<p>One of the more annoying things about reversing crypto libraries is that a lot of them use their own custom structures to represent big integers. To find the curve parameters, we first need to figure out how the integer values associated with those parameters are stored and how arithmetic is performed on them. In this case, big integers are stored as an array of 32-bit integers:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">BigInteger</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">method_table</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">num_qwords</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">qwords</span><span class="p">[</span><span class="n">num_qwords</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="/assets/images/flareon11/bignum.png" alt="" /></p>

<p>Each 32-bit integer in the array is little-endian, but the values in the array are stored in big-endian order. This means that the array of bytes representing the value of the BigInteger is neither big-endian nor little-endian, which is something that really tripped me up initially.</p>

<h3 id="ecfieldelement">ECFieldElement</h3>

<p>Looking at BouncyCastle’s ECC code, I found that coordinates were members of the <a href="https://github.com/bcgit/bc-csharp/blob/master/crypto/src/math/ec/ECFieldElement.cs">ECFieldElement</a> class. An ECFieldElement is an integer modulo the curve prime <code class="language-plaintext highlighter-rouge">p</code>, and the class defines methods for performing various arithmetic operations modulo <code class="language-plaintext highlighter-rouge">p</code>.</p>

<p>To determine <code class="language-plaintext highlighter-rouge">p</code>, I would need to find the offset of the modulus in the ECFieldElement structure. I searched for functions that I thought might take an ECFieldElement as an argument, then compared those to the methods in the BouncyCastle source code. At this point in the process, I hadn’t yet figured out how to generate function signatures, so instead I tried to match the source code with the decompilation by looking for strings. One such string was the string <code class="language-plaintext highlighter-rouge">value invalid for Fp field element</code> (FpFieldElement is the base class that ECFieldElement inherits from). I didn’t see this string anywhere in the C# source code, but Googling the string gave me a <a href="https://github.com/bcgit/bc-java/blob/main/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java#L792">result</a> in the Java implementation of BouncyCastle:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">ECFieldElement</span> <span class="nf">fromBigInteger</span><span class="o">(</span><span class="nc">BigInteger</span> <span class="n">x</span><span class="o">)</span>
<span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">x</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">x</span><span class="o">.</span><span class="na">signum</span><span class="o">()</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">x</span><span class="o">.</span><span class="na">compareTo</span><span class="o">(</span><span class="n">q</span><span class="o">)</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="o">)</span>
    <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"x value invalid for Fp field element"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="k">new</span> <span class="nc">ECFieldElement</span><span class="o">.</span><span class="na">Fp</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">q</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">r</span><span class="o">,</span> <span class="n">x</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Compare this with the decompiled function containing the same error string:</p>

<p><img src="/assets/images/flareon11/ec_fieldelement.png" alt="" /></p>

<p>If we assume the first argument is an ECFieldElement, it’s clear that <code class="language-plaintext highlighter-rouge">sub_14006c9f0(x, *(uint64_t*)(arg1 + 0x50), arg3)</code> corresponds to <code class="language-plaintext highlighter-rouge">x.compareTo(q)</code>, meaning that the modulus <code class="language-plaintext highlighter-rouge">q</code> is at offset 0x50. Setting a debugger breakpoint on this function, we can check this offset to obtain the curve prime <code class="language-plaintext highlighter-rouge">p</code>, which in this case turns out to be:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p = 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd
</code></pre></div></div>

<h3 id="ecpoint">ECPoint</h3>

<p>Points on the curve are represented as an <code class="language-plaintext highlighter-rouge">ECPoint</code>, which contains pointers to <code class="language-plaintext highlighter-rouge">ECFieldElement</code> structures representing the coordinates x and y. In this case, the <a href="https://github.com/bcgit/bc-csharp/blob/master/crypto/src/math/ec/ECPoint.cs#L407">ToString</a> function was a good one to search for in the decompiled code, as it accesses both coordinates and contains the searchable string “INF”. This told me that <code class="language-plaintext highlighter-rouge">x</code> was stored at offset 0x10 and <code class="language-plaintext highlighter-rouge">y</code> was stored at offset 0x18.</p>

<p><img src="/assets/images/flareon11/ec_point.png" alt="" /></p>

<p>From there, I looked for functions that might have to do with multiplication of an ECPoint by a scalar. I’ll admit, I wasn’t very systematic about how I looked for this function, and I eventually found it mostly through luck and a lot of single stepping in x64dbg. However, one thing that helped a lot was adding labels in x64dbg to the vtables of classes I’d identified, which made function arguments a lot more obvious. When I saw a function that took an ECPoint and a big integer as arguments and then returned another ECPoint, I knew it was a likely candidate for the multiplication function.</p>

<p><img src="/assets/images/flareon11/multiply_args.png" alt="" /></p>

<p>One of the first things that happens in an elliptic curve key exchange is the calculation of the public key from the private key, so I looked for the first call to the multiplication function. The point used in the multiplication would be the curve’s generator <code class="language-plaintext highlighter-rouge">G</code>, and the scalar would be the private key. I obtained the following value for the generator:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gx = 0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8
gy = 0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182
</code></pre></div></div>

<p>The scalar used in the multiplication was a 16-byte value that changed each time. Looking more carefully, I found that the value was being generated by BCryptGenRandom, confirming that it was likely the private key.</p>

<h3 id="eccurve">ECCurve</h3>

<p>To find the curve parameters <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>, I looked at BouncyCastle’s <a href="https://github.com/bcgit/bc-csharp/blob/master/crypto/src/math/ec/ECCurve.cs">ECCurve</a> class. By this point I’d figured out how to generate function signatures for the BouncyCastle functions, so the ECCurve vtable was easy to find using the signatures I’d generated.</p>

<p><img src="/assets/images/flareon11/ec_curve_signatures.png" alt="" /></p>

<p>None of the functions identified in the signatures helped me find <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>, but now that I knew which functions in the decompilation were ECCurve methods, I could set debugger breakpoints on whichever ones looked interesting. I eventually saw two getter functions being called that returned <code class="language-plaintext highlighter-rouge">ECFieldElement</code>s.</p>

<p><img src="/assets/images/flareon11/ec_curve.png" alt="" /></p>

<p>These seemed like possible candidates for <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>. Since I already had the generator point, it was easy to check which one was <code class="language-plaintext highlighter-rouge">a</code> and which one was <code class="language-plaintext highlighter-rouge">b</code> by testing whether the generator point satisfied the curve equation <code class="language-plaintext highlighter-rouge">y**2 = x**3 + ax + b</code>. I obtained the following values:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a = 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f
b = 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380
</code></pre></div></div>

<h3 id="the-symmetric-key-derivation">The symmetric key derivation</h3>

<p>By this point I’d figured out that the main function responsible for the network communication was located at <code class="language-plaintext highlighter-rouge">0x140107ea0</code>. This function calculated the public key, XORed the x- and y-coordinates with the constant 0x1337, sent them over the network, and waited for the server to send its own public key back.</p>

<p><img src="/assets/images/flareon11/main.png" alt="" /></p>

<p>Whatever happened immediately after this key exchange had to be the derivation of the shared secret. I found that the X-coordinate of the public key was being passed to a function that my signature matching had cryptically identified as <code class="language-plaintext highlighter-rouge">System_Security_Cryptography_System_Security_Cryptography_CngKey__Import_3</code>. After following several nested function calls, I found that it was calling a function called <code class="language-plaintext highlighter-rouge">System_Security_Cryptography_System_Security_Cryptography_SHA512__TryHashData</code>, which is much more helpful.</p>

<p>This meant that the symmetric key was derived from only the X-coordinate of the shared secret, and that the SHA512 hash function was being used to derive the key. Immediately after the SHA512 hash was generated, I saw a call to the constructor for BouncyCastle’s <code class="language-plaintext highlighter-rouge">Salsa20Engine</code> that passed in the first 32 bytes of the hash as the key and the next 8 bytes as the nonce. (The <code class="language-plaintext highlighter-rouge">Salsa20Engine</code> in BouncyCastle is used for both Salsa20 and ChaCha20, so once I figured out the key I just tried both. It turned out to be ChaCha20.)</p>

<h2 id="part-3-breaking-the-cryptography">Part 3: Breaking the cryptography</h2>

<p>Now that I’d recovered all the parameters of the curve, it was time to look for a weakness in the encryption. The only unusual thing about this ECC implementation is that it’s using a custom curve, so the problem likely had something to do with the curve parameters.</p>

<p>My first step was to check whether the generator <code class="language-plaintext highlighter-rouge">G</code> was of a small order. This would mean that there would be a small number of distinct possibilities for the private key, which would have allowed us to bruteforce it (see <a href="/ctf/2024/03/14/htb-arranged.html">this writeup</a> for an example of a previous challenge with this vulnerability). Unfortunately, it wasn’t going to be that easy:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sage</span><span class="p">:</span> <span class="n">G</span><span class="p">.</span><span class="n">order</span><span class="p">()</span>
<span class="mi">30937339651019945892244794266256713890440922455872051984762505561763526780311616863989511376879697740787911484829297</span>
</code></pre></div></div>

<p>One thing that stood out, however, was that the order of <code class="language-plaintext highlighter-rouge">G</code> was composite. The standard curves used in elliptic curve cryptography all have prime order. Moreover, all but one of the prime factors was very small:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sage</span><span class="p">:</span> <span class="n">sympy</span><span class="p">.</span><span class="n">factorint</span><span class="p">(</span><span class="n">order</span><span class="p">)</span>
<span class="p">{</span><span class="mi">57301</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">35809</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">56369</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">46027</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">65063</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">113111</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">111659</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 <span class="mi">7072010737074051173701300310820071551428959987622994965153676442076542799542912293</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>
</code></pre></div></div>

<p>In order to get an idea of what to do next, I researched past CTF challenges involving custom elliptic curves I eventually found a <a href="https://ctftime.org/writeup/29702">writeup</a> of another challenge that involved a curve of composite order, which uses the <a href="https://en.wikipedia.org/wiki/Pohlig%E2%80%93Hellman_algorithm">Pohlig-Hellman algorithm</a> to calculate a private key <code class="language-plaintext highlighter-rouge">k</code> from the public key point <code class="language-plaintext highlighter-rouge">k * G</code>.</p>

<p>The algorithm takes advantage of the fact that if the order of <code class="language-plaintext highlighter-rouge">G</code> has a small factor, then it is possible to calculate <code class="language-plaintext highlighter-rouge">k</code> modulo that small factor. Given the prime factorization <code class="language-plaintext highlighter-rouge">p_1 * ... * p_n</code> of the order of G, if we calculate <code class="language-plaintext highlighter-rouge">k mod p_1</code>, …, <code class="language-plaintext highlighter-rouge">k mod p_n</code>, then we can use the Chinese Remainder Theorem to calculate <code class="language-plaintext highlighter-rouge">k mod (p_1 * ... * p_n) = k mod G</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">F</span> <span class="o">=</span> <span class="n">GF</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">E</span> <span class="o">=</span> <span class="n">EllipticCurve</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">])</span>

<span class="n">G</span> <span class="o">=</span> <span class="n">E</span><span class="p">(</span><span class="n">gx</span><span class="p">,</span> <span class="n">gy</span><span class="p">)</span>
<span class="n">order</span> <span class="o">=</span> <span class="n">G</span><span class="p">.</span><span class="n">order</span><span class="p">()</span>

<span class="n">primes</span> <span class="o">=</span> <span class="p">[</span><span class="mi">57301</span><span class="p">,</span> <span class="mi">35809</span><span class="p">,</span> <span class="mi">56369</span><span class="p">,</span> <span class="mi">46027</span><span class="p">,</span> <span class="mi">65063</span><span class="p">,</span> <span class="mi">113111</span><span class="p">,</span> <span class="mi">111659</span><span class="p">]</span>
<span class="n">dlogs</span> <span class="o">=</span> <span class="p">[]</span>

<span class="n">sG</span> <span class="o">=</span> <span class="n">E</span><span class="p">(</span><span class="n">x_send</span><span class="p">,</span> <span class="n">y_send</span><span class="p">)</span>
<span class="k">for</span> <span class="n">fac</span> <span class="ow">in</span> <span class="n">primes</span><span class="p">:</span>
    <span class="n">t</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="o">//</span> <span class="nb">int</span><span class="p">(</span><span class="n">fac</span><span class="p">)</span>
    <span class="n">dlog</span> <span class="o">=</span> <span class="p">(</span><span class="n">t</span><span class="o">*</span><span class="n">G</span><span class="p">).</span><span class="n">discrete_log</span><span class="p">(</span><span class="n">t</span><span class="o">*</span><span class="n">sG</span><span class="p">)</span>
    <span class="n">dlogs</span> <span class="o">+=</span> <span class="p">[</span><span class="n">dlog</span><span class="p">]</span>

<span class="n">secret</span> <span class="o">=</span> <span class="n">crt</span><span class="p">(</span><span class="n">dlogs</span><span class="p">,</span> <span class="n">primes</span><span class="p">)</span>
</code></pre></div></div>

<p>A more detailed explanation of the attack is available <a href="https://crypto.stackexchange.com/questions/72613/elliptic-curve-discrete-log-in-a-composite-ring">here</a>.</p>

<p>If every prime factor of <code class="language-plaintext highlighter-rouge">order</code> was small, we’d already be done. However, the order has one very large prime factor (let’s call it <code class="language-plaintext highlighter-rouge">fac</code>), and it would take far too long to solve the discrete log problem modulo this prime. The best we can do is calculate the private key <code class="language-plaintext highlighter-rouge">k</code> modulo <code class="language-plaintext highlighter-rouge">order / fac</code>. To obtain the value of <code class="language-plaintext highlighter-rouge">k</code> from this, we’d also have to know the value of the quotient <code class="language-plaintext highlighter-rouge">q = k / (order / fac)</code>.</p>

<p>Luckily, there’s another vulnerability we can take advantage of to work around this: the private key is too short. Ordinarily, a private ECC key would have the same bit length as the points on the curve (in this case, 384), but <code class="language-plaintext highlighter-rouge">k</code> is only 128 bits, i.e., <code class="language-plaintext highlighter-rouge">k &lt; 2**129</code>. Thus <code class="language-plaintext highlighter-rouge">q &lt; 2**129 / (order / fac) = 155571</code>. We can easily test every possible value of <code class="language-plaintext highlighter-rouge">q</code> within minutes by checking whether it produces the right public key:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">secret_mod</span> <span class="o">=</span> <span class="mi">3914004671535485983675163411331184</span>
<span class="n">prod</span> <span class="o">=</span> <span class="mi">4374617177662805965808447230529629</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">155571</span><span class="p">):</span>
	<span class="n">C</span> <span class="o">=</span> <span class="p">(</span><span class="n">secret_mod</span> <span class="o">+</span> <span class="n">i</span><span class="o">*</span><span class="n">prod</span><span class="p">)</span> <span class="o">*</span> <span class="n">G</span>
	<span class="k">if</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">C</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">==</span> <span class="n">x_send</span><span class="p">):</span>
		<span class="k">print</span><span class="p">(</span><span class="s">'Found shared secret'</span><span class="p">,</span> <span class="n">secret_mod</span> <span class="o">+</span> <span class="n">i</span><span class="o">*</span><span class="n">prod</span><span class="p">)</span>
		<span class="k">break</span>
</code></pre></div></div>

<p>When I tried this on the sender’s public key from the pcap, the script spit out the private key <code class="language-plaintext highlighter-rouge">168606034648973740214207039875253762473</code>. Exchanging this with the receiver’s public key, we obtain the shared secret:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x = 0x3c54f90f4d2cc9c0b62df2866c2b4f0c5afae8136d2a1e76d2694999624325f5609c50b4677efa21a37664b50cec92c0
y = 0x2793143a038955091171acdcb93bb5b369980f1655c6edcdf5476ed12b5f08465637e6536dd63346aabaf7efb64be82
</code></pre></div></div>

<p>Taking the SHA512 hash of the X-coordinate, we obtain the ChaCha20 key and nonce:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>key = B4 8F 8F A4 C8 56 D4 96 AC DE CD 16 D9 C9 4C C6 B0 1A A1 C0 06 5B 02 3B E9 7A FD D1 21 56 F3 DC`
nonce = 3F D4 80 97 84 85 D8 18
</code></pre></div></div>

<p>Using <a href="https://github.com/Demonslay335/CryptoTester">CryptoTester</a> to do the ChaCha20 decryption, I saw that the result was an ASCII string.</p>

<p><img src="/assets/images/flareon11/chacha.png" alt="" /></p>

<p>The result was a communication between the client and the server in which the client reads <code class="language-plaintext highlighter-rouge">flag.txt</code> from the server’s filesystem:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>verify verify ls === dirs ===
secrets
=== files ===
fullspeed.exe
 cd|secrets ok ls === dirs ===
super secrets
=== files ===
 cd|super secrets ok ls === dirs ===
.hidden
=== files ===
 cd|.hidden ok ls === dirs ===
wait, dot folders aren't hidden on windows
=== files ===
 cd|wait, dot folders aren't hidden on windows ok ls === dirs ===
=== files ===
flag.txt
 cat|flag.txt RDBudF9VNWVfeTB1cl9Pd25fQ3VSdjNzQGZsYXJlLW9uLmNvbQ== exit 
</code></pre></div></div>

<p>Decoding the base64, this gets us the flag: <code class="language-plaintext highlighter-rouge">D0nt_U5e_y0ur_Own_CuRv3s@flare-on.com</code></p>]]></content><author><name></name></author><category term="flareon" /><summary type="html"><![CDATA[.NET AOT reversing and breaking a custom elliptic curve]]></summary></entry><entry><title type="html">BGGP5: Download</title><link href="/bggp/2024/09/01/bggp5.html" rel="alternate" type="text/html" title="BGGP5: Download" /><published>2024-09-01T16:20:00+00:00</published><updated>2024-09-01T16:20:00+00:00</updated><id>/bggp/2024/09/01/bggp5</id><content type="html" xml:base="/bggp/2024/09/01/bggp5.html"><![CDATA[<p>This year, I participated in the <a href="https://binary.golf/">Binary Golf Grand Prix</a>, a competition where the goal is to create the smallest possible binary to accomplish a given task. The theme of this year’s event was “Download”, and the goal was to create the smallest program that could download and display the text file at <code class="language-plaintext highlighter-rouge">https://binary.golf/5/5</code>.</p>

<p>I decided to make my entry an ELF x86_64 binary. Since this is my first attempt at doing this sort of thing, I was more focused on learning about the ELF file format than I was on being competitive. Most of the techniques I used for this are already pretty well known, and I recommend checking out the articles I link to throughout this writeup to get a more in-depth understanding of how these techniques work. I was also pretty surprised by how much of a difference there was between an executable produced by gcc and an executable created manually - the ELF produced by gcc without any special compiler flags was nearly 100 times the size of my final entry! Throughout this writeup, I’ve tried to explain where all that extra overhead comes from.</p>

<h2 id="a-first-attempt">A First Attempt</h2>

<p>I started off with a simple assembly program that prints the contents of the BGGP5 page using <code class="language-plaintext highlighter-rouge">curl</code>. (One could argue that it’s cheating to call an external binary to do all the work for you, but unfortunately I didn’t have enough free time this month to learn how SSL works.)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>section .data
    path:  db "/bin/curl", 0
    arg:   db "https://binary.golf/5/5", 0

    argv:   dq path, arg
    dq 0

section .text
    global _start

_start:
    mov rdi, path
    mov rsi, argv
    xor rdx, rdx
    mov rax, 59 ; sys_execve
    syscall
</code></pre></div></div>

<p>These instructions take up 30 bytes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000: 48 bf 00 20 40 00 00 00 00 00 48 be 22 20 40 00  H.. @.....H." @.
00000010: 00 00 00 00 48 31 d2 b8 3b 00 00 00 0f 05        ....H1..;.....
</code></pre></div></div>
<p>To get a sense of what the compiler would normally produce, we’ll start by compiling the binary in the most naive way possible, without any tricks. The resulting executable is 13696 bytes, which is certainly a lot more than we’d expect from a sequence of instructions that’s only 30 bytes long! Stripping symbols with the <code class="language-plaintext highlighter-rouge">-s</code> flag helps a little, but not by much - we’re still at 13224 bytes.</p>

<p>Looking at the output of <code class="language-plaintext highlighter-rouge">readelf</code>, we can see a list of 13 sections:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Section Headers:
  [Nr] Name              Type             Address           Offset      Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000    0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238    000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.bu[...] NOTE             0000000000000254  00000254    0000000000000024  0000000000000000   A       0     0     4
  [ 3] .gnu.hash         GNU_HASH         0000000000000278  00000278    000000000000001c  0000000000000000   A       4     0     8
  [ 4] .dynsym           DYNSYM           0000000000000298  00000298    0000000000000018  0000000000000018   A       5     1     8
  [ 5] .dynstr           STRTAB           00000000000002b0  000002b0    0000000000000001  0000000000000000   A       0     0     1
  [ 6] .rela.dyn         RELA             00000000000002b8  000002b8    0000000000000060  0000000000000018   A       4     0     8
  [ 7] .text             PROGBITS         0000000000001000  00001000    000000000000001e  0000000000000000  AX       0     0     16
  [ 8] .eh_frame         PROGBITS         0000000000002000  00002000    0000000000000000  0000000000000000   A       0     0     8
  [ 9] .dynamic          DYNAMIC          0000000000002ee0  00002ee0    0000000000000120  0000000000000010  WA       5     0     8
  [10] .data             PROGBITS         0000000000003000  00003000    000000000000003a  0000000000000000  WA       0     0     4
  [11] .symtab           SYMTAB           0000000000000000  00003040    0000000000000108  0000000000000018          12     7     8
  [12] .strtab           STRTAB           0000000000000000  00003148    0000000000000038  0000000000000000           0     0     1
  [13] .shstrtab         STRTAB           0000000000000000  00003180    0000000000000079  0000000000000000  
</code></pre></div></div>

<p>When I was experimenting, I found that if I disabled PIE with the <code class="language-plaintext highlighter-rouge">-no-pie</code> flag, the <code class="language-plaintext highlighter-rouge">.interp</code>, <code class="language-plaintext highlighter-rouge">.gnu.hash</code>, <code class="language-plaintext highlighter-rouge">.dynsym</code>, <code class="language-plaintext highlighter-rouge">.dynstr</code>, <code class="language-plaintext highlighter-rouge">.rela.dyn</code>, <code class="language-plaintext highlighter-rouge">.eh_frame</code>, <code class="language-plaintext highlighter-rouge">.dynamic</code>, <code class="language-plaintext highlighter-rouge">.symtab</code>, and <code class="language-plaintext highlighter-rouge">.strtab</code> sections were no longer present, resulting in a file that’s 8616 bytes. That’s still pretty bad, but it’s a significant decrease in size.</p>

<p>These extra sections are related to the relocations necessary to run a position-independent executable. Since a position-independent executable could potentially be loaded at any base address, the program needs to store metadata describing what addresses need to be modified. For example, the instruction <code class="language-plaintext highlighter-rouge">mov rdi, path</code> is represented as <code class="language-plaintext highlighter-rouge">mov rdi, 0x3000</code> (<code class="language-plaintext highlighter-rouge">48 bf 00 30 00 00 00 00 00 00</code>) when viewed in a disassembler, but when we run the program in GDB the disassembly reads <code class="language-plaintext highlighter-rouge">movabs rdi, 0x555555557000</code> (<code class="language-plaintext highlighter-rouge">48 bf 00 70 55 55 55 55 00 00</code>). The relocation information indicated that the address <code class="language-plaintext highlighter-rouge">0x3000</code> was a relative address that needed to be added to the base address <code class="language-plaintext highlighter-rouge">0x555555554000</code>.</p>

<p>However, if we disable PIE, we don’t need to relocate anything - we know that <code class="language-plaintext highlighter-rouge">path</code> will always be loaded at the address <code class="language-plaintext highlighter-rouge">0x402000</code>, so the instruction <code class="language-plaintext highlighter-rouge">mov rdi, path</code> can simply be represented as <code class="language-plaintext highlighter-rouge">mov rdi, 0x402000</code> in the <code class="language-plaintext highlighter-rouge">.text</code> section.</p>

<h2 id="section-alignment">Section Alignment</h2>

<p>8616 bytes is still a lot for such a simple program, so what’s going on? Looking at the executable in a hex editor, the most obvious problem is that the instructions in the <code class="language-plaintext highlighter-rouge">.text</code> section are followed by a sequence of null bytes much longer than the instructions themselves.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000: 48 bf 00 20 40 00 00 00 00 00 48 be 22 20 40 00  H.. @.....H." @.
00000010: 00 00 00 00 48 31 d2 b8 3b 00 00 00 0f 05 00 00  ....H1..;.......
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
</code></pre></div></div>

<p>The reason for this has to do with page alignment. Since our executable contains sections with different permissions (<code class="language-plaintext highlighter-rouge">.text</code> has permissions <code class="language-plaintext highlighter-rouge">r-x</code>, and <code class="language-plaintext highlighter-rouge">.data</code> section has permissions <code class="language-plaintext highlighter-rouge">rw-</code>), the sections must be loaded in different pages. In order to handle this, padding bytes are inserted so that both the <code class="language-plaintext highlighter-rouge">.text</code> and <code class="language-plaintext highlighter-rouge">.data</code> sections start at a page-aligned address.</p>

<p>We can deal with this by passing the <code class="language-plaintext highlighter-rouge">-N</code> option to ld. This combines the <code class="language-plaintext highlighter-rouge">.text</code> and <code class="language-plaintext highlighter-rouge">.data</code> sections into a single <code class="language-plaintext highlighter-rouge">rwx</code> segment, thus removing the need for padding. Without the padding, our file is down to 680 bytes - a marked improvement over our earlier attempts, but still not great.</p>

<h2 id="building-an-elf-file-manually">Building an ELF file manually</h2>

<p>Let’s take a look at what’s left in the file at this point:</p>

<ul>
  <li>The ELF header (64 bytes), program headers (56 * 2 = 112 bytes), and section headers (64 * 5 = 320 bytes).</li>
  <li>The <code class="language-plaintext highlighter-rouge">.text</code> section containing the instructions themselves (30 bytes).</li>
  <li>The <code class="language-plaintext highlighter-rouge">.data</code> section containing the arguments to <code class="language-plaintext highlighter-rouge">curl</code> (58 bytes).</li>
  <li>The <code class="language-plaintext highlighter-rouge">.note.gnu.build-id</code> section (36 bytes).</li>
  <li>The <code class="language-plaintext highlighter-rouge">.shstrtab</code> section (42 bytes).</li>
  <li>Sequences of 0x00 bytes to keep the sections 16 byte aligned (20 bytes).</li>
</ul>

<p>We clearly need the instructions in the <code class="language-plaintext highlighter-rouge">.text</code> section and the arguments in the <code class="language-plaintext highlighter-rouge">.data</code> section, but the other sections are completely unnecessary. How do we compile a binary without them?</p>

<p>At this point, we’re running out of ways to tell the compiler to generate a smaller binary for us, so it’s time to start doing things by hand. We can create a minimal ELF file that doesn’t contain any extraeneous information, then insert our data and instructions into it manually. To do so, we’ll construct the ELF headers ourselves and compile the executable as a flat binary in nasm, which gets us to 208 bytes. Brian Raiter’s <a href="https://muppetlabs.com/~breadbox/software/tiny/teensy.html">writeup</a> on tiny ELF files was extremely helpful for me in figuring out how to do this.</p>

<p>Our program so far:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BITS 64
org 0x400000

ehdr:
    db 7Fh, "ELF"   ; magic
    db 2    ; class
    db 1    ; encoding
    db 1    ; version
    db 0    ; os
    db 0    ; abi_version
    db 0,0,0,0,0,0,0    ; padding

    dw 2    ; e_type
    dw 3Eh  ; e_machine
    dd 1    ; e_version
    dq _start  ; e_entry
    dq phdr - $$  ; e_phoff
    dq 0 ; e_shoff
    dd 0    ; e_flags
    dw ehdrsize  ; e_ehsize
    dw phdrsize ; e_phentsize
    dw 1    ; e_phnum
    dw 0  ; e_shentsize
    dw 0    ; e_shnum
    dw 0    ; e_shstrndx

ehdrsize equ $ - ehdr

phdr:
    dd 1    ; p_type
    dd 7    ; p_flags
    dq 0  ; p_offset
    dq $$  ; p_vaddr
    dq $$  ; p_paddr
    dq filesize ; p_filesz
    dq filesize ; p_memsz
    dq 0x10 ; p_align

phdrsize equ $ - phdr

args:
    path:  db "/bin/curl", 0 
    arg:   db "https://binary.golf/5/5", 0

    argv:   dq path, arg
    dq 0

_start:
    mov rdi, path
    mov rsi, argv
    xor rdx, rdx
    mov rax, 59 ; sys_execve
    syscall

filesize equ $ - $$
</code></pre></div></div>

<p>(When I was troubleshooting, I also found that GDB no longer recognizes our program as a valid executable, presumably because we don’t have a section header. I’m a little surprised I haven’t run into any malware that uses this as an anti-debugging technique.)</p>

<h2 id="storing-data-in-the-headers">Storing data in the headers</h2>

<p>There are a few fields in the ELF header that aren’t necessary for our executable to run. The 7-byte <code class="language-plaintext highlighter-rouge">padding</code> field is an obvious first place to try storing data, and the path <code class="language-plaintext highlighter-rouge">/bin/curl</code> isn’t much longer than that. Turns out the <code class="language-plaintext highlighter-rouge">version</code>, <code class="language-plaintext highlighter-rouge">os</code>, and <code class="language-plaintext highlighter-rouge">abi_version</code> fields don’t matter either, so we can modify the start of the ELF header to look like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ehdr:
    db 7Fh, "ELF"   ; magic
    db 2    ; class
    db 1    ; encoding
    
    path:
    db "/bin/curl", 0 ; version, os, abi_version, padding
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">p_align</code> field at the end of the program header also isn’t used, so we can use that space to instead store the first 8 bytes of the URL <code class="language-plaintext highlighter-rouge">https://binary.golf/5/5</code>. In addition, it seems that the area in memory immediately after our executable code is filled with 0s, so if we put <code class="language-plaintext highlighter-rouge">argv</code> right at the end of the file then it doesn’t need to be null terminated.</p>

<p>Other unused fields can be used to store the instructions themselves. The <code class="language-plaintext highlighter-rouge">e_shoff</code> field of the ELF header is unused since we don’t have a section header, so we can use that field as well as the adjacent unused <code class="language-plaintext highlighter-rouge">e_flags</code> field to store 12 bytes of instructions. The <code class="language-plaintext highlighter-rouge">p_paddr</code> field of the program header can hold another 8 bytes. We can cut down a little bit on instruction size by replacing our 64-bit operands to <code class="language-plaintext highlighter-rouge">mov</code> and <code class="language-plaintext highlighter-rouge">xor</code> with 32-bit operands, thereby allowing us to fit the entirety of the program into the headers.</p>

<p>We can also remove the last 6 bytes of the ELF header, causing it to overlap with the start of the program header. We can get away with this because <code class="language-plaintext highlighter-rouge">e_shentsize</code> and <code class="language-plaintext highlighter-rouge">e_shstrndx</code> are never parsed if we don’t actually have any section headers.</p>

<p>Pretty much all of the tricks I used here are shamelessly stolen from Nathan Otterness’ <a href="https://nathanotterness.com/2021/10/tiny_elf_modernized.html">writeup</a> on small ELF files, which includes a helpful graph showing which bytes of the ELF header can be used to store arbitrary data. xcellerator’s <a href="https://tmpout.sh/1/1.html">article</a> on the same subject goes into some more detail on what some of these fields are and why we can overwrite them.</p>

<p>Our final result now looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BITS 64
org 0x400000

ehdr:
    db 7Fh, "ELF"   ; magic
    db 2    ; class
    db 1    ; encoding

    path:
    db "/bin/curl", 0 ; version, os, abi_version, padding
    dw 2    ; e_type
    dw 3Eh  ; e_machine
    dd 1    ; e_version

    dq _start  ; e_entry
    dq phdr - $$  ; e_phoff

    _start:
        mov edi, path ; e_shoff, e_flags
        mov esi, argv
        jmp part2

    dw ehdrsize  ; e_ehsize
    dw phdrsize ; e_phentsize
    dw 1    ; e_phnum

ehdrsize equ $ + 6 - ehdr

phdr:
    dd 1    ; p_type, e_shentsize, e_shnum
    dd 7    ; p_flags, e_shstrndx
    dq 0  ; p_offset
    dq $$  ; p_vaddr

    part2:  ; p_paddr
        xor edx, edx
        xor eax, eax
        mov al, 59 ; sys_execve
        syscall  

    dq filesize ; p_filesz
    dq filesize; p_memsz

phdrsize equ $ - phdr + 8
filesize equ $ - $$

arg:   db "https://binary.golf/5/5", 0
argv:   dq path, arg
</code></pre></div></div>

<p>This gets us an executable that’s only 146 bytes!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000: 7f45 4c46 0201 2f62 696e 2f63 7572 6c00  .ELF../bin/curl.
00000010: 0200 3e00 0100 0000 2800 4000 0000 0000  ..&gt;.....(.@.....
00000020: 3a00 0000 0000 0000 bf06 0040 00be 8200  :..........@....
00000030: 4000 eb1e 3a00 3800 0100 0100 0000 0700  @...:.8.........
00000040: 0000 0000 0000 0000 0000 0000 4000 0000  ............@...
00000050: 0000 31d2 31c0 b03b 0f05 6a00 0000 0000  ..1.1..;..j.....
00000060: 0000 6a00 0000 0000 0000 6874 7470 733a  ..j.......https:
00000070: 2f2f 6269 6e61 7279 2e67 6f6c 662f 352f  //binary.golf/5/
00000080: 3500 0600 4000 0000 0000 6a00 4000 0000  5...@.....j.@...
00000090: 0000  
</code></pre></div></div>

<h2 id="potential-improvements">Potential Improvements</h2>

<p>I was able to produce a much smaller executable than I expected, but I still suspect it may be possible to do a little better. In particular, it’s seriously bothering me that I couldn’t think of a way to fit the <code class="language-plaintext highlighter-rouge">argv</code> array into the ELF header or program header somehow.</p>

<p>When I was experimenting with this, I found that <code class="language-plaintext highlighter-rouge">argv[0]</code> doesn’t have to be the path to the <code class="language-plaintext highlighter-rouge">curl</code> executable - that’s just a convention, and it’s not something that <code class="language-plaintext highlighter-rouge">curl</code> relies on the be true. It <em>does</em>, however, have to be a pointer to valid memory. So in order to fit <code class="language-plaintext highlighter-rouge">argv</code> into the header, you’d need to construct a pattern that looks like the following:</p>

<p><code class="language-plaintext highlighter-rouge">[any valid pointer][pointer to URL string][null terminator]</code></p>

<p>This <em>almost</em> fits in the program header - <code class="language-plaintext highlighter-rouge">p_vaddr</code> is a valid pointer, and it’s followed by <code class="language-plaintext highlighter-rouge">p_paddr</code>, where we can put anything. However, the field that follows it is <code class="language-plaintext highlighter-rouge">p_filesz</code>, which we can’t set to 0. I’m still not convinced it’s totally impossible to sneak <code class="language-plaintext highlighter-rouge">argv</code> in somewhere, though. Maybe I’ll think of it before next year’s BGGP!</p>]]></content><author><name></name></author><category term="bggp" /><summary type="html"><![CDATA[Constructing a tiny ELF file]]></summary></entry><entry><title type="html">Reverse engineering the XZ backdoor</title><link href="/malware/2024/04/23/xz.html" rel="alternate" type="text/html" title="Reverse engineering the XZ backdoor" /><published>2024-04-23T18:48:00+00:00</published><updated>2024-04-23T18:48:00+00:00</updated><id>/malware/2024/04/23/xz</id><content type="html" xml:base="/malware/2024/04/23/xz.html"><![CDATA[<h2 id="overview">Overview</h2>

<p>Over the past few weeks, I’ve been analyzing the <a href="https://www.openwall.com/lists/oss-security/2024/03/29/4">xz backdoor</a>. The modifications to the build process that inject the backdoor have already been analyzed very thoroughly (<a href="https://gynvael.coldwind.pl/?lang=en&amp;id=782">1</a>, <a href="https://research.swtch.com/xz-script">2</a>), so this writeup will be entirely focused on the binary blob that gets included into liblzma at the end. This writeup mainly focuses on the process by which the hook function is inserted into sshd, as opposed to the actual code execution capabilities of the hook. The hook function itself may be the subject of a future blog post.</p>

<p>Note: Any decompiler output that I show in this writeup is from my compiled version of <code class="language-plaintext highlighter-rouge">liblzma.so.5.6.1</code>, but the 5.6.0 version looks mostly identical. Since the 5.6.0 version has symbols, I’ll be using those symbol names to refer to the functions I discuss here.</p>

<h2 id="initial-steps">Initial Steps</h2>

<p>The entry point of the backdoor code is <code class="language-plaintext highlighter-rouge">crc64_resolve</code>, which calls <code class="language-plaintext highlighter-rouge">_get_cpuid</code>. <code class="language-plaintext highlighter-rouge">get_cpuid</code> is obfuscated to look like an ordinary functino to collect CPU information: it calls <code class="language-plaintext highlighter-rouge">_cpuid</code>, which is a legitimate function to execute the CPUID instruction.</p>

<p>However, <code class="language-plaintext highlighter-rouge">_get_cpuid</code> makes an obfuscated call to the entry point of the malicious code at <code class="language-plaintext highlighter-rouge">.Llzma_delta_props_encoder</code>. This is accomplished by finding the location where <code class="language-plaintext highlighter-rouge">_cpuid</code> is stored in the GOT, then overwriting the entry with the address of <code class="language-plaintext highlighter-rouge">.Llzma_delta_props_encoder</code>. It also appears that the GOT entry for <code class="language-plaintext highlighter-rouge">_cpuid</code> is restored after the backdoor code finishes executing in order to avoid raising suspicion.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00004</span><span class="n">d4d</span>          <span class="kt">int64_t</span> <span class="n">real_cpuid</span> <span class="o">=</span> <span class="o">*</span><span class="n">cpuid_addr</span>
<span class="mo">00004</span><span class="n">d54</span>          <span class="c1">// overwrite _cpuid ptr in GOT with .Llzma_delta_props_encoder</span>
<span class="mo">00004</span><span class="n">d54</span>          <span class="o">*</span><span class="n">cpuid_addr</span> <span class="o">=</span> <span class="n">got_baseaddr</span> <span class="o">-</span> <span class="mh">0x1a918</span>
<span class="mo">00004</span><span class="n">d57</span>          <span class="kt">int32_t</span><span class="o">*</span> <span class="n">r8</span>
<span class="mo">00004</span><span class="n">d57</span>          <span class="n">got_baseaddr</span> <span class="o">=</span> <span class="n">_cpuid</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">cpuid_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data_2f200</span><span class="p">,</span> <span class="n">r8</span><span class="p">)</span>
<span class="mo">00004</span><span class="n">d62</span>          <span class="o">*</span><span class="n">cpuid_addr</span> <span class="o">=</span> <span class="n">real_cpuid</span>
</code></pre></div></div>

<h2 id="setup-and-symbol-parsing">Setup and Symbol Parsing</h2>

<h3 id="elf-loading">ELF Loading</h3>

<p>The backdoor uses many functions from shared libraries including libc and libcrypto, as well as functions from the <code class="language-plaintext highlighter-rouge">sshd</code> binary itself. In order to determine the locations of these functions, the backdoor must find the locations of each of the required ELF files in memory and parse certain segments. The entry point of this parsing is <code class="language-plaintext highlighter-rouge">.Lget_literal_price.part.0</code>, which takes a pointer to an ELF executable as its first argument and saves certain fields to a struct (<code class="language-plaintext highlighter-rouge">struct elf_data</code>) that gets passed in as the second argument.</p>

<p>The <code class="language-plaintext highlighter-rouge">elf_data</code> struct is defined in the following way:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">elf_data</span> <span class="n">__packed</span>
<span class="p">{</span>
    <span class="kt">int64_t</span> <span class="n">baseaddr</span><span class="p">;</span> <span class="c1">// pointer to the start of the ELF file in memory</span>
    <span class="kt">int64_t</span> <span class="n">phdr_p_vaddr</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">Elf64_Phdr</span><span class="o">*</span> <span class="n">phdr_table</span><span class="p">;</span>
    <span class="kt">int16_t</span> <span class="n">hdr_count</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_1a</span><span class="p">[</span><span class="mi">6</span><span class="p">];</span>
    <span class="k">struct</span> <span class="n">Elf64_Phdr</span><span class="o">*</span> <span class="n">phdr_dynamic_addr</span><span class="p">;</span> <span class="c1">// location of the first header with p_type PT_DYNAMIC</span>
    <span class="kt">uint32_t</span> <span class="n">dyn_count</span><span class="p">;</span> <span class="c1">// total number of Elf64_Dyn structures in dynamic section</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_2c</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">strtab</span><span class="p">;</span> <span class="c1">// pointer to string table</span>
    <span class="k">struct</span> <span class="n">Elf64_Sym</span><span class="o">*</span> <span class="n">symtab</span><span class="p">;</span> <span class="c1">// pointer to symbol table</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">relocs_jmprel</span><span class="p">;</span> <span class="c1">// value read from Elf64_Dyn with tag DT_JMPREL</span>
    <span class="kt">uint32_t</span> <span class="n">rel_count</span><span class="p">;</span> <span class="c1">// calculated from Elf64_Dyn with tag DT_PLTRELSZ</span>
    <span class="kt">int32_t</span> <span class="n">gnu_relro_flag</span><span class="p">;</span> <span class="c1">// whether a segment of type PT_GNU_RELRO is present</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">gnu_relro_vaddr</span><span class="p">;</span> <span class="c1">// address of PT_GNU_RELRO segment, if it exists</span>
    <span class="kt">int64_t</span> <span class="n">gnu_relro_p_memsz</span><span class="p">;</span> <span class="c1">// size of PT_GNU_RELRO, if it exists</span>
    <span class="kt">int16_t</span><span class="o">*</span> <span class="n">dt_verdef</span><span class="p">;</span> <span class="c1">// address of version definition table</span>
    <span class="kt">int64_t</span> <span class="n">dt_verdefnum</span><span class="p">;</span> <span class="c1">// number of entries in version definition table</span>
    <span class="n">Elf64_Versym</span><span class="o">*</span> <span class="n">dt_versym</span><span class="p">;</span> <span class="c1">// address of .gnu.version (DT_VERSYM)</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">dt_rela</span><span class="p">;</span> <span class="c1">// pointer to relocation table, read from Elf64_Dyn with DT_RELA tag</span>
    <span class="kt">uint32_t</span> <span class="n">rela_count</span><span class="p">;</span> <span class="c1">// number of relocations</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_84</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">dt_tag_24</span><span class="p">;</span> <span class="c1">// location of Elf64_Dyn with d_tag = 24, couldnt find what that represents</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">dt_tag_23</span><span class="p">;</span> <span class="c1">// Elf64_Dyn with d_tag = 23</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">seg_x_start</span><span class="p">;</span> <span class="c1">// PT_LOAD segment with flags PF_X</span>
    <span class="kt">int64_t</span> <span class="n">seg_x_size</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">seg_r_addr</span><span class="p">;</span> <span class="c1">// PT_LOAD segment with flags PF_R</span>
    <span class="kt">int64_t</span> <span class="n">seg_r_size</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">field_b8</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">field_c0</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">field_c8</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">flags</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_d1</span><span class="p">[</span><span class="mi">7</span><span class="p">];</span>
    <span class="kt">int32_t</span> <span class="n">gnu_hash_nbucket</span><span class="p">;</span> <span class="c1">// values from DT_GNU_HASH struct</span>
    <span class="kt">int32_t</span> <span class="n">gnu_hash_bucket</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">gnu_hash_bloom_shift</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_e4</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">gnu_hash_bloom</span><span class="p">;</span>
    <span class="kt">int32_t</span><span class="o">*</span> <span class="n">gnu_hash_buckets</span><span class="p">;</span>
    <span class="kt">int64_t</span><span class="o">*</span> <span class="n">gnu_hash_chain</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<h4 id="string-table-and-symbol-table">String Table and Symbol Table</h4>

<p>The function <code class="language-plaintext highlighter-rouge">.Lcrc_init.0</code> looks up a symbol by name in a given ELF executable. As an added layer of obfuscation, the name of the string to look up is passed in as an index in a prefix trie, as described <a href="https://social.hackerspace.pl/@q3k/112184695043115759">here</a>.</p>

<p>The function enumerates symbols by iterating through the <a href="https://flapenguin.me/elf-dt-gnu-hash">DT_GNU_HASH</a> table:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0000765</span><span class="n">c</span>              <span class="k">for</span> <span class="p">(</span><span class="kt">int32_t</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="n">u</span><span class="o">&lt;</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">gnu_hash_nbucket</span><span class="p">;</span> <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="mo">00007670</span>                  <span class="kt">void</span><span class="o">*</span> <span class="n">bucket</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">elf</span><span class="o">-&gt;</span><span class="n">gnu_hash_buckets</span><span class="p">[</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">i</span><span class="p">)]</span>
<span class="mo">000076</span><span class="mi">83</span>                  <span class="c1">// original name: .Lparse_bcj.0</span>
<span class="mo">000076</span><span class="mi">83</span>                  <span class="k">if</span> <span class="p">(</span><span class="n">j_elf_lookup_addr</span><span class="p">(</span><span class="n">elf</span><span class="p">,</span> <span class="n">addr</span><span class="o">:</span> <span class="n">bucket</span><span class="p">,</span> <span class="n">size</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="n">flags</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000076</span><span class="mi">83</span>                      <span class="k">break</span>
<span class="mo">000076</span><span class="mi">98</span>                  <span class="c1">// get an individual hash from chain</span>
<span class="mo">000076</span><span class="mi">98</span>                  <span class="kt">void</span><span class="o">*</span> <span class="n">hash_ptr</span> <span class="o">=</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">gnu_hash_chain</span> <span class="o">+</span> <span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="o">*</span><span class="n">bucket</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<p>For each hash retrieved from the table, the corresponding entry in the symbol table is located. The <code class="language-plaintext highlighter-rouge">st_name</code> field is used to look up the offset of the symbol in the string table, at which point the name is looked up in the prefix trie to see if it matches the desired symbol. If a match is found, the function returns.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0000771</span><span class="mi">8</span>                          <span class="kt">int64_t</span> <span class="n">sym_ptr</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">sym</span><span class="o">-&gt;</span><span class="n">st_name</span><span class="p">)</span> <span class="o">+</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">strtab</span>
<span class="mo">00007736</span>                          <span class="k">if</span> <span class="p">(</span><span class="n">j_elf_lookup_addr</span><span class="p">(</span><span class="n">elf</span><span class="p">,</span> <span class="n">addr</span><span class="o">:</span> <span class="n">sym_ptr</span><span class="p">,</span> <span class="n">size</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">flags</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00007736</span>                              <span class="k">break</span>
<span class="mo">0000774</span><span class="n">c</span>                          <span class="c1">// original name: .Lsimple_coder_update.0</span>
<span class="mo">0000774</span><span class="n">c</span>                          <span class="k">if</span> <span class="p">(</span><span class="n">lookup_trie</span><span class="p">(</span><span class="n">sym_ptr</span><span class="p">,</span> <span class="n">nullptr</span><span class="p">)</span> <span class="o">==</span> <span class="n">sym_trie_idx</span><span class="p">)</span>
<span class="mo">00007753</span>                              <span class="k">if</span> <span class="p">(</span><span class="n">version_trie_idx</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00007</span><span class="mi">875</span>                                  <span class="k">return</span> <span class="n">sym</span>
</code></pre></div></div>

<p>Optionally, <code class="language-plaintext highlighter-rouge">.Lcrc_init.0</code> can also check whether the version information of the executable contains a given string. As far as I can tell, the only version entry that is ever searched for in this way is the string <code class="language-plaintext highlighter-rouge">GLIBC_2.2.5</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0000775</span><span class="n">c</span>                              <span class="n">Elf64_Versym</span><span class="o">*</span> <span class="n">versym_ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">elf</span><span class="o">-&gt;</span><span class="n">dt_versym</span><span class="p">[</span><span class="n">sym_idx</span><span class="p">]</span>
<span class="mo">00007777</span>                              <span class="k">if</span> <span class="p">(</span><span class="n">j_elf_lookup_addr</span><span class="p">(</span><span class="n">elf</span><span class="p">,</span> <span class="n">addr</span><span class="o">:</span> <span class="n">versym_ptr</span><span class="p">,</span> <span class="n">size</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="n">flags</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00007777</span>                                  <span class="k">break</span>
<span class="mo">0000777</span><span class="n">d</span>                              <span class="kt">int32_t</span> <span class="n">rax_14</span>
<span class="mo">0000777</span><span class="n">d</span>                              <span class="n">rax_14</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">flags</span>
<span class="mo">000077</span><span class="mi">83</span>                              <span class="n">Elf64_Versym</span> <span class="n">versym</span> <span class="o">=</span> <span class="o">*</span><span class="n">versym_ptr</span>
<span class="mo">000077</span><span class="mi">98</span>                              <span class="k">if</span> <span class="p">((</span><span class="n">rax_14</span><span class="p">.</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mh">0x18</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x18</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">versym</span> <span class="o">&amp;</span> <span class="mh">0x7ffe</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000077</span><span class="n">a5</span>                                  <span class="n">Elf64_Verdef</span><span class="o">*</span> <span class="n">dt_verdef</span> <span class="o">=</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">dt_verdef</span>
<span class="mo">000077</span><span class="n">ae</span>                                  <span class="kt">int32_t</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">000077</span><span class="n">ba</span>                                  <span class="k">while</span> <span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">j</span><span class="p">)</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">dt_verdefnum</span><span class="p">)</span>
<span class="mo">000077</span><span class="n">d7</span>                                      <span class="k">if</span> <span class="p">(</span><span class="n">j_elf_lookup_addr</span><span class="p">(</span><span class="n">elf</span><span class="p">,</span> <span class="n">addr</span><span class="o">:</span> <span class="n">dt_verdef</span><span class="p">,</span> <span class="n">size</span><span class="o">:</span> <span class="mh">0x14</span><span class="p">,</span> <span class="n">flags</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000077</span><span class="n">d7</span>                                          <span class="k">break</span>
<span class="mf">000077e3</span>                                      <span class="k">if</span> <span class="p">(</span><span class="n">dt_verdef</span><span class="o">-&gt;</span><span class="n">vd_version</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span>
<span class="mf">000077e3</span>                                          <span class="k">break</span>
<span class="mf">000077e9</span>                                      <span class="kt">int32_t</span> <span class="n">rax_17</span>
<span class="mf">000077e9</span>                                      <span class="n">rax_17</span><span class="p">.</span><span class="n">w</span> <span class="o">=</span> <span class="n">versym</span> <span class="o">&amp;</span> <span class="mh">0x7fff</span>
<span class="mo">000077</span><span class="n">f3</span>                                      <span class="k">if</span> <span class="p">(</span><span class="n">rax_17</span><span class="p">.</span><span class="n">w</span> <span class="o">==</span> <span class="n">dt_verdef</span><span class="o">-&gt;</span><span class="n">vd_ndx</span><span class="p">)</span>
<span class="mo">00007</span><span class="mi">81</span><span class="n">b</span>                                          <span class="n">Elf64_Verdaux</span><span class="o">*</span> <span class="n">aux</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">dt_verdef</span><span class="o">-&gt;</span><span class="n">vd_aux</span><span class="p">)</span> <span class="o">+</span> <span class="n">dt_verdef</span>
<span class="mo">00007</span><span class="mi">828</span>                                          <span class="k">if</span> <span class="p">(</span><span class="n">j_elf_lookup_addr</span><span class="p">(</span><span class="n">elf</span><span class="p">,</span> <span class="n">addr</span><span class="o">:</span> <span class="n">aux</span><span class="p">,</span> <span class="n">size</span><span class="o">:</span> <span class="mi">8</span><span class="p">,</span> <span class="n">flags</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00007</span><span class="mi">828</span>                                              <span class="k">break</span>
<span class="mo">00007</span><span class="mi">831</span>                                          <span class="kt">int64_t</span> <span class="n">vda_name</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">aux</span><span class="o">-&gt;</span><span class="n">vda_name</span><span class="p">)</span> <span class="o">+</span> <span class="n">elf</span><span class="o">-&gt;</span><span class="n">strtab</span>
<span class="mo">00007</span><span class="mi">84</span><span class="n">f</span>                                          <span class="k">if</span> <span class="p">(</span><span class="n">j_elf_lookup_addr</span><span class="p">(</span><span class="n">elf</span><span class="p">,</span> <span class="n">addr</span><span class="o">:</span> <span class="n">vda_name</span><span class="p">,</span> <span class="n">size</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">flags</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00007</span><span class="mi">84</span><span class="n">f</span>                                              <span class="k">break</span>
<span class="mo">00007</span><span class="mi">863</span>                                          <span class="k">if</span> <span class="p">(</span><span class="n">version_trie_idx</span> <span class="o">==</span> <span class="n">lookup_trie</span><span class="p">(</span><span class="n">vda_name</span><span class="p">,</span> <span class="n">nullptr</span><span class="p">))</span>
<span class="mo">00007</span><span class="mi">875</span>                                              <span class="k">return</span> <span class="n">sym</span>
</code></pre></div></div>

<h4 id="relocations">Relocations</h4>

<p>In order to obtain the correct addresses for the symbols it looks up, the backdoor also parses the relocation table to calculate the address of the symbol after relocation. The relocation table is found during the initial parsing of the <code class="language-plaintext highlighter-rouge">.dynamic</code> section of the executable: the DT_JMPREL tag corresponds to the relocations associated with the PLT, and the DT_RELA tag corresponds to other relocations.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// .Llz_encode.1</span>
<span class="mo">0000</span><span class="n">afa0</span>  <span class="kt">int64_t</span> <span class="n">do_reloc</span><span class="p">(</span><span class="k">struct</span> <span class="n">elf_data</span><span class="o">*</span> <span class="n">elf_data</span><span class="p">,</span> <span class="n">Elf64_Rela</span><span class="o">*</span> <span class="n">rela</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">count</span><span class="p">,</span> <span class="kt">int64_t</span> <span class="n">idx</span><span class="p">,</span> 
<span class="mo">0000</span><span class="n">afa0</span>      <span class="kt">int32_t</span> <span class="n">sym_name</span><span class="p">)</span>

<span class="mo">0000</span><span class="n">afb7</span>      <span class="n">Elf64_Rela</span><span class="o">*</span> <span class="n">rela_1</span> <span class="o">=</span> <span class="n">rela</span>
<span class="mo">0000</span><span class="n">afd1</span>      <span class="kt">int32_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="n">check</span><span class="p">(</span><span class="n">code</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">bit_idx</span><span class="o">:</span> <span class="mh">0x67</span><span class="p">,</span> <span class="n">num_bits</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="n">flag_idx</span><span class="o">:</span> <span class="mi">4</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">afd6</span>      <span class="kt">int64_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0000</span><span class="n">afda</span>      <span class="k">if</span> <span class="p">(</span><span class="n">rax</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">aff5</span>          <span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">count</span><span class="p">))</span>
<span class="mo">0000</span><span class="n">afe0</span>              <span class="kt">uint64_t</span> <span class="n">sym_idx</span> <span class="o">=</span> <span class="n">rela_1</span><span class="o">-&gt;</span><span class="n">r_info</span>
<span class="mo">0000</span><span class="n">afe9</span>              <span class="k">if</span> <span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">sym_idx</span><span class="p">.</span><span class="n">d</span><span class="p">)</span> <span class="o">==</span> <span class="n">idx</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">b001</span>                  <span class="k">struct</span> <span class="n">Elf64_Sym</span><span class="o">*</span> <span class="n">sym_addr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">elf_data</span><span class="o">-&gt;</span><span class="n">symtab</span><span class="p">[</span><span class="n">sym_idx</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mh">0x20</span><span class="p">]</span>
<span class="mo">0000</span><span class="n">b00a</span>                  <span class="k">if</span> <span class="p">(</span><span class="n">sym_addr</span><span class="o">-&gt;</span><span class="n">st_shndx</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">lookup_trie</span><span class="p">(</span><span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">sym_addr</span><span class="o">-&gt;</span><span class="n">st_name</span><span class="p">)</span> <span class="o">+</span> <span class="n">elf_data</span><span class="o">-&gt;</span><span class="n">strtab</span><span class="p">,</span> <span class="n">nullptr</span><span class="p">)</span> <span class="o">==</span> <span class="n">sym_name</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">b026</span>                      <span class="k">break</span>
<span class="mo">0000</span><span class="n">afeb</span>              <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="mo">0000</span><span class="n">afee</span>              <span class="n">rela_1</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">rela_1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="mo">0000</span><span class="n">aff5</span>      <span class="kt">int64_t</span> <span class="n">reloc_addr</span>
<span class="mo">0000</span><span class="n">aff5</span>      <span class="k">if</span> <span class="p">(</span><span class="n">rax</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="p">(</span><span class="n">rax</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">i</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">count</span><span class="p">)))</span>
<span class="mo">0000</span><span class="n">afdc</span>          <span class="n">reloc_addr</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0000</span><span class="n">aff5</span>      <span class="k">if</span> <span class="p">(</span><span class="n">rax</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">i</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">count</span><span class="p">))</span>
<span class="mo">0000</span><span class="n">b02c</span>          <span class="n">reloc_addr</span> <span class="o">=</span> <span class="n">rela_1</span><span class="o">-&gt;</span><span class="n">r_offset</span> <span class="o">+</span> <span class="n">elf_data</span><span class="o">-&gt;</span><span class="n">baseaddr</span>
<span class="mo">0000</span><span class="n">b03c</span>      <span class="k">return</span> <span class="n">reloc_addr</span>
</code></pre></div></div>

<h3 id="the-x86_64-disassembler">The x86_64 Disassembler</h3>

<p>As many people have already pointed out, the function <code class="language-plaintext highlighter-rouge">.Lx86_code.part.0</code> is an x86_64 disassembler. The first argument is a struct (<code class="language-plaintext highlighter-rouge">struct dasm</code>) that gets filled in with different fields of the instruction. I haven’t determined what every field of it corresponds to, but here’s the struct definition with the fields that I do have:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">dasm</span> <span class="n">__packed</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">addr</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_10</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_11</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
    <span class="kt">char</span> <span class="n">mandatory_prefix</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">segment</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">op_size_override</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">addr_size_override</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_18</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_19</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_1a</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="kt">char</span> <span class="n">rex_prefix</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">modRM</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">modRM_mod</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">modRM_reg</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">modRM_rm</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_20</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_21</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_22</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_23</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_24</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
    <span class="kt">uint32_t</span> <span class="n">opcode</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_2c</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
    <span class="kt">int64_t</span> <span class="n">mem_operand</span><span class="p">;</span>
    <span class="kt">int64_t</span> <span class="n">imm_operand</span><span class="p">;</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_40</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_48</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
    <span class="n">__padding</span> <span class="kt">char</span> <span class="n">_50</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<p>The field that I’ve called <code class="language-plaintext highlighter-rouge">opcode</code> in this struct isn’t the actual opcode of the instruction, but it’s clearly related. In every example where I’ve seen it used, it’s actually equal to the opcode + 0x80, but I haven’t confirmed that that’s always what it is.</p>

<p>The disassembler is used to find locations of functions and structures in the <code class="language-plaintext highlighter-rouge">sshd</code> binary. Unlike the shared libraries, <code class="language-plaintext highlighter-rouge">sshd</code> doesn’t export any function names, and it’s usually not compiled with debug symbols. That means the backdoor needs a different way to determine where certain functions are. In addition, since <code class="language-plaintext highlighter-rouge">sshd</code> is compiled on many different version of many different Linux distributions, the backdoor can’t rely on specific functions being at specific offsets in the compiled binary. The disassembler allows the backdoor to search for patterns of instructions that it expects to appear in every version of sshd. There are too many of these searches for me to explain all of them here, but here’s one example to give you an idea of the level of complexity involved:</p>

<p>The function <code class="language-plaintext highlighter-rouge">.Llzma_buf_cpy.0</code> is an example of one function that uses the disassembler to search for memory addresses. It first searches for a <code class="language-plaintext highlighter-rouge">call</code> instruction whose operand matches a given function. (The target function is passed as an argument to <code class="language-plaintext highlighter-rouge">.Llzma_buf_cpy.0</code>, but I only ever saw it called once to search for calls to <code class="language-plaintext highlighter-rouge">xcalloc</code> in the <code class="language-plaintext highlighter-rouge">main</code> function of <code class="language-plaintext highlighter-rouge">usr/sbin/sshd</code>.) Once it finds that <code class="language-plaintext highlighter-rouge">call</code> instruction, it checks for a <code class="language-plaintext highlighter-rouge">mov</code> immediately after.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mf">0000e793</span>              <span class="k">if</span> <span class="p">(</span><span class="n">code</span> <span class="n">u</span><span class="o">&lt;</span> <span class="n">end</span> <span class="o">&amp;&amp;</span> <span class="n">dasm_find_call_insn</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">target</span><span class="o">:</span> <span class="n">xcalloc_ptr</span><span class="p">,</span> <span class="n">dasm</span><span class="o">:</span> <span class="n">dasm_ptr</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="c1">// dasm_find_call_insn = .Llzma_optimum_normal.0</span>
<span class="mf">0000e7</span><span class="n">cd</span>                  <span class="n">code</span> <span class="o">=</span> <span class="n">dasm</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">dasm</span><span class="p">.</span><span class="n">addr</span>
<span class="mf">0000e7</span><span class="n">d9</span>                  <span class="c1">// dasm_find_opcode_arg = .Llzma_properties_size.0, opcode target is 0x109 - 0x80 = 0x89 (mov)</span>
<span class="mf">0000e7</span><span class="n">d9</span>                  <span class="kt">int32_t</span> <span class="n">rax_3</span> <span class="o">=</span> <span class="n">dasm_find_opcode_arg</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">end</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">code</span><span class="p">[</span><span class="mh">0x20</span><span class="p">],</span> <span class="n">dasm</span><span class="o">:</span> <span class="n">dasm_ptr</span><span class="p">,</span> <span class="n">opcode</span><span class="o">:</span> <span class="mh">0x109</span><span class="p">,</span> <span class="n">target_operand</span><span class="o">:</span> <span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<p>This particular <code class="language-plaintext highlighter-rouge">mov</code> instruction is opcode <code class="language-plaintext highlighter-rouge">0x89</code>, which means the destination of the <code class="language-plaintext highlighter-rouge">mov</code> is a memory address. The ModR/M field of the instruction is checked to see if RIP-relative addressing is used, and if it is, the function takes that into account and adds the correct value to the memory address.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mf">0000e856</span>                              <span class="k">if</span> <span class="p">((</span><span class="n">modrm</span> <span class="o">&amp;</span> <span class="mh">0xff00ff00</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x5000000</span><span class="p">)</span>
<span class="mf">0000e85</span><span class="n">d</span>                                  <span class="n">mem_operand</span> <span class="o">=</span> <span class="n">mem_operand</span> <span class="o">+</span> <span class="n">dasm</span><span class="p">.</span><span class="n">addr</span> <span class="o">+</span> <span class="n">dasm</span><span class="p">.</span><span class="n">size</span>
</code></pre></div></div>

<p>Looking at <code class="language-plaintext highlighter-rouge">main</code>, we can see that there are several calls to <code class="language-plaintext highlighter-rouge">xcalloc</code> that fit this pattern, all of which look something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>e822860400         call    xcalloc
4c89e7             mov     rdi, r12
be04000000         mov     esi, 0x4
4889c5             mov     rbp, rax
488905a8310d00     mov     qword [rel data_e3ba8], rax
</code></pre></div></div>

<p>These calls appear to be allocating memory for global data structures, with the <code class="language-plaintext highlighter-rouge">mov</code> instruction storing the pointer to the new structure at a known address. The structures that get allocated during these calls are <code class="language-plaintext highlighter-rouge">sensitive_data</code>, <code class="language-plaintext highlighter-rouge">startup_pipes</code>, <code class="language-plaintext highlighter-rouge">startup_flags</code>, and <code class="language-plaintext highlighter-rouge">rexec_argv</code>.</p>

<p>The function iterates through all of <code class="language-plaintext highlighter-rouge">main</code> looking for <code class="language-plaintext highlighter-rouge">mov</code> instructions that follow an <code class="language-plaintext highlighter-rouge">xcmalloc</code> call, saving each destination address of the <code class="language-plaintext highlighter-rouge">mov</code> to an array (<code class="language-plaintext highlighter-rouge">operands</code> in the code snippet below). It then appears to traverse the array looking for a set of three destination addresses that are adjacent to each other:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mf">0000e8</span><span class="n">b3</span>              <span class="n">op1</span> <span class="o">=</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">operands</span><span class="p">)[</span><span class="n">i</span><span class="p">]</span>
<span class="mf">0000e8</span><span class="n">b7</span>              <span class="kt">int64_t</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mf">0000e8</span><span class="n">ec</span>              <span class="kt">int64_t</span> <span class="n">k</span>
<span class="mf">0000e8</span><span class="n">ec</span>              <span class="kt">void</span><span class="o">*</span> <span class="n">op2</span>
<span class="mf">0000e8</span><span class="n">ec</span>              <span class="k">do</span>
<span class="mf">0000e8</span><span class="n">b9</span>                  <span class="n">op2</span> <span class="o">=</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">operands</span><span class="p">)[</span><span class="n">j</span><span class="p">]</span>
<span class="mf">0000e8</span><span class="n">bd</span>                  <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mf">0000e8</span><span class="n">d3</span>                  <span class="k">while</span> <span class="p">(</span><span class="n">not</span><span class="p">(</span><span class="n">op1</span> <span class="o">==</span> <span class="n">op2</span> <span class="o">-</span> <span class="mi">8</span> <span class="o">&amp;&amp;</span> <span class="n">op2</span> <span class="o">==</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">operands</span><span class="p">)[</span><span class="n">k</span><span class="p">]</span> <span class="o">-</span> <span class="mi">8</span><span class="p">))</span>
<span class="mf">0000e8</span><span class="n">e0</span>                      <span class="n">k</span> <span class="o">=</span> <span class="n">k</span> <span class="o">+</span> <span class="mi">1</span>
<span class="mf">0000e8</span><span class="n">e5</span>                      <span class="k">if</span> <span class="p">(</span><span class="n">k</span><span class="p">.</span><span class="n">d</span> <span class="n">u</span><span class="o">&gt;=</span> <span class="n">max</span><span class="p">)</span>
<span class="mf">0000e8</span><span class="n">e5</span>                          <span class="k">break</span>
</code></pre></div></div>

<p>The search for three adjacent addresses allows the function to determine which of the <code class="language-plaintext highlighter-rouge">xcmalloc</code> calls correspond to the <code class="language-plaintext highlighter-rouge">sensitive_data</code> struct. <code class="language-plaintext highlighter-rouge">sensitive_data</code> is <a href="https://sources.debian.org/src/openssh/1%3A7.9p1-10%2Bdeb10u2/sshd.c/#L210">defined</a> in the following way:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct {
	struct sshkey	**host_keys;		/* all private host keys */
	struct sshkey	**host_pubkeys;		/* all public host keys */
	struct sshkey	**host_certificates;	/* all public host certificates */
	int		have_ssh2_key;
} sensitive_data;

</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">host_keys</code>, <code class="language-plaintext highlighter-rouge">host_pubkeys</code>, and <code class="language-plaintext highlighter-rouge">host_certificates</code> structures are allocated with three separate calls to <code class="language-plaintext highlighter-rouge">xcmalloc</code>, and pointers to the structures are stored in three adjacent fields of the <code class="language-plaintext highlighter-rouge">sensitive_data</code> struct.</p>

<p>To recap, it appears that the entire purpose of the function <code class="language-plaintext highlighter-rouge">.Llzma_buf_cpy.0</code> is to find the <code class="language-plaintext highlighter-rouge">sensitive_data</code> struct, which it does by 1) disassembling until it finds a call to <code class="language-plaintext highlighter-rouge">xzmalloc</code>, 2) finding a <code class="language-plaintext highlighter-rouge">mov</code> instruction right after that call and storing the destination address, correctly accounting for relative addressing, and 3) determining which of those allocations correspond to contiguous fields of a struct.</p>

<h3 id="anti-debug--key-obfuscation">Anti-Debug / Key Obfuscation</h3>

<p>There are three different functions that seem to be used as anti-debug checks called at the start of a function: <code class="language-plaintext highlighter-rouge">.Llzma2_decoder_end.1</code>, <code class="language-plaintext highlighter-rouge">.Llzma_index_iter_rewind.cold</code>, and <code class="language-plaintext highlighter-rouge">.Llzma_check_init.part.0</code>. All of these are wrappers around <code class="language-plaintext highlighter-rouge">.Lrc_read_init.part.0</code> (<code class="language-plaintext highlighter-rouge">check_internal</code>), which starts off the anti-debug checking process.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000256</span><span class="mi">87</span>      <span class="kt">void</span><span class="o">*</span> <span class="n">code_1</span> <span class="o">=</span> <span class="n">nullptr</span>
<span class="mo">000256</span><span class="mi">8</span><span class="n">c</span>      <span class="k">struct</span> <span class="n">global_ctx</span><span class="o">*</span> <span class="n">global_ctx_1</span> <span class="o">=</span> <span class="n">global_ctx</span>
<span class="mo">000256</span><span class="mi">93</span>      <span class="k">if</span> <span class="p">(</span><span class="n">global_ctx_1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000256</span><span class="mi">93</span>          <span class="k">goto</span> <span class="n">done</span>
<span class="mo">000256</span><span class="mi">95</span>      <span class="kt">uint64_t</span> <span class="n">flag_idx_1</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">flag_idx</span><span class="p">)</span>
<span class="mo">000256</span><span class="n">a1</span>      <span class="k">if</span> <span class="p">(</span><span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">checked_before</span><span class="p">[</span><span class="n">flag_idx_1</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000256</span><span class="n">a1</span>          <span class="k">goto</span> <span class="n">done</span>
<span class="mo">000256</span><span class="n">a3</span>      <span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">checked_before</span><span class="p">[</span><span class="n">flag_idx_1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">000256</span><span class="n">d1</span>      <span class="c1">// do_tamper_checks: .Llzma2_encoder_init.1</span>
<span class="mo">000256</span><span class="n">d1</span>      <span class="kt">int32_t</span> <span class="n">rax</span> <span class="o">=</span> <span class="n">do_tamper_checks</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">code_1</span><span class="p">,</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">liblzma_start</span><span class="p">,</span> <span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">liblzma_end</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="mo">000256</span><span class="n">d8</span>      <span class="kt">int32_t</span> <span class="n">rax_2</span>
<span class="mo">000256</span><span class="n">d8</span>      <span class="kt">int64_t</span> <span class="n">rax_3</span>
<span class="mo">000256</span><span class="n">d8</span>      <span class="k">if</span> <span class="p">(</span><span class="n">rax</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000256</span><span class="n">fd</span>          <span class="c1">// dasm_update_bit_arr: .Llzma_memlimit_get.1</span>
<span class="mo">000256</span><span class="n">fd</span>          <span class="n">rax_2</span> <span class="o">=</span> <span class="n">dasm_update_bit_arr</span><span class="p">(</span><span class="n">code</span><span class="o">:</span> <span class="n">code_1</span><span class="p">,</span> <span class="n">global_ctx</span><span class="o">-&gt;</span><span class="n">liblzma_end</span><span class="p">,</span> <span class="n">bit_idx</span><span class="p">,</span> <span class="n">num_bits</span><span class="p">,</span> <span class="n">zx</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">option</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span>
<span class="mo">00025704</span>          <span class="k">if</span> <span class="p">(</span><span class="n">rax_2</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00025706</span>              <span class="k">struct</span> <span class="n">global_ctx</span><span class="o">*</span> <span class="n">global_ctx_2</span> <span class="o">=</span> <span class="n">global_ctx</span>
<span class="mo">0002570</span><span class="n">a</span>              <span class="n">global_ctx_2</span><span class="o">-&gt;</span><span class="n">bit_arr_count</span> <span class="o">=</span> <span class="n">global_ctx_2</span><span class="o">-&gt;</span><span class="n">bit_arr_count</span> <span class="o">+</span> <span class="n">num_bits</span>
<span class="mo">00025710</span>              <span class="n">done</span><span class="o">:</span>
<span class="mo">00025710</span>              <span class="n">rax_3</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">00025704</span>      <span class="k">if</span> <span class="p">(</span><span class="n">rax</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="p">(</span><span class="n">rax</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">rax_2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span>
<span class="mo">000256</span><span class="n">da</span>          <span class="n">rax_3</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0002571</span><span class="n">f</span>      <span class="k">return</span> <span class="n">rax_3</span>
</code></pre></div></div>

<p>The primary method of checking for a debugger is to iterate through a region of code and check for the presence of an <code class="language-plaintext highlighter-rouge">endbr64</code> instruction. The function <code class="language-plaintext highlighter-rouge">.Llzma_block_buffer_encode.0</code> (<code class="language-plaintext highlighter-rouge">check_endbr64</code>) performs this check. The third argument passed to this function is always <code class="language-plaintext highlighter-rouge">0xe230</code>, so the condition that’s checked here holds if <code class="language-plaintext highlighter-rouge">code == 0xfa1e0ff3</code>, the opcode for <code class="language-plaintext highlighter-rouge">endbr64</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0000</span><span class="n">d9f0</span>  <span class="kt">uint64_t</span> <span class="n">check_endbr64</span><span class="p">(</span><span class="kt">int64_t</span> <span class="n">code</span><span class="p">,</span> <span class="kt">int64_t</span> <span class="n">end</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">const_e230</span><span class="p">)</span>

<span class="mo">0000</span><span class="n">d9f7</span>      <span class="kt">int32_t</span> <span class="n">var_8</span> <span class="o">=</span> <span class="mh">0x5e2</span>
<span class="mo">0000</span><span class="n">da01</span>      <span class="kt">uint32_t</span> <span class="n">rdx</span> <span class="o">=</span> <span class="mi">0</span>
<span class="mo">0000</span><span class="n">da03</span>      <span class="kt">int32_t</span> <span class="n">var_4</span> <span class="o">=</span> <span class="mh">0xf223</span>
<span class="mo">0000</span><span class="n">da0f</span>      <span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">code</span> <span class="n">s</span><span class="o">&gt;</span> <span class="mi">3</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">da18</span>          <span class="kt">int32_t</span> <span class="n">var_8_1</span> <span class="o">=</span> <span class="mh">0x5e20000</span>
<span class="mo">0000</span><span class="n">da35</span>          <span class="n">rdx</span> <span class="o">=</span> <span class="p">(</span><span class="mh">0x5e20000</span> <span class="o">|</span> <span class="n">const_e230</span><span class="p">)</span> <span class="o">+</span> <span class="o">*</span><span class="n">code</span> <span class="o">==</span> <span class="mh">0xf223</span> <span class="c1">// (0xfa1e0ff3 + 0x5e2e230) &amp; 0xffffffff = 0xf223</span>
<span class="mo">0000</span><span class="n">da3a</span>      <span class="k">return</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">rdx</span><span class="p">)</span>
</code></pre></div></div>

<p>Interestingly, the function that calls <code class="language-plaintext highlighter-rouge">check_endbr64</code> also seems to have an option to perform a different check instead, but I never saw it used. Depending on a flag that’s passed as an argument, instead of calling <code class="language-plaintext highlighter-rouge">check_endbr64</code>, it uses the disassembler to search for an instruction where <code class="language-plaintext highlighter-rouge">opcode == 0xf9f</code>. I’m not sure when this would’ve been called, or even what opcode <code class="language-plaintext highlighter-rouge">0xf9f</code> corresponds to, as the values that the disassembler uses to represent opcodes aren’t equal to the real opcode values.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0001</span><span class="n">f4be</span>      <span class="k">if</span> <span class="p">(</span><span class="n">option</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0001</span><span class="n">f4de</span>          <span class="kt">int32_t</span> <span class="n">option_1</span> <span class="o">=</span> <span class="n">option</span>
<span class="mo">0001</span><span class="n">f4f2</span>          <span class="k">struct</span> <span class="n">dasm</span> <span class="n">dasm</span>
<span class="mo">0001</span><span class="n">f4f2</span>          <span class="kt">int64_t</span> <span class="n">rcx</span>
<span class="mo">0001</span><span class="n">f4f2</span>          <span class="kt">int64_t</span> <span class="n">rdi</span>
<span class="mo">0001</span><span class="n">f4f2</span>          <span class="n">rdi</span><span class="p">,</span> <span class="n">rcx</span> <span class="o">=</span> <span class="n">__memfill_u32</span><span class="p">(</span><span class="o">&amp;</span><span class="n">dasm</span><span class="p">,</span> <span class="n">option_1</span><span class="p">,</span> <span class="mh">0x58</span><span class="p">)</span>
<span class="mo">0001</span><span class="n">f50a</span>          <span class="k">if</span> <span class="p">(</span><span class="n">code_dasm</span><span class="p">(</span><span class="n">dasm</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">dasm</span><span class="p">,</span> <span class="n">code</span><span class="p">,</span> <span class="n">end</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">dasm</span><span class="p">.</span><span class="n">opcode</span> <span class="o">==</span> <span class="mh">0xf9f</span><span class="p">)</span>
<span class="mo">0001</span><span class="n">f511</span>              <span class="kt">char</span><span class="o">*</span> <span class="n">next</span> <span class="o">=</span> <span class="n">dasm</span><span class="p">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">dasm</span><span class="p">.</span><span class="n">addr</span>
<span class="mo">0001</span><span class="n">f518</span>              <span class="k">if</span> <span class="p">((</span><span class="n">next</span><span class="p">.</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mh">0xf</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0001</span><span class="n">f51d</span>                  <span class="k">if</span> <span class="p">(</span><span class="n">insn_out</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0001</span><span class="n">f526</span>                      <span class="o">*</span><span class="n">insn_out</span> <span class="o">=</span> <span class="n">next</span>
<span class="mo">0001</span><span class="n">f51f</span>                  <span class="n">option_1</span> <span class="o">=</span> <span class="mi">1</span>
<span class="mo">0001</span><span class="n">f52c</span>          <span class="n">option_2</span> <span class="o">=</span> <span class="n">option_1</span>
<span class="mo">0001</span><span class="n">f4be</span>      <span class="k">else</span>
<span class="mo">0001</span><span class="n">f4c5</span>          <span class="n">option_2</span> <span class="o">=</span> <span class="n">check_endbr64</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">offset</span><span class="o">:</span> <span class="mh">0xe230</span><span class="p">)</span>
</code></pre></div></div>

<p>If the <code class="language-plaintext highlighter-rouge">endbr64</code> instruction is successfully located, the function <code class="language-plaintext highlighter-rouge">.Llzma_lzma_encoder_init.0</code> (<code class="language-plaintext highlighter-rouge">update_bit_arr</code>) is called. This function makes some comparisons against the opcode produced in the disassembler output.  If the opcode is <em>not</em> one of the opcodes in a given list, then a single 1 bit is stored to a specific offset in a bit array of size 0x1c8 bits (0x39 bytes).</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mf">00019e70</span>  <span class="kt">int64_t</span> <span class="n">update_bit_arr</span><span class="p">(</span><span class="k">struct</span> <span class="n">dasm</span><span class="o">*</span> <span class="n">arg1</span><span class="p">,</span> <span class="kt">int32_t</span><span class="o">*</span> <span class="n">bit_idx_ptr</span><span class="p">)</span>

<span class="mf">00019e74</span>      <span class="kt">int32_t</span> <span class="n">bit_idx</span> <span class="o">=</span> <span class="o">*</span><span class="n">bit_idx_ptr</span>
<span class="mf">00019e7</span><span class="n">e</span>      <span class="k">if</span> <span class="p">(</span><span class="n">bit_idx</span> <span class="n">u</span><span class="o">&lt;=</span> <span class="mh">0x1c7</span><span class="p">)</span>
<span class="mf">00019e80</span>          <span class="kt">uint32_t</span> <span class="n">opcode</span> <span class="o">=</span> <span class="n">arg1</span><span class="o">-&gt;</span><span class="n">opcode</span>
<span class="mo">0001</span><span class="mi">9</span><span class="n">eb2</span>          <span class="k">if</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">!=</span> <span class="mh">0x109</span> <span class="o">&amp;&amp;</span> <span class="n">opcode</span> <span class="o">!=</span> <span class="mh">0xbb</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">-</span> <span class="mh">0x83</span> <span class="n">u</span><span class="o">&gt;</span> <span class="mh">0x2e</span> <span class="o">||</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">-</span> <span class="mh">0x83</span> <span class="n">u</span><span class="o">&lt;=</span> <span class="mh">0x2e</span> <span class="o">&amp;&amp;</span> <span class="p">((</span><span class="mh">0x410100000101</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="p">(</span><span class="n">opcode</span> <span class="o">-</span> <span class="mh">0x83</span><span class="p">).</span><span class="n">b</span><span class="p">).</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)))</span>
<span class="mo">0001</span><span class="mi">9</span><span class="n">ec0</span>              <span class="kt">uint64_t</span> <span class="n">byte_idx</span> <span class="o">=</span> <span class="n">zx</span><span class="p">.</span><span class="n">q</span><span class="p">(</span><span class="n">bit_idx</span> <span class="n">u</span><span class="o">&gt;&gt;</span> <span class="mi">3</span><span class="p">)</span>
<span class="mo">0001</span><span class="mi">9</span><span class="n">ec3</span>              <span class="k">struct</span> <span class="n">global_ctx</span><span class="o">*</span> <span class="n">global_ctx_1</span> <span class="o">=</span> <span class="n">global_ctx</span>
<span class="mo">0001</span><span class="mi">9</span><span class="n">ecf</span>              <span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">bit_arr</span><span class="p">[</span><span class="n">byte_idx</span><span class="p">]</span> <span class="o">=</span> <span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">bit_arr</span><span class="p">[</span><span class="n">byte_idx</span><span class="p">]</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="n">bit_idx</span><span class="p">.</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mi">7</span><span class="p">)).</span><span class="n">b</span>
<span class="mo">0001</span><span class="mi">9</span><span class="n">ed7</span>          <span class="o">*</span><span class="n">bit_idx_ptr</span> <span class="o">=</span> <span class="n">bit_idx</span> <span class="o">+</span> <span class="mi">1</span>
<span class="mo">0001</span><span class="mi">9</span><span class="n">ede</span>      <span class="k">return</span> <span class="mi">1</span>
</code></pre></div></div>

<p>By storing different bits to different offsets, the anti-debug checks gradually build up the value of a chacha20-encrypted ed448 key, which is used later on in the backdoor. Therefore, if you naively patch the check functions to always return success, the key value will still be wrong and the backdoor will not function.</p>

<h2 id="hooking">Hooking</h2>

<h3 id="setting-up-the-hooks">Setting Up The Hooks</h3>

<p>The backdoor appears to set hooks for three functions: <code class="language-plaintext highlighter-rouge">RSA_public_decrypt</code>, <code class="language-plaintext highlighter-rouge">RSA_get0_key</code>, and <code class="language-plaintext highlighter-rouge">EVP_PKEY_set1_RSA</code>. However, all three hooks are wrappers around the function <code class="language-plaintext highlighter-rouge">.Llzma_index_stream_size.1</code>, which is responsible for the malicious behavior of the backdoor. After the malicious function has returned, the real function is called.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00016670</span>  <span class="kt">void</span> <span class="n">RSA_get0_key_hook</span><span class="p">(</span><span class="n">RSA</span><span class="o">*</span> <span class="n">r</span><span class="p">,</span> <span class="k">struct</span> <span class="n">BIGNUM</span><span class="o">**</span> <span class="n">n</span><span class="p">,</span> <span class="k">struct</span> <span class="n">BIGNUM</span><span class="o">**</span> <span class="n">e</span><span class="p">,</span> <span class="k">struct</span> <span class="n">BIGNUM</span><span class="o">**</span> <span class="n">d</span><span class="p">)</span>

<span class="mo">000166</span><span class="mi">80</span>      <span class="k">struct</span> <span class="n">global_ctx</span><span class="o">*</span> <span class="n">global_ctx_1</span> <span class="o">=</span> <span class="n">global_ctx</span>
<span class="mo">000166</span><span class="mi">8</span><span class="n">a</span>      <span class="k">if</span> <span class="p">(</span><span class="n">global_ctx_1</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000166</span><span class="mi">8</span><span class="n">c</span>          <span class="k">struct</span> <span class="n">crypto_table</span><span class="o">*</span> <span class="n">crypto_table</span> <span class="o">=</span> <span class="n">global_ctx_1</span><span class="o">-&gt;</span><span class="n">crypto_table</span>
<span class="mo">000166</span><span class="mi">93</span>          <span class="k">if</span> <span class="p">(</span><span class="n">crypto_table</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000166</span><span class="mi">95</span>              <span class="kt">void</span><span class="o">*</span> <span class="n">real_RSA_get0_key</span> <span class="o">=</span> <span class="n">crypto_table</span><span class="o">-&gt;</span><span class="n">real_RSA_get0_key</span>
<span class="mo">000166</span><span class="mi">9</span><span class="n">c</span>              <span class="k">if</span> <span class="p">(</span><span class="n">real_RSA_get0_key</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000166</span><span class="n">a4</span>                  <span class="k">if</span> <span class="p">(</span><span class="n">r</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000166</span><span class="n">b0</span>                      <span class="n">RSA</span><span class="o">*</span> <span class="n">r_1</span> <span class="o">=</span> <span class="n">r</span>
<span class="mo">000166</span><span class="n">b4</span>                      <span class="kt">void</span> <span class="n">done_before</span>  <span class="c1">// all_hooks: .Llzma_index_stream_size.1</span>
<span class="mo">000166</span><span class="n">b4</span>                      <span class="n">all_hooks</span><span class="p">(</span><span class="n">rsa</span><span class="o">:</span> <span class="n">r</span><span class="p">,</span> <span class="n">global_ctx</span><span class="o">:</span> <span class="n">global_ctx_1</span><span class="p">,</span> <span class="n">done_before</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">done_before</span><span class="p">)</span>
<span class="mo">000166</span><span class="n">d4</span>                  <span class="n">jump</span><span class="p">(</span><span class="n">real_RSA_get0_key</span><span class="p">)</span>
</code></pre></div></div>

<p>The mechanism by which the hooks are set is already explained in detail in <a href="https://securelist.com/xz-backdoor-story-part-1/112354/">this writeup</a> by Kaspersky, so I’m not going to get too far into it. Essentially, there’s a callback function called <code class="language-plaintext highlighter-rouge">symbind64</code> that gets called when a symbol is resolved. The backdoor overwrites this callback with a malicious version of <code class="language-plaintext highlighter-rouge">symbind64</code> that replaces <code class="language-plaintext highlighter-rouge">RSA_public_decrypt</code>, <code class="language-plaintext highlighter-rouge">RSA_get0_key</code>, and <code class="language-plaintext highlighter-rouge">EVP_PKEY_set1_RSA</code> with its own hook functions. It also saves the real addresses of the three hooked symbols so that they can be called later.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">0000</span><span class="n">b3b2</span>          <span class="c1">// //lookup_trie: .Lsimple_coder_update.0</span>
<span class="mo">0000</span><span class="n">b3b2</span>          <span class="kt">int32_t</span> <span class="n">trie_val</span> <span class="o">=</span> <span class="n">lookup_trie</span><span class="p">(</span><span class="n">sym_name</span><span class="p">,</span> <span class="n">nullptr</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">b3b7</span>          <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_public_decrypt_got</span> <span class="o">=</span> <span class="n">crypto_syms</span><span class="o">-&gt;</span><span class="n">RSA_public_decrypt_got</span>
<span class="mo">0000</span><span class="n">b3c5</span>          <span class="k">if</span> <span class="p">(</span><span class="n">trie_val</span> <span class="o">==</span> <span class="mh">0x1d0</span> <span class="o">&amp;&amp;</span> <span class="n">RSA_public_decrypt_got</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">b3c7</span>              <span class="kt">int64_t</span> <span class="n">real_RSA_public_decrypt</span> <span class="o">=</span> <span class="o">*</span><span class="n">RSA_public_decrypt_got</span>
<span class="mo">0000</span><span class="n">b3d0</span>              <span class="k">if</span> <span class="p">(</span><span class="n">real_RSA_public_decrypt</span> <span class="n">u</span><span class="o">&gt;</span> <span class="mh">0xffffff</span><span class="p">)</span>
<span class="mo">0000</span><span class="n">b3d6</span>                  <span class="n">crypto_syms</span><span class="o">-&gt;</span><span class="n">real_RSA_public_decrypt</span> <span class="o">=</span> <span class="n">real_RSA_public_decrypt</span>
<span class="mo">0000</span><span class="n">b3da</span>                  <span class="kt">uint64_t</span> <span class="n">RSA_public_decrypt_hook</span> <span class="o">=</span> <span class="n">main_ctx</span><span class="o">-&gt;</span><span class="n">field_110</span>
<span class="mo">0000</span><span class="n">b3e2</span>                  <span class="c1">// RSA_public_decrypt overwrite</span>
<span class="mo">0000</span><span class="n">b3e2</span>                  <span class="o">*</span><span class="n">RSA_public_decrypt_got</span> <span class="o">=</span> <span class="n">RSA_public_decrypt_hook</span>
</code></pre></div></div>

<h3 id="the-hook-function-llzma_index_stream_size1">The Hook Function (<code class="language-plaintext highlighter-rouge">.Llzma_index_stream_size.1</code>)</h3>

<h4 id="the-ed448-key">The ed448 Key</h4>

<p>Remember that array that all the anti-debug checks were storing bits to? If every check passes, the resulting bit array is the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0d bf cd 93 43 56 2e 97 a5 fa a4 18 27 2b f0 fa
ee 05 6f 55 8d 99 63 dc 71 2e 3d 8d fc 43 c0 ae 
fb fe 1a d1 f8 b8 d8 72 15 ce c6 be 1f da 8b d3 
c4 d8 5b 51 58 85 8d 66 da
</code></pre></div></div>

<p>The function <code class="language-plaintext highlighter-rouge">.Lparse_lzma12.0</code> takes the bit array and decrypts it using ChaCha20. First, 48 null bytes are ChaCha20-encrypted using a key, nonce, and counter of all 0s. Then, the first 32 bytes of the result are used as a key, the next 4 bytes as a little-endian counter, and the remaining 12 bytes as a nonce to decrypt the bit array.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00024</span><span class="mi">9</span><span class="n">be</span>              <span class="c1">// use a key and IV of all 0s to encrypt 0x30 bytes,</span>
<span class="mo">00024</span><span class="mi">9</span><span class="n">be</span>              <span class="c1">// generating the next key and IV</span>
<span class="mo">00024</span><span class="mi">9</span><span class="n">be</span>              <span class="kt">void</span> <span class="n">chacha_iv</span>
<span class="mo">00024</span><span class="mi">9</span><span class="n">be</span>              <span class="n">rax_1</span> <span class="o">=</span> <span class="n">chacha20</span><span class="p">(</span><span class="n">chacha_in</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">var_b8</span><span class="p">,</span> <span class="n">chacha_inl</span><span class="o">:</span> <span class="mh">0x30</span><span class="p">,</span> <span class="n">chacha_key</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">var_b8</span><span class="p">,</span> <span class="n">chacha_iv</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">chacha_iv</span><span class="p">,</span> <span class="n">chacha_out</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">chacha_1_out</span><span class="p">,</span> <span class="n">table</span><span class="o">:</span> <span class="n">crypto_table</span><span class="p">)</span>
<span class="mo">00024</span><span class="mi">9</span><span class="n">c5</span>              <span class="k">if</span> <span class="p">(</span><span class="n">rax_1</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="mf">000249e9</span>                  <span class="c1">// use the generated key and IV to decrypt the array</span>
<span class="mf">000249e9</span>                  <span class="c1">// of bits</span>
<span class="mf">000249e9</span>                  <span class="kt">void</span> <span class="n">chacha_iv_1</span>
<span class="mf">000249e9</span>                  <span class="kt">int32_t</span> <span class="n">rax_2</span>
<span class="mf">000249e9</span>                  <span class="n">rax_2</span><span class="p">.</span><span class="n">b</span> <span class="o">=</span> <span class="n">chacha20</span><span class="p">(</span><span class="n">chacha_in</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">global_ctx</span><span class="o">-&gt;</span><span class="n">bit_arr</span><span class="p">,</span> <span class="n">chacha_inl</span><span class="o">:</span> <span class="mh">0x39</span><span class="p">,</span> <span class="n">chacha_key</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">chacha_1_out</span><span class="p">,</span> <span class="n">chacha_iv</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">chacha_iv_1</span><span class="p">,</span> <span class="n">chacha_out</span><span class="o">:</span> <span class="n">result</span><span class="p">,</span> <span class="n">table</span><span class="o">:</span> <span class="n">global_ctx</span><span class="o">-&gt;</span><span class="n">crypto_table</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span>
</code></pre></div></div>

<p>The end result of this decryption is the following key:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0a 31 fd 3b 2f 1f c6 92 92 68 32 52 c8 c1 ac 28 
34 d1 f2 c9 75 c4 76 5e b1 f6 88 58 88 93 3e 48 
10 0c b0 6c 3a be 14 ee 09 28 a5 14 98 eb 16 89 
d5 fd 21 25 25 c8 43 36 00
</code></pre></div></div>

<p>Once the ed448 key is decrypted successfully, the first 32 bytes are used for decryption of the payload. The first 16 bytes of the modulus of the RSA key are used as the IV (consisting of a 4-byte counter followed by a 12-byte nonce), and the remaining bytes are the ciphertext.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00017562</span>                      <span class="n">int128_t</span> <span class="n">chacha_iv</span> <span class="o">=</span> <span class="n">rsa_bytes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">o</span>
<span class="mo">00017571</span>                      <span class="kt">void</span> <span class="n">ed448_key</span>
<span class="mo">00017571</span>                      <span class="k">if</span> <span class="p">(</span><span class="n">decrypt_ed448_key</span><span class="p">(</span><span class="n">result</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">ed448_key</span><span class="p">,</span> <span class="n">global_ctx</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="c1">// decrypt_ed448_key: .Lparse_lzma12.0</span>
<span class="mo">00017571</span>                          <span class="k">goto</span> <span class="n">field_18_1</span>
<span class="mo">000175</span><span class="mi">9</span><span class="n">b</span>                      <span class="k">if</span> <span class="p">(</span><span class="n">chacha20</span><span class="p">(</span><span class="n">chacha_in</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">rsa_bytes</span><span class="p">[</span><span class="mh">0x10</span><span class="p">],</span> <span class="n">chacha_inl</span><span class="o">:</span> <span class="n">rsa_key_size</span> <span class="o">-</span> <span class="mh">0x10</span><span class="p">,</span> <span class="n">chacha_key</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">ed448_key</span><span class="p">,</span> <span class="n">chacha_iv</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">chacha_iv</span><span class="p">,</span> <span class="n">chacha_out</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">rsa_bytes</span><span class="p">[</span><span class="mh">0x10</span><span class="p">],</span> <span class="n">table</span><span class="o">:</span> <span class="n">global_ctx</span><span class="o">-&gt;</span><span class="n">crypto_table</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000175</span><span class="mi">9</span><span class="n">b</span>                          <span class="k">goto</span> <span class="n">field_18_1</span>
</code></pre></div></div>

<h4 id="code-execution">Code Execution</h4>

<p>The methods used by the backdoor to perform code execution have already been pretty extensively documented. The writeup of this <a href="https://github.com/amlweems/xzbot">proof of concept</a> explains the format of the payload in detail, and I highly recommend reading through it. Another proof of concept with additional functionality is available <a href="https://github.com/blasty/JiaTansSSHAgent">here</a>.</p>

<p>The hook function unpacks three little-endian integers from the start of the RSA modulus and calculates the value <code class="language-plaintext highlighter-rouge">rsa_key[0:4] * rsa_key[4:8] + rsa_key[8:16]</code>. The resulting value is expected to be a value from 0 to 3, and it appears to specify a choice of multiple possible formats for the rest of the payload.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000174</span><span class="n">d9</span>                      <span class="kt">uint32_t</span> <span class="n">rsa_field1</span> <span class="o">=</span> <span class="n">rsa_bytes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">d</span>
<span class="mf">000174e2</span>                      <span class="k">if</span> <span class="p">(</span><span class="n">rsa_field1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mf">000174e2</span>                          <span class="k">goto</span> <span class="n">done_hook</span>
<span class="mf">000174e8</span>                      <span class="kt">uint32_t</span> <span class="n">rsa_field2</span> <span class="o">=</span> <span class="n">rsa_bytes</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="n">d</span>
<span class="mo">000174</span><span class="n">f1</span>                      <span class="k">if</span> <span class="p">(</span><span class="n">rsa_field2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000174</span><span class="n">f1</span>                          <span class="k">goto</span> <span class="n">done_hook</span>
<span class="mo">000174</span><span class="n">fb</span>                      <span class="kt">int64_t</span> <span class="n">choice</span> <span class="o">=</span> <span class="n">rsa_field1</span> <span class="o">*</span> <span class="n">rsa_field2</span> <span class="o">+</span> <span class="n">rsa_bytes</span><span class="p">[</span><span class="mi">8</span><span class="p">].</span><span class="n">q</span>
<span class="mo">00017507</span>                      <span class="k">if</span> <span class="p">(</span><span class="n">choice</span> <span class="n">u</span><span class="o">&gt;</span> <span class="mi">3</span><span class="p">)</span>
<span class="mo">00017507</span>                          <span class="k">goto</span> <span class="n">done_hook</span>
</code></pre></div></div>

<p>The now-decrypted ed448 public key is used to check whether the payload has a valid signature, ensuring that only the creator of the backdoor is able to use it. The PoCs that have been produced so far use a patched version of the binary where the public key is replaced with a key they control, allowing them to sign the payloads with their own key instead.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">000150</span><span class="n">ed</span>      <span class="n">EVP_PKEY</span><span class="o">*</span> <span class="n">pkey</span> <span class="o">=</span> <span class="n">crypto_table</span><span class="o">-&gt;</span><span class="n">EVP_PKEY_new_raw_public_key</span><span class="p">(</span><span class="n">type</span><span class="o">:</span> <span class="mh">0x440</span><span class="p">,</span> <span class="n">e</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">key</span><span class="o">:</span> <span class="n">ecc_key</span><span class="p">,</span> <span class="n">keylen</span><span class="o">:</span> <span class="mh">0x39</span><span class="p">)</span>
<span class="mo">000150</span><span class="n">f6</span>      <span class="k">if</span> <span class="p">(</span><span class="n">pkey</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">000150</span><span class="n">f6</span>          <span class="k">goto</span> <span class="n">label_15163</span>
<span class="mo">000150</span><span class="n">f8</span>      <span class="n">EVP_MD_CTX</span><span class="o">*</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">crypto_table</span><span class="o">-&gt;</span><span class="n">EVP_MD_CTX_new</span><span class="p">()</span>
<span class="mo">00015101</span>      <span class="k">if</span> <span class="p">(</span><span class="n">ctx</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00015101</span>          <span class="k">goto</span> <span class="n">label_1515d</span>
<span class="mo">00015117</span>      <span class="kt">uint64_t</span> <span class="n">rax_8</span>
<span class="mo">00015117</span>      <span class="k">if</span> <span class="p">(</span><span class="n">crypto_table</span><span class="o">-&gt;</span><span class="n">EVP_DigestVerifyInit</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">pctx</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">type</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">e</span><span class="o">:</span> <span class="n">nullptr</span><span class="p">,</span> <span class="n">pkey</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span>
<span class="mo">0001512</span><span class="n">c</span>          <span class="kt">int32_t</span> <span class="n">rax_7</span> <span class="o">=</span> <span class="n">crypto_table</span><span class="o">-&gt;</span><span class="n">EVP_DigestVerify</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">sig</span><span class="o">:</span> <span class="n">rsa_n</span><span class="p">,</span> <span class="n">siglen</span><span class="o">:</span> <span class="mh">0x72</span><span class="p">,</span> <span class="n">tbs</span><span class="p">,</span> <span class="n">tbslen</span><span class="o">:</span> <span class="n">arg3</span> <span class="o">+</span> <span class="mh">0x20</span><span class="p">)</span>
</code></pre></div></div>

<p>The primary method of code execution looks to be the use of <code class="language-plaintext highlighter-rouge">system()</code> to execute a command that is included as another field of the RSA modulus payload. This occurs when the payload format is set to option 2, suggesting that options 0, 1, and 3 might be for something other than code execution through <code class="language-plaintext highlighter-rouge">system()</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mo">00017</span><span class="n">ef4</span>                                      <span class="kt">void</span><span class="o">*</span> <span class="n">command</span> <span class="o">=</span> <span class="n">payload_base</span> <span class="o">+</span> <span class="n">payload_offset</span>
<span class="mo">00017</span><span class="n">efb</span>                                      <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">command</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="mo">00017</span><span class="n">efb</span>                                          <span class="k">goto</span> <span class="n">exit</span>
<span class="mo">00017</span><span class="n">f01</span>                                      <span class="n">global_ctx</span><span class="o">-&gt;</span><span class="n">syscalls</span><span class="o">-&gt;</span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>Overall, this was one of the most interesting reversing projects I’ve done in a while: the use of a disassembler to find functions in a binary is a lot more sophisticated than the techniques you typically see, as was the obfuscation of the initial ChaCha20 key. Unfortunately, I wasn’t able to do much more than a very surface-level analysis of how the code execution works - someone with a little more knowledge of OpenSSH internals could find quite a bit more. I may come back to this at some point to see if I can find some of the things referenced in <a href="https://threadreaderapp.com/thread/1776691497506623562.html">this thread</a>.</p>

<h2 id="references">References</h2>

<ul>
  <li>
    <p>https://www.openwall.com/lists/oss-security/2024/03/29/4</p>
  </li>
  <li>
    <p>https://gynvael.coldwind.pl/?lang=en&amp;id=782</p>
  </li>
  <li>
    <p>https://research.swtch.com/xz-script</p>
  </li>
  <li>
    <p>https://gist.github.com/smx-smx/a6112d54777845d389bd7126d6e9f504</p>
  </li>
  <li>
    <p>https://gist.github.com/q3k/af3d93b6a1f399de28fe194add452d01</p>
  </li>
  <li>
    <p>https://github.com/amlweems/xzbot</p>
  </li>
  <li>
    <p>https://github.com/blasty/JiaTansSSHAgent</p>
  </li>
  <li>
    <p>https://bsky.app/profile/filippo.abyssdomain.expert/post/3kowjkx2njy2b</p>
  </li>
  <li>
    <p>https://securelist.com/xz-backdoor-story-part-1/112354/</p>
  </li>
  <li>
    <p>https://threadreaderapp.com/thread/1776691497506623562.html</p>
  </li>
</ul>

<h2 id="appendix-function-tables">Appendix: Function Tables</h2>

<p>The backdoor stores the library functions that it uses at offsets to the global structure <code class="language-plaintext highlighter-rouge">global_ctx</code> (a pointer to this struct is saved at <code class="language-plaintext highlighter-rouge">.Llzma12_coder.1</code>). I kept track of which function were which by defining a struct for each function table with the names of the functions being used. I’ve included them here in case it’s useful for anyone else who’s been analyzing this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">crypto_table</span> <span class="n">__packed</span> <span class="c1">// global_ctx+8</span>
<span class="p">{</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">real_RSA_public_decrypt</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">real_EVP_PKEY_set1_RSA</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">real_RSA_get0_key</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_public_decrypt_got</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_PKEY_set1_RSA_got</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_get0_key_got</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">DSA_get0_pqg</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">DSA_get0_pub_key</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EC_POINT_point2oct</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EC_KEY_get0_public_key</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EC_KEY_get0_group</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_sha256</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_get0_key</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">BN_num_bits</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_PKEY_new_raw_public_key</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_MD_CTX_new</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_DigestVerifyInit</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_DigestVerify</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_MD_CTX_free</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_PKEY_free</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_CIPHER_CTX_new</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_DecryptInit_ex</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_DecryptUpdate</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_DecryptFinal_ex</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_CIPHER_CTX_free</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_chacha20</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_new</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">BN_dup</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">BN_bin2bn</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_set0_key</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">EVP_Digest</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_sign</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">BN_bn2bin</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">RSA_free</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">BN_free</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">syscalls_table</span><span class="o">*</span> <span class="n">syscalls</span><span class="p">;</span>
    <span class="kt">int32_t</span> <span class="n">count</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">syscalls_table</span> <span class="n">__packed</span> <span class="c1">// global_ctx+0x10</span>
<span class="p">{</span>
    <span class="kt">int64_t</span> <span class="n">count</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">malloc_usable_size</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">getuid</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">_exit</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">setresgid</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">setresuid</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">system</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">write</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">pselect</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">read</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">errno_location</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">setlogmask</span><span class="p">;</span>
    <span class="kt">void</span><span class="o">*</span> <span class="n">shutdown</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="malware" /><summary type="html"><![CDATA[An in-depth look at the backdoor in XZ]]></summary></entry><entry><title type="html">HTB Apocalypse CTF 2024 - arranged</title><link href="/ctf/2024/03/14/htb-arranged.html" rel="alternate" type="text/html" title="HTB Apocalypse CTF 2024 - arranged" /><published>2024-03-14T22:30:00+00:00</published><updated>2024-03-14T22:30:00+00:00</updated><id>/ctf/2024/03/14/htb-arranged</id><content type="html" xml:base="/ctf/2024/03/14/htb-arranged.html"><![CDATA[<h2 id="overview">Overview</h2>

<p>We are given the following code that encrypts the flag:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
<span class="kn">from</span> <span class="nn">Crypto.Util.Padding</span> <span class="kn">import</span> <span class="n">pad</span>
<span class="kn">from</span> <span class="nn">Crypto.Util.number</span> <span class="kn">import</span> <span class="n">long_to_bytes</span>
<span class="kn">from</span> <span class="nn">hashlib</span> <span class="kn">import</span> <span class="n">sha256</span>

<span class="kn">from</span> <span class="nn">secret</span> <span class="kn">import</span> <span class="n">FLAG</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">priv_a</span><span class="p">,</span> <span class="n">priv_b</span>

<span class="n">F</span> <span class="o">=</span> <span class="n">GF</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">E</span> <span class="o">=</span> <span class="n">EllipticCurve</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="p">[</span><span class="mi">726</span><span class="p">,</span> <span class="n">b</span><span class="p">])</span>
<span class="n">G</span> <span class="o">=</span> <span class="n">E</span><span class="p">(</span><span class="mi">926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367</span><span class="p">,</span> <span class="mi">4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253</span><span class="p">)</span>

<span class="n">A</span> <span class="o">=</span> <span class="n">G</span> <span class="o">*</span> <span class="n">priv_a</span>
<span class="n">B</span> <span class="o">=</span> <span class="n">G</span> <span class="o">*</span> <span class="n">priv_b</span>

<span class="k">print</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">B</span><span class="p">)</span>

<span class="n">C</span> <span class="o">=</span> <span class="n">priv_a</span> <span class="o">*</span> <span class="n">B</span>

<span class="k">assert</span> <span class="n">C</span> <span class="o">==</span> <span class="n">priv_b</span> <span class="o">*</span> <span class="n">A</span>

<span class="c1"># now use it as shared secret
</span><span class="n">secret</span> <span class="o">=</span> <span class="n">C</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

<span class="nb">hash</span> <span class="o">=</span> <span class="n">sha256</span><span class="p">()</span>
<span class="nb">hash</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">long_to_bytes</span><span class="p">(</span><span class="n">secret</span><span class="p">))</span>

<span class="n">key</span> <span class="o">=</span> <span class="nb">hash</span><span class="p">.</span><span class="n">digest</span><span class="p">()[</span><span class="mi">16</span><span class="p">:</span><span class="mi">32</span><span class="p">]</span>
<span class="n">iv</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'u</span><span class="se">\x8f</span><span class="s">o</span><span class="se">\x9a</span><span class="s">K</span><span class="se">\xc5\x17\xa7</span><span class="s">&gt;[</span><span class="se">\x18\xa3\xc5\x11\x9e</span><span class="s">n'</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="n">iv</span><span class="p">)</span>

<span class="n">encrypted</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">pad</span><span class="p">(</span><span class="n">FLAG</span><span class="p">,</span> <span class="mi">16</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">encrypted</span><span class="p">)</span>
</code></pre></div></div>

<p>This code generates the elliptic curve given by the equation <code class="language-plaintext highlighter-rouge">y**2 = x**3 + 726x + b</code> over a finite field of order <code class="language-plaintext highlighter-rouge">p</code>. A generator point <code class="language-plaintext highlighter-rouge">G</code> is given, along with two private keys <code class="language-plaintext highlighter-rouge">priv_a</code> and <code class="language-plaintext highlighter-rouge">priv_b</code>, which are used to generate a shared secret using elliptic curve Diffie-Hellman. (If you’re not familiar with this algorithm, here’s my <a href="/malware/2023/03/18/ecc.html">writeup</a> on the basics of how it works.) The shared secret is then used to derive an AES key that encrypts the flag.</p>

<p>We are given the public keys <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code>, but we do not know either of the private keys <code class="language-plaintext highlighter-rouge">priv_a</code> or <code class="language-plaintext highlighter-rouge">priv_b</code>, so we are unable to derive the shared secret. Our goal is to exploit a weakness in the encryption algorithm to calculate <code class="language-plaintext highlighter-rouge">priv_a</code> or <code class="language-plaintext highlighter-rouge">priv_b</code>.</p>

<h2 id="the-hidden-curve-parameters">THe Hidden Curve Parameters</h2>

<p>The interesting thing about this challenge is that the parameters <code class="language-plaintext highlighter-rouge">p</code> and <code class="language-plaintext highlighter-rouge">b</code> are hidden from us. In order for the ECDH algorithm to work, both parties must know the order <code class="language-plaintext highlighter-rouge">p</code> of the finite field and the equation of the curve. In general, the value of <code class="language-plaintext highlighter-rouge">p</code>, the parameters of the curve, and the generator <code class="language-plaintext highlighter-rouge">G</code> are chosen from one of a set of standard curves that are known to be secure. For example, the curve <code class="language-plaintext highlighter-rouge">secp256k1</code> is specified by the following parameters:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>y**2 = x**3 + 7
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
G = (0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 
     0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
</code></pre></div></div>

<p>The generator point <code class="language-plaintext highlighter-rouge">G</code> that we are given is not equal to the standard generator point of any commonly used curves, so chances are, we’re dealing with a nonstandard curve that was designed specifically for this challenge - probably because it’s insecure in some way. Since it’s not a standard curve, we’ll have to determine <code class="language-plaintext highlighter-rouge">p</code> and <code class="language-plaintext highlighter-rouge">b</code> solely based on the information given to us in the challenge, so let’s look at what we know.</p>

<p>The x- and y-coordinates of <code class="language-plaintext highlighter-rouge">G</code> are 509 and 511 bits respectively, suggesting that <code class="language-plaintext highlighter-rouge">p</code> is probably a 512-bit prime. In addition, we’re given three different points on the curve: <code class="language-plaintext highlighter-rouge">G</code>, <code class="language-plaintext highlighter-rouge">A</code>, and <code class="language-plaintext highlighter-rouge">B</code>. Each of those points <code class="language-plaintext highlighter-rouge">(x,y)</code> will need to satisfy <code class="language-plaintext highlighter-rouge">y**2 = x**3 + 726x + b (mod p)</code>.</p>

<p>Let’s call the three sets of points <code class="language-plaintext highlighter-rouge">(x_A, y_A)</code>, <code class="language-plaintext highlighter-rouge">(x_B, y_B)</code>, and <code class="language-plaintext highlighter-rouge">(x_G, y_G)</code>. We can calculate three values <code class="language-plaintext highlighter-rouge">b_A</code>, <code class="language-plaintext highlighter-rouge">b_B</code>, and <code class="language-plaintext highlighter-rouge">b_G</code> using the equation of the curve: <code class="language-plaintext highlighter-rouge">b = y**2 - x**3 - 726x</code>. This will get us three different values: the curve is defined over <code class="language-plaintext highlighter-rouge">GF(p)</code> not over the integers, so <code class="language-plaintext highlighter-rouge">b_A</code>, <code class="language-plaintext highlighter-rouge">b_B</code>, and <code class="language-plaintext highlighter-rouge">b_G</code> won’t necessarily be equal - but they <em>will</em> be congruent mod <code class="language-plaintext highlighter-rouge">p</code>, whatever <code class="language-plaintext highlighter-rouge">p</code> is.</p>

<p>This gives us enough information to guess a value of <code class="language-plaintext highlighter-rouge">p</code>. Since <code class="language-plaintext highlighter-rouge">b_A</code>, <code class="language-plaintext highlighter-rouge">b_B</code>, and <code class="language-plaintext highlighter-rouge">b_G</code> are all congruent mod <code class="language-plaintext highlighter-rouge">p</code>, their differences <code class="language-plaintext highlighter-rouge">b_A - b_B</code>, <code class="language-plaintext highlighter-rouge">b_G - b_B</code>, and <code class="language-plaintext highlighter-rouge">b_A - b_G</code> are all equal to 0 mod <code class="language-plaintext highlighter-rouge">p</code>, i.e., they are all divisible by p. Knowing that, we can look at the common divisors of <code class="language-plaintext highlighter-rouge">b_A - b_B</code>, <code class="language-plaintext highlighter-rouge">b_G - b_B</code>, and <code class="language-plaintext highlighter-rouge">b_A - b_G</code>. If one of those common divisors is a 512-bit prime, that’s almost certainly the value of <code class="language-plaintext highlighter-rouge">p</code>.</p>

<p>The following script calculates the greatest common divisor of <code class="language-plaintext highlighter-rouge">b_A - b_B</code>, <code class="language-plaintext highlighter-rouge">b_G - b_B</code>, and <code class="language-plaintext highlighter-rouge">b_A - b_G</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_b</span><span class="p">(</span><span class="n">xy</span><span class="p">):</span>
	<span class="n">x</span> <span class="o">=</span> <span class="n">xy</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
	<span class="n">y</span> <span class="o">=</span> <span class="n">xy</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
	<span class="k">return</span> <span class="n">y</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="p">(</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">726</span><span class="o">*</span><span class="n">x</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">guess_p</span><span class="p">(</span><span class="n">G_xy</span><span class="p">,</span> <span class="n">A_xy</span><span class="p">,</span> <span class="n">B_xy</span><span class="p">):</span>
	<span class="n">b_G</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">G_xy</span><span class="p">)</span>
	<span class="n">b_A</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">A_xy</span><span class="p">)</span>
	<span class="n">b_B</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">B_xy</span><span class="p">)</span>

	<span class="k">return</span> <span class="n">gcd</span><span class="p">(</span><span class="n">b_G</span> <span class="o">-</span> <span class="n">b_A</span><span class="p">,</span> <span class="n">gcd</span><span class="p">(</span><span class="n">b_B</span> <span class="o">-</span> <span class="n">b_A</span><span class="p">,</span> <span class="n">b_G</span> <span class="o">-</span> <span class="n">b_B</span><span class="p">))</span>
	
<span class="n">G_xy</span> <span class="o">=</span> <span class="p">(</span><span class="mi">926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367</span><span class="p">,</span> <span class="mi">4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253</span><span class="p">)</span>

<span class="n">A_xy</span> <span class="o">=</span> <span class="p">(</span><span class="mi">6174416269259286934151093673164493189253884617479643341333149124572806980379124586263533252636111274525178176274923169261099721987218035121599399265706997</span><span class="p">,</span> <span class="mi">2456156841357590320251214761807569562271603953403894230401577941817844043774935363309919542532110972731996540328492565967313383895865130190496346350907696</span><span class="p">)</span>

<span class="n">B_xy</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4226762176873291628054959228555764767094892520498623417484902164747532571129516149589498324130156426781285021938363575037142149243496535991590582169062734</span><span class="p">,</span> <span class="mi">425803237362195796450773819823046131597391930883675502922975433050925120921590881749610863732987162129269250945941632435026800264517318677407220354869865</span><span class="p">)</span>

<span class="n">p</span> <span class="o">=</span> <span class="n">guess_p</span><span class="p">(</span><span class="n">G_xy</span><span class="p">,</span> <span class="n">A_xy</span><span class="p">,</span> <span class="n">B_xy</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
</code></pre></div></div>
<p>This prints out the value <code class="language-plaintext highlighter-rouge">6811640204116707417092117962115673978365477767365408659433165386030330695774965849821512765233994033921595018695941912899856987893397852151975650548637533</code>, which is in fact a 512-bit prime. That means we’re on the right track!</p>

<h2 id="the-vulnerability">The Vulnerability</h2>

<p>Now that we know the full equation of the curve, we can find the order of the generator point <code class="language-plaintext highlighter-rouge">G</code>: the number of distinct points on the curve that can be obtained by repeatedly adding <code class="language-plaintext highlighter-rouge">G</code> to itself. Sage has a built-in function <code class="language-plaintext highlighter-rouge">G.order()</code> to do this. Since every point used in the ECDH algorithm is a multiple of <code class="language-plaintext highlighter-rouge">G</code>, this gives us a measure of how feasible it would be to bruteforce the value of one of the private keys.</p>

<p>For this curve, it turns out that the order of <code class="language-plaintext highlighter-rouge">G</code> is only 11, so guessing a private key is easy. To calculate <code class="language-plaintext highlighter-rouge">priv_a</code>, we just need to calculate <code class="language-plaintext highlighter-rouge">kG</code> for values of <code class="language-plaintext highlighter-rouge">k</code> in the range 0 through 10, then compare the result to the public key point <code class="language-plaintext highlighter-rouge">A</code>. The value of <code class="language-plaintext highlighter-rouge">k</code> that produces a matching point is <code class="language-plaintext highlighter-rouge">priv_a</code>. From there, we just need to calculate the shared secret, which is equal to <code class="language-plaintext highlighter-rouge">priv_a * B</code>, to decrypt the flag.</p>

<p>Final solve script:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
<span class="kn">from</span> <span class="nn">Crypto.Util.Padding</span> <span class="kn">import</span> <span class="n">pad</span>
<span class="kn">from</span> <span class="nn">Crypto.Util.number</span> <span class="kn">import</span> <span class="n">long_to_bytes</span>
<span class="kn">from</span> <span class="nn">hashlib</span> <span class="kn">import</span> <span class="n">sha256</span>

<span class="k">def</span> <span class="nf">get_b</span><span class="p">(</span><span class="n">xy</span><span class="p">):</span>
	<span class="n">x</span> <span class="o">=</span> <span class="n">xy</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
	<span class="n">y</span> <span class="o">=</span> <span class="n">xy</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
	<span class="k">return</span> <span class="n">y</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="p">(</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">726</span><span class="o">*</span><span class="n">x</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">guess_p</span><span class="p">(</span><span class="n">G_xy</span><span class="p">,</span> <span class="n">A_xy</span><span class="p">,</span> <span class="n">B_xy</span><span class="p">):</span>
	<span class="n">b_G</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">G_xy</span><span class="p">)</span>
	<span class="n">b_A</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">A_xy</span><span class="p">)</span>
	<span class="n">b_B</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">B_xy</span><span class="p">)</span>

	<span class="k">return</span> <span class="n">gcd</span><span class="p">(</span><span class="n">b_G</span> <span class="o">-</span> <span class="n">b_A</span><span class="p">,</span> <span class="n">gcd</span><span class="p">(</span><span class="n">b_B</span> <span class="o">-</span> <span class="n">b_A</span><span class="p">,</span> <span class="n">b_G</span> <span class="o">-</span> <span class="n">b_B</span><span class="p">))</span>
	
<span class="n">G_xy</span> <span class="o">=</span> <span class="p">(</span><span class="mi">926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367</span><span class="p">,</span> <span class="mi">4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253</span><span class="p">)</span>

<span class="n">A_xy</span> <span class="o">=</span> <span class="p">(</span><span class="mi">6174416269259286934151093673164493189253884617479643341333149124572806980379124586263533252636111274525178176274923169261099721987218035121599399265706997</span><span class="p">,</span> <span class="mi">2456156841357590320251214761807569562271603953403894230401577941817844043774935363309919542532110972731996540328492565967313383895865130190496346350907696</span><span class="p">)</span>

<span class="n">B_xy</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4226762176873291628054959228555764767094892520498623417484902164747532571129516149589498324130156426781285021938363575037142149243496535991590582169062734</span><span class="p">,</span> <span class="mi">425803237362195796450773819823046131597391930883675502922975433050925120921590881749610863732987162129269250945941632435026800264517318677407220354869865</span><span class="p">)</span>

<span class="n">p</span> <span class="o">=</span> <span class="n">guess_p</span><span class="p">(</span><span class="n">G_xy</span><span class="p">,</span> <span class="n">A_xy</span><span class="p">,</span> <span class="n">B_xy</span><span class="p">)</span> 
<span class="n">b</span> <span class="o">=</span> <span class="n">get_b</span><span class="p">(</span><span class="n">G_xy</span><span class="p">)</span> <span class="o">%</span> <span class="n">p</span>

<span class="n">F</span> <span class="o">=</span> <span class="n">GF</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">E</span> <span class="o">=</span> <span class="n">EllipticCurve</span><span class="p">(</span><span class="n">F</span><span class="p">,</span> <span class="p">[</span><span class="mi">726</span><span class="p">,</span> <span class="n">b</span> <span class="o">%</span> <span class="n">p</span><span class="p">])</span>

<span class="n">G</span> <span class="o">=</span> <span class="n">E</span><span class="p">(</span><span class="n">G_xy</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">G_xy</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">A</span> <span class="o">=</span> <span class="n">E</span><span class="p">(</span><span class="n">A_xy</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">A_xy</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">B</span> <span class="o">=</span> <span class="n">E</span><span class="p">(</span><span class="n">B_xy</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">B_xy</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>


<span class="k">print</span><span class="p">(</span><span class="s">"Order of G:"</span><span class="p">,</span> <span class="n">G</span><span class="p">.</span><span class="n">order</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">11</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">G</span>
	<span class="k">if</span><span class="p">(</span><span class="n">P</span> <span class="o">==</span> <span class="n">A</span><span class="p">):</span>
		<span class="n">priv_a</span> <span class="o">=</span> <span class="n">i</span>
		<span class="k">break</span>

<span class="k">print</span><span class="p">(</span><span class="n">priv_a</span><span class="p">)</span>
	
<span class="n">C</span> <span class="o">=</span> <span class="n">priv_a</span> <span class="o">*</span> <span class="n">B</span>
<span class="n">secret</span> <span class="o">=</span> <span class="n">C</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

<span class="nb">hash</span> <span class="o">=</span> <span class="n">sha256</span><span class="p">()</span>
<span class="nb">hash</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">long_to_bytes</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">secret</span><span class="p">)))</span>

<span class="n">ciphertext</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'V</span><span class="se">\x1b\xc6</span><span class="s">&amp;</span><span class="se">\x04</span><span class="s">Z</span><span class="se">\xb0</span><span class="s">c</span><span class="se">\xec\x1a\t</span><span class="s">n</span><span class="se">\xd9\xa6</span><span class="s">(</span><span class="se">\xc1\xe1\xc5</span><span class="s">I</span><span class="se">\xf5\x1c\xd3\xa7\xdd\xa0\x84</span><span class="s">j</span><span class="se">\x9b</span><span class="s">ob</span><span class="se">\x9d</span><span class="s">"</span><span class="se">\xd8\xf7\x98</span><span class="s">?^</span><span class="se">\x9d</span><span class="s">A{</span><span class="se">\xde\x08\x8f\x84</span><span class="s">i</span><span class="se">\xbf\x1f\xab</span><span class="s">'</span>
<span class="n">key</span> <span class="o">=</span> <span class="nb">hash</span><span class="p">.</span><span class="n">digest</span><span class="p">()[</span><span class="mi">16</span><span class="p">:</span><span class="mi">32</span><span class="p">]</span>
<span class="n">iv</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'u</span><span class="se">\x8f</span><span class="s">o</span><span class="se">\x9a</span><span class="s">K</span><span class="se">\xc5\x17\xa7</span><span class="s">&gt;[</span><span class="se">\x18\xa3\xc5\x11\x9e</span><span class="s">n'</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="n">iv</span><span class="p">)</span>

<span class="n">flag</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ciphertext</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span>
</code></pre></div></div>

<p>This gets us the flag: <code class="language-plaintext highlighter-rouge">HTB{0rD3r_mUsT_b3_prEs3RveD_!!@!}</code></p>]]></content><author><name></name></author><category term="ctf" /><summary type="html"><![CDATA[Exploiting a weak elliptic curve]]></summary></entry><entry><title type="html">Flare-On 10 writeup: 11 - over_the_rainbow</title><link href="/flareon/2023/11/18/over_the_rainbow.html" rel="alternate" type="text/html" title="Flare-On 10 writeup: 11 - over_the_rainbow" /><published>2023-11-18T20:06:00+00:00</published><updated>2023-11-18T20:06:00+00:00</updated><id>/flareon/2023/11/18/over_the_rainbow</id><content type="html" xml:base="/flareon/2023/11/18/over_the_rainbow.html"><![CDATA[<p>This year, I completed Flare-On for the second time. Overall, I found the difficulty to be a significant step up from last year, and I finished with only a week left to spare.</p>

<p><img src="/assets/images/over_the_rainbow/scoreboard.png" alt="" /></p>

<p>I found challenge 11 to be one of the more interesting challenges, as it required a more in-depth understanding of cryptography than last year’s ransomware challenge.</p>

<h2 id="overview">Overview</h2>

<p>We are given two files: the challenge binary <code class="language-plaintext highlighter-rouge">FLAREON_2023.exe</code>, as well as a file <code class="language-plaintext highlighter-rouge">very_important_file.d3crypt_m3</code>. This immediately tells us that we’re likely dealing with a ransomware sample, and that we’ll have to reverse the encryption algorithm in order to recover the file.</p>

<p>Running the binary without any arguments didn’t do anything, so the first step was to figure out the expected argument format. Looking through the strings, I found the string <code class="language-plaintext highlighter-rouge">.3ncrypt_m3</code>, suggesting that the program would only encrypt files with this extension. Presumably this is intended as a safeguard to ensure that people don’t accidentally encrypt their entire filesystem. After some experimenting, I found that the program took a directory as an argument, and it would then encrypt any <code class="language-plaintext highlighter-rouge">.3ncrypt_m3</code> files in that directory.</p>

<p>After figuring out the argument format, the next thing I tried was to encrypt a test file. We can create a test file consisting of 0x1000 zero bytes with the command <code class="language-plaintext highlighter-rouge">fsutil file createNew zeroes.3ncrypt_m3 0x1000</code>.</p>

<p>After encryption, the size of the file was 0x1100 bytes. This suggested to me that the original 0x1000 bytes of the file had been encrypted with a symmetric encryption algorithm, and that the symmetric key had been encrypted with RSA and appended to the end of the file. This is about what I was expecting, as most real ransomware performs its encryption this way.</p>

<p><img src="/assets/images/over_the_rainbow/extra_bytes.png" alt="" /></p>

<h2 id="the-encryption-algorithm">The Encryption Algorithm</h2>

<h3 id="the-symmetric-encryption">The Symmetric Encryption</h3>

<p>I noticed that the string <code class="language-plaintext highlighter-rouge">expand 32-byte k</code> appeared in the binary, which is a constant that is used in the Salsa20 and ChaCha20 encryption algorithms. This string is accessed in the function <code class="language-plaintext highlighter-rouge">sub_14007ee60</code>, and the string <code class="language-plaintext highlighter-rouge">d3crypt_m3</code> is accessed in the same function. This indicated to me that <code class="language-plaintext highlighter-rouge">sub_14007ee60</code> was the function where the actual encryption took place.</p>

<p><img src="/assets/images/over_the_rainbow/enc_loop.png" alt="" /></p>

<p>It looked like two different random keys were being generated. The first key was 0x30 bytes, and it was concatenated to the <code class="language-plaintext highlighter-rouge">expand 32-byte k</code> string to form the ChaCha20 matrix. The second key was 0x18 bytes, and it was XORed with the ciphertext after the ChaCha20 encryption was performed. Both keys were then concatenated together and RSA encrypted.</p>

<p><img src="/assets/images/over_the_rainbow/keygen.png" alt="" /></p>

<p>Looking at how the key was generated, I found that a new key was generated for each encrypted file. The function that generated the key bytes contained the string <code class="language-plaintext highlighter-rouge">crypto\rand\rand_lib.c</code>, which told me that OpenSSL’s random number generation was being used. This function uses <code class="language-plaintext highlighter-rouge">BCryptGenRandom</code> internally and it is cryptographically secure. This effectively rules out the possibility that we’ll be able to break the encryption by guessing the key, so to break the encryption we’ll need to focus on the RSA.</p>

<h3 id="the-rsa-encryption">The RSA Encryption</h3>

<p>The RSA encryption function is located at <code class="language-plaintext highlighter-rouge">sub_1400987b0</code>. The strings in this function helpfully tell us that the source file is located at <code class="language-plaintext highlighter-rouge">crypto/rsa/rsa_ossl.c</code>.</p>

<p><img src="/assets/images/over_the_rainbow/rsa_ossl.png" alt="" /></p>

<p>Comparing this source file to the decompiled code, we can see that the encryption function is <a href="https://github.com/openssl/openssl/blob/master/crypto/rsa/rsa_ossl.c#L99C5-L99C5">rsa_ossl_public_encrypt</a>, and that its arguments and return value are given by <code class="language-plaintext highlighter-rouge">static int rsa_ossl_public_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)</code>.</p>

<p>The struct <code class="language-plaintext highlighter-rouge">RSA</code> ia defined in <a href="https://github.com/openssl/openssl/blob/master/crypto/rsa/rsa_local.h#L48C10-L48C10">crypto/rsa/rsa_local.h</a>, and among other things it contains the value of the modulus <code class="language-plaintext highlighter-rouge">N</code> and the public exponent <code class="language-plaintext highlighter-rouge">e</code>. Setting a breakpoint at the RSA function in the debugger, we find</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>N = 0xc9c330728f68087afc60a133e49b9d3de49f0ff9995c5e12e5c65c11897bc718e3e4d272d5a58ce463755b2c63467f0d09f93c31cb67fe318809af7fc8b2c8c721ab547ce4db63dbdfff5d9b06c85799fdee690f90c479c6d0b9e3a3f66e55d63029ce5a02ef84c6aadc5e2241683024cc65d75642afe0babe76f29a677ceb159be48bb3265ebd2bd519a2af7e036cc2e6401c37555761a81c3d1d28a456c38b91b559035bff013dda0439053b9e96f4b278f719e939e677d058bc6e98005aff230814a497ab34b7fa902b666d180de84e24e90f753d79db0b7217acb5c46f4d1aa56bee573f2d47a4337ddd1e2b967edc7038feeb090dec7492d94d9689bb61
e = 3
</code></pre></div></div>

<p>We can also see that the <code class="language-plaintext highlighter-rouge">padding</code> argument is equal to 3, which corresponds to the constant <code class="language-plaintext highlighter-rouge">RSA_NO_PADDING</code>.</p>

<h2 id="breaking-the-rsa">Breaking The RSA</h2>

<p>For a modulus <code class="language-plaintext highlighter-rouge">N</code>, public exponent <code class="language-plaintext highlighter-rouge">e</code>, and message <code class="language-plaintext highlighter-rouge">m</code>, the ciphertext <code class="language-plaintext highlighter-rouge">c</code> is given by the equation <code class="language-plaintext highlighter-rouge">c = m**e % N</code>. The security of RSA relies on this equation being difficult to solve for <code class="language-plaintext highlighter-rouge">m</code>, but there’s one case where it’s easy: if <code class="language-plaintext highlighter-rouge">m**e</code> is less than <code class="language-plaintext highlighter-rouge">N</code>, we can find <code class="language-plaintext highlighter-rouge">m</code> by taking the <code class="language-plaintext highlighter-rouge">e</code>th root of <code class="language-plaintext highlighter-rouge">c</code>. If the public exponent used is large and if short messages are padded using a secure padding algorithm, this never happens, but this challenge uses a public exponent of 3 and uses no padding.</p>

<p>At 0x58 bytes, the message isn’t quite short enough to be recovered simply by taking the cube root of the ciphertext, but we can use a similar strategy. We know the last 0x10 bytes of the plaintext are <code class="language-plaintext highlighter-rouge">expand 32-byte k</code>. If we let <code class="language-plaintext highlighter-rouge">m</code> refer to just the <em>unknown</em> part of the message, tnd we let <code class="language-plaintext highlighter-rouge">x</code> refer to the known bytes <code class="language-plaintext highlighter-rouge">expand 32-byte k</code>, then the equation for the ciphertext is given by <code class="language-plaintext highlighter-rouge">(2**16 * m + x)**3 = c</code>. Expanding this equation out, we obtain a cubic which can be solved exactly for <code class="language-plaintext highlighter-rouge">m</code>, as shown in the following script using Sympy:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sympy</span>
<span class="kn">from</span> <span class="nn">sympy.abc</span> <span class="kn">import</span> <span class="n">m</span>

<span class="n">N</span> <span class="o">=</span> <span class="mh">0xc9c330728f68087afc60a133e49b9d3de49f0ff9995c5e12e5c65c11897bc718e3e4d272d5a58ce463755b2c63467f0d09f93c31cb67fe318809af7fc8b2c8c721ab547ce4db63dbdfff5d9b06c85799fdee690f90c479c6d0b9e3a3f66e55d63029ce5a02ef84c6aadc5e2241683024cc65d75642afe0babe76f29a677ceb159be48bb3265ebd2bd519a2af7e036cc2e6401c37555761a81c3d1d28a456c38b91b559035bff013dda0439053b9e96f4b278f719e939e677d058bc6e98005aff230814a497ab34b7fa902b666d180de84e24e90f753d79db0b7217acb5c46f4d1aa56bee573f2d47a4337ddd1e2b967edc7038feeb090dec7492d94d9689bb61</span>
<span class="n">c</span> <span class="o">=</span> <span class="mh">0x1336e28042804094b2bf03051257aaaaba7eba3e3dd6facff7e3abdd571e9d2e2d2c84f512c0143b27207a3eac0ef965a23f4f4864c7a1ceb913ce1803dba02feb1b56cd8ebe16656abab222e8edca8e9c0dda17c370fce72fe7f6909eed1e6b02e92ebf720ba6051fd7f669cf309ba5467c1fb5d7bb2b7aeca07f11a575746c1047ea35cc3ce246ac0861f0778880d18b71fb2a8d7a736a646cf99b3dcec362d413414beb9f01815db7f72f6e081aee91f191572a28b9576f6c532349f8235b6daf31b39b5add7ade0cfbd30f704eb83d983c215de3261f73565843539f6bb46c9457df16e807449f99f3dabdddd5764fd63d09bc9c4e6844ec3410dc821ab4</span>

<span class="n">x</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="sa">b</span><span class="s">'expand 32-byte k'</span><span class="p">,</span><span class="s">'big'</span><span class="p">)</span>

<span class="n">cubic</span> <span class="o">=</span> <span class="n">sympy</span><span class="p">.</span><span class="n">Eq</span><span class="p">((</span><span class="mi">2</span><span class="o">**</span><span class="mi">256</span> <span class="o">*</span> <span class="n">m</span><span class="o">**</span><span class="mi">3</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">128</span><span class="p">)</span> <span class="o">*</span> <span class="n">m</span><span class="o">**</span><span class="mi">2</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="n">m</span> <span class="o">*</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">c</span> <span class="o">-</span> <span class="n">x</span><span class="o">**</span><span class="mi">3</span><span class="p">)</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">128</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">N</span><span class="p">))</span><span class="o">%</span><span class="n">N</span><span class="p">)</span> 
<span class="k">print</span><span class="p">(</span><span class="n">sympy</span><span class="p">.</span><span class="n">solve</span><span class="p">(</span><span class="n">cubic</span><span class="p">))</span>
</code></pre></div></div>

<p>As expected, this gives us an integer solution: <code class="language-plaintext highlighter-rouge">0x06f7768ff2b963f356fc25b3443f7b729f68bcbdd65f22de685c3cb5c8a2697224368530e264fd388dc962f5d737cb873e24f39709d294224a5268c3512ddb6b3e54419b41c810cf</code>.</p>

<p>The first 0x18 bytes of this integer correspond to the XOR key, and the remaining 0x30 are the unknown bytes of the ChaCha20 matrix. Since the XOR and ChaCha20 steps of the encryption are symmetric, we don’t have to reimplement them: we can just rename <code class="language-plaintext highlighter-rouge">very_important_file.d3crypt_m3</code> to <code class="language-plaintext highlighter-rouge">very_important_file.3ncrypt_m3</code>, then set a breakpoint after the random bytes are generated and replace them with the ones we decrypted.</p>

<p>This gets us the flag: <code class="language-plaintext highlighter-rouge">Wa5nt_th1s_Supp0s3d_t0_b3_4_r3vers1nG_ch4l1eng3@flare-on.com</code></p>]]></content><author><name></name></author><category term="flareon" /><summary type="html"><![CDATA[Attacking a weak RSA implementation]]></summary></entry></feed>