2

(Note)I have already seen this post: Reverse Engineering program written in Python, compiled with "freeze"

Would anyone know how to do what is done in the link above using the PyCommand but with a mac app? The program I am trying to reverse engineer is compiled using the exact same program but the binary is a mac application.

Unfortunately Immunity Debugger is only able to be run on windows, so I cannot use the PyCommand (to my knowledge). Any help is appreciated, thank you.

  • The answer on the page to which you linked gives the exact steps necessary to dump the module. Just replace "Ollydbg" in Step 1 with "gdb" or similar. – Jason Geffner May 21 '15 at 22:15
  • Would I have to do that for each module? There are over 2,000 modules in the program I am wanting to decompile. – Michael Wass May 22 '15 at 2:38
2

As per the linked post you can use any debugger to dump frozen modules compiled with freeze. However since you are on a mac, PyCommand and Immunity Debugger will not work. However that does not prevent you from working your ways in.

On a non Windows OS, you can use frida to inject your code inside the running executable. Since frida injects javascript inside the process, you need to translate the python code to js.

You only need to dump relevant frozen modules, and it would happen automatically as _frozen is an array of all frozen modules.


UPDATE

Here is a script to dump frozen modules using the python bindings for frida. The script is fairly portable and should work on all OS. You only need to provide the PID of the relevant process which you can get from Activity Monitor / System Monitor / Task Manager for your OS.

import frida, struct, sys

# Magic value of pyc files, The value provided here is for python 2.7
# You can get it by imp.get_magic()
PYTHONMAGIC = '\x03\xF3\x0D\x0A' + '\x00' * 4

# Provide the pid of your process
PID = 2008

# The size of a pointer is 8 bytes on 64 bit OS and 4 on a 32 bit OS
ptr_size = 8 if sys.maxsize > 2**32 else 4

# Name of python shared library
if sys.platform.startswith('linux'):
    lib_name = 'libpython2.7.so'
elif sys.platform.startswith('darwin'):
    lib_name = 'libpython2.7.dylib'
elif sys.platform.startswith('win32'):
    lib_name = 'python27.dll'
else:
    raise Exception('Unsupported platform')


session = frida.attach(PID)
export_va = 0

# Read a null terminated C string or a char array of a given length
def readString(addr, size = 0):
    if size > 0:
        return struct.unpack('@{}s'.format(size) , session.read_bytes(addr, size))[0]
    elif size == 0:
        s = ''
        while True:
            ch, = struct.unpack('@c', session.read_bytes(addr, 1))
            addr += 1
            if ch == '\x00':
                break
            else:
                s += ch
        return s
    else:
        return ''


modules = session.enumerate_modules()
for module in modules:
    if module.name == lib_name:
        exports = module.enumerate_exports()
        for export in exports:
            if export.name == 'PyImport_FrozenModules':
                export_va = module.base_address + export.relative_address
                break
    if export_va != 0:
        break


if export_va == 0:
    raise Exception("Could not get export address of PyImport_FrozenModules")

structAddr, = struct.unpack_from('@P', session.read_bytes(export_va , ptr_size))

while True:
    ptrToName, ptrToCode, sizeOfCode = struct.unpack_from('@PPi', session.read_bytes(structAddr, ptr_size * 2 + 4))
    structAddr += ptr_size * 2 + 4

    # The array is terminated by a structure whose members are null
    if ptrToName == 0 and ptrToCode == 0 and sizeOfCode == 0:
        break

    moduleName = readString(ptrToName)
    moduleCode = readString(ptrToCode, sizeOfCode)

    # You can change the output path here
    with open(moduleName + '.pyc', 'wb') as f:
        f.write(PYTHONMAGIC + moduleCode)

print '[*] Frozen modules dumped'
  • For some reason the script is not working. It is saying it cannot find PyImport_FrozenModules. I tried it out on the PC executable compiled with freeze and got the same error. Here is a download for the binary on the mac, could you try it and see if it works for you: dropbox.com/s/w7qpgm1hnwk5j9j/Archive.zip?dl=0 – Michael Wass May 26 '15 at 0:12
  • Update: I was able to get the script working on the PC. The issue is with the lib_name. On the mac the python dylib is embedded inside of the app itself. How would I be able to call that? – Michael Wass May 26 '15 at 1:02
  • @MichaelWass The lib_name depends on the version of python used. For PyImport_FrozenModules, you need to find out which module exports it and accordingly change lib_name – 0xec May 26 '15 at 5:17
  • Would it matter if it is the actual library being used or not? Or just any old lib from python 2.4? – Michael Wass May 28 '15 at 4:17
  • @MichaelWass lib_name must be the actual library which is used by the application. – 0xec May 28 '15 at 8:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.