[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