To detect or remove hardware breakpoints, malware can use SEH to get the thread context, check the values of the DR registers and clear all of them to remove the hardware breakpoints—or at least just check their values and exit if a debugger is detected. The code is as follows:
xor eax, eax
push offset except_callback
push d fs:[eax]
mov fs:[eax], esp
int 3 ;force an exception to occur
...
except_callback:
mov eax, [esp+0ch] ;get ContextRecord
mov ecx, [eax+4] ;Dr0
or ecx, [eax+8] ;Dr1
or ecx, [eax+0ch] ;Dr2
or ecx, [eax+10h] ;Dr3
jne <Debugger_Detected>
Another way to remove hardware breakpoints is to use the GetThreadContext() API to access the current thread (or another thread) context and check for the presence of hardware breakpoints or clear them using the SetThreadContext() API.
The best way to deal with these techniques is to set a breakpoint on GetThreadContext, SetThreadContext, or on the exception callback function...