From 67bd07949d47e4c70808c2a3e4487066c8f2a069 Mon Sep 17 00:00:00 2001 From: Louvenarde Date: Sat, 3 Feb 2024 00:31:03 +0100 Subject: [PATCH] GUID On runtime --- .../Modules/AssetInterfaces/IXModel.cpp | 5 - src/Components/Modules/Auth.cpp | 111 +++++++------ src/Components/Modules/Download.cpp | 2 +- src/STDInclude.hpp | 3 + src/Utils/Cryptography.cpp | 146 ++++++++++++------ src/Utils/Cryptography.hpp | 9 +- 6 files changed, 165 insertions(+), 111 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IXModel.cpp b/src/Components/Modules/AssetInterfaces/IXModel.cpp index ac22bc5e..5daf43fc 100644 --- a/src/Components/Modules/AssetInterfaces/IXModel.cpp +++ b/src/Components/Modules/AssetInterfaces/IXModel.cpp @@ -873,11 +873,6 @@ namespace Assets void IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(Game::XModel* model, Utils::Memory::Allocator& allocator) { - if (model->name == "body_airport_com_a"s) - { - printf(""); - } - std::string requiredBonesForHumanoid[] = { "j_spinelower", "j_spineupper", diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index 2cae7191..c3642b70 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -19,14 +19,15 @@ namespace Components std::vector Auth::BannedUids = { - 0xf4d2c30b712ac6e3, + // No longer necessary + /* 0xf4d2c30b712ac6e3, 0xf7e33c4081337fa3, 0x6f5597f103cc50e9, - 0xecd542eee54ffccf, + 0xecd542eee54ffccf,*/ }; bool Auth::HasAccessToReservedSlot; - + void Auth::Frame() { if (TokenContainer.generating) @@ -45,7 +46,7 @@ namespace Components if (mseconds < 0) mseconds = 0; } - Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", Utils::String::VA("Increasing security level from %d to %d (est. %s)",GetSecurityLevel(), TokenContainer.targetLevel, Utils::String::FormatTimeSpan(static_cast(mseconds)).data())); + Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", Utils::String::VA("Increasing security level from %d to %d (est. %s)", GetSecurityLevel(), TokenContainer.targetLevel, Utils::String::FormatTimeSpan(static_cast(mseconds)).data())); } else if (TokenContainer.thread.joinable()) { @@ -53,7 +54,7 @@ namespace Components TokenContainer.generating = false; StoreKey(); - Logger::Debug("Security level is {}",GetSecurityLevel()); + Logger::Debug("Security level is {}", GetSecurityLevel()); Command::Execute("closemenu security_increase_popmenu", false); if (!TokenContainer.cancel) @@ -212,7 +213,7 @@ namespace Components SteamID guid; guid.bits = xuid; - if (Bans::IsBanned({guid, address.getIP()})) + if (Bans::IsBanned({ guid, address.getIP() })) { Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address)); Network::Send(address, "error\nEXE_ERR_BANNED_PERM"); @@ -304,15 +305,15 @@ namespace Components xor eax, eax jmp safeContinue - noAccess: - mov eax, dword ptr [edx + 0x10] + noAccess : + mov eax, dword ptr[edx + 0x10] - safeContinue: - // Game code skipped by hook - add esp, 0xC + safeContinue : + // Game code skipped by hook + add esp, 0xC - push 0x460FB3 - ret + push 0x460FB3 + ret } } @@ -342,6 +343,8 @@ namespace Components void Auth::StoreKey() { + // We write the key as a decoy I suppose - it's really no longer needed + // TODO Remove this part if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && GuidKey.isValid()) { Proto::Auth::Certificate cert; @@ -366,23 +369,31 @@ namespace Components if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return; if (!force && GuidKey.isValid()) return; + // We no longer read the key from disk + // While having obvious advantages to palliate the fact that some users are not playing on Steam, + // it is creating a lot of issues because GUID files get packaged with the game when people share it + // and it makes it harder for server owners to identify players uniquely + // Note that we could store it in Appdata, but then it would be dissociated from the rest of player files, + // so for now we're doing something else: the key is generated uniquely from the machine's characteristics + // It is not (necessarily) stored and therefore, not loaded, so it could make it harder to evade bans without + // using a custom client that would need regeneration at each update. +#if false Proto::Auth::Certificate cert; if (cert.ParseFromString(::Utils::IO::ReadFile("players/guid.dat"))) { GuidKey.deserialize(cert.privatekey()); GuidToken = cert.token(); ComputeToken = cert.ctoken(); - } + } else { GuidKey.free(); } if (!GuidKey.isValid()) - { +#endif Auth::GenerateKey(); - } - } +} uint32_t Auth::GetSecurityLevel() { @@ -404,18 +415,18 @@ namespace Components // Start thread TokenContainer.thread = std::thread([&level]() - { - TokenContainer.generating = true; - TokenContainer.hashes = 0; - TokenContainer.startTime = Game::Sys_Milliseconds(); - IncrementToken(GuidToken, ComputeToken, GuidKey.getPublicKey(), TokenContainer.targetLevel, &TokenContainer.cancel, &TokenContainer.hashes); - TokenContainer.generating = false; - - if (TokenContainer.cancel) { - Logger::Print("Token incrementation thread terminated\n"); - } - }); + TokenContainer.generating = true; + TokenContainer.hashes = 0; + TokenContainer.startTime = Game::Sys_Milliseconds(); + IncrementToken(GuidToken, ComputeToken, GuidKey.getPublicKey(), TokenContainer.targetLevel, &TokenContainer.cancel, &TokenContainer.hashes); + TokenContainer.generating = false; + + if (TokenContainer.cancel) + { + Logger::Print("Token incrementation thread terminated\n"); + } + }); } } @@ -521,36 +532,36 @@ namespace Components // Guid command Command::Add("guid", [] - { - Logger::Print("Your guid: {:#X}\n", Steam::SteamUser()->GetSteamID().bits); - }); + { + Logger::Print("Your guid: {:#X}\n", Steam::SteamUser()->GetSteamID().bits); + }); if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) { Command::Add("securityLevel", [](const Command::Params* params) - { - if (params->size() < 2) - { - const auto level = GetZeroBits(GuidToken, GuidKey.getPublicKey()); - Logger::Print("Your current security level is {}\n", level); - Logger::Print("Your security token is: {}\n", Utils::String::DumpHex(GuidToken.toString(), "")); - Logger::Print("Your computation token is: {}\n", Utils::String::DumpHex(ComputeToken.toString(), "")); - - Toast::Show("cardicon_locked", "^5Security Level", Utils::String::VA("Your security level is %d", level), 3000); - } - else { - const auto level = std::strtoul(params->get(1), nullptr, 10); - IncreaseSecurityLevel(level); - } - }); + if (params->size() < 2) + { + const auto level = GetZeroBits(GuidToken, GuidKey.getPublicKey()); + Logger::Print("Your current security level is {}\n", level); + Logger::Print("Your security token is: {}\n", Utils::String::DumpHex(GuidToken.toString(), "")); + Logger::Print("Your computation token is: {}\n", Utils::String::DumpHex(ComputeToken.toString(), "")); + + Toast::Show("cardicon_locked", "^5Security Level", Utils::String::VA("Your security level is %d", level), 3000); + } + else + { + const auto level = std::strtoul(params->get(1), nullptr, 10); + IncreaseSecurityLevel(level); + } + }); } UIScript::Add("security_increase_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) - { - TokenContainer.cancel = true; - Logger::Print("Token incrementation process canceled!\n"); - }); + { + TokenContainer.cancel = true; + Logger::Print("Token incrementation process canceled!\n"); + }); } Auth::~Auth() diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 237c3ef9..0123504e 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -463,7 +463,7 @@ namespace Components void Download::Reply(mg_connection* connection, const std::string& contentType, const std::string& data) { - const auto formatted = std::format("Content-Type: {}\r\n", contentType); + const auto formatted = std::format("Content-Type: {}\r\nAccess-Control-Allow-Origin: *\r\n", contentType); mg_http_reply(connection, 200, formatted.c_str(), "%s", data.c_str()); } diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 03aad46b..56d3e7b1 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -56,6 +56,9 @@ #include #pragma comment (lib, "dwmapi.lib") +#include +#pragma comment (lib, "iphlpapi.lib") + // Ignore the warnings #pragma warning(push) #pragma warning(disable: 4100) diff --git a/src/Utils/Cryptography.cpp b/src/Utils/Cryptography.cpp index d07525ff..cbfe3c82 100644 --- a/src/Utils/Cryptography.cpp +++ b/src/Utils/Cryptography.cpp @@ -8,10 +8,80 @@ namespace Utils { void Initialize() { - DES3::Initialize(); Rand::Initialize(); } + std::string GetEntropy() + { + DWORD volumeID; + if (GetVolumeInformationA("C:\\", + NULL, + NULL, + &volumeID, + NULL, + NULL, + NULL, + NULL + )) + { + // Drive info + return std::to_string(volumeID); + } + else + { + // Resort to mac address + unsigned long outBufLen = 0; + DWORD dwResult = GetAdaptersInfo(NULL, &outBufLen); + if (dwResult == ERROR_BUFFER_OVERFLOW) // This is what we're expecting + { + // Now allocate a structure of the required size. + PIP_ADAPTER_INFO pIpAdapterInfo = reinterpret_cast(malloc(outBufLen)); + dwResult = GetAdaptersInfo(pIpAdapterInfo, &outBufLen); + if (dwResult == ERROR_SUCCESS) + { + while (pIpAdapterInfo) + { + switch (pIpAdapterInfo->Type) + { + default: + pIpAdapterInfo = pIpAdapterInfo->Next; + continue; + + case IF_TYPE_IEEE80211: + case MIB_IF_TYPE_ETHERNET: + { + + std::string macAddress{}; + for (size_t i = 0; i < ARRAYSIZE(pIpAdapterInfo->Address); i++) + { + macAddress += std::to_string(pIpAdapterInfo->Address[i]); + } + + free(pIpAdapterInfo); + return macAddress; + } + } + } + } + else + { + // :( + // Continue to fallback + } + + // Free before going next because clearly this is not working + free(pIpAdapterInfo); + } + else + { + // No MAC, no C: drive? Come on + } + + // ultimate fallback + return std::to_string(Rand::GenerateInt()); + } + } + #pragma region Rand prng_state Rand::State; @@ -51,8 +121,29 @@ namespace Utils Key key; ltc_mp = ltm_desc; - register_prng(&sprng_desc); - ecc_make_key(nullptr, find_prng("sprng"), bits / 8, key.getKeyPtr()); + int descriptorIndex = register_prng(&chacha20_prng_desc); + + // allocate state + { + prng_state* state = new prng_state(); + + chacha20_prng_start(state); + + const auto entropy = Cryptography::GetEntropy(); + chacha20_prng_add_entropy(reinterpret_cast(entropy.data()), entropy.size(), state); + + chacha20_prng_ready(state); + + const auto result = ecc_make_key(state, descriptorIndex, bits / 8, key.getKeyPtr()); + + if (result != CRYPT_OK) + { + Components::Logger::PrintError(Game::conChannel_t::CON_CHANNEL_ERROR, "There was an issue generating your unique player ID! Please contact support"); + } + + // Deallocate state + delete state; + } return key; } @@ -79,8 +170,8 @@ namespace Utils int result = 0; return (ecc_verify_hash(reinterpret_cast(signature.data()), signature.size(), - reinterpret_cast(message.data()), message.size(), - &result, key.getKeyPtr()) == CRYPT_OK && result != 0); + reinterpret_cast(message.data()), message.size(), + &result, key.getKeyPtr()) == CRYPT_OK && result != 0); } #pragma endregion @@ -115,7 +206,7 @@ namespace Utils ltc_mp = ltm_desc; rsa_sign_hash_ex(reinterpret_cast(hash.data()), hash.size(), - buffer, &length, LTC_PKCS_1_V1_5, nullptr, 0, hash_index, 0, key.getKeyPtr()); + buffer, &length, LTC_PKCS_1_V1_5, nullptr, 0, hash_index, 0, key.getKeyPtr()); return std::string{ reinterpret_cast(buffer), length }; } @@ -133,47 +224,8 @@ namespace Utils auto result = 0; return (rsa_verify_hash_ex(reinterpret_cast(signature.data()), signature.size(), - reinterpret_cast(hash.data()), hash.size(), LTC_PKCS_1_V1_5, - hash_index, 0, &result, key.getKeyPtr()) == CRYPT_OK && result != 0); - } - -#pragma endregion - -#pragma region DES3 - - void DES3::Initialize() - { - register_cipher(&des3_desc); - } - - std::string DES3::Encrypt(const std::string& text, const std::string& iv, const std::string& key) - { - std::string encData; - encData.resize(text.size()); - - symmetric_CBC cbc; - const auto des3 = find_cipher("3des"); - - cbc_start(des3, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), static_cast(key.size()), 0, &cbc); - cbc_encrypt(reinterpret_cast(text.data()), reinterpret_cast(encData.data()), text.size(), &cbc); - cbc_done(&cbc); - - return encData; - } - - std::string DES3::Decrpyt(const std::string& data, const std::string& iv, const std::string& key) - { - std::string decData; - decData.resize(data.size()); - - symmetric_CBC cbc; - const auto des3 = find_cipher("3des"); - - cbc_start(des3, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), static_cast(key.size()), 0, &cbc); - cbc_decrypt(reinterpret_cast(data.data()), reinterpret_cast(decData.data()), data.size(), &cbc); - cbc_done(&cbc); - - return decData; + reinterpret_cast(hash.data()), hash.size(), LTC_PKCS_1_V1_5, + hash_index, 0, &result, key.getKeyPtr()) == CRYPT_OK && result != 0); } #pragma endregion diff --git a/src/Utils/Cryptography.hpp b/src/Utils/Cryptography.hpp index 46ac5e14..36fbdd28 100644 --- a/src/Utils/Cryptography.hpp +++ b/src/Utils/Cryptography.hpp @@ -5,6 +5,7 @@ namespace Utils namespace Cryptography { void Initialize(); + std::string GetEntropy(); class Token { @@ -327,14 +328,6 @@ namespace Utils static bool VerifyMessage(Key key, const std::string& message, const std::string& signature); }; - class DES3 - { - public: - static void Initialize(); - static std::string Encrypt(const std::string& text, const std::string& iv, const std::string& key); - static std::string Decrpyt(const std::string& text, const std::string& iv, const std::string& key); - }; - class Tiger { public: