对一个进程进行调试,调试器要不然就是装载一个可执行程序然后运行它,要不然就是动态的附加到一个运行的进程。
第一种方法:
就是从调试器本身调用一个程序,即在windows上创建一个进程,可通过调用CreateProcessA()
函数调用,原型如下:
BOOL WINAPI CreateProcessA(
LPCSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
这里我们只关心在调试器中创建一个进程需要注意的几个参数,其他参数可以设置为空值NULL。
lpApplicationName
,lpCommandLine
:这两个函数用于设置需要执行的程序的路径和我们希望传递给程序的参数。
dwCreationFlags
:(创建标记)参数接受一个特定值,表示我们希望程序以被调试的状态启动。
lpStartupInfo
,lpProcessInformation
:这两个参数分别指向两个结构体(STARTUPINFO and PROCESS_INFORMATION),包含了进程如何启动,以及启动后的许多重要信息。
STARTUPINFO:用于在创建子进程时设置各种属性。
PROCESS_INFORMATION:用来在进程创建后接受相关信息,该结构由系统填写。
程序创建
创建两个Python文件my_debugger.py
和my_debugger_defines.py
。
my_debugger.py
用于创建一个debugger()父类并逐渐增加各种调试函数。
my_debugger_deffines.py
用于把所有的结构,联合,常量放到进去以便维护。
#my_debugger_defines.py
from ctypes import *
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
# Constants
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010
# 定义STARTUPINFO结构体
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结构体
class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess", HANDLE),
("hThread", HANDLE),
("dwProcessId", DWORD),
("dwThreadId", DWORD),
]
# my_debugger.py
from ctypes import *
from my_debugger_defines import *
kernel32 = windll.kernel32
class debugger():
def __init__(self):
pass
def load(self,path_to_exe):
# creation_flags决定如何创建进程
# 如果希望看到GUI界面
# 可以把creation_flages的值设置为CREATE_NEW_CONSOLE
creation_flags = DEBUG_PROCESS
# 实例化结构体
startupinfo = STARTUPINFO()
process_information = PROCESS_INFORMATION()
# 下面两项配置允许开启的进程以一个独立的窗口显示
startupinfo.dwFlags = 0x1
startupinfo.wShowWindow = 0x0
# 初始化cb属性,其大小就是startupinfo结构体的大小
startupinfo.cb = sizeof(startupinfo)
#调用kernel32.CreateProcessA()函数创建调试进程
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
else:
print "[*] Error: 0x%08x." % kernel32.GetLastError()
测试程序
有了上面调试器类的定义和结构体以及常量的定义,现在可以写一个测试程序测试我们的调试器
#my_test.py
import my_debugger
debugger = my_debugger.debugger()
debugger.load("C:\\WINDOWS\\system32\\calc.exe")
以上程序很简单,实例化调试器后,创建了calc.exe的调试进程,这时我们在终端界面应该能看到创建进程的PID,并且在资源管理器中也可以看到该调试程序。