[3주] 04장 - 접근 위반 핸들러

Posted by dw0rdptr
2015. 1. 26. 05:48 Study/파이썬 해킹 프로그래밍

접근 위반은 접근할 권한이 없는 메모리에 접근하려고 하거나 허용되지 않은 방법으로 메모리에 접근하려고 할 때

프로세스 내부에서 발생한다.

PyDbg로 예외와 관련된 정보를 얻을 수 있는 함수들을 사용할 수 있다.


strcpy()함수를 이용해 버퍼오버플로우를 발생시키는 프로그램의 접근 위반을 처리하는 PyDbg 스크립트를 짜보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#buffer_overflow.py
from ctypes import *
 
msvcrt = cdll.msvcrt
 
raw_input("Once the debugger is attached, press any key.")
 
buffer = c_char_p("AAAAA")
 
overflow = "A" * 100
 
msvcrt.strcpy(buffer, overflow)
 
 
cs



이제 접근위반을 처리하는 스크립트를 짠다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#access_violation_handler.py
from pydbg import *
from pydbg.defines import *
 
# Utility libraries included with pydbg
import utils
 
def check_accessv(dbg):
 
    if dbg.dbg.u.Exception.dwFirstChance:
        return DBG_EXCEPTION_NOT_HANDLED
 
    crash_bin = utils.crash_binning.crash_binning()
    crash_bin.record_crash(dbg)
    print crash_bin.crash_synopsis()
 
    dbg.terminate_process()
 
    return DBG_EXCEPTION_NOT_HANDLED
 
pid = raw_input("Enter the Process ID : ")
 
dbg = pydbg()
dbg.attach(int(pid))
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, check_accessv)
dbg.run()
 
 
 
cs


1. buffer_overflow.py 실행
2. PID 확인
3. access_violation_handler.py 실행하고 PID 입력
4. buffer_overflow.py 프로세스가 계속 실행할 수 있도록 엔터키를 누름

결과

1. 접근위반예외를 발생시킨 명령과 그 명령이 속한 모듈을 알려줌

2. 모든 레지스터의 컨텍스트 정보를 보여줌

3. 예외를 발생시킨 명령어 주위에 잇는 명령을 디스어셈블해 보여줌

4. 예외가 발생했을 때 등록되는 SEH(structured exception handling) 핸들러 리스트를 보여줌


[3주] 04장 - 브레이크포인트 확장

Posted by dw0rdptr
2015. 1. 26. 05:17 Study/파이썬 해킹 프로그래밍

PyDbg를 이용하면 사용자 정의 콜백 함수를 구현함으로써

기본적인 이벤트 핸들러를 쉽게 확장시킬 수 있다.


디버깅 대상 프로세스 내부의 이벤트를 실제로 관찰하고 교체해보자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#-*- coding: utf-8 -*-
from pydbg import *
from pydbg.defines import *
 
import struct
import random
 
#사용자 정의 콜백 함수
def printf_randomizer(dbg):
 
    # ESP + 0x8 위치에 있는 counter DWORD 값을 읽어들인다.
    parameter_addr = dbg.context.Esp + 0x4
    counter = dbg.read_process_memory(parameter_addr,4)
 
    #read_process_memory는 패킹된 바이너리 문자열 리턴
    #언어 언팩먼저 수행
    string_addr = struct.unpack("L",counter)[0]
 
    #"Loop iteration %d!\n" 문자열의 길이는 20바이트
    str_len = 20
 
    #문자열의 주소를 알았고, 해당 문자열의 주소에 문자열 사이즈만큼 얻어옴
    counter_string = dbg.read_process_memory(string_addr,int(str_len))
    counter_string = struct.unpack(str(str_len)+"s",counter_string)[0]
 
    #!\n은 필요가 없으므로 뺌
    counter_string = counter_string.split("!\n")[0]
 
    #앞에있는 문자열 버리고 숫자만 가져옴
    counter = counter_string[15:]
    print "Counter : %d" % int(counter)
 
    #랜덤한 숫자 생성, "Loop iteration" 뒤에 넣어줌
    random_counter = str(random.randint(1,100))
 
    #디버깅 대상 프로세스에 임의의 수를 써넣고 프로세스가 계속 실행되게만듬
    dbg.write_process_memory(string_addr + 0x0F, random_counter)
    return DBG_CONTINUE
 
#pydbg 클래스의 인스턴스
dbg = pydbg()
pid = raw_input("Enter the printf_loop.py PID:")
 
