Focused on three anti-debugging techniques
Windows NT, 2000 and XP 32 bit Heap Flags
Windows XP 64bit and Up
Force Flags
/* Gets the Heap Flag Offset */
int GetHeapFlagsOffset(bool x64)
{
int retVal = 0;
if (x64) {
if (IsVistaOrHigher()) {
retVal = 0x70;
}
else {
retVal = 0x14;
}
}
else {
if (IsVistaOrHigher()) {
retVal = 0x40;
}
else {
retVal = 0x0C;
}
}
return retVal;
}
/* Returns the Heap Force Flags offset */
int GetHeapForceFlagsOffset(bool x64)
{
int retVal = 0;
if (x64) {
if (IsVistaOrHigher()) {
retVal = 0x74;
}
else {
retVal = 0x18;
}
}
else {
if (IsVistaOrHigher()) {
retVal = 0x44;
}
else {
retVal = 0x10;
}
}
return retVal;
}
int CheckHeap()
{
/* Initializes variables and retrieves the process environment blocks */
PVOID pPeb = GetPEB();
PVOID pPeb64 = GetPEB64();
PVOID heap = 0;
DWORD offsetProcessHeap = 0;
PDWORD heapFlagsPtr = 0, heapForceFlagsPtr = 0;
BOOL x64 = FALSE;
#ifdef _WIN64
x64 = TRUE;
offsetProcessHeap = 0x30;
#else
offsetProcessHeap = 0x18;
#endif
/* Get the heap and the flags associated with it */
heap = (PVOID) * (PDWORD_PTR)((PBYTE)pPeb + offsetProcessHeap);
heapFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapFlagsOffset(x64));
heapForceFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapForceFlagsOffset(x64));
if (*heapFlagsPtr & ~HEAP_GROWABLE || *heapForceFlagsPtr != 0)
{
/* First instance of catching modified heap flags */
printf("That is not allowed!\n");
return -1;
}
/* For the case where we are using Process Environment Block 64 */
if (pPeb64)
{
/* Get the heap and the flags associated with it */
heap = (PVOID) * (PDWORD_PTR)((PBYTE)pPeb64 + 0x30);
heapFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapFlagsOffset(true));
heapForceFlagsPtr = (PDWORD)((PBYTE)heap + GetHeapForceFlagsOffset(true));
if (*heapFlagsPtr & ~HEAP_GROWABLE || *heapForceFlagsPtr != 0)
{
/* Second instance of catching modified heap flags */
printf("That is not allowed!\n");
return -1;
}
}
return 0;
}
It is possible by using a different debugger, you can bypass this.
DWORD g_origCrc = value; //Initialize with Crc value
DWORD CalcFuncCrc(PUCHAR funcBegin, PUCHAR funcEnd)
{
DWORD crc = 0;
for (; funcBegin < funcEnd; ++funcBegin)
{
crc += *funcBegin;
}
return crc;
}
VOID BeginSimulation(){
//Code Goes here
}
VOID EndSimulation() {
//Can be empty or do nothing
}
int main(){
DWORD crc = CalcFuncCrc((PUCHAR)BeginSimulation, (PUCHAR)EndSimulation);
if (g_origCrc != crc)
{
printf("Stop debugging program!");
exit(-1);
}
}
DWORD g_origCrc = value; //Initialize with Crc value
DWORD CalcFuncCrc(PUCHAR funcBegin, PUCHAR funcEnd)
{
DWORD crc = 0;
for (; funcBegin < funcEnd; ++funcBegin)
{
crc += *funcBegin;
}
return crc;
}
VOID BeginSimulation(){
//Code Goes here
}
VOID EndSimulation() {
//Can be empty or do nothing
}
int main(){
DWORD crc = CalcFuncCrc((PUCHAR)BeginSimulation, (PUCHAR)EndSimulation);
if (g_origCrc != crc)
{
printf("Stop debugging program!");
exit(-1);
}
}
The address to Begin Simulation and End Simulation are used to calculate funcBegin and FuncEnd
int main()
{
CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(GetCurrentThread(), &ctx))
{
if (ctx.Dr0 != 0 || ctx.Dr1 != 0 || ctx.Dr2 != 0 || ctx.Dr3 != 0)
{
printf("Stop debugging program!");
exit(-1);
}
}
}
0
0
0
0
0
0
0
0
/* From https://www.apriorit.com/dev-blog/367-anti-reverse-engineering-protection-techniques-to-use-before-releasing-software */
typedef NTSTATUS(NTAPI *pfnNtGetContextThread)(
_In_ HANDLE ThreadHandle,
_Out_ PCONTEXT pContext
);
typedef NTSTATUS(NTAPI *pfnNtSetContextThread)(
_In_ HANDLE ThreadHandle,
_In_ PCONTEXT pContext
);
pfnNtGetContextThread g_origNtGetContextThread = NULL;
pfnNtSetContextThread g_origNtSetContextThread = NULL;
NTSTATUS NTAPI HookNtGetContextThread(
_In_ HANDLE ThreadHandle,
_Out_ PCONTEXT pContext)
{
DWORD backupContextFlags = pContext->ContextFlags;
pContext->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
NTSTATUS status = g_origNtGetContextThread(ThreadHandle, pContext);
pContext->ContextFlags = backupContextFlags;
return status;
}
NTSTATUS NTAPI HookNtSetContextThread(
_In_ HANDLE ThreadHandle,
_In_ PCONTEXT pContext)
{
DWORD backupContextFlags = pContext->ContextFlags;
pContext->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
NTSTATUS status = g_origNtSetContextThread(ThreadHandle, pContext);
pContext->ContextFlags = backupContextFlags;
return status;
}
void HookThreadContext()
{
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
g_origNtGetContextThread = (pfnNtGetContextThread)GetProcAddress(hNtDll, "NtGetContextThread");
g_origNtSetContextThread = (pfnNtSetContextThread)GetProcAddress(hNtDll, "NtSetContextThread");
Mhook_SetHook((PVOID*)&g_origNtGetContextThread, HookNtGetContextThread);
}
EXCEPTION_DISPOSITION ExceptionRoutine(
PEXCEPTION_RECORD ExceptionRecord,
PVOID EstablisherFrame,
PCONTEXT ContextRecord,
PVOID DispatcherContext)
{
if (EXCEPTION_INVALID_HANDLE == ExceptionRecord->ExceptionCode)
{
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
return ExceptionContinueExecution;
}
int main()
{
__asm
{
// set SEH handler
push ExceptionRoutine
push dword ptr fs : [0]
mov dword ptr fs : [0], esp
}
CloseHandle((HANDLE)0xBAAD);
__asm
{
// return original SEH handler
mov eax, [esp]
mov dword ptr fs : [0], eax
add esp, 8
}
return 0
}
#define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A
WCHAR * outputString = L"Any text";
ULONG_PTR args[4] = {0};
args[0] = (ULONG_PTR)wcslen(outputString) + 1;
args[1] = (ULONG_PTR)outputString;
__try
{
RaiseException(DBG_PRINTEXCEPTION_WIDE_C, 0, 4, args);
printf("Debugger detected");
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("Debugger NOT detected");
}