逆向开发经常有这个需求:拦截局部数据。一般的做法是hook,通过修改代码的流程来获得数据。还有一种办法是下断点调试。这里聊聊怎样通过下断点来拦截局部数据。
零、原理简介
下断点拦截数据,原理就是对目标进程进行下断点调试。
设置断点的原理就是在某个地址写入0xCC。对某个地址设置断点之后,当被调试的目标进程运行到这个地址,就会报错抛出异常给我们的调试进程。我们的调试进程就可以拿到当前的环境数据。
拿到数据之后,就要让目标进程继续正常执行了,这时候是先把0xCC恢复成原本的内容;然后EIP减一,让当前的指令重新正常执行;
但这个时候断点已经恢复,想重复拦截数据的话就需要重新设置断点:设置单步标志,当前的指令重新正常执行之后,触发单步异常,这个时候再重新下断点就可以了。
一、调试进程
// 被调试的进程ID
static DWORD g_processID = 0;
// 将进程改为被调试状态
DebugActiveProcess(g_processID);
// 退出调试的时候,不关闭被调试进程
BOOL kRet = DebugSetProcessKillOnExit(false);
二、设置断点
// 保存断点地址和对应的内存内容,用于恢复断点
static map<DWORD, BYTE> g_mBpAddress2Content;
// 添加断点,原理就是写入0xCC,程序运行到这里,会触发异常
BOOL CBreakPointHelper::AddBreakPoint(DWORD address)
{
BYTE content;
SIZE_T bytesRead;
BOOL rRet = ReadProcessMemory(g_hProcess, (LPCVOID)address, &content, 1, &bytesRead);
BYTE intInst = 0xCC;
SIZE_T byteWriten;
BOOL wRet = WriteProcessMemory(g_hProcess, (LPVOID)address, &intInst, 1, &byteWriten);
if (wRet) g_mBpAddress2Content[address] = content;
return rRet && wRet;
}
// 删除断点,原理就是把原本的内存内容写回去
BOOL CBreakPointHelper::DelBreakPoint(DWORD address)
{
if (g_mBpAddress2Content.count(address) <= 0) return false;
SIZE_T byteWriten;
BYTE content = g_mBpAddress2Content[g_resetUserBpAddress];
WriteProcessMemory(g_hProcess, (LPVOID)(address), &(content), 1, &byteWriten);
return true;
}
三、监听消息
DEBUG_EVENT debugEvent;
while (WaitForDebugEvent(&debugEvent, INFINITE))
{
switch (debugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
OnException(&debugEvent);
break;
}
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
}
四、处理异常
void CBreakPointHelper::OnException(const DEBUG_EVENT* pEvent)
{
const EXCEPTION_DEBUG_INFO* pInfo = &(pEvent->u.Exception);
switch (pInfo->ExceptionRecord.ExceptionCode)
{
case EXCEPTION_BREAKPOINT: // 断点
OnBreakPoint(pEvent);
break;
case EXCEPTION_SINGLE_STEP: // 单步
OnSingleStep(pEvent);
break;
}
}
void CBreakPointHelper::OnBreakPoint(const DEBUG_EVENT* pEvent)
{
// 获取当前地址
const EXCEPTION_DEBUG_INFO* pInfo = &(pEvent->u.Exception);
DWORD address = (DWORD)pInfo->ExceptionRecord.ExceptionAddress;
g_resetUserBpAddress = address;
DelBreakPoint(address);//删除断点,当前指令恢复正常
// 获取环境
CONTEXT context;
context.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &context);
// 拦截数据
BreakPointCB(address, &context);
// 修改环境
context.Eip -= 1; // eip减一,重新执行当前指令
context.EFlags |= 0x100; // 设置单步标记, 用于恢复断点
SetThreadContext(hThread, &context);
}
void CBreakPointHelper::OnSingleStep(const DEBUG_EVENT* pEvent)
{
// 恢复断点
AddBreakPoint(g_resetUserBpAddress);
}