new stub for old steam lib

This commit is contained in:
a
2025-08-01 18:35:19 +03:00
parent a273f2e0e4
commit 82e7b2c570
7 changed files with 612 additions and 0 deletions

View File

@@ -43,6 +43,8 @@ jobs:
"steamclient_experimental_loader",
"steamclient_experimental_extra",
"lib_game_overlay_renderer",
# steam old lib
"lib_steam_old",
# tools
"tool_lobby_connect",
"tool_generate_interfaces",

View File

@@ -56,6 +56,10 @@ if exist "%TARGET_DIR%\experimental\" (
copy /y "%ROOT%\post_build\README.experimental.md" "%TARGET_DIR%\experimental\"
)
if exist "%TARGET_DIR%\steam_old_lib\" (
copy /y "%ROOT%\post_build\README.steam_old_lib.md" "%TARGET_DIR%\steam_old_lib\"
)
if exist "%TARGET_DIR%\steamclient_experimental\" (
xcopy /y /s /e /r "%ROOT%\post_build\win\ColdClientLoader.EXAMPLE\" "%TARGET_DIR%\steamclient_experimental\dll_injection.EXAMPLE\"
copy /y "%ROOT%\post_build\README.experimental_steamclient.md" "%TARGET_DIR%\steamclient_experimental\"

View File

@@ -0,0 +1,12 @@
## What is this ?
This is an old Windows-only library equivalent to `steam_api.dll` that's still used by some old games.
## How to use ?
- Copy/Paste it beside the game's `.exe` file
- Inject it via `ColdClientLoader`, or any other loader you prefer
## Purpose ?
Bypass initial/basic checks in old games protected with old version of Stub drm.
## Note
Note that it doesn't emulate any Steam functionality at the moment.

View File

@@ -1354,6 +1354,45 @@ project "steamclient_experimental_extra"
-- End steamclient_experimental_extra
-- Project lib_steam_old
project "lib_steam_old"
-- https://premake.github.io/docs/Configurations-and-Platforms/#per-project-configurations
removeplatforms { "x64" }
kind "SharedLib"
location "%{wks.location}/%{prj.name}"
targetdir(path.join(build_dir, os_iden, _ACTION, "%{cfg.buildcfg}/steam_old_lib"))
targetname "Steam"
-- include dir
---------
-- x32 include dir
includedirs {
x32_deps_include,
}
-- common source & header files
---------
filter {} -- reset the filter and remove all active keywords
files {
"steam_old_lib/**",
"helpers/common_helpers.cpp", "helpers/common_helpers/**",
'libs/utfcpp/**',
-- detours
detours_files,
}
removefiles {
'libs/detours/uimports.cc',
}
-- x32 common source files
files {
"resources/win/client/32/resources.rc"
}
-- End lib_steam_old
-- Project steamclient_experimental_loader
project "steamclient_experimental_loader"
kind "WindowedApp"

34
steam_old_lib/dllmain.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "steam_old_lib.hpp"
static bool dll_loaded = false;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
switch (reason) {
case DLL_PROCESS_ATTACH: {
if (!soldlib::patch((void*)hModule)) {
#ifdef SOLD_EXTRA_DEBUG
MessageBoxA(nullptr, "Failed to initialize", "Main", MB_OK | MB_ICONERROR);
#endif
// https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain
// "The system immediately calls your entry-point function with DLL_PROCESS_DETACH and unloads the DLL"
return FALSE;
}
dll_loaded = true;
}
break;
case DLL_PROCESS_DETACH: {
if (dll_loaded) {
soldlib::restore();
}
}
break;
}
return TRUE;
}

View File

