[2주] 03장 - 윈도우 디버거 개발 (3)

Posted by dw0rdptr
2015. 1. 19. 04:11 Study/파이썬 해킹 프로그래밍

디버거는 cpu 레지스터의 상태정보를 캡쳐하고 변경할 수 있어야 한다.

이 글에서는 디버깅 대상 프로세스의 현재 실행중인 스레드 핸들을 얻어 레지스터 값을 출력할 것이다.




스레드 핸들을 구해 레지스터값을 알아내는 것 까지 구현한 my_debugger 소스코드
#my_debugger.py
#-*- coding: utf-8 -*-
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

	def load(self,path_to_exe):
		# dwCreation 플래그를 이용해 프로세스를 어떻게 생성할 것인지 판단.
		# 계산기의 GUI를 보고자 한다면 creation_flags를 CREATE_NEW_CONSOLE로 설정
		creation_flags = DEBUG_PROCESS
		
		#구조체 인스턴트화
		startupinfo			= STARTUPINFO()
		process_information	= PROCESS_INFORMATION()

		# 프로세스가 독립적인 창으로 실행되게 해줌
		# STARTUPINFO struct 구조체내용에따라 디버기프로세스에 어떤영향을 미치는지 보여줌
		startupinfo.dwFlags		= 0x1
		startupinfo.wShowWindow = 0x0


		# STARTUPINFO struct 구조체 자신의 크기를 나타내는 cb 변수값을 초기화
		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_infomation.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)
			
		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):

			#아직 디버그이벤트를 처리한 핸들러 구현x 단순히 프로세스가 실행을 계속하게 함
			#raw_input("Press a key to continue...")
			#self.debugger_active = False
			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 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."
			return False

	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=None, h_thread = None):
		context = CONTEXT()
		context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

		if not h_thread:
			self.open_thread(thread_id)

		#스레드의 핸들을 구한다.
		h_thread = self.open_thread(thread_id)
		if kernel32.GetThreadContext(h_thread, byref(context)):
			kernel32.CloseHandle(h_thread)
			return context
		else:
			return False



테스트를 위한 소스코드
#-*- coding: utf-8 -*-
import my_debugger

debugger = my_debugger.debugger()

pid = raw_input("Enter the PID of the process to attach to: ")

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
	print "[**] EIP:0x%08x" %thread_context.Eip
	print "[**] ESP:0x%08x" %thread_context.Esp
	print "[**] EBP:0x%08x" %thread_context.Ebp
	print "[**] EAX:0x%08x" %thread_context.Eax
	print "[**] EBX:0x%08x" %thread_context.Ebx
	print "[**] ECX:0x%08x" %thread_context.Ecx
	print "[**] EDX:0x%08x" %thread_context.Edx
	print "[*] END DUMP"

debugger.detach()


my_test.py 실행결과
Enter the PID of the process to attach to: 1092
[*] Dumping registers for thread ID : 0x00000ddc
[**] EIP:0x77a170b4
[**] ESP:0x0016ed58
[**] EBP:0x0016ed74
[**] EAX:0x002df130
[**] EBX:0x00000000
[**] ECX:0x76f4372c
[**] EDX:0x002df130
[*] END DUMP
[*] Dumping registers for thread ID : 0x00000fb0
[**] EIP:0x77a170b4
[**] ESP:0x00a2fd28
[**] EBP:0x00a2fdc4
[**] EAX:0x747887f8
[**] EBX:0x00a2fd78
[**] ECX:0x00000300
[**] EDX:0x77a170b4
[*] END DUMP
[*] Dumping registers for thread ID : 0x0000080c
[**] EIP:0x77a170b4
[**] ESP:0x02cffc2c
[**] EBP:0x02cffcd4
[**] EAX:0x00000001
[**] EBX:0x00000000
[**] ECX:0x002ae6d8
[**] EDX:0x77a170b4
[*] END DUMP
[*] Dumping registers for thread ID : 0x00000244
[**] EIP:0x77a170b4
[**] ESP:0x02e3fde0
[**] EBP:0x02e3ff74
[**] EAX:0x02e3ff24
[**] EBX:0x779ffe90
[**] ECX:0x00000000
[**] EDX:0x779ffd0f
[*] END DUMP
[*] Dumping registers for thread ID : 0x0000035c
[**] EIP:0x77a17098
[**] ESP:0x01c0fcdc
[**] EBP:0x00000000
[**] EAX:0x77a6f125
[**] EBX:0x00000000
[**] ECX:0x00000000
[**] EDX:0x00000000
[*] END DUMP
[*] Finished debugging. Exiting...
레지스터 상태 캡쳐성공