[컴][디버그] windows 에서 process 에 debugging 을 위해 attach 하기 - WaitForDebugEvent

attach to the process

OpenProcess 로 handle 을 가져오고, DebugActiveProcess(pid) 로 process 에  attach 한다.
WaitForDebugEvent() 로 break 가 걸리면, process 가 멈추고, 이 함수에 lpDebugEvent 로 debug event 를 넘겨준다.
DebugActiveProcessStop() 은 detach 를 할 때 쓰인다.

아래는 PyDbg 를 이용한 source code 이다. 이미 실행되어진 process 의 pid 를 task-manager 로 알아낸 후에 입력을 해주면, 그 process 로 attach 를 하는 코드이다.
if kernel32.DebugActiveProcess(pid):
    self.debugger_active = True
    self.pid = int(pid)
    self.run() 
    
    while self.debugger_active == True:
        
        debug_event = DEBUG_EVENT()
        continue_status = DBG_CONTINUE

        if kernel32.WaitForDebugEvent(byref(debug_event), INFINTE):
            raw_input("press a key to continue...")
            self.debugger_active = False
            kernel32.ContinuewDebugEvent(\
                debug_event.dwProcessId,\
                debug_event.dwThreadId,\
                continue_status)

                
if kernel32.DebugActiveProcessStop(self.pid):
    print "[*] Finished debugging. Exiting..."
    return True
else:
    print "There was an error"
    return False




HANDLE WINAPI OpenProcess(
  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  DWORD dwProcessId
);
 
BOOL WINAPI DebugActiveProcess(
  _In_  DWORD dwProcessId
);
BOOL WINAPI WaitForDebugEvent(
  _Out_  LPDEBUG_EVENT lpDebugEvent,
  _In_   DWORD dwMilliseconds // INFINITE or maximum wait time
);


BOOL WINAPI ContinueDebugEvent(
  _In_  DWORD dwProcessId,
  _In_  DWORD dwThreadId,
  _In_  DWORD dwContinueStatus
);
BOOL WINAPI DebugActiveProcessStop(
  _In_  DWORD dwProcessId
);


DEBUG_EVENT structure


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;
} DEBUG_EVENT, *LPDEBUG_EVENT;

dwDebugEventCode : 어떤 종류의 event 로 인해 멈추게 되었는지 알려준다.

그리고 이 dwDebugEventCode 가 갖는 값에 따라 union 의 값도 결정된다. 예를 들면, dwDebugEventCode 가
EXCEPTION_DEBUG_EVENT (0x1) -> u.Exception, EXCEPTION_DEBUG_INFO structure.
CREATE_THREAD_DEBUG_EVENT (0x2) -> u.CreateThread, CREATE_THREAD_DEBUG_INFO structure.
이런 식이 된다.


Test

아래 debuggee_process.py 를 실행한 상태에서 debugger.py 를 실행해서 debuggee process 에 attach 를 하게 되면, system 에서 debug event 를 보내주게 된다. 이 debug event 들을 debugger 에서는 보여주게 되어 있고, 이중에 printf 함수가 실행될 때 printf 함수의 주소를 출력해 준다.

LOAD_DLL_DEBUG_EVENT


처음에 attach를 하게 되면 LOAD_DLL_DEBUG_EVENT 가 화면에 찍히게 된다. 이 이벤트가 이미 dll 이 load 가 끝난상태인데 왜 찍힐 까 생각했는데, ref. 1 에서 아래처럼 얘기하고 있다.

이 event 는 debuggee process(디버그 하고 있는 process) 가 LoadLibrary() 를 실행할 때에 발생하기도 하지만, PE loader 가 dll library 의 link 를 파악할 때도 발생한다.


그래서 아마도 attach 할 때 dll library 의 link 를 파악하는 과정이 있어서 event 가 발생하는 듯 하다.

Debug event 에 대한 설명은 ref. 1 을 참조하도록 하자.


debugger.py
#source from Gray Hat Python
#

memory_breakpoints = {}
kernel32 = windll.kernel32
pid = raw_input("Enter the PID of the process to attach to: ")

# attach
kernel32.DebugActiveProcess(int(pid))

# func_resolve
dll = "msvcrt.dll"
function = "printf"

handle  = kernel32.GetModuleHandleA(dll)
address = kernel32.GetProcAddress(handle, function)

kernel32.CloseHandle(handle)



# bp_set_mem
mbi = MEMORY_BASIC_INFORMATION()

size = 10
memory_breakpoints[address] = (address, size, mbi)



# run
debug_event    = DEBUG_EVENT()
continue_status = DBG_CONTINUE

while True :

    if kernel32.WaitForDebugEvent(byref(debug_event),100):
        # grab various information with regards to the current exception.
        thread_id = debug_event.dwThreadId
        h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)
        
        context = CONTEXT()
        context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
                        
        context = kernel32.GetThreadContext(h_thread, byref(context))

        
        
                   
        print "Event Code: %d Thread ID: %d" % \
            (debug_event.dwDebugEventCode,debug_event.dwThreadId)
        
        if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
            exception = debug_event.u.Exception.ExceptionRecord.ExceptionCode
            exception_address = debug_event.u.Exception.ExceptionRecord.ExceptionAddress
            
            # call the internal handler for the exception event that just occured.
            if exception == EXCEPTION_ACCESS_VIOLATION:
                print "Access Violation Detected."

            elif exception == EXCEPTION_BREAKPOINT:
                print "[*] Exception address: 0x%08x" % exception_address

                # check if the breakpoint is one that we set
                if not memory_breakpoints.has_key(exception_address):
                    continue_status = DBG_CONTINUE

            elif exception == EXCEPTION_GUARD_PAGE:
                print "Guard Page Access Detected."

            elif exception == EXCEPTION_SINGLE_STEP:
                exception_handler_single_step()
            
        kernel32.ContinueDebugEvent(debug_event.dwProcessId,\
                                 debug_event.dwThreadId, continue_status)

debugee_process.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


INT3 의 동작


INT3 interrupt 가 걸려서 WaitForDebugEvent() 에 control 을 넘겨주는 과정은 아래 경로를 참고하면 조금 이해가 될 것이다.

http://i5on9i.blogspot.kr/2013/04/int-3.html


References

  1. Debugging a Running Process
  2. DBG_CONTINUE vs. DBG_EXCEPTION_HANDLED
  3. http://win32assembly.programminghorizon.com/tut28.html

댓글 없음:

댓글 쓰기