파이썬 해킹 프로그래밍 3장 최종 소스.
참 여러 삽질 끝에 도달했다.
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 self.exception = None self.exception_address = None self.breakpoints = {} self.first_breakpoint = True self.hardware_breakpoints = {} self.guarded_pages = [] self.memory_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 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 # 스레드의 핸들을 구한다. if h_thread is None: self.h_thread = self.open_thread(thread_id) if kernel32.GetThreadContext(self.h_thread, byref(context)): # 레지스터 정보를 얻기 위해서 GetThreadContext 함수를 사용한다. # 첫 인자는 레지스터를 얻고자 하는 스레드의 핸들이며, 두번째는 정보가 저장되어야 하는 context의 포인터이다. 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), 100): # 스레드의 컨텍스터 정보를 구한다. 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 : %s / Thread Id: %d" % (debug_event.dwDebugEventCode,resultMap.get(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 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: print "Single Stepping." 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 exception_handler_breakpoint(self): #print "[+] Inside the breakpoint handler." print "Exception Address: 0x%08x" % self.exception_address if not self.breakpoints.has_key(self.exception_address): if self.first_breakpoint == True: self.first_breakpoint = False print "[+] Hit the first breakpoint." return DBG_CONTINUE else: print "[+] Hit user defined breakpoint." self.write_process_memory(self.exception_address,self.breakpoints[self.exception_address]) self.context = self.get_thread_context(h_thread=self.h_thread) self.context.Eip -= 0x01 # Error! kernel32.SetThreadContext(self.h_thread,byref(self.context)) continue_status = DBG_CONTINUE return continue_status def read_process_memory(self,address,length): data = "" read_buf = create_string_buffer(length) count = c_ulong(0) if not kernel32.ReadProcessMemory(self.h_process,address,read_buf,length,byref(count)): return False else: data = 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): if not self.breakpoints.has_key(address): try: #원래의 바이트 값을 저장 original_byte = self.read_process_memory(address,1) # cc opcode write self.write_process_memory(address,"\xCC") # 리스트에 bp 추가 self.breakpoints[address] = (original_byte) print self.breakpoints[address] except: return False return True def resolve_func(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 length value if length not in (1,2,4): return False else: length -= 1 # check condition value if condition not in (HW_ACCESS,HW_EXECUTE,HW_WRITE): return False # check Debug register 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 # configure Debug Register at whole thread for thread_id in self.enumerate_threads(): context = self.get_thread_context(thread_id = thread_id) # 어느 디버그 레지스터를 이용해 브레이크포인트를 설정할 것인지 # DR7 레지스터의 플래그 값을 설정함. context.Dr7 |= 1 << (available * 2) # 브레이크 포인트를 설정할 주소를 유효한 디버그 레지스터에 저장한다. if available == 0: context.Dr0 = address elif available == 1: context.Dr1 = address elif available == 2: context.Dr2 = address elif available == 3: context.Dr3 = address # 브레이크 포인트 조건 플래그 설정. context.Dr7 |= condition << ((available * 4) + 16) # set length context.Dr7 |= length << ((available * 4) + 18) # 브레이크 포인트가 설정된 쓰레드 컨텍스트를 설정 h_thread = self.open_thread(thread_id) kernel32.SetThreadContext(h_thread,byref(context)) # 내부적으로 관리하는 하드웨어 브레이크포인트 배열을 갱신한다. self.hardware_breakpoints[available] = (address,length,condition) return True def exception_handler_single_step(self): 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 = 1 elif self.context.Dr6 & 0x4 and self.hardware_breakpoints.has_key(2): slot = 2 elif self.context.Dr6 & 0x8 and self.hardware_breakpoints.has_key(3): slot = 3 else: # 하드웨어 브레이크 포인트에 의해 발생한 INT 1 continue_status = DBG_EXCEPTION_NOT_HANDLED # 이제는 리스트에서 브레이크 포인트를 제거하자 if self.bp_del_hw(slot): continue_status = DBG_CONTINUE print "[+] Hardware breakpoint removed." return continue_status def bp_del_hw(self,slot): # 동작 중인 모든 쓰레드의 하드웨어 브레이크 포인트를 제거한다. for thread_id in self.enumerate_threads(): context = self.get_thread_context(thread_id = thread_id) # 하드웨어 브레이크 포인트를 제거하기 위해 Dr7 레지스터 플래그 초기화 context.Dr7 &= ~(1 << (slot * 2)) # 브레이크 포인트 주소를 제거 if slot == 0: context.Dr0 = 0x00000000 elif slot == 1: context.Dr1 = 0x00000000 elif slot == 2: context.Dr2 = 0x00000000 elif slot == 3: context.Dr3 = 0x00000000 # 브레이크 포인트 조건 플래그 리셋 context.Dr7 &= ~(3 << ((slot * 4) + 16)) # 길이 플래그 리셋 context.Dr7 &= ~(3 << ((slot * 4) + 18)) # 브레이크 포인트가 제거된 쓰레드 컨텍스트로 설정 h_thread = self.open_thread(thread_id) kernel32.SetThreadContext(h_thread,byref(context)) # 내부 리스트에서 해당 브레이크 포인트를 제거 del self.hardware_breakpoints[slot] return True def bp_set_mem(self,address,size): mbi = MEMORY_BASIC_INFORMATION() # VirtualQueryEx() 함수의 반환 값이 MEMORY_BASIC_INFORMATION 구조체의 크기보다 작으면 False 반환 if kernel32.VirtualQueryEx(self.h_process,address,byref(mbi),sizeof(mbi)) < sizeof(mbi): return False current_page = mbi.BaseAddress # 메모리 브레이크 포인트를 설정할 메모리 영역이 속하는 모든 메모리 페이지의 접근 권한을 변경 while current_page <= address + size: # 페이지를 리스트에 추가한다. # 운영체제나 디버그 대상 프로세스에 의해 페이지 접근 권한이 변경된 경우와 구별하기 위해서 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 # Default 시스템 메모리 페이지 사이즈만큼 증가시킨다. current_page += self.page_size # 전역 리스트에 메모리 브레이크포인트를 추가한다. self.memory_breakpoints[address] = (address,size,mbi) return True
my_test_mem.py
# -*- coding: cp949 -*- import my_debugger from my_debugger_defines import * f=open("pid.txt",'r') pid=f.readline() debugger = my_debugger.debugger() printf_address = debugger.resolve_func("msvcrt.dll","printf") debugger.attach(int(pid)) print "pid : %s" % pid print "[+] Address of printf : 0x%08x" % printf_address #change debugger.bp_set_mem(printf_address,8) debugger.run() debugger.detach()
'스터디 > └ 소스파일들' 카테고리의 다른 글
8시간의 대장정. (0) | 2015.01.26 |
---|---|
책 원본 소스파일 (0) | 2015.01.20 |
Hardware Breakpoint 까지 소스 (0) | 2015.01.17 |
Soft Breakpoint 까지의 소스들 (0) | 2015.01.17 |
my_test.py (0) | 2015.01.15 |