Commit aab0d25a authored by Sebastian Lackner's avatar Sebastian Lackner Committed by Alexandre Julliard

ntdll: Add support for ATL thunk 'MOV this,ecx; JMP func'.

parent 1e3f15d8
...@@ -1882,7 +1882,7 @@ static inline DWORD send_message_excpt( HWND hWnd, UINT uMsg, WPARAM wParam, LPA ...@@ -1882,7 +1882,7 @@ static inline DWORD send_message_excpt( HWND hWnd, UINT uMsg, WPARAM wParam, LPA
pNtCurrentTeb()->Tib.ExceptionList = &frame; pNtCurrentTeb()->Tib.ExceptionList = &frame;
num_guard_page_calls = num_execute_fault_calls = 0; 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; pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
...@@ -1900,14 +1900,18 @@ static LRESULT CALLBACK jmp_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR ...@@ -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 ) static LRESULT CALLBACK atl_test_func( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{ {
DWORD arg = (DWORD)hWnd; 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; return 43;
} }
static void test_atl_thunk_emulation( ULONG dep_flags ) static void test_atl_thunk_emulation( ULONG dep_flags )
{ {
static const char code_jmp[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; 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"; static const char cls_name[] = "atl_thunk_class";
DWORD ret, size, old_prot; DWORD ret, size, old_prot;
ULONG old_flags = MEM_EXECUTE_OPTION_ENABLE; ULONG old_flags = MEM_EXECUTE_OPTION_ENABLE;
...@@ -2008,7 +2012,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) ...@@ -2008,7 +2012,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags )
/* Now test with a proper ATL thunk instruction. */ /* 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); *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13);
success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot ); success = VirtualProtect( base, size, PAGE_EXECUTE_READWRITE, &old_prot );
...@@ -2077,6 +2081,23 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) ...@@ -2077,6 +2081,23 @@ static void test_atl_thunk_emulation( ULONG dep_flags )
win_skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n" ); 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 */ /* Restore the JMP instruction, set to executable, and then destroy the Window */
memcpy( base, code_jmp, sizeof(code_jmp) ); memcpy( base, code_jmp, sizeof(code_jmp) );
...@@ -2194,7 +2215,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags ) ...@@ -2194,7 +2215,7 @@ static void test_atl_thunk_emulation( ULONG dep_flags )
/* Now test with a proper ATL thunk instruction. */ /* 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); *(DWORD *)(base + 9) = (DWORD_PTR)atl_test_func - (DWORD_PTR)(base + 13);
count = 64; count = 64;
......
...@@ -1616,12 +1616,22 @@ static inline BOOL check_invalid_gs( CONTEXT *context ) ...@@ -1616,12 +1616,22 @@ static inline BOOL check_invalid_gs( CONTEXT *context )
#include "pshpack1.h" #include "pshpack1.h"
struct atl_thunk union atl_thunk
{ {
DWORD movl; /* movl this,4(%esp) */ struct
DWORD this; {
BYTE jmp; /* jmp func */ DWORD movl; /* movl this,4(%esp) */
int func; 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" #include "poppack.h"
...@@ -1632,26 +1642,36 @@ struct atl_thunk ...@@ -1632,26 +1642,36 @@ struct atl_thunk
*/ */
static BOOL check_atl_thunk( EXCEPTION_RECORD *rec, CONTEXT *context ) static BOOL check_atl_thunk( EXCEPTION_RECORD *rec, CONTEXT *context )
{ {
const struct atl_thunk *thunk = (const struct atl_thunk *)rec->ExceptionInformation[1]; const union atl_thunk *thunk = (const union atl_thunk *)rec->ExceptionInformation[1];
struct atl_thunk thunk_copy; union atl_thunk thunk_copy;
BOOL ret = FALSE; SIZE_T thunk_len;
if (virtual_uninterrupted_read_memory( thunk, &thunk_copy, sizeof(*thunk) ) != sizeof(*thunk)) thunk_len = virtual_uninterrupted_read_memory( thunk, &thunk_copy, sizeof(*thunk) );
return FALSE; 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, 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; context->Eip = (DWORD_PTR)(&thunk->t1.func + 1) + thunk_copy.t1.func;
TRACE( "emulating ATL thunk at %p, func=%08x arg=%08x\n", TRACE( "emulating ATL thunk type 1 at %p, func=%08x arg=%08x\n",
thunk, context->Eip, thunk_copy.this ); thunk, context->Eip, thunk_copy.t1.this );
ret = TRUE; 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;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment