From: Sebastian Lackner Subject: [1/6] ntdll: Add support for ATL thunk 'MOV this,ecx; JMP func'. Message-Id: <5445FE24.9050807@fds-team.de> Date: Tue, 21 Oct 2014 08:33:08 +0200 To avoid regressions when implementing SetProcessDEPPolicy, this series implements more ATL thunks supported on Windows. The list of ATL thunks is based on http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.151.8827 --- dlls/kernel32/tests/virtual.c | 31 +++++++++++++++++++++---- dlls/ntdll/signal_i386.c | 54 +++++++++++++++++++++++++++++-------------- 2 files changed, 63 insertions(+), 22 deletions(-) From 8e3e614be59024340582e761477f2e5b7e906c79 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Tue, 21 Oct 2014 05:55:27 +0200 Subject: ntdll: Add support for ATL thunk 'MOV this,ecx; JMP func'. --- dlls/kernel32/tests/virtual.c | 31 +++++++++++++++++++++---- dlls/ntdll/signal_i386.c | 54 +++++++++++++++++++++++++++++-------------- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/dlls/kernel32/tests/virtual.c b/dlls/kernel32/tests/virtual.c index 73b753e..5f83ead 100644 --- a/dlls/kernel32/tests/virtual.c +++ b/dlls/kernel32/tests/virtual.c @@ -1882,7 +1882,7 @@ static inline DWORD send_message_excpt( HWND hWnd, UINT uMsg, WPARAM wParam, LPA pNtCurrentTeb()->Tib.ExceptionList = &frame; num_guard_page_calls = num_execute_fault_calls = 0; - ret = SendMessageA( hWnd, WM_USER, 0, 0 ); + ret = SendMessageA( hWnd, uMsg, wParam, lParam ); pNtCurrentTeb()->Tib.ExceptionList = frame.Prev; @@ -1900,14 +1900,18 @@ static LRESULT CALLBACK jmp_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR static LRESULT CALLBACK atl_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { DWORD arg = (DWORD)hWnd; - ok( arg == 0x11223344, "arg is 0x%08x instead of 0x11223344\n", arg ); + if (uMsg == WM_USER) + ok( arg == 0x11223344, "arg is 0x%08x instead of 0x11223344\n", arg ); + else + ok( arg != 0x11223344, "arg is unexpectedly 0x11223344\n" ); return 43; } static void test_atl_thunk_emulation( ULONG dep_flags ) { static const char code_jmp[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; - static const char code_atl[] = {0xC7, 0x44, 0x24, 0x04, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00}; + static const char code_atl1[] = {0xC7, 0x44, 0x24, 0x04, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00}; + static const char code_atl2[] = {0xB9, 0x44, 0x33, 0x22, 0x11, 0xE9, 0x00, 0x00, 0x00, 0x00}; static const char cls_name[] = "atl_thunk_class"; DWORD ret, size, old_prot; ULONG old_flags = MEM_EXECUTE_OPTION_ENABLE; @@ -2008,7 +2012,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) /* Now test with a proper ATL thunk instruction. */ - memcpy( base, code_atl, sizeof(code_atl) ); + memcpy( base, code_atl1, sizeof(code_atl1) ); *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13); success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); @@ -2077,6 +2081,23 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) win_skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n" ); } + /* Test alternative ATL thunk instructions. */ + + memcpy( base, code_atl2, sizeof(code_atl2) ); + *(DWORD *)(base + 6) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 10); + + success = VirtualProtect( base, size, PAGE_READWRITE, &old_prot ); + ok( success, "VirtualProtect failed %u\n", GetLastError() ); + + ret = send_message_excpt( hWnd, WM_USER + 1, 0, 0 ); + /* FIXME: we don't check the content of the register ECX yet */ + ok( ret == 43, "call returned wrong result, expected 43, 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 + 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) ); @@ -2194,7 +2215,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) /* Now test with a proper ATL thunk instruction. */ - memcpy( base, code_atl, sizeof(code_atl) ); + memcpy( base, code_atl1, sizeof(code_atl1) ); *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13); count = 64; diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index f97799c..dde4e2d 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -1616,12 +1616,22 @@ static inline BOOL check_invalid_gs( CONTEXT *context ) #include "pshpack1.h" -struct atl_thunk +union atl_thunk { - DWORD movl; /* movl this,4(%esp) */ - DWORD this; - BYTE jmp; /* jmp func */ - int func; + struct + { + DWORD movl; /* movl this,4(%esp) */ + DWORD this; + BYTE jmp; /* jmp func */ + int func; + } t1; + struct + { + BYTE movl; /* movl this,ecx */ + DWORD this; + BYTE jmp; /* jmp func */ + int func; + } t2; }; #include "poppack.h" @@ -1632,26 +1642,36 @@ struct atl_thunk */ static BOOL check_atl_thunk( EXCEPTION_RECORD *rec, CONTEXT *context ) { - const struct atl_thunk *thunk = (const struct atl_thunk *)rec->ExceptionInformation[1]; - struct atl_thunk thunk_copy; - BOOL ret = FALSE; + const union atl_thunk *thunk = (const union atl_thunk *)rec->ExceptionInformation[1]; + union atl_thunk thunk_copy; + SIZE_T thunk_len; - if (virtual_uninterrupted_read_memory( thunk, &thunk_copy, sizeof(*thunk) ) != sizeof(*thunk)) - return FALSE; + thunk_len = virtual_uninterrupted_read_memory( thunk, &thunk_copy, sizeof(*thunk) ); + if (!thunk_len) return FALSE; - if (thunk_copy.movl == 0x042444c7 && thunk_copy.jmp == 0xe9) + if (thunk_len >= sizeof(thunk_copy.t1) && thunk_copy.t1.movl == 0x042444c7 && + thunk_copy.t1.jmp == 0xe9) { if (virtual_uninterrupted_write_memory( (DWORD *)context->Esp + 1, - &thunk_copy.this, sizeof(DWORD) ) == sizeof(DWORD)) + &thunk_copy.t1.this, sizeof(DWORD) ) == sizeof(DWORD)) { - context->Eip = (DWORD_PTR)(&thunk->func + 1) + thunk_copy.func; - TRACE( "emulating ATL thunk at %p, func=%08x arg=%08x\n", - thunk, context->Eip, thunk_copy.this ); - ret = TRUE; + context->Eip = (DWORD_PTR)(&thunk->t1.func + 1) + thunk_copy.t1.func; + TRACE( "emulating ATL thunk type 1 at %p, func=%08x arg=%08x\n", + thunk, context->Eip, thunk_copy.t1.this ); + return TRUE; } } + else if (thunk_len >= sizeof(thunk_copy.t2) && thunk_copy.t2.movl == 0xb9 && + thunk_copy.t2.jmp == 0xe9) + { + context->Ecx = thunk_copy.t2.this; + context->Eip = (DWORD_PTR)(&thunk->t2.func + 1) + thunk_copy.t2.func; + TRACE( "emulating ATL thunk type 2 at %p, func=%08x ecx=%08x\n", + thunk, context->Eip, context->Ecx ); + return TRUE; + } - return ret; + return FALSE; } -- 2.1.2