User Controls

plz help with python debugger

  1. #1
    So I am working on a little windows debugger from the book grey hat python. The code I have thus far is in three files as follow:


    #my_debugger_defines.py
    #This file is definitions and shit for the debugger

    from ctypes import *

    #Map of Microsoft types to ctypes
    WORD = c_ushort
    DWORD = c_ulong
    LPBYTE = POINTER(c_ubyte)
    LPTSTR = POINTER(c_char)
    HANDLE = c_void_p

    #constants
    DEBUG_PROCESS = 0x00000001
    CREATE_NEW_CONSOLE = 0x00000010

    #structures for CreateProcessA() function
    class STARTUPINFO(Structure):
    _feilds_ = [
    ("cb", DWORD),
    ("lpReserved", LPTSTR),
    ("lpDesktop", LPTSTR),
    ("lpTitle", LPTSTR),
    ("dwX", DWORD),
    ("dwY", DWORD),
    ("dwXSize", DWORD),
    ("dwYSize", DWORD),
    ("dwXCountChars", DWORD),
    ("dwYCountChars", DWORD),
    ("dwFillAttribute", DWORD),
    ("dwFlags", DWORD),
    ("wShowWindow", WORD),
    ("cbReserved2", WORD),
    ("lpReserved2", LPBYTE),
    ("hStdInput", HANDLE),
    ("hStdOutput", HANDLE),
    ("hStdError", HANDLE),
    ]

    class PROCESS_INFORMATION(Structure):
    _fields_ = [
    ("hProcess", HANDLE),
    ("hThread", HANDLE),
    ("dwProcessId", DWORD),
    ("dwThreadId", DWORD),
    ]



    #my_debugger.py
    #the code of the debugger

    from ctypes import *
    from my_debugger_defines import *

    kernel32 = windll.kernel32

    class debugger():

    def _init_(self):
    self.h_process = None
    self.pid = None
    self.debugger_active = False

    def load(self,path_to_exe):
    #dwCreation flag determines how to create the process
    #set creation_flags = CREATE_NEW_CONSOLE for calc GUI
    creation_flags = DEBUG_PROCESS

    #instantiate the structs
    startupinfo = STARTUPINFO()
    process_information = PROCESS_INFORMATION()

    #options allow started process to be shown as a seperate window
    startupinfo.dwFlags = 0x1
    startupinfo.wShowWindow = 0x0

    #initialize cb variable in STARTUPINFO struct
    startupinfo.cb = sizeof(startupinfo)

    if kernel32.CreateProcessA(path_to_exe,
    None,
    None,
    None,
    None,
    creation_flags,
    None,
    None,
    byref(startupinfo),
    byref(process_information)):

    print "
    [*] We have successfully launched the process!"
    print "
    [*] PID: %d" % process_information.dwProcessId
    # obtain and stove valid handle
    self.h_process = self.open_process(process_information.dwProcessId)
    else:
    print "
    [*] Error: 0x%08x." % kernel32.GetLastError()

    def open_process(self,pid):

    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS,pid,False)
    return h_process

    def attach(self,pid):

    self.h_process = self.open_process(pid)

    # Attempt to attach to process
    # if this fails we exit the call
    if kernel32.DebugActiveProcess(pid):
    self.debugger_active = True
    self.pid = int(pid)
    self.run()
    else:
    print "
    [*] Unable to attach to the process."

    def run(self):
    #poll debugee for degbugging events
    while self.debugger_active == True:
    self.get_debug_event()

    def get_debug_event(self):

    debug_event = DEBUG_EVENT()
    continue_status = DBG_CONTINUE

    if kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):

    raw_input("Press a key to continue..")
    self.debugger_active = False
    kernel32.ContinueDebugEvent( \
    debug_event.dwProcessId, \
    debug_event.dwThreadId, \
    continue_status )

    def detach(self):

    if kernel32.DebugActiveProcessStop(self.pid):
    print "
    [*] Finished debugging. Exiting..."
    return True
    else:
    print "There was an error"
    return False



    #my_test.py
    # code I run to test the debugger and its components
    import my_debugger

    debugger = my_debugger.debugger()

    #debugger.load("C:\\WINDOWS\\system32\\calc.exe")
    pid = raw_input("Enter the PID of the process to attach to: ")

    debugger.attach(int(pid))

    debugger.detach()


    So when I run the debugger test I get this:

    >>>
    Enter the PID of the process to attach to: 6564

    Traceback (most recent call last):
    File "C:\Users\The Toddster\Documents\py\my_test.py", line 8, in <module>
    debugger.attach(int(pid))
    File "C:\Users\The Toddster\Documents\py\my_debugger.py", line 54, in attach
    self.h_process = self.open_process(pid)
    File "C:\Users\The Toddster\Documents\py\my_debugger.py", line 49, in open_process
    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS,pid,False)
    NameError: global name 'PROCESS_ALL_ACCESS' is not defined

    So what I understand that the PROCESS_ALL_ACCESS is not defined or some shit but the code in the book is identical to that which I typed. I looked it over soooo many damn times but I dont understand why it wont work. The book doesnt have a definition of PROCESS_ALL_ACCESS so I am not sure what I need to do to define it.

    For reference the working code can be found here:
    https://drive.google.com/file/d/0B94WN9ZjhJVNN2hlUndkb1FNVkk/edit?pli=1

    The my_debugger_defines.py code is on page 27 and the my_test.py and my_debugger.py can be found on pages 31 and 32. You can also see some code for the debugger and test on page 28 and 29. I got this version of the program to work fine but upon expanding the code I get the above error. Anyway can someone help me out?
  2. #2
    Sophie Pedophile Tech Support
    None reproducible in my case. Also, i think PROCESS_ALL_ACCESS should be defined in the ctypes module. Do you have the required privilege to attach your debugger to the process you're trying to target?

    This is what the debugger scripts look like for me.



    # My debugger
    from ctypes import *
    from my_debugger_defines import *

    import sys
    import time
    kernel32 = windll.kernel32

    class debugger():

    def __init__(self):
    self.h_process = None
    self.pid = None
    self.debugger_active = False
    self.h_thread = None
    self.context = None
    self.breakpoints = {}
    self.first_breakpoint= True
    self.hardware_breakpoints = {}

    # Here let's determine and store
    # the default page size for the system
    # determine the system page size.
    system_info = SYSTEM_INFO()
    kernel32.GetSystemInfo(byref(system_info))
    self.page_size = system_info.dwPageSize

    # TODO: test
    self.guarded_pages = []
    self.memory_breakpoints = {}

    def load(self,path_to_exe):

    # dwCreation flag determines how to create the process
    # set creation_flags = CREATE_NEW_CONSOLE if you want
    # to see the calculator GUI
    creation_flags = DEBUG_PROCESS

    # instantiate the structs
    startupinfo = STARTUPINFO()
    process_information = PROCESS_INFORMATION()

    # The following two options allow the started process
    # to be shown as a separate window. This also illustrates
    # how different settings in the STARTUPINFO struct can affect
    # the debuggee.
    startupinfo.dwFlags = 0x1
    startupinfo.wShowWindow = 0x0

    # We then initialize the cb variable in the STARTUPINFO struct
    # which is just the size of the struct itself
    startupinfo.cb = sizeof(startupinfo)

    if kernel32.CreateProcessA(path_to_exe,
    None,
    None,
    None,
    None,
    creation_flags,
    None,
    None,
    byref(startupinfo),
    byref(process_information)):

    print "
    [*] We have successfully launched the process!"
    print "
    [*] The Process ID I have is: %d" % \
    process_information.dwProcessId
    self.pid = process_information.dwProcessId
    self.h_process = self.open_process(self,process_information.dwProcessId)
    self.debugger_active = True
    else:
    print "
    [*] Error with error code %d." % kernel32.GetLastError()

    def open_process(self,pid):

    # PROCESS_ALL_ACCESS = 0x0x001F0FFF
    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS,False,pid)

    return h_process

    def attach(self,pid):

    self.h_process = self.open_process(pid)

    # We attempt to attach to the process
    # if this fails we exit the call
    if kernel32.DebugActiveProcess(pid):
    self.debugger_active = True
    self.pid = int(pid)

    else:
    print "
    [*] Unable to attach to the process."

    def run(self):

    # Now we have to poll the debuggee for
    # debugging events
    while self.debugger_active == True:
    self.get_debug_event()

    def get_debug_event(self):

    debug_event = DEBUG_EVENT()
    continue_status = DBG_CONTINUE

    if kernel32.WaitForDebugEvent(byref(debug_event),100):
    # grab various information with regards to the current exception.
    self.h_thread = self.open_thread(debug_event.dwThreadId)
    self.context = self.get_thread_context(h_thread=self.h_thread)
    self.debug_event = debug_event


    print "Event Code: %d Thread ID: %d" % \
    (debug_event.dwDebugEventCode,debug_event.dwThreadId)

    if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
    self.exception = debug_event.u.Exception.ExceptionRecord.ExceptionCode
    self.exception_address = debug_event.u.Exception.ExceptionRecord.ExceptionAddress

    # call the internal handler for the exception event that just occured.
    if self.exception == EXCEPTION_ACCESS_VIOLATION:
    print "Access Violation Detected."
    elif self.exception == EXCEPTION_BREAKPOINT:
    continue_status = self.exception_handler_breakpoint()
    elif self.exception == EXCEPTION_GUARD_PAGE:
    print "Guard Page Access Detected."
    elif self.exception == EXCEPTION_SINGLE_STEP:
    self.exception_handler_single_step()

    kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)


    def detach(self):

    if kernel32.DebugActiveProcessStop(self.pid):
    print "
    [*] Finished debugging. Exiting..."
    return True
    else:
    print "There was an error"
    return False

    def open_thread (self, thread_id):

    h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)

    if h_thread is not None:
    return h_thread
    else:
    print "
    [*] Could not obtain a valid thread handle."
    return False

    def enumerate_threads(self):

    thread_entry = THREADENTRY32()
    thread_list = []
    snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)

    if snapshot is not None:

    # You have to set the size of the struct
    # or the call will fail
    thread_entry.dwSize = sizeof(thread_entry)

    success = kernel32.Thread32First(snapshot, byref(thread_entry))

    while success:
    if thread_entry.th32OwnerProcessID == self.pid:
    thread_list.append(thread_entry.th32ThreadID)

    success = kernel32.Thread32Next(snapshot, byref(thread_entry))

    # No need to explain this call, it closes handles
    # so that we don't leak them.
    kernel32.CloseHandle(snapshot)
    return thread_list
    else:
    return False

    def get_thread_context (self, thread_id=None,h_thread=None):

    context = CONTEXT()
    context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

    # Obtain a handle to the thread
    if h_thread is None:
    self.h_thread = self.open_thread(thread_id)

    if kernel32.GetThreadContext(self.h_thread, byref(context)):

    return context
    else:
    return False

    def read_process_memory(self,address,length):

    data = ""
    read_buf = create_string_buffer(length)
    count = c_ulong(0)


    kernel32.ReadProcessMemory(self.h_process, address, read_buf, 5, byref(count))
    data = read_buf.raw

    return data


    def write_process_memory(self,address,data):

    count = c_ulong(0)
    length = len(data)

    c_data = c_char_p(data[count.value:])

    if not kernel32.WriteProcessMemory(self.h_process, address, c_data, length, byref(count)):
    return False
    else:
    return True

    def bp_set(self,address):
    print "
    [*] Setting breakpoint at: 0x%08x" % address
    if not self.breakpoints.has_key(address):

    # store the original byte
    old_protect = c_ulong(0)
    kernel32.VirtualProtectEx(self.h_process, address, 1, PAGE_EXECUTE_READWRITE, byref(old_protect))

    original_byte = self.read_process_memory(address, 1)
    if original_byte != False:

    # write the INT3 opcode
    if self.write_process_memory(address, "\xCC"):

    # register the breakpoint in our internal list
    self.breakpoints[address] = (original_byte)
    return True
    else:
    return False


    def exception_handler_breakpoint(self):
    print "
    [*] Exception address: 0x%08x" % self.exception_address
    # check if the breakpoint is one that we set
    if not self.breakpoints.has_key(self.exception_address):

    # if it is the first Windows driven breakpoint
    # then let's just continue on
    if self.first_breakpoint == True:
    self.first_breakpoint = False
    print "
    [*] Hit the first breakpoint."
    return DBG_CONTINUE

    else:
    print "
    [*] Hit user defined breakpoint."
    # this is where we handle the breakpoints we set
    # first put the original byte back
    self.write_process_memory(self.exception_address, self.breakpoints[self.exception_address])

    # obtain a fresh context record, reset EIP back to the
    # original byte and then set the thread's context record
    # with the new EIP value
    self.context = self.get_thread_context(h_thread=self.h_thread)
    self.context.Eip -= 1

    kernel32.SetThreadContext(self.h_thread,byref(self.context))

    continue_status = DBG_CONTINUE


    return continue_status

    def func_resolve(self,dll,function):

    handle = kernel32.GetModuleHandleA(dll)
    address = kernel32.GetProcAddress(handle, function)

    kernel32.CloseHandle(handle)

    return address

    def bp_set_hw(self, address, length, condition):

    # Check for a valid length value
    if length not in (1, 2, 4):
    return False
    else:
    length -= 1

    # Check for a valid condition
    if condition not in (HW_ACCESS, HW_EXECUTE, HW_WRITE):
    return False

    # Check for available slots
    if not self.hardware_breakpoints.has_key(0):
    available = 0
    elif not self.hardware_breakpoints.has_key(1):
    available = 1
    elif not self.hardware_breakpoints.has_key(2):
    available = 2
    elif not self.hardware_breakpoints.has_key(3):
    available = 3
    else:
    return False

    # We want to set the debug register in every thread
    for thread_id in self.enumerate_threads():
    context = self.get_thread_context(thread_id=thread_id)

    # Enable the appropriate flag in the DR7
    # register to set the breakpoint
    context.Dr7 |= 1 << (available * 2)

    # Save the address of the breakpoint in the
    # free register that we found
    if available == 0: context.Dr0 = address
    elif available == 1: context.Dr1 = address
    elif available == 2: context.Dr2 = address
    elif available == 3: context.Dr3 = address

    # Set the breakpoint condition
    context.Dr7 |= condition << ((available * 4) + 16)

    # Set the length
    context.Dr7 |= length << ((available * 4) + 18)

    # Set this threads context with the debug registers
    # set
    h_thread = self.open_thread(thread_id)
    kernel32.SetThreadContext(h_thread,byref(context))

    # update the internal hardware breakpoint array at the used slot index.
    self.hardware_breakpoints[available] = (address,length,condition)

    return True

    def exception_handler_single_step(self):
    print "
    [*] Exception address: 0x%08x" % self.exception_address
    # Comment from PyDbg:
    # determine if this single step event occured in reaction to a hardware breakpoint and grab the hit breakpoint.
    # according to the Intel docs, we should be able to check for the BS flag in Dr6. but it appears that windows
    # isn't properly propogating that flag down to us.
    if self.context.Dr6 & 0x1 and self.hardware_breakpoints.has_key(0):
    slot = 0

    elif self.context.Dr6 & 0x2 and self.hardware_breakpoints.has_key(1):
    slot = 0
    elif self.context.Dr6 & 0x4 and self.hardware_breakpoints.has_key(2):
    slot = 0
    elif self.context.Dr6 & 0x8 and self.hardware_breakpoints.has_key(3):
    slot = 0
    else:
    # This wasn't an INT1 generated by a hw breakpoint
    continue_status = DBG_EXCEPTION_NOT_HANDLED

    # Now let's remove the breakpoint from the list
    if self.bp_del_hw(slot):
    continue_status = DBG_CONTINUE

    print "
    [*] Hardware breakpoint removed."
    return continue_status

    def bp_del_hw(self,slot):

    # Disable the breakpoint for all active threads
    for thread_id in self.enumerate_threads():

    context = self.get_thread_context(thread_id=thread_id)

    # Reset the flags to remove the breakpoint
    context.Dr7 &= ~(1 << (slot * 2))

    # Zero out the address
    if slot == 0:
    context.Dr0 = 0x00000000
    elif slot == 1:
    context.Dr1 = 0x00000000
    elif slot == 2:
    context.Dr2 = 0x00000000
    elif slot == 3:
    context.Dr3 = 0x00000000

    # Remove the condition flag
    context.Dr7 &= ~(3 << ((slot * 4) + 16))

    # Remove the length flag
    context.Dr7 &= ~(3 << ((slot * 4) + 18))

    # Reset the thread's context with the breakpoint removed
    h_thread = self.open_thread(thread_id)
    kernel32.SetThreadContext(h_thread,byref(context))

    # remove the breakpoint from the internal list.
    del self.hardware_breakpoints[slot]

    return True

    #TODO: test
    def bp_set_mem (self, address, size):

    mbi = MEMORY_BASIC_INFORMATION()

    # Attempt to discover the base address of the memory page
    if kernel32.VirtualQueryEx(self.h_process, address, byref(mbi), sizeof(mbi)) < sizeof(mbi):
    return False


    current_page = mbi.BaseAddress

    # We will set the permissions on all pages that are
    # affected by our memory breakpoint.
    while current_page <= address + size:

    # Add the page to the list, this will
    # differentiate our guarded pages from those
    # that were set by the OS or the debuggee process
    self.guarded_pages.append(current_page)

    old_protection = c_ulong(0)
    if not kernel32.VirtualProtectEx(self.h_process, current_page, size, mbi.Protect | PAGE_GUARD, byref(old_protection)):
    return False

    # Increase our range by the size of the
    # default system memory page size
    current_page += self.page_size

    # Add the memory breakpoint to our global list
    self.memory_breakpoints[address] = (address, size, mbi)

    return True



    # My debugger defines
    from ctypes import *

    # Let's map the Microsoft types to ctypes for clarity
    BYTE = c_ubyte
    WORD = c_ushort
    DWORD = c_ulong
    LPBYTE = POINTER(c_ubyte)
    LPTSTR = POINTER(c_char)
    HANDLE = c_void_p
    PVOID = c_void_p
    LPVOID = c_void_p
    UINT_PTR = c_ulong
    SIZE_T = c_ulong

    # Constants
    DEBUG_PROCESS = 0x00000001
    CREATE_NEW_CONSOLE = 0x00000010
    PROCESS_ALL_ACCESS = 0x001F0FFF
    INFINITE = 0xFFFFFFFF
    DBG_CONTINUE = 0x00010002


    # Debug event constants
    EXCEPTION_DEBUG_EVENT = 0x1
    CREATE_THREAD_DEBUG_EVENT = 0x2
    CREATE_PROCESS_DEBUG_EVENT = 0x3
    EXIT_THREAD_DEBUG_EVENT = 0x4
    EXIT_PROCESS_DEBUG_EVENT = 0x5
    LOAD_DLL_DEBUG_EVENT = 0x6
    UNLOAD_DLL_DEBUG_EVENT = 0x7
    OUTPUT_DEBUG_STRING_EVENT = 0x8
    RIP_EVENT = 0x9

    # debug exception codes.
    EXCEPTION_ACCESS_VIOLATION = 0xC0000005
    EXCEPTION_BREAKPOINT = 0x80000003
    EXCEPTION_GUARD_PAGE = 0x80000001
    EXCEPTION_SINGLE_STEP = 0x80000004


    # Thread constants for CreateToolhelp32Snapshot()
    TH32CS_SNAPHEAPLIST = 0x00000001
    TH32CS_SNAPPROCESS = 0x00000002
    TH32CS_SNAPTHREAD = 0x00000004
    TH32CS_SNAPMODULE = 0x00000008
    TH32CS_INHERIT = 0x80000000
    TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
    THREAD_ALL_ACCESS = 0x001F03FF

    # Context flags for GetThreadContext()
    CONTEXT_FULL = 0x00010007
    CONTEXT_DEBUG_REGISTERS = 0x00010010

    # Memory permissions
    PAGE_EXECUTE_READWRITE = 0x00000040

    # Hardware breakpoint conditions
    HW_ACCESS = 0x00000003
    HW_EXECUTE = 0x00000000
    HW_WRITE = 0x00000001

    # Memory page permissions, used by VirtualProtect()
    PAGE_NOACCESS = 0x00000001
    PAGE_READONLY = 0x00000002
    PAGE_READWRITE = 0x00000004
    PAGE_WRITECOPY = 0x00000008
    PAGE_EXECUTE = 0x00000010
    PAGE_EXECUTE_READ = 0x00000020
    PAGE_EXECUTE_READWRITE = 0x00000040
    PAGE_EXECUTE_WRITECOPY = 0x00000080
    PAGE_GUARD = 0x00000100
    PAGE_NOCACHE = 0x00000200
    PAGE_WRITECOMBINE = 0x00000400


    # Structures for CreateProcessA() function
    # STARTUPINFO describes how to spawn the process
    class STARTUPINFO(Structure):
    _fields_ = [
    ("cb", DWORD),
    ("lpReserved", LPTSTR),
    ("lpDesktop", LPTSTR),
    ("lpTitle", LPTSTR),
    ("dwX", DWORD),
    ("dwY", DWORD),
    ("dwXSize", DWORD),
    ("dwYSize", DWORD),
    ("dwXCountChars", DWORD),
    ("dwYCountChars", DWORD),
    ("dwFillAttribute",DWORD),
    ("dwFlags", DWORD),
    ("wShowWindow", WORD),
    ("cbReserved2", WORD),
    ("lpReserved2", LPBYTE),
    ("hStdInput", HANDLE),
    ("hStdOutput", HANDLE),
    ("hStdError", HANDLE),
    ]

    # PROCESS_INFORMATION receives its information
    # after the target process has been successfully
    # started.
    class PROCESS_INFORMATION(Structure):
    _fields_ = [
    ("hProcess", HANDLE),
    ("hThread", HANDLE),
    ("dwProcessId", DWORD),
    ("dwThreadId", DWORD),
    ]

    # When the dwDebugEventCode is evaluated
    class EXCEPTION_RECORD(Structure):
    pass

    EXCEPTION_RECORD._fields_ = [
    ("ExceptionCode", DWORD),
    ("ExceptionFlags", DWORD),
    ("ExceptionRecord", POINTER(EXCEPTION_RECORD)),
    ("ExceptionAddress", PVOID),
    ("NumberParameters", DWORD),
    ("ExceptionInformation", UINT_PTR * 15),
    ]

    class _EXCEPTION_RECORD(Structure):
    _fields_ = [
    ("ExceptionCode", DWORD),
    ("ExceptionFlags", DWORD),
    ("ExceptionRecord", POINTER(EXCEPTION_RECORD)),
    ("ExceptionAddress", PVOID),
    ("NumberParameters", DWORD),
    ("ExceptionInformation", UINT_PTR * 15),
    ]

    # Exceptions
    class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
    ("ExceptionRecord", EXCEPTION_RECORD),
    ("dwFirstChance", DWORD),
    ]

    # it populates this union appropriately
    class DEBUG_EVENT_UNION(Union):
    _fields_ = [
    ("Exception", EXCEPTION_DEBUG_INFO),
    # ("CreateThread", CREATE_THREAD_DEBUG_INFO),
    # ("CreateProcessInfo", CREATE_PROCESS_DEBUG_INFO),
    # ("ExitThread", EXIT_THREAD_DEBUG_INFO),
    # ("ExitProcess", EXIT_PROCESS_DEBUG_INFO),
    # ("LoadDll", LOAD_DLL_DEBUG_INFO),
    # ("UnloadDll", UNLOAD_DLL_DEBUG_INFO),
    # ("DebugString", OUTPUT_DEBUG_STRING_INFO),
    # ("RipInfo", RIP_INFO),
    ]

    # DEBUG_EVENT describes a debugging event
    # that the debugger has trapped
    class DEBUG_EVENT(Structure):
    _fields_ = [
    ("dwDebugEventCode", DWORD),
    ("dwProcessId", DWORD),
    ("dwThreadId", DWORD),
    ("u", DEBUG_EVENT_UNION),
    ]

    # Used by the CONTEXT structure
    class FLOATING_SAVE_AREA(Structure):
    _fields_ = [

    ("ControlWord", DWORD),
    ("StatusWord", DWORD),
    ("TagWord", DWORD),
    ("ErrorOffset", DWORD),
    ("ErrorSelector", DWORD),
    ("DataOffset", DWORD),
    ("DataSelector", DWORD),
    ("RegisterArea", BYTE * 80),
    ("Cr0NpxState", DWORD),
    ]

    # The CONTEXT structure which holds all of the
    # register values after a GetThreadContext() call
    class CONTEXT(Structure):
    _fields_ = [

    ("ContextFlags", DWORD),
    ("Dr0", DWORD),
    ("Dr1", DWORD),
    ("Dr2", DWORD),
    ("Dr3", DWORD),
    ("Dr6", DWORD),
    ("Dr7", DWORD),
    ("FloatSave", FLOATING_SAVE_AREA),
    ("SegGs", DWORD),
    ("SegFs", DWORD),
    ("SegEs", DWORD),
    ("SegDs", DWORD),
    ("Edi", DWORD),
    ("Esi", DWORD),
    ("Ebx", DWORD),
    ("Edx", DWORD),
    ("Ecx", DWORD),
    ("Eax", DWORD),
    ("Ebp", DWORD),
    ("Eip", DWORD),
    ("SegCs", DWORD),
    ("EFlags", DWORD),
    ("Esp", DWORD),
    ("SegSs", DWORD),
    ("ExtendedRegisters", BYTE * 512),
    ]

    # THREADENTRY32 contains information about a thread
    # we use this for enumerating all of the system threads

    class THREADENTRY32(Structure):
    _fields_ = [
    ("dwSize", DWORD),
    ("cntUsage", DWORD),
    ("th32ThreadID", DWORD),
    ("th32OwnerProcessID", DWORD),
    ("tpBasePri", DWORD),
    ("tpDeltaPri", DWORD),
    ("dwFlags", DWORD),
    ]

    # Supporting struct for the SYSTEM_INFO_UNION union
    class PROC_STRUCT(Structure):
    _fields_ = [
    ("wProcessorArchitecture", WORD),
    ("wReserved", WORD),
    ]


    # Supporting union for the SYSTEM_INFO struct
    class SYSTEM_INFO_UNION(Union):
    _fields_ = [
    ("dwOemId", DWORD),
    ("sProcStruc", PROC_STRUCT),
    ]
    # SYSTEM_INFO structure is populated when a call to
    # kernel32.GetSystemInfo() is made. We use the dwPageSize
    # member for size calculations when setting memory breakpoints
    class SYSTEM_INFO(Structure):
    _fields_ = [
    ("uSysInfo", SYSTEM_INFO_UNION),
    ("dwPageSize", DWORD),
    ("lpMinimumApplicationAddress", LPVOID),
    ("lpMaximumApplicationAddress", LPVOID),
    ("dwActiveProcessorMask", DWORD),
    ("dwNumberOfProcessors", DWORD),
    ("dwProcessorType", DWORD),
    ("dwAllocationGranularity", DWORD),
    ("wProcessorLevel", WORD),
    ("wProcessorRevision", WORD),
    ]

    # MEMORY_BASIC_INFORMATION contains information about a
    # particular region of memory. A call to kernel32.VirtualQuery()
    # populates this structure.
    class MEMORY_BASIC_INFORMATION(Structure):
    _fields_ = [
    ("BaseAddress", PVOID),
    ("AllocationBase", PVOID),
    ("AllocationProtect", DWORD),
    ("RegionSize", SIZE_T),
    ("State", DWORD),
    ("Protect", DWORD),
    ("Type", DWORD),
    ]


    If i run these with your sample code:




    #my_test.py
    # code I run to test the debugger and its components
    import my_debugger

    debugger = my_debugger.debugger()

    #debugger.load("C:\\WINDOWS\\system32\\calc.exe")
    pid = raw_input("Enter the PID of the process to attach to: ")

    debugger.attach(int(pid))

    debugger.detach()


    And i input a PID i selected from a running process from my task manager list. It prints that debugging has been successful.

    I have something that may interest you though as a side note, if you run your code like this from an elevated command prompt.




    #my_test.py
    # code I run to test the debugger and its components
    import my_debugger
    from win32com.client import GetObject
    import sys, os

    debugger = my_debugger.debugger()

    debugger.load("C:\\WINDOWS\\system32\\calc.exe")

    Wmi = GetObject('winmgmts:')
    processes = Wmi.InstancesOf('Win32_Process')
    # Get process
    calculator = Wmi.ExecQuery('select * from Win32_Process where Name="calc.exe"')
    # Grab its PID
    pid =calculator[0].Properties_('ProcessId').Value

    debugger.attach(int(pid))

    debugger.detach()


    It's going to grab the PID of calc.exe on it's own if it's a win32 process that is.
  3. #3
    mmQ Lisa Turtle
    WWLN (what would Lanny do)
  4. #4
    Sophie Pedophile Tech Support
    WWLN (what would Lanny do)

    Who knows? The Lan works in mysterious ways.
  5. #5
    Lanny Bird of Courage
    Not reading enough of the code to really understand what's going on, try inserting:

    from win32con import *


    at the top of my_debugger.py, a little googling suggests that constant lives in the win32con module (you'll need the win32 extensions to be installed to import that module, make sure you've done that, if you haven't you can grab the appropriate release here). Try adding that and let us know what the output is. Also like soph said, you'll almost certainly need admin rights to access other processes memory space but that shouldn't produce this particular error. It seems like some versions of ctypes has this constant in it since some people seem to be able to run the code but it's better to use the win32 extensions version even if you could get it out of ctypes because this is, obviously, a win32 specific flag.

    As an aside, this is one reason why the `from X import *` pattern is a bad practice, it becomes difficult to identify which library a variable is coming from if you don't use a prefix. While the "import X" syntax is prefered because it forces you to use the canonical form of a library name (making one-liners comprehensible), if you want to save on typing you can do something like `import ctypes as c` and then use things like `c.c_int32`. It's two characters more but it's intelligible to humans with a simple lookup. I realize this is derived from someone else's code so whatever, but it's still a good headsup when doing your own sutff.
  6. #6
    Vise-versa, man.


    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
Jump to Top