And when PEB->BeingDebugged is set. Lords Of The Fallen anti-debug does this and only succeeds if an OutputDebugStringA exception is received.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/kernel32/heap.c | 5 +++ dlls/kernel32/tests/heap.c | 69 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+)
diff --git a/dlls/kernel32/heap.c b/dlls/kernel32/heap.c index b7bd6f5f91d..121cf931e43 100644 --- a/dlls/kernel32/heap.c +++ b/dlls/kernel32/heap.c @@ -135,6 +135,11 @@ BOOL WINAPI HeapDestroy( HANDLE heap /* [in] Handle of heap */ ) return TRUE; } if (!RtlDestroyHeap( heap )) return TRUE; + if (NtCurrentTeb()->Peb->BeingDebugged) + { + OutputDebugStringA( "Attempt to destroy an invalid heap\n" ); + DbgBreakPoint(); + } SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } diff --git a/dlls/kernel32/tests/heap.c b/dlls/kernel32/tests/heap.c index fa372b14e21..63d294999d2 100644 --- a/dlls/kernel32/tests/heap.c +++ b/dlls/kernel32/tests/heap.c @@ -626,6 +626,74 @@ static void test_HeapCreate(void) ok(HeapDestroy(heap),"HeapDestroy failed\n"); }
+static BOOL test_heap_destroy_dbgstr = FALSE; +static BOOL test_heap_destroy_break = FALSE; + +static LONG CALLBACK test_heap_destroy_except_handler( EXCEPTION_POINTERS *eptrs ) +{ + if (eptrs->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) + { +#if defined( __i386__ ) + eptrs->ContextRecord->Eip += 1; + test_heap_destroy_break = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; +#elif defined( __x86_64__ ) + eptrs->ContextRecord->Rip += 1; + test_heap_destroy_break = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; +#endif + } + + if (eptrs->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C) + { + test_heap_destroy_dbgstr = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; + } + + return (LONG)EXCEPTION_CONTINUE_SEARCH; +} + +/* partially copied from ntdll/heap.c */ +#define HEAP_VALIDATE_PARAMS 0x40000000 + +struct heap +{ + DWORD_PTR unknown1[2]; + DWORD unknown2[2]; + DWORD_PTR unknown3[4]; + DWORD unknown4; + DWORD_PTR unknown5[2]; + DWORD unknown6[3]; + DWORD_PTR unknown7[2]; + DWORD flags; + DWORD force_flags; + DWORD_PTR unknown8[6]; +}; + +static void test_HeapDestroy( void ) +{ + const struct heap invalid = {{0, 0}, {0, HEAP_VALIDATE_PARAMS}, {0, 0, 0, 0}, 0, {0, 0}, {0, 0, 0}, {0, 0}, HEAP_VALIDATE_PARAMS, 0, {0}}; + HANDLE heap = (HANDLE)&invalid; + PEB *Peb = NtCurrentTeb()->Peb; + BOOL ret, debugged; + + AddVectoredExceptionHandler( TRUE, test_heap_destroy_except_handler ); + + SetLastError( 0xdeadbeef ); + test_heap_destroy_dbgstr = FALSE; + test_heap_destroy_break = FALSE; + debugged = Peb->BeingDebugged; + Peb->BeingDebugged = TRUE; + ret = HeapDestroy( heap ); + ok( !ret, "HeapDestroy with invalid heap succeeded\n" ); + ok( GetLastError() == ERROR_INVALID_HANDLE, + "HeapDestroy error %u, expected ERROR_INVALID_HANDLE\n", GetLastError() ); + ok( test_heap_destroy_dbgstr, "HeapDestroy didn't call OutputDebugStrA\n" ); + ok( test_heap_destroy_break, "HeapDestroy didn't call DbgBreakPoint\n" ); + Peb->BeingDebugged = debugged; + + RemoveVectoredExceptionHandler( test_heap_destroy_except_handler ); +}
static void test_GlobalAlloc(void) { @@ -1233,6 +1301,7 @@ START_TEST(heap) test_heap(); test_obsolete_flags(); test_HeapCreate(); + test_HeapDestroy(); test_GlobalAlloc(); test_LocalAlloc();