From 1c1e7ed016c129dacc52e5f6432587270c91f681 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Tue, 21 Oct 2014 06:03:57 +0200 Subject: [PATCH] ntdll: Add support for ATL thunk 'POP ecx; POP eax; PUSH ecx; JMP 4(%eax)'. --- dlls/kernel32/tests/virtual.c | 41 +++++++++++++++++++++++++++++++++++ dlls/ntdll/signal_i386.c | 28 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c index ae0ede55de7..1592b87e289 100644 --- a/dlls/kernel32/tests/virtual.c +++ b/dlls/kernel32/tests/virtual.c @@ -1889,6 +1889,23 @@ static inline DWORD send_message_excpt( HWND hWnd, UINT uMsg, WPARAM wParam, LPA return ret; } +static inline DWORD call_proc_excpt( DWORD (CALLBACK *code)(void *), void *arg ) +{ + EXCEPTION_REGISTRATION_RECORD frame; + DWORD ret; + + frame.Handler = execute_fault_seh_handler; + frame.Prev = pNtCurrentTeb()->Tib.ExceptionList; + pNtCurrentTeb()->Tib.ExceptionList = &frame; + + num_guard_page_calls = num_execute_fault_calls = 0; + ret = code( arg ); + + pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; + + return ret; +} + static LRESULT CALLBACK jmp_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { if (uMsg == WM_USER) @@ -1907,6 +1924,11 @@ static LRESULT CALLBACK atl_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR return 43; } +static DWORD CALLBACK atl5_test_func( void ) +{ + return 44; +} + static void test_atl_thunk_emulation( ULONG dep_flags ) { static const char code_jmp[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; @@ -1914,6 +1936,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) static const char code_atl2[] = {0xB9, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00}; static const char code_atl3[] = {0xBA, 0x44, 0x33, 0x22, 0x11, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE1}; static const char code_atl4[] = {0xB9, 0x44, 0x33, 0x22, 0x11, 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0}; + static const char code_atl5[] = {0x59, 0x58, 0x51, 0xFF, 0x60, 0x04}; static const char cls_name[] = "atl_thunk_class"; DWORD ret, size, old_prot; ULONG old_flags = MEM_EXECUTE_OPTION_ENABLE; @@ -2133,6 +2156,24 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) else ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + memcpy( base, code_atl5, sizeof(code_atl5) ); + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = (DWORD_PTR)atl5_test_func; + ret = call_proc_excpt( (void *)base, &ret - 1 ); + /* FIXME: We don't check the content of the registers EAX/ECX yet */ + ok( ret == 44, "call returned wrong result, expected 44, got %d\n", ret ); + ok( num_guard_page_calls == 0, "expected no STATUS_GUARD_PAGE_VIOLATION exception, got %d exceptions\n", num_guard_page_calls ); + if ((dep_flags & MEM_EXECUTE_OPTION_DISABLE) && (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)) + ok( num_execute_fault_calls == 1, "expected one STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else if (dep_flags & MEM_EXECUTE_OPTION_DISABLE) + ok( num_execute_fault_calls == 0 || broken(num_execute_fault_calls == 1) /* Windows XP */, + "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + else + ok( num_execute_fault_calls == 0, "expected no STATUS_ACCESS_VIOLATION exception, got %d exceptions\n", num_execute_fault_calls ); + /* Restore the JMP instruction, set to executable, and then destroy the Window */ memcpy( base, code_jmp, sizeof(code_jmp) ); diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index b315e553012..13df4bb7520 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -1648,6 +1648,14 @@ union atl_thunk DWORD func; WORD jmp; /* jmp eax */ } t4; + struct + { + DWORD inst1; /* pop ecx + * pop eax + * push ecx + * jmp 4(%eax) */ + WORD inst2; + } t5; }; #include "poppack.h" @@ -1708,6 +1716,26 @@ static BOOL check_atl_thunk( EXCEPTION_RECORD *rec, CONTEXT *context ) thunk, context->Eip, context->Eax, context->Ecx ); return TRUE; } + else if (thunk_len >= sizeof(thunk_copy.t5) && thunk_copy.t5.inst1 == 0xff515859 && + thunk_copy.t5.inst2 == 0x0460) + { + DWORD func, stack[2]; + if (virtual_uninterrupted_read_memory( (DWORD *)context->Esp, + stack, sizeof(stack) ) == sizeof(stack) && + virtual_uninterrupted_read_memory( (DWORD *)stack[1] + 1, + &func, sizeof(DWORD) ) == sizeof(DWORD) && + virtual_uninterrupted_write_memory( (DWORD *)context->Esp + 1, + &stack[0], sizeof(stack[0]) ) == sizeof(stack[0])) + { + context->Ecx = stack[0]; + context->Eax = stack[1]; + context->Esp = context->Esp + sizeof(DWORD); + context->Eip = func; + TRACE( "emulating ATL thunk type 5 at %p, func=%08x eax=%08x ecx=%08x esp=%08x\n", + thunk, context->Eip, context->Eax, context->Ecx, context->Esp ); + return TRUE; + } + } return FALSE; }