11

I'm having trouble switching from debugging one thread to another in pydbg. I don't have much experience with multithreading, so I'm hoping that I'm just missing something obvious.

Basically, I want to suspend all threads, then start single stepping in one thread. In my case, there are two threds.

First, I suspend all threads. Then, I set a breakpoint on the location where EIP will be when thread 2 is resumed. (This location is confirmed by using IDA). Then, I enable single-stepping as I would in any other context, and resume Thread 2.

However, pydbg doesn't seem to catch the breakpoint exception! Thread 2 seems to resume and even though it MUST hit that address, there is no indication that pydbg is catching the breakpoint exception. I included a "print "HIT BREAKPOINT" inside pydbg's internal breakpoint handler, and that never seems to be called after resuming Thread 2.

I'm not too sure about where to go next, so any suggestions are appreciated!

    dbg.suspend_all_threads()
    print dbg.enumerate_threads()[0]
    oldcontext = dbg.get_thread_context(thread_id=dbg.enumerate_threads()[0])
    if (dbg.disasm(oldcontext.Eip) == "ret"):
        print disasm_at(dbg,oldcontext.Eip)
        print "Thread EIP at a ret"
        addrstr = int("0x"+(dbg.read(oldcontext.Esp + 4,4))[::-1].encode("hex"),16)
        print hex(addrstr)
        dbg.bp_set(0x7C90D21A,handler=Thread_Start_bp_Handler)
        print dbg.read(0x7C90D21A,1).encode("hex")
    dbg.bp_set(oldcontext.Eip + dbg.instruction.length,handler=Thread_Start_bp_Handler)
    dbg.set_thread_context(oldcontext,thread_id=dbg.enumerate_threads()[0])
    dbg.context = oldcontext
    dbg.resume_thread(dbg.enumerate_threads()[0])
    dbg.single_step(enable=True)
    return DBG_CONTINUE

Sorry about the "magic numbers", but they are correct as far as I can tell.

1 Answer 1

4

One of your problems is that you try to single step through Thread2 and you only refer to Thread1 in your code:

dbg.enumerate_threads()[0] # <--- Return handle to the first thread.

In addition, the code the you posted is not reflective of the complete structure of your script, which makes it hard to judge wether you have other errors or not. You also try to set breakpoint within the sub-brach that disassembles your instructions, which does not make a lot of sense to me logically. Let me try to explain what I know, and lay it out in an organized manner. That way you might look back at your code, re-think it and correct it.

Let's start with basic framework of debugging an application with pydbg:

  1. Create debugger instance
  2. Attache to the process
  3. Set breakpoints
  4. Run it
  5. Breakpoint gets hit - handle it.

This is how it could look like:

from pydbg import *
from pydbg.defines import *

# This is maximum number of instructions we will log
MAX_INSTRUCTIONS = 20

# Address of the breakpoint
func_address = "0x7C90D21A"

# Create debugger instance
dbg = pydbg()

# PID to attach to
pid = int(raw_input("Enter PID: "))

# Attach to the process with debugger instance created earlier.
# Attaching the debugger will pause the process.
dbg.attach(pid)

# Let's set the breakpoint and handler as thread_step_setter,
# which we will define a little later...
dbg.bp_set(func_address, handler=thread_step_setter)

# Let's set our "personalized" handler for Single Step Exception
# It will get triggered if execution of a thread goes into single step mode.
dbg.set_callback(EXCEPTION_SINGLE_STEP, single_step_handler)

# Setup is done. Let's run it...
dbg.run() 

Now having the basic structure, let's define our personalized handlers for breakpoint and single stepping. The code snippet below defines our "custom" handlers. What will happen is when breakpoint hits we will iterate through threads and set them to single step mode. It will in turn trigger single step exception, which we will handle and disassemble MAX_INSTRUCTIONS amount of instructions:

def thread_step_setter(dbg):
    dbg.suspend_all_threads()
    for thread_id in dbg.enumerate_threads():
        print "Single step for thread: 0x%08x" % thread_id
        h_thread = dbg.open_thread(thread_id)
        dbg.single_step(True, h_thread)
        dbg.close_handle(h_thread)

    # Resume execution, which will pass control to step handler
    dbg.resume_all_threads()

    return DBG_CONTINUE

def single_step_handler(dbg):
    global total_instructions
    if instructions == MAX_INSTRUCTION:
        dbg.single_step(False)
        return DBG_CONTINUE
    else:
        # Disassemble the instruction
        current_instruction = dbg.disasm(dbg.context,Eip)
        print "#%d\t0x%08x : %s" % (total_instructions, dbg.context.Eip, current_instruction)
        total_instructions += 1
        dbg.single_step(True)

    return DBG_CONTINUE

Discloser: I do not guarantee that the code above will work if copied and pasted. I typed it out and haven't tested it. However, if basic understanding is acquired, the small syntactical error could be easily fixed. I apologize in advanced if I have any. I don't currently have means or time to test it.

I really hope it helps you out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.