«
怎样通过下断点来获取局部数据

时间:2021-12-9    作者:范文泉    分类: 编程


逆向开发经常有这个需求:获取局部数据。一般的做法是hook,通过修改代码的流程来获得数据。还有一种办法是下断点调试来获取局部数据。

1、原理简介

下断点获取数据原理就是对目标进程进行下断点调试。

设置断点的原理就是在某个地址写入0xCC。对某个地址设置断点之后,当被调试的目标进程运行到这个地址,就会报错抛出异常给我们的调试进程。我们的调试进程就可以拿到当前的环境数据。

拿到数据之后,就要让目标进程继续正常执行了,这时候是先把0xCC恢复成原本的内容;然后EIP减一,让当前的指令重新正常执行;

但这个时候断点已经恢复,想重复拦截数据的话就需要重新设置断点:设置单步标志,当前的指令重新正常执行之后,触发单步异常,这个时候再重新下断点就可以了。

2、调试进程

// 被调试的进程ID
static DWORD g_processID = 0;

// 将进程改为被调试状态
DebugActiveProcess(g_processID);

// 退出调试的时候,不关闭被调试进程
BOOL kRet = DebugSetProcessKillOnExit(false);

3、设置断点

// 保存断点地址和对应的内存内容,用于恢复断点
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;
}

4、监听消息

DEBUG_EVENT debugEvent;
while (WaitForDebugEvent(&debugEvent, INFINITE)) 
{
    switch (debugEvent.dwDebugEventCode) 
    {
    case EXCEPTION_DEBUG_EVENT:
            OnException(&debugEvent);
        break;
    }

    ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
}

5、处理异常

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);
}

标签: 逆向 调试 拦截