openfh.py is a python script that can be used as a PyCommand within Immunity Debugger to open a file handle under the debugged process. Just place it within the PyCommands directory and type !openfh <path_to_file> in the command box to run it. The new file handle will be shown in the message box at the bottom of the window and additional details will be written to the Log window (Alt + L). Registers and CPU status flags should all be preserved.

Background

It is very common to see malicious documents (pdf, doc, xls, etc.) containing an exploit with shellcode that drops malicious files and executes/opens them. I’ve had multiple cases where I didn’t have a vulnerable version of the software or some other factor prevented the malware from successfully dropping its payload and the embedded content was obfuscated so I couldn’t just copy it out.

Once the malicious shellcode was identified, I used pelib to dump it into a free-standing executable in order to disassemble/debug it. In a few cases, the shellcode used a loop to cycle through all possible file handles available to the running process looking for the malicious dropper document (for file I/O).

During normal exploitation, the shellcode will be executing within Acrobat, Word, Excel, etc. so there should always be an open file handle associated with the document; however, in our extracted shellcode executable, there isn’t.

Immunity Debugger has a built in openTextFile() function but it opens a file under the debugger process and not the debugged process. The first couple of times, I compiled a barebones C program that called fopen() on the malicious document right before executing the shellcode. This worked, but it required additional effort every time. openfh.py was written to be a longer term solution where it isn’t necessary to compile anything in order to open up a file handle.

How it works

openfh.py locates a code cave (sequence of 0x00s that is presumed to be unused) within the debugged process that is big enough to write some custom code to. Appropriate offsets for Call and Jmp instructions are calculated and used to assemble the API call to kernel32.CreateFileA which is written to the code cave. Program execution is diverted to our custom code and we run through until we Jmp back to where we started.

Download

edit: This script is now hosted on GitHub.