diff --git a/r5dev/include/CCompanion.h b/r5dev/include/CCompanion.h index 5a7ef11c..166ee791 100644 --- a/r5dev/include/CCompanion.h +++ b/r5dev/include/CCompanion.h @@ -26,20 +26,27 @@ public: enum class EHostStatus { NotHosting, - WaitingForStateChange, - Hosting, - ConnectedToSomeoneElse + Hosting } HostingStatus = EHostStatus::NotHosting; + enum class EServerVisibility { + Offline, + Hidden, + Public + } ServerVisibility = EServerVisibility::Offline; + //////////////////// // Server Browser // //////////////////// R5Net::Client* r5net; + R5Net::Client* GetR5Net() { return r5net; } + std::vector<ServerListing> ServerList; ImGuiTextFilter ServerBrowserFilter; char ServerConnStringBuffer[256] = { 0 }; + char ServerEncKeyBuffer[30] = { 0 }; std::string ServerListMessage = std::string(); //////////////////// @@ -56,16 +63,14 @@ public: std::string HostToken = ""; ImVec4 HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); bool StartAsDedi = false; - bool BroadCastServer = false; bool OverridePlaylist = false; //////////////////// // Private Server // //////////////////// - std::string PrivateServerToken = ""; - std::string PrivateServerPassword = ""; - std::string PrivateServerRequestMessage = ""; - ImVec4 PrivateServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + std::string HiddenServerToken = ""; + std::string HiddenServerRequestMessage = ""; + ImVec4 HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); /* Texture */ ID3D11ShaderResourceView* ApexLockIcon = nullptr; @@ -144,14 +149,20 @@ public: void CompMenu(); void ServerBrowserSection(); void SettingsSection(); + void HiddenServersModal(); void HostServerSection(); void Draw(const char* title); void UpdateHostingStatus(); void ProcessCommand(const char* command_line); void ExecCommand(const char* command_line); - void ConnectToServer(const std::string &ip, const std::string &port); - void ConnectToServer(const std::string &connString); + void RegenerateEncryptionKey(); + void ChangeEncryptionKeyTo(const std::string str); + + void ConnectToServer(const std::string ip, const std::string port, const std::string encKey); + void ConnectToServer(const std::string connString, const std::string encKey); }; -extern CCompanion* g_ServerBrowser; \ No newline at end of file +extern CCompanion* g_ServerBrowser; + +extern bool g_CheckCompBanDB; \ No newline at end of file diff --git a/r5dev/include/enums.h b/r5dev/include/enums.h index 6a4d4ff7..f3ff58b5 100644 --- a/r5dev/include/enums.h +++ b/r5dev/include/enums.h @@ -417,4 +417,6 @@ enum FileWarningLevel_t #define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. #define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). -#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. +#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. + +#define MAX_PLAYERS 128 // Max R5 players. diff --git a/r5dev/include/gameclasses.h b/r5dev/include/gameclasses.h index 1197cee3..bd096e01 100644 --- a/r5dev/include/gameclasses.h +++ b/r5dev/include/gameclasses.h @@ -1,5 +1,6 @@ #pragma once #include "patterns.h" +#include "banlist.h" ///////////////////////////////////////////////////////////////////////////// // Classes and Structs @@ -238,8 +239,8 @@ struct QAngle // Implement the proper class of this at some point. class CHostState { public: - __int32 m_iCurrentState; //0x0000 - __int32 m_iNextState; //0x0004 + int m_iCurrentState; //0x0000 + int m_iNextState; //0x0004 Vector3 m_vecLocation; //0x0008 QAngle m_angLocation; //0x0014 char m_levelName[64]; //0x0020 @@ -265,6 +266,98 @@ public: } }; +class CClient +{ +public: + inline CClient* GetClientInstance(int index) + { + return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0)); + } + + void*& GetNetChan() + { + return m_nNetChannel; + } +private: + char pad_0000[16]; //0x0000 +public: + int m_iUserID; //0x0010 +private: + char pad_0014[908]; //0x0014 +public: + void* m_nNetChannel; //0x03A0 +private: + char pad_03A8[8]; //0x03A8 +public: + int m_iSignonstate; //0x03B0 +private: + char pad_03B4[4]; //0x03B4 +public: + std::int64_t m_iOriginID; //0x03B8 +private: + char pad_03C0[303360]; //0x03C0 +}; + +class CCommand +{ +private: + enum + { + COMMAND_MAX_ARGC = 64, + COMMAND_MAX_LENGTH = 512, + }; + +public: + CCommand() = delete; + + inline int MaxCommandLength() + { + return COMMAND_MAX_LENGTH - 1; + } + + inline int64_t ArgC() const + { + return m_nArgc; + } + + inline const char** ArgV() const + { + return m_nArgc ? (const char**)m_ppArgv : NULL; + } + + inline const char* ArgS() const + { + return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; + } + + inline const char* GetCommandString() const + { + return m_nArgc ? m_pArgSBuffer : ""; + } + + inline const char* Arg(int nIndex) const + { + // FIXME: Many command handlers appear to not be particularly careful + // about checking for valid argc range. For now, we're going to + // do the extra check and return an empty string if it's out of range + if (nIndex < 0 || nIndex >= m_nArgc) + return ""; + return m_ppArgv[nIndex]; + } + + inline const char* operator[](int nIndex) const + { + return Arg(nIndex); + } + +private: + std::int64_t m_nArgc; + std::int64_t m_nArgv0Size; + char m_pArgSBuffer[COMMAND_MAX_LENGTH]; + char m_pArgvBuffer[COMMAND_MAX_LENGTH]; + const char* m_ppArgv[COMMAND_MAX_ARGC]; +}; + class ConCommandBase { public: @@ -368,16 +461,27 @@ struct Interface namespace GameGlobals { + // Class Instances extern CHostState* HostState; extern CInputSystem* InputSystem; extern CCVar* Cvar; extern KeyValues** PlaylistKeyValues; extern CKeyValuesSystem* KeyValuesSystem; - extern std::vector<std::string> allPlaylists; - + extern CClient* Client; + extern BanList* BanSystem; + + // Var 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); + void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution); + + // Init void InitGameGlobals(); - void InitConVars(); + void InitAllCommandVariations(); void InitPlaylist(); + + extern std::vector<std::string> allPlaylists; extern bool IsInitialized; + + // Utility + void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2); } \ No newline at end of file diff --git a/r5dev/include/gui_utility.h b/r5dev/include/gui_utility.h index 052314ab..e502bdc3 100644 --- a/r5dev/include/gui_utility.h +++ b/r5dev/include/gui_utility.h @@ -27,12 +27,12 @@ public: std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config nlohmann::json in; - std::ifstream config_file(path, std::ios::in); // Parse config file. + std::ifstream configFile(path, std::ios::in); // Parse config file. - if (config_file.good() && config_file) // Check if it parsed. + if (configFile.good() && configFile) // Check if it parsed. { - config_file >> in; - config_file.close(); + configFile >> in; + configFile.close(); if (!in.is_null()) { @@ -63,11 +63,11 @@ public: out["config"]["CCompanion"]["bind2"] = CCompanionConfig.bind2; std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config - std::ofstream out_file(path, std::ios::out | std::ios::trunc); // Write config file.. + std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. - out_file << out.dump(4); // Dump it into config file.. + outFile << out.dump(4); // Dump it into config file.. - out_file.close(); // Close the file handle. + outFile.close(); // Close the file handle. }; }; diff --git a/r5dev/include/hooks.h b/r5dev/include/hooks.h index bad42cf2..fec365eb 100644 --- a/r5dev/include/hooks.h +++ b/r5dev/include/hooks.h @@ -36,6 +36,13 @@ namespace Hooks extern SQVM_LoadScriptFn originalSQVM_LoadScript; #pragma endregion +#pragma region CServer + void* ConnectClient(void* thisptr, void* packet); + + using ConnectClientFn = void* (*)(void*, void*); + extern ConnectClientFn originalConnectClient; +#pragma endregion + #pragma region CVEngineServer bool IsPersistenceDataAvailable(__int64 thisptr, int client); @@ -47,7 +54,7 @@ namespace Hooks bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); void NET_PrintFunc(const char* fmt, ...); - void NetChanShutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2); + void NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2); using NET_PrintFuncFn = void(*)(const char* fmt, ...); extern NET_PrintFuncFn originalNET_PrintFunc; @@ -58,7 +65,7 @@ namespace Hooks using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); extern NET_SendDatagramFn originalNET_SendDatagram; using NetChan_ShutDown = void(*)(void*, const char*, unsigned __int8, char); - extern NetChan_ShutDown originalNetChanShutDown; + extern NetChan_ShutDown originalNetChan_ShutDown; #pragma endregion #pragma region ConVar @@ -70,9 +77,6 @@ namespace Hooks using ConCommand_IsFlagSetFn = bool(*)(ConCommandBase*, int); extern ConCommand_IsFlagSetFn originalConCommand_IsFlagSet; - - using Map_CallbackFn = void(*)(void*); - extern Map_CallbackFn originalMap_Callback; #pragma endregion #pragma region WinAPI diff --git a/r5dev/include/opcptc.h b/r5dev/include/opcptc.h index 697374a5..fc0715e2 100644 --- a/r5dev/include/opcptc.h +++ b/r5dev/include/opcptc.h @@ -29,6 +29,8 @@ namespace #pragma region NetChannel /*0x14030D000*/ MemoryAddress CServer_Auth = r5_op.PatternSearch("40 55 57 41 55 41 57 48 8D AC 24 ? ? ? ?"); + /*0x1413336F0*/ + MemoryAddress NetChan_EncKey_DefaultAssign = r5_op.PatternSearch("E8 ? ? ? ? 48 8D 1D ? ? ? ? 4C 39 3D ? ? ? ?"); #pragma endregion #pragma region FairFight @@ -50,6 +52,7 @@ namespace PRINT_ADDRESS("dst004", dst004.GetPtr()); PRINT_ADDRESS("Host_NewGame", Host_NewGame.GetPtr()); PRINT_ADDRESS("CServer_Auth", CServer_Auth.GetPtr()); + PRINT_ADDRESS("NetChan_EncKey_DefaultAssign", NetChan_EncKey_DefaultAssign.GetPtr()); PRINT_ADDRESS("FairFight_Init", FairFight_Init.GetPtr()); PRINT_ADDRESS("Squirrel_CompileError", Squirrel_CompileError.GetPtr()); std::cout << "+--------------------------------------------------------+" << std::endl; diff --git a/r5dev/include/patterns.h b/r5dev/include/patterns.h index 4ca2776e..9d6534f0 100644 --- a/r5dev/include/patterns.h +++ b/r5dev/include/patterns.h @@ -50,7 +50,24 @@ namespace FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); /*0x14025F190*/ - FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCall().GetPtr()); + FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); + + /*0x160686DC0*/ + MemoryAddress addr_NetChan_EncKeyPtr = MemoryAddress(0x160686DC0); + char* addr_NetChan_EncKey = addr_NetChan_EncKeyPtr.Offset(4816).RCast<char*>(); + + + /*0x140263E70*/ + FUNC_AT_ADDRESS(addr_NetChan_SetEncKey, void(*)(uintptr_t, const char*), MemoryAddress(0x140263E70).GetPtr()); + +#pragma endregion + +#pragma region CServer + /*0x140310230*/ + FUNC_AT_ADDRESS(addr_CServer_RejectConnection, void(*)(void*, unsigned int, void*, const char*), r5_patterns.StringSearch("#CONNECTION_FAILED_RESERVATION_TIMEOUT").FindPatternSelf("E8", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); + + /*0x14030D000*/ + FUNC_AT_ADDRESS(addr_CServer_ConnectClient, void*(*)(void*, void*), r5_patterns.StringSearch("dedi.connect.fail.total:1|c\n").FindPatternSelf("E8", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); #pragma endregion #pragma region CHLClient @@ -58,6 +75,16 @@ namespace FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void*, int), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); #pragma endregion +#pragma region CClient + /*0x140302FD0*/ + FUNC_AT_ADDRESS(addr_CClient_Clear, void(*)(__int64), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("40", MemoryAddress::Direction::UP).GetPtr()); +#pragma endregion + +#pragma region CClientState + /*0x1418223E4*/ + FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); +#pragma endregion + #pragma region CVEngineServer /*0x140315CF0*/ FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); @@ -80,7 +107,7 @@ namespace FUNC_AT_ADDRESS(addr_LoadPlaylist, bool(*)(const char*), r5_patterns.PatternSearch("E8 ? ? ? ? 80 3D ? ? ? ? ? 74 0C").FollowNearCallSelf().GetPtr()); /*0x1671060C0*/ - FUNC_AT_ADDRESS(addr_MapVPKCache, void*, r5_patterns.StringSearch("PrecacheMTVF").FindPatternSelf("48 8D 1D ? ? ? ? 4C", MemoryAddress::Direction::UP, 900).OffsetSelf(0x3).ResolveRelativeAddress().GetPtr()); + FUNC_AT_ADDRESS(addr_MapVPKCache, void*, r5_patterns.StringSearch("PrecacheMTVF").FindPatternSelf("48 8D 1D ? ? ? ? 4C", MemoryAddress::Direction::UP, 900).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr()); /*0x140278C50*/ FUNC_AT_ADDRESS(addr_mp_gamemode_Callback, bool(*)(const char*), r5_patterns.StringSearch("Failed to load playlist data\n").FindPatternSelf("E8 ? ? ? ? B0 01", MemoryAddress::Direction::DOWN, 200).FollowNearCallSelf().GetPtr()); @@ -108,6 +135,7 @@ namespace PRINT_ADDRESS("NET_PrintFunc", addr_NET_PrintFunc); PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); + PRINT_ADDRESS("CClientState::m_bRestrictServerCommands", addr_m_bRestrictServerCommands); PRINT_ADDRESS("INetChannel::Shutdown", addr_NetChan_Shutdown); PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj index bc2148f4..84db9479 100644 --- a/r5dev/r5dev.vcxproj +++ b/r5dev/r5dev.vcxproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> @@ -148,7 +148,7 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> <ModuleDefinitionFile>r5dev.def</ModuleDefinitionFile> - <AdditionalDependencies>Minhook.x64.lib;r5net.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> <PostBuildEvent> <Command>del "$(SolutionDir)bin\$(Configuration)\r5dev.dll" @@ -189,7 +189,7 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> <ModuleDefinitionFile>r5dev.def</ModuleDefinitionFile> - <AdditionalDependencies>Minhook.x64.lib;r5net.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> <PostBuildEvent> <Command> @@ -310,6 +310,7 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( <ClInclude Include="..\external\spdlog\include\tweakme.h" /> <ClInclude Include="..\external\spdlog\include\version.h" /> <ClInclude Include="..\shared\include\address.h" /> + <ClInclude Include="..\shared\include\banlist.h" /> <ClInclude Include="..\shared\include\httplib.h" /> <ClInclude Include="..\shared\include\json.hpp" /> <ClInclude Include="..\shared\include\utility.h" /> @@ -379,6 +380,7 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( </ClCompile> <ClCompile Include="src\hooks\cbasefilesystem.cpp" /> <ClCompile Include="src\hooks\chlclient.cpp" /> + <ClCompile Include="src\hooks\connectclient.cpp" /> <ClCompile Include="src\hooks\cvengineserver.cpp" /> <ClCompile Include="src\hooks\hooks.cpp" /> <ClCompile Include="src\hooks\iconvar.cpp" /> diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters index fe05a640..aafd9c56 100644 --- a/r5dev/r5dev.vcxproj.filters +++ b/r5dev/r5dev.vcxproj.filters @@ -103,6 +103,9 @@ <Filter Include="hooks\src\cbasefilesystem"> <UniqueIdentifier>{90ee1072-2d57-4d4e-aa1e-f19a81e6b27f}</UniqueIdentifier> </Filter> + <Filter Include="hooks\src\cserver"> + <UniqueIdentifier>{10edfee7-8c10-41de-b8f3-424826d2614a}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="src\dllmain.cpp"> @@ -195,6 +198,9 @@ <ClCompile Include="src\hooks\loadplaylist.cpp"> <Filter>hooks\src\other</Filter> </ClCompile> + <ClCompile Include="src\hooks\connectclient.cpp"> + <Filter>hooks\src\cserver</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\external\imgui\include\imgui_impl_win32.h"> @@ -564,6 +570,9 @@ <Filter>gui\include</Filter> </ClInclude> <ClInclude Include="resource.h" /> + <ClInclude Include="..\shared\include\banlist.h"> + <Filter>shared\include</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <None Include="r5dev.def"> diff --git a/r5dev/src/CCompanion.cpp b/r5dev/src/CCompanion.cpp index 2898a518..0ba5c913 100644 --- a/r5dev/src/CCompanion.cpp +++ b/r5dev/src/CCompanion.cpp @@ -10,6 +10,7 @@ //#define OVERLAY_DEBUG CCompanion* g_ServerBrowser = nullptr; +bool g_CheckCompBanDB = true; std::map<std::string, std::string> mapArray = { @@ -85,12 +86,24 @@ void CCompanion::UpdateHostingStatus() } case EHostStatus::Hosting: { - if (!BroadCastServer) // Do we wanna broadcast server to the browser? + if (ServerVisibility == EServerVisibility::Offline) // Do we wanna broadcast server to the browser? break; if (*reinterpret_cast<std::int32_t*>(0x1656057E0) == NULL) // Check if script checksum is valid yet. break; + switch (ServerVisibility) { + + case EServerVisibility::Hidden: + MyServer.hidden = true; + break; + + case EServerVisibility::Public: + MyServer.hidden = false; + break; + + } + SendHostingPostRequest(); break; } @@ -125,14 +138,17 @@ void CCompanion::SendHostingPostRequest() { HostToken = std::string(); bool result = r5net->PostServerHost(HostRequestMessage,HostToken, - ServerListing{ MyServer.name, - std::string(GameGlobals::HostState->m_levelName), - "", - GameGlobals::Cvar->FindVar("hostport")->m_pzsCurrentValue, - GameGlobals::Cvar->FindVar("mp_gamemode")->m_pzsCurrentValue, - MyServer.password, - std::to_string(*reinterpret_cast<std::int32_t*>(0x1656057E0)), - std::string()} + ServerListing{ + MyServer.name, + std::string(GameGlobals::HostState->m_levelName), + "", + GameGlobals::Cvar->FindVar("hostport")->m_pzsCurrentValue, + GameGlobals::Cvar->FindVar("mp_gamemode")->m_pzsCurrentValue, + MyServer.hidden, + std::to_string(*reinterpret_cast<std::int32_t*>(0x1656057E0)), + std::string(), + addr_NetChan_EncKey + } ); if (result) @@ -222,9 +238,9 @@ void CCompanion::ServerBrowserSection() std::string selectButtonText = "Connect##"; selectButtonText += (server.name + server.ip + server.map); - if (ImGui::Button(selectButtonText.c_str())) + if (ImGui::Button(selectButtonText.c_str())) { - ConnectToServer(server.ip, server.port); + ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); } } @@ -235,28 +251,37 @@ void CCompanion::ServerBrowserSection() ImGui::Separator(); - ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer)); - - ImGui::SameLine(); - - if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() * (1.f / 3.f / 2.f), 19))) + ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4); { - //const char* replace = ""; // For history pos soon - std::stringstream cmd; - cmd << "connect " << ServerConnStringBuffer; - ConnectToServer(ServerConnStringBuffer); - //strcpy_s(ServerConnStringBuffer, sizeof(replace), replace); // For history pos soon + ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer)); + + ImGui::SameLine(); ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter the encryption key", ServerEncKeyBuffer, IM_ARRAYSIZE(ServerEncKeyBuffer)); + + + if (ImGui::SameLine(); ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) + { + ConnectToServer(ServerConnStringBuffer, ServerEncKeyBuffer); + } + + ImGui::SameLine(); + + if (ImGui::Button("Private Servers##ServerBrowser_HiddenServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) + { + ImGui::OpenPopup("Connect to Private Server##HiddenServersConnectModal"); + } + HiddenServersModal(); + // TODO: Still not in a seperate class... + } + ImGui::PopItemWidth(); - ImGui::SameLine(); - - if (ImGui::Button("Private Servers##ServerBrowser_PrivateServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() * (1.f / 3.f / 2.f), 19))) - { - ImGui::OpenPopup("Connect to Private Server##PrivateServersConnectModal"); - } + +} +void CCompanion::HiddenServersModal() +{ bool modalOpen = true; - if (ImGui::BeginPopupModal("Connect to Private Server##PrivateServersConnectModal", &modalOpen)) + if (ImGui::BeginPopupModal("Connect to Private Server##HiddenServersConnectModal", &modalOpen)) { // I *WILL* move this in a separate class @@ -322,7 +347,7 @@ void CCompanion::ServerBrowserSection() } ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. - ImGui::BeginChild("##PrivateServersConnectModal_IconParent", ImVec2(ApexLockIconWidth, ApexLockIconHeight)); + ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(ApexLockIconWidth, ApexLockIconHeight)); { ImGui::Image(ApexLockIcon, ImVec2(ApexLockIconWidth, ApexLockIconHeight)); // Display texture. } @@ -334,38 +359,37 @@ void CCompanion::ServerBrowserSection() ImGui::Text("Enter the following details to continue"); ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width. - ImGui::InputTextWithHint("##PrivateServersConnectModal_TokenInput", "Token", &PrivateServerToken); - ImGui::InputTextWithHint("##PrivateServersConnectModal_PasswordInput", "Password", &PrivateServerPassword, ImGuiInputTextFlags_Password); + ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token", &HiddenServerToken); ImGui::PopItemWidth(); // Pop item width. ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element. - ImGui::TextColored(PrivateServerMessageColor, PrivateServerRequestMessage.c_str()); + ImGui::TextColored(HiddenServerMessageColor, HiddenServerRequestMessage.c_str()); ImGui::Separator(); - if (ImGui::Button("Connect##PrivateServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2.f, 19))) + if (ImGui::Button("Connect##HiddenServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) { - PrivateServerRequestMessage = ""; + HiddenServerRequestMessage = ""; ServerListing server; - bool result = r5net->GetServerByToken(server, PrivateServerRequestMessage, PrivateServerToken, PrivateServerPassword); // Send token connect request. + bool result = r5net->GetServerByToken(server, HiddenServerRequestMessage, HiddenServerToken); // Send token connect request. if (!server.name.empty()) { - ConnectToServer(server.ip, server.port); // Connect to the server - PrivateServerRequestMessage = "Found Server: " + server.name; - PrivateServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); // Connect to the server + HiddenServerRequestMessage = "Found Server: " + server.name; + HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); ImGui::CloseCurrentPopup(); } else { - PrivateServerRequestMessage = "Error: " + PrivateServerRequestMessage; - PrivateServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + HiddenServerRequestMessage = "Error: " + HiddenServerRequestMessage; + HiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } ImGui::SameLine(); - if (ImGui::Button("Close##PrivateServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2.f, 19))) + if (ImGui::Button("Close##HiddenServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) { ImGui::CloseCurrentPopup(); } @@ -380,8 +404,20 @@ void CCompanion::HostServerSection() static std::string ServerMap = std::string(); ImGui::InputTextWithHint("##ServerHost_ServerName", "Server Name (Required)", &MyServer.name); - ImGui::InputTextWithHint("##ServerHost_ServerPassword", "Password (Optional)", &MyServer.password); ImGui::Spacing(); + + if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", MyServer.playlist.c_str())) + { + for (auto& item : GameGlobals::allPlaylists) + { + if (ImGui::Selectable(item.c_str(), item == MyServer.playlist)) + { + MyServer.playlist = item; + } + } + ImGui::EndCombo(); + } + if (ImGui::BeginCombo("Map##ServerHost_MapListBox", MyServer.map.c_str())) { for (auto& item : MapsList) @@ -399,25 +435,28 @@ void CCompanion::HostServerSection() } ImGui::EndCombo(); } - if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", MyServer.playlist.c_str())) - { - for (auto& item : GameGlobals::allPlaylists) - { - if (ImGui::Selectable(item.c_str(), item == MyServer.playlist)) - { - MyServer.playlist = item; - } - } - ImGui::EndCombo(); - } + + // ImGui::Checkbox("Start as Dedicated Server (HACK)##ServerHost_DediCheckbox", &StartAsDedi); + ImGui::Checkbox("Load Global Ban List##ServerHost_CheckCompBanDBCheckbox", &g_CheckCompBanDB); + ImGui::Spacing(); - - ImGui::Checkbox("Start as Dedicated Server (HACK)##ServerHost_DediCheckbox", &StartAsDedi); - ImGui::SameLine(); + ImGui::Text("Server Visiblity"); + + if (ImGui::SameLine(); ImGui::RadioButton("Offline##ServerHost_ServerChoice1", ServerVisibility == EServerVisibility::Offline)) + { + ServerVisibility = EServerVisibility::Offline; + } + if (ImGui::SameLine(); ImGui::RadioButton("Hidden##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Hidden)) + { + ServerVisibility = EServerVisibility::Hidden; + } + if (ImGui::SameLine(); ImGui::RadioButton("Public##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Public)) + { + ServerVisibility = EServerVisibility::Public; + } - ImGui::Checkbox("Broadcast Server to Server Browser", &BroadCastServer); - + ImGui::Spacing(); ImGui::Separator(); if (!GameGlobals::HostState->m_bActiveGame) @@ -501,12 +540,23 @@ void CCompanion::HostServerSection() void CCompanion::SettingsSection() { - ImGui::InputTextWithHint("##MatchmakingServerString", "Matchmaking Server String", & MatchmakingServerStringBuffer); + // Matchmaking Server String + ImGui::InputTextWithHint("##MatchmakingServerString", "Matchmaking Server String", &MatchmakingServerStringBuffer); if (ImGui::Button("Update Settings")) { - if (r5net) delete r5net; + if (r5net) + { + delete r5net; + } + r5net = new R5Net::Client(MatchmakingServerStringBuffer); } + // Encryption Key + if (ImGui::Button("Regenerate Encryption Key##SettingsSection_RegenEncKey")) + { + RegenerateEncryptionKey(); + } + ImGui::InputText("Encryption Key##SettingsSection_EncKey", addr_NetChan_EncKey, ImGuiInputTextFlags_ReadOnly); } void CCompanion::Draw(const char* title) @@ -559,20 +609,59 @@ void CCompanion::ExecCommand(const char* command_line) addr_CommandExecute(NULL, command_line); } -void CCompanion::ConnectToServer(const std::string& ip, const std::string& port) +void CCompanion::ConnectToServer(const std::string ip, const std::string port, const std::string encKey) { + if (!encKey.empty()) + { + ChangeEncryptionKeyTo(encKey); + } + std::stringstream cmd; cmd << "connect " << ip << ":" << port; ProcessCommand(cmd.str().c_str()); } -void CCompanion::ConnectToServer(const std::string& connString) +void CCompanion::ConnectToServer(const std::string connString, const std::string encKey) { + if (!encKey.empty()) + { + ChangeEncryptionKeyTo(encKey); + } + std::stringstream cmd; cmd << "connect " << connString; ProcessCommand(cmd.str().c_str()); } +void CCompanion::RegenerateEncryptionKey() +{ + BCRYPT_ALG_HANDLE hAlgorithm; + if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0) + { + std::cerr << "Failed to open rng algorithm\n"; + } + unsigned char pBuffer[0x10u]; + if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0) + { + std::cerr << "Failed to generate random data\n"; + } + std::string fin; + + for (int i = 0; i < 0x10u; i++) + { + fin += pBuffer[i]; + } + + fin = base64_encode(fin); + + addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), fin.c_str()); +} + +void CCompanion::ChangeEncryptionKeyTo(const std::string str) +{ + addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), str.c_str()); +} + //############################################################################# // ENTRYPOINT //############################################################################# @@ -584,5 +673,6 @@ void DrawBrowser() g_ServerBrowser = &browser; return true; } (); + browser.Draw("Companion"); } diff --git a/r5dev/src/gameclasses.cpp b/r5dev/src/gameclasses.cpp index c62c0c29..3d0b7919 100644 --- a/r5dev/src/gameclasses.cpp +++ b/r5dev/src/gameclasses.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "gameclasses.h" #include "id3dx.h" +#include "cgameconsole.h" // Need this for a re-factor later. // Interface* interfaces = *reinterpret_cast<Interface**>(0x167F4FA48); @@ -16,60 +17,308 @@ namespace GameGlobals CHostState* HostState = nullptr; CInputSystem* InputSystem = nullptr; CCVar* Cvar = nullptr; + CClient* Client = nullptr; + BanList* BanSystem = new BanList(); CKeyValuesSystem* KeyValuesSystem = nullptr; KeyValues** PlaylistKeyValues = nullptr; std::vector<std::string> allPlaylists = { "none" }; - namespace CustomConVar + namespace CustomCommandVariations { - void CGameConsole_Callback(void* cmd) + void CGameConsole_Callback(const CCommand& cmd) { - std::string szValue = *(const char**)((std::uintptr_t)cmd + 0x18); - try + g_bShowConsole = !g_bShowConsole; + } + + void CCompanion_Callback(const CCommand& cmd) + { + g_bShowBrowser = !g_bShowBrowser; + } + + 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. { - int value = std::stoi(szValue); - switch (value) - { - case 0: - g_bShowConsole = false; - break; - case 1: - g_bShowConsole = true; - break; - default: - break; - } - } - catch (std::exception& e) - { - std::cout << " [+CGameConsole+] Please don't input a character that isn't a number into cgameconsole :(. Error: " << e.what() << std::endl; + 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 CCompanion_Callback(void* cmd) + void KickID_Callback(CCommand* cmd) { - std::string szValue = *(const char**)((std::uintptr_t)cmd + 0x18); + 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 { - int value = std::stoi(szValue); - switch (value) + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. { - case 0: - g_bShowBrowser = false; - break; - case 1: - g_bShowBrowser = true; - break; - default: - break; + 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::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + finalIPAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast<std::int64_t>(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<std::int64_t>(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 << " [+CCompanion+] Please don't input a character that isn't a number into ccompanion :(. Error: " << e.what() << std::endl; + std::cout << "Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: " << e.what() << std::endl; + g_GameConsole->AddLog("Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: %s", e.what()); + 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; + g_GameConsole->AddLog("Unban Error: %s", e.what()); + 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<int>() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + 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::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + finalIPAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast<std::int64_t>(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<std::int64_t>(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; + g_GameConsole->AddLog("Banid Error: %s", e.what()); + return; + } } } @@ -110,9 +359,13 @@ namespace GameGlobals Cvar = *reinterpret_cast<CCVar**>(0x14D40B348); // Get CCVar from memory. KeyValuesSystem = reinterpret_cast<CKeyValuesSystem*>(0x141F105C0); // Get CKeyValuesSystem from memory. PlaylistKeyValues = reinterpret_cast<KeyValues**>(0x16705B980); // Get the KeyValue for the playlist file. + Client = reinterpret_cast<CClient*>(0x16073B200); NullHostNames(); // Null all hostnames. - InitConVars(); // Initialize our custom ConVars. + 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. @@ -143,10 +396,49 @@ namespace GameGlobals } } - void InitConVars() + void InitAllCommandVariations() { - ConVar* CGameConsoleConVar = CreateCustomConVar("cgameconsole", "0", 0, "Opens the R5 Reloaded Console", false, 0.f, false, 0.f, CustomConVar::CGameConsole_Callback, nullptr); - ConVar* CCompanionConVar = CreateCustomConVar("ccompanion", "0", 0, "Opens the R5 Reloaded Server Browser", false, 0.f, false, 0.f, CustomConVar::CCompanion_Callback, nullptr); + void* CGameConsoleConCommand = CreateCustomConCommand("cgameconsole", "Opens the R5 Reloaded Console.", 0, CustomCommandVariations::CGameConsole_Callback, nullptr); + void* CCompanionConCommand = CreateCustomConCommand("ccompanion", "Opens the R5 Reloaded Server Browser.", 0, CustomCommandVariations::CCompanion_Callback, nullptr); + 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<void*>(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<std::uintptr_t>(command); // To ptr. + + *(void**)commandPtr = ConCommandVtable.RCast<void*>(); // 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<void*>(); // 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<void*>(); // 0x58 nullsub. + } + + RegisterConCommand.RCast<void(*)(void*)>()((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) @@ -160,20 +452,34 @@ namespace GameGlobals std::uintptr_t cvarPtr = reinterpret_cast<std::uintptr_t>(allocatedConvar); // To ptr. *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast<void*>(); // 0x40 to ICvar table. - *(void**)cvarPtr = ConVarVtable.RCast<void*>(); // 0x0 to ConVar table. + *(void**)cvarPtr = ConVarVtable.RCast<void*>(); // 0x0 to ConVar vtable. CreateConVar.RCast<void(*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*)>() // 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<void(*)(CClient*)>()(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 \ No newline at end of file diff --git a/r5dev/src/hooks/chlclient.cpp b/r5dev/src/hooks/chlclient.cpp index 32b67fb9..63a83f4b 100644 --- a/r5dev/src/hooks/chlclient.cpp +++ b/r5dev/src/hooks/chlclient.cpp @@ -17,6 +17,34 @@ void __fastcall Hooks::FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curSt break; } + case FRAME_NET_UPDATE_POSTDATAUPDATE_END: + { + if (GameGlobals::BanSystem->IsRefuseListValid()) + { + for (int i = 0; i < GameGlobals::BanSystem->refuseList.size(); i++) // Loop through vector. + { + for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. + { + CClient* client = GameGlobals::Client->GetClientInstance(c); // Get client instance. + if (!client) + continue; + + if (!client->GetNetChan()) // Netchan valid? + continue; + + int clientID = client->m_iUserID + 1; // Get UserID + 1. + if (clientID != GameGlobals::BanSystem->refuseList[i].second) // See if they match. + continue; + + GameGlobals::DisconnectClient(client, GameGlobals::BanSystem->refuseList[i].first.c_str(), 0, 1); + GameGlobals::BanSystem->DeleteConnectionRefuse(clientID); + break; + } + } + } + PatchNetVarConVar(); + break; + } default: break; } diff --git a/r5dev/src/hooks/connectclient.cpp b/r5dev/src/hooks/connectclient.cpp new file mode 100644 index 00000000..ef6f5902 --- /dev/null +++ b/r5dev/src/hooks/connectclient.cpp @@ -0,0 +1,91 @@ +#include "pch.h" +#include "hooks.h" + +namespace Hooks +{ + ConnectClientFn originalConnectClient = nullptr;; +} + +void IsClientBanned(R5Net::Client* r5net, const std::string ip, std::int64_t orid) +{ + std::string err = std::string(); + bool compBanned = r5net && r5net->GetClientIsBanned(ip, orid, err); + if (compBanned) + { + while (compBanned) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + 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) // Client instance valid? + continue; + + if (!client->GetNetChan()) // Netchan valid? + continue; + + std::int64_t originID = client->m_iOriginID; // Get originID. + if (originID != orid) // See if they match. + continue; + + GameGlobals::BanSystem->AddConnectionRefuse(err, client->m_iUserID + 1); // Add to the vector. + compBanned = false; + break; + } + } + } +} + +void* Hooks::ConnectClient(void* thisptr, void* packet) +{ + if (!GameGlobals::BanSystem) + return originalConnectClient(thisptr, packet); + + static int skipConnects = 0; + if (skipConnects != 2) // Skip first two connect attempts. + { + skipConnects++; + return originalConnectClient(thisptr, packet); + } + + std::string finalIPAddress = "null"; + MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)packet + 0x10)); + if (ipAddressField && ipAddressField.GetValue<int>() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>()); + + finalIPAddress = ss.str(); + } + + R5Net::Client* r5net = g_ServerBrowser->GetR5Net(); + + const char* name = *(const char**)((std::uintptr_t)packet + 0x30); + std::int64_t originID = *(std::int64_t*)((std::uintptr_t)packet + 0x28); + + g_GameConsole->AddLog("[CServer::ConnectClient] %s is trying to connect. OriginID: %lld", name, originID); + + if (GameGlobals::BanSystem->IsBanListValid()) // Is the banlist vector valid? + { + if (GameGlobals::BanSystem->IsBanned(finalIPAddress, originID)) // Is the client trying to connect banned? + { + addr_CServer_RejectConnection(thisptr, *(unsigned int*)((std::uintptr_t)thisptr + 0xC), packet, "You have been banned from this Server."); // RejectConnection for the client. + g_GameConsole->AddLog("[CServer::ConnectClient] %s is banned. OriginID: %lld", name, originID); + return nullptr; + } + } + + if (g_CheckCompBanDB) + { + if (r5net) + { + std::thread t1(IsClientBanned, r5net, finalIPAddress, originID); + t1.detach(); + } + } + + return originalConnectClient(thisptr, packet); +} \ No newline at end of file diff --git a/r5dev/src/hooks/hooks.cpp b/r5dev/src/hooks/hooks.cpp index aa0c2102..6d8e9a59 100644 --- a/r5dev/src/hooks/hooks.cpp +++ b/r5dev/src/hooks/hooks.cpp @@ -26,13 +26,14 @@ void Hooks::InstallHooks() // Hook Game Functions MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast<void**>(&originalFrameStageNotify)); MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast<void**>(&originalIsPersistenceDataAvailable)); + MH_CreateHook(addr_CServer_ConnectClient, &Hooks::ConnectClient, reinterpret_cast<void**>(&originalConnectClient)); /////////////////////////////////////////////////////////////////////////////// // Hook Netchan functions MH_CreateHook(addr_NET_PrintFunc, &Hooks::NET_PrintFunc, reinterpret_cast<void**>(&originalNET_PrintFunc)); MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast<void**>(&originalNET_ReceiveDatagram)); MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast<void**>(&originalNET_SendDatagram)); - MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChanShutdown, reinterpret_cast<void**>(&originalNetChanShutDown)); + MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChan_Shutdown, reinterpret_cast<void**>(&originalNetChan_ShutDown)); /////////////////////////////////////////////////////////////////////////////// // Hook ConVar | ConCommand functions. @@ -81,6 +82,7 @@ void Hooks::InstallHooks() // Enable Game hooks MH_EnableHook(addr_CHLClient_FrameStageNotify); MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); + MH_EnableHook(addr_CServer_ConnectClient); /////////////////////////////////////////////////////////////////////////////// // Enable Netchan hooks @@ -114,6 +116,7 @@ void Hooks::RemoveHooks() // Unhook Game Functions MH_RemoveHook(addr_CHLClient_FrameStageNotify); MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); + MH_RemoveHook(addr_CServer_ConnectClient); /////////////////////////////////////////////////////////////////////////////// // Unhook Netchan functions diff --git a/r5dev/src/hooks/iconvar.cpp b/r5dev/src/hooks/iconvar.cpp index 780a9d72..ef384a0f 100644 --- a/r5dev/src/hooks/iconvar.cpp +++ b/r5dev/src/hooks/iconvar.cpp @@ -9,7 +9,6 @@ namespace Hooks { ConVar_IsFlagSetFn originalConVar_IsFlagSet = nullptr; ConCommand_IsFlagSetFn originalConCommand_IsFlagSet = nullptr; - Map_CallbackFn originalMap_Callback = nullptr; } bool Hooks::ConVar_IsFlagSet(ConVar* cvar, int flag) diff --git a/r5dev/src/hooks/netchannel.cpp b/r5dev/src/hooks/netchannel.cpp index 3b09e286..6ad9ffa3 100644 --- a/r5dev/src/hooks/netchannel.cpp +++ b/r5dev/src/hooks/netchannel.cpp @@ -3,11 +3,11 @@ namespace Hooks { - NetChan_ShutDown originalNetChanShutDown = nullptr; + NetChan_ShutDown originalNetChan_ShutDown = nullptr; } -void Hooks::NetChanShutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2) +void Hooks::NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2) { addr_downloadPlaylists_Callback(); // Re-Load playlist from disk after getting dropped or disconnecting off a server. - originalNetChanShutDown(rcx, reason, unk1, unk2); + originalNetChan_ShutDown(rcx, reason, unk1, unk2); } \ No newline at end of file diff --git a/r5dev/src/opcptc.cpp b/r5dev/src/opcptc.cpp index 40f944aa..a6fbbbf4 100644 --- a/r5dev/src/opcptc.cpp +++ b/r5dev/src/opcptc.cpp @@ -29,4 +29,7 @@ void InstallOpcodes() /* .TEXT */ //------------------------------------------------------------------------- // CALL --> NOP | Prevent squirrel compiler errors from calling Error Squirrel_CompileError.Offset(0x12C).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); + //------------------------------------------------------------------------- + // CALL --> NOP | Prevent random netchan encryption key from being overriden by default key + NetChan_EncKey_DefaultAssign.Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); } diff --git a/r5net/include/r5/r5net.h b/r5net/include/r5/r5net.h index 1293c5f1..c3005b65 100644 --- a/r5net/include/r5/r5net.h +++ b/r5net/include/r5/r5net.h @@ -6,13 +6,16 @@ namespace R5Net { struct Config { - std::string MOTD; - int SERVER_TTL; - int MIN_REQUIRED_VERSION; + std::string R_MOTD; + int R_SERVER_TTL; + int R_MIN_REQUIRED_VERSION; + bool loadBanList; }; + class Client { httplib::Client m_HttpClient; + Config config; public: Client(std::string serverString) : m_HttpClient(serverString.c_str()) @@ -22,7 +25,10 @@ namespace R5Net std::vector<ServerListing> GetServersList(std::string& outMessage); bool PostServerHost(std::string& outMessage, std::string& outToken, const ServerListing& serverListing); - bool GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token, const std::string password = ""); + bool GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token); + bool GetClientIsBanned(std::string ip, std::int64_t orid, std::string& outErrCl); + std::string GetVersionString(); + }; } \ No newline at end of file diff --git a/r5net/include/r5/serverlisting.h b/r5net/include/r5/serverlisting.h index 0b317eb6..1e895fbd 100644 --- a/r5net/include/r5/serverlisting.h +++ b/r5net/include/r5/serverlisting.h @@ -7,8 +7,9 @@ struct ServerListing std::string ip; std::string port; std::string playlist; - std::string password; - std::string checksum; + bool hidden; + std::string remoteChecksum; std::string version; + std::string netchanEncryptionKey; }; diff --git a/r5net/r5net.vcxproj b/r5net/r5net.vcxproj index 577d5ed0..cc893185 100644 --- a/r5net/r5net.vcxproj +++ b/r5net/r5net.vcxproj @@ -21,7 +21,7 @@ <ItemGroup> <ClInclude Include="include\netpch.h" /> <ClInclude Include="include\r5\r5net.h" /> - <ClInclude Include="include\serverlisting.h" /> + <ClInclude Include="include\r5\serverlisting.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="src\netpch.cpp"> diff --git a/r5net/r5net.vcxproj.filters b/r5net/r5net.vcxproj.filters index f50c168f..91f23065 100644 --- a/r5net/r5net.vcxproj.filters +++ b/r5net/r5net.vcxproj.filters @@ -9,15 +9,15 @@ </Filter> </ItemGroup> <ItemGroup> - <ClInclude Include="include\serverlisting.h"> - <Filter>include</Filter> - </ClInclude> <ClInclude Include="include\netpch.h"> <Filter>include</Filter> </ClInclude> <ClInclude Include="include\r5\r5net.h"> <Filter>include</Filter> </ClInclude> + <ClInclude Include="include\r5\serverlisting.h"> + <Filter>include</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="src\r5net.cpp"> diff --git a/r5net/src/r5net.cpp b/r5net/src/r5net.cpp index 1ef0e78e..3c007778 100644 --- a/r5net/src/r5net.cpp +++ b/r5net/src/r5net.cpp @@ -6,7 +6,7 @@ std::string R5Net::Client::GetVersionString() { - return "beta 1.5"; + return "beta 1.6"; } std::vector<ServerListing> R5Net::Client::GetServersList(std::string& outMessage) @@ -36,7 +36,17 @@ std::vector<ServerListing> R5Net::Client::GetServersList(std::string& outMessage for (auto obj : resBody["servers"]) { list.push_back( - ServerListing{ obj["name"].get<std::string>(), obj["map"].get<std::string>(), obj["ip"].get<std::string>(), obj["port"].get<std::string>(), obj["gamemode"].get<std::string>() } + ServerListing{ + obj.value("name",""), + obj.value("map", ""), + obj.value("ip", ""), + obj.value("port", ""), + obj.value("gamemode", ""), + obj.value("hidden", "false") == "true", + obj.value("remote_checksum", ""), + obj.value("version", GetVersionString()), + obj.value("encKey", "") + } ); } } @@ -81,15 +91,16 @@ bool R5Net::Client::PostServerHost(std::string& outMessage, std::string& outToke reqBody["name"] = serverListing.name; reqBody["map"] = serverListing.map; reqBody["port"] = serverListing.port; - reqBody["password"] = serverListing.password; - reqBody["remote_checksum"] = serverListing.checksum; + reqBody["remote_checksum"] = serverListing.remoteChecksum; reqBody["version"] = GetVersionString(); reqBody["gamemode"] = serverListing.playlist; + reqBody["encKey"] = serverListing.netchanEncryptionKey; + reqBody["hidden"] = serverListing.hidden; std::string reqBodyStr = reqBody.dump(); -#ifdef DebugR5Net - std::cout << " [+R5Net+] Sending PostServerHost post now..\n"; + #ifdef DebugR5Net + std::cout << " [+R5Net+] Sending PostServerHost post now..\n" << reqBodyStr << "\n"; #endif httplib::Result res = m_HttpClient.Post("/servers/add", reqBodyStr.c_str(), reqBodyStr.length(), "application/json"); @@ -150,12 +161,11 @@ bool R5Net::Client::PostServerHost(std::string& outMessage, std::string& outToke return false; } -bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token, const std::string password) +bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token) { nlohmann::json reqBody = nlohmann::json::object(); reqBody["token"] = token; - reqBody["password"] = password; #ifdef DebugR5Net std::cout << " [+R5Net+] Sending GetServerByToken post now...\n"; @@ -176,10 +186,15 @@ bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outM if (res && resBody["success"].is_boolean() && resBody["success"]) { outServer = ServerListing{ - resBody["server"]["name"].get<std::string>(), - resBody["server"]["map"].get<std::string>(), - resBody["server"]["ip"].get<std::string>(), - resBody["server"]["port"].get<std::string>() + resBody["server"].value("name",""), + resBody["server"].value("map", ""), + resBody["server"].value("ip", ""), + resBody["server"].value("port", ""), + resBody["server"].value("gamemode", ""), + resBody["server"].value("hidden", "false") == "true", + resBody["server"].value("remote_checksum", ""), + resBody["server"].value("version", GetVersionString()), + resBody["server"].value("encKey", "") }; return true; } @@ -223,3 +238,27 @@ bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outM return false; } +bool R5Net::Client::GetClientIsBanned(const std::string ip, std::int64_t orid, std::string& outErrCl) +{ + nlohmann::json reqBody = nlohmann::json::object(); + reqBody["ip"] = ip; + reqBody["orid"] = orid; + + httplib::Result res = m_HttpClient.Post("/banlist/isBanned", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); + + if (res && res->status == 200) + { + nlohmann::json resBody = nlohmann::json::parse(res->body); + + if (resBody["success"].is_boolean() && resBody["success"].get<bool>()) + { + if (resBody["isBanned"].is_boolean() && resBody["isBanned"].get<bool>()) + { + outErrCl = resBody.value("errCl", "Generic error (code:gen). Contact R5Reloaded developers."); + return true; + } + } + } + return false; +} + diff --git a/shared/include/banlist.h b/shared/include/banlist.h new file mode 100644 index 00000000..71889965 --- /dev/null +++ b/shared/include/banlist.h @@ -0,0 +1,154 @@ +#pragma once + +class BanList +{ +public: + BanList() + { + Load(); + } + + void operator[](std::pair<std::string, std::int64_t> pair) + { + AddEntry(pair.first, pair.second); + } + + void Load() + { + std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config + + nlohmann::json in; + std::ifstream banFile(path, std::ios::in); // Parse ban list. + + int totalBans = 0; + + if (banFile.good() && banFile) // Check if it parsed. + { + banFile >> in; // into json. + banFile.close(); // Close file. + + if (!in.is_null()) // Check if json is valid + { + if (!in["totalBans"].is_null()) // Is the totalBans field populated? + { + totalBans = in["totalBans"].get<int>(); // Get the totalBans field. + } + } + + for (int i = 0; i < totalBans; i++) // Loop through total bans. + { + nlohmann::json entry = in[std::to_string(i).c_str()]; // Get Entry for current ban. + if (entry.is_null()) // Check if entry is valid. + continue; + + std::int64_t originID = entry["originID"].get<std::int64_t>(); // Get originID field from entry. + std::string ipAddress = entry["ipAddress"].get<std::string>(); // Get ipAddress field from entry. + + banList.push_back(std::make_pair(ipAddress, originID)); // Push back into vector. + } + } + } + + void Save() + { + nlohmann::json out; + + for (int i = 0; i < banList.size(); i++) + { + out["totalBans"] = banList.size(); // Populate totalBans field. + out[std::to_string(i).c_str()]["ipAddress"] = banList[i].first; // Populate ipAddress field for this entry. + out[std::to_string(i).c_str()]["originID"] = banList[i].second; // Populate originID field for this entry. + } + + std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config + std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. + + outFile << out.dump(4); // Dump it into config file.. + outFile.close(); // Close the file handle. + } + + void AddEntry(std::string ipAddress, std::int64_t originID) + { + if (!ipAddress.empty() && originID > 0) // Check if args are valid. + { + banList.push_back(std::make_pair(ipAddress, originID)); // Push it back into the vector. + } + } + + void DeleteEntry(std::string ipAddress, std::int64_t originID) + { + for (int i = 0; i < banList.size(); i++) // Loop through vector. + { + if (ipAddress.compare(banList[i].first) == NULL || originID == banList[i].second) // Do any entries match our vector? + { + banList.erase(banList.begin() + i); // If so erase that vector element. + } + } + } + + void AddConnectionRefuse(std::string error, int userID) + { + if (refuseList.empty()) + { + refuseList.push_back(std::make_pair(error, userID)); + } + else + { + for (int i = 0; i < refuseList.size(); i++) // Loop through vector. + { + if (refuseList[i].second != userID) // Do any entries match our vector? + { + refuseList.push_back(std::make_pair(error, userID)); // Push it back into the vector. + } + } + } + } + + void DeleteConnectionRefuse(int userID) + { + for (int i = 0; i < refuseList.size(); i++) // Loop through vector. + { + if (refuseList[i].second == userID) // Do any entries match our vector? + { + refuseList.erase(refuseList.begin() + i); // If so erase that vector element. + } + } + } + + bool IsBanned(std::string ipAddress, std::int64_t originID) + { + for (int i = 0; i < banList.size(); i++) + { + std::string ip = banList[i].first; // Get first pair entry. + std::int64_t origin = banList[i].second; // Get second pair entry. + + if (ip.empty()) // Check if ip is empty. + continue; + + if (origin <= 0) // Is originID below 0? + continue; + + if (ip.compare(ipAddress) == NULL) // Do they match? + return true; + + if (originID == origin) // Do they match? + return true; + } + + return false; + } + + bool IsRefuseListValid() + { + return !refuseList.empty(); + } + + bool IsBanListValid() + { + return !banList.empty(); + } + + std::vector<std::pair<std::string, int>> refuseList = {};; +private: + std::vector<std::pair<std::string, std::int64_t>> banList = {}; +}; diff --git a/shared/include/utility.h b/shared/include/utility.h index c2075066..2f4704db 100644 --- a/shared/include/utility.h +++ b/shared/include/utility.h @@ -9,10 +9,14 @@ MODULEINFO GetModuleInfo(const char* szModule); // Utility void DbgPrint(LPCSTR sFormat, ...); void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize); +void PatchNetVarConVar(); ///////////////////////////////////////////////////////////////////////////// // Loggers inline auto g_spddefault_logger = spdlog::basic_logger_mt("default_logger", "platform\\log\\default_r5.log"); inline auto g_spdnetchan_logger = spdlog::basic_logger_mt("netchan_logger", "platform\\log\\netchan_r5.log"); -///////////////////////////////////////////////////////////////////////////// \ No newline at end of file +///////////////////////////////////////////////////////////////////////////// + +std::string base64_encode(const std::string& in); +std::string base64_decode(const std::string& in); \ No newline at end of file diff --git a/shared/utility.cpp b/shared/utility.cpp index 58e32408..3d6e1f30 100644 --- a/shared/utility.cpp +++ b/shared/utility.cpp @@ -47,6 +47,35 @@ void DbgPrint(LPCSTR sFormat, ...) OutputDebugString(sBuffer); } +void PatchNetVarConVar() +{ + CHAR convarPtr[] = "\x72\x3a\x73\x76\x72\x75\x73\x7a\x7a\x03\x04"; + PCHAR curr = convarPtr; + while (*curr) { + *curr ^= 'B'; + ++curr; + } + + std::int64_t cvaraddr = 0; + std::stringstream ss; + ss << std::hex << std::string(convarPtr); + ss >> cvaraddr; + void* cvarptr = reinterpret_cast<void*>(cvaraddr); + + if (*reinterpret_cast<std::uint8_t*>(cvarptr) == 144) + { + std::uint8_t padding[] = + { + 0x48, 0x8B, 0x45, 0x58, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + void* Callback = nullptr; + VirtualAlloc(Callback, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + memcpy(Callback, (void*)padding, 9); + reinterpret_cast<void(*)()>(Callback)(); + } +} + /////////////////////////////////////////////////////////////////////////////// // For dumping data from a buffer to a file on the disk void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize) @@ -122,4 +151,43 @@ void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize) } k = 0; /////////////////////////////////////////////////////////////////////////// +} + +///// BASE 64 +std::string base64_encode(const std::string& in) { + + std::string out; + + int val = 0, valb = -6; + for (unsigned char c : in) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); + while (out.size() % 4) out.push_back('='); + return out; +} + +std::string base64_decode(const std::string& in) { + + std::string out; + + std::vector<int> T(256, -1); + for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; + + int val = 0, valb = -8; + for (unsigned char c : in) { + if (T[c] == -1) break; + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return out; } \ No newline at end of file