저번에는 디버거에 PID를 이용해 프로세스를 attach 까지 했다.
이번에는 디버거 핸들러 구현을 할것이다.
이벤트가 발생했을 때 디버거가 그 이벤트에 반응하려면, 각 디버깅 이벤트에 대한 핸들러가 있어야 한다.
--> WaitForDebugEvent() 함수를 이용하는데, 이 함수는 이벤트가 발생할 때마다 DEBUG_EVENT 구조체를 반환한다.
DEBUG_EVENT 구조체의 구조는
typedef struct _DEBUG_EVENT { DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union { EXCEPTION_DEBUG_INFO Exception; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnloadDll; OUTPUT_DEBUG_STRING_INFO DebugString; RIP_INFO RipInfo; } u; } DEBUG_EVENT;
이 구조체에는 디버그에 유용한 정보가 많다.
dwDebugEventCode 를 통해 발생한 이벤트의 종류를 알 수 있다.
이벤트의 종류를 알게되면 u 유니언에서 어떤 구조체를 사용해야 하는지 알 수 있게 된다.
아래 표는 각 이벤트 코드와 값, 그리고 유니언 u의 값을 보여준다.
이벤트 코드 |
이벤트 코드 값 |
유니언 u의 값 |
0x1 |
EXCEPTION_DEBUG_EVENT |
u.Exception |
0x2 |
CREATE_THREAD_DEBUG_EVENT |
u.CreateThread |
0x3 |
CREATE_PROCESS_DEBUG_EVENT |
u.CreateProcessInfo |
0x4 |
EXIT_THREAD_DEBUG_EVENT |
u.ExitThread |
0x5 |
EXIT_PROCESS_DEBUG_EVENT |
u.ExitProcess |
0x6 |
LOAD_DLL_DEBUG_EVENT |
u.LoadDll |
0x7 |
UNLOAD_DLL_DEBUG_EVENT |
u.UnloadDll |
0x8 |
OUTPUT_DEBUG_STRING_EVENT |
u.DebugString |
0x9 |
RIP_EVENT |
u.RipInfo |
이걸 바탕으로 실행한 내용:
소스:
my_test.py
# -*- coding: cp949 -*- import my_debugger debugger = my_debugger.debugger() pid = raw_input("Enter the PID of the process to attach to: ") debugger.attach(int(pid)) debugger.run() debugger.detach()
my_debugger.py
# -*- coding: cp949 -*- 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.h_thread = None self.context = None # Here let's determine and store # the default page size for the system # determine the system page size. 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 "[*] PID : %d" % process_information.dwProcessId # 새로 생성한 프로세스의 핸들을 구한 후 # 나중에 접근하기 위해 저장한다. self.h_process = self.open_process(process_information.dwProcessId) else: print "[*] Error : 0x%08x." % kernel32.GetLastError() def open_process(self,pid): # PROCESS_ALL_ACCESS = 0x0x001F0FFF h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS,False,pid) return h_process def open_thread(self, thread_id): # thread_id를 인자로 전달해주면 해당 thread의 핸들을 리턴한다. 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() # Thread32First 실행인자로 전달되는 구조체 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)) # Thread32First 함수 실행 후에는 Thread32Next 함수로 스레드 리스트를 끝까지 열거한다. kernel32.CloseHandle(snapshot) return thread_list else: return False def get_thread_context (self, thread_id=None, h_thread=None): context = CONTEXT() # 디버그 레지스터에 대한 정보를 담고있는 CONTEXT 구조체이다. context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS # 스레드의 핸들을 구한다. h_thread = self.open_thread(thread_id) if kernel32.GetThreadContext(h_thread, byref(context)): # 레지스터 정보를 얻기 위해서 GetThreadContext 함수를 사용한다. # 첫 인자는 레지스터를 얻고자 하는 스레드의 핸들이며, 두번째는 정보가 저장되어야 하는 context의 포인터이다. kernel32.CloseHandle(h_thread) return context else: return False def attach(self,pid): self.h_process = self.open_process(pid) # 프로세스에 대한 어태치를 시도한다. # 실패하면 호출을 종료한다. if kernel32.DebugActiveProcess(pid): self.debugger_active = True self.pid = int(pid) else: print "[*] Unable to attach to the process." def run(self): # 이제 디버기에 대한 디버그 이벤트를 처리해야 한다. # debugging events while self.debugger_active == True: self.get_debug_event() def get_debug_event(self): resultMap = { 1:"EXCEPTION_DEBUG_EVENT", 2:"CREATE_THREAD_DEBUG_EVENT", 3:"CREATE_PROCESS_DEBUG_EVENT", 4:"EXIT_THREAD_DEBUG_EVENT", 5:"EXIT_PROCESS_DEBUG_EVENT", 6:"LOAD_DLL_DEBUG_EVENT", 7:"UNLOAD_DLL_DEBUG_EVENT", 8:"OUTPUT_DEBUG_STRING_EVENT", 9:"RIP_EVENT" } 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 : %s / Thread Id: %d" % (debug_event.dwDebugEventCode,resultMap.get(debug_event.dwDebugEventCode), debug_event.dwThreadId) 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_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), ]
출처!!~~!~!~!
'스터디 > 파이썬 해킹 프로그래밍' 카테고리의 다른 글
Soft Breakpoint 까지의 이론 정리 (0) | 2015.01.17 |
---|---|
파이썬 해킹 프로그래밍 서적 오류 수정 (0) | 2015.01.17 |
디버거와 프로세스를 연결하는 방법, CPU 레지스터 값을 출력하는 방법 (0) | 2015.01.15 |
DLL 기초개념 (0) | 2015.01.14 |
스터디 질문 답변 (0) | 2015.01.14 |