From 0f5874651573f54dbaf160f526ff720bf952a708 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Sun, 17 Mar 2019 00:16:09 +0100 Subject: [PATCH 01/33] experimental tweaks for diablo and warcraft 2 --- ddraw.def | 6 +- ddraw.rc | 2 +- inc/main.h | 2 + src/main.c | 136 +++++++++++++++++++++++++++++++--------------- src/mouse.c | 4 +- src/render.c | 3 + src/render_d3d9.c | 3 + src/render_soft.c | 7 ++- src/settings.c | 21 +++++-- src/surface.c | 49 +++++++++++++++++ 10 files changed, 176 insertions(+), 57 deletions(-) diff --git a/ddraw.def b/ddraw.def index 9e214cd..ff1a079 100644 --- a/ddraw.def +++ b/ddraw.def @@ -1,6 +1,6 @@ LIBRARY ddraw.dll EXPORTS - DirectDrawCreate @1 - DirectDrawEnumerateA @2 - GameHandlesClose DATA + DirectDrawCreate @1 + DirectDrawEnumerateA @2 + GameHandlesClose DATA diff --git a/ddraw.rc b/ddraw.rc index e9ed161..15528d4 100644 --- a/ddraw.rc +++ b/ddraw.rc @@ -21,7 +21,7 @@ PRODUCTVERSION VERSION VALUE "FileDescription", "DirectDraw replacement" VALUE "FileVersion", VERSION_STRING VALUE "InternalName", "ddraw" - VALUE "LegalCopyright", "Copyright (c) 2010-2018" + VALUE "LegalCopyright", "Copyright (c) 2010-2019" VALUE "LegalTrademarks", "" VALUE "OriginalFileName", "ddraw.dll" VALUE "ProductName", "cnc-ddraw" diff --git a/inc/main.h b/inc/main.h index 4599e17..c6248a9 100644 --- a/inc/main.h +++ b/inc/main.h @@ -125,6 +125,8 @@ typedef struct IDirectDrawImpl BOOL altenter; BOOL hidecursor; BOOL accurateTimers; + int bnetHack; + BOOL bnetActive; SpeedLimiter ticksLimiter; SpeedLimiter flipLimiter; SpeedLimiter fpsLimiter; diff --git a/src/main.c b/src/main.c index a46f6e2..a385297 100644 --- a/src/main.c +++ b/src/main.c @@ -1161,51 +1161,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; - case WM_ACTIVATE: - if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) - { - if (!ddraw->windowed) - { - if (!Direct3D9Active) - { - ChangeDisplaySettings(&ddraw->render.mode, CDS_FULLSCREEN); - - if (wParam == WA_ACTIVE) - { - mouse_lock(); - } - } - - InterlockedExchange(&ddraw->minimized, FALSE); - } - - if (!ddraw->handlemouse) - RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); - } - else if (wParam == WA_INACTIVE) - { - if (!ddraw->windowed && !ddraw->locked && ddraw->noactivateapp) - return 0; - - mouse_unlock(); - - if (ddraw->wine && LastSetWindowPosTick + 500 > timeGetTime()) - return 0; - - /* minimize our window on defocus when in fullscreen */ - if (!ddraw->windowed) - { - if (!Direct3D9Active) - { - ShowWindow(ddraw->hWnd, SW_MINIMIZE); - ChangeDisplaySettings(&ddraw->mode, 0); - } - - InterlockedExchange(&ddraw->minimized, TRUE); - } - } - return 0; - //workaround for a bug where sometimes a background window steals the focus case WM_WINDOWPOSCHANGING: { @@ -1228,7 +1183,77 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mouse_unlock(); return 0; + + case WM_ENABLE: + { + if (ddraw->bnetHack) + { + if (wParam) + mouse_lock(); + else + mouse_unlock(); + + HWND hWnd = FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL); + if (hWnd) + { + RECT rc; + if (GetWindowRect(hWnd, &rc) && (rc.bottom - rc.top != 479)) + while (ShowCursor(FALSE) > 0); + } + + ddraw->bnetActive = !wParam; + } + break; + } + + case WM_ACTIVATE: + { + return 0; + } + case WM_ACTIVATEAPP: + + if (wParam) + { + if (!ddraw->windowed) + { + if (!Direct3D9Active) + { + ChangeDisplaySettings(&ddraw->render.mode, CDS_FULLSCREEN); + + mouse_lock(); + } + + InterlockedExchange(&ddraw->minimized, FALSE); + } + + if (!ddraw->handlemouse) + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); + } + else + { + if (!ddraw->windowed && !ddraw->locked && ddraw->noactivateapp) + goto aapp_end; + + mouse_unlock(); + + if (ddraw->wine && LastSetWindowPosTick + 500 > timeGetTime()) + goto aapp_end; + + /* minimize our window on defocus when in fullscreen */ + if (!ddraw->windowed) + { + if (!Direct3D9Active) + { + ShowWindow(ddraw->hWnd, SW_MINIMIZE); + ChangeDisplaySettings(&ddraw->mode, 0); + } + + InterlockedExchange(&ddraw->minimized, TRUE); + } + } + + aapp_end: /* C&C and RA stop drawing when they receive this with FALSE wParam, disable in windowed mode */ if (ddraw->windowed || ddraw->noactivateapp) { @@ -1619,6 +1644,16 @@ HRESULT WINAPI DirectDrawEnumerateA(LPDDENUMCALLBACK lpCallback, LPVOID lpContex return DD_OK; } +//Force redraw when the "Player Profile" screen exits - Idea taken from Aqrit's war2 ddraw +static WNDPROC ButtonWndProc_original; +LRESULT __stdcall ButtonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_DESTROY) + RedrawWindow(NULL, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); + + return ButtonWndProc_original(hwnd, msg, wParam, lParam); +} + int stdout_open = 0; HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnknown FAR* pUnkOuter) { @@ -1668,5 +1703,16 @@ HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnk Settings_Load(); + if (ddraw->bnetHack) + { + WNDCLASS wc; + HINSTANCE hInst = GetModuleHandle(NULL); + GetClassInfo(NULL, "Button", &wc); + wc.hInstance = hInst; + ButtonWndProc_original = wc.lpfnWndProc; + wc.lpfnWndProc = ButtonWndProc; + RegisterClass(&wc); + } + return DD_OK; } diff --git a/src/mouse.c b/src/mouse.c index 01be2b8..f4573fd 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -250,7 +250,7 @@ void mouse_lock() if (ddraw->handlemouse) { - SetCapture(ddraw->hWnd); + //SetCapture(ddraw->hWnd); ClipCursor(&rc); while (ShowCursor(FALSE) > 0); } @@ -315,7 +315,7 @@ void mouse_unlock() } ClipCursor(NULL); - ReleaseCapture(); + //ReleaseCapture(); SetCursorPos( rc.left + ddraw->render.viewport.x + (ddraw->cursor.x * ddraw->render.scaleW), diff --git a/src/render.c b/src/render.c index cfb40eb..805d7da 100644 --- a/src/render.c +++ b/src/render.c @@ -787,6 +787,9 @@ static void Render() glEnd(); } + if (ddraw->bnetActive) + glClear(GL_COLOR_BUFFER_BIT); + SwapBuffers(ddraw->render.hDC); #if _DEBUG diff --git a/src/render_d3d9.c b/src/render_d3d9.c index 10968f7..361241c 100644 --- a/src/render_d3d9.c +++ b/src/render_d3d9.c @@ -410,6 +410,9 @@ DWORD WINAPI render_d3d9_main(void) IDirect3DDevice9_DrawPrimitive(D3dDev, D3DPT_TRIANGLESTRIP, 0, 2); IDirect3DDevice9_EndScene(D3dDev); + if (ddraw->bnetActive) + IDirect3DDevice9_Clear(D3dDev, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); + if (FAILED(IDirect3DDevice9_Present(D3dDev, NULL, NULL, NULL, NULL))) { DWORD_PTR dwResult; diff --git a/src/render_soft.c b/src/render_soft.c index 4ab7203..cc08735 100644 --- a/src/render_soft.c +++ b/src/render_soft.c @@ -103,7 +103,12 @@ DWORD WINAPI render_soft_main(void) EnumChildWindows(ddraw->hWnd, EnumChildProc, (LPARAM)ddraw->primary); } - if (scaleCutscene) + if (ddraw->bnetActive) + { + RECT rc = { 0, 0, ddraw->render.width, ddraw->render.height }; + FillRect(ddraw->render.hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); + } + else if (scaleCutscene) { StretchDIBits( ddraw->render.hDC, diff --git a/src/settings.c b/src/settings.c index f01b270..0aefa3f 100644 --- a/src/settings.c +++ b/src/settings.c @@ -49,6 +49,8 @@ void Settings_Load() ddraw->render.maxfps = GetInt("maxfps", 125); + ddraw->bnetHack = GetBool("bnetHack", TRUE); + if (ddraw->accurateTimers || ddraw->vsync) ddraw->fpsLimiter.hTimer = CreateWaitableTimer(NULL, TRUE, NULL); //can't fully set it up here due to missing ddraw->mode.dmDisplayFrequency @@ -102,6 +104,9 @@ void Settings_Load() GetString("renderer", "auto", tmp, sizeof(tmp)); printf("Using %s renderer\n", tmp); + if (ddraw->bnetHack && tolower(tmp[0]) != 'g') + ddraw->windowed = TRUE; + if (tolower(tmp[0]) == 's' || tolower(tmp[0]) == 'g') //gdi { ddraw->renderer = render_soft_main; @@ -274,6 +279,10 @@ static void CreateSettingsIni() "; Force CPU0 affinity, avoids crashes/freezing, *might* have a performance impact\n" "singlecpu=true\n" "\n" + "; Workaround for battle.net on Diablo and Warcraft 2 BNE\n" + "; Note: This hack as a negative side-effect, you can only play fullscreen with 'renderer=gdi' or via 'fullscreen=true'\n" + "bnetHack=false\n" + "\n" "\n" "\n" "; ### Game specific settings ###\n" @@ -389,11 +398,13 @@ static void CreateSettingsIni() "handlemouse=false\n" "maxfps=60\n" "\n" - "; Command & Conquer: Red Alert 2: Yuri's Revenge - XWIS\n" - "[Yuri's Revenge]\n" - "noactivateapp=true\n" - "handlemouse=false\n" - "maxfps=60\n" + "; Diablo\n" + "[Diablo]\n" + "bnetHack=true\n" + "\n" + "; Warcraft 2 Battle.net Edition\n" + "[Warcraft II BNE]\n" + "bnetHack=true\n" "\n" , fh); diff --git a/src/surface.c b/src/surface.c index 27d8088..7e434ba 100644 --- a/src/surface.c +++ b/src/surface.c @@ -992,6 +992,55 @@ HRESULT __stdcall ddraw_surface_Unlock(IDirectDrawSurfaceImpl *This, LPVOID lpRe printf("DirectDrawSurface::Unlock(This=%p, lpRect=%p)\n", This, lpRect); #endif + HWND hWnd = ddraw->bnetHack ? FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL) : NULL; + if (hWnd && (This->caps & DDSCAPS_PRIMARYSURFACE)) + { + if (ddraw->primary->palette && ddraw->primary->palette->data_rgb) + SetDIBColorTable(ddraw->primary->hDC, 0, 256, ddraw->primary->palette->data_rgb); + + //GdiTransparentBlt idea taken from Aqrit's war2 ddraw + + RGBQUAD quad; + GetDIBColorTable(ddraw->primary->hDC, 0xFE, 1, &quad); + COLORREF color = RGB(quad.rgbRed, quad.rgbGreen, quad.rgbBlue); + BOOL erase = FALSE; + + do + { + RECT rc; + if (GetWindowRect(hWnd, &rc)) + { + if (rc.bottom - rc.top == 479) + erase = TRUE; + + HDC hDC = GetDCEx(hWnd, NULL, DCX_PARENTCLIP | DCX_CACHE); + + GdiTransparentBlt( + hDC, + 0, + 0, + rc.right - rc.left, + rc.bottom - rc.top, + ddraw->primary->hDC, + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + color + ); + + ReleaseDC(hWnd, hDC); + } + + } while ((hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL))); + + if (erase) + { + DDBLTFX fx = { .dwFillColor = 0xFE }; + IDirectDrawSurface_Blt(This, NULL, NULL, NULL, DDBLT_COLORFILL, &fx); + } + } + if (This->caps & DDSCAPS_PRIMARYSURFACE && ddraw->render.run && (!(This->flags & DDSD_BACKBUFFERCOUNT) || This->lastFlipTick + FLIP_REDRAW_TIMEOUT < timeGetTime())) From 909c78e84d2f2fe587bec8133be71ab62246c2f7 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Sun, 17 Mar 2019 00:39:10 +0100 Subject: [PATCH 02/33] default to gdi renderer for now (opengl and direct3d only work in windowed mode) --- src/settings.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings.c b/src/settings.c index 0aefa3f..f76beec 100644 --- a/src/settings.c +++ b/src/settings.c @@ -401,10 +401,12 @@ static void CreateSettingsIni() "; Diablo\n" "[Diablo]\n" "bnetHack=true\n" + "renderer=gdi\n" "\n" "; Warcraft 2 Battle.net Edition\n" "[Warcraft II BNE]\n" "bnetHack=true\n" + "renderer=gdi\n" "\n" , fh); From 4506b00863239493d8e9c7f7e563f0ae2c94dc68 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Sun, 17 Mar 2019 01:17:02 +0100 Subject: [PATCH 03/33] Forces Nvidia and AMD high performance graphics. --- ddraw.def | 2 ++ src/main.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ddraw.def b/ddraw.def index ff1a079..c2b0db0 100644 --- a/ddraw.def +++ b/ddraw.def @@ -4,3 +4,5 @@ EXPORTS DirectDrawCreate @1 DirectDrawEnumerateA @2 GameHandlesClose DATA + NvOptimusEnablement DATA + AmdPowerXpressRequestHighPerformance DATA diff --git a/src/main.c b/src/main.c index a385297..0d7060b 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,8 @@ int WindowState = -1; BOOL Direct3D9Active; BOOL GameHandlesClose; BOOL ChildWindowExists; +DWORD NvOptimusEnablement = 1; +DWORD AmdPowerXpressRequestHighPerformance = 1; //BOOL WINAPI DllMainCRTStartup(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) BOOL WINAPI DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved) From 868a4479ba9aeccdd982293b5dc90cdd61cc45c6 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Sun, 17 Mar 2019 01:42:47 +0100 Subject: [PATCH 04/33] fix hidden cursor bug --- src/main.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 0d7060b..976a8df 100644 --- a/src/main.c +++ b/src/main.c @@ -1198,8 +1198,17 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) HWND hWnd = FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL); if (hWnd) { - RECT rc; - if (GetWindowRect(hWnd, &rc) && (rc.bottom - rc.top != 479)) + BOOL hideCursor = TRUE; + + do + { + RECT rc; + if (GetWindowRect(hWnd, &rc) && rc.bottom - rc.top == 479) + hideCursor = FALSE; + + } while ((hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL))); + + if (hideCursor) while (ShowCursor(FALSE) > 0); } From d42284c4bf7a5fb80de3c35897fba29481d2c048 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 19 Mar 2019 06:57:49 +0100 Subject: [PATCH 05/33] optional hotpatch support --- Makefile | 1 + cnc-ddraw.vcxproj | 3 + cnc-ddraw.vcxproj.filters | 9 ++ inc/hook.h | 51 ++++++++++ inc/main.h | 1 + inc/mouse.h | 27 ++++++ src/dinput.c | 5 +- src/hook.c | 190 ++++++++++++++++++++++++++++++++++++++ src/main.c | 57 ++++++------ src/mouse.c | 167 +++++++++------------------------ src/settings.c | 8 +- src/surface.c | 3 +- 12 files changed, 361 insertions(+), 161 deletions(-) create mode 100644 inc/hook.h create mode 100644 inc/mouse.h create mode 100644 src/hook.c diff --git a/Makefile b/Makefile index baab3fa..99e6501 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ FILES = src/debug.c \ src/settings.c \ src/lodepng.c \ src/dinput.c \ + src/hook.c \ src/opengl.c all: diff --git a/cnc-ddraw.vcxproj b/cnc-ddraw.vcxproj index e4f75cd..39864d4 100644 --- a/cnc-ddraw.vcxproj +++ b/cnc-ddraw.vcxproj @@ -14,6 +14,7 @@ + @@ -29,8 +30,10 @@ + + diff --git a/cnc-ddraw.vcxproj.filters b/cnc-ddraw.vcxproj.filters index 810b4c8..2423530 100644 --- a/cnc-ddraw.vcxproj.filters +++ b/cnc-ddraw.vcxproj.filters @@ -57,6 +57,9 @@ Source Files + + Source Files + @@ -101,6 +104,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/inc/hook.h b/inc/hook.h new file mode 100644 index 0000000..e4251e1 --- /dev/null +++ b/inc/hook.h @@ -0,0 +1,51 @@ +#ifndef HOOK_H +#define HOOK_H + +#include + +typedef HFONT(__stdcall* CREATEFONTINDIRECTA)(CONST LOGFONT*); + +typedef BOOL (WINAPI* GETCURSORPOSPROC)(LPPOINT); +typedef BOOL(WINAPI* CLIPCURSORPROC)(const RECT*); +typedef int (WINAPI* SHOWCURSORPROC)(BOOL); +typedef HCURSOR (WINAPI* SETCURSORPROC)(HCURSOR); +typedef BOOL (WINAPI* GETWINDOWRECTPROC)(HWND, LPRECT); +typedef BOOL (WINAPI* GETCLIENTRECTPROC)(HWND, LPRECT); +typedef BOOL (WINAPI* CLIENTTOSCREENPROC)(HWND, LPPOINT); +typedef BOOL (WINAPI* SCREENTOCLIENTPROC)(HWND, LPPOINT); +typedef BOOL (WINAPI* SETCURSORPOSPROC)(int, int); +typedef HWND (WINAPI* WINDOWFROMPOINTPROC)(POINT); +typedef BOOL (WINAPI* GETCLIPCURSORPROC)(LPRECT); +typedef BOOL (WINAPI* GETCURSORINFOPROC)(PCURSORINFO); +typedef int (WINAPI* GETSYSTEMMETRICSPROC)(int); +typedef BOOL (WINAPI* SETWINDOWPOSPROC)(HWND, HWND, int, int, int, int, UINT); +typedef BOOL (WINAPI* MOVEWINDOWPROC)(HWND, int, int, int, int, BOOL); +typedef LRESULT (WINAPI* SENDMESSAGEAPROC)(HWND, UINT, WPARAM, LPARAM); +typedef LONG (WINAPI* SETWINDOWLONGAPROC)(HWND, int, LONG); + +extern GETCURSORPOSPROC real_GetCursorPos; +extern CLIPCURSORPROC real_ClipCursor; +extern SHOWCURSORPROC real_ShowCursor; +extern SETCURSORPROC real_SetCursor; +extern GETWINDOWRECTPROC real_GetWindowRect; +extern GETCLIENTRECTPROC real_GetClientRect; +extern CLIENTTOSCREENPROC real_ClientToScreen; +extern SCREENTOCLIENTPROC real_ScreenToClient; +extern SETCURSORPOSPROC real_SetCursorPos; +extern WINDOWFROMPOINTPROC real_WindowFromPoint; +extern GETCLIPCURSORPROC real_GetClipCursor; +extern GETCURSORINFOPROC real_GetCursorInfo; +extern GETSYSTEMMETRICSPROC real_GetSystemMetrics; +extern SETWINDOWPOSPROC real_SetWindowPos; +extern MOVEWINDOWPROC real_MoveWindow; +extern SENDMESSAGEAPROC real_SendMessageA; +extern SETWINDOWLONGAPROC real_SetWindowLongA; + +extern BOOL Hook_Active; + +void Hook_Init(); +void Hook_PatchIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction); +PROC Hook_HotPatch(PROC function, PROC newFunction); +void Hook_TryHotPatch(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction, PROC *function); + +#endif diff --git a/inc/main.h b/inc/main.h index c6248a9..e3551ee 100644 --- a/inc/main.h +++ b/inc/main.h @@ -127,6 +127,7 @@ typedef struct IDirectDrawImpl BOOL accurateTimers; int bnetHack; BOOL bnetActive; + BOOL hotPatch; SpeedLimiter ticksLimiter; SpeedLimiter flipLimiter; SpeedLimiter fpsLimiter; diff --git a/inc/mouse.h b/inc/mouse.h new file mode 100644 index 0000000..dd18506 --- /dev/null +++ b/inc/mouse.h @@ -0,0 +1,27 @@ +#ifndef MOUSE_H +#define MOUSE_H + +#include + +void mouse_lock(); +void mouse_unlock(); + +BOOL WINAPI fake_GetCursorPos(LPPOINT lpPoint); +BOOL WINAPI fake_ClipCursor(const RECT *lpRect); +int WINAPI fake_ShowCursor(BOOL bShow); +HCURSOR WINAPI fake_SetCursor(HCURSOR hCursor); +BOOL WINAPI fake_GetWindowRect(HWND hWnd, LPRECT lpRect); +BOOL WINAPI fake_GetClientRect(HWND hWnd, LPRECT lpRect); +BOOL WINAPI fake_ClientToScreen(HWND hWnd, LPPOINT lpPoint); +BOOL WINAPI fake_ScreenToClient(HWND hWnd, LPPOINT lpPoint); +BOOL WINAPI fake_SetCursorPos(int X, int Y); +HWND WINAPI fake_WindowFromPoint(POINT Point); +BOOL WINAPI fake_GetClipCursor(LPRECT lpRect); +BOOL WINAPI fake_GetCursorInfo(PCURSORINFO pci); +int WINAPI fake_GetSystemMetrics(int nIndex); +BOOL WINAPI fake_SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); +BOOL WINAPI fake_MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint); +LRESULT WINAPI fake_SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +LONG WINAPI fake_SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong); + +#endif diff --git a/src/dinput.c b/src/dinput.c index 5185c53..75579e6 100644 --- a/src/dinput.c +++ b/src/dinput.c @@ -1,7 +1,6 @@ #include #include - -void HookIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction); +#include "hook.h" typedef HRESULT (WINAPI *DInputCreateA)(HINSTANCE, DWORD, LPDIRECTINPUTA*, LPUNKNOWN); typedef HRESULT (WINAPI *DICreateDevice)(IDirectInputA*, REFGUID, LPDIRECTINPUTDEVICEA *, LPUNKNOWN); @@ -64,5 +63,5 @@ static HRESULT WINAPI fake_DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, void dinput_init() { - HookIAT(GetModuleHandle(NULL), "dinput.dll", "DirectInputCreateA", (PROC)fake_DirectInputCreateA); + Hook_PatchIAT(GetModuleHandle(NULL), "dinput.dll", "DirectInputCreateA", (PROC)fake_DirectInputCreateA); } diff --git a/src/hook.c b/src/hook.c new file mode 100644 index 0000000..d85febe --- /dev/null +++ b/src/hook.c @@ -0,0 +1,190 @@ +#include +#include +#include "main.h" +#include "mouse.h" +#include "hook.h" + +BOOL Hook_Active; +GETCURSORPOSPROC real_GetCursorPos = GetCursorPos; +CLIPCURSORPROC real_ClipCursor = ClipCursor; +SHOWCURSORPROC real_ShowCursor = ShowCursor; +SETCURSORPROC real_SetCursor = SetCursor; +GETWINDOWRECTPROC real_GetWindowRect = GetWindowRect; +GETCLIENTRECTPROC real_GetClientRect = GetClientRect; +CLIENTTOSCREENPROC real_ClientToScreen = ClientToScreen; +SCREENTOCLIENTPROC real_ScreenToClient = ScreenToClient; +SETCURSORPOSPROC real_SetCursorPos = SetCursorPos; +WINDOWFROMPOINTPROC real_WindowFromPoint = WindowFromPoint; +GETCLIPCURSORPROC real_GetClipCursor = GetClipCursor; +GETCURSORINFOPROC real_GetCursorInfo = GetCursorInfo; +GETSYSTEMMETRICSPROC real_GetSystemMetrics = GetSystemMetrics; +SETWINDOWPOSPROC real_SetWindowPos = SetWindowPos; +MOVEWINDOWPROC real_MoveWindow = MoveWindow; +SENDMESSAGEAPROC real_SendMessageA = SendMessageA; +SETWINDOWLONGAPROC real_SetWindowLongA = SetWindowLongA; + +void Hook_PatchIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction) +{ + if (!hMod || hMod == INVALID_HANDLE_VALUE || !newFunction) + return; + + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMod; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + return; + + PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew); + if (pNTHeaders->Signature != IMAGE_NT_SIGNATURE) + return; + + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + + (DWORD)(pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); + + if (pImportDescriptor == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeaders) + return; + + while (pImportDescriptor->FirstThunk) + { + char *impModuleName = (char *)((DWORD)pDosHeader + (DWORD)(pImportDescriptor->Name)); + + if (_stricmp(impModuleName, moduleName) == 0) + { + PIMAGE_THUNK_DATA pFirstThunk = + (PIMAGE_THUNK_DATA)((DWORD)pDosHeader + (DWORD)pImportDescriptor->FirstThunk); + + PIMAGE_THUNK_DATA pOrigFirstThunk = + (PIMAGE_THUNK_DATA)((DWORD)pDosHeader + (DWORD)pImportDescriptor->OriginalFirstThunk); + + while (pFirstThunk->u1.Function && pOrigFirstThunk->u1.AddressOfData) + { + PIMAGE_IMPORT_BY_NAME pImport = + (PIMAGE_IMPORT_BY_NAME)((DWORD)pDosHeader + pOrigFirstThunk->u1.AddressOfData); + + if ((pOrigFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) == 0 && + _stricmp((const char *)pImport->Name, functionName) == 0) + { + DWORD oldProtect; + + if (VirtualProtect(&pFirstThunk->u1.Function, sizeof(DWORD), PAGE_READWRITE, &oldProtect)) + { + pFirstThunk->u1.Function = (DWORD)newFunction; + VirtualProtect(&pFirstThunk->u1.Function, sizeof(DWORD), oldProtect, &oldProtect); + } + + break; + } + + pFirstThunk++; + pOrigFirstThunk++; + } + } + + pImportDescriptor++; + } +} + +PROC Hook_HotPatch(PROC function, PROC newFunction) +{ + PROC result = function; + + unsigned short *bytes = (unsigned short *)function; + if (function && *bytes == 0xFF8B) // mov edi, edi + { + char *address = ((char *)function) - 5; + DWORD oldProtect; + + if (VirtualProtect(address, 7, PAGE_EXECUTE_READWRITE, &oldProtect)) + { + if (memcmp(address, (const char[]) { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }, 5) == 0 || + memcmp(address, (const char[]) { 0x90, 0x90, 0x90, 0x90, 0x90 }, 5) == 0) + { + address[0] = 0xE9; // long jump + *((DWORD *)(&address[1])) = ((char *)newFunction) - address - 5; + *((WORD *)(&address[5])) = 0xF9EB; // short jump to our long jump + + result = (PROC)(((char *)function) + 2); + } + + VirtualProtect(address, 7, oldProtect, &oldProtect); + } + } + + return result; +} + +void Hook_TryHotPatch(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction, PROC *function) +{ + PROC org = GetProcAddress(GetModuleHandle(moduleName), functionName); + if (org) + { + *function = Hook_HotPatch(org, newFunction); + + if (*function == org) // hotpatch failed... + Hook_PatchIAT(hMod, moduleName, functionName, newFunction); + } +} + +void Hook_Init() +{ + if (!Hook_Active) + { + Hook_Active = TRUE; + + if (ddraw->hotPatch) + { + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos, (PROC *)&real_GetCursorPos); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "ClipCursor", (PROC)fake_ClipCursor, (PROC *)&real_ClipCursor); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "ShowCursor", (PROC)fake_ShowCursor, (PROC *)&real_ShowCursor); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "SetCursor", (PROC)fake_SetCursor, (PROC *)&real_SetCursor); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "GetWindowRect", (PROC)fake_GetWindowRect, (PROC *)&real_GetWindowRect); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "GetClientRect", (PROC)fake_GetClientRect, (PROC *)&real_GetClientRect); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "ClientToScreen", (PROC)fake_ClientToScreen, (PROC *)&real_ClientToScreen); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "ScreenToClient", (PROC)fake_ScreenToClient, (PROC *)&real_ScreenToClient); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "SetCursorPos", (PROC)fake_SetCursorPos, (PROC *)&real_SetCursorPos); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "GetClipCursor", (PROC)fake_GetClipCursor, (PROC *)&real_GetClipCursor); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "WindowFromPoint", (PROC)fake_WindowFromPoint, (PROC *)&real_WindowFromPoint); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "GetCursorInfo", (PROC)fake_GetCursorInfo, (PROC *)&real_GetCursorInfo); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "GetSystemMetrics", (PROC)fake_GetSystemMetrics, (PROC *)&real_GetSystemMetrics); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "SetWindowPos", (PROC)fake_SetWindowPos, (PROC *)&real_SetWindowPos); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "MoveWindow", (PROC)fake_MoveWindow, (PROC *)&real_MoveWindow); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "SendMessageA", (PROC)fake_SendMessageA, (PROC *)&real_SendMessageA); + Hook_TryHotPatch( + GetModuleHandle(NULL), "user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA, (PROC *)&real_SetWindowLongA); + } + else + { + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ClipCursor", (PROC)fake_ClipCursor); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ShowCursor", (PROC)fake_ShowCursor); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetCursor", (PROC)fake_SetCursor); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetWindowRect", (PROC)fake_GetWindowRect); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetClientRect", (PROC)fake_GetClientRect); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ClientToScreen", (PROC)fake_ClientToScreen); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ScreenToClient", (PROC)fake_ScreenToClient); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetCursorPos", (PROC)fake_SetCursorPos); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetClipCursor", (PROC)fake_GetClipCursor); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "WindowFromPoint", (PROC)fake_WindowFromPoint); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorInfo", (PROC)fake_GetCursorInfo); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetSystemMetrics", (PROC)fake_GetSystemMetrics); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetWindowPos", (PROC)fake_SetWindowPos); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "MoveWindow", (PROC)fake_MoveWindow); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SendMessageA", (PROC)fake_SendMessageA); + Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA); + } + } +} diff --git a/src/main.c b/src/main.c index 976a8df..611e120 100644 --- a/src/main.c +++ b/src/main.c @@ -27,15 +27,12 @@ #include "palette.h" #include "surface.h" #include "clipper.h" +#include "hook.h" +#include "mouse.h" #include "render_d3d9.h" #define IDR_MYMENU 93 -BOOL WINAPI fake_GetCursorPos(LPPOINT lpPoint); -void mouse_init(); -void mouse_lock(); -void mouse_unlock(); - BOOL screenshot(struct IDirectDrawSurfaceImpl *); void Settings_Load(); void Settings_Save(RECT *lpRect, int windowState); @@ -115,7 +112,7 @@ BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam) RECT size; RECT pos; - if (GetClientRect(hWnd, &size) && GetWindowRect(hWnd, &pos) && size.right > 1 && size.bottom > 1) + if (real_GetClientRect(hWnd, &size) && real_GetWindowRect(hWnd, &pos) && size.right > 1 && size.bottom > 1) { ChildWindowExists = TRUE; @@ -552,13 +549,13 @@ HRESULT __stdcall ddraw_SetDisplayMode(IDirectDrawImpl *This, DWORD width, DWORD return DDERR_UNSUPPORTED; } - const HANDLE hbicon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDR_MYMENU), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0); + const HANDLE hbicon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDR_MYMENU), IMAGE_ICON, real_GetSystemMetrics(SM_CXICON), real_GetSystemMetrics(SM_CYICON), 0); if (hbicon) - SendMessage(This->hWnd, WM_SETICON, ICON_BIG, (LPARAM)hbicon); + real_SendMessageA(This->hWnd, WM_SETICON, ICON_BIG, (LPARAM)hbicon); - const HANDLE hsicon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDR_MYMENU), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0); + const HANDLE hsicon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDR_MYMENU), IMAGE_ICON, real_GetSystemMetrics(SM_CXSMICON), real_GetSystemMetrics(SM_CYSMICON), 0); if (hsicon) - SendMessage(This->hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hsicon); + real_SendMessageA(This->hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hsicon); } if (ddraw->altenter) @@ -764,23 +761,23 @@ HRESULT __stdcall ddraw_SetDisplayMode(IDirectDrawImpl *This, DWORD width, DWORD { if (!This->border) { - SetWindowLong(This->hWnd, GWL_STYLE, GetWindowLong(This->hWnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU)); + real_SetWindowLongA(This->hWnd, GWL_STYLE, GetWindowLong(This->hWnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU)); } else { - SetWindowLong(This->hWnd, GWL_STYLE, (GetWindowLong(This->hWnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW) & ~WS_MAXIMIZEBOX); + real_SetWindowLongA(This->hWnd, GWL_STYLE, (GetWindowLong(This->hWnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW) & ~WS_MAXIMIZEBOX); } if (ddraw->wine) - SetWindowLong(This->hWnd, GWL_STYLE, (GetWindowLong(This->hWnd, GWL_STYLE) | WS_MINIMIZEBOX) & ~(WS_MAXIMIZEBOX | WS_THICKFRAME)); + real_SetWindowLongA(This->hWnd, GWL_STYLE, (GetWindowLong(This->hWnd, GWL_STYLE) | WS_MINIMIZEBOX) & ~(WS_MAXIMIZEBOX | WS_THICKFRAME)); /* center the window with correct dimensions */ int x = (WindowRect.left != -32000) ? WindowRect.left : (This->mode.dmPelsWidth / 2) - (This->render.width / 2); int y = (WindowRect.top != -32000) ? WindowRect.top : (This->mode.dmPelsHeight / 2) - (This->render.height / 2); RECT dst = { x, y, This->render.width + x, This->render.height + y }; AdjustWindowRect(&dst, GetWindowLong(This->hWnd, GWL_STYLE), FALSE); - SetWindowPos(ddraw->hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - MoveWindow(This->hWnd, dst.left, dst.top, (dst.right - dst.left), (dst.bottom - dst.top), TRUE); + real_SetWindowPos(ddraw->hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + real_MoveWindow(This->hWnd, dst.left, dst.top, (dst.right - dst.left), (dst.bottom - dst.top), TRUE); if (This->renderer == render_d3d9_main) InitDirect3D9(); @@ -800,9 +797,9 @@ HRESULT __stdcall ddraw_SetDisplayMode(IDirectDrawImpl *This, DWORD width, DWORD } if (ddraw->wine) - SetWindowLong(This->hWnd, GWL_STYLE, GetWindowLong(This->hWnd, GWL_STYLE) | WS_MINIMIZEBOX); + real_SetWindowLongA(This->hWnd, GWL_STYLE, GetWindowLong(This->hWnd, GWL_STYLE) | WS_MINIMIZEBOX); - SetWindowPos(This->hWnd, HWND_TOPMOST, 0, 0, This->render.width, This->render.height, SWP_SHOWWINDOW); + real_SetWindowPos(This->hWnd, HWND_TOPMOST, 0, 0, This->render.width, This->render.height, SWP_SHOWWINDOW); LastSetWindowPosTick = timeGetTime(); mouse_lock(); @@ -840,7 +837,7 @@ void ToggleFullscreen() { mouse_unlock(); WindowState = ddraw->windowed = FALSE; - SetWindowLong(ddraw->hWnd, GWL_STYLE, GetWindowLong(ddraw->hWnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU)); + real_SetWindowLongA(ddraw->hWnd, GWL_STYLE, GetWindowLong(ddraw->hWnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU)); ddraw->altenter = TRUE; ddraw_SetDisplayMode(ddraw, ddraw->width, ddraw->height, ddraw->bpp); mouse_lock(); @@ -951,8 +948,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (!ddraw->windowed) { LastSetWindowPosTick = timeGetTime(); - SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 1, 1, ddraw->render.width, ddraw->render.height, SWP_SHOWWINDOW); - SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 0, 0, ddraw->render.width, ddraw->render.height, SWP_SHOWWINDOW); + real_SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 1, 1, ddraw->render.width, ddraw->render.height, SWP_SHOWWINDOW); + real_SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 0, 0, ddraw->render.width, ddraw->render.height, SWP_SHOWWINDOW); } return 0; } @@ -1203,13 +1200,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) do { RECT rc; - if (GetWindowRect(hWnd, &rc) && rc.bottom - rc.top == 479) + if (real_GetWindowRect(hWnd, &rc) && rc.bottom - rc.top == 479) hideCursor = FALSE; } while ((hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL))); if (hideCursor) - while (ShowCursor(FALSE) > 0); + while (real_ShowCursor(FALSE) > 0); } ddraw->bnetActive = !wParam; @@ -1282,8 +1279,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_AUTORENDERER: { mouse_unlock(); - SetWindowPos(ddraw->hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + real_SetWindowPos(ddraw->hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + real_SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); if (!ddraw->wine) { ShowWindow(ddraw->hWnd, SW_MINIMIZE); @@ -1448,11 +1445,11 @@ HRESULT __stdcall ddraw_SetCooperativeLevel(IDirectDrawImpl *This, HWND hWnd, DW if (!This->WndProc) { - mouse_init(); + Hook_Init(); This->WndProc = (LRESULT(CALLBACK *)(HWND, UINT, WPARAM, LPARAM))GetWindowLong(hWnd, GWL_WNDPROC); - SetWindowLong(This->hWnd, GWL_WNDPROC, (LONG)WndProc); + real_SetWindowLongA(This->hWnd, GWL_WNDPROC, (LONG)WndProc); if (!This->render.hDC) { @@ -1470,11 +1467,11 @@ HRESULT __stdcall ddraw_SetCooperativeLevel(IDirectDrawImpl *This, HWND hWnd, DW if (ddraw->handlemouse && ddraw->windowed) { - while (ShowCursor(FALSE) > 0); //workaround for direct input games - while (ShowCursor(TRUE) < 0); + while (real_ShowCursor(FALSE) > 0); //workaround for direct input games + while (real_ShowCursor(TRUE) < 0); } - SetCursor(LoadCursor(NULL, IDC_ARROW)); + real_SetCursor(LoadCursor(NULL, IDC_ARROW)); GetWindowText(This->hWnd, (LPTSTR)&This->title, sizeof(This->title)); @@ -1593,7 +1590,7 @@ ULONG __stdcall ddraw_Release(IDirectDrawImpl *This) DeleteCriticalSection(&This->cs); /* restore old wndproc, subsequent ddraw creation will otherwise fail */ - SetWindowLong(This->hWnd, GWL_WNDPROC, (LONG)This->WndProc); + real_SetWindowLongA(This->hWnd, GWL_WNDPROC, (LONG)This->WndProc); HeapFree(GetProcessHeap(), 0, This); ddraw = NULL; return 0; diff --git a/src/mouse.c b/src/mouse.c index f4573fd..d9d19bb 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -14,27 +14,25 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* This is a special mouse coordinate fix for games that use GetCursorPos and expect to be in fullscreen */ - #include #include #include "main.h" #include "surface.h" +#include "hook.h" -BOOL mouse_active = FALSE; int yAdjust = 0; BOOL WINAPI fake_GetCursorPos(LPPOINT lpPoint) { POINT pt, realpt; - if (!GetCursorPos(&pt) || !ddraw) + if (!real_GetCursorPos(&pt) || !ddraw) return FALSE; realpt.x = pt.x; realpt.y = pt.y; - if(ddraw->locked && (!ddraw->windowed || ScreenToClient(ddraw->hWnd, &pt))) + if(ddraw->locked && (!ddraw->windowed || real_ScreenToClient(ddraw->hWnd, &pt))) { //fallback solution for possible ClipCursor failure int diffx = 0, diffy = 0; @@ -66,7 +64,7 @@ BOOL WINAPI fake_GetCursorPos(LPPOINT lpPoint) } if (diffx || diffy) - SetCursorPos(realpt.x - diffx, realpt.y - diffy); + real_SetCursorPos(realpt.x - diffx, realpt.y - diffy); if(ddraw->adjmouse) @@ -98,7 +96,7 @@ BOOL WINAPI fake_GetCursorPos(LPPOINT lpPoint) } if (diffx || diffy) - SetCursorPos(realpt.x - diffx, realpt.y - diffy); + real_SetCursorPos(realpt.x - diffx, realpt.y - diffy); } } @@ -127,7 +125,7 @@ int WINAPI fake_ShowCursor(BOOL bShow) static int count; if (ddraw && !ddraw->handlemouse) - return ShowCursor(bShow); + return real_ShowCursor(bShow); return bShow ? ++count : --count; } @@ -135,70 +133,11 @@ int WINAPI fake_ShowCursor(BOOL bShow) HCURSOR WINAPI fake_SetCursor(HCURSOR hCursor) { if (ddraw && !ddraw->handlemouse) - return SetCursor(hCursor); + return real_SetCursor(hCursor); return NULL; } -void HookIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction) -{ - if (!hMod || hMod == INVALID_HANDLE_VALUE || !newFunction) - return; - - PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMod; - if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) - return; - - PIMAGE_NT_HEADERS pNTHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew); - if (pNTHeaders->Signature != IMAGE_NT_SIGNATURE) - return; - - PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + - (DWORD)(pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); - - if (pImportDescriptor == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeaders) - return; - - while (pImportDescriptor->FirstThunk) - { - char *impModuleName = (char *)((DWORD)pDosHeader + (DWORD)(pImportDescriptor->Name)); - - if (_stricmp(impModuleName, moduleName) == 0) - { - PIMAGE_THUNK_DATA pFirstThunk = - (PIMAGE_THUNK_DATA)((DWORD)pDosHeader + (DWORD)pImportDescriptor->FirstThunk); - - PIMAGE_THUNK_DATA pOrigFirstThunk = - (PIMAGE_THUNK_DATA)((DWORD)pDosHeader + (DWORD)pImportDescriptor->OriginalFirstThunk); - - while (pFirstThunk->u1.Function && pOrigFirstThunk->u1.AddressOfData) - { - PIMAGE_IMPORT_BY_NAME pImport = - (PIMAGE_IMPORT_BY_NAME)((DWORD)pDosHeader + pOrigFirstThunk->u1.AddressOfData); - - if ((pOrigFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) == 0 && - _stricmp((const char *)pImport->Name, functionName) == 0) - { - DWORD oldProtect; - - if (VirtualProtect(&pFirstThunk->u1.Function, sizeof(DWORD), PAGE_READWRITE, &oldProtect)) - { - pFirstThunk->u1.Function = (DWORD)newFunction; - VirtualProtect(&pFirstThunk->u1.Function, sizeof(DWORD), oldProtect, &oldProtect); - } - - break; - } - - pFirstThunk++; - pOrigFirstThunk++; - } - } - - pImportDescriptor++; - } -} - void mouse_lock() { RECT rc; @@ -206,15 +145,15 @@ void mouse_lock() if (ddraw->devmode) { if (ddraw->handlemouse) - while(ShowCursor(FALSE) > 0); + while(real_ShowCursor(FALSE) > 0); return; } - if (mouse_active && !ddraw->locked) + if (Hook_Active && !ddraw->locked) { // Get the window client area. - GetClientRect(ddraw->hWnd, &rc); + real_GetClientRect(ddraw->hWnd, &rc); if(ddraw->adjmouse) { @@ -230,8 +169,8 @@ void mouse_lock() // Convert the client area to screen coordinates. POINT pt = { rc.left, rc.top }; POINT pt2 = { rc.right, rc.bottom }; - ClientToScreen(ddraw->hWnd, &pt); - ClientToScreen(ddraw->hWnd, &pt2); + real_ClientToScreen(ddraw->hWnd, &pt); + real_ClientToScreen(ddraw->hWnd, &pt2); SetRect(&rc, pt.x, pt.y, pt2.x, pt2.y); @@ -239,29 +178,29 @@ void mouse_lock() if(ddraw->adjmouse) { - SetCursorPos( + real_SetCursorPos( rc.left + (ddraw->cursor.x * ddraw->render.scaleW), rc.top + ((ddraw->cursor.y - yAdjust) * ddraw->render.scaleH)); } else { - SetCursorPos(rc.left + ddraw->cursor.x, rc.top + ddraw->cursor.y - yAdjust); + real_SetCursorPos(rc.left + ddraw->cursor.x, rc.top + ddraw->cursor.y - yAdjust); } if (ddraw->handlemouse) { //SetCapture(ddraw->hWnd); - ClipCursor(&rc); - while (ShowCursor(FALSE) > 0); + real_ClipCursor(&rc); + while (real_ShowCursor(FALSE) > 0); } else { if (ddraw->hidecursor) { ddraw->hidecursor = FALSE; - ShowCursor(FALSE); + real_ShowCursor(FALSE); } - ClipCursor(&rc); + real_ClipCursor(&rc); } ddraw->locked = TRUE; @@ -275,12 +214,12 @@ void mouse_unlock() if (ddraw->devmode) { if (ddraw->handlemouse) - while(ShowCursor(TRUE) < 0); + while(real_ShowCursor(TRUE) < 0); return; } - if(!mouse_active) + if(!Hook_Active) { return; } @@ -290,34 +229,34 @@ void mouse_unlock() ddraw->locked = FALSE; // Get the window client area. - GetClientRect(ddraw->hWnd, &rc); + real_GetClientRect(ddraw->hWnd, &rc); // Convert the client area to screen coordinates. POINT pt = { rc.left, rc.top }; POINT pt2 = { rc.right, rc.bottom }; - ClientToScreen(ddraw->hWnd, &pt); - ClientToScreen(ddraw->hWnd, &pt2); + real_ClientToScreen(ddraw->hWnd, &pt); + real_ClientToScreen(ddraw->hWnd, &pt2); SetRect(&rc, pt.x, pt.y, pt2.x, pt2.y); if (ddraw->handlemouse) { - while (ShowCursor(TRUE) < 0); - SetCursor(LoadCursor(NULL, IDC_ARROW)); + while (real_ShowCursor(TRUE) < 0); + real_SetCursor(LoadCursor(NULL, IDC_ARROW)); } else { CURSORINFO ci = { .cbSize = sizeof(CURSORINFO) }; - if (GetCursorInfo(&ci) && ci.flags == 0) + if (real_GetCursorInfo(&ci) && ci.flags == 0) { ddraw->hidecursor = TRUE; - while (ShowCursor(TRUE) < 0); + while (real_ShowCursor(TRUE) < 0); } } - ClipCursor(NULL); + real_ClipCursor(NULL); //ReleaseCapture(); - SetCursorPos( + real_SetCursorPos( rc.left + ddraw->render.viewport.x + (ddraw->cursor.x * ddraw->render.scaleW), rc.top + ddraw->render.viewport.y + ((ddraw->cursor.y + yAdjust) * ddraw->render.scaleH)); } @@ -338,11 +277,11 @@ BOOL WINAPI fake_GetWindowRect(HWND hWnd, LPRECT lpRect) } else { - return GetWindowRect(hWnd, lpRect) && MapWindowPoints(HWND_DESKTOP, ddraw->hWnd, (LPPOINT)lpRect, 2); + return real_GetWindowRect(hWnd, lpRect) && MapWindowPoints(HWND_DESKTOP, ddraw->hWnd, (LPPOINT)lpRect, 2); } } - return GetWindowRect(hWnd, lpRect); + return real_GetWindowRect(hWnd, lpRect); } BOOL WINAPI fake_GetClientRect(HWND hWnd, LPRECT lpRect) @@ -357,13 +296,13 @@ BOOL WINAPI fake_GetClientRect(HWND hWnd, LPRECT lpRect) return TRUE; } - return GetClientRect(hWnd, lpRect); + return real_GetClientRect(hWnd, lpRect); } BOOL WINAPI fake_ClientToScreen(HWND hWnd, LPPOINT lpPoint) { if (ddraw && ddraw->hWnd != hWnd) - return ClientToScreen(hWnd, lpPoint) && ScreenToClient(ddraw->hWnd, lpPoint); + return real_ClientToScreen(hWnd, lpPoint) && real_ScreenToClient(ddraw->hWnd, lpPoint); return TRUE; } @@ -371,7 +310,7 @@ BOOL WINAPI fake_ClientToScreen(HWND hWnd, LPPOINT lpPoint) BOOL WINAPI fake_ScreenToClient(HWND hWnd, LPPOINT lpPoint) { if (ddraw && ddraw->hWnd != hWnd) - return ClientToScreen(ddraw->hWnd, lpPoint) && ScreenToClient(hWnd, lpPoint); + return real_ClientToScreen(ddraw->hWnd, lpPoint) && real_ScreenToClient(hWnd, lpPoint); return TRUE; } @@ -379,13 +318,13 @@ BOOL WINAPI fake_ScreenToClient(HWND hWnd, LPPOINT lpPoint) BOOL WINAPI fake_SetCursorPos(int X, int Y) { POINT pt = { X, Y }; - return ddraw && ClientToScreen(ddraw->hWnd, &pt) && SetCursorPos(pt.x, pt.y); + return ddraw && real_ClientToScreen(ddraw->hWnd, &pt) && real_SetCursorPos(pt.x, pt.y); } HWND WINAPI fake_WindowFromPoint(POINT Point) { POINT pt = { Point.x, Point.y }; - return ddraw && ClientToScreen(ddraw->hWnd, &pt) ? WindowFromPoint(pt) : NULL; + return ddraw && real_ClientToScreen(ddraw->hWnd, &pt) ? real_WindowFromPoint(pt) : NULL; } BOOL WINAPI fake_GetClipCursor(LPRECT lpRect) @@ -405,7 +344,7 @@ BOOL WINAPI fake_GetClipCursor(LPRECT lpRect) BOOL WINAPI fake_GetCursorInfo(PCURSORINFO pci) { - return pci && ddraw && GetCursorInfo(pci) && ScreenToClient(ddraw->hWnd, &pci->ptScreenPos); + return pci && ddraw && real_GetCursorInfo(pci) && real_ScreenToClient(ddraw->hWnd, &pci->ptScreenPos); } int WINAPI fake_GetSystemMetrics(int nIndex) @@ -419,7 +358,7 @@ int WINAPI fake_GetSystemMetrics(int nIndex) return ddraw->height; } - return GetSystemMetrics(nIndex); + return real_GetSystemMetrics(nIndex); } BOOL WINAPI fake_SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags) @@ -427,7 +366,7 @@ BOOL WINAPI fake_SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int if (ddraw && ddraw->hWnd == hWnd) return TRUE; - return SetWindowPos(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); + return real_SetWindowPos(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); } BOOL WINAPI fake_MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint) @@ -435,12 +374,12 @@ BOOL WINAPI fake_MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BO if (ddraw && ddraw->hWnd == hWnd) return TRUE; - return MoveWindow(hWnd, X, Y, nWidth, nHeight, bRepaint); + return real_MoveWindow(hWnd, X, Y, nWidth, nHeight, bRepaint); } LRESULT WINAPI fake_SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { - LRESULT result = SendMessageA(hWnd, Msg, wParam, lParam); + LRESULT result = real_SendMessageA(hWnd, Msg, wParam, lParam); if (result && ddraw && Msg == CB_GETDROPPEDCONTROLRECT) { @@ -457,27 +396,5 @@ LONG WINAPI fake_SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong) if (ddraw && ddraw->hWnd == hWnd && nIndex == GWL_STYLE) return 0; - return SetWindowLongA(hWnd, nIndex, dwNewLong); -} - -void mouse_init() -{ - HookIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos); - HookIAT(GetModuleHandle(NULL), "user32.dll", "ClipCursor", (PROC)fake_ClipCursor); - HookIAT(GetModuleHandle(NULL), "user32.dll", "ShowCursor", (PROC)fake_ShowCursor); - HookIAT(GetModuleHandle(NULL), "user32.dll", "SetCursor", (PROC)fake_SetCursor); - HookIAT(GetModuleHandle(NULL), "user32.dll", "GetWindowRect", (PROC)fake_GetWindowRect); - HookIAT(GetModuleHandle(NULL), "user32.dll", "GetClientRect", (PROC)fake_GetClientRect); - HookIAT(GetModuleHandle(NULL), "user32.dll", "ClientToScreen", (PROC)fake_ClientToScreen); - HookIAT(GetModuleHandle(NULL), "user32.dll", "ScreenToClient", (PROC)fake_ScreenToClient); - HookIAT(GetModuleHandle(NULL), "user32.dll", "SetCursorPos", (PROC)fake_SetCursorPos); - HookIAT(GetModuleHandle(NULL), "user32.dll", "GetClipCursor", (PROC)fake_GetClipCursor); - HookIAT(GetModuleHandle(NULL), "user32.dll", "WindowFromPoint", (PROC)fake_WindowFromPoint); - HookIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorInfo", (PROC)fake_GetCursorInfo); - HookIAT(GetModuleHandle(NULL), "user32.dll", "GetSystemMetrics", (PROC)fake_GetSystemMetrics); - HookIAT(GetModuleHandle(NULL), "user32.dll", "SetWindowPos", (PROC)fake_SetWindowPos); - HookIAT(GetModuleHandle(NULL), "user32.dll", "MoveWindow", (PROC)fake_MoveWindow); - HookIAT(GetModuleHandle(NULL), "user32.dll", "SendMessageA", (PROC)fake_SendMessageA); - HookIAT(GetModuleHandle(NULL), "user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA); - mouse_active = TRUE; + return real_SetWindowLongA(hWnd, nIndex, dwNewLong); } diff --git a/src/settings.c b/src/settings.c index f76beec..986033a 100644 --- a/src/settings.c +++ b/src/settings.c @@ -41,6 +41,8 @@ void Settings_Load() ddraw->noactivateapp = GetBool("noactivateapp", FALSE); ddraw->vhack = GetBool("vhack", FALSE); ddraw->accurateTimers = GetBool("accuratetimers", FALSE); + ddraw->hotPatch = GetBool("hotPatch", FALSE); + ddraw->bnetHack = GetBool("bnetHack", TRUE); WindowRect.right = GetInt("width", 0); WindowRect.bottom = GetInt("height", 0); @@ -49,8 +51,6 @@ void Settings_Load() ddraw->render.maxfps = GetInt("maxfps", 125); - ddraw->bnetHack = GetBool("bnetHack", TRUE); - if (ddraw->accurateTimers || ddraw->vsync) ddraw->fpsLimiter.hTimer = CreateWaitableTimer(NULL, TRUE, NULL); //can't fully set it up here due to missing ddraw->mode.dmDisplayFrequency @@ -279,6 +279,10 @@ static void CreateSettingsIni() "; Force CPU0 affinity, avoids crashes/freezing, *might* have a performance impact\n" "singlecpu=true\n" "\n" + "; Use hotpatching rather than IAT hooking\n" + "; Note: Can be used to fix issues related to new features added by cnc-ddraw such as windowed mode or stretching\n" + "hotPatch=false\n" + "\n" "; Workaround for battle.net on Diablo and Warcraft 2 BNE\n" "; Note: This hack as a negative side-effect, you can only play fullscreen with 'renderer=gdi' or via 'fullscreen=true'\n" "bnetHack=false\n" diff --git a/src/surface.c b/src/surface.c index 7e434ba..5c84db9 100644 --- a/src/surface.c +++ b/src/surface.c @@ -18,6 +18,7 @@ #include #include "main.h" +#include "hook.h" #include "surface.h" #include "scale_pattern.h" @@ -1008,7 +1009,7 @@ HRESULT __stdcall ddraw_surface_Unlock(IDirectDrawSurfaceImpl *This, LPVOID lpRe do { RECT rc; - if (GetWindowRect(hWnd, &rc)) + if (real_GetWindowRect(hWnd, &rc)) { if (rc.bottom - rc.top == 479) erase = TRUE; From 11c103f36c41922c69835225446c7c105a3d99f9 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 19 Mar 2019 09:43:47 +0100 Subject: [PATCH 06/33] some more hook.c tweaks --- inc/hook.h | 2 +- src/hook.c | 88 +++++++++++++++++------------------------------------- 2 files changed, 28 insertions(+), 62 deletions(-) diff --git a/inc/hook.h b/inc/hook.h index e4251e1..199eddc 100644 --- a/inc/hook.h +++ b/inc/hook.h @@ -46,6 +46,6 @@ extern BOOL Hook_Active; void Hook_Init(); void Hook_PatchIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction); PROC Hook_HotPatch(PROC function, PROC newFunction); -void Hook_TryHotPatch(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction, PROC *function); +void Hook_TryHotPatch(char *moduleName, char *functionName, PROC newFunction, PROC *function); #endif diff --git a/src/hook.c b/src/hook.c index d85febe..1aa5a83 100644 --- a/src/hook.c +++ b/src/hook.c @@ -111,15 +111,19 @@ PROC Hook_HotPatch(PROC function, PROC newFunction) return result; } -void Hook_TryHotPatch(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction, PROC *function) +void Hook_TryHotPatch(char *moduleName, char *functionName, PROC newFunction, PROC *function) { - PROC org = GetProcAddress(GetModuleHandle(moduleName), functionName); - if (org) + FARPROC org = GetProcAddress(GetModuleHandle(moduleName), functionName); + if (ddraw->hotPatch && org) { *function = Hook_HotPatch(org, newFunction); if (*function == org) // hotpatch failed... - Hook_PatchIAT(hMod, moduleName, functionName, newFunction); + Hook_PatchIAT(GetModuleHandle(NULL), moduleName, functionName, newFunction); + } + else + { + Hook_PatchIAT(GetModuleHandle(NULL), moduleName, functionName, newFunction); } } @@ -129,62 +133,24 @@ void Hook_Init() { Hook_Active = TRUE; - if (ddraw->hotPatch) - { - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos, (PROC *)&real_GetCursorPos); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "ClipCursor", (PROC)fake_ClipCursor, (PROC *)&real_ClipCursor); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "ShowCursor", (PROC)fake_ShowCursor, (PROC *)&real_ShowCursor); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "SetCursor", (PROC)fake_SetCursor, (PROC *)&real_SetCursor); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "GetWindowRect", (PROC)fake_GetWindowRect, (PROC *)&real_GetWindowRect); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "GetClientRect", (PROC)fake_GetClientRect, (PROC *)&real_GetClientRect); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "ClientToScreen", (PROC)fake_ClientToScreen, (PROC *)&real_ClientToScreen); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "ScreenToClient", (PROC)fake_ScreenToClient, (PROC *)&real_ScreenToClient); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "SetCursorPos", (PROC)fake_SetCursorPos, (PROC *)&real_SetCursorPos); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "GetClipCursor", (PROC)fake_GetClipCursor, (PROC *)&real_GetClipCursor); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "WindowFromPoint", (PROC)fake_WindowFromPoint, (PROC *)&real_WindowFromPoint); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "GetCursorInfo", (PROC)fake_GetCursorInfo, (PROC *)&real_GetCursorInfo); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "GetSystemMetrics", (PROC)fake_GetSystemMetrics, (PROC *)&real_GetSystemMetrics); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "SetWindowPos", (PROC)fake_SetWindowPos, (PROC *)&real_SetWindowPos); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "MoveWindow", (PROC)fake_MoveWindow, (PROC *)&real_MoveWindow); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "SendMessageA", (PROC)fake_SendMessageA, (PROC *)&real_SendMessageA); - Hook_TryHotPatch( - GetModuleHandle(NULL), "user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA, (PROC *)&real_SetWindowLongA); - } - else - { - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ClipCursor", (PROC)fake_ClipCursor); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ShowCursor", (PROC)fake_ShowCursor); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetCursor", (PROC)fake_SetCursor); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetWindowRect", (PROC)fake_GetWindowRect); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetClientRect", (PROC)fake_GetClientRect); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ClientToScreen", (PROC)fake_ClientToScreen); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "ScreenToClient", (PROC)fake_ScreenToClient); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetCursorPos", (PROC)fake_SetCursorPos); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetClipCursor", (PROC)fake_GetClipCursor); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "WindowFromPoint", (PROC)fake_WindowFromPoint); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorInfo", (PROC)fake_GetCursorInfo); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetSystemMetrics", (PROC)fake_GetSystemMetrics); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetWindowPos", (PROC)fake_SetWindowPos); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "MoveWindow", (PROC)fake_MoveWindow); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SendMessageA", (PROC)fake_SendMessageA); - Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA); - } + Hook_TryHotPatch("user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos, (PROC *)&real_GetCursorPos); + Hook_TryHotPatch("user32.dll", "ClipCursor", (PROC)fake_ClipCursor, (PROC *)&real_ClipCursor); + Hook_TryHotPatch("user32.dll", "ShowCursor", (PROC)fake_ShowCursor, (PROC *)&real_ShowCursor); + Hook_TryHotPatch("user32.dll", "SetCursor", (PROC)fake_SetCursor, (PROC *)&real_SetCursor); + Hook_TryHotPatch("user32.dll", "GetWindowRect", (PROC)fake_GetWindowRect, (PROC *)&real_GetWindowRect); + Hook_TryHotPatch("user32.dll", "GetClientRect", (PROC)fake_GetClientRect, (PROC *)&real_GetClientRect); + Hook_TryHotPatch("user32.dll", "ClientToScreen", (PROC)fake_ClientToScreen, (PROC *)&real_ClientToScreen); + Hook_TryHotPatch("user32.dll", "ScreenToClient", (PROC)fake_ScreenToClient, (PROC *)&real_ScreenToClient); + Hook_TryHotPatch("user32.dll", "SetCursorPos", (PROC)fake_SetCursorPos, (PROC *)&real_SetCursorPos); + Hook_TryHotPatch("user32.dll", "GetClipCursor", (PROC)fake_GetClipCursor, (PROC *)&real_GetClipCursor); + Hook_TryHotPatch("user32.dll", "WindowFromPoint", (PROC)fake_WindowFromPoint, (PROC *)&real_WindowFromPoint); + Hook_TryHotPatch("user32.dll", "GetCursorInfo", (PROC)fake_GetCursorInfo, (PROC *)&real_GetCursorInfo); + Hook_TryHotPatch("user32.dll", "GetSystemMetrics", (PROC)fake_GetSystemMetrics, (PROC *)&real_GetSystemMetrics); + Hook_TryHotPatch("user32.dll", "SetWindowPos", (PROC)fake_SetWindowPos, (PROC *)&real_SetWindowPos); + Hook_TryHotPatch("user32.dll", "MoveWindow", (PROC)fake_MoveWindow, (PROC *)&real_MoveWindow); + Hook_TryHotPatch("user32.dll", "SendMessageA", (PROC)fake_SendMessageA, (PROC *)&real_SendMessageA); + Hook_TryHotPatch("user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA, (PROC *)&real_SetWindowLongA); + + //Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos); } } From 63584509e22ace91e826d4de64da28be3e1f7f41 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 20 Mar 2019 05:07:28 +0100 Subject: [PATCH 07/33] improve hotpatching --- src/hook.c | 24 +++++++++++++++++++++++- src/settings.c | 5 +++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/hook.c b/src/hook.c index 1aa5a83..5698f0e 100644 --- a/src/hook.c +++ b/src/hook.c @@ -86,8 +86,30 @@ PROC Hook_HotPatch(PROC function, PROC newFunction) { PROC result = function; + if (!function) + return result; + unsigned short *bytes = (unsigned short *)function; - if (function && *bytes == 0xFF8B) // mov edi, edi + + if (*bytes == 0x25FF) // JMP DWORD PTR + { + char *address = (char *)function; + DWORD oldProtect; + + if (VirtualProtect(address, 8, PAGE_EXECUTE_READWRITE, &oldProtect)) + { + if (memcmp(address + 6, (const char[]) { 0xCC, 0xCC }, 2) == 0 || + memcmp(address + 6, (const char[]) { 0x90, 0x90 }, 2) == 0) + { + memmove(address + 2, address, 6); + *((WORD *)(&address[0])) = 0xFF8B; // mov edi, edi + } + + VirtualProtect(address, 8, oldProtect, &oldProtect); + } + } + + if (*bytes == 0xFF8B) // mov edi, edi { char *address = ((char *)function) - 5; DWORD oldProtect; diff --git a/src/settings.c b/src/settings.c index 986033a..7446a97 100644 --- a/src/settings.c +++ b/src/settings.c @@ -41,7 +41,7 @@ void Settings_Load() ddraw->noactivateapp = GetBool("noactivateapp", FALSE); ddraw->vhack = GetBool("vhack", FALSE); ddraw->accurateTimers = GetBool("accuratetimers", FALSE); - ddraw->hotPatch = GetBool("hotPatch", FALSE); + ddraw->hotPatch = GetBool("hotpatch", FALSE); ddraw->bnetHack = GetBool("bnetHack", TRUE); WindowRect.right = GetInt("width", 0); @@ -281,7 +281,8 @@ static void CreateSettingsIni() "\n" "; Use hotpatching rather than IAT hooking\n" "; Note: Can be used to fix issues related to new features added by cnc-ddraw such as windowed mode or stretching\n" - "hotPatch=false\n" + "hotpatch=false\n" + "\n" "\n" "; Workaround for battle.net on Diablo and Warcraft 2 BNE\n" "; Note: This hack as a negative side-effect, you can only play fullscreen with 'renderer=gdi' or via 'fullscreen=true'\n" From 0b5064152d862d2208e3c6859df29d8fec54157d Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Fri, 12 Apr 2019 14:11:56 +0200 Subject: [PATCH 08/33] add export for warvideo --- ddraw.def | 1 + src/surface.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/ddraw.def b/ddraw.def index c2b0db0..20f0c25 100644 --- a/ddraw.def +++ b/ddraw.def @@ -6,3 +6,4 @@ EXPORTS GameHandlesClose DATA NvOptimusEnablement DATA AmdPowerXpressRequestHighPerformance DATA + pvBmpBits DATA diff --git a/src/surface.c b/src/surface.c index 5c84db9..b1cdc98 100644 --- a/src/surface.c +++ b/src/surface.c @@ -29,6 +29,7 @@ void dump_ddbltflags(DWORD dwFlags); void dump_ddscaps(DWORD dwCaps); void dump_ddsd(DWORD dwFlags); DWORD WINAPI render_soft_main(void); +void *pvBmpBits; HRESULT __stdcall ddraw_surface_QueryInterface(IDirectDrawSurfaceImpl *This, REFIID riid, void **obj) { @@ -1199,6 +1200,9 @@ HRESULT __stdcall ddraw_CreateSurface(IDirectDrawImpl *This, LPDDSURFACEDESC lpD if (!Surface->bitmap) Surface->surface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Surface->lPitch * (Surface->height + 200) * Surface->lXPitch); + if (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE) + pvBmpBits = Surface->surface; + SelectObject(Surface->hDC, Surface->bitmap); } From af281c38d2110ff5385c3a031c89627bf804ebef Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Sun, 28 Jul 2019 02:55:25 +0200 Subject: [PATCH 09/33] add support for fake bilinear shader --- src/render.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/render.c b/src/render.c index 805d7da..c7218d8 100644 --- a/src/render.c +++ b/src/render.c @@ -46,6 +46,7 @@ static GLuint FrameBufferTexId; static GLuint ScaleVBOs[3], ScaleVAO; static BOOL UseOpenGL; static BOOL AdjustAlignment; +static BOOL UseBilinear; static HGLRC CreateContext(HDC hdc); static void SetMaxFPS(); @@ -176,6 +177,8 @@ static void BuildPrograms() ScaleProgram = OpenGL_BuildProgramFromFile(ddraw->shader); else OpenGL_GotVersion3 = FALSE; + + UseBilinear = strstr(ddraw->shader, "\\bilinear.glsl") != 0; } if (OpenGL_GotVersion2 && !MainProgram) @@ -485,8 +488,8 @@ static void InitScaleProgram() glGenTextures(1, &FrameBufferTexId); glBindTexture(GL_TEXTURE_2D, FrameBufferTexId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, UseBilinear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, UseBilinear ? GL_LINEAR : GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SurfaceTexWidth, SurfaceTexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FrameBufferTexId, 0); From 18f2e16bb0febe03191fdbf97dc65767caa663eb Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 6 Aug 2019 04:37:06 +0200 Subject: [PATCH 10/33] make bnet moveable --- inc/hook.h | 8 ++++-- inc/mouse.h | 5 ++++ src/hook.c | 10 +++++++ src/main.c | 73 ++++++++++++++++++++++++++------------------------- src/mouse.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- src/surface.c | 3 ++- 6 files changed, 130 insertions(+), 40 deletions(-) diff --git a/inc/hook.h b/inc/hook.h index 199eddc..8da0fec 100644 --- a/inc/hook.h +++ b/inc/hook.h @@ -3,8 +3,6 @@ #include -typedef HFONT(__stdcall* CREATEFONTINDIRECTA)(CONST LOGFONT*); - typedef BOOL (WINAPI* GETCURSORPOSPROC)(LPPOINT); typedef BOOL(WINAPI* CLIPCURSORPROC)(const RECT*); typedef int (WINAPI* SHOWCURSORPROC)(BOOL); @@ -22,6 +20,9 @@ typedef BOOL (WINAPI* SETWINDOWPOSPROC)(HWND, HWND, int, int, int, int, UINT); typedef BOOL (WINAPI* MOVEWINDOWPROC)(HWND, int, int, int, int, BOOL); typedef LRESULT (WINAPI* SENDMESSAGEAPROC)(HWND, UINT, WPARAM, LPARAM); typedef LONG (WINAPI* SETWINDOWLONGAPROC)(HWND, int, LONG); +typedef BOOL (WINAPI* ENABLEWINDOWPROC)(HWND, BOOL); +typedef HWND (WINAPI* CREATEWINDOWEXAPROC)(DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID); +typedef BOOL (WINAPI* DESTROYWINDOWPROC)(HWND); extern GETCURSORPOSPROC real_GetCursorPos; extern CLIPCURSORPROC real_ClipCursor; @@ -40,6 +41,9 @@ extern SETWINDOWPOSPROC real_SetWindowPos; extern MOVEWINDOWPROC real_MoveWindow; extern SENDMESSAGEAPROC real_SendMessageA; extern SETWINDOWLONGAPROC real_SetWindowLongA; +extern ENABLEWINDOWPROC real_EnableWindow; +extern CREATEWINDOWEXAPROC real_CreateWindowExA; +extern DESTROYWINDOWPROC real_DestroyWindow; extern BOOL Hook_Active; diff --git a/inc/mouse.h b/inc/mouse.h index dd18506..a11fd3d 100644 --- a/inc/mouse.h +++ b/inc/mouse.h @@ -23,5 +23,10 @@ BOOL WINAPI fake_SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int BOOL WINAPI fake_MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint); LRESULT WINAPI fake_SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); LONG WINAPI fake_SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong); +BOOL WINAPI fake_EnableWindow(HWND hWnd, BOOL bEnable); +BOOL WINAPI fake_DestroyWindow(HWND hWnd); +HWND WINAPI fake_CreateWindowExA( + DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, + int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); #endif diff --git a/src/hook.c b/src/hook.c index 5698f0e..3755bed 100644 --- a/src/hook.c +++ b/src/hook.c @@ -22,6 +22,10 @@ SETWINDOWPOSPROC real_SetWindowPos = SetWindowPos; MOVEWINDOWPROC real_MoveWindow = MoveWindow; SENDMESSAGEAPROC real_SendMessageA = SendMessageA; SETWINDOWLONGAPROC real_SetWindowLongA = SetWindowLongA; +ENABLEWINDOWPROC real_EnableWindow = EnableWindow; +CREATEWINDOWEXAPROC real_CreateWindowExA = CreateWindowExA; +DESTROYWINDOWPROC real_DestroyWindow = DestroyWindow; + void Hook_PatchIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction) { @@ -146,6 +150,9 @@ void Hook_TryHotPatch(char *moduleName, char *functionName, PROC newFunction, PR else { Hook_PatchIAT(GetModuleHandle(NULL), moduleName, functionName, newFunction); + + if (ddraw->bnetHack) + Hook_PatchIAT(GetModuleHandle("storm.dll"), moduleName, functionName, newFunction); } } @@ -172,6 +179,9 @@ void Hook_Init() Hook_TryHotPatch("user32.dll", "MoveWindow", (PROC)fake_MoveWindow, (PROC *)&real_MoveWindow); Hook_TryHotPatch("user32.dll", "SendMessageA", (PROC)fake_SendMessageA, (PROC *)&real_SendMessageA); Hook_TryHotPatch("user32.dll", "SetWindowLongA", (PROC)fake_SetWindowLongA, (PROC *)&real_SetWindowLongA); + Hook_TryHotPatch("user32.dll", "EnableWindow", (PROC)fake_EnableWindow, (PROC *)&real_EnableWindow); + Hook_TryHotPatch("user32.dll", "CreateWindowExA", (PROC)fake_CreateWindowExA, (PROC *)&real_CreateWindowExA); + Hook_TryHotPatch("user32.dll", "DestroyWindow", (PROC)fake_DestroyWindow, (PROC *)&real_DestroyWindow); //Hook_PatchIAT(GetModuleHandle(NULL), "user32.dll", "GetCursorPos", (PROC)fake_GetCursorPos); } diff --git a/src/main.c b/src/main.c index 611e120..78076d1 100644 --- a/src/main.c +++ b/src/main.c @@ -196,6 +196,28 @@ void LimitGameTicks() } } +void UpdateBnetPos(int oldX, int oldY, int newX, int newY) +{ + HWND hWnd = FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL); + + while (hWnd != NULL) + { + RECT rc; + real_GetWindowRect(hWnd, &rc); + + real_SetWindowPos( + hWnd, + 0, + rc.left + (newX - oldX), + rc.top + (newY - oldY), + 0, + 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL); + } +} + HRESULT __stdcall ddraw_Compact(IDirectDrawImpl *This) { printf("DirectDraw::Compact(This=%p) ???\n", This); @@ -776,7 +798,6 @@ HRESULT __stdcall ddraw_SetDisplayMode(IDirectDrawImpl *This, DWORD width, DWORD int y = (WindowRect.top != -32000) ? WindowRect.top : (This->mode.dmPelsHeight / 2) - (This->render.height / 2); RECT dst = { x, y, This->render.width + x, This->render.height + y }; AdjustWindowRect(&dst, GetWindowLong(This->hWnd, GWL_STYLE), FALSE); - real_SetWindowPos(ddraw->hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); real_MoveWindow(This->hWnd, dst.left, dst.top, (dst.right - dst.left), (dst.bottom - dst.top), TRUE); if (This->renderer == render_d3d9_main) @@ -833,6 +854,9 @@ HRESULT __stdcall ddraw_SetDisplayMode2(IDirectDrawImpl *This, DWORD width, DWOR void ToggleFullscreen() { + if (ddraw->bnetHack) + return; + if (ddraw->windowed) { mouse_unlock(); @@ -1083,6 +1107,9 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (CopyRect(&clientrc, windowrc) && UnadjustWindowRectEx(&clientrc, GetWindowLong(hWnd, GWL_STYLE), FALSE, GetWindowLong(hWnd, GWL_EXSTYLE))) { + if (ddraw->bnetHack && WindowRect.left != -32000) + UpdateBnetPos(WindowRect.left, WindowRect.top, clientrc.left, clientrc.top); + WindowRect.left = clientrc.left; WindowRect.top = clientrc.top; WindowRect.right = clientrc.right - clientrc.left; @@ -1129,11 +1156,17 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (ddraw->windowed) { + int x = (int)(short)LOWORD(lParam); + int y = (int)(short)HIWORD(lParam); + + if (x != -32000) + { + if (ddraw->bnetHack && WindowRect.left != -32000) + UpdateBnetPos(WindowRect.left, WindowRect.top, x, y); + } + if (inSizeMove || ddraw->wine) { - int x = (int)(short)LOWORD(lParam); - int y = (int)(short)HIWORD(lParam); - if (x != -32000) WindowRect.left = x; // -32000 = Exit/Minimize @@ -1182,38 +1215,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mouse_unlock(); return 0; - - case WM_ENABLE: - { - if (ddraw->bnetHack) - { - if (wParam) - mouse_lock(); - else - mouse_unlock(); - - HWND hWnd = FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL); - if (hWnd) - { - BOOL hideCursor = TRUE; - - do - { - RECT rc; - if (real_GetWindowRect(hWnd, &rc) && rc.bottom - rc.top == 479) - hideCursor = FALSE; - - } while ((hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL))); - - if (hideCursor) - while (real_ShowCursor(FALSE) > 0); - } - - ddraw->bnetActive = !wParam; - } - break; - } - case WM_ACTIVATE: { return 0; diff --git a/src/mouse.c b/src/mouse.c index d9d19bb..b50e639 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -142,6 +142,9 @@ void mouse_lock() { RECT rc; + if (ddraw->bnetHack && ddraw->bnetActive) + return; + if (ddraw->devmode) { if (ddraw->handlemouse) @@ -277,7 +280,14 @@ BOOL WINAPI fake_GetWindowRect(HWND hWnd, LPRECT lpRect) } else { - return real_GetWindowRect(hWnd, lpRect) && MapWindowPoints(HWND_DESKTOP, ddraw->hWnd, (LPPOINT)lpRect, 2); + if (real_GetWindowRect(hWnd, lpRect)) + { + MapWindowPoints(HWND_DESKTOP, ddraw->hWnd, (LPPOINT)lpRect, 2); + + return TRUE; + } + + return FALSE; } } @@ -398,3 +408,62 @@ LONG WINAPI fake_SetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong) return real_SetWindowLongA(hWnd, nIndex, dwNewLong); } + +BOOL WINAPI fake_EnableWindow(HWND hWnd, BOOL bEnable) +{ + if (ddraw && ddraw->hWnd == hWnd) + { + if (ddraw->bnetHack) + return FALSE; + } + + return real_EnableWindow(hWnd, bEnable); +} + +BOOL WINAPI fake_DestroyWindow(HWND hWnd) +{ + BOOL result = real_DestroyWindow(hWnd); + + if (ddraw && ddraw->hWnd != hWnd && ddraw->bnetActive && ddraw->bnetHack && !FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL)) + { + ddraw->bnetActive = FALSE; + mouse_lock(); + } + + return result; +} + +HWND WINAPI fake_CreateWindowExA( + DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, + int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) +{ + HWND hWnd = real_CreateWindowExA( + dwExStyle, + lpClassName, + lpWindowName, + dwStyle | WS_CLIPCHILDREN, + X, + Y, + nWidth, + nHeight, + hWndParent, + hMenu, + hInstance, + lpParam); + + if (_strcmpi(lpClassName, "SDlgDialog") == 0 && ddraw && ddraw->bnetHack) + { + if (!ddraw->bnetActive) + { + ddraw->bnetActive = TRUE; + mouse_unlock(); + } + + POINT pt = { 0, 0 }; + real_ClientToScreen(ddraw->hWnd, &pt); + + real_SetWindowPos(hWnd, 0, pt.x + X, pt.y + Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + } + + return hWnd; +} diff --git a/src/surface.c b/src/surface.c index b1cdc98..e0e5932 100644 --- a/src/surface.c +++ b/src/surface.c @@ -20,6 +20,7 @@ #include "main.h" #include "hook.h" #include "surface.h" +#include "mouse.h" #include "scale_pattern.h" // enables redraw via blt/unlock if there wasn't any flip for X ms @@ -1010,7 +1011,7 @@ HRESULT __stdcall ddraw_surface_Unlock(IDirectDrawSurfaceImpl *This, LPVOID lpRe do { RECT rc; - if (real_GetWindowRect(hWnd, &rc)) + if (fake_GetWindowRect(hWnd, &rc)) { if (rc.bottom - rc.top == 479) erase = TRUE; From cec1d7246d4978111df70cf4ea9d8ac561179f42 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 6 Aug 2019 04:44:37 +0200 Subject: [PATCH 11/33] optimize default values for war2 --- src/settings.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/settings.c b/src/settings.c index 7446a97..f2e0c80 100644 --- a/src/settings.c +++ b/src/settings.c @@ -210,20 +210,20 @@ static void CreateSettingsIni() "\n" "; Override the width/height settings shown above and always stretch to fullscreen\n" "; Note: Can be combined with 'windowed=true' to get windowed-fullscreen aka borderless mode\n" - "fullscreen=false\n" + "fullscreen=true\n" "\n" "; Run in windowed mode rather than going fullscreen\n" - "windowed=false\n" + "windowed=true\n" "\n" "; Maintain aspect ratio - (Requires 'handlemouse=true')\n" - "maintas=false\n" + "maintas=true\n" "\n" "; Windowboxing / Integer Scaling - (Requires 'handlemouse=true')\n" "boxing=false\n" "\n" "; Real rendering rate, -1 = screen rate, 0 = unlimited, n = cap\n" "; Note: Does not have an impact on the game speed, to limit your game speed use 'maxgameticks='\n" - "maxfps=125\n" + "maxfps=60\n" "\n" "; Vertical synchronization, enable if you get tearing - (Requires 'renderer=auto/opengl/direct3d9')\n" "vsync=false\n" @@ -286,7 +286,7 @@ static void CreateSettingsIni() "\n" "; Workaround for battle.net on Diablo and Warcraft 2 BNE\n" "; Note: This hack as a negative side-effect, you can only play fullscreen with 'renderer=gdi' or via 'fullscreen=true'\n" - "bnetHack=false\n" + "bnetHack=true\n" "\n" "\n" "\n" @@ -406,12 +406,10 @@ static void CreateSettingsIni() "; Diablo\n" "[Diablo]\n" "bnetHack=true\n" - "renderer=gdi\n" "\n" "; Warcraft 2 Battle.net Edition\n" "[Warcraft II BNE]\n" "bnetHack=true\n" - "renderer=gdi\n" "\n" , fh); From a3b373253d5208238b3c4d0a4af70a76ae847bba Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 6 Aug 2019 05:24:18 +0200 Subject: [PATCH 12/33] default to bilinear shader --- src/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.c b/src/settings.c index f2e0c80..0c53814 100644 --- a/src/settings.c +++ b/src/settings.c @@ -234,7 +234,7 @@ static void CreateSettingsIni() "\n" "; Preliminary libretro shader support - (Requires 'renderer=opengl') https://github.com/libretro/glsl-shaders\n" "; Example: shader=Shaders\\crt-lottes-fast-no-warp.glsl\n" - "shader=\n" + "shader=Shaders\\bilinear.glsl\n" "\n" "; Window position, -32000 = center to screen\n" "posX=-32000\n" From c926ed0ac089c69aad426238c43f38ae54909de5 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 6 Aug 2019 07:19:00 +0200 Subject: [PATCH 13/33] some tweaks to make bnet moving a bit more failsafe --- src/main.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index 78076d1..3d71889 100644 --- a/src/main.c +++ b/src/main.c @@ -198,6 +198,17 @@ void LimitGameTicks() void UpdateBnetPos(int oldX, int oldY, int newX, int newY) { + RECT mainrc; + real_GetClientRect(ddraw->hWnd, &mainrc); + + POINT pt = { mainrc.left, mainrc.top }; + real_ClientToScreen(ddraw->hWnd, &pt); + + SetRect(&mainrc, pt.x, pt.y, pt.x + ddraw->width, pt.y + ddraw->height); + + int adjY = 0; + int adjX = 0; + HWND hWnd = FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL); while (hWnd != NULL) @@ -205,17 +216,53 @@ void UpdateBnetPos(int oldX, int oldY, int newX, int newY) RECT rc; real_GetWindowRect(hWnd, &rc); + OffsetRect(&rc, newX - oldX, newY - oldY); + real_SetWindowPos( hWnd, 0, - rc.left + (newX - oldX), - rc.top + (newY - oldY), + rc.left, + rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + if (rc.bottom > mainrc.bottom) + adjY = mainrc.bottom - rc.bottom; + else if (rc.top < mainrc.top) + adjY = mainrc.top - rc.top; + + if (rc.right > mainrc.right) + adjX = mainrc.right - rc.right; + else if (rc.left < mainrc.left) + adjX = mainrc.left - rc.left; + hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL); } + + if (adjX || adjY) + { + HWND hWnd = FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL); + + while (hWnd != NULL) + { + RECT rc; + real_GetWindowRect(hWnd, &rc); + + OffsetRect(&rc, adjX, adjY); + + real_SetWindowPos( + hWnd, + 0, + rc.left, + rc.top, + 0, + 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL); + } + } } HRESULT __stdcall ddraw_Compact(IDirectDrawImpl *This) @@ -1107,9 +1154,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (CopyRect(&clientrc, windowrc) && UnadjustWindowRectEx(&clientrc, GetWindowLong(hWnd, GWL_STYLE), FALSE, GetWindowLong(hWnd, GWL_EXSTYLE))) { - if (ddraw->bnetHack && WindowRect.left != -32000) - UpdateBnetPos(WindowRect.left, WindowRect.top, clientrc.left, clientrc.top); - WindowRect.left = clientrc.left; WindowRect.top = clientrc.top; WindowRect.right = clientrc.right - clientrc.left; @@ -1159,10 +1203,23 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) int x = (int)(short)LOWORD(lParam); int y = (int)(short)HIWORD(lParam); - if (x != -32000) + if (ddraw->bnetHack) { - if (ddraw->bnetHack && WindowRect.left != -32000) - UpdateBnetPos(WindowRect.left, WindowRect.top, x, y); + static int lastX = -32000; + static int lastY = -32000; + + if (x != -32000) + { + if (lastX != -32000 && lastY != -32000) + UpdateBnetPos(lastX, lastY, x, y); + + lastX = x; + } + + if (y != -32000) + { + lastY = y; + } } if (inSizeMove || ddraw->wine) From fde85be33da82ce785acf05d171e3e3f076c41ec Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 6 Aug 2019 08:37:41 +0200 Subject: [PATCH 14/33] restore SetWindowPos call --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index 3d71889..637da0c 100644 --- a/src/main.c +++ b/src/main.c @@ -845,6 +845,7 @@ HRESULT __stdcall ddraw_SetDisplayMode(IDirectDrawImpl *This, DWORD width, DWORD int y = (WindowRect.top != -32000) ? WindowRect.top : (This->mode.dmPelsHeight / 2) - (This->render.height / 2); RECT dst = { x, y, This->render.width + x, This->render.height + y }; AdjustWindowRect(&dst, GetWindowLong(This->hWnd, GWL_STYLE), FALSE); + real_SetWindowPos(ddraw->hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); real_MoveWindow(This->hWnd, dst.left, dst.top, (dst.right - dst.left), (dst.bottom - dst.top), TRUE); if (This->renderer == render_d3d9_main) From aadfd7fefdcedac6b378ce7ef39928fb57c81714 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Tue, 6 Aug 2019 09:26:17 +0200 Subject: [PATCH 15/33] always use highest value --- src/main.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index 637da0c..dbd244b 100644 --- a/src/main.c +++ b/src/main.c @@ -198,12 +198,10 @@ void LimitGameTicks() void UpdateBnetPos(int oldX, int oldY, int newX, int newY) { - RECT mainrc; - real_GetClientRect(ddraw->hWnd, &mainrc); - - POINT pt = { mainrc.left, mainrc.top }; + POINT pt = { 0, 0 }; real_ClientToScreen(ddraw->hWnd, &pt); + RECT mainrc; SetRect(&mainrc, pt.x, pt.y, pt.x + ddraw->width, pt.y + ddraw->height); int adjY = 0; @@ -227,14 +225,14 @@ void UpdateBnetPos(int oldX, int oldY, int newX, int newY) 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); - if (rc.bottom > mainrc.bottom) + if (rc.bottom > mainrc.bottom && abs(mainrc.bottom - rc.bottom) > abs(adjY)) adjY = mainrc.bottom - rc.bottom; - else if (rc.top < mainrc.top) + else if (rc.top < mainrc.top && abs(mainrc.top - rc.top) > abs(adjY)) adjY = mainrc.top - rc.top; - if (rc.right > mainrc.right) + if (rc.right > mainrc.right && abs(mainrc.right - rc.right) > abs(adjX)) adjX = mainrc.right - rc.right; - else if (rc.left < mainrc.left) + else if (rc.left < mainrc.left && abs(mainrc.left - rc.left) > abs(adjX)) adjX = mainrc.left - rc.left; hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL); From 170946c4d3dc0d126bd1aaec82e7679199b0a858 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 02:04:07 +0200 Subject: [PATCH 16/33] fix GDI fullscreen --- src/hook.c | 5 +++++ src/main.c | 42 ++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/hook.c b/src/hook.c index 3755bed..cd3f847 100644 --- a/src/hook.c +++ b/src/hook.c @@ -145,7 +145,12 @@ void Hook_TryHotPatch(char *moduleName, char *functionName, PROC newFunction, PR *function = Hook_HotPatch(org, newFunction); if (*function == org) // hotpatch failed... + { Hook_PatchIAT(GetModuleHandle(NULL), moduleName, functionName, newFunction); + + if (ddraw->bnetHack) + Hook_PatchIAT(GetModuleHandle("storm.dll"), moduleName, functionName, newFunction); + } } else { diff --git a/src/main.c b/src/main.c index dbd244b..9628cda 100644 --- a/src/main.c +++ b/src/main.c @@ -196,8 +196,18 @@ void LimitGameTicks() } } -void UpdateBnetPos(int oldX, int oldY, int newX, int newY) +void UpdateBnetPos(int newX, int newY) { + static int oldX = -32000; + static int oldY = -32000; + + if (oldX == -32000 || oldY == -32000) + { + oldX = newX; + oldY = newY; + return; + } + POINT pt = { 0, 0 }; real_ClientToScreen(ddraw->hWnd, &pt); @@ -261,6 +271,9 @@ void UpdateBnetPos(int oldX, int oldY, int newX, int newY) hWnd = FindWindowEx(HWND_DESKTOP, hWnd, "SDlgDialog", NULL); } } + + oldX = newX; + oldY = newY; } HRESULT __stdcall ddraw_Compact(IDirectDrawImpl *This) @@ -900,7 +913,7 @@ HRESULT __stdcall ddraw_SetDisplayMode2(IDirectDrawImpl *This, DWORD width, DWOR void ToggleFullscreen() { - if (ddraw->bnetHack) + if (ddraw->bnetHack && ddraw->renderer != render_soft_main) return; if (ddraw->windowed) @@ -910,6 +923,7 @@ void ToggleFullscreen() real_SetWindowLongA(ddraw->hWnd, GWL_STYLE, GetWindowLong(ddraw->hWnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU)); ddraw->altenter = TRUE; ddraw_SetDisplayMode(ddraw, ddraw->width, ddraw->height, ddraw->bpp); + UpdateBnetPos(0, 0); mouse_lock(); } else @@ -920,7 +934,7 @@ void ToggleFullscreen() if (Direct3D9Active) Direct3D9_Reset(); else - ChangeDisplaySettings(&ddraw->mode, 0); + ChangeDisplaySettings(&ddraw->mode, CDS_FULLSCREEN); ddraw_SetDisplayMode(ddraw, ddraw->width, ddraw->height, ddraw->bpp); mouse_lock(); @@ -1202,24 +1216,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) int x = (int)(short)LOWORD(lParam); int y = (int)(short)HIWORD(lParam); - if (ddraw->bnetHack) - { - static int lastX = -32000; - static int lastY = -32000; - - if (x != -32000) - { - if (lastX != -32000 && lastY != -32000) - UpdateBnetPos(lastX, lastY, x, y); - - lastX = x; - } - - if (y != -32000) - { - lastY = y; - } - } + if (ddraw->bnetHack && x != -32000 && y != -32000) + UpdateBnetPos(x, y); if (inSizeMove || ddraw->wine) { @@ -1311,7 +1309,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (!Direct3D9Active) { ShowWindow(ddraw->hWnd, SW_MINIMIZE); - ChangeDisplaySettings(&ddraw->mode, 0); + ChangeDisplaySettings(&ddraw->mode, CDS_FULLSCREEN); } InterlockedExchange(&ddraw->minimized, TRUE); From a19d11a2623e91c717977457ce7f3678b26eb073 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 02:59:42 +0200 Subject: [PATCH 17/33] prevent fullscreen exclusive mode with opengl on war2 --- src/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 9628cda..889ccaa 100644 --- a/src/main.c +++ b/src/main.c @@ -788,6 +788,9 @@ HRESULT __stdcall ddraw_SetDisplayMode(IDirectDrawImpl *This, DWORD width, DWORD } } + if (!ddraw->windowed && ddraw->bnetHack && ddraw->renderer == render_main) + This->render.height++; + if (!ddraw->handlemouse) This->boxing = maintas = FALSE; @@ -913,7 +916,7 @@ HRESULT __stdcall ddraw_SetDisplayMode2(IDirectDrawImpl *This, DWORD width, DWOR void ToggleFullscreen() { - if (ddraw->bnetHack && ddraw->renderer != render_soft_main) + if (ddraw->bnetHack && ddraw->renderer == render_d3d9_main) return; if (ddraw->windowed) From 462809e76e059e543739ac333d2e00a56d07c3eb Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 03:02:50 +0200 Subject: [PATCH 18/33] fix unlock cursor keys --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 889ccaa..68132c9 100644 --- a/src/main.c +++ b/src/main.c @@ -1370,7 +1370,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return 0; } } - if (wParam == VK_MENU) + if (wParam == VK_CONTROL || wParam == VK_MENU) { if ((GetAsyncKeyState(VK_RMENU) & 0x8000) && GetAsyncKeyState(VK_RCONTROL) & 0x8000) { From 0caaf3677a68bd4b7e4e5233d8fb0cfb168e3910 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 03:04:21 +0200 Subject: [PATCH 19/33] don't forced windowed with opengl --- src/settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.c b/src/settings.c index 0c53814..ab7920e 100644 --- a/src/settings.c +++ b/src/settings.c @@ -104,7 +104,7 @@ void Settings_Load() GetString("renderer", "auto", tmp, sizeof(tmp)); printf("Using %s renderer\n", tmp); - if (ddraw->bnetHack && tolower(tmp[0]) != 'g') + if (ddraw->bnetHack && tolower(tmp[0]) == 'd') ddraw->windowed = TRUE; if (tolower(tmp[0]) == 's' || tolower(tmp[0]) == 'g') //gdi From 92e4a85160de6ef575dabd32047d0929ee4a5fe8 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 03:21:20 +0200 Subject: [PATCH 20/33] fix autorenderer bug on win xp --- src/main.c | 5 ----- src/render_soft.c | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index 68132c9..71e3c2d 100644 --- a/src/main.c +++ b/src/main.c @@ -1339,11 +1339,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mouse_unlock(); real_SetWindowPos(ddraw->hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); real_SetWindowPos(ddraw->hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - if (!ddraw->wine) - { - ShowWindow(ddraw->hWnd, SW_MINIMIZE); - ShowWindow(ddraw->hWnd, SW_RESTORE); - } mouse_lock(); return 0; } diff --git a/src/render_soft.c b/src/render_soft.c index cc08735..f36ef4b 100644 --- a/src/render_soft.c +++ b/src/render_soft.c @@ -29,6 +29,8 @@ DWORD WINAPI render_soft_main(void) char warningText[512] = { 0 }; if (ShowDriverWarning) { + ShowDriverWarning = FALSE; + if (!ddraw->windowed) PostMessage(ddraw->hWnd, WM_AUTORENDERER, 0, 0); @@ -37,8 +39,8 @@ DWORD WINAPI render_soft_main(void) "-WARNING- Using slow software rendering, please update your graphics card driver (%s)", strlen(OpenGL_Version) > 10 ? "" : OpenGL_Version); } - else - Sleep(500); + + Sleep(500); DWORD lastTick = 0; int maxFPS = ddraw->render.maxfps; From c8fc032cd2163db08f69960860fa3dc0bce8822f Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 03:22:52 +0200 Subject: [PATCH 21/33] tweak old xp workaround --- src/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.c b/src/main.c index 71e3c2d..6623099 100644 --- a/src/main.c +++ b/src/main.c @@ -1754,6 +1754,9 @@ HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnk { This->DirectDrawCreate = (HRESULT(WINAPI *)(GUID FAR*, LPDIRECTDRAW FAR*, IUnknown FAR*))GetProcAddress(This->real_dll, "DirectDrawCreate"); + + if (This->DirectDrawCreate == DirectDrawCreate) + This->DirectDrawCreate = NULL; } InitializeCriticalSection(&This->cs); From 57df372da18fd142e270408a2c04844e88610c6e Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 03:31:01 +0200 Subject: [PATCH 22/33] adjust default settings --- src/settings.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/settings.c b/src/settings.c index ab7920e..4ace007 100644 --- a/src/settings.c +++ b/src/settings.c @@ -101,7 +101,7 @@ void Settings_Load() // to do: read .glslp config file instead of the shader and apply the correct settings GetString("shader", "", ddraw->shader, sizeof(ddraw->shader)); - GetString("renderer", "auto", tmp, sizeof(tmp)); + GetString("renderer", "opengl", tmp, sizeof(tmp)); printf("Using %s renderer\n", tmp); if (ddraw->bnetHack && tolower(tmp[0]) == 'd') @@ -210,7 +210,7 @@ static void CreateSettingsIni() "\n" "; Override the width/height settings shown above and always stretch to fullscreen\n" "; Note: Can be combined with 'windowed=true' to get windowed-fullscreen aka borderless mode\n" - "fullscreen=true\n" + "fullscreen=false\n" "\n" "; Run in windowed mode rather than going fullscreen\n" "windowed=true\n" @@ -241,7 +241,7 @@ static void CreateSettingsIni() "posY=-32000\n" "\n" "; Renderer, possible values: auto, opengl, gdi, direct3d9 (auto = try direct3d9/opengl, fallback = gdi)\n" - "renderer=auto\n" + "renderer=opengl\n" "\n" "; Developer mode (don't lock the cursor)\n" "devmode=false\n" @@ -283,7 +283,6 @@ static void CreateSettingsIni() "; Note: Can be used to fix issues related to new features added by cnc-ddraw such as windowed mode or stretching\n" "hotpatch=false\n" "\n" - "\n" "; Workaround for battle.net on Diablo and Warcraft 2 BNE\n" "; Note: This hack as a negative side-effect, you can only play fullscreen with 'renderer=gdi' or via 'fullscreen=true'\n" "bnetHack=true\n" From 9a9e9feeadba1af4a820ef0d0af50039efe45f8f Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 04:40:43 +0200 Subject: [PATCH 23/33] automatically switch between windowed/fullscreen with d3d9 renderer --- inc/main.h | 2 ++ src/main.c | 2 +- src/mouse.c | 46 +++++++++++++++++++++++++++++----------------- src/settings.c | 3 --- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/inc/main.h b/inc/main.h index e3551ee..c1765d3 100644 --- a/inc/main.h +++ b/inc/main.h @@ -37,6 +37,7 @@ extern BOOL ChildWindowExists; BOOL detect_cutscene(); void LimitGameTicks(); +void ToggleFullscreen(); DWORD WINAPI render_main(void); DWORD WINAPI render_soft_main(void); BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam); @@ -127,6 +128,7 @@ typedef struct IDirectDrawImpl BOOL accurateTimers; int bnetHack; BOOL bnetActive; + BOOL bnetD3d9Fullscreen; BOOL hotPatch; SpeedLimiter ticksLimiter; SpeedLimiter flipLimiter; diff --git a/src/main.c b/src/main.c index 6623099..a64152d 100644 --- a/src/main.c +++ b/src/main.c @@ -916,7 +916,7 @@ HRESULT __stdcall ddraw_SetDisplayMode2(IDirectDrawImpl *This, DWORD width, DWOR void ToggleFullscreen() { - if (ddraw->bnetHack && ddraw->renderer == render_d3d9_main) + if (ddraw->bnetHack && ddraw->bnetActive && ddraw->renderer == render_d3d9_main) return; if (ddraw->windowed) diff --git a/src/mouse.c b/src/mouse.c index b50e639..04e341a 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -19,6 +19,7 @@ #include "main.h" #include "surface.h" #include "hook.h" +#include "render_d3d9.h" int yAdjust = 0; @@ -428,6 +429,12 @@ BOOL WINAPI fake_DestroyWindow(HWND hWnd) { ddraw->bnetActive = FALSE; mouse_lock(); + + if (ddraw->windowed && ddraw->bnetD3d9Fullscreen && ddraw->renderer == render_d3d9_main) + { + ToggleFullscreen(); + ddraw->bnetD3d9Fullscreen = FALSE; + } } return result; @@ -437,7 +444,28 @@ HWND WINAPI fake_CreateWindowExA( DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) { - HWND hWnd = real_CreateWindowExA( + if (lpClassName && _strcmpi(lpClassName, "SDlgDialog") == 0 && ddraw && ddraw->bnetHack) + { + if (!ddraw->bnetActive) + { + if (!ddraw->windowed && !ddraw->bnetD3d9Fullscreen && ddraw->renderer == render_d3d9_main) + { + ToggleFullscreen(); + ddraw->bnetD3d9Fullscreen = TRUE; + } + + ddraw->bnetActive = TRUE; + mouse_unlock(); + } + + POINT pt = { 0, 0 }; + real_ClientToScreen(ddraw->hWnd, &pt); + + X += pt.x; + Y += pt.y; + } + + return real_CreateWindowExA( dwExStyle, lpClassName, lpWindowName, @@ -450,20 +478,4 @@ HWND WINAPI fake_CreateWindowExA( hMenu, hInstance, lpParam); - - if (_strcmpi(lpClassName, "SDlgDialog") == 0 && ddraw && ddraw->bnetHack) - { - if (!ddraw->bnetActive) - { - ddraw->bnetActive = TRUE; - mouse_unlock(); - } - - POINT pt = { 0, 0 }; - real_ClientToScreen(ddraw->hWnd, &pt); - - real_SetWindowPos(hWnd, 0, pt.x + X, pt.y + Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); - } - - return hWnd; } diff --git a/src/settings.c b/src/settings.c index 4ace007..a738b69 100644 --- a/src/settings.c +++ b/src/settings.c @@ -104,9 +104,6 @@ void Settings_Load() GetString("renderer", "opengl", tmp, sizeof(tmp)); printf("Using %s renderer\n", tmp); - if (ddraw->bnetHack && tolower(tmp[0]) == 'd') - ddraw->windowed = TRUE; - if (tolower(tmp[0]) == 's' || tolower(tmp[0]) == 'g') //gdi { ddraw->renderer = render_soft_main; From c9229738ec396ca82ec28760cc8151e5798cd627 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 04:48:05 +0200 Subject: [PATCH 24/33] remove note from settings ini --- src/settings.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/settings.c b/src/settings.c index a738b69..9a3ab4b 100644 --- a/src/settings.c +++ b/src/settings.c @@ -281,7 +281,6 @@ static void CreateSettingsIni() "hotpatch=false\n" "\n" "; Workaround for battle.net on Diablo and Warcraft 2 BNE\n" - "; Note: This hack as a negative side-effect, you can only play fullscreen with 'renderer=gdi' or via 'fullscreen=true'\n" "bnetHack=true\n" "\n" "\n" From 0a779ac4fef246bfe042e3a28c4823ca1a69a4d6 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Wed, 7 Aug 2019 12:45:40 +0200 Subject: [PATCH 25/33] sync with master --- Makefile | 16 +- cnc-ddraw.vcxproj | 17 +- cnc-ddraw.vcxproj.filters | 36 + ddraw.rc | 2 +- inc/debug.h | 3 + inc/hook.h | 8 +- inc/main.h | 2 - src/clipper.c | 14 +- src/debug.c | 30 + src/detours/CREDITS.TXT | 115 + src/detours/LICENSE.md | 23 + src/detours/README.md | 48 + src/detours/creatwth.cpp | 1586 +++++++++++++ src/detours/detours.cpp | 2489 +++++++++++++++++++++ src/detours/detours.h | 1080 +++++++++ src/detours/detver.h | 27 + src/detours/disasm.cpp | 4344 ++++++++++++++++++++++++++++++++++++ src/detours/disolarm.cpp | 2 + src/detours/disolarm64.cpp | 2 + src/detours/disolia64.cpp | 2 + src/detours/disolx64.cpp | 2 + src/detours/disolx86.cpp | 2 + src/detours/image.cpp | 2247 +++++++++++++++++++ src/detours/modules.cpp | 929 ++++++++ src/detours/uimports.cpp | 269 +++ src/dinput.c | 47 +- src/hook.c | 163 +- src/main.c | 65 +- src/palette.c | 6 +- src/render.c | 8 +- src/render_d3d9.c | 6 - src/settings.c | 33 +- src/surface.c | 64 +- 33 files changed, 13499 insertions(+), 188 deletions(-) create mode 100644 src/detours/CREDITS.TXT create mode 100644 src/detours/LICENSE.md create mode 100644 src/detours/README.md create mode 100644 src/detours/creatwth.cpp create mode 100644 src/detours/detours.cpp create mode 100644 src/detours/detours.h create mode 100644 src/detours/detver.h create mode 100644 src/detours/disasm.cpp create mode 100644 src/detours/disolarm.cpp create mode 100644 src/detours/disolarm64.cpp create mode 100644 src/detours/disolia64.cpp create mode 100644 src/detours/disolx64.cpp create mode 100644 src/detours/disolx86.cpp create mode 100644 src/detours/image.cpp create mode 100644 src/detours/modules.cpp create mode 100644 src/detours/uimports.cpp diff --git a/Makefile b/Makefile index 99e6501..92071d1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ -CC=gcc -WINDRES=windres -CFLAGS=-Iinc -Wall -Wl,--enable-stdcall-fixup -s -LIBS=-lgdi32 -lwinmm +-include config.mk + +WINDRES ?= windres +LDFLAGS = -Iinc -Wall -Wl,--enable-stdcall-fixup -s +CFLAGS = -std=c99 +LIBS = -lgdi32 -lwinmm FILES = src/debug.c \ src/main.c \ @@ -21,8 +23,8 @@ FILES = src/debug.c \ all: $(WINDRES) -J rc ddraw.rc ddraw.rc.o - $(CC) $(CFLAGS) -shared -o ddraw.dll $(FILES) ddraw.def ddraw.rc.o $(LIBS) -# $(CC) $(CFLAGS) -nostdlib -shared -o ddraw.dll $(FILES) ddraw.def ddraw.rc.o $(LIBS) -lkernel32 -luser32 -lmsvcrt + $(CC) $(CFLAGS) $(LDFLAGS) -shared -o ddraw.dll $(FILES) ddraw.def ddraw.rc.o $(LIBS) +# $(CC) $(CFLAGS) $(LDFLAGS) -nostdlib -shared -o ddraw.dll $(FILES) ddraw.def ddraw.rc.o $(LIBS) -lkernel32 -luser32 -lmsvcrt clean: - rm -f ddraw.dll + $(RM) ddraw.dll ddraw.rc.o diff --git a/cnc-ddraw.vcxproj b/cnc-ddraw.vcxproj index 39864d4..3781845 100644 --- a/cnc-ddraw.vcxproj +++ b/cnc-ddraw.vcxproj @@ -13,6 +13,19 @@ + + + + + + + + + + + + true + @@ -99,7 +112,7 @@ Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) MultiThreadedDebug - inc + inc;src\detours Windows @@ -124,7 +137,7 @@ if exist "$(LocalDebuggerCommand)" if exist "$(LocalDebuggerWorkingDirectory)" true WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) MultiThreaded - inc + inc;src\detours Windows diff --git a/cnc-ddraw.vcxproj.filters b/cnc-ddraw.vcxproj.filters index 2423530..24513d4 100644 --- a/cnc-ddraw.vcxproj.filters +++ b/cnc-ddraw.vcxproj.filters @@ -13,6 +13,9 @@ {9b152f9d-a092-42a9-ac47-0594f135a640} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {af194dd7-3316-4887-93d6-9f2af2135f94} + @@ -60,6 +63,39 @@ Source Files + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + + + Source Files\detours + diff --git a/ddraw.rc b/ddraw.rc index 15528d4..4af3f0c 100644 --- a/ddraw.rc +++ b/ddraw.rc @@ -4,7 +4,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_BUILD 4 -#define VERSION_REVISION 1 +#define VERSION_REVISION 3 #define VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION #define VERSION_STRING ver_str(VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION) diff --git a/inc/debug.h b/inc/debug.h index 9c30178..19d9907 100644 --- a/inc/debug.h +++ b/inc/debug.h @@ -8,6 +8,7 @@ double CounterStop(); void DebugPrint(const char *format, ...); void DrawFrameInfoStart(); void DrawFrameInfoEnd(); +int dprintf(const char *fmt, ...); extern double DebugFrameTime; extern DWORD DebugFrameCount; @@ -24,6 +25,8 @@ extern DWORD DebugFrameCount; #ifdef _DEBUG_S #define printf(format, ...) DebugPrint("xDBG " format, ##__VA_ARGS__) +#else +#define printf(format, ...) dprintf(format, ##__VA_ARGS__) #endif #else diff --git a/inc/hook.h b/inc/hook.h index 8da0fec..c147065 100644 --- a/inc/hook.h +++ b/inc/hook.h @@ -3,6 +3,8 @@ #include +typedef HFONT(__stdcall* CREATEFONTINDIRECTA)(CONST LOGFONT*); + typedef BOOL (WINAPI* GETCURSORPOSPROC)(LPPOINT); typedef BOOL(WINAPI* CLIPCURSORPROC)(const RECT*); typedef int (WINAPI* SHOWCURSORPROC)(BOOL); @@ -45,11 +47,13 @@ extern ENABLEWINDOWPROC real_EnableWindow; extern CREATEWINDOWEXAPROC real_CreateWindowExA; extern DESTROYWINDOWPROC real_DestroyWindow; +extern int HookingMethod; extern BOOL Hook_Active; void Hook_Init(); +void Hook_Exit(); void Hook_PatchIAT(HMODULE hMod, char *moduleName, char *functionName, PROC newFunction); -PROC Hook_HotPatch(PROC function, PROC newFunction); -void Hook_TryHotPatch(char *moduleName, char *functionName, PROC newFunction, PROC *function); +void Hook_Create(char *moduleName, char *functionName, PROC newFunction, PROC *function); +void Hook_Revert(char *moduleName, char *functionName, PROC newFunction, PROC *function); #endif diff --git a/inc/main.h b/inc/main.h index c1765d3..7d6dcbb 100644 --- a/inc/main.h +++ b/inc/main.h @@ -122,14 +122,12 @@ typedef struct IDirectDrawImpl BOOL handlemouse; char shader[MAX_PATH]; BOOL wine; - LONG minimized; BOOL altenter; BOOL hidecursor; BOOL accurateTimers; int bnetHack; BOOL bnetActive; BOOL bnetD3d9Fullscreen; - BOOL hotPatch; SpeedLimiter ticksLimiter; SpeedLimiter flipLimiter; SpeedLimiter fpsLimiter; diff --git a/src/clipper.c b/src/clipper.c index 6660950..fd70511 100644 --- a/src/clipper.c +++ b/src/clipper.c @@ -20,7 +20,7 @@ HRESULT __stdcall ddraw_clipper_QueryInterface(IDirectDrawClipperImpl *This, REFIID riid, void **obj) { - printf("DirectDrawClipper::QueryInterface(This=%p, riid=%08X, obj=%p) ???\n", This, (unsigned int)riid, obj); + printf("??? DirectDrawClipper::QueryInterface(This=%p, riid=%08X, obj=%p)\n", This, (unsigned int)riid, obj); return S_OK; } @@ -52,37 +52,37 @@ ULONG __stdcall ddraw_clipper_Release(IDirectDrawClipperImpl *This) HRESULT __stdcall ddraw_clipper_GetClipList(IDirectDrawClipperImpl *This, LPRECT a, LPRGNDATA b, LPDWORD c) { - printf("IDirectDrawClipper::GetClipList(This=%p, ...) ???\n", This); + printf("??? IDirectDrawClipper::GetClipList(This=%p, ...)\n", This); return DD_OK; } HRESULT __stdcall ddraw_clipper_GetHWnd(IDirectDrawClipperImpl *This, HWND FAR *a) { - printf("IDirectDrawClipper::GetHWnd(This=%p, ...) ???\n", This); + printf("??? IDirectDrawClipper::GetHWnd(This=%p, ...)\n", This); return DD_OK; } HRESULT __stdcall ddraw_clipper_Initialize(IDirectDrawClipperImpl *This, LPDIRECTDRAW a, DWORD b) { - printf("IDirectDrawClipper::Initialize(This=%p, ...) ???\n", This); + printf("??? IDirectDrawClipper::Initialize(This=%p, ...)\n", This); return DD_OK; } HRESULT __stdcall ddraw_clipper_IsClipListChanged(IDirectDrawClipperImpl *This, BOOL FAR *a) { - printf("IDirectDrawClipper::IsClipListChanged(This=%p, ...) ???\n", This); + printf("??? IDirectDrawClipper::IsClipListChanged(This=%p, ...)\n", This); return DD_OK; } HRESULT __stdcall ddraw_clipper_SetClipList(IDirectDrawClipperImpl *This, LPRGNDATA a, DWORD b) { - printf("IDirectDrawClipper::SetClipList(This=%p, ...) ???\n", This); + printf("??? IDirectDrawClipper::SetClipList(This=%p, ...)\n", This); return DD_OK; } HRESULT __stdcall ddraw_clipper_SetHWnd(IDirectDrawClipperImpl *This, DWORD a, HWND b) { - printf("IDirectDrawClipper::SetHWnd(This=%p, ...) ???\n", This); + printf("??? IDirectDrawClipper::SetHWnd(This=%p, ...)\n", This); return DD_OK; } diff --git a/src/debug.c b/src/debug.c index 88690e1..b9e08f2 100644 --- a/src/debug.c +++ b/src/debug.c @@ -35,6 +35,36 @@ void DebugPrint(const char *format, ...) OutputDebugStringA(buffer); } +int dprintf(const char *fmt, ...) +{ + static CRITICAL_SECTION cs; + static BOOL initialized; + + if (!initialized) + { + initialized = TRUE; + InitializeCriticalSection(&cs); + } + + EnterCriticalSection(&cs); + + va_list args; + int ret; + + SYSTEMTIME st; + GetLocalTime(&st); + + fprintf(stdout, "[%lu] %02d:%02d:%02d.%03d ", GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); + + va_start(args, fmt); + ret = vfprintf(stdout, fmt, args); + va_end(args); + + LeaveCriticalSection(&cs); + + return ret; +} + void DrawFrameInfoStart() { static DWORD tick_fps = 0; diff --git a/src/detours/CREDITS.TXT b/src/detours/CREDITS.TXT new file mode 100644 index 0000000..a122676 --- /dev/null +++ b/src/detours/CREDITS.TXT @@ -0,0 +1,115 @@ +============================================================================== +The following individuals have helped identify specific bugs and improvements +in Detours. The entire Detours community has benefited from their help. +============================================================================== + +* Jay Krell: Identified issue with VirtualSize == 0 files created in + NT 3.1 images. (Build_339) + +* Igor Odnovorov: Identified an issue with the placement of the trampoline + region when a function is detoured twice and the second + trampoline region is outside of the +/- 2GB range of + the target. (Build_337) + +* Jay Krell: Identified need for some programs to enumerate the + address of IAT entries. (Build_336) + +* Calvin Hsia: Identified need for some program to change the excluded + system region. (Build_336) + +* Adam Smith: Identified error in failure handling when VirtualProect + cannot make pages executable because the Prohibit + Dynamic Code Generation mitigation policy has been + applied to a process. (Build_335) + +* Ben Faull: Identified fix to detour_alloc_region_from_lo and + detour_alloc_region_from_hi that preserves ASLR entropy. + (Build_334) + +* Shaoxiang Su: Reported errors building with Visual Studio 2015. + (Build_332) + +* Jay Krell: Identified and resolved significant gaps in the X86, X64 + and IA64 disassemblers for instruction found in code, + but seldom found in function prologues. (Build_331) + +* Allan Murphy: Identify error in rep and jmp ds: encodings. (Build_331) + +* Philip Bacon: Identified incorrect entry point return for pure + resource-only binaries. (Build_330) + +* Jay Krell: Identified failure in DetourAttachEx to update nAlign. + (Build_330) + +* Sumit Sarin: Helped debug error with packed binaries. + (Build_329) + +* Nitya Kumar Sharma: Reported bug in DetourAfterWithDll for 32/64 agnostic + EXEs. + (Build_327) + +* Richard Black: Identified a large number of typos in documentation. + (Build_326) + +* Michael Bilodeau: Identified bug in DetourUpdateProcessWithDll when the + target process contains a Detours payload *after* all + valid PE binaries. + (Build_324) + +* Meera Jindal: Reported bug in identification of target address in + DetourCopyInstruction for jmp[] and call[] on x86 & x64, + the ff15 and ff25 opcodes. + (Build_323) + +* Ken Johnson: Assistance with SAL 2.0 annotations. + (Build_319) + +* Nick Wood: Identified bug in DetourFindFunction on ARM. + (Build_314) + +* Mark Russinovich: Helped debug DetourCreateProcessWithDllEx. + (Build_314) + +* John Lin: Implementation idea for DetoursCreateProcessWithDllEx. + (Build_314) + +* Andrew Zawadowskiy Reported an improper memory page permissions + vulnerability in Detours 2.1. (Vulnerability does not + exist in versions later than Detours 2.1.) + (Build_223) + +* Nightxie: Identified bug in detour_alloc_round_up_to_region. + (Build_310) + +* Diana Milirud: Identified bug in B* instructions on ARM. + (Build_309) + +* Juan Carlos Identified correct MSIL entry point for unsigned MSIL. + Luciani: (Build_308) + +* Lee Hunt Suggested improvements in algorithm for allocation of + Lawrence Landauer trampoline regions on x64 to avoid collisions with + Joe Laughlin: system DLLs. + (Build_307) + +* Tyler Sims Identified bug in handling of "anycpu" MSIL binaries + Darren Kennedy: on x64. + (Build_307) + +* Andre Vachon: Help with optimized binaries. + (Build 301) + +* Chris Mann: Identified fix not forward ported from 2.2 to 3.0. + (Build_301) + +* Mark Irving: Identified bug with EXEs missing second import table. + (Build_300) + +* Ben Schwarz: Identified bug in handling of multi-byte NOPs. + (Build_300) + +* Aaron Giles Coded initial ARM/Thumb2 disassembler. + Jared Henderson: (Build_300) + +* Doug Brubacher: Coded initial x86 disassembler. + (Build_100) diff --git a/src/detours/LICENSE.md b/src/detours/LICENSE.md new file mode 100644 index 0000000..e6a4c56 --- /dev/null +++ b/src/detours/LICENSE.md @@ -0,0 +1,23 @@ +# Copyright (c) Microsoft Corporation + +All rights reserved. + +# MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/detours/README.md b/src/detours/README.md new file mode 100644 index 0000000..b5c5239 --- /dev/null +++ b/src/detours/README.md @@ -0,0 +1,48 @@ +# Microsoft Research Detours Package + +Detours is a software package for monitoring and instrumenting API calls on Windows. Detours +has been used by many ISVs and is also used by product teams at Microsoft. Detours is now available under +a standard open source license (MIT). This simplifies licensing for programmers using Detours +and allows the community to support Detours using open source tools and processes. + +Detours is compatible with the Windows NT family of +operating systems: Windows NT, Windows XP, Windows Server 2003, Windows 7, +Windows 8, and Windows 10. It cannot be used by Window Store apps +because Detours requires APIs not available to those applications. +This repo contains the source code for version 4.0.1 of Detours. + +For technical documentation on Detours, see the [Detours Wiki](https://github.com/microsoft/Detours/wiki). +For directions on how to build and run samples, see the +samples [README.txt](https://github.com/Microsoft/Detours/blob/master/samples/README.TXT) file. + +## Contributing + +The [`Detours`](https://github.com/microsoft/detours) repository is where development is done. +Here are some ways you can participate in the project: + +* [Answer questions](https://github.com/microsoft/detours/issues) about using Detours. +* [Improve the Wiki](https://github.com/microsoft/detours/wiki). +* [Submit bugs](https://github.com/microsoft/detours/issues) and help us verify fixes and changes as they are checked in. +* Review [source code changes](https://github.com/microsoft/detours/pulls). + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Issues, questions, and feedback + +* Open an issue on [GitHub Issues](https://github.com/Microsoft/detours/issues). + +## Mailing list for announcements + +The detours-announce mailing list is a low-traffic email list for important announcements +about the project, such as the availability of new versions of Detours. To join it, send +an email to listserv@lists.research.microsoft.com with a +message body containing only the text SUBSCRIBE DETOURS-ANNOUNCE. +To leave it, send an email to listserv@lists.research.microsoft.com with a +message body containing only the text UNSUBSCRIBE DETOURS-ANNOUNCE. + + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the [MIT](LICENSE.md) License. diff --git a/src/detours/creatwth.cpp b/src/detours/creatwth.cpp new file mode 100644 index 0000000..bbe7cc8 --- /dev/null +++ b/src/detours/creatwth.cpp @@ -0,0 +1,1586 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Create a process with a DLL (creatwth.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 +#include +#include +#pragma warning(push) +#if _MSC_VER > 1400 +#pragma warning(disable:6102 6103) // /analyze warnings +#endif +#include +#pragma warning(pop) + +// #define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL + +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif + +#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] +#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] +#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] +#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] + +////////////////////////////////////////////////////////////////////////////// +// +const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */ + 0xea0251b9, 0x5cde, 0x41b5, + { 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }}; + +////////////////////////////////////////////////////////////////////////////// +// +// Enumate through modules in the target process. +// +static BOOL WINAPI LoadNtHeaderFromProcess(HANDLE hProcess, + HMODULE hModule, + PIMAGE_NT_HEADERS32 pNtHeader) +{ + PBYTE pbModule = (PBYTE)hModule; + + if (pbModule == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) { + return FALSE; + } + + IMAGE_DOS_HEADER idh; + + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + return FALSE; + } + + if (idh.e_magic != IMAGE_DOS_SIGNATURE || + (DWORD)idh.e_lfanew > mbi.RegionSize || + (DWORD)idh.e_lfanew < sizeof(idh)) { + + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, + pNtHeader, sizeof(*pNtHeader), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %d\n", + pbModule + idh.e_lfanew, + pbModule + idh.e_lfanew + sizeof(*pNtHeader), + pbModule, + GetLastError())); + return FALSE; + } + + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + return TRUE; +} + +static HMODULE WINAPI EnumerateModulesInProcess(HANDLE hProcess, + HMODULE hModuleLast, + PIMAGE_NT_HEADERS32 pNtHeader) +{ + PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY; + + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + // Find the next memory region that contains a mapped PE image. + // + + for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { + if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { + break; + } + + // Usermode address space has such an unaligned region size always at the + // end and only at the end. + // + if ((mbi.RegionSize & 0xfff) == 0xfff) { + break; + } + if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) { + break; + } + + // Skip uncommitted regions and guard pages. + // + if ((mbi.State != MEM_COMMIT) || + ((mbi.Protect & 0xff) == PAGE_NOACCESS) || + (mbi.Protect & PAGE_GUARD)) { + continue; + } + + if (LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader)) { + return (HMODULE)pbLast; + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Find a region of memory in which we can create a replacement import table. +// +static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbModule, PBYTE pbBase, DWORD cbAlloc) +{ + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + PBYTE pbLast = pbBase; + for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { + + ZeroMemory(&mbi, sizeof(mbi)); + if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + break; + } + DETOUR_TRACE(("VirtualQueryEx(%p) failed: %d\n", + pbLast, GetLastError())); + break; + } + // Usermode address space has such an unaligned region size always at the + // end and only at the end. + // + if ((mbi.RegionSize & 0xfff) == 0xfff) { + break; + } + + // Skip anything other than a pure free region. + // + if (mbi.State != MEM_FREE) { + continue; + } + + // Use the max of mbi.BaseAddress and pbBase, in case mbi.BaseAddress < pbBase. + PBYTE pbAddress = (PBYTE)mbi.BaseAddress > pbBase ? (PBYTE)mbi.BaseAddress : pbBase; + + // Round pbAddress up to the nearest MM allocation boundary. + const DWORD_PTR mmGranularityMinusOne = (DWORD_PTR)(MM_ALLOCATION_GRANULARITY -1); + pbAddress = (PBYTE)(((DWORD_PTR)pbAddress + mmGranularityMinusOne) & ~mmGranularityMinusOne); + +#ifdef _WIN64 + // The offset from pbModule to any replacement import must fit into 32 bits. + // For simplicity, we check that the offset to the last byte fits into 32 bits, + // instead of the largest offset we'll actually use. The values are very similar. + const size_t GB4 = ((((size_t)1) << 32) - 1); + if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) { + DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress)); + return NULL; + } +#else + UNREFERENCED_PARAMETER(pbModule); +#endif + + DETOUR_TRACE(("Free region %p..%p\n", + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize)); + + for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) { + PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (pbAlloc == NULL) { + DETOUR_TRACE(("VirtualAllocEx(%p) failed: %d\n", pbAddress, GetLastError())); + continue; + } +#ifdef _WIN64 + // The offset from pbModule to any replacement import must fit into 32 bits. + if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) { + DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress)); + return NULL; + } +#endif + DETOUR_TRACE(("[%p..%p] Allocated for import table.\n", + pbAlloc, pbAlloc + cbAlloc)); + return pbAlloc; + } + } + return NULL; +} + +static inline DWORD PadToDword(DWORD dw) +{ + return (dw + 3) & ~3u; +} + +static inline DWORD PadToDwordPtr(DWORD dw) +{ + return (dw + 7) & ~7u; +} + +static inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest, + _In_ size_t cchDest, + _In_z_ LPCSTR pszSize) +{ + if (cchDest == 0 || pszDest == NULL || pszSize == NULL || + pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') { + + // can not write into empty buffer or with string other than two chars. + return ERROR_INVALID_PARAMETER; + } + + for (; cchDest >= 2; cchDest--, pszDest++) { + if (pszDest[0] == '?' && pszDest[1] == '?') { + pszDest[0] = pszSize[0]; + pszDest[1] = pszSize[1]; + break; + } + } + + return S_OK; +} + +static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der) +{ + // Save the various headers for DetourRestoreAfterWith. + ZeroMemory(&der, sizeof(der)); + der.cb = sizeof(der); + + der.pidh = (PBYTE)hModule; + der.cbidh = sizeof(der.idh); + if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + der.pidh, der.pidh + der.cbidh, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh)); + + // We read the NT header in two passes to get the full size. + // First we read just the Signature and FileHeader. + der.pinh = der.pidh + der.idh.e_lfanew; + der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader); + if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + der.pinh, der.pinh + der.cbinh, GetLastError())); + return FALSE; + } + + // Second we read the OptionalHeader and Section headers. + der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + der.inh.FileHeader.SizeOfOptionalHeader + + der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)); + + if (der.cbinh > sizeof(der.raw)) { + return FALSE; + } + + if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + der.pinh, der.pinh + der.cbinh, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh)); + + // Third, we read the CLR header + + if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 && + der.inh32.CLR_DIRECTORY.Size != 0) { + + DETOUR_TRACE(("CLR32.VirtAddr=%x, CLR.Size=%x\n", + der.inh32.CLR_DIRECTORY.VirtualAddress, + der.inh32.CLR_DIRECTORY.Size)); + + der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress; + } + } + else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 && + der.inh64.CLR_DIRECTORY.Size != 0) { + + DETOUR_TRACE(("CLR64.VirtAddr=%x, CLR.Size=%x\n", + der.inh64.CLR_DIRECTORY.VirtualAddress, + der.inh64.CLR_DIRECTORY.Size)); + + der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress; + } + } + + if (der.pclr != 0) { + der.cbclr = sizeof(der.clr); + if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %d\n", + der.pclr, der.pclr + der.cbclr, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); + } + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +#if DETOURS_32BIT +#define DWORD_XX DWORD32 +#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC +#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32 +#define UPDATE_IMPORTS_XX UpdateImports32 +#define DETOURS_BITS_XX 32 +#include "uimports.cpp" +#undef DETOUR_EXE_RESTORE_FIELD_XX +#undef DWORD_XX +#undef IMAGE_NT_HEADERS_XX +#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX +#undef IMAGE_ORDINAL_FLAG_XX +#undef UPDATE_IMPORTS_XX +#endif // DETOURS_32BIT + +#if DETOURS_64BIT +#define DWORD_XX DWORD64 +#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC +#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64 +#define UPDATE_IMPORTS_XX UpdateImports64 +#define DETOURS_BITS_XX 64 +#include "uimports.cpp" +#undef DETOUR_EXE_RESTORE_FIELD_XX +#undef DWORD_XX +#undef IMAGE_NT_HEADERS_XX +#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX +#undef IMAGE_ORDINAL_FLAG_XX +#undef UPDATE_IMPORTS_XX +#endif // DETOURS_64BIT + +////////////////////////////////////////////////////////////////////////////// +// +#if DETOURS_64BIT + +C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16); + +static BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine, + DETOUR_EXE_RESTORE& der) +{ + IMAGE_DOS_HEADER idh; + IMAGE_NT_HEADERS32 inh32; + IMAGE_NT_HEADERS64 inh64; + IMAGE_SECTION_HEADER sects[32]; + PBYTE pbModule = (PBYTE)hModule; + DWORD n; + + ZeroMemory(&inh32, sizeof(inh32)); + ZeroMemory(&inh64, sizeof(inh64)); + ZeroMemory(sects, sizeof(sects)); + + DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine)); + //////////////////////////////////////////////////////// Read old headers. + // + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n", + pbModule, pbModule + sizeof(idh))); + + PBYTE pnh = pbModule + idh.e_lfanew; + if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + pnh, pnh + sizeof(inh32), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32))); + + if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) { + return FALSE; + } + + PBYTE psects = pnh + + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + inh32.FileHeader.SizeOfOptionalHeader; + ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %d\n", + psects, psects + cb, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb)); + + ////////////////////////////////////////////////////////// Convert header. + // + inh64.Signature = inh32.Signature; + inh64.FileHeader = inh32.FileHeader; + inh64.FileHeader.Machine = machine; + inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); + + inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion; + inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion; + inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode; + inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData; + inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData; + inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint; + inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode; + inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase; + inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment; + inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment; + inh64.OptionalHeader.MajorOperatingSystemVersion + = inh32.OptionalHeader.MajorOperatingSystemVersion; + inh64.OptionalHeader.MinorOperatingSystemVersion + = inh32.OptionalHeader.MinorOperatingSystemVersion; + inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion; + inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion; + inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion; + inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion; + inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue; + inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage; + inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders; + inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum; + inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem; + inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics; + inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve; + inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit; + inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve; + inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit; + inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags; + inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes; + for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) { + inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n]; + } + + /////////////////////////////////////////////////////// Write new headers. + // + DWORD dwProtect = 0; + if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, + PAGE_EXECUTE_READWRITE, &dwProtect)) { + return FALSE; + } + + if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %d\n", + pnh, pnh + sizeof(inh64), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64))); + + psects = pnh + + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + inh64.FileHeader.SizeOfOptionalHeader; + cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) { + DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %d\n", + psects, psects + cb, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb)); + + // Record the updated headers. + if (!RecordExeRestore(hProcess, hModule, der)) { + return FALSE; + } + + // Remove the import table. + if (der.pclr != NULL && (der.clr.Flags & 1)) { + inh64.IMPORT_DIRECTORY.VirtualAddress = 0; + inh64.IMPORT_DIRECTORY.Size = 0; + + if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %d\n", + pnh, pnh + sizeof(inh64), GetLastError())); + return FALSE; + } + } + + DWORD dwOld = 0; + if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, + dwProtect, &dwOld)) { + return FALSE; + } + + return TRUE; +} +#endif // DETOURS_64BIT + +////////////////////////////////////////////////////////////////////////////// +// +BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls) +{ + // Find the next memory region that contains a mapped PE image. + // + BOOL bHas64BitDll = FALSE; + BOOL bHas32BitExe = FALSE; + BOOL bIs32BitProcess; + HMODULE hModule = NULL; + HMODULE hLast = NULL; + + DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%d)\n", hProcess, nDlls)); + + for (;;) { + IMAGE_NT_HEADERS32 inh; + + if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh)) == NULL) { + break; + } + + DETOUR_TRACE(("%p machine=%04x magic=%04x\n", + hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic)); + + if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) { + hModule = hLast; + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC + && inh.FileHeader.Machine != 0) { + + bHas32BitExe = TRUE; + } + DETOUR_TRACE(("%p Found EXE\n", hLast)); + } + else { + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC + && inh.FileHeader.Machine != 0) { + + bHas64BitDll = TRUE; + } + } + } + + if (hModule == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + if (!bHas32BitExe) { + bIs32BitProcess = FALSE; + } + else if (!bHas64BitDll) { + bIs32BitProcess = TRUE; + } + else { + if (!IsWow64Process(hProcess, &bIs32BitProcess)) { + return FALSE; + } + } + + DETOUR_TRACE((" 32BitExe=%d 32BitProcess\n", bHas32BitExe, bIs32BitProcess)); + + return DetourUpdateProcessWithDllEx(hProcess, + hModule, + bIs32BitProcess, + rlpDlls, + nDlls); +} + +BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, + _In_ HMODULE hModule, + _In_ BOOL bIs32BitProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls) +{ + // Find the next memory region that contains a mapped PE image. + // + BOOL bIs32BitExe = FALSE; + + DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%d)\n", hProcess, hModule, nDlls)); + + IMAGE_NT_HEADERS32 inh; + + if (hModule == NULL || LoadNtHeaderFromProcess(hProcess, hModule, &inh) == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC + && inh.FileHeader.Machine != 0) { + + bIs32BitExe = TRUE; + } + + DETOUR_TRACE((" 32BitExe=%d 32BitProcess\n", bIs32BitExe, bIs32BitProcess)); + + if (hModule == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + // Save the various headers for DetourRestoreAfterWith. + // + DETOUR_EXE_RESTORE der; + + if (!RecordExeRestore(hProcess, hModule, der)) { + return FALSE; + } + +#if defined(DETOURS_64BIT) + // Try to convert a neutral 32-bit managed binary to a 64-bit managed binary. + if (bIs32BitExe && !bIs32BitProcess) { + if (!der.pclr // Native binary + || (der.clr.Flags & 1) == 0 // Or mixed-mode MSIL + || (der.clr.Flags & 2) != 0) { // Or 32BIT Required MSIL + + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!UpdateFrom32To64(hProcess, hModule, +#if defined(DETOURS_X64) + IMAGE_FILE_MACHINE_AMD64, +#elif defined(DETOURS_IA64) + IMAGE_FILE_MACHINE_IA64, +#elif defined(DETOURS_ARM64) + IMAGE_FILE_MACHINE_ARM64, +#else +#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit. +#endif + der)) { + return FALSE; + } + bIs32BitExe = FALSE; + } +#endif // DETOURS_64BIT + + // Now decide if we can insert the detour. + +#if defined(DETOURS_32BIT) + if (bIs32BitProcess) { + // 32-bit native or 32-bit managed process on any platform. + if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) { + return FALSE; + } + } + else { + // 64-bit native or 64-bit managed process. + // + // Can't detour a 64-bit process with 32-bit code. + // Note: This happens for 32-bit PE binaries containing only + // manage code that have been marked as 64-bit ready. + // + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +#elif defined(DETOURS_64BIT) + if (bIs32BitProcess || bIs32BitExe) { + // Can't detour a 32-bit process with 64-bit code. + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + else { + // 64-bit native or 64-bit managed process on any platform. + if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) { + return FALSE; + } + } +#else +#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT. +#endif // DETOURS_64BIT + + /////////////////////////////////////////////////// Update the CLR header. + // + if (der.pclr != NULL) { + DETOUR_CLR_HEADER clr; + CopyMemory(&clr, &der.clr, sizeof(clr)); + clr.Flags &= 0xfffffffe; // Clear the IL_ONLY flag. + + DWORD dwProtect; + if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %d\n", GetLastError())); + return FALSE; + } + + if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(clr) failed: %d\n", GetLastError())); + return FALSE; + } + + if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %d\n", GetLastError())); + return FALSE; + } + DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); + +#if DETOURS_64BIT + if (der.clr.Flags & 0x2) { // Is the 32BIT Required Flag set? + // X64 never gets here because the process appears as a WOW64 process. + // However, on IA64, it doesn't appear to be a WOW process. + DETOUR_TRACE(("CLR Requires 32-bit\n", der.pclr, der.pclr + der.cbclr)); + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +#endif // DETOURS_64BIT + } + + //////////////////////////////// Save the undo data to the target process. + // + if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); + return FALSE; + } + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); + PROCESS_INFORMATION pi; + BOOL fResult = FALSE; + + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + fResult = pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwMyCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi); + + if (lpProcessInformation != NULL) { + CopyMemory(lpProcessInformation, &pi, sizeof(pi)); + } + + if (!fResult) { + return FALSE; + } + + LPCSTR rlpDlls[2]; + DWORD nDlls = 0; + if (lpDllName != NULL) { + rlpDlls[nDlls++] = lpDllName; + } + + if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { + TerminateProcess(pi.hProcess, ~0u); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(pi.hThread); + } + return TRUE; +} + + +BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); + PROCESS_INFORMATION pi; + + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + BOOL fResult = pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwMyCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi); + + if (lpProcessInformation) { + CopyMemory(lpProcessInformation, &pi, sizeof(pi)); + } + + if (!fResult) { + return FALSE; + } + + LPCSTR rlpDlls[2]; + DWORD nDlls = 0; + if (lpDllName != NULL) { + rlpDlls[nDlls++] = lpDllName; + } + + if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { + TerminateProcess(pi.hProcess, ~0u); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(pi.hThread); + } + return TRUE; +} + +BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData) +{ + DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) + + sizeof(IMAGE_NT_HEADERS) + + sizeof(IMAGE_SECTION_HEADER) + + sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + + PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal, + MEM_COMMIT, PAGE_READWRITE); + if (pbBase == NULL) { + DETOUR_TRACE(("VirtualAllocEx(%d) failed: %d\n", cbTotal, GetLastError())); + return FALSE; + } + + PBYTE pbTarget = pbBase; + IMAGE_DOS_HEADER idh; + IMAGE_NT_HEADERS inh; + IMAGE_SECTION_HEADER ish; + DETOUR_SECTION_HEADER dsh; + DETOUR_SECTION_RECORD dsr; + SIZE_T cbWrote = 0; + + ZeroMemory(&idh, sizeof(idh)); + idh.e_magic = IMAGE_DOS_SIGNATURE; + idh.e_lfanew = sizeof(idh); + if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) || + cbWrote != sizeof(idh)) { + DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError())); + return FALSE; + } + pbTarget += sizeof(idh); + + ZeroMemory(&inh, sizeof(inh)); + inh.Signature = IMAGE_NT_SIGNATURE; + inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader); + inh.FileHeader.Characteristics = IMAGE_FILE_DLL; + inh.FileHeader.NumberOfSections = 1; + inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC; + if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) || + cbWrote != sizeof(inh)) { + return FALSE; + } + pbTarget += sizeof(inh); + + ZeroMemory(&ish, sizeof(ish)); + memcpy(ish.Name, ".detour", sizeof(ish.Name)); + ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase); + ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) || + cbWrote != sizeof(ish)) { + return FALSE; + } + pbTarget += sizeof(ish); + + ZeroMemory(&dsh, sizeof(dsh)); + dsh.cbHeaderSize = sizeof(dsh); + dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE; + dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER); + dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) || + cbWrote != sizeof(dsh)) { + return FALSE; + } + pbTarget += sizeof(dsh); + + ZeroMemory(&dsr, sizeof(dsr)); + dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD); + dsr.nReserved = 0; + dsr.guid = rguid; + if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) || + cbWrote != sizeof(dsr)) { + return FALSE; + } + pbTarget += sizeof(dsr); + + if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) || + cbWrote != cbData) { + return FALSE; + } + pbTarget += cbData; + + DETOUR_TRACE(("Copied %d byte payload into target process at %p\n", + cbTotal, pbTarget - cbTotal)); + return TRUE; +} + +static BOOL s_fSearchedForHelper = FALSE; +static PDETOUR_EXE_HELPER s_pHelper = NULL; + +VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, + _In_ HINSTANCE, + _In_ LPSTR, + _In_ INT) +{ + LPCSTR * rlpDlls = NULL; + DWORD Result = 9900; + DWORD cOffset = 0; + DWORD cSize = 0; + HANDLE hProcess = NULL; + + if (s_pHelper == NULL) { + DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n")); + Result = 9905; + goto Cleanup; + } + + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid); + if (hProcess == NULL) { + DETOUR_TRACE(("OpenProcess(pid=%d) failed: %d\n", + s_pHelper->pid, GetLastError())); + Result = 9901; + goto Cleanup; + } + + rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls]; + cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER); + for (DWORD n = 0; n < s_pHelper->nDlls; n++) { + size_t cchDest = 0; + HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest); + if (!SUCCEEDED(hr)) { + Result = 9902; + goto Cleanup; + } + + rlpDlls[n] = &s_pHelper->rDlls[cOffset]; + cOffset += (DWORD)cchDest + 1; + } + + if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) { + DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%d) failed: %d\n", + s_pHelper->pid, GetLastError())); + Result = 9903; + goto Cleanup; + } + Result = 0; + + Cleanup: + if (rlpDlls != NULL) { + delete[] rlpDlls; + rlpDlls = NULL; + } + + ExitProcess(Result); +} + +BOOL WINAPI DetourIsHelperProcess(VOID) +{ + PVOID pvData; + DWORD cbData; + + if (s_fSearchedForHelper) { + return (s_pHelper != NULL); + } + + s_fSearchedForHelper = TRUE; + pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData); + + if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) { + return FALSE; + } + + s_pHelper = (PDETOUR_EXE_HELPER)pvData; + if (s_pHelper->cb < sizeof(*s_pHelper)) { + s_pHelper = NULL; + return FALSE; + } + + return TRUE; +} + +static +BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper, + _In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls) +{ + PDETOUR_EXE_HELPER Helper = NULL; + BOOL Result = FALSE; + _Field_range_(0, cSize - 4) DWORD cOffset = 0; + DWORD cSize = 4; + + if (pHelper == NULL) { + goto Cleanup; + } + *pHelper = NULL; + + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + + for (DWORD n = 0; n < nDlls; n++) { + HRESULT hr; + size_t cchDest = 0; + + hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + cSize += (DWORD)cchDest + 1; + } + + Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize]; + if (Helper == NULL) { + goto Cleanup; + } + + Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize; + Helper->pid = dwTargetPid; + Helper->nDlls = nDlls; + + for (DWORD n = 0; n < nDlls; n++) { + HRESULT hr; + size_t cchDest = 0; + + if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) { + goto Cleanup; + } + + if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) { + goto Cleanup; + } + + _Analysis_assume_(cOffset + 1 < cSize); + _Analysis_assume_(cOffset < 0x10000); + _Analysis_assume_(cSize < 0x10000); + + PCHAR psz = &Helper->rDlls[cOffset]; + + hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + +// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call. +// REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program. +#pragma warning(suppress:28020 28313) + hr = StringCchLengthA(psz, cSize - cOffset, &cchDest); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + // Replace "32." with "64." or "64." with "32." + + for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) { +#if DETOURS_32BIT + if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') { + psz[c - 3] = '6'; psz[c - 2] = '4'; + break; + } +#else + if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') { + psz[c - 3] = '3'; psz[c - 2] = '2'; + break; + } +#endif + } + + cOffset += (DWORD)cchDest + 1; + } + + *pHelper = Helper; + Helper = NULL; + Result = TRUE; + + Cleanup: + if (Helper != NULL) { + delete[] (PBYTE)Helper; + Helper = NULL; + } + return Result; +} + +static +VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper) +{ + if (*pHelper != NULL) { + delete[] (PBYTE)*pHelper; + *pHelper = NULL; + } +} + +BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA); +} + + +BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + BOOL Result = FALSE; + PROCESS_INFORMATION pi; + STARTUPINFOA si; + CHAR szExe[MAX_PATH]; + CHAR szCommand[MAX_PATH]; + PDETOUR_EXE_HELPER helper = NULL; + HRESULT hr; + DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe)); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%d,dlls=%d)\n", dwTargetPid, nDlls)); + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { + goto Cleanup; + } + + if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { + goto Cleanup; + } + +#if DETOURS_OPTION_BITS +#if DETOURS_32BIT + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe"); +#else // !DETOURS_32BIT + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe"); +#endif // !DETOURS_32BIT +#else // DETOURS_OPTIONS_BITS + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe"); +#endif // DETOURS_OPTIONS_BITS + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand), + "rundll32.exe \"%hs\",#1", &helper->rDlls[0]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand)); + if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi)) { + + if (!DetourCopyPayloadToProcess(pi.hProcess, + DETOUR_EXE_HELPER_GUID, + helper, helper->cb)) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); + TerminateProcess(pi.hProcess, ~0u); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + goto Cleanup; + } + + ResumeThread(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 500; + GetExitCodeProcess(pi.hProcess, &dwResult); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (dwResult != 0) { + DETOUR_TRACE(("Rundll32.exe failed: result=%d\n", dwResult)); + goto Cleanup; + } + Result = TRUE; + } + else { + DETOUR_TRACE(("CreateProcess failed: %d\n", GetLastError())); + goto Cleanup; + } + + Cleanup: + FreeExeHelper(&helper); + return Result; +} + +BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW); +} + +BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + BOOL Result = FALSE; + PROCESS_INFORMATION pi; + STARTUPINFOW si; + WCHAR szExe[MAX_PATH]; + WCHAR szCommand[MAX_PATH]; + PDETOUR_EXE_HELPER helper = NULL; + HRESULT hr; + DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe)); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%d,dlls=%d)\n", dwTargetPid, nDlls)); + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { + goto Cleanup; + } + + if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { + goto Cleanup; + } + +#if DETOURS_OPTION_BITS +#if DETOURS_32BIT + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe"); +#else // !DETOURS_32BIT + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe"); +#endif // !DETOURS_32BIT +#else // DETOURS_OPTIONS_BITS + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe"); +#endif // DETOURS_OPTIONS_BITS + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand), + L"rundll32.exe \"%hs\",#1", &helper->rDlls[0]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand)); + if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi)) { + + if (!DetourCopyPayloadToProcess(pi.hProcess, + DETOUR_EXE_HELPER_GUID, + helper, helper->cb)) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); + TerminateProcess(pi.hProcess, ~0u); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + goto Cleanup; + } + + ResumeThread(pi.hThread); + + ResumeThread(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 500; + GetExitCodeProcess(pi.hProcess, &dwResult); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (dwResult != 0) { + DETOUR_TRACE(("Rundll32.exe failed: result=%d\n", dwResult)); + goto Cleanup; + } + Result = TRUE; + } + else { + DETOUR_TRACE(("CreateProcess failed: %d\n", GetLastError())); + goto Cleanup; + } + + Cleanup: + FreeExeHelper(&helper); + return Result; +} + +BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + LPCSTR szDll = lpDllName; + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) && + !DetourProcessViaHelperA(lpProcessInformation->dwProcessId, + lpDllName, + pfCreateProcessA)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + + LPCSTR sz = lpDllName; + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) && + !DetourProcessViaHelperW(lpProcessInformation->dwProcessId, + lpDllName, + pfCreateProcessW)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && + !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId, + nDlls, + rlpDlls, + pfCreateProcessA)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && + !DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId, + nDlls, + rlpDlls, + pfCreateProcessW)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + return TRUE; +} + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/src/detours/detours.cpp b/src/detours/detours.cpp new file mode 100644 index 0000000..fb18ef8 --- /dev/null +++ b/src/detours/detours.cpp @@ -0,0 +1,2489 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality (detours.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma warning(disable:4068) // unknown pragma (suppress) + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif + +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 +#include + +#if (_MSC_VER < 1299) +#pragma warning(disable: 4710) +#endif + +//#define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL + +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif + +#define NOTHROW + +////////////////////////////////////////////////////////////////////////////// +// +struct _DETOUR_ALIGN +{ + BYTE obTarget : 3; + BYTE obTrampoline : 5; +}; + +C_ASSERT(sizeof(_DETOUR_ALIGN) == 1); + +////////////////////////////////////////////////////////////////////////////// +// +// Region reserved for system DLLs, which cannot be used for trampolines. +// +static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000; +static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000; + +////////////////////////////////////////////////////////////////////////////// +// +static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress) +{ + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi)); + __try { + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + return false; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + return false; + } + + if (pbAddress >= ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && + pbAddress < ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) { + return true; + } + } +#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.") + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + return false; + } + return false; +} + +inline ULONG_PTR detour_2gb_below(ULONG_PTR address) +{ + return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000; +} + +inline ULONG_PTR detour_2gb_above(ULONG_PTR address) +{ +#if defined(DETOURS_64BIT) + return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000; +#else + return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000; +#endif +} + +///////////////////////////////////////////////////////////////////////// X86. +// +#ifdef DETOURS_X86 + +struct _DETOUR_TRAMPOLINE +{ + BYTE rbCode[30]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + // And, within +/- 2GB of relative jmp targets. + if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", lo, pbCode, hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X86 + +///////////////////////////////////////////////////////////////////////// X64. +// +#ifdef DETOURS_X64 + +struct _DETOUR_TRAMPOLINE +{ + // An X64 instuction can be 15 bytes long. + // In practice 11 seems to be the limit. + BYTE rbCode[30]; // target code + jmp to pbRemain. + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[30]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + BYTE rbCodeIn[8]; // jmp [pbDetour] +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 6; + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + // And, within +/- 2GB of relative jmp vectors. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] [+imm32]\n", lo, pbCode, hi)); + } + // And, within +/- 2GB of relative jmp targets. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", lo, pbCode, hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X64 + +//////////////////////////////////////////////////////////////////////// IA64. +// +#ifdef DETOURS_IA64 + +struct _DETOUR_TRAMPOLINE +{ + // On the IA64, a trampoline is used for both incoming and outgoing calls. + // + // The trampoline contains the following bundles for the outgoing call: + // movl gp=target_gp; + // + // brl target_code; + // + // The trampoline contains the following bundles for the incoming call: + // alloc r41=ar.pfs, b, 0, 8, 0 + // mov r40=rp + // + // adds r50=0, r39 + // adds r49=0, r38 + // adds r48=0, r37 ;; + // + // adds r47=0, r36 + // adds r46=0, r35 + // adds r45=0, r34 + // + // adds r44=0, r33 + // adds r43=0, r32 + // adds r42=0, gp ;; + // + // movl gp=ffffffff`ffffffff ;; + // + // brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;; + // + // adds gp=0, r42 + // mov rp=r40, +0 ;; + // mov.i ar.pfs=r41 + // + // br.ret.sptk.many rp ;; + // + // This way, we only have to relocate a single bundle. + // + // The complicated incoming trampoline is required because we have to + // create an additional stack frame so that we save and restore the gp. + // We must do this because gp is a caller-saved register, but not saved + // if the caller thinks the target is in the same DLL, which changes + // when we insert a detour. + // + DETOUR_IA64_BUNDLE bMovlTargetGp; // Bundle which sets target GP + BYTE rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle. + DETOUR_IA64_BUNDLE bBrlRemainEip; // Brl to pbRemain + // This must be adjacent to bBranchIslands. + + // Each instruction in the moved bundle could be a IP-relative chk or branch or call. + // Any such instructions are changed to point to a brl in bBranchIslands. + // This must be adjacent to bBrlRemainEip -- see "pbPool". + DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE]; + + // Target of brl inserted in target function + DETOUR_IA64_BUNDLE bAllocFrame; // alloc frame + DETOUR_IA64_BUNDLE bSave37to39; // save r37, r38, r39. + DETOUR_IA64_BUNDLE bSave34to36; // save r34, r35, r36. + DETOUR_IA64_BUNDLE bSaveGPto33; // save gp, r32, r33. + DETOUR_IA64_BUNDLE bMovlDetourGp; // set detour GP. + DETOUR_IA64_BUNDLE bCallDetour; // call detour. + DETOUR_IA64_BUNDLE bPopFrameGp; // pop frame and restore gp. + DETOUR_IA64_BUNDLE bReturn; // return to caller. + + PLABEL_DESCRIPTOR pldTrampoline; + + BYTE rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle. + BYTE cbRestore; // size of original target code. + BYTE cbCode; // size of moved target code. + _DETOUR_ALIGN rAlign[14]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + PPLABEL_DESCRIPTOR ppldDetour; // [pbDetour,gpDetour] + PPLABEL_DESCRIPTOR ppldTarget; // [pbTarget,gpDetour] +}; + +C_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16); +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16); + +enum { + SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE) +}; + +inline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals) +{ + PBYTE pGlobals = NULL; + PBYTE pbCode = NULL; + + if (pPointer != NULL) { + PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer; + pbCode = (PBYTE)ppld->EntryPoint; + pGlobals = (PBYTE)ppld->GlobalPointer; + } + if (ppGlobals != NULL) { + *ppGlobals = pGlobals; + } + if (pbCode == NULL) { + return NULL; + } + + DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode; + + // IA64 Local Import Jumps look like: + // addl r2=ffffffff`ffe021c0, gp ;; + // ld8 r2=[r2] + // nop.i 0 ;; + // + // ld8 r3=[r2], 8 ;; + // ld8 gp=[r2] + // mov b6=r3, +0 + // + // nop.m 0 + // nop.i 0 + // br.cond.sptk.few b6 + // + + // 002024000200100b + if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b && + pb[0].wide[1] == 0x0004000000203008 && + pb[1].wide[0] == 0x001014180420180a && + pb[1].wide[1] == 0x07000830c0203008 && + pb[2].wide[0] == 0x0000000100000010 && + pb[2].wide[1] == 0x0080006000000200) { + + ULONG64 offset = + ((pb[0].wide[0] & 0x0000000001fc0000) >> 18) | // imm7b + ((pb[0].wide[0] & 0x000001ff00000000) >> 25) | // imm9d + ((pb[0].wide[0] & 0x00000000f8000000) >> 11); // imm5c + if (pb[0].wide[0] & 0x0000020000000000) { // sign + offset |= 0xffffffffffe00000; + } + PBYTE pbTarget = pGlobals + offset; + DETOUR_TRACE(("%p: potential import jump, target=%p\n", pb, pbTarget)); + + if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) { + DETOUR_TRACE(("%p: is import jump, label=%p\n", pb, *(PBYTE *)pbTarget)); + + PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget; + pbCode = (PBYTE)ppld->EntryPoint; + pGlobals = (PBYTE)ppld->GlobalPointer; + if (ppGlobals != NULL) { + *ppGlobals = pGlobals; + } + } + } + return pbCode; +} + + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + (void)pbCode; + *ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000; + *ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + // Routine not needed on IA64. + (void)pbCode; + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // Routine not needed on IA64. + (void)pbCode; + return 0; +} + +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +struct _DETOUR_TRAMPOLINE +{ + // A Thumb-2 instruction can be 2 or 4 bytes long. + BYTE rbCode[62]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104); + +enum { + SIZE_OF_JMP = 8 +}; + +inline PBYTE align4(PBYTE pValue) +{ + return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u); +} + +inline ULONG fetch_thumb_opcode(PBYTE pbCode) +{ + ULONG Opcode = *(UINT16 *)&pbCode[0]; + if (Opcode >= 0xe800) { + Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2]; + } + return Opcode; +} + +inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode) +{ + if (Opcode >= 0x10000) { + *((UINT16*&)pbCode)++ = Opcode >> 16; + } + *((UINT16*&)pbCode)++ = (UINT16)Opcode; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 4; + pbLiteral = *ppPool; + } + else { + pbLiteral = align4(pbCode + 6); + } + + *((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal); + LONG delta = pbLiteral - align4(pbCode + 4); + + write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n] + + if (ppPool == NULL) { + if (((ULONG)pbCode & 2) != 0) { + write_thumb_opcode(pbCode, 0xdefe); // BREAK + } + pbCode += 4; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_thumb_opcode(pbCode, 0xdefe); + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // Skip over the import jump if there is one. + pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode); + ULONG Opcode = fetch_thumb_opcode(pbCode); + + if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx + ULONG Opcode2 = fetch_thumb_opcode(pbCode+4); + + if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx + ULONG Opcode3 = fetch_thumb_opcode(pbCode+8); + if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12] + PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) | + ((Opcode2 << 1) & 0x08000000) | + ((Opcode2 << 16) & 0x00ff0000) | + ((Opcode >> 4) & 0x0000f700) | + ((Opcode >> 15) & 0x00000800) | + ((Opcode >> 0) & 0x000000ff)); + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew); + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + return pbNew; + } + } + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + ULONG Opcode = fetch_thumb_opcode(pbCode); + if ((Opcode & 0xffffff87) == 0x4700 || // bx + (Opcode & 0xf800d000) == 0xf0009000) { // b + return TRUE; + } + if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop. + return 2; + } + if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding. + return 2; + } + return 0; +} + +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + +struct _DETOUR_TRAMPOLINE +{ + // An ARM64 instruction is 4 bytes long. + // + // The overwrite is always 2 instructions plus a literal, so 16 bytes, 4 instructions. + // + // Copied instructions can expand. + // + // The scheme using MovImmediate can cause an instruction + // to grow as much as 6 times. + // That would be Bcc or Tbz with a large address space: + // 4 instructions to form immediate + // inverted tbz/bcc + // br + // + // An expansion of 4 is not uncommon -- bl/blr and small address space: + // 3 instructions to form immediate + // br or brl + // + // A theoretical maximum for rbCode is thefore 4*4*6 + 16 = 112 (another 16 for jmp to pbRemain). + // + // With literals, the maximum expansion is 5, including the literals: 4*4*5 + 16 = 96. + // + // The number is rounded up to 128. m_rbScratchDst should match this. + // + BYTE rbCode[128]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak[3]; // padding to make debugging easier. + BYTE rbRestore[24]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak[3]; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 184); + +enum { + SIZE_OF_JMP = 16 +}; + +inline ULONG fetch_opcode(PBYTE pbCode) +{ + return *(ULONG *)pbCode; +} + +inline void write_opcode(PBYTE &pbCode, ULONG Opcode) +{ + *(ULONG *)pbCode = Opcode; + pbCode += 4; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 8; + pbLiteral = *ppPool; + } + else { + pbLiteral = pbCode + 8; + } + + *((PBYTE*&)pbLiteral) = pbJmpVal; + LONG delta = (LONG)(pbLiteral - pbCode); + + write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] + write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17 + + if (ppPool == NULL) { + pbCode += 8; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_opcode(pbCode, 0xd4100000 | (0xf000 << 5)); + } + return pbCode; +} + +inline INT64 detour_sign_extend(UINT64 value, UINT bits) +{ + const UINT left = 64 - bits; + const INT64 m1 = -1; + const INT64 wide = (INT64)(value << left); + const INT64 sign = (wide < 0) ? (m1 << left) : 0; + return value | sign; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // Skip over the import jump if there is one. + pbCode = (PBYTE)pbCode; + ULONG Opcode = fetch_opcode(pbCode); + + if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT + ULONG Opcode2 = fetch_opcode(pbCode + 4); + + if ((Opcode2 & 0xffe003ff) == 0xf9400210) { // ldr x16, [x16, IAT] + ULONG Opcode3 = fetch_opcode(pbCode + 8); + + if (Opcode3 == 0xd61f0200) { // br x16 + +/* https://static.docs.arm.com/ddi0487/bb/DDI0487B_b_armv8_arm.pdf + The ADRP instruction shifts a signed, 21-bit immediate left by 12 bits, adds it to the value of the program counter with + the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the + calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or + a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address + within +/- 4GB of the current PC. + +PC-rel. addressing + This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are + decoded from Data Processing -- Immediate on page C4-226. + Add/subtract (immediate) + This section describes the encoding of the Add/subtract (immediate) instruction class. The encodings in this section + are decoded from Data Processing -- Immediate on page C4-226. + Decode fields + Instruction page + op + 0 ADR + 1 ADRP + +C6.2.10 ADRP + Form PC-relative address to 4KB page adds an immediate value that is shifted left by 12 bits, to the PC value to + form a PC-relative address, with the bottom 12 bits masked out, and writes the result to the destination register. + ADRP ,