#해당 프로세스에 디버거 붙임
dbg.attach(int(pid))
 
 
#printf_randomizer 함수를 콜백함수로 등록하면서 브레이크포인트를 설정
printf_address = dbg.func_resolve("msvcrt","printf")
dbg.bp_set(printf_address, description = "printf_address", handler = printf_randomizer)
 
dbg.run()
 
 
cs


그리고 앞에서 짜놓은 printf_loop.py를 실행하고 printf_random.py에 PID를 써넣자
결과


counter 2까지는 정상적으로 루프를 수행하다가  3~부터 printf_loop의 값이 랜덤하게 나오는 것을 알 수 있다. 


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

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

브레이크포인트 부분을 my_debugger에 확장


#-*- 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
		self.exception 			= None
		self.exception_address  = None
		self.breakpoints		= {}
		self.first_breakpoint 	= True

	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):
		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(h_thread=self.h_thread)
			self.debug_event = debug_event

			print "Event Code: %d Thread Id :%d"%(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"

			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 h_thread is None:
			self.h_thread = self.open_thread(thread_id)

		if kernel32.GetThreadContext(self.h_thread, byref(context)):
			return context
		else:
			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 += 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:])

		kernel32.VirtualProtect(self.h_process, address, 0x40, dwSi)

		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)
				
				#INT3 opcode를 써넣음
				self.write_process_memory(address, "\xCC")
				
				#내부 리스트에 브레이크보인트 등록
				self.breakpoints[address] = (original_byte)
				print self.breakpoints[address]
			except:
				return False

		return True

	def func_resolve(self,dll,function):
		handle = kernel32.GetModuleHandleA(dll)
		address = kernel32.GetProcAddress(handle, function)

		kernel32.CloseHandle(handle)

		return address


루프를 돌면서 printf()함수를 호출하는 테스트 프로그램
#printf_loop.py
from ctypes import *
import time

msvcrt = cdll.msvcrt
counter = 0

while 1:
	msvcrt.printf("Loop iteration %d!\n" % counter)
	time.sleep(2)
	counter +=1


printf() 함수에 브레이크포인트를 테스트 할 수 있게 변경한 테스트프로그램
#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))

printf_address = debugger.func_resolve("msvcrt.dll","printf")

print "[*] Address of printf : 0x%08x" % printf_address

debugger.bp_set(printf_address)

debugger.run()

print_loop.py를 실행시키고 작업관리자로 python.exe 프로세스의 PID를 구한다.
그 후 my_test.py 스크립트를 실행시켜 python.exe의 PID를 입력한다 



결과

Enter the PID of the process to attach to: 3104
[*] Address of printf : 0x7720c5b9
Event Code: 3 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 6 Thread Id :1924
Event Code: 2 Thread Id :3852
Event Code: 1 Thread Id :3852
Exception Address: 0x77a040f0
[+] Hit the first breakpoint.
Event Code: 4 Thread Id :3852


[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를 확인해보면 현재 프로세스가 일시정지되어 있는 상태이기 때문에

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


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

Posted by dw0rdptr
2015. 1. 17. 22:12 Study/파이썬 해킹 프로그래밍

프로세스를 디버깅하려면 해당 프로세스를 연결해야하는데, 두가지 방법이 있다.


첫번째는 디버거가 직접 바이너리를 실행시키는 것이다. 이 경우에는 해당 프로세스의 코드가 실행되기 전에 제어 할 수 있다는 장점이 있으므로 악성코드나 기타 다른 형태의 악의적인 코드를 분석할 때 편리하다.

두번째 방법은 디버거를 이미 실행중인 프로세스에 attach(붙이는) 하는 방법이다. 이는 프로세스가 시작되면서 실행되는 코드를 건너뛸 수 있으며 특정 영역의 코드만을 분석할 수 있다.

윈도우에서는 이 두가지 작업을 쉽게 수행할 수 있게 하는 디버깅 api를 제공한다.

* 구동환경은 윈도우7 32bit


CreateProcessA함수로 계산기(calc.exe)를 열어보자



#my_debugger_defines.py
#-*- coding: utf-8 -*-
from ctypes import *

# ctypes 형태의 타입을 마이크로소프트의 타입으로 매핑
WORD	= c_ushort
DWORD	= c_ulong
LPBYTE	= POINTER(c_ubyte)
LPTSTR	= POINTER(c_char)
HANDLE	= c_void_p

#상수
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010

#CreateProcesssA() 함수를 위한 구조체
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),
	]


