반응형

 

디버기를 만드는 과정에서 호출 하는 py코드를 공유한다.

 

my_loader.py 

# -*- coding: euc-kr -*-
import my_debugger
import sys

debugger = my_debugger.debugger()

pid = input("\nEnter The PID of the process to attach to : ")

if (pid == ""):
    print ("[#] Warning Please Enter PID Number")
    sys.exit()

print ("[*] Select PID %d" % int(pid))

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 ("[*] RIP : 0x{0:016x}" .format(thread_context.Rip))
    print ("[*] RSP : 0x{0:016x}" .format(thread_context.Rsp))
    print ("[*] RBP : 0x{0:016x}" .format(thread_context.Rbp))
    print ("[*] RAX : 0x{0:016x}" .format(thread_context.Rax))
    print ("[*] RBX : 0x{0:016x}" .format(thread_context.Rbx))
    print ("[*] RCX : 0x{0:016x}" .format(thread_context.Rcx))
    print ("[*] RDX : 0x{0:016x}" .format(thread_context.Rdx))
    print ("[*] End DUMP")


debugger.detach() 

 

 my_debugger.py

 # -*- coding: euc-kr -*-

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

    def load(self, path_to_exe):

        # dwCreation 플래그를 이용해 프로세스를 어떻게 생성할 것인지 판단한다.
        # 계산기의 GUI를 보고자 한다면 creation)flags를 CREATE_NEW_CONSOLE로 설정하면 된다.
        # creation_flags = CREATE_NEW_CONSOLE
        # DEBUG_PROCESS로 넣을 경우 메모리에 올라갔다가 내려 간다.
        creation_flags = DEBUG_PROCESS
       
       
        # 구조체 인스턴스화
        startupinfo = STARTUPINFO()
        process_information = PROCESS_INFORMATION()
       
        # 다음의 두 옵션은 프로세스가 독립적인 창으로 실행되게 만들어준다.
        # 이는 STARTUPINFOR 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 ("\n[*] We have successfully launched the process!")
            print ("[*] PID : %d" % process_information.dwProcessId)
       
        else:
            print ("[*] Error: 0x%08x." % kernel32.GetLastError())
    
    def open_process(self, pid):
       
        h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
        print ("[*] OpenProcess Handle : %d" % h_process)
        return h_process
   
    def attach(self, pid):
       
        self.h_process = self.open_process(pid)
        print ("[*] self.h_process : %d" % self.h_process)
       
        # 프로세스에 대한 어태치를 시도한다.
        # 실패하면 호출을 종료한다.
       
        if kernel32.DebugActiveProcess(pid):
            self.debugger_active = True
            self.pid = int(pid)
        else:
            print ("[*] Error: 0x%08x." % kernel32.GetLastError())
            print ("[*] Unable to attach to the process.")
            exit
       
    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
       
        event_group = {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:"OUPUT_DEBUG_STRING_EVENT",
                       9:"RIP_EVENT"
                       }
       
        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 (%s)\t[##] thread ID: %d" % (debug_event.dwDebugEventCode,
                                                                     event_group.get(debug_event.dwDebugEventCode),
                                                                     debug_event.dwThreadId)
          
            # 발생한 이벤트의 종류가 예외 이벤트이면 그것을 좀 더 자세하게 출력한다.
            if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
                # 예외코드 구하기
                exception = debug_event.u.Exception.ExceptionRecord.ExceptionCode
                self.exception_address = debug_event.u.Exception.ExceptionRecord.ExceptionAddress
           
                if exception == EXCEPTION_ACCESS_VIOLATION:
                    print "Access Violation Detected."
           
                #브레이크 포인트인 경우에는 내부 핸들러를 호출한다.
                elif exception == EXCEPTION_BREAKPOINT:
                    continue_status = self.exception_handler_breakpoint()
           
                elif exception == EXCEPTION_GUARD_PAGE:
                    print "Guard Page Access Detected."
           
                elif 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)
       
        print ("[*] Enumerate Thread inside")
       
        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:
            h_thread = self.open_thread(thread_id)
      
        # 스페드의 핸들을 구한다.
        if kernel32.GetThreadContext(h_thread, byref(context)):
            kernel32.CloseHandle(h_thread)
            # 64bit의 경우 확장이기 때문에 변환을 해야 함.
            kernel32.RtlCaptureContext(byref(context))
           
            return context
        else:
            #print "[##] thread_id(0x%x) GetThreadContext False" % (thread_id)
            return False
       

    def exception_handler_breakpoint(self):
        print "[*] Inside the breakpoint handler."
        print "Exception Address : 0x%08x" % self.exception_address
       
        return DBG_CONTINUE

 

