python打造windows调试器

这几天也是在阅读《Python灰帽子》这本书,根据所学写一个python自制的调试器吧!!!

学逆向的人都要会一个库:ctype。ctype可以调用动态链接库的函数,还可以像C语言一样有底层操作能力。

调试器需要打开执行程序作为自身子程序执行

所以我们需要用到CreateProcessA()这个函数帮我们创建一个进程:

BOOL CreateProcess
(
LPCTSTR lpApplicationName, //可执行文件所在路径
LPTSTR lpCommandLine,  //命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,  //可调式设置
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,  //STARTUPINFO结构体
LPPROCESS_INFORMATIONlpProcessInformation   //PROCESS_INFORMATION结构体
);

 

重要的参数是:lpApplicationName、lpCommandLine、dwCreationFlags、lpStartupInfo和lpProcessInformation,其余参数可以设置为NULL。

我们也需要OpenProcess()这个函数帮我们进行获取进程句柄

HANDLE OpenProcess(
DWORD dwDesiredAccess, //渴望得到的访问权限(标志)
BOOL bInheritHandle, // 是否继承句柄
DWORD dwProcessId// 进程标示符
);

 

DebugActiveProcess()来实现进程的附加(只有一个参数是PID)

BOOL WINAPI DebugActiveProcess(
  __in DWORD dwProcessId
  );

 

WaitForDebugEvent()则是不断获取调试事件的函数(将dwMilliseconds设置为:0xFFFFFFFF)

WaiteForDebugEvent(LPDEBUG_EVENT _DEBUG_EVENT,DWORD dwMilliseconds)

 

OpenThread()则使我们获得CPU寄存器状态

HANDLE WINAPI OpenThread(
    _In_ DWORD dwDesiredAccess,
    _In_ BOOL  bInheritHandle,
    _In_ DWORD dwThreadId 
);

 

CONTEXT结构体(寄存器信息)

