10

I am attempting to reverse engineer an EXE compiled using the utility "Freeze" or "pfreeze" that ships with Python (not to get confused with "cx_freeze"). Inside the EXE there is an array of memory called PyImport_FrozenModules. When the program starts up that array gets populated with the programs bytecode(aka .pyo/.pyc files). I have been researching this for a few months now and i'm at a bit of a standstill. I need to find a way to extract the bytecode out of that array. The program originally connected to a server so once the EXE is executed, it will run for about 10-20 seconds before crashing. To prolong the 10-20 seconds I need to suspend the program(this is easily done with "Process Explorer")

What i need help on is actually dumping the PyImport_FrozenModules, so i can get the .pyc's and go on from there.

I've been able to get it to this : enter image description here
link : http://gyazo.com/83bcde5240df7fb9ec34e7e3de7699ba

by loading the exe in a debugger like ollydbg, finding the line of the import, and just setting them to NOP'S. Some people say i have to write a script for this, and others says it's easier than i think. I just have no clue where to start.

Any help is appreciated. Thanks.

1
  • I've learned you can easy find the functions in All Names. Also where, how, and when do you calculate a python magic number for a different version of python? Commented Sep 14, 2015 at 20:38

1 Answer 1

16

This is how you should proceed in such cases.

Step 1 : Load the executable in a debugger like Ollydbg.

Step 2 : In the memory dump window, navigate to PyImport_FrozenModules by pressing Ctrl + G

Step 3 : PyImport_FrozenModules is a pointer which is initialized to point to an array of struct _frozen records. Follow this pointer in dump.

Step 4 : Now you are at the position where there is an array of _frozen structures. This structure is defined as

struct _frozen {
    char *name;
    unsigned char *code;
    int size;
};

The above array is terminated by a structure whose all of its members are null.

Step 5 : In the above structure, the first member is the pointer to the name of the frozen module, the second member is a pointer to the bytecode of the module. (This is the thing which you are looking for). The last member will give you the size in bytes of the bytecode. Using the size, dump out the bytecode to a file.

Step 6 : The bytecode which you have just dumped out does not contain the magic header value for pyc files (for python 2.7 this is 03 F3 0D 0A, followed by a 4 byte timestamp) Add the header and now the file should be decompile-able.

To automate the above process, you could also write a script.


UPDATE

Here is a PyCommand ( a immunity debugger script) to dump the frozen modules. You need to run the script after all frozen modules are loaded. For this you may set a breakpoint on the function PyImport_ImportFrozenModule() so that you can track each frozen module as they are loaded. If you are new to Immunity Debugger see this and this

import immlib

DESC = 'PyCommand to dump frozen python modules'
PYTHONMAGIC = '\x03\xF3\x0D\x0A\x00\x00\x00\x00' # Change this value according to the version of python used. The value given here is for Python 2.7

'''
Run this pycommand when all frozen modules are loaded.
This will dump each frozen module in a .pyc file in 
immunity debugger installation directory
'''

def main(args):
    imm = immlib.Debugger()
    addr = imm.getAddress('PyImport_FrozenModules')
    structAddr = imm.readLong(addr)

    while True:
        ptrToName = imm.readLong(structAddr)
        ptrToCode = imm.readLong(structAddr + 4)
        sizeOfCode = imm.readLong(structAddr + 8)
        structAddr += 12

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

        if sizeOfCode > 0 and sizeOfCode < 2147483647:            
            moduleName = imm.readString(ptrToName)
            moduleCode = imm.readMemory(ptrToCode, sizeOfCode)

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

    return '[*] Frozen modules dumped'

After the frozen modules are dumped, use a decompiler on the resultant pyc files. You may use Easy Python Decompiler for that.

38
  • Thank you very much for the reply. I'm just stuck on getting to the array of "struct_frozen". Also, i'm not sure on what you mean when you said "dump out the bytecode to a file". Thanks.
    – ThatOneGuy
    Commented Jul 15, 2014 at 16:13
  • @ThatOneGuy I mean in Ollydbg memory dump window, select the range containing the bytecode, next right click -> Backup -> Save data to file. This way you can dump the bytecode.
    – 0xec
    Commented Jul 15, 2014 at 16:24
  • I'm getting stuck on this, my bad. Tell me if this is right: I'll find this line in the dissasembler gyazo.com/f9e8c1db5bd63b314658cc901792a184 Then, i right click and hit "follow in dump" and then i go on from there. Is that correct? I'm sorry about his, very new and nervous.
    – ThatOneGuy
    Commented Jul 15, 2014 at 16:43
  • For an example, i'll copy the byte from the dump after right clicking and hitting "Follow in dump" and only get Hex values like: 00401299 A1 C4 20 40 00
    – ThatOneGuy
    Commented Jul 15, 2014 at 17:08
  • @ThatOneGuy The image you have posted in the comment is not at all right. You need to follow PyImport_FrozenModules in the memory dump window
    – 0xec
    Commented Jul 15, 2014 at 17:17

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.