my_debugger_defines.py

 

from ctypes import *

# Let's map the Microsoft types to ctypes for clarity
BYTE      = c_ubyte
WORD      = c_ushort
DWORD     = c_ulong
DWORD64   = c_ulonglong
LPBYTE    = POINTER(c_ubyte)
LPTSTR    = POINTER(c_char)
HANDLE    = c_void_p
PVOID     = c_void_p
LPVOID    = c_void_p
UINT_PTR  = c_ulong
SIZE_T    = c_ulong

# Constants
DEBUG_PROCESS         = 0x00000001
CREATE_NEW_CONSOLE    = 0x00000010
PROCESS_ALL_ACCESS    = 0x001F0FFF
INFINITE              = 0xFFFFFFFF
DBG_CONTINUE          = 0x00010002


# Debug event constants
EXCEPTION_DEBUG_EVENT      =    0x1
CREATE_THREAD_DEBUG_EVENT  =    0x2
CREATE_PROCESS_DEBUG_EVENT =    0x3
EXIT_THREAD_DEBUG_EVENT    =    0x4
EXIT_PROCESS_DEBUG_EVENT   =    0x5
LOAD_DLL_DEBUG_EVENT       =    0x6
UNLOAD_DLL_DEBUG_EVENT     =    0x7
OUTPUT_DEBUG_STRING_EVENT  =    0x8
RIP_EVENT                  =    0x9

# debug exception codes.
EXCEPTION_ACCESS_VIOLATION     = 0xC0000005
EXCEPTION_BREAKPOINT           = 0x80000003
EXCEPTION_GUARD_PAGE           = 0x80000001
EXCEPTION_SINGLE_STEP          = 0x80000004


# Thread constants for CreateToolhelp32Snapshot()
TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPPROCESS  = 0x00000002
TH32CS_SNAPTHREAD   = 0x00000004
TH32CS_SNAPMODULE   = 0x00000008
TH32CS_INHERIT      = 0x80000000
TH32CS_SNAPALL      = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
THREAD_ALL_ACCESS   = 0x001F03FF

# Context flags for GetThreadContext()
CONTEXT_FULL                    = 0x00010007
CONTEXT_DEBUG_REGISTERS         = 0x00010010

# Memory permissions
PAGE_EXECUTE_READWRITE         = 0x00000040

# Hardware breakpoint conditions
HW_ACCESS                      = 0x00000003
HW_EXECUTE                     = 0x00000000
HW_WRITE                       = 0x00000001

# Memory page permissions, used by VirtualProtect()
PAGE_NOACCESS                  = 0x00000001
PAGE_READONLY                  = 0x00000002
PAGE_READWRITE                 = 0x00000004
PAGE_WRITECOPY                 = 0x00000008
PAGE_EXECUTE                   = 0x00000010
PAGE_EXECUTE_READ              = 0x00000020
PAGE_EXECUTE_READWRITE         = 0x00000040
PAGE_EXECUTE_WRITECOPY         = 0x00000080
PAGE_GUARD                     = 0x00000100
PAGE_NOCACHE                   = 0x00000200
PAGE_WRITECOMBINE              = 0x00000400


# Structures for CreateProcessA() function
# STARTUPINFO describes how to spawn the process
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),
        ]

# PROCESS_INFORMATION receives its information
# after the target process has been successfully
# started.
class PROCESS_INFORMATION(Structure):
    _fields_ = [
        ("hProcess",    HANDLE),
        ("hThread",     HANDLE),
        ("dwProcessId", DWORD),
        ("dwThreadId",  DWORD),
        ]

# When the dwDebugEventCode is evaluated
class EXCEPTION_RECORD(Structure):
    pass
   
