이 이외의 소스는 변경되지 않았기에, 업로드 하지 않았다. (my_debugger_defines.py , printf_loop 등)
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 = {}
# 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
# 스레드의 핸들을 구한다.
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
my_test_hw.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_hw(printf_address,1,HW_EXECUTE)
debugger.run()
debugger.detach()
'스터디 > └ 소스파일들' 카테고리의 다른 글
| 책 원본 소스파일 (0) | 2015.01.20 |
|---|---|
| Memory Breakpoint 까지의 소스 (0) | 2015.01.17 |
| Soft Breakpoint 까지의 소스들 (0) | 2015.01.17 |
| my_test.py (0) | 2015.01.15 |
| my_debugger_defines.py (0) | 2015.01.15 |
my_debugger.py