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

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

이벤트가 발생했을 때,디버거가 그 이벤트에 반응하기 위해서는 각 디버깅 이벤트에 대한 핸들러를 구현해야 한다.


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;
};

dwDebugEventCode를 통해 발생한 이벤트가 어떤 종류의 이벤트인지 판단한다.



이벤트코드   이벤트 코드 값                                 유니언u의 값      

Ox1         EXCEPTION DEBUG EVENT              u.Exception

Ox2         CREATE THREAD DEBUG EVENT     u.Create Thread

Ox3         CREATE PROCESS DEBUG EVENT    u.CreateProcesslnfo

Ox4         EXIT THREAD DEBUG EVENT     u.ExitThread

Ox5         EXIT PROCESS DEBUG EVENT     u.ExilProcess

Ox6         LOAD DLL DEBUG EVENT     u.LoadDII

Ox7         UNLOAD DLL DEBUG EVENT     u.UnloadDII

Ox8         OUPUT DEBUG STRING EVENT     u.DebugString

Ox9         RIP EVENT                                     u.RipInfo


위 표는 각 이벤트에 어떤 구조체를 사용해야 하는지 보여준다.



현재까지의 소스코드
#-*- 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
		self.h_thread			= None
		self.context  			= None

	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):

			#스레드의 컨텍스트 정보를 구한다.
			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 )

	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

my_test.py
#-*- 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))
debugger.run()
debugger.detach()
실행결과

CREATE_PROCESS_EVENT(0x3)이 가장 먼저 발생되었고 바로 

LOAD_DLL_DEBUG_EVENT(0x6)이 여러번 발생된 것을 알 수 있다. 

그 이후에는 CREATE_THREAD_DEBUG_EVENT(0x2)와 EXCEPTION_DEBUG_EVENT(0x1)이 발생되었다.

EXCEPTION_DEBUG_EVENT(0x1)은 윈도우가 처리하는 이벤트로서 디버거가 해당프로세스를 계속 실행되게 만들기 전에 프로세스의 상태정보를 조사할 수 있게 한다. 마지막으로 발생한 EXIT_THREAD_DEBUG_EVENT(0x4)가 발생되어 THREAD가 종료되었다는 것을 알 수 있다.





[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...
레지스터 상태 캡쳐성공

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

Posted by dw0rdptr
2015. 1. 18. 21:53 Study/파이썬 해킹 프로그래밍

앞서 CreateProcessA 함수를 이용해 디버거로 직접 프로세스를 실행시켜 보았다. 

이번엔 두번째 방법인 디버거를 실행중인 프로세스에 붙이는(attach) 방법이다.


*-------------------------------------------------

my_debugger_defines .py

이 책에서는 앞에서 짠 my_debugger_defines.py 외 추가적인 구조체,유니온,상수의 정의를 설명하지 않기 때문에

앞으로는 필요한 모든 정의가 되어있는 위의 파일을 쓴다.

*-------------------------------------------------


프로세스에 붙이기 위에선 먼저 해당 프로세스의 핸들을 구하여야 한다. 이 글에서 쓰일 함수들은 대부분

핸들을 필요로 한다.

프로세스 핸들을 얻기 위해 사용하는 함수는 OpenProcess()이다.

#OpenProcess() 프로토타입
#kernel32.dll에서 익스포트
HANDLE WINAPI OpenProcess(
     DWORD dwDesiredAccess,    #어떤 종류의 접근 권한을 가진 프로세스 핸들을 원하는지 입력
     BOOL bInheritHandle             #항상 False 값으로 설정
     DWORD dwProcessId            #핸들을 얻고자 하는 프로세스의 PID 입력
);

프로세스에 붙일 때는 DebugActiveProcess() 함수 이용
#DebugActiveProcess() 프로토타입
BOOL WINAPI DebugActiveProcess(
       DWORD dwProcessId      #attach할 프로세스이 PID
);

디버거는 WaitForDebugEvent() 함수를 이용해 디버그 이벤트를 처리한다.
#WaitForDebugEvent() 프로토타입
BOOL WINAPI WaitForDebugEvent(
     LPDEBUG_EVENT lpDebugEvent, #DEBUG_EVENT 구조체에 대한 포인터
     DWORD dwMilliseconds       #INFINITE로 설정하면 디버그이벤트가 발생할 때까지 리턴x
);

ContinueDebugEvent()는 각 이벤트 핸들러의 작업이 완료되면 프로세스가 실행을 계속하게 만들어준다.
#ContinueDebugEvent() 프로토타입
BOOL WINAPI ContinueDebugEvent(
        DWORD dwProcessId,
        DWORD dwThreadId,
        DWORD dwContinueStatus
);


아래는 위 함수들을 이용해 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





아래는 테스트를 위한 코드
#my_test.py

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()

계산기를 실행시킨 후 작업관리자를 열어 PID를 확인한다. 현재 PID는 536. 

이제 python으로 my_test.py를 실행시켜보자


Enter the PID of the process to attach to: 536 

Press a key to continue... 


위처럼 떴을 때 계산기의 GUI를 확인해보면 현재 프로세스가 일시정지되어 있는 상태이기 때문에

버튼이나 메뉴를 눌러도 반응이 없는것을 알 수 있다. 엔터를 누르면 종료된다.