2012. 6. 27. 00:44

[출처 : 파이썬 해킹 프로그래밍 ] 



----------------------------------- my_test.py -----------------------------------------------------

import my_debugger  my_debugger 클래스를 가져온다. (이는 다음 부분에 설명되어있다)
debugger = my_debugger.debugger()    #my_debugger 에서 debugger 함수를 호출하고.
debugger.load("C:₩₩WINDOWS₩₩system32₩₩calc.exe") ##그인자값으로 계산기를 보낸다. 

--------------------------------------------------------------------------------------------------


------------------------------------------- my_debugger ----------------------------------------

from ctypes import *   ## c언어 타입이란걸 정의한다. 

from my_debugger_defines import *   ##my_debugger_deifnes 은 정의된 모든 상수값과 구조체, 유니언을 위치시킨다.


import sys   ##system 을 import 시킨다. 어디에 사용되는지는 아직 잘 모르겠다.

import time  ##시간을 사용하기 위한 클래스를 import 시킨다. 어디에 사용되는지는 아직 잘 모르겠다.

kernel32 = windll.kernel32   ##window dll 을 파이선에서 사용할 수 있도록 한다.


class debugger():

    

  ##프로세스 디버깅을 


##init 이란 구조체는 모든값을 초기화 시킨다. 

    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): ##load 라는 것을 정의한다. 어떠한 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)

        

##exe 파일을 프로새스를 생성시킨다.

        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(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.py----------------------------------

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),

]

    

----------------------------------------------------------------------------------------------







Posted by k1rha