While using [x64dbg](https://x64dbg.com/) I was experiencing incredibly strange issue - `int3` was swallowed and `KiUserExceptionDispatcher` was never invoked.
Turns out that x64dbg ([TitanEngine](https://github.com/x64dbg/TitanEngine/blob/x64dbg/TitanEngine/TitanEngine.De...)) expects system breakpoint to be first breakpoint that will be triggered and it always swallows it (it's hardcoded no setting to change it's behavior).
That means if you have a DLL with `int3` for example ```c LONG ExceptionHandler(EXCEPTION_POINTERS *ExceptionInfo) { ExceptionInfo->ContextRecord->Rip++; return EXCEPTION_CONTINUE_EXECUTION; }
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { AddVectoredExceptionHandler(1, ExceptionHandler); asm("int $0x03; nop; nop;"); } return TRUE; } ```
That `int3` would be swallowed and `ExceptionHandler` never invoked when running under x64dbg. Now with this MR applied it works correctly and as expected (same as on Windows).
Another interesting thing is that without this MR it wouldn't just swallow `int3` but even skip first `nop` aswell and RIP would end up on 2nd `nop`.
-- v3: ntdll: loader_init() invoke breakpoint before loading dlls
From: Dāvis Mosāns davispuh@gmail.com
x64dbg (TitanEngine) expects that system breakpoint is first breakpoint that will be triggered. Before this patch that wasn't the case causing user breakpoint (eg. in some DLL) to be swallowed instead. --- dlls/ntdll/loader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 4fc8af2662e..48e017f6034 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -4340,6 +4340,9 @@ void loader_init( CONTEXT *context, void **entry ) NtTerminateProcess( GetCurrentProcess(), status ); }
+ NtQueryInformationProcess( GetCurrentProcess(), ProcessDebugPort, &port, sizeof(port), NULL ); + if (port) process_breakpoint(); + if ((status = walk_node_dependencies( wm->ldr.DdagNode, context, process_attach ))) { if (last_failed_modref) @@ -4352,9 +4355,6 @@ void loader_init( CONTEXT *context, void **entry ) release_address_space(); if (wm->ldr.TlsIndex == -1) call_tls_callbacks( wm->ldr.DllBase, DLL_PROCESS_ATTACH ); if (wm->ldr.ActivationContext) RtlDeactivateActivationContext( 0, cookie ); - - NtQueryInformationProcess( GetCurrentProcess(), ProcessDebugPort, &port, sizeof(port), NULL ); - if (port) process_breakpoint(); } else {
Ping? I rebased this now
This would need tests.
On Wed Feb 7 01:55:34 2024 +0000, Alexandre Julliard wrote:
This would need tests.
That seems quite complicated for this case, any ideas how exactly?
Test would need to load a DLL that would use `int3` but then you also need debugger attached so that `process_breakpoint()` is invoked.
On Wed Feb 7 01:55:34 2024 +0000, Dāvis Mosāns (davispuh) wrote:
That seems quite complicated for this case, any ideas how exactly? Test would need to load a DLL that would use `int3` but then you also need debugger attached so that `process_breakpoint()` is invoked.
You can use Win32 debugging APIs. Please refer to dlls/ntdll/tests/exception.c for examples.