During an investigation, the FLARE team came across an interesting
Python malware sample (MD5: class="code">61a9f80612d3f7566db5bdf37bbf22cf ) that is
packaged using href="http://www.py2exe.org">py2exe. Py2exe is a popular way to
compile and package Python scripts into executables. When we encounter
this type of malware we typically just decompile and read the Python
source code. However, this malware was different, it had its bytecode
manipulated to prevent it from being decompiled easily!
In this blog we’ll analyze the malware and show how we removed the
obfuscation, which allowed us to produce a clean decompile. Here we
release source code to our bytecode_graph module to help you analyze
obfuscated Python bytecode
(https://github.com/fireeye/flare-bytecode_graph). This module allows
you to remove instructions from a bytecode stream, refactor offsets
and generate a new code object that can be further analyzed.
Py2exe is a utility that turns a Python script into an executable,
which allows it to run on a system without a Python interpreter
installed. Analyzing a Py2exe binary is generally a straightforward
process that starts with extracting the Python bytecode followed by
decompiling the code object with a module such as
href="http://srossross.github.io/Meta/html/api/decompile.html">meta
or
href="https://github.com/wibiti/uncompyle2">uncompyle2.
Appendix A contains an example script that demonstrates how to extract
a code object from a Py2exe binary.
When attempting to decompile this sample using uncompyle2, the
exception shown in Figure 1 is generated. The exception suggests the
bytecode stream contains code sequences that the decompiler is not expecting.

Figure 1: Uncompyle2 exception trace
To understand why the decompiler is failing, we first need to take a
closer look at the bytecode disassembly. A simple method to
disassemble Python bytecode is to use the built-in module
href="https://docs.python.org/2/library/dis.html">dis. When
using the dis module, it is important to use
the same version of Python as the bytecode to get an accurate
disassembly. Figure 2 contains an example interactive session that
disassembles the script “import sys”. Each line in the disassembly
output contains an optional line number, followed by the bytecode
offset and finally the bytecode instruction mnemonic and any arguments.

Figure 2: