要想调试一个程序, 就需要与目标程序建立某种联系, 为此, 我们的调试器应该具备两种基本能力:
- 打开一个程序, 并使它以自身子进程的形式运行起来
- 附加到一个正在进去的程序上
1. 创建子进程
使用 CreateProcessA() 来实现就可以了.
这个函数其实已经很熟悉了, 回顾一下吧:
BOOL CreateProcess (
LPCTSTR lpApplicationName, // 程序路径
LPTSTR lpCommandLine, // 命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags, // 创建的标志
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo, // 指向一个用于决定新进程的主窗体如何显示的 STARTUPINFO 结构体
LPPROCESS_INFORMATION lpProcessInformation // 指向一个用来接收新进程的识别信息的 PROCESS_INFORMATION 结构体
);
基本上, 我们只要关心 lpApplicationName、lpCommandLine、dwCreationFlags、lpStartupInfo 和 lpProcessInformation 这五个参数, 其它的设为 NULL即可.
其中通过给 dwCreationFlags 参数设定一个指定的值, 那么跑起来的程序就有了可调试权限.
现在我们来通过 python 来运行一个新进程.
首先新建两个 py 文件: my_debugger.py 和 my_debugger_defines.py
my_debugger_defines.py 保存的是程序常量及结构定义的地方.
my_debugger.py 是核心代码实现的地方.
先来定义我们程序所需要的数据结构:
# -*- coding: utf-8 -*-
from ctypes import *
# 统一命名风格
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPSTR = c_char_p
LPWSTR = c_wchar_p
HANDLE = c_void_p
DEBUG_PROCESS = 0x00000001 # 与父进程共用一个控制台(以子进程的方式运行)
CREATE_NEW_CONSOLE = 0x00000010 # 独占一个新控制台(以单独的进程运行)
# 定义 CreateProcessA() 所需的结构体
class STARTUPINFOA(Structure):
_fields_ = [
('cb', DWORD),
('lpReserved', LPSTR),
('lpDesktop', LPSTR),
('lpTitle', LPSTR),
('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),
]
class PROCESS_INFORMATIONA(Structure):
_fields_ = [
('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', DWORD),
('dwThreadId', DWORD),
]
再来书写核心代码:
# -*- coding: utf-8 -*-