diff --git a/source/VersionInfo.lua b/source/VersionInfo.lua index 8fd4af4..c303cf2 100644 --- a/source/VersionInfo.lua +++ b/source/VersionInfo.lua @@ -1,7 +1,7 @@ defines { "rsc_FullName=\"DirectPlay Stub\"", - "rsc_MinorVersion=0", + "rsc_MinorVersion=1", "rsc_RevisionID=1", "rsc_BuildID=0", - "rsc_Copyright=\"2023\"" + "rsc_Copyright=\"2023-2024\"" } \ No newline at end of file diff --git a/source/dplayx.def b/source/dplayx.def index 4dc8969..22a165c 100644 --- a/source/dplayx.def +++ b/source/dplayx.def @@ -1,8 +1,9 @@ LIBRARY DPLAYX EXPORTS - DirectPlayCreate=DirectPlayCreate_Stub @1 - DirectPlayEnumerateA=DirectPlayEnumerate_Stub @2 - DirectPlayEnumerateW=DirectPlayEnumerate_Stub @3 - DirectPlayLobbyCreateA=DirectPlayLobbyCreate_Stub @4 - DirectPlayLobbyCreateW=DirectPlayLobbyCreate_Stub @5 - DirectPlayEnumerate=DirectPlayEnumerate_Stub @9 + DirectPlayCreate @1 + DirectPlayEnumerateA @2 + DirectPlayEnumerateW @3 + DirectPlayLobbyCreateA @4 + DirectPlayLobbyCreateW @5 + DirectPlayEnumerate @9 + gdwDPlaySPRefCount @11 diff --git a/source/dplayx/dplayx.cpp b/source/dplayx/dplayx.cpp index 95856a2..bfbbddb 100644 --- a/source/dplayx/dplayx.cpp +++ b/source/dplayx/dplayx.cpp @@ -3,49 +3,88 @@ #include #include +#include -#include "../dplay-stub.h" +#include "../exportChecker.h" +#include "stubs.h" -HRESULT WINAPI DirectPlayCreate_Stub(LPGUID lpGUID, LPVOID* lplpDP, IUnknown* lpUnk) +#include +#include + +static auto DirectPlayCreate_Func = &DirectPlayCreate_Stub; +static auto DirectPlayEnumerate_Func = &DirectPlayEnumerate_Stub; +static auto DirectPlayEnumerateA_Func = &DirectPlayEnumerate_Stub; +static auto DirectPlayEnumerateW_Func = &DirectPlayEnumerate_Stub; +static auto DirectPlayLobbyCreateA_Func = &DirectPlayLobbyCreate_Stub; +static auto DirectPlayLobbyCreateW_Func = &DirectPlayLobbyCreate_Stub; + +static std::once_flag TryPassthroughFlag; +static void DoTryPassthrough() { - if (lpUnk != nullptr) - { - return CLASS_E_NOAGGREGATION; - } - if (lpGUID == nullptr || lplpDP == nullptr) + PWSTR system32Path = nullptr; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_System, KF_FLAG_DEFAULT, nullptr, &system32Path))) { - return DPERR_INVALIDPARAM; + const std::filesystem::path dllPath(std::filesystem::path(system32Path) / L"dplayx.dll"); + if (CheckExports(dllPath.c_str(), { + "DirectPlayCreate", "DirectPlayEnumerate", "DirectPlayEnumerateA", "DirectPlayEnumerateW", "DirectPlayLobbyCreateA", "DirectPlayLobbyCreateW" + })) + { + HMODULE systemDplay = LoadLibraryW(dllPath.c_str()); + if (systemDplay != nullptr) + { + DirectPlayCreate_Func = reinterpret_cast(GetProcAddress(systemDplay, "DirectPlayCreate")); + DirectPlayEnumerate_Func = reinterpret_cast(GetProcAddress(systemDplay, "DirectPlayEnumerate")); + DirectPlayEnumerateA_Func = reinterpret_cast(GetProcAddress(systemDplay, "DirectPlayEnumerateA")); + DirectPlayEnumerateW_Func = reinterpret_cast(GetProcAddress(systemDplay, "DirectPlayEnumerateW")); + DirectPlayLobbyCreateA_Func = reinterpret_cast(GetProcAddress(systemDplay, "DirectPlayLobbyCreateA")); + DirectPlayLobbyCreateW_Func = reinterpret_cast(GetProcAddress(systemDplay, "DirectPlayLobbyCreateW")); + + // "Leak" the DLL handle on purpose + } + } + + CoTaskMemFree(system32Path); } +} - *lplpDP = nullptr; - return DPERR_UNSUPPORTED; +static void TryPassthrough() +{ + std::call_once(TryPassthroughFlag, DoTryPassthrough); } -HRESULT WINAPI DirectPlayEnumerate_Stub(LPVOID lpEnumCallback, LPVOID /*lpContext*/) + +HRESULT WINAPI DirectPlayCreate(LPGUID lpGUID, LPVOID* lplpDP, IUnknown* lpUnk) { - if (lpEnumCallback == nullptr) - { - return DPERR_INVALIDPARAM; - } - return DP_OK; + TryPassthrough(); + return DirectPlayCreate_Func(lpGUID, lplpDP, lpUnk); } -HRESULT WINAPI DirectPlayLobbyCreate_Stub(LPGUID lpGUIDDSP, LPVOID* lplpDPL, IUnknown* lpUnk, void* lpData, DWORD dwDataSize) +HRESULT WINAPI DirectPlayEnumerate(LPVOID lpEnumCallback, LPVOID lpContext) { - if (lpGUIDDSP != nullptr && *lpGUIDDSP != GUID_NULL) - { - return DPERR_INVALIDPARAM; - } - if (lpUnk != nullptr) - { - return CLASS_E_NOAGGREGATION; - } - if (lpData != nullptr || dwDataSize != 0) - { - return DPERR_INVALIDPARAM; - } + TryPassthrough(); + return DirectPlayEnumerate_Func(lpEnumCallback, lpContext); +} + +HRESULT WINAPI DirectPlayEnumerateA(LPVOID lpEnumCallback, LPVOID lpContext) +{ + TryPassthrough(); + return DirectPlayEnumerateA_Func(lpEnumCallback, lpContext); +} - // lplpDPL is not checked for validity in the real DLL... - *lplpDPL = nullptr; - return DPERR_UNSUPPORTED; +HRESULT WINAPI DirectPlayEnumerateW(LPVOID lpEnumCallback, LPVOID lpContext) +{ + TryPassthrough(); + return DirectPlayEnumerateW_Func(lpEnumCallback, lpContext); +} + +HRESULT WINAPI DirectPlayLobbyCreateA(LPGUID lpGUIDDSP, LPVOID* lplpDPL, IUnknown* lpUnk, void* lpData, DWORD dwDataSize) +{ + TryPassthrough(); + return DirectPlayLobbyCreateA_Func(lpGUIDDSP, lplpDPL, lpUnk, lpData, dwDataSize); +} + +HRESULT WINAPI DirectPlayLobbyCreateW(LPGUID lpGUIDDSP, LPVOID* lplpDPL, IUnknown* lpUnk, void* lpData, DWORD dwDataSize) +{ + TryPassthrough(); + return DirectPlayLobbyCreateW_Func(lpGUIDDSP, lplpDPL, lpUnk, lpData, dwDataSize); } diff --git a/source/dplayx/stubs.cpp b/source/dplayx/stubs.cpp new file mode 100644 index 0000000..66a7819 --- /dev/null +++ b/source/dplayx/stubs.cpp @@ -0,0 +1,47 @@ +#include "stubs.h" + +HRESULT WINAPI DirectPlayCreate_Stub(LPGUID lpGUID, LPVOID* lplpDP, IUnknown* lpUnk) +{ + if (lpUnk != nullptr) + { + return CLASS_E_NOAGGREGATION; + } + if (lpGUID == nullptr || lplpDP == nullptr) + { + return DPERR_INVALIDPARAM; + } + + *lplpDP = nullptr; + return DPERR_UNSUPPORTED; +} + +HRESULT WINAPI DirectPlayEnumerate_Stub(LPVOID lpEnumCallback, LPVOID /*lpContext*/) +{ + if (lpEnumCallback == nullptr) + { + return DPERR_INVALIDPARAM; + } + return DP_OK; +} + +HRESULT WINAPI DirectPlayLobbyCreate_Stub(LPGUID lpGUIDDSP, LPVOID* lplpDPL, IUnknown* lpUnk, void* lpData, DWORD dwDataSize) +{ + if (lpGUIDDSP != nullptr && *lpGUIDDSP != GUID_NULL) + { + return DPERR_INVALIDPARAM; + } + if (lpUnk != nullptr) + { + return CLASS_E_NOAGGREGATION; + } + if (lpData != nullptr || dwDataSize != 0) + { + return DPERR_INVALIDPARAM; + } + + // lplpDPL is not checked for validity in the real DLL... + *lplpDPL = nullptr; + return DPERR_UNSUPPORTED; +} + +DWORD gdwDPlaySPRefCount = 0; diff --git a/source/dplayx/stubs.h b/source/dplayx/stubs.h new file mode 100644 index 0000000..7aa0067 --- /dev/null +++ b/source/dplayx/stubs.h @@ -0,0 +1,13 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX + +#include +#include + +#include "../dplay-stub.h" + +HRESULT WINAPI DirectPlayCreate_Stub(LPGUID lpGUID, LPVOID* lplpDP, IUnknown* lpUnk); +HRESULT WINAPI DirectPlayEnumerate_Stub(LPVOID lpEnumCallback, LPVOID /*lpContext*/); +HRESULT WINAPI DirectPlayLobbyCreate_Stub(LPGUID lpGUIDDSP, LPVOID* lplpDPL, IUnknown* lpUnk, void* lpData, DWORD dwDataSize); diff --git a/source/exportChecker.cpp b/source/exportChecker.cpp new file mode 100644 index 0000000..062298b --- /dev/null +++ b/source/exportChecker.cpp @@ -0,0 +1,42 @@ +#include "exportChecker.h" + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX + +#include + +#include + +bool CheckExports(const wchar_t* path, std::initializer_list exports) +{ + std::vector exportsToCheck(exports); + + HMODULE file = LoadLibraryExW(path, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (file != nullptr) + { + DWORD_PTR instance = reinterpret_cast(file) & ~3; + const PIMAGE_NT_HEADERS ntHeader = reinterpret_cast(instance + reinterpret_cast(instance)->e_lfanew); + const DWORD exportVA = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (exportVA != 0) + { + const PIMAGE_EXPORT_DIRECTORY exportDirectory = reinterpret_cast(instance + exportVA); + const DWORD* functionNames = reinterpret_cast(instance + exportDirectory->AddressOfNames); + for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++) + { + const char* nameRva = reinterpret_cast(instance + functionNames[i]); + auto it = std::find(exportsToCheck.begin(), exportsToCheck.end(), nameRva); + if (it != exportsToCheck.end()) + { + exportsToCheck.erase(it); + if (exportsToCheck.empty()) + { + // Early out if all required functions are found + break; + } + } + } + } + FreeLibrary(file); + } + return exportsToCheck.empty(); +} diff --git a/source/exportChecker.h b/source/exportChecker.h new file mode 100644 index 0000000..8193cac --- /dev/null +++ b/source/exportChecker.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +bool CheckExports(const wchar_t* path, std::initializer_list exports);