Skip to content

Commit 181c824

Browse files
authored
Merge pull request #15 from AceSLS/dev
Update to latest dev branch
2 parents bc6d529 + 69430f2 commit 181c824

11 files changed

Lines changed: 188 additions & 24 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ Afterwards run the installer again if that's what you've been using to launch SL
107107

108108
## Credits
109109

110+
- Special thanks to all the staff members of the Anti Denuvo Sanctuary
111+
for all the hard work they do. They also found a way to use SLSsteam
112+
I didn't even intend to, so shoutout to them
110113
- [DeveloperMikey](https://github.com/DeveloperMikey): Added Nix support
111114
- Riku_Wayfinder: Being extremely supportive and lightening my workload by a lot.
112115
So show him some love my guys <3

src/config.cpp

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "config.hpp"
22

3+
#include "log.hpp"
34
#include "yaml-cpp/yaml.h"
45

56
#include <cmath>
@@ -21,6 +22,11 @@ static const char* defaultConfig =
2122
"# AppId:\n"
2223
"# FirstDlcAppId: \"Dlc Name\"\n"
2324
"# SecondDlcAppId: \"Dlc Name\"\n\n"
25+
"#Example of DenuvoGames:\n"
26+
"#DenuvoGames:\n"
27+
"# SteamId:\n"
28+
"# - AppId1\n"
29+
"# - AppId2\n\n"
2430
"#Disables Family Share license locking for self and others\n"
2531
"DisableFamilyShareLock: yes\n\n"
2632
"#Switches to whitelist instead of the default blacklist\n"
@@ -37,12 +43,20 @@ static const char* defaultConfig =
3743
"#Extra Data for Dlcs belonging to a specific AppId. Only needed\n"
3844
"#when the App you're playing is hit by Steams 64 DLC limit\n"
3945
"DlcData:\n\n"
46+
"#Blocks games from unlocking on wrong accounts\n"
47+
"DenuvoGames:\n\n"
48+
"#Spoof Denuvo Games owner instead of blocking them\n"
49+
"DenuvoSpoof: no\n\n"
4050
"#Automatically disable SLSsteam when steamclient.so does not match a predefined file hash that is known to work\n"
4151
"#You should enable this if you're planing to use SLSsteam with Steam Deck's gamemode\n"
4252
"SafeMode: no\n\n"
53+
"#Toggles notifications via notify-send\n"
54+
"Notifications: yes\n\n"
4355
"#Warn user via notification when steamclient.so hash differs from known safe hash\n"
4456
"#Mostly useful for development so I don't accidentally miss an update\n"
4557
"WarnHashMissmatch: no\n\n"
58+
"#Notify when SLSsteam is done initializing\n"
59+
"NotifyInit: yes\n\n"
4660
"#Logs all calls to Steamworks (this makes the logfile huge! Only useful for debugging/analyzing\n"
4761
"ExtendedLogging: no";
4862

@@ -129,17 +143,23 @@ bool CConfig::loadSettings()
129143
automaticFilter = getSetting<bool>(node, "AutoFilterList", true);
130144
playNotOwnedGames = getSetting<bool>(node, "PlayNotOwnedGames", false);
131145
safeMode = getSetting<bool>(node, "SafeMode", false);
146+
notifications = getSetting<bool>(node, "Notifications", true);
132147
warnHashMissmatch = getSetting<bool>(node, "WarnHashMissmatch", false);
148+
notifyInit = getSetting<bool>(node, "NotifyInit", true);
133149
extendedLogging = getSetting<bool>(node, "ExtendedLogging", false);
150+
denuvoSpoof = getSetting<bool>(node, "DenuvoSpoof", false);
134151

135152
//TODO: Create smart logging function to log them automatically via getSetting
136153
g_pLog->info("DisableFamilyShareLock: %i\n", disableFamilyLock);
137154
g_pLog->info("UseWhitelist: %i\n", useWhiteList);
138155
g_pLog->info("AutoFilterList: %i\n", automaticFilter);
139156
g_pLog->info("PlayNotOwnedGames: %i\n", playNotOwnedGames);
140157
g_pLog->info("SafeMode: %i\n", safeMode);
158+
g_pLog->info("Notifications: %i\n", notifications);
141159
g_pLog->info("WarnHashMissmatch: %i\n", warnHashMissmatch);
160+
g_pLog->info("NotifyInit: %i\n", notifyInit);
142161
g_pLog->info("ExtendedLogging: %i\n", extendedLogging);
162+
g_pLog->info("DenuvoSpoof: %i\n", denuvoSpoof);
143163

144164
//TODO: Create function to parse these kinda nodes, instead of c+p them
145165
const auto appIdsNode = node["AppIds"];
@@ -220,7 +240,37 @@ bool CConfig::loadSettings()
220240
}
221241
else
222242
{
223-
g_pLog->notify("Missing DlcData entry in config!\n");
243+
g_pLog->notify("Missing DlcData entry in config!");
244+
}
245+
246+
const auto denuvoGamesNode = node["DenuvoGames"];
247+
if (denuvoGamesNode)
248+
{
249+
for (auto& steamIdNode : denuvoGamesNode)
250+
{
251+
try
252+
{
253+
const uint32_t steamId = steamIdNode.first.as<uint32_t>();
254+
denuvoGames[steamId] = std::unordered_set<uint32_t>();
255+
256+
for (auto& appIdNode : steamIdNode.second)
257+
{
258+
const uint32_t appId = appIdNode.as<uint32_t>();
259+
denuvoGames[steamId].emplace(appId);
260+
261+
//Again, not loggin SteamId because of privacy
262+
g_pLog->debug("Added DenuvoGame %u\n", appId, steamId);
263+
}
264+
}
265+
catch (...)
266+
{
267+
g_pLog->notify("Failed to parse DenuvoGames!");
268+
}
269+
}
270+
}
271+
else
272+
{
273+
g_pLog->notify("Missing DenuvoGames entry in config!");
224274
}
225275