class PROCESS_INFORMATION(Structure):
	_fields_ = [
		("hProcess",		HANDLE),
		("hThrea",			HANDLE),
		("dwProcessId",		DWORD),
		("dwThreadId",		DWORD),
	]
구조체,유니온,상수를 미리 정의해놓는다.
#my_debugger.py
#-*- coding: utf-8 -*-
from ctypes import *
from my_debugger_defines import *


kernel32 = windll.kernel32

class debugger():
	def __init__(self):
		pass

	def load(self,path_to_exe):
		#dwCreation 플래그를 이용해 프로세스를 어떻게 생성할 것인지 판단.
		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

		else:
			print "[*] Error : 0x%08x." % kernel32.GetLastError()
#my_test.py
import my_debugger

debugger = my_debugger.debugger()

debugger.load("C:\\Windows\\System32\\calc.exe")

raw_input("")
세 파일을 같은 디렉토리에 두고 my_test.py를 실행하면  
[*] We have successfully launched the process! 
[*] PID : 1588 

calc.exe 프로세스를 열었다. PID는 1588 
하지만 디버거로부터 실행을 계속하게 하는 명령을 받지 못해서 계산기 프로그램의 GUI를 볼수는 없다. 
이부분은 나중에 하자.


두번째 방법인 디버거를 프로세스에 attach하는 방법은 다음 글에서 정리하겠다.




'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[2주] 03장 - 윈도우 디버거 개발 (3)  (0) 2015.01.19
[2주] 03장 - 윈도우 디버거 개발 (2)  (0) 2015.01.18
api 기초  (0) 2015.01.12
[1주] 02장-디버거  (0) 2015.01.12
[1주] 01장 - 개발환경 구축  (0) 2015.01.12

api 기초

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

API(Application Programming Interface) 함수 

윈도우 프로그램을 작성할 수 있도록 윈도우즈에서 지원하는 C 함수들

    - API 함수를 사용하려면 Windows.h를 포함하여야 한다.


핸들(handle)

    - 구체적인 어떤 대상에 붙여진 번호이며 문법적으로는 32비트의 정수값이다.
             *구분을 위해서는 문자열보다 정수를 사용하는 것이 훨씬 더 속도가 빠르기 때문이다.

     - 핸들을 사용하는 이유는 구분을 위한 것이므로 같은 종류의 핸들끼리는 중복되지 않아야 한다.

    - 핸들을 담기 위한 데이터형으로는 HWND, HPEN, HBRUSH, HDC 등이 있다.(모두 부호없는 정수형)

     - 핸들은 단순한 표식이므로 그 실제값을 알 필요는 없다.


변수와 데이터형

- 변수형은 길지도 짧지도 않게 정하며 자주쓰는 변수 형식에 대해선 관습적로 정해진 접두어를 사용한다

접두어         원래말            의미

cb          Count of Bytes        바이트 수

dw            double word        부호없는 long형 정수

h                handle               윈도우, 비트맵, 파일 등의 핸들

sz            Null Terminated    NULL 종료 문자열

w                 Word               부호없는 정수형

i                 Integer              정수형

b                Bool                 논리형


-새로운 변수명을 만들어서 사용하기도 한다


데이터형            의미

BYTE               unsigned char형

WORD              unsigned short형

DWORD            unsigned long형

LONG              long형과 동일

LPSTR             char *와 동일

BOOL              정수형이며 TRUE, FALSE 중 한 값을 가짐







[1주] 02장-디버거

Posted by dw0rdptr
2015. 1. 12. 17:31 Study/파이썬 해킹 프로그래밍

디버거 

디버거는 실행중인 프로세스를 트레이스하거나 동적 분석을 할수 있게 도와준다.

특히 동적분석은 악성코드 조사나 퍼저 적용에 있어 필수적이기 때문에 해커에게 디버거는 매우 중요하다고 볼 수 있다


디버거에는 화이트박스 디버거와 블랙박스 디버거가 있다.

화이트박스 디버거 : 개발플랫폼이나 IDE 등이 개발자가 자신이 작성한 코드를 디버그할 수 있게 자체적으로                             내장하고 있는 디버거이다.

블랙박스 디버거 : 리버스 엔지니어링이나 버그를 찾아내는 등 원본 소스코드가 없는 상태에서 분석을 목적으로                         사용되는 디버거이다.




범용 CPU 레지스터

레지스터는 CPU 내의 작은 저장공간으로 매우 빠르다.x86 아키텍처에서 CPU는                                       다음 8개의 범용 레지스터를 사용한다.


