Python自定义Windows调试器(一)

xiaoxiao2021-02-28  102

目前绝大多数的IDE都包含断点调试的功能,缺少这个,用print来调试代码,简直就是个灾难,下面我们来实现一个简陋版的“断点调试”;

环境:WIN7 x86_64  Python2.7

///了解///

调试类别:白盒调试:源码在手          黑盒调试:软件包

黑盒调试 -> 用户态(ring3);内核态(ring0)

ring3 -> WinDbg;OllyDbg;     GNU(gdb):Linux ...

八个通用寄存器(习惯了32位,64位请自己查询):EAX,EDX.ECX.ESI.EDI.EBP.ESP.EBX

EAX:累加器,常用的运算操作和传递函数调用的返回值

EDX:数据寄存器,可理解为EAX的延伸

ECX:计数器

counter = 0

while counter<5#ECX = 5 每执行一次ECX -= 1 : SUB ECX 1H

print counter

counter+=1

ESI:源变址寄存器,可简单理解为存储输入数据流位置的信息,用于“读”

EDI:用于“写”

ESP:栈指针 指向栈顶(低位)

EBP:栈基指 指向栈底(高位)

EIP*:始终指向cpu当前执行的指令,缓冲区溢出原理就是利用EIP被覆盖,来执行自己构建的代码

断点:软断点 ,硬件断点,内存断点

ctypes 库

kernel32.dll

///开始///

#代码参考

Gray Hat Python: Python Programming for Hackers and Reverse Engineers

# 获取进程的句柄 def open_process(self, pid): h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid) return h_process # 附加到进程pid = pid(某些内核进程无法附加) def attach(self, pid): self.h_process = self.open_process(pid) if kernel32.DebugActiveProcess(pid): self.pid = pid else: print "附加进程失败" ernel32.OpenProcess 返回的是PID进程的句柄 参数: PROCESS_ALL_ACCESS 设置访问目标进程的权限为所有(值为0x001F0FFF) False 表示所得到的进程句柄不能被继承 pid 附加进程PID kernel32.DebugActiveProcess此函数允许将调试器捆绑到一个正在运行的进程上 # 打开线程 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: print "enumerate_threads fail." return False # 获取线程上下文 def get_thread_context(self, thread_id): context = CONTEXT() context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS h_thread = self.open_thread(thread_id) if kernel32.GetThreadContext(h_thread, byref(context)): kernel32.CloseHandle(h_thread) return context else: print "get_thread_context fail" return False

THREADENTRY32结构体为

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

      这个结构体中包含着枚举到的首个线程的相关信息,在调用kernel32.Thread32First()之前要确保成员变量dwSize被正确的初始化, th32ThreadID为当前枚举到线程的TID值,th32OwnerProcessID可以获取当前线程对应的进程PID,通过比对th32OwnerProcessID与 目标进程的PID值,筛选出标识符与之相匹配的线程,由此断定这个线程为我们的目标线程,然后通过Thread32Next()从快照中获取 下一个线程,CreateToolhelp32Snapshot函数为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程[THREAD])建立一个快照[snapshot]。     CONTEXT线程数据结构体为:

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), ("Edi", DWORD), ("Esi", DWORD), ("Ebx", DWORD), ("Edx", DWORD), ("Ecx", DWORD), ("Eax", DWORD), ("Ebp", DWORD), ("Eip", DWORD), ("SegCs", DWORD), ("EFlags", DWORD), ("Esp", DWORD), ("SegSs", DWORD), ("ExtendedRegisters", BYTE * 512), ]

我们所有关心的数据通过这个结构体返回   #放开调试 def detach(self): if kernel32.DebugActiveProcessStop(self.pid): print "[*] Finished debugging. Exiting..." else: print "There was an error" return False  这个结构体中包含着枚举到的首个线程的相关信息,在调用kernel32.Thread32First()之前要确保成员变量dwSize被正确的初始化, th32ThreadID为当前枚举到线程的TID值,th32OwnerProcessID可以获取当前线程对应的进程PID,通过比对th32OwnerProcessID与 目标进程的PID值,筛选出标识符与之相匹配的线程,由此断定这个线程为我们的目标线程,然后通过Thread32Next()从快照中获取 下一个线程,CreateToolhelp32Snapshot函数为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程[THREAD])建立一个快照[snapshot]。          调用 if __name__ == "__main__": pid = raw_input("Enter the PID of the process to attach to:") attach(int(pid)) threadList = enumerate_threads() for thread in threadList: thread_context = get_thread_context(thread) print "[*] Dumping registers for thread ID:0xx" % thread print "[**] EIP:0xx" % thread_context.Eip print "[**] ESP:0xx" % thread_context.Esp print "[**] EBP:0xx" % thread_context.Ebp print "[**] EAX:0xx" % thread_context.Eax print "[**] EBX:0xx" % thread_context.Ebx print "[**] ECX:0xx" % thread_context.Ecx print "[**] EDX:0xx" % thread_context.Edx print "[*] END DUMP" detach()

转载请注明原文地址: https://www.6miu.com/read-31198.html

最新回复(0)