226276
return true;
@@ -260,4 +310,18 @@ bool CConfig::shouldExcludeAppId(uint32_t appId)
260310
return exclude;
261311
}
262312

313+
uint32_t CConfig::getDenuvoGameOwner(uint32_t appId)
314+
{
315+
for(const auto& tpl : denuvoGames)
316+
{
317+
if (tpl.second.contains(appId))
318+
{
319+
//g_pLog->once("%u is DenuvoGame\n", appId);
320+
return tpl.first;
321+
}
322+
}
323+
324+
return 0;
325+
}
326+
263327
CConfig g_config = CConfig();

src/config.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@ class CConfig {
2626
std::unordered_set<uint32_t> appIds;
2727
std::unordered_set<uint32_t> addedAppIds;
2828
std::unordered_map<uint32_t, CDlcData> dlcData;
29+
//SteamId, AppIds tuple
30+
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> denuvoGames;
31+
bool denuvoSpoof;
2932

3033
bool disableFamilyLock;
3134
bool useWhiteList;
3235
bool automaticFilter;
3336
bool playNotOwnedGames;
3437
bool safeMode;
38+
bool notifications;
3539
bool warnHashMissmatch;
40+
bool notifyInit;
3641
bool extendedLogging;
3742

3843
std::string getDir();
@@ -64,6 +69,7 @@ class CConfig {
6469
bool addAdditionalAppId(uint32_t appId);
6570

6671
bool shouldExcludeAppId(uint32_t appId);
72+
uint32_t getDenuvoGameOwner(uint32_t appId);
6773
};
6874

6975
extern CConfig g_config;

src/hooks.cpp

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "log.hpp"
55
#include "memhlp.hpp"
66
#include "patterns.hpp"
7+
#include "sdk/IClientUser.hpp"
78
#include "vftableinfo.hpp"
89

910
#include "libmem/libmem.h"
@@ -43,11 +44,11 @@ VFTHook<T>::VFTHook(const char* name) : Hook<T>::Hook(name)
4344
}
4445

