How to extract Python source code from Py2App packed Mach-O Binaries

taha aka "lordx64"
4 min readMar 6, 2021
Photo by Raul Cacho Oses on Unsplash

I got many requests after my last tweet on the discovery of a backdoored Electrum wallet, that was notarized by Apple !

The requests were about how I was able to extract the python sourcecode from a Py2App packaged Mach-O file and discover the backdoor code.

Here’s the hash to use for this morning quick tutorial : 313efd27555f2d024e56d88a31ffda045e0e0d13db868867e416f0330e3ba773 electrum-4.0.9.dmg

Below are the steps I followed

Step 1

  • Mount the .dmg file, and locate the packed mach-o file we want to extract the source code from :
hdiutil mount electrum-4.0.9.dmg

Step 2

Sweet! now let’s locate the Mach-o run_electrum file, packed with py2App

For this wallet , it is usually under

Electrum/Electrum.app/Content/MacOS/run_electrum

step 3

Use Binwalk :)

Binwalk is the go to tool for firmware analysis :) but let’s keep it simple, and run it on this run_electrum mach-o file, you will notice a couple of zlib headers are detected and that’s where the python compiled code is hiding:

Sweet ! now we could use the same command, and we add the -e switch to Binwalk to automatically extract the zlibs for us. Binwalk will create a directory called _run_electrum.extracted, where we can find all the extracted python compiled code :

Step 4

Now that we extracted Python compile code, let’s try to find the original run_electrum.pyc file ?

grep -Rni --color "run_electrum" *

Gave us one and only hit the file D5E9 so we will rename this file to run_electrum.pyc for now:

This file looks like random data, but you will probably see it is a python compiled code :)

Only that it doesn’t contain a valid Python 3.7 header needed for decompyl3 to work.

Step 5

get decompyle3 at https://github.com/rocky/python-decompile3

if you run decompyle3 on this run_electrum.pyc it won’t work and will complain about the header as expected:

in this case let’s give decompyle3 a valid python 3.7 header :)

Step 6

Let’s patch the run_electrum.pyc header.

Note: Normally I should use Hiew to pay respect to the reverse engineers out there, but man it’s 7 am, and I am using my mac right now..

So I will use Hex Fiend, which is a GUI tool to patch binaries. let’s open run_electrum.pyc with Hex Fiend:

And add the following header :

420D0D0A 00000000 00000000 00000000

run decompyle3 again and see the magic happens:

This will create a run_electrum.py for you, you can now open it with your favorite text editor, and enjoy python:

Step 6 : bonus

Where to find the backdoor code ? you can repeat the steps above, on the compiled object 1D6CF file, this file is the python compiled object of the original file electrum/util.py where the backdoor was inserted. If you open this file after decompilation you will be greeted with free Firestore credentials, and the prefix_log() function that was inserted to exfiltrate the wallets and the wallet passwords ..

If you liked this quick tutorial or similar discoveries, please show me some love and follow me on Twitter at @lordx64

Have a great weekend !

--

--

taha aka "lordx64"

Malware reverse engineer & curiosity - twitter at @lordx64