EAX : 산술 연산을 수행하기위해 또는 함수의 리턴값을 전달하기 위해 사용

EDX : 복잡한 연산을 위해 추가적으로 데이터를 저장할때 사용(또는 범용목적의 저장소)

ECX : 반복적으로 수행되는 연산에 주로 사용(미리 반복할 값을 넣어두고 감소시키면서 카운트를 셈)

ESI  : 시작지 인덱스(Source Index)

EDI  : 목적지 인덱스(Destination Index)

EBX : 특정한 목적이 없으므로 사용자가 목적에 맞게 씀

스택 레지스터

EBP : base pointer 레지스터. 스택을 위에서부터 아래로 자란다고 할때, 가장 위쪽이되는 위치를 가리키는 레지스터

ESP : stack pointer레지스터, 스택을 위에서부터 아래로 자란다고 할때, 스택의 가장 낮은 위치를 가리키는 레지스터



스택

스택은 FILO 구조이며 보통 스택을 아래에서부터 쌓아올리는 방식으로 표현하는데, 실제로 스택은 메모리를 효율적으로 쓰기 위해 높은주소부터 낮은주소로 쌓이므로 고드름처럼 자라는 방식으로 이해했다.

예시는 1장에서 함수호출규약을 정리할때 쓴 소스이다


ex) int c_fun(a,b,c){

int a_fun

{
        

         

cdecl 호출규약을 기준으로 파라미터 c,b,a 순서대로 push되고 지역변수 a_fun 을 선언했을때 스택프레임 모습이다.





[1주] 01장 - 개발환경 구축

Posted by dw0rdptr
2015. 1. 12. 16:15 Study/파이썬 해킹 프로그래밍

ctypes?

복잡한 C 데이터 타입을 사용할수 있게하며 로우레벨 함수를 제공하는 강력한 라이브러리 중 하나.


ctypes 라이브러리에서는 cdll(), windll(),oledll() 세 가지 방법으로 동적 라이브러리를 로드할 수 있다. 


* 동적라이브러리 : 다수의 응용 프로그램들이 공유할 수 있는 기능을 별도의 바이너리 파일로 분리해

   프로세스가 실행될 때 해당 프로세스에 동적으로 링크되는 것이다. 윈도우에서는 dll,        

   리눅스에서는 so라 불리고 이들은 모두 외부에 export 함수를 제공한다.


* export : dll이 변수나 함수를 외부로 제공하는 것


cdll() 방식은 표준 cdecl 호출규약을 이용하는 함수를 익스포트하는 라이브러리를 로드하는데 쓰이고

windll()방식은 win32api가 사용하는 stdcall 호출규약을 이용하는 함수를 익스포트하는 라이브러리를 로드


ctypes 라이브러리를 import 한다음, c 런타임함수인 printf()를 이용해보자(c 런타임 라이브러리는 msvcrt.dll)



>>>form ctypes import *

>>>msvcrt = cdll.msvcrt

>>>string = "Hello World!"

>>>msvcrt.printf("%s",string)

Hello World!



함수호출규약 : 함수를 어떻게 호출하는지 방법을정의한것


cdecl 호출규약 : 파라미터(매개변수)를 오른쪽에서 왼쪽방향으로 스택에 push한다.

그리고 함수 호출자가 스택에 push된 파라미터를 정리한다. 대부분의 C 시스템에서

      사용되므로 꼭 알아두자


ex) int c_fun(a,b,c);             *C언어

   

      push c                         *어셈블리 언어

push b

push a

call c_fun

add esp,12            //int형 파라미터가 세개이므로 스택포인터레지스터(esp) 를 12바이트증가



stdcall 호출규약 : cdecl과 마찬가지로 파라미터를 오른쪽에서 왼쪽방향으로 스택에 push

 호출된 함수(예시에선 c_fun 함수)가 리턴하기 전에 스택을 정리한다.


두 호출규약 모두 EAX 레지스터를 이용해 리턴값을 전달한다.


C 데이터 타입


파이썬의 C 데이터 타입 지원으로 C나 C++로 작성된 컴포넌트와의 통합이 가능






'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[2주] 03장 - 윈도우 디버거 개발 (2)  (0) 2015.01.18
[2주] 03장 - 윈도우 디버거 개발 (1)  (0) 2015.01.17
api 기초  (0) 2015.01.12
[1주] 02장-디버거  (0) 2015.01.12
점프투파이썬 정리  (0) 2015.01.12