Skip to content

Commit

Permalink
Merge pull request #1 from CookiePLMonster/passthrough-stub
Browse files Browse the repository at this point in the history
Introduce DirectPlay passthrough
  • Loading branch information
CookiePLMonster authored Mar 17, 2024
2 parents f6e4e5c + ec3d660 commit af19d01
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 40 deletions.
4 changes: 2 additions & 2 deletions source/VersionInfo.lua
Original file line number Diff line number Diff line change
@@ -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\""
}
13 changes: 7 additions & 6 deletions source/dplayx.def
Original file line number Diff line number Diff line change
@@ -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
103 changes: 71 additions & 32 deletions source/dplayx/dplayx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,88 @@

#include <windows.h>
#include <combaseapi.h>
#include <ShlObj.h>

#include "../dplay-stub.h"
#include "../exportChecker.h"
#include "stubs.h"

HRESULT WINAPI DirectPlayCreate_Stub(LPGUID lpGUID, LPVOID* lplpDP, IUnknown* lpUnk)
#include <filesystem>
#include <mutex>

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<decltype(DirectPlayCreate_Func)>(GetProcAddress(systemDplay, "DirectPlayCreate"));
DirectPlayEnumerate_Func = reinterpret_cast<decltype(DirectPlayEnumerate_Func)>(GetProcAddress(systemDplay, "DirectPlayEnumerate"));
DirectPlayEnumerateA_Func = reinterpret_cast<decltype(DirectPlayEnumerateA_Func)>(GetProcAddress(systemDplay, "DirectPlayEnumerateA"));
DirectPlayEnumerateW_Func = reinterpret_cast<decltype(DirectPlayEnumerateW_Func)>(GetProcAddress(systemDplay, "DirectPlayEnumerateW"));
DirectPlayLobbyCreateA_Func = reinterpret_cast<decltype(DirectPlayLobbyCreateA_Func)>(GetProcAddress(systemDplay, "DirectPlayLobbyCreateA"));
DirectPlayLobbyCreateW_Func = reinterpret_cast<decltype(DirectPlayLobbyCreateW_Func)>(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);
}
47 changes: 47 additions & 0 deletions source/dplayx/stubs.cpp
Original file line number Diff line number Diff line change
@@ -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;
13 changes: 13 additions & 0 deletions source/dplayx/stubs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <windows.h>
#include <combaseapi.h>

#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);
42 changes: 42 additions & 0 deletions source/exportChecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "exportChecker.h"

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <windows.h>

#include <vector>

bool CheckExports(const wchar_t* path, std::initializer_list<std::string_view> exports)
{
std::vector<std::string_view> exportsToCheck(exports);

HMODULE file = LoadLibraryExW(path, nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (file != nullptr)
{
DWORD_PTR instance = reinterpret_cast<DWORD_PTR>(file) & ~3;
const PIMAGE_NT_HEADERS ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(instance + reinterpret_cast<PIMAGE_DOS_HEADER>(instance)->e_lfanew);
const DWORD exportVA = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (exportVA != 0)
{
const PIMAGE_EXPORT_DIRECTORY exportDirectory = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(instance + exportVA);
const DWORD* functionNames = reinterpret_cast<DWORD*>(instance + exportDirectory->AddressOfNames);
for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++)
{
const char* nameRva = reinterpret_cast<const char*>(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();
}
6 changes: 6 additions & 0 deletions source/exportChecker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#include <initializer_list>
#include <string_view>

bool CheckExports(const wchar_t* path, std::initializer_list<std::string_view> exports);

0 comments on commit af19d01

Please sign in to comment.