3

I am using Python to write a rudimentary debugger that will use Windows APIs to debug C source files. I am stuck on creating the software breakpoints. I did some research and found this article that explains setting software breakpoints involves setting 0xCC to a location.

original = ctypes.c_ubyte()
bytes_read = SIZE_T()
handle = OpenProcess(PROCESS_ALL_ACCESS, False, self.process_id)

# Read original byte
ReadProcessMemory(handle, address, ctypes.byref(original), 1, ctypes.byref(bytes_read))

# Write INT3
int3 = ctypes.c_ubyte(0xCC)
bytes_written = SIZE_T()
WriteProcessMemory(handle, address, ctypes.byref(int3), 1, ctypes.byref(bytes_written))

self.breakpoints[address] = original.value
CloseHandle(handle)

Getting the address using comtypes that uses the MSDIA SDK to parse a PDB file:

source_files = self.session.findFile(None, r"/path/to/source/file.c", 0x2 | 0x4)
source_file = source_files.Item(0)
line_numbers = self.session.findLinesByLinenum(source_file.compilands[0], source_file, line, 0)
line_number = line_numbers.Next(1)[0]
address = self.base_address + line_number.addressOffset  # + line_number.virtualAddress

Logic to handle software breakpoints:

def handle_software_breakpoint(self, thread_id, exception_address):
    """Handle a software breakpoint hit (INT3)"""
    if exception_address not in self.breakpoints:
        print("[!] Breakpoint hit at unknown address 0x%X" % exception_address)
        return

    # Fix instruction pointer to re-execute original instruction
    context = CONTEXT32()
    context.ContextFlags = CONTEXT_ALL
    thread_handle = OpenThread(THREAD_ALL_ACCESS, False, thread_id)
    GetThreadContext(thread_handle, ctypes.byref(context))
    context.Eip -= 1  # Rewind past INT3
    SetThreadContext(thread_handle, ctypes.byref(context))
    CloseHandle(thread_handle)

    original_byte = self.breakpoints[exception_address]
    handle = OpenProcess(PROCESS_ALL_ACCESS, False, self.process_id)
    # Restore original instruction byte
    orig = ctypes.c_ubyte(original_byte)
    size = SIZE_T()
    WriteProcessMemory(handle, exception_address, ctypes.byref(orig), 1, ctypes.byref(size))
    CloseHandle(handle)

    print("[*] Software breakpoint handled at 0x%X" % exception_address)

However the only breakpoint that is handled is at address 0x77BF1B52. How can I ensure that the breakpoints are reached? Does one have to account for comments when reading memory addresses from a line in source code?
Python/C/C++ solutions are acceptable.

Below is the debug loop:

def run_debug_loop(self):
    print("[+] Starting debug loop... waiting for events.")
    debug_event = DEBUG_EVENT()

    while WaitForDebugEvent(ctypes.byref(debug_event), INFINITE):
        code = debug_event.dwDebugEventCode
        pid = debug_event.dwProcessId
        tid = debug_event.dwThreadId

        if code == CREATE_PROCESS_DEBUG_EVENT:
            print("[+] Process created, setting breakpoints...")
            # Example: set a breakpoint at main()
            self.set_software_breakpoint(self.io_addresses["B"])
            ResumeThread(self.thread_handle)

        elif code == EXCEPTION_DEBUG_EVENT:
            record = debug_event.u.Exception.ExceptionRecord
            exc_code = record.ExceptionCode
            addr = record.ExceptionAddress

            if exc_code == EXCEPTION_BREAKPOINT:
                self.handle_software_breakpoint(tid, addr)
            elif exc_code == EXCEPTION_SINGLE_STEP:
                print("[!] Unexpected single-step (from restored breakpoint)")

        elif code == 5:  # EXIT_PROCESS_DEBUG_EVENT
            print("[+] Process exited.")
            break

        ContinueDebugEvent(pid, tid, DBG_CONTINUE)
10
  • 5
    Why do you tag C++ here? As far as I can see your question is about "C" and "Python", and C++ has nothing to do with it Commented Oct 30 at 16:20
  • 2
    The WIndows API is pure "C" (and sometimes hell to use from C++ regarding const correctness and other things). All in all C++ is a completly different language with its own way of working and (some) backward compatibility with "C".. And as far as debugging goes, you don't want to go into C++ name mangling land if you only want to debug "C" Commented Oct 30 at 16:28
  • 3
    @PepijnKramer "All in all C++ is a completly different language" - C and C++ are not "completely different" programming languages. They share so much (vast overlap in syntax and semantics, memory model, build model [ignoring C++ modules], ...). Tagging questions with both c and c++ is fine until a dogmatist enters the scene. Commented Oct 30 at 16:57
  • 1
    The question has no C++ content, and is much more likely to get useful expertise from the winapi tag. Commented Oct 30 at 17:25
  • 1
    @IInspectable Then consider me a dogmatist :) But I am not the only one Is C/C++ one language or two languages? Commented Oct 30 at 17:30

1 Answer 1

3

What seems to currently work is:

address = self.base_address + line_number.virtualAddress

Here I am using virtualAddress instead of addressOffset.

Sign up to request clarification or add additional context in comments.

1 Comment

Makes sense - as the other one is most likely an file offset.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.