调试器程序框架
这个调试器主要是测试支线软件调试的内容,只是一个具体框架,没有实现很复杂的细节。
实现了:
1)软件断点
在进程开始的位置加的断点用于测试。
2)硬件断点
在软件断点中断后,在其后面的地址又加了硬件断点,因为硬件断点需要线程启动后才能添加,测试时只有在软件断点后确保线程启动了后才加的断点。换成内存断点后面也可以。
3)内存断点
在进程开始的位置加的断点用于测试。
4)单步异常(单步步入)
这个是在用户操作时加的事件。
5)单步步过 – 未实现
DebugTest.h
定义:
#pragma once
#include <iostream>
#include <windows.h>
#include <winbase.h.>
#include <string>
BOOL _bIsSystemInt3 = TRUE;
char _newModCode;
HANDLE _hOpenProcess = NULL;
DWORD _flOldProtect = 0; //保存先前的页属性
DWORD _AddBreakpointAddrecss = 0; //添加内存断点的地址
typedef HANDLE(WINAPI* FnOpenThread) (_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwThreadId);
BOOL ExceptionManipulationFunction(LPDEBUG_EVENT pdebugEvent);
BOOL ExceptionBreakpoint(LPEXCEPTION_DEBUG_INFO pExceptionInfo, HANDLE hThread); //处理软件断点
BOOL VisitAnomalyBreakpoint(LPEXCEPTION_DEBUG_INFO pExceptionInfo, HANDLE hThread); //处理内存断点
BOOL SingleStepExceptionProc(LPEXCEPTION_DEBUG_INFO pExceptionInfo, HANDLE hThread); //处理单步异常
BOOL WaitForUSerCommand(HANDLE hThread); //用户输入事件
BOOL SetProcessOEPINT3(LPDEBUG_EVENT pdebugEvent); //设置软件断点
BOOL SetBreakPoint(LPDEBUG_EVENT pdebugEvent); //设置断点
BOOL SetInternalStorageBreakPointOEP(LPDEBUG_EVENT pdebugEvent); //设置内存断点
BOOL SetHardwareBreakpoint(HANDLE hThread, PVOID pAddress); //添加硬件断点
DebugTest.cpp
实现:
#include <iostream>
#include <windows.h>
#include <winbase.h.>
#include <string>
#include "DebugTest.h"
using namespace std;
int main()
{
BOOL bRet = FALSE;
BOOL bIsContinue = TRUE;
wstring wstrFilePath = L"C:\\Users\\WGH\\Desktop\\Dbgview.exe"; //设置路径
DWORD dwContinueStatus = DBG_CONTINUE;
DEBUG_EVENT debugEvent;
memset(&debugEvent, 0, sizeof(debugEvent));
//创建调试进程
STARTUPINFO StartUpInfo;
memset(&StartUpInfo, 0, sizeof(StartUpInfo));
PROCESS_INFORMATION pInfo;
memset(&pInfo, 0, sizeof(PROCESS_INFORMATION));
GetStartupInfo(&StartUpInfo); //取得进程在启动时被指定的 STARTUPINFO 结构。
bRet = CreateProcessW(wstrFilePath.c_str(), NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &StartUpInfo, &pInfo);
if (bRet == FALSE)
{
printf("CreateProcess Error: %d \n", GetLastError());
}
//附加形式
/*
if (!DebugActiveProcess(21872)) //接受一个进程ID
{
return 0;
}
*/
//保存进程句柄
_hOpenProcess = pInfo.hProcess;
//调试循环
while (bIsContinue)
{
bRet = WaitForDebugEvent(&debugEvent, INFINITE); //等待异常消息
if (bRet == FALSE)
{
printf("WaitForDebugEvent Error: %d \n", GetLastError());
return 0;
}
dwContinueStatus = DBG_CONTINUE;
switch (debugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT : //异常
if (!ExceptionManipulationFunction(&debugEvent))
{
//处理失败
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
}
break;
case CREATE_THREAD_DEBUG_EVENT: //创建线程
break;
case CREATE_PROCESS_DEBUG_EVENT: //创建进程
SetBreakPoint(&debugEvent);
break;
case EXIT_THREAD_DEBUG_EVENT: //退出线程
break;
case EXIT_PROCESS_DEBUG_EVENT: //退出进程
break;
case LOAD_DLL_DEBUG_EVENT: //加载DLL
break;
case UNLOAD_DLL_DEBUG_EVENT: //卸载DLL
break;
}
//DBG_CONTINUE, 表示编译器已经处理了改异常
//DBG_EXCEPTION_NOT_HANDLED, 即表示调试器没有处理该异常,转回到用户态中执行,寻找可以处理该异常的异常处理器
bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, dwContinueStatus); //调试事件处理完毕
if (bRet == FALSE)
{
printf("ContinueDebugEvent Error: %d \n", GetLastError());
}
}
CloseHandle(pInfo.hThread);
CloseHandle(pInfo.hProcess);
return 0;
}
BOOL ExceptionManipulationFunction(LPDEBUG_EVENT pdebugEvent)
{
BOOL bRetu = TRUE;
HANDLE hThread = NULL;
LPEXCEPTION_DEBUG_INFO pExceptionInfo = NULL;
pExceptionInfo = &pdebugEvent->u.Exception;
//获线程句柄
FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibraryA("Kernel32.dll"), "OpenThread");
if (MyOpenThread != NULL)
{
hThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pdebugEvent->dwThreadId);
}
switch (pExceptionInfo->ExceptionRecord.ExceptionCode)
{
//INT 3 断点
case EXCEPTION_BREAKPOINT:
bRetu = ExceptionBreakpoint(pExceptionInfo, hThread);
break;
//内存不可访问断点
case EXCEPTION_ACCESS_VIOLATION:
bRetu = VisitAnomalyBreakpoint(pExceptionInfo, hThread);
break;
//单步异常
case EXCEPTION_SINGLE_STEP:
bRetu = SingleStepExceptionProc(pExceptionInfo, hThread);
break;
}
CloseHandle(hThread);
return bRetu;
}
BOOL ExceptionBreakpoint(LPEXCEPTION_DEBUG_INFO pExceptionInfo, HANDLE hThread)
{
BOOL bRet = FALSE;
DWORD dwSize = 0;
DWORD dwOldProt = 0;
DWORD dwNewProt = 0;
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
//判断是否为我们需要的 INT 3指令
if (_bIsSystemInt3) //第一次一般为系统的INT 3 即系统断点
{
_bIsSystemInt3 = FALSE;
return TRUE;
}
//将INT 3修复为原来的数据
if (VirtualProtectEx(_hOpenProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, 1, PAGE_EXECUTE_READWRITE, &dwOldProt))
{
WriteProcessMemory(_hOpenProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &_newModCode, 1, &dwSize);
VirtualProtectEx(_hOpenProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, 1, dwOldProt, &dwNewProt);
}
//显示断点位置
printf("Int 3 Address: 0x%p \r\n", pExceptionInfo->ExceptionRecord.ExceptionAddress);
//获取线程上下文,显示寄存器信息
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &context);
printf("EIP: 0x%p \r\n", context.Eip);
//修复EIP
context.Eip--; //0xcc只占了1个字节
SetThreadContext(hThread, &context);
//添加一个硬件断点
SetHardwareBreakpoint(hThread, (PVOID)((DWORD)(pExceptionInfo->ExceptionRecord.ExceptionAddress) + 5)); //在当前位置的后一个地址下断点
//显示反汇编
//等待用户命令
bRet = WaitForUSerCommand(hThread);
return bRet;
}
BOOL WaitForUSerCommand(HANDLE hThread)
{
BOOL bRetu = FALSE;
string strText;
cin >> strText;
if (strText == "g") //继续执行
{
bRetu = TRUE;
}
else if (strText == "t") //单步执行
{
bRetu = TRUE;
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
//获取线程上下文,显示寄存器信息
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &context);
context.EFlags |= 0x100; //设置 TF = 1
//设置线程上下文
SetThreadContext(hThread, &context);
}
return bRetu;
}
BOOL SetBreakPoint(LPDEBUG_EVENT pdebugEvent)
{
BOOL bRetu = FALSE;
//添加软件断点,将程序断在进程入口点,即在入口位置添加0xcc软件断点
bRetu = SetProcessOEPINT3(pdebugEvent);
//添加内存断点,将内存断点加载程序入口处
//bRetu = SetInternalStorageBreakPointOEP(pdebugEvent);
return TRUE;
}
BOOL SetProcessOEPINT3(LPDEBUG_EVENT pdebugEvent)
{
DWORD dwSize = 0;
HANDLE hThread = NULL;
DWORD dwOldProt = 0;
DWORD dwNewProt = 0;
BOOL bRet = FALSE;
LPCREATE_PROCESS_DEBUG_INFO pProcessInfo = &pdebugEvent->u.CreateProcessInfo;
//写入 int 3
char ModCodeInt3;
ModCodeInt3 = 0xcc;
if (VirtualProtectEx(_hOpenProcess, pProcessInfo->lpStartAddress, 1, PAGE_EXECUTE_READWRITE, &dwOldProt))
{
if (ReadProcessMemory(_hOpenProcess, pProcessInfo->lpStartAddress, &_newModCode, 1, &dwSize))
{
if (WriteProcessMemory(_hOpenProcess, pProcessInfo->lpStartAddress, &ModCodeInt3, 1, &dwSize))
{
bRet = TRUE;
}
}
VirtualProtectEx(_hOpenProcess, pProcessInfo->lpStartAddress, 1, dwOldProt, &dwNewProt);
}
return bRet;
}
BOOL SetInternalStorageBreakPointOEP(LPDEBUG_EVENT pdebugEvent)
{
LPCREATE_PROCESS_DEBUG_INFO pProcessInfo = &pdebugEvent->u.CreateProcessInfo;
//设置不可访问内存断点
VirtualProtectEx(_hOpenProcess, pProcessInfo->lpStartAddress, 1, PAGE_NOACCESS, &_flOldProtect);
//保存加断点的地址,若真正的编译器用链表
_AddBreakpointAddrecss = (DWORD)pProcessInfo->lpStartAddress;
return TRUE;
}
BOOL VisitAnomalyBreakpoint(LPEXCEPTION_DEBUG_INFO pExceptionInfo, HANDLE hThread)
{
BOOL bRet = FALSE;
DWORD dwAddressFlag = 0;
DWORD dwAddressAdd = 0;
DWORD newflOldProtect = 0;
CONTEXT contex;
memset(&contex, 0, sizeof(CONTEXT));
//1)获取异常信息,异常原因,异常地址
dwAddressFlag = pExceptionInfo->ExceptionRecord.ExceptionInformation[0]; //获取标志位
dwAddressAdd = pExceptionInfo->ExceptionRecord.ExceptionInformation[1]; //异常地址
printf("VisitAnomalyBreakpoint: Flag = %x, Address = %x \n", dwAddressFlag, dwAddressAdd); //打印地址
//判断断下的是否是我们下断点的地址,不是则放过,是则处理
if (dwAddressAdd != _AddBreakpointAddrecss)
{
return TRUE;
}
//修改会原来的页属性
VirtualProtectEx(_hOpenProcess, (VOID *)dwAddressAdd, 1, _flOldProtect, &newflOldProtect);
//获取线程上线文:显示寄存器的信息
contex.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &contex);
printf("EIP: 0x%p \r\n", contex.Eip);
//显示反汇编
//等待用户命令
bRet = WaitForUSerCommand(hThread);
return bRet;
}
BOOL SetHardwareBreakpoint(HANDLE hThread, PVOID pAddress)
{
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
//获取线程上下文,显示寄存器信息
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &context);
//设置断点位置
context.Dr0 = (DWORD)pAddress;
context.Dr7 |= 1; //设置为局部断点
//清零
context.Dr7 &= 0xFFF0FFFF; //清零 16 - 19位 LEN0 和 RWE0
//设置断点长度
context.Dr7 |= 0x00000000; //18 - 19 置0 -- 1字节
//contex.Dr7 |= 0x0000C0000; //18 - 19 置0011 -- 4字节
//设置断点类型
context.Dr7 |= 0x00000000; //执行断点(17、18位置零)
//设置线程上下文
SetThreadContext(hThread, &context);
return TRUE;
}
BOOL SingleStepExceptionProc(LPEXCEPTION_DEBUG_INFO pExceptionInfo, HANDLE hThread)
{
BOOL bRet = FALSE;
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
//获取线程上下文,显示寄存器信息
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread, &context);
//判断是否是硬件断点导致的异常
if (context.Dr6 & 0xF)
{
//显示异常信息,因为只设了 DR0, 不然要判断哪个硬件断点引起的异常
printf("Hardware Breakpoint: %x, 0x%p", context.Dr7 & 0x0003000, context.Dr0);
//显示寄存器信息
//显示反汇编
//去掉断点
context.Dr0 = 0;
context.Dr7 &= 0xfffffffc;
}
else
{
//单步断点
printf("SingleStep: Eip = 0x%p \n", context.Eip);
//还原
context.EFlags |= 0xfffffeff; //还原TF位
}
SetThreadContext(hThread, &context);
//等待用户命令
bRet = WaitForUSerCommand(hThread);
return bRet;
}