逆向開發經常有這個需求:攔截局部數據。一般的做法是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);
}