Extended pdb debugger with functionality useful when reverse engineering Python .pyc/.pyo files without the .py source
For a list of integers and a given value find the list element it’s closest to
Make some best guesses on how to remap the opcodes from the currently running obfuscated runtime.
NOTE: If there is an exiting auto_remap project it will be overwritten
Usage: auto_remap <path to remapped pycs>
Try to determine the version of the running Python (the runtime we are injected into)
If there is a mismatch between the different ways we can do this you will be prompted to choose for yourself
Usage: detect_version
Download the specified Python runtime sourcecode from python.org and decompress it. It is saved to the ‘Downloaded_Runtimes’ subdir and shared between all projects
Usage: download_runtime 2.5.4
Decompile obfuscated bytecode by traversing the filesystem from a given start point but do NOT rely on the presence of the marshal module to be able to get thebytecode from the pyc file found. Instead each pyc found is imported and each of its objects are interogated for their bytecode. These bytecode objects are what is decompiled.
usage: fs_mem_decompile <path to obfuscated pyc’s> example: fs_mem_decompile /tmp/foo.app/Contents/Resources/runtime/site_packages/
Decompile obfuscated bytecode by traversing the filesystem from a given start point and using the current runtimes marshal module to unmarshal the bytecode of each .pyc found.
usage: fs_um_decompile <path to obfuscated pyc’s> example: fs_um_decompile /tmp/foo.app/Contents/Resources/runtime/site_packages/
Generate obfuscated Python bytecode for the modules at the path specified using the current runtime we are running from. The generated bytecode will be used to diff against the reference bytecode to deduce a modified opcode map. In general you should point this at the directory containing the obfuscated stdlib .pyc’s for the obfuscated runtime
The more commonality between the reference and obfuscated bytecode there the higher the number of opcodes that will be able to be remapped.
Usage: gen_obf <path to directory of obfusctaed python .pyc’s> Example: gen_obf /tmp/foo.app/Contents/Resources/runtime/site_packages/
Generate reference bytecode from the .py’s at the path specified using the Python runtime version already set. If not path is specified the relevant Python runtime will be used if it has already been downloaded with download_runtime.
The generated bytecode will be used to diff against the obfuscated bytecode to deduce a modified opcode map. The more commonality between the reference and obfuscated bytecode there the higher the number of opcodes that will be able to be remapped.
version as the reference source]
gen_ref <path to directory of reference python source code>
Example: gen_ref /tmp/python2.5.4/Lib
Get the reported version of the currently executing Python runtime Note: This may not be accurate, a runtime can be made to report any version.
Use only as an indicator when choosing a reference runtime to use.
usage: get_version
Determine if the current runtime has remapped its opcode table
Usage: is_remapped
For the supplied object, all of it’s methods/attributes etc are mirrored in the calling objects namespace This is a dirty way of acting as an object proxy meaning we can be injected in place of another object and be sure we won’t break the larger app
If no frame is specified then the frame from which the debugger was called is used If “debugger” is given as the frame the debugger frame is used
Usage: obj_mirror <instantiated object to mirror>
Decompile to source code purely from instantiated objects. This assumes no availability of the marshal module or even access to the filesystem where the pyc files reside, this decompiles directly from the currently executing runtimes namespace. The specified object is decompiled and the objects it contains are traversed and decompiled until no more remain.
[ Currently available objects can be seen by typing dir() at the REpdb prompt]
usage: pure_mem_decompile <name of object to decompile> example: pure_mem_decompile AnObjectsName
For the path specified do a recursive recompilation of all .py’s found using the current runtimes compiler (if available)
Usage: recompile <path to modules> Example: recompile /tmp/python_254/Libs
From the two sets of .pyb’s produced by gen_r2x and gen_o2x do the compares to work out the new opcode map. From this new opcode map create new files opcode.py (for the running stdlib) and opcodes.py (for UnPYC)
Note: the .pyb’s must already have been generated from the gen_xxx calls
Usage: remap
Restore the original opcode.py and opcodes.py module that was archived by swap_opcodes
Usage: restore_opcodes
Set an exclusion filter for the callgraph functionality, this defines modules/functions that are not included in the callgraph tracing
Argument is a comma seperated list of filter expresions
Usage: set_callgraph_exclude foo,bar*,blat
Create a new project or switch to an existing one
Usage: set_project <project name>
Reset the location of the standard Python 2.4 runtime used to generate reference bytecode
Usage: set_py24 /usr/local/bin/python2.4
Reset the location of the standard Python 2.5 runtime used to generate reference bytecode
Usage: set_py25 /usr/local/bin/python2.5
Reset the location of the standard Python 2.6 runtime used to generate reference bytecode
Usage: set_py26 /usr/local/bin/python2.6
Reset the location of the standard Python 2.7 runtime used to generate reference bytecode
Usage: set_py27 /usr/local/bin/python2.7
Set the version number of the runtime we are running in to a specific value
Usage: set_version 2.5.4
Leave the project name the same but change the root directory location on the filesystem. The currently set project is used as the project to relocate
Usage: set_project_root /tmp/pyretic_dump
Initialise a callgraph trace using pycallgraph After this is set use the pdb commands ‘n’, ‘c’ etc to step through the application being debugged and generate the callgraph
The callgraph can be stopped & written at anytime by ‘stop_callgraph’, if the debugging exits the callgraph is automatically stopped and written
Usage: start_callgraph <name of callgraph if you want non-default name>
Swap the remapped opcodes.py module for the original module in the UnPYC directory. Until this is done UnPYC will not be able to decompile correctly as it will be using the wrong opcode map. The opcodes.py file that will be used is the one that is located at the PROJECT_DIR/libs
Ssage: swap_modules
Determine whether a project of the given name already exists
Return boolean
For the current project has a re-mapped opcode table been generated, set var accordingly ?