EXCEPTION_RECORD._fields_ = [
        ("ExceptionCode",        DWORD),
        ("ExceptionFlags",       DWORD),
        ("ExceptionRecord",      POINTER(EXCEPTION_RECORD)),
        ("ExceptionAddress",     PVOID),
        ("NumberParameters",     DWORD),
        ("ExceptionInformation", UINT_PTR * 15),
        ]

class _EXCEPTION_RECORD(Structure):
    _fields_ = [
        ("ExceptionCode",        DWORD),
        ("ExceptionFlags",       DWORD),
        ("ExceptionRecord",      POINTER(EXCEPTION_RECORD)),
        ("ExceptionAddress",     PVOID),
        ("NumberParameters",     DWORD),
        ("ExceptionInformation", UINT_PTR * 15),
        ]

# Exceptions
class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ("ExceptionRecord",    EXCEPTION_RECORD),
        ("dwFirstChance",      DWORD),
        ]

# it populates this union appropriately
class DEBUG_EVENT_UNION(Union):
    _fields_ = [
        ("Exception",         EXCEPTION_DEBUG_INFO),
#        ("CreateThread",      CREATE_THREAD_DEBUG_INFO),
#        ("CreateProcessInfo", CREATE_PROCESS_DEBUG_INFO),
#        ("ExitThread",        EXIT_THREAD_DEBUG_INFO),
#        ("ExitProcess",       EXIT_PROCESS_DEBUG_INFO),
#        ("LoadDll",           LOAD_DLL_DEBUG_INFO),
#        ("UnloadDll",         UNLOAD_DLL_DEBUG_INFO),
#        ("DebugString",       OUTPUT_DEBUG_STRING_INFO),
#        ("RipInfo",           RIP_INFO),
        ]  

# DEBUG_EVENT describes a debugging event
# that the debugger has trapped
class DEBUG_EVENT(Structure):
    _fields_ = [
        ("dwDebugEventCode", DWORD),
        ("dwProcessId",      DWORD),
        ("dwThreadId",       DWORD),
        ("u",                DEBUG_EVENT_UNION),
        ]

# Used by the CONTEXT structure
class FLOATING_SAVE_AREA(Structure):
   _fields_ = [
  
        ("ControlWord", DWORD),
        ("StatusWord", DWORD),
        ("TagWord", DWORD),
        ("ErrorOffset", DWORD),
        ("ErrorSelector", DWORD),
        ("DataOffset", DWORD),
        ("DataSelector", DWORD),
        ("RegisterArea", BYTE * 80),
        ("Cr0NpxState", DWORD),
]

# The CONTEXT structure which holds all of the
# register values after a GetThreadContext() call
class CONTEXT(Structure):
    _fields_ = [
   
        ("ContextFlags", DWORD),
        ("Dr0", DWORD),
        ("Dr1", DWORD),
        ("Dr2", DWORD),
        ("Dr3", DWORD),
        ("Dr6", DWORD),
        ("Dr7", DWORD),
        ("FloatSave", FLOATING_SAVE_AREA),
        ("SegGs", DWORD),
        ("SegFs", DWORD),
        ("SegEs", DWORD),
        ("SegDs", DWORD),

# 64bit Register
        ("Rdi", DWORD64),
        ("Rsi", DWORD64),
        ("Rbx", DWORD64),
        ("Rdx", DWORD64),
        ("Rcx", DWORD64),
        ("Rax", DWORD64),
        ("Rbp", DWORD64),
        ("Rip", DWORD64),
        ("Rsp", DWORD64),
       
        ("SegCs", DWORD),
        ("EFlags", DWORD),
        ("SegSs", DWORD),
        ("ExtendedRegisters", BYTE * 512),
]

# THREADENTRY32 contains information about a thread
# we use this for enumerating all of the system threads

class THREADENTRY32(Structure):
    _fields_ = [
        ("dwSize",             DWORD),
        ("cntUsage",           DWORD),
        ("th32ThreadID",       DWORD),
        ("th32OwnerProcessID", DWORD),
        ("tpBasePri",          DWORD),
        ("tpDeltaPri",         DWORD),
        ("dwFlags",            DWORD),
    ]

