承接上一篇,进程调试基础1
第二种方法:将调试附加到一个正在运行的进程。
1 - 要附加到指定的进程,需要先获得他的句柄。这个任务由OpenProcess()
完成,次函数由kernel32.dll
库导出。原型如下
HANDLE WINAPI OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle
DWORD dwProcessId
);
dwDesiredAccess
:参数决定我们希望对将要打开的进程拥有什么样的权限
因为要执行调试,我们设置成PROCESS_ALL_ACCESS,即所有权限
bInheritHandle
:参数设置成False
dwProcessId
:参数设置成我们希望获得句柄的进程ID,即PID。
如果函数执行成功,将返回一个目标进程的句柄。
2 - 获得目标进程的句柄后,接着调用DebugActiveProcess()
函数附加到目标进程:
函数原型:
BOOL WINAPI DebugActiveProcess(
DWORD dwProcessId
)
注意,此处的参数仍为调试程序的PID,一旦系统认为我们有权限访问目标进程,目标进程就假定我们的调试器已经准备好处理调试事件,然后把进程的控制权转移给调试器。
3 - 调试器接着循环调用WaitDebugEvent()
以便俘获调试事件。
函数原型:
BOOL WINAPI WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent,
DWORD dwMilliseconds
);
lpDebugEvent
:指向DEBUG_EVENT结构体,这个结构描述了一个调试事件。
dwMilliseconds
:设置成INFINITE,表示无限等待,这样WaitForDebugEvent()
就不用返回,一直等待直到一个事件产生。
调试器捕获的每一个事件都有相关联的事件处理函数,在程序继续执行前可以完成不同的操作。
4 - 当处理函数完成了操作,我们希望进程继续执行,这时候调用ContinueDebugEvent()
原型如下
BOOL WINAPI ContinueDebugEvent(
DWORD dwProcessId,
DWORD dwThreadId,
DWORD dwContinueStatus
);
dwProcessId
和dwThreadId
参数有DEBUG_EVENT结构里的数据填充。
当调试器捕捉到调试事件的时候,也就是WaitForDebugEvent()
成功执行的时候,进程ID和线程ID就已经初始化好了。
dwContinueStatus
:参数高速进城是继续执行(DBG_CONTINUE),还是产生异常(DBG_EXCEPTION_NOT_HANDLED)
5 - 调试结束后,我们需要把调试从进程中分离出来,也就是把进程ID传递给函数DebugActiveProcessStop()
。
程序创建
先说功能:
通过上面对附加进程原理的理解,我们要完成将调试附加和分离一个进程的功能。同时,加上打开一个进程和获取进程句柄的能力。最后我们在主循环里完成事件处理函数。
1 - 功能函数
#my_debugger.py
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 = None
#打开进程
def open_process(self,pid):
h_process = kerne32.OpenProcess(PROCESS_ALL_ACCESS,pid,False)
return h_prcess
#附加到进程,同时获取进程句柄
def attach(self,pid):
self.h_process = self.open_process(pid)
if kernel32.DebugActiveProcess(pid):
self.debugger_active = True
self.pid = int(pid)
self.run()
else:
print"[*]Unable 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):
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
2 - 常量定义
常量和结构体定义在后续的文章中也都有用到,这里单用一片文章进行说明
详细定义参考my_debugger_defines.py
3 - 测试程序
#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.detach()
windows按一下步骤进行测试:
(1)打开一个程序,如计算器
(2)通过任务管理器查看该程序的pid(任务管理器-详细信息标签栏可以看到PID)
(3)运行my_test.py
(4)在终端中输入pid
(5)当press a key to continue 打印在屏幕上时,试着操作计算器界面,这时应该是什么都按不了,因为进程被调试器挂起了。
(6)在Phont控制台里按任何键,脚本将输出别的信息,然后退出。
(7)这时计算器可以再次操作了。
测试正常后,把下面两行从 my_debugger.py 中注释掉,如果不注释掉会影响后续程序运行:
# raw_input("Press any key to continue...")
# self.debugger_active = False