<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Posts about Python internals</title><link>https://chriswarrick.com/</link><atom:link href="https://chriswarrick.com/blog/tags/python-internals.xml" rel="self" type="application/rss+xml" /><description>A rarely updated blog, mostly about programming.</description><lastBuildDate>Thu, 20 Sep 2018 13:52:20 GMT</lastBuildDate><generator>https://github.com/Kwpolska/YetAnotherBlogGenerator</generator><item><title>Python Hackery: merging signatures of two Python functions</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/</link><pubDate>Thu, 20 Sep 2018 13:52:20 GMT</pubDate><guid>https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/</guid><description>
Today’s blog post is going to contain fairly advanced Python hackery. We’ll
take two functions — one is a wrapper for the other, but also adds some
positional arguments.  And we’ll change the signature displayed everywhere from
the uninformative f(new_arg, *args, **kwargs) to something more
appropriate.
</description><content:encoded><![CDATA[
<p>Today’s blog post is going to contain fairly advanced Python hackery. We’ll
take two functions — one is a wrapper for the other, but also adds some
positional arguments.  And we’ll change the signature displayed everywhere from
the uninformative <code class="docutils literal">f(new_arg, *args, **kwargs)</code> to something more
appropriate.</p>



<p>This blog post was inspired by F4D3C0D3 on #python (freenode IRC). I also took
some inspiration from
Gynvael Coldwind’s classic <a class="reference external" href="https://www.youtube.com/watch?v=7VJaprmuHcw">Python 101</a> (April Fools) video. (Audio and some comments are in Polish, but even if you don’t speak the language, it’s still worth it to click through the time bar and see some (fairly unusual) magic happen.)</p>
<section id="starting-point">
<h1>Starting point</h1>
<div class="code"><pre class="code python"><a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-1" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">old</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">):</span>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-2" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-2"></a><span class="w">    </span><span class="sd">&quot;&quot;&quot;This is old&#39;s docstring.&quot;&quot;&quot;</span>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-3" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-3"></a>    <span class="nb">print</span><span class="p">(</span><span class="n">foo</span><span class="p">,</span> <span class="n">bar</span><span class="p">)</span>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-4" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-4" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-4"></a>    <span class="k">return</span> <span class="n">foo</span> <span class="o">+</span> <span class="n">bar</span>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-5" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-5" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-5"></a>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-6" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-6" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-6"></a>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-7" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-7" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-7"></a><span class="k">def</span><span class="w"> </span><span class="nf">new</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">foo</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<a id="rest_code_4d7b7ce96f28484bb5c554955ef085fa-8" name="rest_code_4d7b7ce96f28484bb5c554955ef085fa-8" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_4d7b7ce96f28484bb5c554955ef085fa-8"></a>    <span class="k">return</span> <span class="n">old</span><span class="p">(</span><span class="n">prefix</span> <span class="o">+</span> <span class="n">foo</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
<p>Let’s test it.</p>
<div class="code"><pre class="code pycon"><a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-1" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-1"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">o</span> <span class="o">=</span> <span class="n">old</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">)</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-2" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-2"></a><span class="go">a b</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-3" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-3"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">n</span> <span class="o">=</span> <span class="n">new</span><span class="p">(</span><span class="s1">&#39;!&#39;</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">)</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-4" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-4" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-4"></a><span class="go">!a b</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-5" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-5" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-5"></a><span class="gp">&gt;&gt;&gt; </span><span class="nb">print</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">sep</span><span class="o">=</span><span class="s1">&#39; - &#39;</span><span class="p">)</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-6" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-6" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-6"></a><span class="go">ab - !ab</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-7" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-7" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-7"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="n">old</span><span class="p">)</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-8" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-8" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-8"></a><span class="go">Help on function old in module __main__:</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-9" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-9" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-9"></a>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-10" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-10" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-10"></a><span class="go">old(foo, bar)</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-11" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-11" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-11"></a><span class="go">    This is old&#39;s docstring.</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-12" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-12" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-12"></a>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-13" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-13" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-13"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="n">new</span><span class="p">)</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-14" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-14" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-14"></a><span class="go">Help on function new in module __main__:</span>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-15" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-15" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-15"></a>
<a id="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-16" name="rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-16" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_bed2964a8ddd429a8b6c770e6bd1cbd7-16"></a><span class="go">new(prefix, foo, *args, **kwargs)</span>
</pre></div>
<p>The last line is not exactly informative — it doesn’t tell us that we need to
pass <code class="docutils literal">bar</code> as an argument.  Sure, you could define <code class="docutils literal">new</code> as just <code class="docutils literal">(prefix, foo,
bar)</code> — but that means every change to <code class="docutils literal">old</code> requires editing <code class="docutils literal">new</code> as
well. So, not ideal. Let’s try to fix this.</p>
</section>
<section id="the-existing-infrastructure-functools-wraps">
<h1>The existing infrastructure: functools.wraps</h1>
<p>First, let’s start with the basic facility Python already has.  The standard
library already comes with <code class="docutils literal">functools.wraps</code> and
<code class="docutils literal">functools.update_wrapper</code>.</p>
<p>If you’ve never heard of those two functions, here’s a crash course:</p>
<div class="code"><pre class="code python"><a id="rest_code_36bca7dffa6042ad98ceab813c492019-1" name="rest_code_36bca7dffa6042ad98ceab813c492019-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-2" name="rest_code_36bca7dffa6042ad98ceab813c492019-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-2"></a>    <span class="nd">@functools</span><span class="o">.</span><span class="n">wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-3" name="rest_code_36bca7dffa6042ad98ceab813c492019-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-3"></a>    <span class="k">def</span><span class="w"> </span><span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-4" name="rest_code_36bca7dffa6042ad98ceab813c492019-4" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-4"></a>        <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Inside wrapper&quot;</span><span class="p">)</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-5" name="rest_code_36bca7dffa6042ad98ceab813c492019-5" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-5"></a>        <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-6" name="rest_code_36bca7dffa6042ad98ceab813c492019-6" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-6"></a>    <span class="k">return</span> <span class="n">wrapper</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-7" name="rest_code_36bca7dffa6042ad98ceab813c492019-7" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-7"></a>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-8" name="rest_code_36bca7dffa6042ad98ceab813c492019-8" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-8"></a><span class="nd">@decorator</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-9" name="rest_code_36bca7dffa6042ad98ceab813c492019-9" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-9"></a><span class="k">def</span><span class="w"> </span><span class="nf">square</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">float</span><span class="p">:</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-10" name="rest_code_36bca7dffa6042ad98ceab813c492019-10" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-10"></a><span class="w">    </span><span class="sd">&quot;&quot;&quot;Square a number.&quot;&quot;&quot;</span>
<a id="rest_code_36bca7dffa6042ad98ceab813c492019-11" name="rest_code_36bca7dffa6042ad98ceab813c492019-11" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_36bca7dffa6042ad98ceab813c492019-11"></a>    <span class="k">return</span> <span class="n">n</span> <span class="o">*</span> <span class="n">n</span>
</pre></div>
<p>If we try to inspect the <code class="docutils literal">square</code> function, we’ll see the original name, arguments,
annotations, and the docstring.  If we ran this code again, but with the
<code class="docutils literal">&#64;functools.wraps(f)</code> line commented out, we would only see <code class="docutils literal"><span class="pre">wrapper(*args,</span>
**kwargs)</code>.</p>
<p>This approach gives us a hint of what we need to do.  However, if we apply
<code class="docutils literal">wraps</code> (or <code class="docutils literal">update_wrapper</code>, which is what <code class="docutils literal">wraps</code> ends up calling)
to our function, it will only have <code class="docutils literal">foo</code> and <code class="docutils literal">bar</code> as arguments, and its
name will be displayed as <code class="docutils literal">old</code>.</p>
<p>So, let’s take a look at <a class="reference external" href="https://github.com/python/cpython/blob/4fe8dc68577f9e22aaf24db08fb6647277c42d4c/Lib/functools.py#L27-L79">functools.update_wrapper</a>. What does it do? Two things:</p>
<ul class="simple">
<li><p>copy some attributes from the old function to the new one
(<code class="docutils literal">__module__</code>, <code class="docutils literal">__name__</code>, <code class="docutils literal">__qualname__</code>, <code class="docutils literal">__doc__</code>, <code class="docutils literal">__annotations__</code>)</p></li>
<li><p>update <code class="docutils literal">__dict__</code> of the new function</p></li>
<li><p>set <code class="docutils literal">wrapper.__wrapped__</code></p></li>
</ul>
<p>If we try to experiment with it — by changing the list of things to copy, for
example — we’ll find out that the annotations, the docstring, and the displayed name come from
the copied attributes, but the signature itself is apparently taken from <code class="docutils literal">__wrapped__</code>.</p>
<p>Further investigation reveals this fact about <code class="docutils literal">inspect.signature</code>:</p>
<blockquote>
<p><code class="docutils literal">inspect.signature(callable, *, follow_wrapped=True)</code></p>
<p><em>New in version 3.5:</em> <code class="docutils literal">follow_wrapped</code> parameter. Pass <code class="docutils literal">False</code> to get a signature of callable specifically (<code class="docutils literal">callable.__wrapped__</code> will not be used to unwrap decorated callables.)</p>
</blockquote>
<p>And so, this is our <strong>end goal:</strong></p>
<p class="lead">Craft a function with a specific signature (that merges <code class="docutils literal">old</code> and <code class="docutils literal">new</code>) and set it as <code class="docutils literal">new.__wrapped__</code>.</p>
<p>But first, we need to talk about parallel universes.</p>
<p>Or actually, code objects.</p>
</section>
<section id="defining-a-function-programmatically">
<h1>Defining a function programmatically</h1>
<p>Let’s try an experiment.</p>
<div class="code"><pre class="code pycon"><a id="rest_code_a62b013b29454ad78fe76dca6cddcf0f-1" name="rest_code_a62b013b29454ad78fe76dca6cddcf0f-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_a62b013b29454ad78fe76dca6cddcf0f-1"></a><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span><span class="w"> </span><span class="nf">foo</span><span class="p">(</span><span class="n">bar</span><span class="p">):</span> <span class="k">pass</span>
<a id="rest_code_a62b013b29454ad78fe76dca6cddcf0f-2" name="rest_code_a62b013b29454ad78fe76dca6cddcf0f-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_a62b013b29454ad78fe76dca6cddcf0f-2"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">foo</span><span class="o">.</span><span class="n">__wrapped__</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="kc">None</span>
<a id="rest_code_a62b013b29454ad78fe76dca6cddcf0f-3" name="rest_code_a62b013b29454ad78fe76dca6cddcf0f-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_a62b013b29454ad78fe76dca6cddcf0f-3"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">help</span><span class="p">(</span><span class="n">foo</span><span class="p">)</span>
<a id="rest_code_a62b013b29454ad78fe76dca6cddcf0f-4" name="rest_code_a62b013b29454ad78fe76dca6cddcf0f-4" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_a62b013b29454ad78fe76dca6cddcf0f-4"></a><span class="go">foo(x, y)</span>
</pre></div>
<p>So, there are two ways to do this.  The first one would be to generate a string
with the signature and just use <code class="docutils literal">eval</code> to get a <code class="docutils literal">__wrapped__</code> function. But
that would be cheating, and honestly, quite boring. (The inspect module could
help us with preparing the string.)  The second one? Create code objects
manually.</p>
<section id="code-objects">
<h2>Code objects</h2>
<p>To create a function, we’ll need the <code class="docutils literal">types</code> module. <code class="docutils literal">types.FunctionType</code>
gives us a function, but it asks us for a code object. As the <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html">docs</a> state,
<em>Code objects represent byte-compiled executable Python code, or bytecode.</em></p>
<p>To create one by
hand, we’ll need <code class="docutils literal">types.CodeType</code>. Well, not exactly by hand — we’ll end up doing a three-way merge between
<code class="docutils literal">source</code> (<code class="docutils literal">old</code>), <code class="docutils literal">dest</code> (<code class="docutils literal">new</code>) and <code class="docutils literal">def <span class="pre">_blank():</span> pass</code> (a function
that does nothing).</p>
<p>Let’s look at the docstring for <code class="docutils literal">CodeType</code>:</p>
<div class="code"><pre class="code text"><a id="rest_code_6208107589054526ac90219ce627e4dc-1" name="rest_code_6208107589054526ac90219ce627e4dc-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_6208107589054526ac90219ce627e4dc-1"></a>code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
<a id="rest_code_6208107589054526ac90219ce627e4dc-2" name="rest_code_6208107589054526ac90219ce627e4dc-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_6208107589054526ac90219ce627e4dc-2"></a>    constants, names, varnames, filename, name, firstlineno,
<a id="rest_code_6208107589054526ac90219ce627e4dc-3" name="rest_code_6208107589054526ac90219ce627e4dc-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_6208107589054526ac90219ce627e4dc-3"></a>    lnotab[, freevars[, cellvars]])
<a id="rest_code_6208107589054526ac90219ce627e4dc-4" name="rest_code_6208107589054526ac90219ce627e4dc-4" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_6208107589054526ac90219ce627e4dc-4"></a>
<a id="rest_code_6208107589054526ac90219ce627e4dc-5" name="rest_code_6208107589054526ac90219ce627e4dc-5" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_6208107589054526ac90219ce627e4dc-5"></a>Create a code object.  Not for the faint of heart.
</pre></div>
<p>All of the arguments end up being fields of a code objects (name starts with
<code class="docutils literal">co_</code>).  For each
function <code class="docutils literal">f</code>, its code object is <code class="docutils literal">f.__code__</code>. You can find the filename in
<code class="docutils literal">f.__code__.co_filename</code>, for example. The meaning of all fields can be
found in docs for the <a class="reference external" href="https://docs.python.org/3/library/inspect.html#types-and-members">inspect module</a>. We’ll be
interested in the following three fields:</p>
<ul class="simple">
<li><p><code class="docutils literal">argcount</code> — number of arguments (not including keyword only arguments, * or ** args)</p></li>
<li><p><code class="docutils literal">kwonlyargcount</code> — number of keyword only arguments (not including ** arg)</p></li>
<li><p><code class="docutils literal">varnames</code> — tuple of names of arguments and local variables</p></li>
</ul>
<p>For all the other fields, we’ll copy them from the appropriate function (one of
the three).  We don’t expect anyone to call the wrapped function directly; as
long as <code class="docutils literal">help</code> and <code class="docutils literal">inspect</code> members don’t crash when they look into it,
we’re fine.</p>
</section>
<section id="everything-you-need-to-know-about-function-arguments">
<h2>Everything you need to know about function arguments</h2>
<div class="code"><pre class="code pycon"><a id="rest_code_f4b49082358149cf861286d903952272-1" name="rest_code_f4b49082358149cf861286d903952272-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_f4b49082358149cf861286d903952272-1"></a><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">d</span><span class="o">=</span><span class="mi">3</span><span class="p">):</span> <span class="k">pass</span>
<a id="rest_code_f4b49082358149cf861286d903952272-2" name="rest_code_f4b49082358149cf861286d903952272-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_f4b49082358149cf861286d903952272-2"></a><span class="gp">&gt;&gt;&gt; </span><span class="n">inspect</span><span class="o">.</span><span class="n">getfullargspec</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<a id="rest_code_f4b49082358149cf861286d903952272-3" name="rest_code_f4b49082358149cf861286d903952272-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_f4b49082358149cf861286d903952272-3"></a><span class="go">FullArgSpec(args=[&#39;a&#39;, &#39;b&#39;, &#39;c&#39;], varargs=None, varkw=None, defaults=(1, 2), kwonlyargs=[&#39;d&#39;], kwonlydefaults={&#39;d&#39;: 3}, annotations={})</span>
</pre></div>
<p>A function signature has the following syntax:</p>
<ol class="arabic simple">
<li><p>Any positional (non-optional) arguments</p></li>
<li><p>Variable positional arguments (<code class="docutils literal">*x</code>, name stored in <code class="docutils literal">varargs</code>)</p></li>
<li><p>Arguments with defaults (keyword-maybe arguments); their value is stored in <code class="docutils literal">__defaults__</code> left-to-right</p></li>
<li><p>Keyword-only arguments (after an asterisk); their values are stored in a dictionary.  Cannot be used if <code class="docutils literal">varargs</code> are defined.</p></li>
<li><p>Variable keyword arguments (<code class="docutils literal">**y</code>, name stored in <code class="docutils literal">varkw</code>)</p></li>
</ol>
<p>We’re going to make one assumption: we aren’t going to support a <code class="docutils literal">source</code>
function that uses variable arguments of any kind.  So, our final signature
will be composed like this:</p>
<ol class="arabic simple">
<li><p><code class="docutils literal">dest</code> positional arguments</p></li>
<li><p><code class="docutils literal">source</code> positional arguments</p></li>
<li><p><code class="docutils literal">dest</code> keyword-maybe arguments</p></li>
<li><p><code class="docutils literal">source</code> keyword-maybe arguments</p></li>
<li><p><code class="docutils literal">dest</code> keyword-only arguments</p></li>
<li><p><code class="docutils literal">source</code> keyword-only arguments</p></li>
</ol>
<p>That will be saved into <code class="docutils literal">co_names</code>.  The first two arguments are counts —
the first one is <code class="docutils literal">len(1+2+3+4)</code> and the other is <code class="docutils literal">len(5+6)</code>. The remaining
arguments to <code class="docutils literal">CodeType</code> will be either safe minimal defaults, or things taken from
one of the three functions.</p>
<p>We’ll also need to do one more thing: we must ensure <code class="docutils literal">__defaults__</code>,
<code class="docutils literal">__kwdefaults__</code>, and <code class="docutils literal">__annotations__</code> are all in the right places.
That’s also a fairly simple thing to do (it requires more tuple/dict merging).
And with that, we’re done.</p>
</section>
</section>
<section id="final-results">
<h1>Final results</h1>
<p>Before I show you the code, let’s test it out:</p>
<div class="code"><pre class="code python"><a id="rest_code_0f4fd7ac3e33455b8abe64839a219429-1" name="rest_code_0f4fd7ac3e33455b8abe64839a219429-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_0f4fd7ac3e33455b8abe64839a219429-1"></a><span class="c1"># old defined as before</span>
<a id="rest_code_0f4fd7ac3e33455b8abe64839a219429-2" name="rest_code_0f4fd7ac3e33455b8abe64839a219429-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_0f4fd7ac3e33455b8abe64839a219429-2"></a><span class="nd">@merge_args</span><span class="p">(</span><span class="n">old</span><span class="p">)</span>
<a id="rest_code_0f4fd7ac3e33455b8abe64839a219429-3" name="rest_code_0f4fd7ac3e33455b8abe64839a219429-3" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_0f4fd7ac3e33455b8abe64839a219429-3"></a><span class="k">def</span><span class="w"> </span><span class="nf">new</span><span class="p">(</span><span class="n">prefix</span><span class="p">,</span> <span class="n">foo</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<a id="rest_code_0f4fd7ac3e33455b8abe64839a219429-4" name="rest_code_0f4fd7ac3e33455b8abe64839a219429-4" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_0f4fd7ac3e33455b8abe64839a219429-4"></a>    <span class="k">return</span> <span class="n">old</span><span class="p">(</span><span class="n">prefix</span> <span class="o">+</span> <span class="n">foo</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
<p>And the end result — <code class="docutils literal">help(new)</code> says:</p>
<div class="code"><pre class="code text"><a id="rest_code_1088cc0d6c3d483fb32f8946fd791655-1" name="rest_code_1088cc0d6c3d483fb32f8946fd791655-1" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_1088cc0d6c3d483fb32f8946fd791655-1"></a>new(prefix, foo, bar)
<a id="rest_code_1088cc0d6c3d483fb32f8946fd791655-2" name="rest_code_1088cc0d6c3d483fb32f8946fd791655-2" href="https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/#rest_code_1088cc0d6c3d483fb32f8946fd791655-2"></a>    This is old&#39;s docstring.
</pre></div>
<p>We did it!</p>
<p class="lead">The code is available on <a class="reference external" href="https://github.com/Kwpolska/merge_args">GitHub</a> and on <a class="reference external" href="https://pypi.org/project/merge-args/">PyPI</a> (<code class="docutils literal">pip install merge_args</code>).
There’s also an extensive test suite.</p>
<p>PS. you might be interested in another related post of mine, in which I
reverse-engineer the compilation of a function: <a class="reference external" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/">Gynvael’s Mission 11 (en): Python bytecode reverse-engineering</a></p>
</section>
]]></content:encoded><category>Python</category><category>hacking</category><category>Python</category><category>Python hackery</category><category>Python internals</category></item><item><title>Gynvael’s Mission 11 (en): Python bytecode reverse-engineering</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/</link><pubDate>Thu, 03 Aug 2017 10:45:40 GMT</pubDate><guid>https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/</guid><description>
Gynvael Coldwind is a security researcher at Google, who hosts weekly livestreams about security and programming in Polish and English). As part of the streams, he gives out missions — basically, CTF-style reverse engineering tasks. Yesterday’s mission was about Elvish — I mean Paint — I mean Python programming and bytecode.
</description><content:encoded><![CDATA[
<p>Gynvael Coldwind is a security researcher at Google, who hosts weekly livestreams about security and programming in <a class="reference external" href="https://gaming.youtube.com/user/GynvaelColdwind/live">Polish</a> and <a class="reference external" href="https://gaming.youtube.com/user/GynvaelEN/live">English</a>). As part of the streams, he gives out missions — basically, CTF-style reverse engineering tasks. Yesterday’s mission was about Elvish — I mean Paint — I mean Python programming and bytecode.</p>



<div class="code"><pre class="code text"><a id="rest_code_a8243f6403b44a13a671955d2b908777-1" name="rest_code_a8243f6403b44a13a671955d2b908777-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-1"></a>MISSION 011               goo.gl/13Bia9             DIFFICULTY: ██████░░░░ [6╱10]
<a id="rest_code_a8243f6403b44a13a671955d2b908777-2" name="rest_code_a8243f6403b44a13a671955d2b908777-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-2"></a>┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
<a id="rest_code_a8243f6403b44a13a671955d2b908777-3" name="rest_code_a8243f6403b44a13a671955d2b908777-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-3"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-4" name="rest_code_a8243f6403b44a13a671955d2b908777-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-4"></a>Finally some real work!
<a id="rest_code_a8243f6403b44a13a671955d2b908777-5" name="rest_code_a8243f6403b44a13a671955d2b908777-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-5"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-6" name="rest_code_a8243f6403b44a13a671955d2b908777-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-6"></a>One of our field agents managed to infiltrate suspects hideout and steal a
<a id="rest_code_a8243f6403b44a13a671955d2b908777-7" name="rest_code_a8243f6403b44a13a671955d2b908777-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-7"></a>pendrive possibly containing important information. However, the pendrive
<a id="rest_code_a8243f6403b44a13a671955d2b908777-8" name="rest_code_a8243f6403b44a13a671955d2b908777-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-8"></a>actually requires one to authenticate themselves before accessing the stored
<a id="rest_code_a8243f6403b44a13a671955d2b908777-9" name="rest_code_a8243f6403b44a13a671955d2b908777-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-9"></a>files.
<a id="rest_code_a8243f6403b44a13a671955d2b908777-10" name="rest_code_a8243f6403b44a13a671955d2b908777-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-10"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-11" name="rest_code_a8243f6403b44a13a671955d2b908777-11" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-11"></a>We gave the pendrive to our laboratory and they managed to dump the firmware. We
<a id="rest_code_a8243f6403b44a13a671955d2b908777-12" name="rest_code_a8243f6403b44a13a671955d2b908777-12" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-12"></a>looked at the deadlisting they sent and for our best knowledge it&#39;s some form of
<a id="rest_code_a8243f6403b44a13a671955d2b908777-13" name="rest_code_a8243f6403b44a13a671955d2b908777-13" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-13"></a>Elvish. We can&#39;t read it.
<a id="rest_code_a8243f6403b44a13a671955d2b908777-14" name="rest_code_a8243f6403b44a13a671955d2b908777-14" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-14"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-15" name="rest_code_a8243f6403b44a13a671955d2b908777-15" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-15"></a>Here is the firmware: goo.gl/axsAHt
<a id="rest_code_a8243f6403b44a13a671955d2b908777-16" name="rest_code_a8243f6403b44a13a671955d2b908777-16" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-16"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-17" name="rest_code_a8243f6403b44a13a671955d2b908777-17" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-17"></a>And off you go. Bring us back the password.
<a id="rest_code_a8243f6403b44a13a671955d2b908777-18" name="rest_code_a8243f6403b44a13a671955d2b908777-18" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-18"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-19" name="rest_code_a8243f6403b44a13a671955d2b908777-19" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-19"></a>Good luck!
<a id="rest_code_a8243f6403b44a13a671955d2b908777-20" name="rest_code_a8243f6403b44a13a671955d2b908777-20" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-20"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-21" name="rest_code_a8243f6403b44a13a671955d2b908777-21" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-21"></a>---------------------------------------------------------------------------------
<a id="rest_code_a8243f6403b44a13a671955d2b908777-22" name="rest_code_a8243f6403b44a13a671955d2b908777-22" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-22"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-23" name="rest_code_a8243f6403b44a13a671955d2b908777-23" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-23"></a>If you decode the answer, put it in the comments under this video! If you write
<a id="rest_code_a8243f6403b44a13a671955d2b908777-24" name="rest_code_a8243f6403b44a13a671955d2b908777-24" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-24"></a>a blogpost / post your solution online, please add a link in the comments too!
<a id="rest_code_a8243f6403b44a13a671955d2b908777-25" name="rest_code_a8243f6403b44a13a671955d2b908777-25" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-25"></a>
<a id="rest_code_a8243f6403b44a13a671955d2b908777-26" name="rest_code_a8243f6403b44a13a671955d2b908777-26" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-26"></a>P.S. I&#39;ll show/explain the solution on the stream in ~two weeks.
<a id="rest_code_a8243f6403b44a13a671955d2b908777-27" name="rest_code_a8243f6403b44a13a671955d2b908777-27" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_a8243f6403b44a13a671955d2b908777-27"></a>P.S.2. Bonus points for recreating the original high-level code.
</pre></div>
<p>Here’s the firmware:</p>
<div class="code"><pre class="code text"><a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-1" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-1"></a>co_argcount 1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-2" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-2"></a>co_consts (None, &#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;, &#39;hex&#39;, 89, 255, 115, 50)
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-3" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-3"></a>co_flags 67
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-4" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-4"></a>co_name check_password
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-5" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-5"></a>co_names (&#39;decode&#39;, &#39;len&#39;, &#39;False&#39;, &#39;all&#39;, &#39;zip&#39;, &#39;ord&#39;)
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-6" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-6"></a>co_nlocals 4
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-7" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-7"></a>co_stacksize 6
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-8" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-8"></a>co_varnames (&#39;s&#39;, &#39;good&#39;, &#39;cs&#39;, &#39;cg&#39;)
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-9" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-9"></a>              0 LOAD_CONST               1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-10" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-10"></a>              3 LOAD_ATTR                0
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-11" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-11" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-11"></a>              6 LOAD_CONST               2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-12" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-12" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-12"></a>              9 CALL_FUNCTION            1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-13" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-13" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-13"></a>             12 STORE_FAST               1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-14" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-14" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-14"></a>             15 LOAD_GLOBAL              1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-15" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-15" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-15"></a>             18 LOAD_FAST                0
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-16" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-16" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-16"></a>             21 CALL_FUNCTION            1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-17" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-17" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-17"></a>             24 LOAD_GLOBAL              1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-18" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-18" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-18"></a>             27 LOAD_FAST                1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-19" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-19" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-19"></a>             30 CALL_FUNCTION            1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-20" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-20" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-20"></a>             33 COMPARE_OP               3 (!=)
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-21" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-21" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-21"></a>             36 POP_JUMP_IF_FALSE       43
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-22" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-22" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-22"></a>             39 LOAD_GLOBAL              2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-23" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-23" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-23"></a>             42 RETURN_VALUE
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-24" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-24" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-24"></a>        &gt;&gt;   43 LOAD_GLOBAL              3
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-25" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-25" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-25"></a>             46 BUILD_LIST               0
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-26" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-26" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-26"></a>             49 LOAD_GLOBAL              4
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-27" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-27" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-27"></a>             52 LOAD_FAST                0
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-28" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-28" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-28"></a>             55 LOAD_FAST                1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-29" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-29" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-29"></a>             58 CALL_FUNCTION            2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-30" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-30" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-30"></a>             61 GET_ITER
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-31" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-31" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-31"></a>        &gt;&gt;   62 FOR_ITER                52 (to 117)
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-32" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-32" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-32"></a>             65 UNPACK_SEQUENCE          2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-33" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-33" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-33"></a>             68 STORE_FAST               2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-34" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-34" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-34"></a>             71 STORE_FAST               3
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-35" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-35" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-35"></a>             74 LOAD_GLOBAL              5
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-36" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-36" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-36"></a>             77 LOAD_FAST                2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-37" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-37" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-37"></a>             80 CALL_FUNCTION            1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-38" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-38" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-38"></a>             83 LOAD_CONST               3
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-39" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-39" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-39"></a>             86 BINARY_SUBTRACT
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-40" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-40" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-40"></a>             87 LOAD_CONST               4
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-41" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-41" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-41"></a>             90 BINARY_AND
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-42" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-42" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-42"></a>             91 LOAD_CONST               5
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-43" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-43" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-43"></a>             94 BINARY_XOR
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-44" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-44" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-44"></a>             95 LOAD_CONST               6
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-45" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-45" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-45"></a>             98 BINARY_XOR
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-46" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-46" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-46"></a>             99 LOAD_GLOBAL              5
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-47" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-47" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-47"></a>            102 LOAD_FAST                3
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-48" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-48" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-48"></a>            105 CALL_FUNCTION            1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-49" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-49" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-49"></a>            108 COMPARE_OP               2 (==)
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-50" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-50" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-50"></a>            111 LIST_APPEND              2
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-51" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-51" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-51"></a>            114 JUMP_ABSOLUTE           62
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-52" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-52" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-52"></a>        &gt;&gt;  117 CALL_FUNCTION            1
<a id="rest_code_ee28059f26f346f09e4a2ab239ddd619-53" name="rest_code_ee28059f26f346f09e4a2ab239ddd619-53" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ee28059f26f346f09e4a2ab239ddd619-53"></a>            120 RETURN_VALUE
</pre></div>
<p>To the uninitiated, this might look like <em>Elvish</em>. In reality, this is Python bytecode — the instruction set understood by Python’s (CPython 2.7) virtual machine. Python, like many other languages, uses a compiler to translate human-readable source code into something more appropriate for computers. Python code compiles to bytecode, which is then executed by CPython’s virtual machine. CPython bytecode can be ported between different hardware, while machine code cannot. However, machine code can often be faster than languages based on virtual machines and bytecode. (Java and C# work the same way as Python, C compiles directly to machine code)</p>
<p>This is the internal representation of a Python function. The first few lines are the member variables of the <code class="docutils literal">f.__code__</code> object of our function. We know that:</p>
<ul class="simple">
<li><p>it takes 1 argument</p></li>
<li><p>it has 7 constants: None, a long string of hex digits, the string <code class="docutils literal">'hex'</code>, and numbers: 89, 255, 115, 50.</p></li>
<li><p>its <a class="reference external" href="https://docs.python.org/2.7/library/inspect.html#code-objects-bit-flags">flags</a> are set to 67 (CO_NOFREE, CO_NEWLOCALS, CO_OPTIMIZED). This is the “standard” value that most uncomplicated functions take.</p></li>
<li><p>its name is <code class="docutils literal">check_password</code></p></li>
<li><p>it uses the following globals or attribute names: <code class="docutils literal">decode</code>, <code class="docutils literal">len</code>, <code class="docutils literal">False</code>, <code class="docutils literal">all</code>, <code class="docutils literal">zip</code>, <code class="docutils literal">ord</code></p></li>
<li><p>it has 4 local variables</p></li>
<li><p>it uses a stack of size 6</p></li>
<li><p>its variables are named <code class="docutils literal">s</code>, <code class="docutils literal">good</code>, <code class="docutils literal">cs</code>, <code class="docutils literal">cg</code></p></li>
</ul>
<p>There are two ways to solve this task: you can re-assemble the <code class="docutils literal">dis</code> output with the help of the <code class="docutils literal">opcode</code> module, or try to re-create the function by hand, using the bytecode. I chose the latter method.</p>
<section id="reverse-engineering-python-bytecode-re-creating-the-function-by-hand">
<h1>Reverse-engineering Python bytecode: re-creating the function by hand</h1>
<p>I started by recreating the original firmware file. I created an empty function and wrote some code to print out <code class="docutils literal">__code__</code> contents and <code class="docutils literal">dis.dis</code> output. I also added color-coding to help me read it:</p>
<div class="code"><pre class="code python"><a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-1" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-1"></a><span class="ch">#!/usr/bin/env python2</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-2" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-2"></a><span class="kn">import</span><span class="w"> </span><span class="nn">dis</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-3" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-3"></a><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-4" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-4"></a>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-5" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-5"></a><span class="c1"># Write code here</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-6" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-6"></a><span class="k">def</span><span class="w"> </span><span class="nf">check_password</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-7" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-7"></a>    <span class="k">pass</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-8" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-8"></a>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-9" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-9"></a><span class="c1"># Reverse engineering the code</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-10" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-10"></a><span class="n">cnames</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;co_argcount&#39;</span><span class="p">,</span> <span class="s1">&#39;co_consts&#39;</span><span class="p">,</span> <span class="s1">&#39;co_flags&#39;</span><span class="p">,</span> <span class="s1">&#39;co_name&#39;</span><span class="p">,</span> <span class="s1">&#39;co_names&#39;</span><span class="p">,</span> <span class="s1">&#39;co_nlocals&#39;</span><span class="p">,</span> <span class="s1">&#39;co_stacksize&#39;</span><span class="p">,</span> <span class="s1">&#39;co_varnames&#39;</span><span class="p">)</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-11" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-11" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-11"></a><span class="n">cvalues</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">,</span> <span class="mi">89</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">115</span><span class="p">,</span> <span class="mi">50</span><span class="p">),</span> <span class="mi">67</span><span class="p">,</span> <span class="s1">&#39;check_password&#39;</span><span class="p">,</span> <span class="p">(</span><span class="s1">&#39;decode&#39;</span><span class="p">,</span> <span class="s1">&#39;len&#39;</span><span class="p">,</span> <span class="s1">&#39;False&#39;</span><span class="p">,</span> <span class="s1">&#39;all&#39;</span><span class="p">,</span> <span class="s1">&#39;zip&#39;</span><span class="p">,</span> <span class="s1">&#39;ord&#39;</span><span class="p">),</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="p">(</span><span class="s1">&#39;s&#39;</span><span class="p">,</span> <span class="s1">&#39;good&#39;</span><span class="p">,</span> <span class="s1">&#39;cs&#39;</span><span class="p">,</span> <span class="s1">&#39;cg&#39;</span><span class="p">))</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-12" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-12" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-12"></a>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-13" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-13" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-13"></a><span class="k">for</span> <span class="n">n</span><span class="p">,</span> <span class="n">ov</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">cnames</span><span class="p">,</span> <span class="n">cvalues</span><span class="p">):</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-14" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-14" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-14"></a>    <span class="n">v</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">check_password</span><span class="o">.</span><span class="vm">__code__</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-15" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-15" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-15"></a>    <span class="k">if</span> <span class="n">v</span> <span class="o">==</span> <span class="n">ov</span><span class="p">:</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-16" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-16" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-16"></a>        <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\033</span><span class="s1">[1;32m&#39;</span><span class="p">)</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-17" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-17" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-17"></a>    <span class="k">else</span><span class="p">:</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-18" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-18" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-18"></a>        <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\033</span><span class="s1">[1;31m&#39;</span><span class="p">)</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-19" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-19" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-19"></a>    <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-20" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-20" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-20"></a>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-21" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-21" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-21"></a>    <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot; &quot;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-22" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-22" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-22"></a>    <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-23" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-23" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-23"></a>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-24" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-24" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-24"></a>    <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\033</span><span class="s1">[0m&#39;</span><span class="p">)</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-25" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-25" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-25"></a>    <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-26" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-26" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-26"></a>
<a id="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-27" name="rest_code_db3df9519cfe4fcebd1d69fff256cc2e-27" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_db3df9519cfe4fcebd1d69fff256cc2e-27"></a><span class="n">dis</span><span class="o">.</span><span class="n">dis</span><span class="p">(</span><span class="n">check_password</span><span class="p">)</span>
</pre></div>
<p>If we run this solver, we get the following output (text in brackets added by me):</p>
<div class="code"><pre class="code text"><a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-1" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-1"></a>co_argcount 1            [OK]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-2" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-2"></a>co_consts (None,)        [1/7 match]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-3" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-3"></a>co_flags 67              [OK]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-4" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-4"></a>co_name check_password   [OK]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-5" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-5"></a>co_names ()              [0/6 match]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-6" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-6"></a>co_nlocals 1             [should be 4]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-7" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-7"></a>co_stacksize 1           [should be 6]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-8" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-8"></a>co_varnames (&#39;s&#39;,)       [1/4 match]
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-9" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-9"></a>  7           0 LOAD_CONST               0 (None)
<a id="rest_code_27f66539bcdd4afa8277aa02dff98f11-10" name="rest_code_27f66539bcdd4afa8277aa02dff98f11-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_27f66539bcdd4afa8277aa02dff98f11-10"></a>              3 RETURN_VALUE
</pre></div>
<p>We can see (with the help of colors, not reproduced here), that we’ve got <code class="docutils literal">co_argcount</code>, <code class="docutils literal">co_flags</code>, <code class="docutils literal">co_name</code> correctly. We also have one constant (<code class="docutils literal">None</code>, in every function) and one variable name (<code class="docutils literal">s</code>, the argument name). We can also see <code class="docutils literal">dis.dis()</code> output. While it looks similar to the assignment, there are a few noticeable differences: there is no <code class="docutils literal">7</code> (line number) at the start, and <code class="docutils literal">LOAD_CONST</code> instructions in the original code did not have anything in parentheses (only comparisions and loops did).  This makes reading bytecode harder, but still possible. (I originally thought about using <code class="docutils literal">diff</code> for help, but it’s not hard to do it by hand. I did use <code class="docutils literal">diff</code> for the final checking after a manual “conversion”)</p>
<p>Let’s stop to look at the constants and names for a second. The long string is followed by <code class="docutils literal">hex</code>, and one of the constants is <code class="docutils literal">decode</code>. This means that we need to use <code class="docutils literal"><span class="pre">str.decode('hex')</span></code> to create a (byte)string of some information. Puzzle answers tend to be human-readable, and this string isn’t — so we need to do some more work.</p>
<p>So, let’s try reproducing the start of the original mission code using what we’ve just discussed. Python’s VM is based on a stack. In the bytecode above, you can see that instructions take 0 or 1 arguments. Some of them put things on the stack, others do actions and remove them. Most instruction names are self-explanatory, but the full list can be found in the <a class="reference external" href="https://docs.python.org/2/library/dis.html#python-bytecode-instructions">dis module documentation</a>.</p>
<p>Instructions like <code class="docutils literal">LOAD</code> and <code class="docutils literal">STORE</code> refer to indices in the constants/names/varnames tuples. To make it easier, here’s a “table” of them:</p>
<div class="code"><pre class="code text"><a id="rest_code_8c089b4cab264b4f851b62c686149db7-1" name="rest_code_8c089b4cab264b4f851b62c686149db7-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-1"></a>constants
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-2" name="rest_code_8c089b4cab264b4f851b62c686149db7-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-2"></a> 0     1                                                       2      3   4    5    6
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-3" name="rest_code_8c089b4cab264b4f851b62c686149db7-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-3"></a>(None, &#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;, &#39;hex&#39;, 89, 255, 115, 50)
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-4" name="rest_code_8c089b4cab264b4f851b62c686149db7-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-4"></a>
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-5" name="rest_code_8c089b4cab264b4f851b62c686149db7-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-5"></a>names (globals, attributes)
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-6" name="rest_code_8c089b4cab264b4f851b62c686149db7-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-6"></a> 0         1      2        3      4      5
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-7" name="rest_code_8c089b4cab264b4f851b62c686149db7-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-7"></a>(&#39;decode&#39;, &#39;len&#39;, &#39;False&#39;, &#39;all&#39;, &#39;zip&#39;, &#39;ord&#39;)
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-8" name="rest_code_8c089b4cab264b4f851b62c686149db7-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-8"></a>
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-9" name="rest_code_8c089b4cab264b4f851b62c686149db7-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-9"></a>varnames (locals, _fast)
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-10" name="rest_code_8c089b4cab264b4f851b62c686149db7-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-10"></a> 0    1       2     3
<a id="rest_code_8c089b4cab264b4f851b62c686149db7-11" name="rest_code_8c089b4cab264b4f851b62c686149db7-11" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_8c089b4cab264b4f851b62c686149db7-11"></a>(&#39;s&#39;, &#39;good&#39;, &#39;cs&#39;, &#39;cg&#39;)
</pre></div>
<p>In order to improve readability, I will use “new” <code class="docutils literal">dis</code> output with names in parentheses below:</p>
<div class="code"><pre class="code text"><a id="rest_code_fb6ce8efa1624395b2049e1148156b1c-1" name="rest_code_fb6ce8efa1624395b2049e1148156b1c-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_fb6ce8efa1624395b2049e1148156b1c-1"></a> 0 LOAD_CONST               1 (&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;)
<a id="rest_code_fb6ce8efa1624395b2049e1148156b1c-2" name="rest_code_fb6ce8efa1624395b2049e1148156b1c-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_fb6ce8efa1624395b2049e1148156b1c-2"></a> 3 LOAD_ATTR                0 (decode)
<a id="rest_code_fb6ce8efa1624395b2049e1148156b1c-3" name="rest_code_fb6ce8efa1624395b2049e1148156b1c-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_fb6ce8efa1624395b2049e1148156b1c-3"></a> 6 LOAD_CONST               2 (&#39;hex&#39;)
<a id="rest_code_fb6ce8efa1624395b2049e1148156b1c-4" name="rest_code_fb6ce8efa1624395b2049e1148156b1c-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_fb6ce8efa1624395b2049e1148156b1c-4"></a> 9 CALL_FUNCTION            1 # function takes 1 argument from stack
<a id="rest_code_fb6ce8efa1624395b2049e1148156b1c-5" name="rest_code_fb6ce8efa1624395b2049e1148156b1c-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_fb6ce8efa1624395b2049e1148156b1c-5"></a>12 STORE_FAST               1 (good)
</pre></div>
<p>As I guessed before, the first line of our function is as follows:</p>
<div class="code"><pre class="code python"><a id="rest_code_85beff924755471e8039dad55a327578-1" name="rest_code_85beff924755471e8039dad55a327578-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_85beff924755471e8039dad55a327578-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">check_password</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<a id="rest_code_85beff924755471e8039dad55a327578-2" name="rest_code_85beff924755471e8039dad55a327578-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_85beff924755471e8039dad55a327578-2"></a>    <span class="n">good</span> <span class="o">=</span> <span class="s1">&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span>  <span class="c1"># new</span>
</pre></div>
<p>If we run the solver again, we’ll see that the first 12 bytes of our bytecode match the mission text. We can also see that <code class="docutils literal">varnames</code> is filled in half, we’ve added two constants, and one name.  The next few lines are as follows:</p>
<div class="code"><pre class="code text"><a id="rest_code_506359919e4b49e6b2cd2712f3301286-1" name="rest_code_506359919e4b49e6b2cd2712f3301286-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-1"></a>15 LOAD_GLOBAL              1
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-2" name="rest_code_506359919e4b49e6b2cd2712f3301286-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-2"></a>18 LOAD_FAST                0
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-3" name="rest_code_506359919e4b49e6b2cd2712f3301286-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-3"></a>21 CALL_FUNCTION            1
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-4" name="rest_code_506359919e4b49e6b2cd2712f3301286-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-4"></a>24 LOAD_GLOBAL              1
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-5" name="rest_code_506359919e4b49e6b2cd2712f3301286-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-5"></a>27 LOAD_FAST                1
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-6" name="rest_code_506359919e4b49e6b2cd2712f3301286-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-6"></a>30 CALL_FUNCTION            1
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-7" name="rest_code_506359919e4b49e6b2cd2712f3301286-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-7"></a>33 COMPARE_OP               3 (!=)
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-8" name="rest_code_506359919e4b49e6b2cd2712f3301286-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-8"></a>36 POP_JUMP_IF_FALSE       43
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-9" name="rest_code_506359919e4b49e6b2cd2712f3301286-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-9"></a>39 LOAD_GLOBAL              2
<a id="rest_code_506359919e4b49e6b2cd2712f3301286-10" name="rest_code_506359919e4b49e6b2cd2712f3301286-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_506359919e4b49e6b2cd2712f3301286-10"></a>42 RETURN_VALUE
</pre></div>
<p>We can see that we’re putting a global object on stack and calling it with one argument. In both cases, the global has the index 1, that’s <code class="docutils literal">len</code>. The two arguments are <code class="docutils literal">s</code> and <code class="docutils literal">good</code>. We put both lengths on stack, then compare them. If the comparison fails (they’re equal), we jump to the instruction starting at byte 43, otherwise we continue execution to load the second global (False) and return it.  This wall of text translates to the following simple code:</p>
<div class="code"><pre class="code python"><a id="rest_code_ff50cca92832460f9863e211d84e2c63-1" name="rest_code_ff50cca92832460f9863e211d84e2c63-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ff50cca92832460f9863e211d84e2c63-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">check_password</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<a id="rest_code_ff50cca92832460f9863e211d84e2c63-2" name="rest_code_ff50cca92832460f9863e211d84e2c63-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ff50cca92832460f9863e211d84e2c63-2"></a>    <span class="n">good</span> <span class="o">=</span> <span class="s1">&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span>
<a id="rest_code_ff50cca92832460f9863e211d84e2c63-3" name="rest_code_ff50cca92832460f9863e211d84e2c63-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ff50cca92832460f9863e211d84e2c63-3"></a>    <span class="k">if</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="nb">len</span><span class="p">(</span><span class="n">good</span><span class="p">):</span>  <span class="c1"># new</span>
<a id="rest_code_ff50cca92832460f9863e211d84e2c63-4" name="rest_code_ff50cca92832460f9863e211d84e2c63-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_ff50cca92832460f9863e211d84e2c63-4"></a>        <span class="k">return</span> <span class="kc">False</span>         <span class="c1"># new</span>
</pre></div>
<p>Let’s take another look at our names. We can see we’re missing <code class="docutils literal">all</code>, <code class="docutils literal">zip</code>, <code class="docutils literal">ord</code>. You can already see a common pattern here: we will iterate over both strings at once (using <code class="docutils literal">zip</code>), do some math based on the character’s codes (<code class="docutils literal">ord</code>), and then check if <code class="docutils literal">all</code> results (of a comparison, usually) are truthy.</p>
<p>Here’s the bytecode with value annotations and comments, which explain what happens where:</p>
<div class="code"><pre class="code text"><a id="rest_code_600c535191904a66bf231f1cfe9efc77-1" name="rest_code_600c535191904a66bf231f1cfe9efc77-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-1"></a>&gt;&gt;   43 LOAD_GLOBAL              3 (all)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-2" name="rest_code_600c535191904a66bf231f1cfe9efc77-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-2"></a>     46 BUILD_LIST               0
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-3" name="rest_code_600c535191904a66bf231f1cfe9efc77-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-3"></a>     49 LOAD_GLOBAL              4 (zip)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-4" name="rest_code_600c535191904a66bf231f1cfe9efc77-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-4"></a>     52 LOAD_FAST                0 (s)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-5" name="rest_code_600c535191904a66bf231f1cfe9efc77-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-5"></a>     55 LOAD_FAST                1 (good)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-6" name="rest_code_600c535191904a66bf231f1cfe9efc77-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-6"></a>     58 CALL_FUNCTION            2           # zip(s, good)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-7" name="rest_code_600c535191904a66bf231f1cfe9efc77-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-7"></a>     61 GET_ITER                             # Start iterating: iter()
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-8" name="rest_code_600c535191904a66bf231f1cfe9efc77-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-8"></a>&gt;&gt;   62 FOR_ITER                52 (to 117)  # for loop iteration start (if iterator exhausted, jump +52 bytes to position 117)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-9" name="rest_code_600c535191904a66bf231f1cfe9efc77-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-9"></a>     65 UNPACK_SEQUENCE          2           # unpack a sequence (a, b = sequence)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-10" name="rest_code_600c535191904a66bf231f1cfe9efc77-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-10"></a>     68 STORE_FAST               2 (cs)      # cs = item from s
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-11" name="rest_code_600c535191904a66bf231f1cfe9efc77-11" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-11"></a>     71 STORE_FAST               3 (cg)      # cg = item from good
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-12" name="rest_code_600c535191904a66bf231f1cfe9efc77-12" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-12"></a>     74 LOAD_GLOBAL              5 (ord)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-13" name="rest_code_600c535191904a66bf231f1cfe9efc77-13" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-13"></a>     77 LOAD_FAST                2 (cs)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-14" name="rest_code_600c535191904a66bf231f1cfe9efc77-14" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-14"></a>     80 CALL_FUNCTION            1           # put ord(cs) on stack
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-15" name="rest_code_600c535191904a66bf231f1cfe9efc77-15" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-15"></a>     83 LOAD_CONST               3 (89)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-16" name="rest_code_600c535191904a66bf231f1cfe9efc77-16" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-16"></a>     86 BINARY_SUBTRACT                      # - 89   [subtract 89 from topmost value]
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-17" name="rest_code_600c535191904a66bf231f1cfe9efc77-17" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-17"></a>     87 LOAD_CONST               4 (255)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-18" name="rest_code_600c535191904a66bf231f1cfe9efc77-18" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-18"></a>     90 BINARY_AND                           # &amp; 255  [bitwise AND with topmost value]
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-19" name="rest_code_600c535191904a66bf231f1cfe9efc77-19" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-19"></a>     91 LOAD_CONST               5 (115)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-20" name="rest_code_600c535191904a66bf231f1cfe9efc77-20" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-20"></a>     94 BINARY_XOR                           # ^ 115  [bitwise XOR with topmost value]
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-21" name="rest_code_600c535191904a66bf231f1cfe9efc77-21" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-21"></a>     95 LOAD_CONST               6 (50)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-22" name="rest_code_600c535191904a66bf231f1cfe9efc77-22" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-22"></a>     98 BINARY_XOR                           # ^ 50   [bitwise XOR with topmost value]
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-23" name="rest_code_600c535191904a66bf231f1cfe9efc77-23" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-23"></a>     99 LOAD_GLOBAL              5 (ord)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-24" name="rest_code_600c535191904a66bf231f1cfe9efc77-24" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-24"></a>    102 LOAD_FAST                3 (cg)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-25" name="rest_code_600c535191904a66bf231f1cfe9efc77-25" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-25"></a>    105 CALL_FUNCTION            1           # put ord(cs) on stack
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-26" name="rest_code_600c535191904a66bf231f1cfe9efc77-26" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-26"></a>    108 COMPARE_OP               2 (==)      # compare the two values on stack
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-27" name="rest_code_600c535191904a66bf231f1cfe9efc77-27" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-27"></a>    111 LIST_APPEND              2           # append topmost value to the list in topmost-1; pop topmost (append to list created in comprehension)
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-28" name="rest_code_600c535191904a66bf231f1cfe9efc77-28" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-28"></a>    114 JUMP_ABSOLUTE           62           # jump back to start of loop
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-29" name="rest_code_600c535191904a66bf231f1cfe9efc77-29" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-29"></a>&gt;&gt;  117 CALL_FUNCTION            1           # after loop: call all([list comprehension result])
<a id="rest_code_600c535191904a66bf231f1cfe9efc77-30" name="rest_code_600c535191904a66bf231f1cfe9efc77-30" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_600c535191904a66bf231f1cfe9efc77-30"></a>    120 RETURN_VALUE                         # return value returned by all()
</pre></div>
<p>We can now write the full answer.</p>
<p><a class="reference external" href="link://listing/listings/gynvaels-mission-11-en/mission11.py">listings/gynvaels-mission-11-en/mission11.py</a>  <a class="reference external" href="link://listing_source/listings/gynvaels-mission-11-en/mission11.py">(Source)</a></p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_937a75fde167457d96c306bb9cde4520-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="rest_code_937a75fde167457d96c306bb9cde4520-1" name="rest_code_937a75fde167457d96c306bb9cde4520-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">check_password</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_937a75fde167457d96c306bb9cde4520-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="rest_code_937a75fde167457d96c306bb9cde4520-2" name="rest_code_937a75fde167457d96c306bb9cde4520-2"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">good</span> <span class="o">=</span> <span class="s1">&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_937a75fde167457d96c306bb9cde4520-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="rest_code_937a75fde167457d96c306bb9cde4520-3" name="rest_code_937a75fde167457d96c306bb9cde4520-3"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</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="nb">len</span><span class="p">(</span><span class="n">good</span><span class="p">):</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_937a75fde167457d96c306bb9cde4520-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="rest_code_937a75fde167457d96c306bb9cde4520-4" name="rest_code_937a75fde167457d96c306bb9cde4520-4"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kc">False</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_937a75fde167457d96c306bb9cde4520-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="rest_code_937a75fde167457d96c306bb9cde4520-5" name="rest_code_937a75fde167457d96c306bb9cde4520-5"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_937a75fde167457d96c306bb9cde4520-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="rest_code_937a75fde167457d96c306bb9cde4520-6" name="rest_code_937a75fde167457d96c306bb9cde4520-6"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="nb">all</span><span class="p">([</span><span class="nb">ord</span><span class="p">(</span><span class="n">cs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">89</span> <span class="o">&amp;</span> <span class="mi">255</span> <span class="o">^</span> <span class="mi">115</span> <span class="o">^</span> <span class="mi">50</span> <span class="o">==</span> <span class="nb">ord</span><span class="p">(</span><span class="n">cg</span><span class="p">)</span> <span class="k">for</span> <span class="n">cs</span><span class="p">,</span> <span class="n">cg</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">good</span><span class="p">)])</span>
</code></td></tr></table></div><p>In the end, our <code class="docutils literal">dis.dis()</code> output matches the mission text (except the removed values, but their IDs do match), our <code class="docutils literal">co_*</code> variables are all green, and we can get to work on solving the puzzle itself!</p>
<p><strong>Side note:</strong> this task uses a list comprehension. You might want to optimize it, remove the brackets, and end up with a generator expression. This would make the task harder, since would require working with the internal generator code object as well:</p>
<div class="code"><pre class="code text"><a id="rest_code_2486792360754ae283652134d687e43c-1" name="rest_code_2486792360754ae283652134d687e43c-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2486792360754ae283652134d687e43c-1"></a>co_consts (None, &#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;, &#39;hex&#39;, &lt;code object &lt;genexpr&gt; at 0x104a86c30, file &quot;mission11-genexpr.py&quot;, line 11&gt;)
<a id="rest_code_2486792360754ae283652134d687e43c-2" name="rest_code_2486792360754ae283652134d687e43c-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2486792360754ae283652134d687e43c-2"></a>
<a id="rest_code_2486792360754ae283652134d687e43c-3" name="rest_code_2486792360754ae283652134d687e43c-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2486792360754ae283652134d687e43c-3"></a>46 LOAD_CONST               3 (&lt;code object &lt;genexpr&gt; at 0x104a86c30, file &quot;mission11-genexpr.py&quot;, line 11&gt;)
</pre></div>
<p><code class="docutils literal">BINARY_*</code> and <code class="docutils literal">ord</code> disappeared from the new listing. You can see the <a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/mission11-genexpr.py.html">modified code</a> (which differs by two bytes) and <a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/mission11-genexpr.txt.html">solver output</a>.</p>
</section>
<section id="solving-the-real-puzzle">
<h1>Solving the real puzzle</h1>
<p>I solved the extra credit part of the puzzle. The <em>real</em> aim of the puzzle was to recover the password — the text for which <code class="docutils literal">check_password()</code> will return True.</p>
<p>This part is pretty boring. I built a dictionary, where I mapped every byte (0…255) to the result of the calculation done in the <code class="docutils literal">check_password()</code> function’s loop. Then I used that to recover the original text.</p>
<div class="code"><pre class="code python"><a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-1" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-1" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-1"></a><span class="n">pass_values</span> <span class="o">=</span> <span class="p">{}</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-2" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-2" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-2"></a><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">256</span><span class="p">):</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-3" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-3" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-3"></a>    <span class="n">result</span> <span class="o">=</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">89</span> <span class="o">&amp;</span> <span class="mi">255</span> <span class="o">^</span> <span class="mi">115</span> <span class="o">^</span> <span class="mi">50</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-4" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-4" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-4"></a>    <span class="n">pass_values</span><span class="p">[</span><span class="n">result</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-5" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-5" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-5"></a>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-6" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-6" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-6"></a><span class="n">good</span> <span class="o">=</span> <span class="s1">&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-7" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-7" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-7"></a><span class="n">password</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-8" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-8" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-8"></a><span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">good</span><span class="p">:</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-9" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-9" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-9"></a>    <span class="n">password</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">pass_values</span><span class="p">[</span><span class="nb">ord</span><span class="p">(</span><span class="n">c</span><span class="p">)])</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-10" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-10" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-10"></a>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-11" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-11" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-11"></a><span class="nb">print</span><span class="p">(</span><span class="n">password</span><span class="p">)</span>
<a id="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-12" name="rest_code_0f5c1acbabcc4365a8eccce0dc13f132-12" href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_0f5c1acbabcc4365a8eccce0dc13f132-12"></a><span class="nb">print</span><span class="p">(</span><span class="n">check_password</span><span class="p">(</span><span class="n">password</span><span class="p">))</span>
</pre></div>
<p><strong>The password is:</strong> <code class="docutils literal">huh, that actually worked!</code>.</p>
</section>
<section id="what-was-that-paint-thing-about">
<h1>What was that Paint thing about?</h1>
<blockquote>Yesterday’s mission was about Elvish — <strong>I mean Paint</strong> — I mean Python programming.<footer>yours truly in this post’s teaser</footer></blockquote><p>Most of my readers were probably puzzled by the mention of Paint. Long-time viewers of Gynvael’s streams in Polish remember the Python 101 video he posted on April Fools last year. See <a class="reference external" href="https://www.youtube.com/watch?v=7VJaprmuHcw">original video</a>, <a class="reference external" href="http://gynvael.coldwind.pl/?id=599">explanation</a>, <a class="reference external" href="https://github.com/gynvael/stream/tree/master/007-python-101">code</a> (video and explanation are both Polish; you can get the gist of the video without hearing the audio commentary though.) <strong>Spoilers ahead.</strong></p>
<p>In that prank, Gynvael taught Python basics. The first part concerned itself with writing bytecode by hand. The second part (starts around 12:00) was about drawing custom Python modules. In Paint. Yes, Paint, the simple graphics program included with Microsoft Windows. He drew a custom Python module in Paint, and saved it using the BMP format. It looked like this (zoomed PNG below; <a class="reference external" href="https://chriswarrick.com/pub/gynvaels-mission-11-en/gynmod.bmp">download gynmod.bmp</a>):</p>
<img alt="/images/gynvaels-mission-11-en/gynmod-zoom.png" class="align-center" src="https://chriswarrick.com/images/gynvaels-mission-11-en/gynmod-zoom.png">
<p>How was this done? There are three things that come into play:</p>
<ul class="simple">
<li><p>Python can import modules from a ZIP file (if it’s appended to sys.path). Some tools that produce <code class="docutils literal">.exe</code> files of Python code use this technique; the old <code class="docutils literal">.egg</code> file format also used ZIPs this way.</p></li>
<li><p>BMP files have their header at the start of a file.</p></li>
<li><p>ZIP files have their header at the end of a file.</p></li>
<li><p>Thus, one file can be a valid BMP and ZIP at the same time.</p></li>
</ul>
<p>I took the code of <code class="docutils literal">check_password</code> and put it in <code class="docutils literal">mission11.py</code> (which I already cited above). Then I compiled to <code class="docutils literal">.pyc</code> and created a <code class="docutils literal">.zip</code> out of it.</p>
<p><a class="reference external" href="link://listing/listings/gynvaels-mission-11-en/mission11.py">listings/gynvaels-mission-11-en/mission11.py</a>  <a class="reference external" href="link://listing_source/listings/gynvaels-mission-11-en/mission11.py">(Source)</a></p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_bec3e104bb384b61922373724d0100a2-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="rest_code_bec3e104bb384b61922373724d0100a2-1" name="rest_code_bec3e104bb384b61922373724d0100a2-1"></a><span class="k">def</span><span class="w"> </span><span class="nf">check_password</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_bec3e104bb384b61922373724d0100a2-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="rest_code_bec3e104bb384b61922373724d0100a2-2" name="rest_code_bec3e104bb384b61922373724d0100a2-2"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">good</span> <span class="o">=</span> <span class="s1">&#39;4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89&#39;</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;hex&#39;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_bec3e104bb384b61922373724d0100a2-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="rest_code_bec3e104bb384b61922373724d0100a2-3" name="rest_code_bec3e104bb384b61922373724d0100a2-3"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</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="nb">len</span><span class="p">(</span><span class="n">good</span><span class="p">):</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_bec3e104bb384b61922373724d0100a2-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="rest_code_bec3e104bb384b61922373724d0100a2-4" name="rest_code_bec3e104bb384b61922373724d0100a2-4"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="kc">False</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_bec3e104bb384b61922373724d0100a2-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="rest_code_bec3e104bb384b61922373724d0100a2-5" name="rest_code_bec3e104bb384b61922373724d0100a2-5"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_bec3e104bb384b61922373724d0100a2-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="rest_code_bec3e104bb384b61922373724d0100a2-6" name="rest_code_bec3e104bb384b61922373724d0100a2-6"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="nb">all</span><span class="p">([</span><span class="nb">ord</span><span class="p">(</span><span class="n">cs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">89</span> <span class="o">&amp;</span> <span class="mi">255</span> <span class="o">^</span> <span class="mi">115</span> <span class="o">^</span> <span class="mi">50</span> <span class="o">==</span> <span class="nb">ord</span><span class="p">(</span><span class="n">cg</span><span class="p">)</span> <span class="k">for</span> <span class="n">cs</span><span class="p">,</span> <span class="n">cg</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">good</span><span class="p">)])</span>
</code></td></tr></table></div><p>Since I’m not an expert in any of the formats, I booted my Windows virtual machine and blindly copied the <a class="reference external" href="http://gynvael.coldwind.pl/img/secapr16_3.png">parameters used by Gynvael</a> to open the ZIP file (renamed <code class="docutils literal">.raw</code>) in IrfanView and saved as <code class="docutils literal">.bmp</code>. I changed the size to 83×2, because my ZIP file was 498 bytes long (3 BPP * 83 px * 2 px = 498 bytes) — by doing that, and through sheer luck with the size, I could avoid adding comments and editing the ZIP archive. I ended up with this (PNG again; <a class="reference external" href="https://chriswarrick.com/pub/gynvaels-mission-11-en/mission11.bmp">download mission11.bmp</a>):</p>
<img alt="/images/gynvaels-mission-11-en/mission11-zoom.png" class="align-center" src="https://chriswarrick.com/images/gynvaels-mission-11-en/mission11-zoom.png">
<p>The <code class="docutils literal">.bmp</code> file is runnable! We can use this code:</p>
<p><a class="reference external" href="link://listing/listings/gynvaels-mission-11-en/ziprunner.py">listings/gynvaels-mission-11-en/ziprunner.py</a>  <a class="reference external" href="link://listing_source/listings/gynvaels-mission-11-en/ziprunner.py">(Source)</a></p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-1" name="rest_code_2c416583ffef4a72a08094db61f84464-1"></a><span class="ch">#!/usr/bin/env python2</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-2" name="rest_code_2c416583ffef4a72a08094db61f84464-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-3" name="rest_code_2c416583ffef4a72a08094db61f84464-3"></a><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-4" name="rest_code_2c416583ffef4a72a08094db61f84464-4"></a><span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&quot;mission11.bmp&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-5" name="rest_code_2c416583ffef4a72a08094db61f84464-5"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-6" name="rest_code_2c416583ffef4a72a08094db61f84464-6"></a><span class="kn">import</span><span class="w"> </span><span class="nn">mission11</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2017/08/03/gynvaels-mission-11-en-python-bytecode-reverse-engineering/#rest_code_2c416583ffef4a72a08094db61f84464-7"><code data-line-number="7"></code></a></td><td class="code"><code><a id="rest_code_2c416583ffef4a72a08094db61f84464-7" name="rest_code_2c416583ffef4a72a08094db61f84464-7"></a><span class="nb">print</span> <span class="s2">&quot;Result:&quot;</span><span class="p">,</span> <span class="n">mission11</span><span class="o">.</span><span class="n">check_password</span><span class="p">(</span><span class="s1">&#39;huh, that actually worked!&#39;</span><span class="p">)</span>
</code></td></tr></table></div><p>And we get this:</p>
<img alt="/images/gynvaels-mission-11-en/running-bmp.png" class="align-center" src="https://chriswarrick.com/images/gynvaels-mission-11-en/running-bmp.png">
</section>
<section id="resources">
<h1>Resources</h1>
<ul class="simple">
<li><p><a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/mission11-solver.py.html">mission11-solver.py (full solver code)</a></p></li>
<li><p><a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/mission11-genexpr.py.html">mission11-genexpr.py</a>, <a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/mission11-genexpr.txt.html">mission11-genexpr.txt</a> (used for side note regarding generator expressions vs. list comprehensions)</p></li>
<li><p><a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/mission11.py.html">mission11.py code, used in BMP file</a></p></li>
<li><p><a class="reference external" href="https://chriswarrick.com/listings/gynvaels-mission-11-en/ziprunner.py.html">ziprunner.py, file that runs the BMP/ZIP module</a> (adapted from Gynvael’s)</p></li>
<li><p><a class="reference external" href="https://chriswarrick.com/pub/gynvaels-mission-11-en/gynmod.bmp">gynmod.bmp</a></p></li>
<li><p><a class="reference external" href="https://chriswarrick.com/pub/gynvaels-mission-11-en/mission11.bmp">mission11.bmp</a></p></li>
<li><p><a class="reference external" href="https://docs.python.org/2/library/dis.html#python-bytecode-instructions">dis module documentation</a>.</p></li>
</ul>
<p>Thanks for the mission (and BMP idea), Gynvael!</p>
</section>
]]></content:encoded><category>Python</category><category>BMP</category><category>Gynvael Coldwind</category><category>hacking</category><category>Paint</category><category>Python</category><category>Python hackery</category><category>Python internals</category><category>reverse engineering</category><category>writeup</category></item></channel></rss>