#include "pch.h" #include "enums.h" #include "gameclasses.h" // Need this for a re-factor later. // Interface* interfaces = *reinterpret_cast(0x167F4FA48); // for (Interface* current = interfaces; current; current = reinterpret_cast(current->NextInterfacePtr)) // { // printf("%s: %p\n", current->InterfaceName, current->InterfacePtr); // } namespace GameGlobals { bool IsInitialized = false; CHostState* HostState = nullptr; CInputSystem* InputSystem = nullptr; CCVar* Cvar = nullptr; CClient* Client = nullptr; BanList* BanSystem = new BanList(); CKeyValuesSystem* KeyValuesSystem = nullptr; KeyValues** PlaylistKeyValues = nullptr; std::vector allPlaylists = { "none" }; namespace CustomCommandVariations { void Kick_Callback(CCommand* cmd) { std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); if (argSize < 2) // Do we atleast have 2 arguments? return; CCommand& cmdReference = *cmd; // Get reference. const char* firstArg = cmdReference[1]; // Get first arg. for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. { CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. if (!client) continue; if (!client->GetNetChan()) // Netchan valid? continue; void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. std::string clientName((char*)clientNamePtr, 32); // Get full name. if (clientName.empty()) // Empty name? continue; if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? continue; DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. } } void KickID_Callback(CCommand* cmd) { static auto HasOnlyDigits = [](const std::string& string) { for (const char& character : string) { if (std::isdigit(character) == 0) return false; } return true; }; std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); if (argSize < 2) // Do we atleast have 2 arguments? return; CCommand& cmdReference = *cmd; // Get reference. std::string firstArg = cmdReference[1]; // Get first arg. try { bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. { CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. if (!client) continue; if (!client->GetNetChan()) // Netchan valid? continue; std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. if (ipAddressField) { std::stringstream ss; ss << std::to_string(ipAddressField.GetValue()) << "." << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." << std::to_string(ipAddressField.Offset(0x3).GetValue()); finalIPAddress = ss.str(); } if (onlyDigits) { std::int64_t ID = static_cast(std::stoll(firstArg)); if (ID > MAX_PLAYERS) // Is it a possible originID? { std::int64_t originID = client->m_iOriginID; if (originID != ID) // See if they match. continue; } else // If its not try by userID. { std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. if (clientID != ID) // See if they match. continue; } DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. } else { if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? continue; DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. } } } catch (std::exception& e) { std::cout << "Kick UID asked for a userID or originID :( You can get the userID with the 'status' command. Error: " << e.what() << std::endl; return; } } void Unban_Callback(CCommand* cmd) { static auto HasOnlyDigits = [](const std::string& string) { for (const char& character : string) { if (std::isdigit(character) == 0) return false; } return true; }; std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); if (argSize < 2) // Do we atleast have 2 arguments? return; CCommand& cmdReference = *cmd; // Get reference. try { const char* firstArg = cmdReference[1]; if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. { GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. GameGlobals::BanSystem->Save(); // Save modified vector to file. } else { GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. GameGlobals::BanSystem->Save(); // Save modified vector to file. } } catch (std::exception& e) { std::cout << "Unban Error: " << e.what() << std::endl; return; } } void ReloadBanList_Callback(CCommand* cmd) { GameGlobals::BanSystem->Load(); // Reload banlist. } void Ban_Callback(CCommand* cmd) { std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); if (argSize < 2) // Do we atleast have 2 arguments? return; CCommand& cmdReference = *cmd; // Get reference. const char* firstArg = cmdReference[1]; // Get first arg. for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. { CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. if (!client) continue; if (!client->GetNetChan()) // Netchan valid? continue; void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. std::string clientName((char*)clientNamePtr, 32); // Get full name. if (clientName.empty()) // Empty name? continue; if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? continue; std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. if (ipAddressField && ipAddressField.GetValue() != 0x0) { std::stringstream ss; ss << std::to_string(ipAddressField.GetValue()) << "." << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." << std::to_string(ipAddressField.Offset(0x3).GetValue()); finalIPAddress = ss.str(); } GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. GameGlobals::BanSystem->Save(); // Save ban list. DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. } } void BanID_Callback(CCommand* cmd) { static auto HasOnlyDigits = [](const std::string& string) { for (const char& character : string) { if (std::isdigit(character) == 0) return false; } return true; }; std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); if (argSize < 2) // Do we atleast have 2 arguments? return; CCommand& cmdReference = *cmd; // Get reference. std::string firstArg = cmdReference[1]; try { bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. { CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. if (!client) continue; if (!client->GetNetChan()) // Netchan valid? continue; std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. if (ipAddressField) { std::stringstream ss; ss << std::to_string(ipAddressField.GetValue()) << "." << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." << std::to_string(ipAddressField.Offset(0x3).GetValue()); finalIPAddress = ss.str(); } if (onlyDigits) { std::int64_t ID = static_cast(std::stoll(firstArg)); if (ID > MAX_PLAYERS) // Is it a possible originID? { std::int64_t originID = client->m_iOriginID; if (originID != ID) // See if they match. continue; } else // If its not try by userID. { std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. if (clientID != ID) // See if they match. continue; } GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. GameGlobals::BanSystem->Save(); // Save ban list. DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. } else { if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? continue; GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. GameGlobals::BanSystem->Save(); // Save ban list. DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. } } } catch (std::exception& e) { std::cout << "Banid Error: " << e.what() << std::endl; return; } } } void NullHostNames() { const char* hostnameArray[] = { "pin_telemetry_hostname", "assetdownloads_hostname", "users_hostname", "persistence_hostname", "speechtotexttoken_hostname", "communities_hostname", "persistenceDef_hostname", "party_hostname", "speechtotext_hostname", "serverReports_hostname", "subscription_hostname", "steamlink_hostname", "staticfile_hostname", "matchmaking_hostname", "skill_hostname", "publication_hostname", "stats_hostname" }; for (int i = 0; i < 17; i++) { const char* name = hostnameArray[i]; Cvar->FindVar(name)->m_pzsCurrentValue = "0.0.0.0"; } } void InitGameGlobals() { HostState = reinterpret_cast(0x141736120); // Get CHostState from memory. InputSystem = *reinterpret_cast(0x14D40B380); // Get IInputSystem from memory. Cvar = *reinterpret_cast(0x14D40B348); // Get CCVar from memory. //KeyValuesSystem = reinterpret_cast(0x141F105C0); // Get CKeyValuesSystem from memory. //PlaylistKeyValues = reinterpret_cast(0x16705B980); // Get the KeyValue for the playlist file. //Client = reinterpret_cast(0x16073B200); //NullHostNames(); // Null all hostnames. //InitAllCommandVariations(); // Initialize our custom ConVars. //*(char*)addr_m_bRestrictServerCommands = true; // Restrict commands. //void* disconnect = Cvar->FindCommand("disconnect"); //*(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. //std::thread t1(InitPlaylist); // Start thread to grab playlists. //t1.detach(); // Detach thread from current one. IsInitialized = true; } void InitPlaylist() { while (true) { if ((*PlaylistKeyValues)) { KeyValues* playlists = (*PlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. if (playlists) { allPlaylists.clear(); for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. { allPlaylists.push_back(dat->GetName()); // Get all playlist names. } break; // Break if playlist got filled. } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } void InitAllCommandVariations() { void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr); void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr); void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr); void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr); void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr); } void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution) { static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70); static MemoryAddress NullSub = MemoryAddress(0x1401B3280); static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990); static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470); void* command = reinterpret_cast(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. memset(command, 0, 0x68); // Set all to null. std::uintptr_t commandPtr = reinterpret_cast(command); // To ptr. *(void**)commandPtr = ConCommandVtable.RCast(); // 0x0 to ConCommand vtable. *(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name. *(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string. *(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags. *(void**)(commandPtr + 0x40) = NullSub.RCast(); // 0x40 Nullsub since every concommand has it. *(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback. *(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. if (callbackAfterExecution) // Do we wanna have a callback after execution? { *(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution. } else { *(void**)(commandPtr + 0x58) = CallbackCompletion.RCast(); // 0x58 nullsub. } RegisterConCommand.RCast()((void*)commandPtr); // Register command in ConVarAccessor. return command; } ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk) { static MemoryAddress ConVarVtable = MemoryAddress(0x14046FB50).Offset(0x12).ResolveRelativeAddress(); // Get vtable ptr for ConVar table. static MemoryAddress ICvarVtable = MemoryAddress(0x14046FB50).Offset(0x29).ResolveRelativeAddress(); // Get vtable ptr for ICvar table. static MemoryAddress CreateConVar = MemoryAddress(0x140470540); // Get CreateConvar address. ConVar* allocatedConvar = reinterpret_cast(addr_MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. memset(allocatedConvar, 0, 0xA0); // Set all to null. std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast(); // 0x40 to ICvar table. *(void**)cvarPtr = ConVarVtable.RCast(); // 0x0 to ConVar vtable. CreateConVar.RCast() // Call to create ConVar. (allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk); return allocatedConvar; // Return allocated ConVar. } void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2) { if (!client) // Client valid? return; if (std::strlen(reason) == NULL) // Is reason null? return; if (!client->GetNetChan()) return; addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan. client->GetNetChan() = nullptr; // Null netchan. MemoryAddress(0x140302FD0).RCast()(client); // Reset CClient instance for client. } } #pragma region KeyValues const char* KeyValues::GetName() { return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); } #pragma endregion