typedef struct _CONTEXT
{
    DWORD           ContextFlags   
    DWORD           Dr0             
    DWORD           Dr1            
    DWORD           Dr2             
    DWORD           Dr3             
    DWORD           Dr6            
    DWORD           Dr7           

    FLOATING_SAVE_AREA FloatSave;   //浮点寄存器

    DWORD           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           EFlag          
    DWORD           Esp         
    DWORD           SegSs       

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;

 

my_debugger_defines.py(所有结构体及函数值)

  1 from ctypes import *
  2 
  3 
  4 WORD = c_ushort
  5 DWORD = c_ulong
  6 BYTE = c_ubyte
  7 LPBYTE = POINTER(c_ubyte)
  8 LPTSTR = POINTER(c_char)
  9 HANDLE = c_void_p
 10 PVOID = c_void_p
 11 LPVOID = c_void_p
 12 UNINT_PTR = c_ulong
 13 SIZE_T = c_ulong
 14 
 15 
 16 DEBUG_PROCESS = 0x00000001
 17 CREATE_NEW_CONSOLE = 0x00000010
 18 PROCESS_ALL_ACCESS = 0x001F0FFF
 19 INFINITE = 0xFFFFFFFF
 20 DBG_CONTINUE = 0x00010002
 21 DBG_EXCEPTION_NOT_HANDLED = 0x80010001
 22 
 23 
 24 EXCEPTION_DEBUG_EVENT = 0x01
 25 CREATE_THREAD_DEBUG_EVENT = 0x02
 26 CREATE_PROCESS_DEBUG_EVENT = 0x03
 27 EXIT_THREAD_DEBUG_EVENT = 0x04
 28 EXIT_PROCESS_DEBUG_EVENT = 0x05
 29 LOAD_DLL_DEBUG_EVENT = 0x06
 30 UNLOAD_DLL_DEBUG_EVENT = 0x07
 31 OUTPUT_DEBUG_STRING_EVENT = 0x08
 32 RIP_EVENT = 0x09
 33 
 34 
 35 EXCEPTION_ACCESS_VIOLATION = 0xC0000005
 36 EXCEPTION_BREAKPOINT = 0x80000003
 37 EXCEPTION_GUARD_PAGE = 0x80000001
 38 EXCEPTION_SINGLE_STEP = 0x80000004
 39 
 40 TH32CS_SNAPTHREAD = 0x00000004
 41 
 42 THREAD_ALL_ACCESS = 0x001F03FF
 43 
 44 CONTEXT_FULL = 0x00010007
 45 CONTEXT_DEBUG_REGISTERS = 0x00010010
 46 
 47 HW_ACCESS = 0x00000003
 48 HW_EXECUTE = 0x00000000
 49 HW_WRITE = 0x00000001
 50 
 51 PAGE_GUARD = 0x00000100
 52 
 53 
 54 class STARTUPINFO(Structure):
 55     _fields_ = [
 56         ("cb",DWORD),
 57         ("IpReserved",LPTSTR),
 58         ("IpDesktop",LPTSTR),
 59         ("IpTitle",LPTSTR),
 60         ("dwX",DWORD),
 61         ("dwY",DWORD),
 62         ("dwXSize",DWORD),
 63         ("dwYSize",DWORD),
 64         ("dwXCountChars",DWORD),
 65         ("dwYCountChars",DWORD),
 66         ("dwFillAttribute",DWORD),
 67         ("dwFlags",DWORD),
 68         ("wShowWindow",WORD),
 69         ("cbReserved2",WORD),
 70         ("IpReserved2",LPBYTE),
 71         ("hStdInput",HANDLE),
 72         ("hStdOutput",HANDLE),
 73         ("hStdError",HANDLE),
 74     ]
 75 
 76 
 77 class PROCESS_INFORMATTION(Structure):
 78     _fields_ = [
 79         ("hProcess",HANDLE),
 80         ("hThread",HANDLE),
 81         ("dwProcessId",DWORD),
 82         ("dwThreadId",DWORD),
 83     ]
 84 
 85 
 86 class EXCEPTION_RECORD(Structure):
 87     pass
 88 class EXCEPTION_RECORD(Structure):
 89     _fields_ = [
 90     ("ExceptionCode", DWORD),
 91     ("ExceptionFlags", DWORD),
 92     ("ExceptionRecord", POINTER(EXCEPTION_RECORD)),
 93     ("ExceptionAddress", PVOID),
 94     ("NumberParameters", DWORD),
 95     ("ExceptionInformation", UNINT_PTR * 15),
 96 ]
 97 
 98 
 99 class EXCEPTION_DEBUG_INFO(Structure):
100     _fields_ = [
101         ("ExceptionRecord", EXCEPTION_RECORD),
102         ("dwFirstChance", DWORD),
103     ]
104 
105 
106 class DEBUG_EVENT_UNION(Union):
107     _fields_ = [
108         ("Exception",EXCEPTION_DEBUG_INFO),
109     ]
110 
111 
112 
113 class DEBUG_EVENT(Structure):
114     _fields_ = [
115         ("dwDebugEventCode",DWORD),
116         ("dwProcessId",DWORD),
117         ("dwThreadId",DWORD),
118         ("u",DEBUG_EVENT_UNION),
119     ]
120 
121 
122 class THREADENTRY32(Structure):
123     _fields_ = [
124         ("dwSize",DWORD),
125         ("cntUsage",DWORD),
126         ("th32ThreadID",DWORD),
127         ("th32OwnerProcessID",DWORD),
128         ("tpBasePri",DWORD),
129         ("tpDeltapri",DWORD),
130         ("dwFlags",DWORD),
131     ]
132 
133 
134 class FLOAT_SAVE_AREA(Structure):
135     _fields_ = [
136         ("ControlWord",DWORD),
137         ("StatusWord",DWORD),
138         ("TagWord",DWORD),
139         ("ErrorOffset",DWORD),
140         ("ErrorSelector",DWORD),
141         ("DataOffset",DWORD),
142         ("DataSelector",DWORD),
143         ("RegisterArea",BYTE * 80),
144         ("Cr0NpxState",DWORD)
145     ]
146 
147 
148 class CONTEXT(Structure):
149     _fields_ = [
150         ("ContextFlags",DWORD),
151         ("Dr0",DWORD),
152         ("Dr1", DWORD),
153         ("Dr2", DWORD),
154         ("Dr3", DWORD),
155         ("Dr6", DWORD),
156         ("Dr7", DWORD),
157         ("FloatSave",FLOAT_SAVE_AREA),
158         ("SegGs",DWORD),
159         ("SegFs", DWORD),
160         ("SegEs", DWORD),
161         ("SegDs", DWORD),
162         ("Edi",DWORD),
163         ("Esi", DWORD),
164         ("Ebx", DWORD),
165         ("Edx", DWORD),
166         ("Ecx", DWORD),
167         ("Eax", DWORD),
168         ("Ebp", DWORD),
169         ("Eip", DWORD),
170         ("SegSS",DWORD),
171         ("EFlags",DWORD),
172         ("Esp",DWORD),
173         ("SegSs",DWORD),
174         ("ExtendedRegisters",BYTE * 512)
175     ]
176 
177 class PROC_STRUCT(Structure):
178     _fields_ = [
179         ("wProcessorArchitecture", WORD),
180         ("wReserved", WORD),
181     ]
182 
183 
184 class SYSTEM_INFO_UNION(Union):
185     _fields_ = [
186         ("dsOemId", DWORD),
187         ("sProcStruc", PROC_STRUCT),
188     ]
189 
190 class SYSTEM_INFO(Structure):
191     _fields_ = [
192         ("uSysInfo", SYSTEM_INFO_UNION),
193         ("dwPageSize", DWORD),
194         ("lpMinimumApplicationAddress", LPVOID),
195         ("lpMaximumApplicationAddress", LPVOID),
196         ("dwActiveProcessMask", DWORD),
197         ("dwNumberOfProcessors", DWORD),
198         ("dwProcessorType", DWORD),
199         ("dwAllocationGranularity", DWORD),
200         ("wProcessorLevel", WORD),
201         ("wProcessorRevision", WORD),
202     ]
203 
204 
205 
206 class SYSTEM_INFO(Structure):
207     _fields_ = [
208         ("uSysInfo", SYSTEM_INFO_UNION),
209         ("dwPageSize", DWORD),
210         ("lpMinimumApplicationAddress", LPVOID),
211         ("lpMaximumApplicationAddress", LPVOID),
212         ("dwActiveProcessMask", DWORD),
213         ("dwNumberOfProcessors", DWORD),
214         ("dwProcessorType", DWORD),
215         ("dwAllocationGranularity", DWORD),
216         ("wProcessorLevel", WORD),
217         ("wProcessorRevision", WORD),
218     ]
219 
220 class PROC_STRUCT(Structure):
221     _fields_ = [
222         ("wProcessorArchitecture", WORD),
223         ("wReserved", WORD),
224     ]
225 class SYSTEM_INFO_UNION(Union):
226     _fields_ = [
227         ("dsOemId", DWORD),
228         ("sProcStruc", PROC_STRUCT),
229     ]
230 
231 class SYSTEM_INFO(Structure):
232     _fields_ = [
233         ("uSysInfo", SYSTEM_INFO_UNION),
234         ("dwPageSize", DWORD),
235         ("lpMinimumApplicationAddress", LPVOID),
236         ("lpMaximumApplicationAddress", LPVOID),
237         ("dwActiveProcessMask", DWORD),
238         ("dwNumberOfProcessors", DWORD),
239         ("dwProcessorType", DWORD),
240         ("dwAllocationGranularity", DWORD),
241         ("wProcessorLevel", WORD),
242         ("wProcessorRevision", WORD),
243     ]
244 
245 
246 
247 class MEMORY_BASIC_INFORMATION(Structure):
248     _fields_ = [
249         ("BaseAddress", PVOID),
250         ("AllocationBase", PVOID),
251         ("AllocationProtect", DWORD),
252         ("RegionSize",SIZE_T),
253         ("State", DWORD),
254         ("Protect", DWORD),
255         ("Type", DWORD),
256     ]

 

 my_debugger.py

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
        self.context = None
        self.exception = None
        self.exception_address = None
        self.software_breakpoints = {}
        self.hardware_breakpoints = {}
        self.memory_breakpoints = {}
        self.first_breakpoints = True
        system_info = SYSTEM_INFO()
        kernel32.GetSystemInfo(byref(system_info))
        self.page_size = system_info.dwPageSize
        self.guarded_pages = []

    def load(self,path_to_exe):
        create_flags = DEBUG_PROCESS 

        startupinfo = STARTUPINFO()
        process_information = PROCESS_INFORMATTION()

        startupinfo.dwFlags = 0x1
        startupinfo.wShowWindow = 0x0

        startupinfo.cb = sizeof(startupinfo)  

        if kernel32.CreateProcessA(
            path_to_exe,
            None,
            None,
            None,
            None,
            create_flags,
            None,
            None,
            byref(startupinfo),
            byref(process_information)
        ):
            print "We have successfully lauched the process"
            print "PID: %d" % process_information.dwProcessId

        else:
            print "ERROR:0x%08x." % kernel32.GetLastError() 

    def open_process(self,pid):
        h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS,False,pid)  

        return h_process

    def attach(self,pid):
        self.h_process = self.open_process(pid)      
                                  
        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):
        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 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"

    def enumerate_threads(self):
        thread_entry = THREADENTRY32()
        thread_list = []

        snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,self.pid)

        if snapshot is not None:
            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))

            kernel32.CloseHandle(snapshot)
            return thread_list
        else:

            return False

    def get_thread_context(self,thread_id):
        context = CONTEXT()

        context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

        h_thread = self.open_thread(thread_id)

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

            return context

        else:

            return False


    def exception_handler_breakpoint(self):
        print "[*] Inside the break handler."
        print "Exception adress:0x%08x" % self.exception_address

        print kernel32.GetLastError()

        if not self.exception_address in self.hardware_breakpoints.keys():
            if self.first_breakpoints == True:
                self.first_breakpoints = False
                print "[*] Hit the first breakpoint."
        else:
            print "[*] Hit user defined breakpoint."
        return DBG_CONTINUE


    def get_debug_event(self):
        debug_event = DEBUG_EVENT()
        continue_status = DBG_CONTINUE

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

            self.h_thread = self.open_thread(debug_event.dwThreadId)
            self.context = self.get_thread_context(self.h_thread)

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

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

 

