首先需要明白什么是硬件斷點,硬件斷點其實是通過一個調試寄存器來實現的,這個調試寄存器是CPU上的東西,就是前面截圖的這個東西,叫做Debug Registers,在intel手冊卷3 17章第二節里面)。
DR0-DR3為設置斷點的地址,DR4和DR5為保留內容。DR6為調試異常產生后顯示的一些信息,DR7保存了斷點是否啟用、斷點類型和長度等信息。
重點在DR0-DR3和DR7這四個里面。
DR0-DR3中存放的是斷點的地址。
然后DR7根據各各字段的不同意義不同:
字段 | 意義 |
---|---|
L0-L3 | 對於DR0-DR3存放的地址斷點是否有效,並且是一個局部斷點 |
G0-G3 | 對應DR0-DR3存放的地址是否有效,但是是一個全局斷點(在Windows中用不了。) |
LEN0-LEN3 | 對應DR0-DR3斷點的長度。00表示1字節,01表示二字節,11表示四字節 |
RW0-RW3 | 對應DR0-DR3斷點的類型。00表示執行斷點,01表示寫入斷點,11表示讀寫斷點。 |
獲取硬點斷點信息:
這里我們要使用到一個API:
BOOL GetThreadContext(
HANDLE hThread,
LPCONTEXT lpContext
);
用這個API來獲取當前線程的環境,肯定有人要問為什么是線程,不是進程,不是啥啥啥的。因為在操作系統中,線程才是真正執行代碼流程的東西,進程只是一個分配資源的概念,而每個代碼執行的時候,自己的寄存器或多或少都會受到影響,簡單來說就是線程才能實實在在執行代碼,如果用進程的話每個線程都會受到影響,而且進程也沒有這個GetProcessContext得到進行環境寄存器的API。
參數:
參數 | 意義 |
---|---|
hThread | 線程句柄 |
lpContext | 指向CONTEXT結構體的指針,需要注意的是context是一個輸出參數就是通過這個API會修改CONTEXT結構體里的值,但是需要設置CONTEXT里的ContextFlags值來限制要獲得什么內容,如果不設置則不能獲得內容。 |
所以如果我們要獲取Debug Register的內容,這里需要設置為CONTEXT_DEBUG_REGISTERS了。
簡單實現硬件斷點反調試:
最后查看通過OD打下硬件斷點的情況,和直接運行的情況:
采用異常實現硬件斷點反調試:
如果說直接用前面那種來實現反調試就太辣雞了,很容易就被識破,但是如果我們添加一個異常呢,這樣就會好很多,因為異常要通過一些寄存器,一些不是上面那種直接調用API的辦法,會麻煩一點。
采用異常過濾器來處理:
關於異常過濾器可以查看直接的博客:
這里我直接上代碼了:
這里eip+3的原因是錯誤代碼的硬編碼是三個字節,跳過就好了。
但是這個用調試器就不行了,會一直在異常這里,不知道為什么。
修正:
官方文檔上寫了這個異常過濾器的函數的描述,如果這個程序正在被調試,那么異常會直接傳遞給調試器,所以當我們進行調試的時候這個異常過濾器是沒有作用的。
采用SEH來處理:
EXCEPTION_DISPOSITION mySEH(struct _EXCEPTION_RECORD* ExceptionRecord,PVOID EstablisherFrame,PCONTEXT pcontext,PVOID DispatcherContext)
{
if (pcontext->Dr1 != 0 || pcontext->Dr2 != 0 || pcontext->Dr3 != 0 || pcontext->Dr0 != 0)
{
printf("SEH驗證異常,程序正在被調試,即將退出程序\n");
ExitProcess(0);
}
else
{
printf("SEH驗證正常\n");
}
pcontext->Eip += 3;
return ExceptionContinueExecution;
}
void TestSeh()
{
printf("test SEH\n");
__asm
{
push mySEH
mov eax,fs:[0]
push eax
mov fs:[0],esp
mov eax, 0
mov[eax], 1
}
cout << "SEH: 跳過了異常代碼" << endl;
cout << "SEH: 程序正常結束" << endl;
}
然后通過調試就OK了,當調試器沒有選擇捕獲你這個異常的時候,異常就是交給程序的VEH,SEH,異常過濾器來處理的。
采用VEH來處理:
LONG WINAPI MyPvectoredExceptionHandler(_EXCEPTION_POINTERS* ExceptionInfo)
{
if (ExceptionInfo->ContextRecord->Dr1 != 0 || ExceptionInfo->ContextRecord->Dr2 != 0 || ExceptionInfo->ContextRecord->Dr3 != 0 || ExceptionInfo->ContextRecord->Dr0 != 0)
{
printf("VEH 驗證異常,程序正在被調試,即將退出程序\n");
ExitProcess(0);
}
else
{
printf("硬件斷點的VEH驗證正常,無硬件斷點\n");
}
ExceptionInfo->ContextRecord->Eip += 3;
return EXCEPTION_CONTINUE_EXECUTION;
}
void TestVeh()
{
printf("test VEH\n");
AddVectoredExceptionHandler(1, MyPvectoredExceptionHandler);
__asm
{
mov eax, 0
mov[eax], 1
}
cout << "VEH: 跳過了異常代碼" << endl;
cout << "VEH: 程序正常結束" << endl;
}
小結