# Supporting struct for the SYSTEM_INFO_UNION union
class PROC_STRUCT(Structure):
    _fields_ = [
        ("wProcessorArchitecture",    WORD),
        ("wReserved",                 WORD),
]


# Supporting union for the SYSTEM_INFO struct
class SYSTEM_INFO_UNION(Union):
    _fields_ = [
        ("dwOemId",    DWORD),
        ("sProcStruc", PROC_STRUCT),
]
# SYSTEM_INFO structure is populated when a call to
# kernel32.GetSystemInfo() is made. We use the dwPageSize
# member for size calculations when setting memory breakpoints
class SYSTEM_INFO(Structure):
    _fields_ = [
        ("uSysInfo", SYSTEM_INFO_UNION),
        ("dwPageSize", DWORD),
        ("lpMinimumApplicationAddress", LPVOID),
        ("lpMaximumApplicationAddress", LPVOID),
        ("dwActiveProcessorMask", DWORD),
        ("dwNumberOfProcessors", DWORD),
        ("dwProcessorType", DWORD),
        ("dwAllocationGranularity", DWORD),
        ("wProcessorLevel", WORD),
        ("wProcessorRevision", WORD),
]

# MEMORY_BASIC_INFORMATION contains information about a
# particular region of memory. A call to kernel32.VirtualQuery()
# populates this structure.
class MEMORY_BASIC_INFORMATION(Structure):
    _fields_ = [
        ("BaseAddress", PVOID),
        ("AllocationBase", PVOID),
        ("AllocationProtect", DWORD),
        ("RegionSize", SIZE_T),
        ("State", DWORD),
        ("Protect", DWORD),
        ("Type", DWORD),

 

나도 인터넷에서 떠돌아다니는 것을 모으고 수정한 것이니,

참고해서 사용하기 바란다.

 

출력 형태

 

Enter The PID of the process to attach to : 7320
[*] Select PID 7320
[*] OpenProcess Handle : 536
[*] self.h_process : 536
[*] Enumerate Thread inside
[*] Dumping registers for Thread ID: 0x000015a8
[*] RIP : 0x00000000025d1588
[*] RSP : 0x000000000021e7f0
[*] RBP : 0x000000000021e5d0
[*] RAX : 0x000000000021e800
[*] RBX : 0x0000000000001100
[*] RCX : 0x00000000025d3128
[*] RDX : 0x000000000021e5d0
[*] End DUMP
[*] Dumping registers for Thread ID: 0x00001d6c
[*] RIP : 0x00000000025d1588
[*] RSP : 0x000000000021e7f0
[*] RBP : 0x000000000021e5d0
[*] RAX : 0x000000000021e800
[*] RBX : 0x0000000000001100
[*] RCX : 0x000000007301692d
[*] RDX : 0x000000000021e5d0
[*] End DUMP
[*] Dumping registers for Thread ID: 0x00001ca0
[*] RIP : 0x00000000025d1588
[*] RSP : 0x000000000021e7f0
[*] RBP : 0x000000000021e5d0
[*] RAX : 0x000000000021e800
[*] RBX : 0x0000000000001100
[*] RCX : 0x000000007301692d
[*] RDX : 0x000000000021e5d0
[*] End DUMP
[*] Dumping registers for Thread ID: 0x00000fa0
[*] RIP : 0x00000000025d1588
[*] RSP : 0x000000000021e7f0
[*] RBP : 0x000000000021e5d0
[*] RAX : 0x000000000021e800
[*] RBX : 0x0000000000001100
[*] RCX : 0x000000007301692d
[*] RDX : 0x000000000021e5d0
[*] End DUMP
[*] Dumping registers for Thread ID: 0x0000218c
[*] RIP : 0x00000000025d1588
[*] RSP : 0x000000000021e7f0
[*] RBP : 0x000000000021e5d0
[*] RAX : 0x000000000021e800
[*] RBX : 0x0000000000001100
[*] RCX : 0x000000007301692d
[*] RDX : 0x000000000021e5d0
[*] End DUMP
[*] finished debugging. Exiting...
done. 

 

반응형

+ Recent posts