[PATCH] kernel32: Call OutputDebugStringA when invalid heap is destroyed.

Rémi Bernon rbernon at codeweavers.com
Wed Nov 18 12:23:21 CST 2020


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 at 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();
 
-- 
2.29.2




More information about the wine-devel mailing list