Make functions ntlea needs to patch hotpatchable. And add a asm wrapper for `GetWindowLongA` to workaround an assumption ntlea made.
-- v2: user32: add hotpatchable wrapper for GetWindowLongA
From: Yuxuan Shui yshui@codeweavers.com
Needed for ntlea. --- dlls/gdi32/objects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/gdi32/objects.c b/dlls/gdi32/objects.c index 42bf6fcf111..070ce9c3885 100644 --- a/dlls/gdi32/objects.c +++ b/dlls/gdi32/objects.c @@ -418,7 +418,7 @@ HGDIOBJ WINAPI GetCurrentObject( HDC hdc, UINT type ) /*********************************************************************** * GetStockObject (GDI32.@) */ -HGDIOBJ WINAPI GetStockObject( INT obj ) +HGDIOBJ WINAPI DECLSPEC_HOTPATCH GetStockObject( INT obj ) { if (obj < 0 || obj > STOCK_LAST + 1 || obj == 9) return 0;
From: Yuxuan Shui yshui@codeweavers.com
Needed for ntlea. --- dlls/user32/winproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/user32/winproc.c b/dlls/user32/winproc.c index 5ab85eb81cd..8804199aed1 100644 --- a/dlls/user32/winproc.c +++ b/dlls/user32/winproc.c @@ -860,7 +860,7 @@ BOOL WINAPI User32CallSendAsyncCallback( const struct send_async_params *params, * * ECMA-234, Win32 */ -LRESULT WINAPI CallWindowProcA( WNDPROC func, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) +LRESULT WINAPI DECLSPEC_HOTPATCH CallWindowProcA( WNDPROC func, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { struct win_proc_params params;
From: Yuxuan Shui yshui@codeweavers.com
ntlea for some reason expects GetWindowLongA to start with a "push $-2", and will try to skip over this instruction. If we don't anticipate this, it will ended up either skipping over critical instructions, or on a desync address. Either way, it would be bad. --- dlls/user32/win.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 28cf40441d9..bc5f55dbeb7 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -884,11 +884,23 @@ WORD WINAPI GetWindowWord( HWND hwnd, INT offset ) return NtUserGetWindowWord( hwnd, offset ); }
- /********************************************************************** * GetWindowLongA (USER32.@) */ -LONG WINAPI GetWindowLongA( HWND hwnd, INT offset ) + +#ifdef __i386__ +__ASM_STDCALL_FUNC(GetWindowLongA, 8, + "movl.s %edi, %edi\n" + "pushl %ebp\n" + "movl.s %esp, %ebp\n" + "pushl $-2\n" + "addl $4, %esp\n" + "popl %ebp\n" + "jmp " __ASM_STDCALL("RealGetWindowLongA", 8) "\n") +LONG WINAPI RealGetWindowLongA( HWND hwnd, INT offset ) +#else +LONG WINAPI DECLSPEC_HOTPATCH GetWindowLongA(HWND hwnd, INT offset) +#endif { switch (offset) { @@ -910,7 +922,6 @@ LONG WINAPI GetWindowLongA( HWND hwnd, INT offset ) } }
- /********************************************************************** * GetWindowLongW (USER32.@) */
Update: eliminated the wrapper for x86_64 build. Not sure if this is better.
Out of curiosity, what was the assumption that ntlea made that necessitates a wrapper around GetWindowLongA? Is there a way to satisfy ntlea without having to write assembly?
Out of curiosity, what was the assumption that ntlea made that necessitates a wrapper around GetWindowLongA? Is there a way to satisfy ntlea without having to write assembly?
That's described in the subject of 3/3. It should really be a comment in the code, though.
Technically we don't need to push $-2, but we need a two-byte instruction that shifts %esp by -4. What ntlea does, in more detail, is:
* Check for the hotpatch prefix (well, actually only the first 4 bytes of it). If it's not present, it makes different assumptions about the internal assembly, that are harder to match. [1]
* Replace the first 5 bytes with a jump to a hook. When the hook returns, it does:
pushl %ebp movl %esp, %ebp pushl $-2
and then jumps to byte offset 7 in the original function. [2]
[1] https://github.com/zxyacb/ntlea/blob/faeacc46c9015e828f312382632e0b0774f7541...
[2] https://github.com/zxyacb/ntlea/blob/master/Tools/ntlea/ntleai/ntleax.c#L325
Zebediah Figura (@zfigura) commented about dlls/user32/win.c:
/**********************************************************************
GetWindowLongA (USER32.@)
*/ -LONG WINAPI GetWindowLongA( HWND hwnd, INT offset )
+#ifdef __i386__ +__ASM_STDCALL_FUNC(GetWindowLongA, 8,
"movl.s %edi, %edi\n"
"pushl %ebp\n"
"movl.s %esp, %ebp\n"
"pushl $-2\n"
"addl $4, %esp\n"
"popl %ebp\n"
"jmp " __ASM_STDCALL("RealGetWindowLongA", 8) "\n")
Can we give this a name that makes it look less like a Win32 function? Something like get_window_longA() would probably be better.