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
下次我們來加入軟硬和記憶體斷點完善偵錯程式。