4546
template<typename T>
46-
bool DetourHook<T>::setup(const char* pattern, const MemHlp::SigFollowMode followMode, T hookFn)
47+
bool DetourHook<T>::setup(const char* pattern, const MemHlp::SigFollowMode followMode, lm_byte_t* extraData, lm_size_t extraDataSize, T hookFn)
4748
{
4849
//Hardcoding g_modSteamClient here is definitely bad design, but we can easily change that
4950
//in case we ever need to
50-
lm_address_t oFn = MemHlp::searchSignature(this->name.c_str(), pattern, g_modSteamClient, followMode);
51+
lm_address_t oFn = MemHlp::searchSignature(this->name.c_str(), pattern, g_modSteamClient, followMode, extraData, extraDataSize);
5152
if (oFn == LM_ADDRESS_BAD)
5253
{
5354
return false;
@@ -59,6 +60,12 @@ bool DetourHook<T>::setup(const char* pattern, const MemHlp::SigFollowMode follo
5960
return true;
6061
}
6162

63+
template<typename T>
64+
bool DetourHook<T>::setup(const char* pattern, const MemHlp::SigFollowMode followMode, T hookFn)
65+
{
66+
return setup(pattern, followMode, nullptr, 0, hookFn);
67+
}
68+
6269
template<typename T>
6370
void DetourHook<T>::place()
6471
{
@@ -159,15 +166,32 @@ static bool hkCheckAppOwnership(void* a0, uint32_t appId, CAppOwnershipInfo* pOw
159166
return ret;
160167
}
161168

169+
const u_int32_t denuvoOwner = g_config.getDenuvoGameOwner(appId);
170+
//Do not modify Denuvo enabled Games
171+
if (!g_config.denuvoSpoof && denuvoOwner && denuvoOwner != g_currentSteamId)
172+
{
173+
//Would love to log the SteamId, but for users anonymity I won't
174+
g_pLog->once("Skipping %u because it's a Denuvo game from someone else\n", appId);
175+
return ret;
176+
}
177+
162178
if (g_config.isAddedAppId(appId) || (g_config.playNotOwnedGames && !pOwnershipInfo->purchased))
163179
{
164-
//Changing the purchased field is enough, but just for nicety in the Steamclient UI we change the owner too
165-
pOwnershipInfo->ownerSteamId = g_currentSteamId;
166-
pOwnershipInfo->purchased = true;
180+
if (!denuvoOwner || denuvoOwner == g_currentSteamId)
181+
{
182+
//Changing the purchased field is enough, but just for nicety in the Steamclient UI we change the owner too
183+
pOwnershipInfo->ownerSteamId = g_currentSteamId;
184+
pOwnershipInfo->familyShared = false;
185+
}
186+
else if (denuvoOwner)
187+
{
188+
pOwnershipInfo->ownerSteamId = denuvoOwner;
189+
pOwnershipInfo->familyShared = true;
190+
}
167191

192+
pOwnershipInfo->purchased = true;
168193
//Unnessecary but whatever
169194
pOwnershipInfo->permanent = true;
170-
pOwnershipInfo->familyShared = false;
171195

172196
//Found in backtrace
173197
pOwnershipInfo->releaseState = 4;
@@ -180,7 +204,7 @@ static bool hkCheckAppOwnership(void* a0, uint32_t appId, CAppOwnershipInfo* pOw
180204

181205
//Doing that might be not worth it since this will most likely be easier to mantain
182206
//TODO: Backtrace those 4 calls and only patch the really necessary ones since this might be prone to breakage
183-
if (g_config.disableFamilyLock && appIdOwnerOverride.count(appId) && appIdOwnerOverride.at(appId) < 4)
207+
if (!denuvoOwner && g_config.disableFamilyLock && appIdOwnerOverride.count(appId) && appIdOwnerOverride.at(appId) < 4)
184208
{
185209
pOwnershipInfo->ownerSteamId = 1; //Setting to "arbitrary" steam Id instead of own, otherwise bypass won't work for own games
186210
//Unnessecarry again, but whatever
@@ -384,6 +408,20 @@ static uint32_t hkClientUser_GetSubscribedApps(void* pClientUser, uint32_t* pApp
384408
return count;
385409
}
386410

411+
static bool hkClientUser_RequiresLegacyCDKey(void* pClientUser, uint32_t appId, uint32_t* a2)
412+
{
413+
const bool requiresKey = Hooks::IClientUser_RequiresLegacyCDKey.tramp.fn(pClientUser, appId, a2);
414+
g_pLog->once("IClientUser::RequiresLegacyCDKey(%p, %u, %u) -> %i\n", pClientUser, appId, a2, requiresKey);
415+
416+
if (requiresKey && g_config.isAddedAppId(appId))
417+
{
418+
g_pLog->once("Disable CD Key for %u\n", appId);
419+
return false;
420+
}
421+
422+
return requiresKey;
423+
}
424+
387425
static void patchRetn(lm_address_t address)
388426
{
389427
constexpr lm_byte_t retn = 0xC3;
@@ -480,6 +518,7 @@ namespace Hooks
480518
DetourHook<IClientApps_PipeLoop_t> IClientApps_PipeLoop("IClientApps::PipeLoop");
481519
DetourHook<IClientUser_BIsSubscribedApp_t> IClientUser_BIsSubscribedApp("IClientUser::BIsSubscribedApp");
482520
DetourHook<IClientUser_GetSubscribedApps_t> IClientUser_GetSubscribedApps("IClientUser::GetSubscribedApps");
521+
DetourHook<IClientUser_RequiresLegacyCDKey_t> IClientUser_RequiresLegacyCDKey("IClientUser::RequiresLegacyCDKey");
483522

484523
VFTHook<IClientAppManager_BIsDlcEnabled_t> IClientAppManager_BIsDlcEnabled("IClientAppManager::BIsDlcEnabled");
485524
VFTHook<IClientAppManager_LaunchApp_t> IClientAppManager_LaunchApp("IClientAppManager::LaunchApp");
@@ -496,8 +535,33 @@ bool Hooks::setup()
496535

497536
IClientUser_GetSteamId = MemHlp::searchSignature("IClientUser::GetSteamId", Patterns::GetSteamId, g_modSteamClient, MemHlp::SigFollowMode::Relative);
498537

499-
lm_address_t runningApp = MemHlp::searchSignature("RunningApp", Patterns::FamilyGroupRunningApp, g_modSteamClient, MemHlp::SigFollowMode::Relative);
500-
lm_address_t stopPlayingBorrowedApp = MemHlp::searchSignature("StopPlayingBorrowedApp", Patterns::StopPlayingBorrowedApp, g_modSteamClient, MemHlp::SigFollowMode::PrologueUpwards);
538+
lm_address_t runningApp = MemHlp::searchSignature("RnningApp", Patterns::FamilyGroupRunningApp, g_modSteamClient, MemHlp::SigFollowMode::Relative);
539+
540+
auto prologue = std::vector<lm_byte_t>({
541+
0x56, 0x57, 0xe5, 0x89, 0x55
542+
});
543+
lm_address_t stopPlayingBorrowedApp = MemHlp::searchSignature
544+
(
545+
"StopPlayingBorrowedApp",
546+
Patterns::StopPlayingBorrowedApp,
547+
g_modSteamClient,
548+
MemHlp::SigFollowMode::PrologueUpwards,
549+
&prologue[0],
550+
prologue.size()
551+
);
552+
553+
//TODO: Make this shit less verbose in case I fail my reversing & refactor for all this crap
554+
prologue = std::vector<lm_byte_t>({
555+
0x53, 0x56, 0x57, 0x55
556+
});
557+
bool requiresLegacyCDKey = IClientUser_RequiresLegacyCDKey.setup
558+
(
559+
Patterns::RequiresLegacyCDKey,
560+
MemHlp::SigFollowMode::PrologueUpwards,
561+
&prologue[0],
562+
prologue.size(),
563+
&hkClientUser_RequiresLegacyCDKey
564+
);
501565

502566
bool succeeded =
503567
CheckAppOwnership.setup(Patterns::CheckAppOwnership, MemHlp::SigFollowMode::Relative, &hkCheckAppOwnership)
@@ -509,7 +573,9 @@ bool Hooks::setup()
509573

510574
&& runningApp != LM_ADDRESS_BAD
511575
&& stopPlayingBorrowedApp != LM_ADDRESS_BAD
512-
&& IClientUser_GetSteamId != LM_ADDRESS_BAD;
576+
&& IClientUser_GetSteamId != LM_ADDRESS_BAD
577+
578+
&& requiresLegacyCDKey;
513579

514580
if (!succeeded)
515581
{
@@ -538,6 +604,7 @@ void Hooks::place()
538604
IClientAppManager_PipeLoop.place();
539605
IClientUser_BIsSubscribedApp.place();
540606
IClientUser_GetSubscribedApps.place();
607+
IClientUser_RequiresLegacyCDKey.place();
541608

542609
createAndPlaceSteamIdHook();
543610
}
@@ -551,6 +618,7 @@ void Hooks::remove()
551618
IClientAppManager_PipeLoop.remove();
552619
IClientUser_BIsSubscribedApp.remove();
553620
IClientUser_GetSubscribedApps.remove();
621+
IClientUser_RequiresLegacyCDKey.remove();
554622

555623
//VFT Hooks
556624
IClientAppManager_BIsDlcEnabled.remove();

src/hooks.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class DetourHook : public Hook<T>
4444
virtual void place();
4545
virtual void remove();
4646

47+
bool setup(const char* pattern, const MemHlp::SigFollowMode followMode, lm_byte_t* extraData, lm_size_t extraDataSize, T hookFn);
4748
bool setup(const char* pattern, const MemHlp::SigFollowMode followMode, T hookFn);
4849
};
4950

@@ -71,13 +72,15 @@ namespace Hooks
7172
typedef void(*IClientApps_PipeLoop_t)(void*, void*, void*, void*);
7273
typedef bool(*IClientUser_BIsSubscribedApp_t)(void*, uint32_t);
7374
typedef uint32_t(*IClientUser_GetSubscribedApps_t)(void*, uint32_t*, size_t, bool);
75+
typedef bool(*IClientUser_RequiresLegacyCDKey_t)(void*, uint32_t, uint32_t*);
7476

7577
extern DetourHook<LogSteamPipeCall_t> LogSteamPipeCall;
7678
extern DetourHook<CheckAppOwnership_t> CheckAppOwnership;
7779
extern DetourHook<IClientAppManager_PipeLoop_t> IClientAppManager_PipeLoop;
7880
extern DetourHook<IClientApps_PipeLoop_t> IClientApps_PipeLoop;
7981
extern DetourHook<IClientUser_BIsSubscribedApp_t> IClientUser_BIsSubscribedApp;
8082
extern DetourHook<IClientUser_GetSubscribedApps_t> IClientUser_GetSubscribedApps;
83+
extern DetourHook<IClientUser_RequiresLegacyCDKey_t> IClientUser_RequiresLegacyCDKey;
8184

8285
typedef bool(*IClientAppManager_BIsDlcEnabled_t)(void*, uint32_t, uint32_t, void*);
8386
typedef void*(*IClientAppManager_LaunchApp_t)(void*, uint32_t*, void*, void*, void*);

src/log.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "log.hpp"
2+
#include "config.hpp"
3+
24
#include <cstdlib>
35
#include <memory>
46

@@ -24,6 +26,12 @@ CLog::~CLog()
2426
}
2527
}
2628

29+
//Dirty workaround for not being able to access g_config from __log
30+
bool CLog::shouldNotify()
31+
{
32+
return g_config.notifications;
33+
}
34+
2735
CLog* CLog::createDefaultLog()
2836
{
2937
const char* home = getenv("HOME");

src/log.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ class CLog
153153
__log(LogLevel::Warn, msg, args...);
154154
}
155155

156+
//Do not include config.hpp in this header, otherwise things will break :) (proly due to recursive inclusion)
157+
static bool shouldNotify();
156158
static CLog* createDefaultLog();
157159
};
158160

0 commit comments

Comments
 (0)