1. 程式人生 > 實用技巧 >python打造windows偵錯程式(一)

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

下次我們來加入軟硬和記憶體斷點完善偵錯程式。