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.

0xec
  • 6,090
  • 3
  • 23
  • 33
ThatOneGuy
  • 111
  • 2
  • 6
  • 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? – Prince Frizzy Sep 14 '15 at 20:38

1 Answers1

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.

0xec
  • 6,090
  • 3
  • 23
  • 33
  • 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 Jul 15 '14 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 Jul 15 '14 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 http://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 Jul 15 '14 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 Jul 15 '14 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 Jul 15 '14 at 17:17
  • @ThatOneGuy You also need to have some experience using Ollydbg before you can understand all the steps fully. – 0xec Jul 15 '14 at 17:21
  • The memory dump window, also known as Memory Map correct? – ThatOneGuy Jul 15 '14 at 17:34
  • @ThatOneGuy Nope, Memory map is different. See *this* – 0xec Jul 15 '14 at 18:09
  • Do you mind if i send you a copy of this program and you take a look at it? I'm dead confused. I've used ollydbg before, but i just can't seem to cope with your instructions. – ThatOneGuy Jul 15 '14 at 18:36
  • @ThatOneGuy Fine, Post a link of the program, and I will take a look later. – 0xec Jul 15 '14 at 18:47
  • http://www.mediafire.com/download/g228iu7ezweehj8/PiratesOnline.zip the .exe is "Pirates_Online.exe" the rest are just necessities to run the program. panda24.dll i believe is what PyImport_FrozenModules is coming from – ThatOneGuy Jul 15 '14 at 19:32
  • Also, in the memory dump window, i am saving data to file and before i save it, i change the extension to ".pyc". Changed the magic number, and it didn't really out put anything after decompiling considering the file size is around 4kb. I guess i am dumping the wrong thing. – ThatOneGuy Jul 16 '14 at 01:05
  • There is already a reversing guide on the said program. Find it *here. Also see this* post – 0xec Jul 16 '14 at 02:26
  • Anti-freeze is no where to be found on the internet. – ThatOneGuy Jul 16 '14 at 02:49
  • have you tried your own steps on this program? – ThatOneGuy Jul 16 '14 at 03:15
  • If anyone has any ideas, let me know! I'd really like this. – ThatOneGuy Jul 16 '14 at 03:55
  • Any idea's @Extreme Coders ? – ThatOneGuy Jul 16 '14 at 15:24
  • I guess no one knows how to do this :( – ThatOneGuy Jul 17 '14 at 01:11
  • @ThatOneGuy I am writing a ImmDbg script for automating the task. – 0xec Jul 17 '14 at 04:34
  • @ThatOneGuy Updated Answer. – 0xec Jul 17 '14 at 07:21
  • @ThatOneGuy Welcome. If the answer solved your problem, please mark it as answered. – 0xec Jul 17 '14 at 16:37
  • One simple issue, the decompiling doesn't work. it says "Invalid .pyc/pyo" (after getting all the .pyc's and trying to decompile them) – ThatOneGuy Jul 17 '14 at 16:58
  • @ThatOneGuy How many files were dumped ? What was the version of python the game was made with ? If is different from Python 2.7 you have to change the PYTHONMAGIC variable appropriately. Also try to decompile the files one by one than the whole bunch at once. – 0xec Jul 17 '14 at 17:01
  • It was made in Python 2.4 – ThatOneGuy Jul 17 '14 at 17:04
  • @ThatOneGuy You may also upload a .pyc file here, to check whether it is really invalid. – 0xec Jul 17 '14 at 17:05
  • http://www.mediafire.com/download/q8ldkcyecrbm2rg/pirates.battle.EnemyGlobals.pyc – ThatOneGuy Jul 17 '14 at 17:06
  • @ThatOneGuy Well, then change PYTHONMAGIC to \x6D\xF2\x0D\x0A\x00\x00\x00\x00 – 0xec Jul 17 '14 at 17:08
  • http://gyazo.com/d4ab515ef3d4cc97f21c3db814c1fea1 Does that look right? – ThatOneGuy Jul 17 '14 at 17:11
  • @ThatOneGuy That's right. Now again run the script & decompile. It should be successful. – 0xec Jul 17 '14 at 17:12
  • @ThatOneGuy Here is the decompiled version of the above pyc file. http://www.datafilehost.com/d/edce8086 – 0xec Jul 17 '14 at 17:47
  • Now after changing that, nothing dumps. it says it finished dumping, but nothing is in the folder – ThatOneGuy Jul 17 '14 at 19:29
  • @ThatOneGuy The script should be identical at the previous one. Only change should be PYTHONMAGIC = '\x6D\xF2\x0D\x0A\x00\x00\x00\x00'. You must have made some mistake, either in running or finding the location where it is dumped. – 0xec Jul 18 '14 at 03:36
  • 97% of everything decompiled perfectly. More or less, about 3-8 failed. It would crash the decompiler. What is my route from here to getting those left-over .pyc's decompiled? – ThatOneGuy Jul 19 '14 at 01:47
  • Thanks for everything! It is all done. Is there any way i can get this question Deleted? – ThatOneGuy Jul 19 '14 at 06:27
  • @ThatOneGuy You shouldn't delete the question. It would be helpful to future users. However if you want, you can remove the links to the files you uploaded (in comments). – 0xec Jul 19 '14 at 06:30
  • Alright man! Thanks for everything you've done! – ThatOneGuy Jul 19 '14 at 06:33
  • Do you know any python 2.4 decompilers? – ThatOneGuy Jul 21 '14 at 08:11