MindshaRE is our weekly look at some simple reverse engineering tips and tricks. The goal is to keep things small and discuss every day aspects of reversing. You can view previous entries here by going through our blog history.
If you aren't familiar with PyDbg you need to read this, this, this, and this. PyDbg, which is included in PaiMei, allows the user to create fully functional debugging scripts in Python.
When we combine the flexibility of PyDbg with the comprehensiveness of IDAPython there really is no limit to what you can do. In today's example I am going to show you how to get started with the very basics. Our goal will be to set a breakpoint based on the current location in IDA, launch or attach to the executable being disassembled, and dump its register state when the breakpoint is hit.
First we are going to gather all the information we need from IDA. This will include the current address, and name of the executable being disassembled.
# IDC Functions
ea = ScreenEA()
process = GetInputFile()The ScreenEA() function returns the current address that your cursor is at in the IDA window. GetInputFile() returns the name of the file being disassembled including its extension. Now let's look at a few of the PyDbg methods.
# PyDbg Functions
dbg = pydbg()
dbg.ida_ea = ea
dbg.process = process
dbg.set_callback(EXCEPTION_BREAKPOINT, breakpoint_handler)Here we have created an instance of the debugger, and set up a callback for breakpoints when they are encountered.
Next we simply load, or attach to the process.
def attach_target_proc(dbg, procname):
imagename = procname.rsplit('\\')[-1]
print "[*] Trying to attach to existing %s" % imagename
for (pid, name) in dbg.enumerate_processes():
if imagename in name.lower():
print "[*] Attaching to %s (%d)" % (name, pid)
print "[!] Problem attaching to %s" % name
print "[*] Trying to load %s" % (procname)
print "[!] Problem loading %s" % (procname)
if attach_target_proc(dbg, process):
print "[!] Couldn't load/attach to %s" % processThis piece of code will attach or load the process under the dbg instance we previously instantiated. Since we have set a breakpoint handler lets look at what that will do when hit.
# Initial module bp
# Set IDA BP
return DBG_CONTINUEHere we see the setting of our current location in IDA. Once the breakpoint is set, execution resumes in the target process until the bp is hit. When the breakpoint is hit we will execute the print_state() function. This function dumps our register state as you would see in WinDbg.
When we run this from IDA we can see the following output.
.text:01012314 mulnumx proc near
.text:01012314 arg_0 = dword ptr 8
.text:01012314 arg_4 = dword ptr 0Ch
.text:01012314 push ebp
.text:01012315 mov ebp, esp
.text:01012317 push esi
[*] Trying to attach to existing calc.exe
[*] Trying to load calc.exe
eax=00000000 ebx=00000020 ecx=000aa440 edx=0000000a esi=000aa410 edi=01014fe8
eip=01012314 esp=0007fd98 ebp=0007fdc0
As you can see this is a very handy way to call a process and dump state information at a location you are interested in. Even though this is a simple example it should help lay the foundation for more complex scripts you dream up. The full version of the above script can be found here: ida_pydbg.py.
calc.exe!01012314 push ebp
Before we wrap this up, there are a few things to keep in mind when running scripts from within IDA. IDA, and IDAPython are run under the same thread. This means if your script is blocking due to the debugger event loop, the IDA UI will also be blocking. Because of this you cannot run the debugger and work in IDA concurrently. This limitation will also prevent you from logging messages in the IDA window, or interacting with the IDAPython script. Try and limit script output until the end, and ensure you display it in a manner suitable for IDA's log window.
With all of this in mind next time you find yourself needing some debugger help, try and utilize the very powerful IDAPython and PyDbg libraries.