my_test.py

import my_debugger
from ctypes import *


debugger = my_debugger.debugger()

debugger.load("C:\Users\Dell\Desktop\Auth.exe")



pid = input()

debugger.attach(int(pid))


list = debugger.enumerate_threads()


for thread in list:
    thread_context = debugger.get_thread_context(thread)
    print "Dumping registers for thread ID: 0x%08x" % thread

 

运行结果为调试事件流

Event CODE: 3  Thread ID : 964
Event CODE: 3  Thread ID : 10428
Event CODE: 6  Thread ID : 10428
Event CODE: 2  Thread ID : 12008
Event CODE: 6  Thread ID : 10428
Event CODE: 6  Thread ID : 10428
Event CODE: 6  Thread ID : 10428
Event CODE: 2  Thread ID : 4456
Event CODE: 1  Thread ID : 4456
Event CODE: 4  Thread ID : 4456
Event CODE: 6  Thread ID : 964
Event CODE: 7  Thread ID : 964
Event CODE: 6  Thread ID : 964
Event CODE: 7  Thread ID : 964
Event CODE: 7  Thread ID : 964
Event CODE: 7  Thread ID : 964
Event CODE: 6  Thread ID : 964
Event CODE: 6  Thread ID : 964
Event CODE: 6  Thread ID : 964
Event CODE: 2  Thread ID : 968
Event CODE: 1  Thread ID : 964

下次我们来加入软硬和内存断点完善调试器。

 

posted @ 2020-08-02 21:15  VIUS_Jcool  阅读(470)  评论(0编辑  收藏  举报