@@ -0,0 +1,399 @@
#include "steam_old_lib.hpp"
#include "common_helpers/common_helpers.hpp"
#include "detours/detours.h"
#include <string>
#include <stdexcept>
#include <string_view>
#include <cstdint>
#include <Softpub.h> // WinVerifyTrust
#define S_API extern "C" __declspec(dllexport)
#define STEAM_CALL __cdecl
#define STEAM_INVALID_CALL_HANDLE 0
static HMODULE my_hModule = nullptr;
HMODULE wintrust_dll = nullptr;
std::wstring wintrust_lib_path = L"";
static bool WinVerifyTrust_hooked = false;
static bool LoadLibraryA_hooked = false;
static bool LoadLibraryExA_hooked = false;
static bool LoadLibraryW_hooked = false;
static bool LoadLibraryExW_hooked = false;
static decltype(WinVerifyTrust) *actual_WinVerifyTrust = nullptr;
__declspec(noinline)
static LONG WINAPI WinVerifyTrust_hook(HWND hwnd, GUID *pgActionID, LPVOID pWVTData) {
if (WinVerifyTrust_hooked) {
SetLastError(ERROR_SUCCESS);
return 0; // success
}
if (actual_WinVerifyTrust) {
return actual_WinVerifyTrust(hwnd, pgActionID, pWVTData);
}
SetLastError(ERROR_SUCCESS);
return 0; // success
}
static decltype(LoadLibraryA) *actual_LoadLibraryA = LoadLibraryA;
__declspec(noinline)
static HMODULE WINAPI LoadLibraryA_hook(LPCSTR lpLibFileName)
{
if (LoadLibraryA_hooked &&
lpLibFileName && lpLibFileName[0] &&
common_helpers::ends_with_i(lpLibFileName, "Steam.dll")) {
return my_hModule;
}
return actual_LoadLibraryA(lpLibFileName);
}
static decltype(LoadLibraryExA) *actual_LoadLibraryExA = LoadLibraryExA;
__declspec(noinline)
static HMODULE WINAPI LoadLibraryExA_hook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
if (LoadLibraryExA_hooked &&
lpLibFileName && lpLibFileName[0] &&
common_helpers::ends_with_i(lpLibFileName, "Steam.dll")) {
return my_hModule;
}
return actual_LoadLibraryExA(lpLibFileName, hFile, dwFlags);
}
static decltype(LoadLibraryW) *actual_LoadLibraryW = LoadLibraryW;
__declspec(noinline)
static HMODULE WINAPI LoadLibraryW_hook(LPCWSTR lpLibFileName)
{
if (LoadLibraryW_hooked &&
lpLibFileName && lpLibFileName[0] &&
common_helpers::ends_with_i(lpLibFileName, L"Steam.dll")) {
return my_hModule;
}
return actual_LoadLibraryW(lpLibFileName);
}
static decltype(LoadLibraryExW) *actual_LoadLibraryExW = LoadLibraryExW;
__declspec(noinline)
static HMODULE WINAPI LoadLibraryExW_hook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
if (LoadLibraryExW_hooked &&
lpLibFileName && lpLibFileName[0] &&
common_helpers::ends_with_i(lpLibFileName, L"Steam.dll")) {
return my_hModule;
}
return actual_LoadLibraryExW(lpLibFileName, hFile, dwFlags);
}
static bool redirect_win32_apis()
{
try {
wintrust_dll = LoadLibraryExW(wintrust_lib_path.c_str(), NULL, 0);
if (!wintrust_dll) throw std::runtime_error("");
actual_WinVerifyTrust = (decltype(actual_WinVerifyTrust))GetProcAddress(wintrust_dll, "WinVerifyTrust");
if (!actual_WinVerifyTrust) throw std::runtime_error("");
if (DetourTransactionBegin() != NO_ERROR) throw std::runtime_error("");
if (DetourUpdateThread(GetCurrentThread()) != NO_ERROR) throw std::runtime_error("");
if (DetourAttach((PVOID *)&actual_WinVerifyTrust, (PVOID)WinVerifyTrust_hook) != NO_ERROR) throw std::runtime_error("");
if (DetourAttach((PVOID *)&actual_LoadLibraryA, (PVOID)LoadLibraryA_hook) != NO_ERROR) throw std::runtime_error("");
if (DetourAttach((PVOID *)&actual_LoadLibraryExA, (PVOID)LoadLibraryExA_hook) != NO_ERROR) throw std::runtime_error("");
if (DetourAttach((PVOID *)&actual_LoadLibraryW, (PVOID)LoadLibraryW_hook) != NO_ERROR) throw std::runtime_error("");
if (DetourAttach((PVOID *)&actual_LoadLibraryExW, (PVOID)LoadLibraryExW_hook) != NO_ERROR) throw std::runtime_error("");
if (DetourTransactionCommit() != NO_ERROR) throw std::runtime_error("");
WinVerifyTrust_hooked = true;
LoadLibraryA_hooked = true;
LoadLibraryExA_hooked = true;
LoadLibraryW_hooked = true;
LoadLibraryExW_hooked = true;
return true;
} catch (...) {
}
if (wintrust_dll) {
FreeLibrary(wintrust_dll);
wintrust_dll = nullptr;
}
return false;
}
static bool restore_win32_apis()
{
WinVerifyTrust_hooked = false;
LoadLibraryA_hooked = false;
LoadLibraryExA_hooked = false;
LoadLibraryW_hooked = false;
LoadLibraryExW_hooked = false;
bool ret = false;
try {
if (DetourTransactionBegin() != NO_ERROR) throw std::runtime_error("");
if (DetourUpdateThread(GetCurrentThread()) != NO_ERROR) throw std::runtime_error("");
if (actual_WinVerifyTrust) {
DetourDetach((PVOID *)&actual_WinVerifyTrust, (PVOID)WinVerifyTrust_hook);
}
DetourDetach((PVOID *)&actual_LoadLibraryA, (PVOID)LoadLibraryA_hook);
DetourDetach((PVOID *)&actual_LoadLibraryExA, (PVOID)LoadLibraryExA_hook);
DetourDetach((PVOID *)&actual_LoadLibraryW, (PVOID)LoadLibraryW_hook);
DetourDetach((PVOID *)&actual_LoadLibraryExW, (PVOID)LoadLibraryExW_hook);
if (DetourTransactionCommit() != NO_ERROR) throw std::runtime_error("");
ret = true;
} catch (...) {
}
if (wintrust_dll) {
FreeLibrary(wintrust_dll);
wintrust_dll = nullptr;
}
return ret;
}
#ifdef SOLD_EXTRA_DEBUG
extern "C" __declspec(dllexport)
#endif
void __cdecl notify_patch_done()
{
#ifdef SOLD_EXTRA_DEBUG
MessageBoxA(nullptr, "Cleanup/Uninstall was called, hook a debugger here!", "Cleanup called", MB_OK | MB_ICONASTERISK);
#endif
}
static void set_ok_err(soldlib::TSteamError *pError) {
if (pError) {
pError->eSteamError = soldlib::ESteamError::eSteamErrorNone;
pError->eDetailedErrorType = soldlib::EDetailedPlatformErrorType::eNoDetailedErrorAvailable;
pError->nDetailedErrorCode = 0;
pError->szDesc[0] = 0;
}
}
static void set_bad_err(soldlib::TSteamError *pError) {
if (pError) {
pError->eSteamError = soldlib::ESteamError::eSteamErrorBadArg;
pError->eDetailedErrorType = soldlib::EDetailedPlatformErrorType::eNoDetailedErrorAvailable;
pError->nDetailedErrorCode = soldlib::ESteamError::eSteamErrorBadArg;
pError->szDesc[0] = 0;
}
}
bool soldlib::patch(void *lib_hModule)
{
my_hModule = (HMODULE)lib_hModule;
if (wintrust_lib_path.empty()) {
auto size = GetSystemDirectoryW(&wintrust_lib_path[0], 0);
if (size <= 0) return false;
wintrust_lib_path.resize(size);
size = GetSystemDirectoryW(&wintrust_lib_path[0], (unsigned)wintrust_lib_path.size());
if (size >= (unsigned)wintrust_lib_path.size()) {
wintrust_lib_path.clear();
return false;
}
wintrust_lib_path.pop_back(); // remove null
wintrust_lib_path += L"\\Wintrust.dll";
}
return redirect_win32_apis();
}
bool soldlib::restore()
{
return restore_win32_apis();
}
S_API int STEAM_CALL SteamStartup( unsigned int uUsingMask, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamCleanup( soldlib::TSteamError *pError )
{
set_ok_err(pError);
// restore_win32_apis(); // appid 7450 loads the dll again at runtime, we can't unload here
notify_patch_done();
return 1;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamUninstall( soldlib::TSteamError *pError )
{
set_ok_err(pError);
// restore_win32_apis(); // appid 7450 loads the dll again at runtime, we can't unload here
notify_patch_done();
return STEAM_INVALID_CALL_HANDLE;
}
S_API int STEAM_CALL SteamGetAccountStatus( unsigned int* puAccountStatusFlags, soldlib::TSteamError *pError )
{
if (puAccountStatusFlags) {
*puAccountStatusFlags =
soldlib::ESteamAccountStatusBitFields::eSteamAccountStatusDefault
| soldlib::ESteamAccountStatusBitFields::eSteamAccountStatusEmailVerified;
}
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamGetCurrentEmailAddress( char *szEmailaddress, unsigned int uBufSize, unsigned int *puEmailaddressChars, soldlib::TSteamError *pError )
{
constexpr const static std::string_view EMAIL = "orca@gbe.com";
unsigned copied = 0;
if (szEmailaddress && uBufSize > 0) {
copied = (unsigned)EMAIL.copy(szEmailaddress, uBufSize);
szEmailaddress[copied] = 0;
}
if (puEmailaddressChars) *puEmailaddressChars = copied;
set_ok_err(pError);
return 1;
}
// https://gitlab.com/KittenPopo/csgo-2018-source/-/blob/main/common/Steam.h
// https://github.com/SteamRE/open-steamworks/blob/master/Open%20Steamworks/Steam.h
S_API int STEAM_CALL SteamIsAppSubscribed( unsigned int uAppId, int *pbIsAppSubscribed, int *pbIsSubscriptionPending, soldlib::TSteamError *pError ) {
if (pbIsAppSubscribed) *pbIsAppSubscribed = 1;
if (pbIsSubscriptionPending) *pbIsSubscriptionPending = 0;
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamIsSubscribed( unsigned int uSubscriptionId, int *pbIsSubscribed, int *pbIsSubscriptionPending, soldlib::TSteamError *pError )
{
if (pbIsSubscribed) *pbIsSubscribed = 1;
if (pbIsSubscriptionPending) *pbIsSubscriptionPending = 0;
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamIsLoggedIn( int *pbIsLoggedIn, soldlib::TSteamError *pError )
{
if (pbIsLoggedIn) *pbIsLoggedIn = 1;
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamIsSecureComputer( int *pbIsSecureComputer, soldlib::TSteamError *pError )
{
if (pbIsSecureComputer) *pbIsSecureComputer = 1;
set_ok_err(pError);
return 1;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamLaunchApp( unsigned int uAppId, unsigned int uLaunchOption, const char *cszArgs, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamLogin( const char *cszUser, const char *cszPassphrase, int bIsSecureComputer, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamRefreshAccountInfo( soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamRefreshAccountInfoEx( int bContentDescriptionOnly, soldlib::TSteamError* pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamRefreshAccountInfo2( int bRefreshAccount, int bRefreshCDDB, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return 1;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamRefreshLogin( const char *cszPassphrase, int bIsSecureComputer, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamSubscribe( unsigned int uSubscriptionId, const void /*TSteamSubscriptionBillingInfo*/ *pSubscriptionBillingInfo, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamUnsubscribe( unsigned int uSubscriptionId, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return 1;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamUpdateAccountBillingInfo( const void /*TSteamPaymentCardInfo*/ *pPaymentCardInfo, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned /*SteamCallHandle_t*/ STEAM_CALL SteamUpdateSubscriptionBillingInfo( unsigned int uSubscriptionId, const void /*TSteamSubscriptionBillingInfo*/ *pSubscriptionBillingInfo, soldlib::TSteamError *pError )
{
set_ok_err(pError);
return STEAM_INVALID_CALL_HANDLE;
}
S_API unsigned int STEAM_CALL SteamNumAppsRunning( soldlib::TSteamError *pError )
{
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamCheckAppOwnership( unsigned int uAppId, int* pbOwned, void /*TSteamGlobalUserID*/ *pSteamID, soldlib::TSteamError* pError )
{
if (pbOwned) *pbOwned = 1;
set_ok_err(pError);
return 1;
}
S_API int STEAM_CALL SteamGetAppDLCStatus( unsigned int uAppId, unsigned int uDLCCacheId, int *pbDownloaded, soldlib::TSteamError *pError )
{
if (pbDownloaded) *pbDownloaded = 0;
if (0 == uAppId || UINT32_MAX == uAppId) {
set_bad_err(pError);
return 0;
}
if (pbDownloaded) *pbDownloaded = 1;
set_ok_err(pError);
return 1;
}

View File

@@ -0,0 +1,122 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#if !defined(SOLD_EXTRA_DEBUG)
#if defined(DEBUG) || defined(_DEBUG)
#define SOLD_EXTRA_DEBUG
#endif
#endif
namespace soldlib {
// https://github.com/SteamRE/open-steamworks/blob/master/Open%20Steamworks/SteamTypes.h
#define STEAM_MAX_PATH 255
// https://github.com/SteamRE/open-steamworks/blob/master/Open%20Steamworks/ESteamError.h
typedef enum ESteamError
{
eSteamErrorNone = 0,
eSteamErrorUnknown = 1,
eSteamErrorLibraryNotInitialized = 2,
eSteamErrorLibraryAlreadyInitialized = 3,
eSteamErrorConfig = 4,
eSteamErrorContentServerConnect = 5,
eSteamErrorBadHandle = 6,
eSteamErrorHandlesExhausted = 7,
eSteamErrorBadArg = 8,
eSteamErrorNotFound = 9,
eSteamErrorRead = 10,
eSteamErrorEOF = 11,
eSteamErrorSeek = 12,
eSteamErrorCannotWriteNonUserConfigFile = 13,
eSteamErrorCacheOpen = 14,
eSteamErrorCacheRead = 15,
eSteamErrorCacheCorrupted = 16,
eSteamErrorCacheWrite = 17,
eSteamErrorCacheSession = 18,
eSteamErrorCacheInternal = 19,
eSteamErrorCacheBadApp = 20,
eSteamErrorCacheVersion = 21,
eSteamErrorCacheBadFingerPrint = 22,
eSteamErrorNotFinishedProcessing = 23,
eSteamErrorNothingToDo = 24,
eSteamErrorCorruptEncryptedUserIDTicket = 25,
eSteamErrorSocketLibraryNotInitialized = 26,
eSteamErrorFailedToConnectToUserIDTicketValidationServer = 27,
eSteamErrorBadProtocolVersion = 28,
eSteamErrorReplayedUserIDTicketFromClient = 29,
eSteamErrorReceiveResultBufferTooSmall = 30,
eSteamErrorSendFailed = 31,
eSteamErrorReceiveFailed = 32,
eSteamErrorReplayedReplyFromUserIDTicketValidationServer = 33,
eSteamErrorBadSignatureFromUserIDTicketValidationServer = 34,
eSteamErrorValidationStalledSoAborted = 35,
eSteamErrorInvalidUserIDTicket = 36,
eSteamErrorClientLoginRateTooHigh = 37,
eSteamErrorClientWasNeverValidated = 38,
eSteamErrorInternalSendBufferTooSmall = 39,
eSteamErrorInternalReceiveBufferTooSmall = 40,
eSteamErrorUserTicketExpired = 41,
eSteamErrorCDKeyAlreadyInUseOnAnotherClient = 42,
eSteamErrorNotLoggedIn = 101,
eSteamErrorAlreadyExists = 102,
eSteamErrorAlreadySubscribed = 103,
eSteamErrorNotSubscribed = 104,
eSteamErrorAccessDenied = 105,
eSteamErrorFailedToCreateCacheFile = 106,
eSteamErrorCallStalledSoAborted = 107,
eSteamErrorEngineNotRunning = 108,
eSteamErrorEngineConnectionLost = 109,
eSteamErrorLoginFailed = 110,
eSteamErrorAccountPending = 111,
eSteamErrorCacheWasMissingRetry = 112,
eSteamErrorLocalTimeIncorrect = 113,
eSteamErrorCacheNeedsDecryption = 114,
eSteamErrorAccountDisabled = 115,
eSteamErrorCacheNeedsRepair = 116,
eSteamErrorRebootRequired = 117,
eSteamErrorNetwork = 200,
eSteamErrorOffline = 201,
} ESteamError;
// https://github.com/SteamRE/open-steamworks/blob/master/Open%20Steamworks/TSteamError.h
typedef enum EDetailedPlatformErrorType
{
eNoDetailedErrorAvailable,
eStandardCerrno,
eWin32LastError,
eWinSockLastError,
eDetailedPlatformErrorCount
} EDetailedPlatformErrorType;
typedef struct TSteamError
{
ESteamError eSteamError;
EDetailedPlatformErrorType eDetailedErrorType;
int nDetailedErrorCode;
char szDesc[STEAM_MAX_PATH];
} TSteamError;
// https://gitlab.com/KittenPopo/csgo-2018-source/-/blob/main/common/SteamCommon.h
// https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/common/steamcommon.h
typedef enum
{
eSteamAccountStatusDefault = 0x00000000,
eSteamAccountStatusEmailVerified = 0x00000001,
/* Note: Mask value 0x2 is reserved for future use. (Some, but not all, public accounts already have this set.) */
eSteamAccountDisabled = 0x00000004
} ESteamAccountStatusBitFields;
bool patch(void *lib_hModule);
bool restore();
}