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'