From 484c827a3b374c124c5ec2a7cf5e382205190b53 Mon Sep 17 00:00:00 2001 From: shelru Date: Sun, 30 Apr 2017 23:55:29 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?= =?UTF-8?q?=D0=B0=D1=8F=20=D1=80=D0=B5=D0=BA=D0=BE=D0=BD=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BA=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MiniBase/MiniBase.vcxproj | 6 +- MiniBase/client.cpp | 177 ++++++++++++++--- MiniBase/client.h | 20 +- MiniBase/enginemsg.cpp | 402 +++++++++++++++++++++++++++++--------- MiniBase/enginemsg.h | 11 ++ MiniBase/main.cpp | 168 +++++++++++++++- MiniBase/main.h | 4 +- MiniBase/redirect.h | 80 ++++++++ 8 files changed, 741 insertions(+), 127 deletions(-) create mode 100644 MiniBase/redirect.h diff --git a/MiniBase/MiniBase.vcxproj b/MiniBase/MiniBase.vcxproj index e8fd6d9..bb62ef1 100644 --- a/MiniBase/MiniBase.vcxproj +++ b/MiniBase/MiniBase.vcxproj @@ -30,7 +30,7 @@ DynamicLibrary true v140_xp - Unicode + MultiByte DynamicLibrary @@ -70,6 +70,7 @@ true + .mix true @@ -190,6 +191,5 @@ - - + \ No newline at end of file diff --git a/MiniBase/client.cpp b/MiniBase/client.cpp index 0368c62..131c41c 100644 --- a/MiniBase/client.cpp +++ b/MiniBase/client.cpp @@ -18,9 +18,11 @@ cvar_t *random; map g_modelsHashMap; cvar_t *logsfiles; cvar_t *events_block; - cvar_t *ex_thud; cvar_t *motd_block; + +vector Cvars; + void HookEngineMessages(){ pEngineMsgBase = (PEngineMsg)offset.FindSVCMessages(); pSVC_StuffText = HookEngineMsg("svc_stufftext", SVC_StuffText); @@ -31,8 +33,14 @@ void HookEngineMessages(){ // pSVC_Resourcelist = HookEngineMsg("svc_resourcelist", SVC_Resourcelist); } -void ConsolePrintColor(BYTE R, BYTE G, BYTE B, char* string){ - TColor24 DefaultColor;PColor24 Ptr;Ptr = Console_TextColor;DefaultColor = *Ptr;Ptr->R = R;Ptr->G = G;Ptr->B = B;g_Engine.Con_Printf("%s", string);*Ptr = DefaultColor; + +void ConsolePrintColor(BYTE R, BYTE G, BYTE B, const char *fmt, ...){ + va_list va_alist; + char buf[256]; + va_start(va_alist, fmt); + _vsnprintf(buf, sizeof(buf), fmt, va_alist); + va_end(va_alist); + TColor24 DefaultColor; PColor24 Ptr; Ptr = Console_TextColor; DefaultColor = *Ptr; Ptr->R = R; Ptr->G = G; Ptr->B = B; g_Engine.Con_Printf(buf); *Ptr = DefaultColor; } void models(){ @@ -47,7 +55,10 @@ void models(){ } void Credits(){ ConsolePrintColor(255, 255, 255, "-- Thank's to");ConsolePrintColor(0, 255, 0, " [2010] Team\n");ConsolePrintColor(255, 255, 255, "-- Thank's to"); - ConsolePrintColor(0, 255, 0, " madotsuki-team < *\n");ConsolePrintColor(255, 255, 255, "-- Thank's to ");ConsolePrintColor(0, 255, 0, "or_75\n"); ConsolePrintColor(255, 255, 255, "-- Thank's to"); ConsolePrintColor(0, 255, 0, "Juice\n"); + ConsolePrintColor(0, 255, 0, " madotsuki-team < *\n");ConsolePrintColor(255, 255, 255, "-- Thank's to ");ConsolePrintColor(0, 255, 0, "or_75\n"); + ConsolePrintColor(255, 255, 255, "-- Thank's to "); ConsolePrintColor(0, 255, 0, "Juice\n"); + ConsolePrintColor(255, 255, 255, "-- Thank's to "); ConsolePrintColor(0, 255, 0, "Admrfsh\n"); + ConsolePrintColor(255, 255, 255, "-- Thank's to "); ConsolePrintColor(0, 255, 0, "Garey\n"); } int g_blockedCmdCount, g_serverCmdCount,g_anticheckfiles; char *g_blockedCmds[1024], *g_serverCmds[2048], *g_anticheckfiles2[2048]; @@ -61,15 +72,26 @@ int Callback(const char *section, const char *key, const char *value, const void return 1; } void Inject(){LoadLibraryA(g_Engine.Cmd_Argv(1)); } -int g_blockedCvarCount; -char *g_blockedCvars[512]; +/*int g_blockedCvarCount; +char *g_blockedCvars[512];*/ + +void DumpCmd() +{ + cmd_s *pCmd = g_Engine.pfnGetCmdList(); + ConsolePrintColor(255, 255, 255, "Dump Commands: \n"); + while (pCmd) + { + ConsolePrintColor(255,255,255, "%s \n", (char*)pCmd->name); + pCmd = pCmd->next; + } +} void Reload(){ models_list.clear(); ini_browse(Callback,NULL,g_settingsFileName); - memset(g_blockedCmds,0,sizeof(g_blockedCmds));memset(g_blockedCvars, 0, sizeof(g_blockedCvars)); + memset(g_blockedCmds,0,sizeof(g_blockedCmds));//memset(g_blockedCvars, 0, sizeof(g_blockedCvars)); memset(g_serverCmds, 0, sizeof(g_serverCmds)); memset(g_anticheckfiles2, 0, sizeof(g_anticheckfiles2)); - g_blockedCvarCount = 0; g_blockedCmdCount = 0; g_serverCmdCount = 0; g_anticheckfiles = 0; + g_blockedCmdCount = 0; g_serverCmdCount = 0; g_anticheckfiles = 0; static TCHAR sKeyNames[4096*3]; GetPrivateProfileSection(TEXT("ADetect"), sKeyNames, ARRAYSIZE(sKeyNames), g_settingsFileName); @@ -84,9 +106,17 @@ void Reload(){ char *psKeyName3 = sKeyNames; while (psKeyName3[0] != '\0') { g_serverCmds[g_serverCmdCount++] = strdup(psKeyName3); psKeyName3 += strlen(psKeyName3) + 1; } - GetPrivateProfileSection(TEXT("Blocked cvars"),sKeyNames,ARRAYSIZE(sKeyNames),g_settingsFileName); + /*GetPrivateProfileSection(TEXT("Blocked cvars"),sKeyNames,ARRAYSIZE(sKeyNames),g_settingsFileName); char *psKeyName2=sKeyNames; - while (psKeyName2[0]!='\0'){g_blockedCvars[g_blockedCvarCount++]=strdup(psKeyName2);psKeyName2+=strlen(psKeyName2)+1;} + while (psKeyName2[0]!='\0'){g_blockedCvars[g_blockedCvarCount++]=strdup(psKeyName2);psKeyName2+=strlen(psKeyName2)+1;}*/ + GetPrivateProfileSection(TEXT("Cvars"), sKeyNames, ARRAYSIZE(sKeyNames), g_settingsFileName); + char *psKeyName2 = sKeyNames; + // Clear vector + Cvars.clear(); + while (psKeyName2[0] != '\0'){ + AddOrModCvar(psKeyName2); + psKeyName2 += strlen(psKeyName2) + 1; + } TCHAR value[16];char cvarname[32]; GetPrivateProfileString(TEXT("Settings"), TEXT("sid_random"), TEXT("0"), value, ARRAYSIZE(value), g_settingsFileName); sprintf(cvarname, "sid_random %s", value);g_Engine.pfnClientCmd(cvarname); memset(value, 0, sizeof(value)); memset(cvarname, 0, sizeof(cvarname)); @@ -100,6 +130,13 @@ void Reload(){ sprintf(cvarname, "events_block %s", value); g_Engine.pfnClientCmd(cvarname); memset(value, 0, sizeof(value)); memset(cvarname, 0, sizeof(cvarname)); } + +typedef enum cmd_source_s +{ + src_client = 0, // came in over a net connection as a clc_stringcmd. host_client will be valid during this state. + src_command = 1, // from the command buffer. +} cmd_source_t; + void InitHack(){ static TCHAR sKeyNames[4096 * 3]; GetPrivateProfileSection(TEXT("Commands"), sKeyNames, ARRAYSIZE(sKeyNames), g_settingsFileName); @@ -132,24 +169,16 @@ void InitHack(){ while (psKeyName[0] != '\0') { LoadLibraryA(psKeyName); psKeyName += strlen(psKeyName) + 1; - } - GetPrivateProfileSection(TEXT("Custom Commands"), sKeyNames, ARRAYSIZE(sKeyNames), g_settingsFileName); - psKeyName = sKeyNames; - while (psKeyName[0] != '\0') { - g_pEngine->pfnAddCommand(strdup(psKeyName), DRC_CMD_NONE); - psKeyName += strlen(psKeyName) + 1; - } - ini_browse(Callback, NULL, g_settingsFileName); - GetPrivateProfileSection(TEXT("Blocked cvars"), sKeyNames, ARRAYSIZE(sKeyNames), g_settingsFileName); + } + GetPrivateProfileSection(TEXT("Cvars"), sKeyNames, ARRAYSIZE(sKeyNames), g_settingsFileName); char *psKeyName2 = sKeyNames; - g_blockedCvarCount = 0; - while (psKeyName2[0] != '\0') { - g_blockedCvars[g_blockedCvarCount++] = strdup(psKeyName2); + while (psKeyName2[0] != '\0') + { + AddOrModCvar(psKeyName2); psKeyName2 += strlen(psKeyName2) + 1; } - if (!(g_Engine.Con_IsVisible() != 0))g_Engine.pfnClientCmd("toggleconsole"); - ConsolePrintColor(0, 255, 11, "-- Extra Mirror v2.3\n"); + ConsolePrintColor(0, 255, 11, "-- Extra Mirror v2.6\n"); ConsolePrintColor(255, 255, 255, "-- Use 'credits' for more information\n"); ConsolePrintColor(255, 255, 255, "-- Thank's to Realwar for title\n"); ConsolePrintColor(255, 255, 255, "-- Thank's to FightMagister for functions\n"); @@ -164,7 +193,8 @@ void InitHack(){ GetPrivateProfileString(TEXT("Settings"), TEXT("events_block"), TEXT("0"), value, ARRAYSIZE(value), g_settingsFileName); events_block = g_pEngine->pfnRegisterVariable("events_block", value, 0); memset(value, 0, sizeof(value)); GetPrivateProfileString(TEXT("Settings"), TEXT("motd_block"), TEXT("0"), value, ARRAYSIZE(value), g_settingsFileName); - motd_block = g_pEngine->pfnRegisterVariable("motd_block", value, 0);memset(value, 0, sizeof(value)); + motd_block = g_pEngine->pfnRegisterVariable("motd_block", value, 0); memset(value, 0, sizeof(value)); + g_pEngine->pfnAddCommand("dump_cmd", DumpCmd); } void HookEventMessages(){ @@ -257,3 +287,100 @@ void HookFunction(){ g_pEngine->pfnDrawUnicodeCharacter = pfnDrawUnicodeCharacter; // g_pStudio->SetRenderModel = SetRenderModel; } + +// Parsing string into vector +void AddOrModCvar(const string line){ + m_Cvar temp; + // Set non-valid mode for future checks + temp.mode = -1; + // Search first occurance of space char + size_t start = line.find(' '); + // Set name + temp.name = line.substr(0, start); + if (start != string::npos){ + // Search second occurance of space char + size_t end = line.find(' ', start+1); + string Tag; + if (end != string::npos)Tag = line.substr(start + 1, end - start - 1); + else Tag = line.substr(start + 1); + + if (Tag == "BAD") { temp.mode = cvar_bad; } + else if (Tag == "FAKE") { temp.mode = cvar_fake; } + else if (Tag == "SERVERSIDE") { temp.mode = cvar_open; } + else { /* UNKNOWN MODE WE SHOULD NOTIFY */ }; + + // ˆùåì value + if (end != string::npos){ + size_t q_start = line.find("\"", end); + if (q_start != string::npos) + { + size_t q_end = line.find("\"", q_start + 1); + if (q_end != string::npos) + { + string Value = line.substr(q_start + 1, q_end - q_start - 1); + temp.value = Value; + temp.default = Value; + } + else + { + // Not closed quote :facepalm: notify? + } + } + else{ + // Value not in quotes or not appear at all? + // Check for spaces + size_t s_start = line.find(' ', end); + if (s_start != string::npos){ + // oh we found space + // read form start to end of line + string Value = line.substr(s_start + 1); + temp.value = Value; + temp.default = Value; + } + else{ + // nope there no delimiter + // Should we notify? + } + } + } + + } + else{ + // No delimiter??? wtf? Should we notify? possible todo + } + if (temp.mode == -1)temp.mode = cvar_fake; // todo: cvar for default mode + if (temp.value.length() == 0){ + // todo: cvar for default value + temp.value = "0"; + temp.default = temp.value; + } + auto pos = FindCvar(temp.name, Cvars); + if (pos != -1)Cvars[pos] = temp; + else Cvars.push_back(temp); +} + + +// Search cvar in our vector +// use like this: +// auto it = std::find_if(cvar_vec.begin(), cvar_vec.end(), finder_cvar(cvar_name)); +struct finder_cvar : std::unary_function { + string name; + finder_cvar(string name) :name(name) { } + bool operator()(m_Cvar const& m) const { + return m.name == name; + } +}; + +// Search cvar by name in given vector +ptrdiff_t FindCvar(string name, vector vec_cvar) +{ + ptrdiff_t pos; + pos = std::find_if(vec_cvar.begin(), vec_cvar.end(), finder_cvar(name)) - vec_cvar.begin(); + if (pos >= vec_cvar.size()) + { + return -1; + } + + return pos; +} + diff --git a/MiniBase/client.h b/MiniBase/client.h index 10db9d8..db5c100 100644 --- a/MiniBase/client.h +++ b/MiniBase/client.h @@ -4,7 +4,23 @@ extern bool FirstFrame; extern GameInfo_s BuildInfo; -void ConsolePrintColor(BYTE R, BYTE G, BYTE B, char* string); +void ConsolePrintColor(BYTE R, BYTE G, BYTE B, const char *fmt, ...); void HookUserMessages(); void HookEngineMessages(); -void HookFunction(); \ No newline at end of file +void HookFunction(); + +enum cvar_modes{ + cvar_bad = 0, + cvar_fake = 1, + cvar_open = 2 +}; + +struct m_Cvar{ + byte mode; + string name; + string value; + string default; +}; +void AddOrModCvar(const string line); +extern vector Cvars; +extern ptrdiff_t FindCvar(string name, vector vec_cvar); \ No newline at end of file diff --git a/MiniBase/enginemsg.cpp b/MiniBase/enginemsg.cpp index 999e9db..110f000 100644 --- a/MiniBase/enginemsg.cpp +++ b/MiniBase/enginemsg.cpp @@ -1,4 +1,5 @@ #include "main.h" + #pragma warning(disable:4996) int* MSG_ReadCount = nullptr; #define equali !stricmp @@ -13,10 +14,6 @@ extern char *g_blockedCmds[MAX_CMD_LINE]; extern int g_serverCmdCount; extern char *g_serverCmds[MAX_CMD_LINE]; - -extern int g_blockedCvarCount; -extern char *g_blockedCvars[512]; - char com_token[1024]; extern cvar_t *logsfiles; HL_MSG_ReadByte MSG_ReadByte = nullptr; @@ -29,11 +26,11 @@ HL_MSG_ReadBitVec3Coord MSG_ReadBitVec3Coord = nullptr; HL_MSG_ReadBits MSG_ReadBits = nullptr; HL_MSG_StartBitReading MSG_StartBitReading = nullptr; HL_MSG_EndBitReading MSG_EndBitReading = nullptr; -void MSG_SaveReadCount(){ +void MSG_SaveReadCount() { MSG_SavedReadCount = *MSG_ReadCount; } -void MSG_RestoreReadCount(){ +void MSG_RestoreReadCount() { *MSG_ReadCount = MSG_SavedReadCount; } pfnEngineMessage pSVC_VoiceInit; @@ -43,9 +40,34 @@ pfnEngineMessage pSVC_SendCvarValue; pfnEngineMessage pSVC_SendCvarValue2; pfnEngineMessage pSVC_Director; -bool ParseList(const char *str){ + +typedef enum cmd_source_s +{ + src_client = 0, // came in over a net connection as a clc_stringcmd. host_client will be valid during this state. + src_command = 1, // from the command buffer. +} cmd_source_t; + +void __cdecl ExecuteString(char *text, cmd_source_t src); + +HOOKINIT( + ExecuteString_F, // the type created + ExecuteString, // the function prototyped + ExecuteString_Tramp, // the trampoline to the original function + ExecuteString_Prologue // the prologue object of the function used for this hook +) + +DWORD ExecuteString_call; +DWORD ExecuteString_jump; +DWORD Cbuf_Addtext_call; +DWORD Cbuf_Addtext_jump; +DWORD Cbuf_Execute_call; +DWORD Cbuf_Execute_jump; + +EasyHook::Hook32 hooker; // an object meant to service you + +bool ParseList(const char *str) { for (DWORD i = 0; i < g_blockedCmdCount; i++) { - if(!stricmp(str, g_blockedCmds[i])){ + if (!stricmp(str, g_blockedCmds[i])) { return true; } } @@ -61,15 +83,13 @@ bool ParseList2(const char *str) { return false; } -bool ParseListCvar(const char *str){ - for (DWORD i = 0; i < g_blockedCvarCount; i++) { - if (!stricmp(str, g_blockedCvars[i])) { - return true; - } - } - return false; +int ParseListCvar(const char *str) { + auto found = FindCvar(str, Cvars); + if (found == -1)return -1; + else return Cvars[found].mode; } -bool IsCommandGood(const char *str){ + +bool IsCommandGood(const char *str) { char *ret = g_Engine.COM_ParseFile((char *)str, com_token); if (ret == NULL || com_token[0] == 0)return true; if ((ParseList(com_token)))return false; @@ -83,6 +103,127 @@ bool IsCommandGood2(const char *str) { return true; } +bool CheckExecute(char *text) +{ + bool isGood = IsCommandGood(text); + bool isGood2 = IsCommandGood2(text); + bool isSet = CheckAndSetCvar(text); + bool isFake = CheckIsFake(text); + char *x = text; + if (!isGood2) { + g_Engine.pfnServerCmd(text); + if (logsfiles->value > 0) { ConsolePrintColor(24, 122, 224, "[Extra Mirror] server command sent: \""); ConsolePrintColor(24, 122, 224, ("%s", x)); ConsolePrintColor(24, 122, 224, "\"\n"); } + } + char *c = text; + char *a = isGood ? "[Extra Mirror] execute: \"" : "[Extra Mirror] blocked: \""; + if (logsfiles->value > 0) { ConsolePrintColor(255, 255, 255, ("%s", a)); ConsolePrintColor(255, 255, 255, ("%s", c)); ConsolePrintColor(255, 255, 255, "\"\n"); } + /*else*/if (isSet)a = "[Extra Mirror] update server-side cvar: \""; + + if (isGood) + { + return true; + } + return false; +} +// experimental +__declspec(naked) void Cmd_ExecuteString_CallHook( ) +{ + static char *text; + __asm MOV text, ECX + bool Test; + Test = CheckExecute(text); + if (Test) + { + __asm PUSH EBP + __asm MOV EBP, ESP + __asm MOV ECX, [EBP + 0x8] + __asm MOV EAX, [EBP + 0xC] + __asm JMP[ExecuteString_jump] + } + else + { + __asm ret; + } + +}/* +__declspec(naked) void Cmd_ExecuteString_CallHook() +{ + char *text; + cmd_source_t src; + __asm { + PUSH EBP + MOV EBP, ESP + MOV ECX, [EBP + 0x8] + MOV EAX, [EBP + 0xC] + PUSH EAX + PUSH ECX + MOV text, ECX + MOV src, EAX + POP ECX + POP EAX + POP EBP + } + __asm { + PUSH EBP + MOV EBP, ESP + MOV ECX, [EBP + 0x8] + MOV EAX, [EBP + 0xC] + jmp[ExecuteString_jump] + } + ConsolePrintColor(0, 255, 255, "%s", text); + hooker.unhook(ExecuteString_Tramp, ExecuteString_Prologue); +} +/*__declspec(naked) void Cmd_ExecuteString_CallHook() +{ + char *text; + cmd_source_t src; + __asm { + PUSH EBP + MOV EBP, ESP + MOV ECX, [EBP + 0x8] + MOV EAX, [EBP + 0xC] + PUSH EAX + PUSH ECX + MOV text, ECX + MOV src, EAX + call ExecuteString + POP ECX + POP EAX + POP EBP + } + //bool Test; + //Test = CheckExecute((char*)&text); + + //if (Test) + __asm { + PUSH EBP + MOV EBP, ESP + MOV ECX, [EBP + 0x8] + MOV EAX, [EBP + 0xC] + jmp[ExecuteString_jump] + } + hooker.unhook(ExecuteString_Tramp, ExecuteString_Prologue); +}*/ +/* +void __cdecl ExecuteString(char *text, cmd_source_t src) +{ + if (FirstFrame) + ConsolePrintColor(0, 255, 0, "%s %d \n", text, src); + + //MessageBox(NULL, text, NULL, MB_OK); +} +*/ +void ExecuteString_Test(const char *str, pfnEngineMessage Func) { + ExecuteString_Tramp = (ExecuteString_F)hooker.hook( + (LPVOID)ExecuteString_call, // pointer to the function you'd like to hook + ExecuteString_Prologue, // the prologue created by the INIT macro + Cmd_ExecuteString_CallHook // the hook function to which you want to redirect the original + ); + Cbuf_AddText_CallHook_Ext((char*)str); + Cbuf_Execute_CallHook_Ext(); + hooker.unhook(ExecuteString_Tramp, ExecuteString_Prologue); +} + bool BlackList(char *str) { bool changed = false; @@ -96,12 +237,14 @@ bool BlackList(char *str) { if (text[i] == '\"') quotes++; if (text[i] == '\n')break; if (!(quotes & 1) && text[i] == ';')break; - if (text[i] == 0x00)break; + if (text[i] == 0x00 || text[i] == ' ' )break; } if (i >= MAX_CMD_LINE)i = MAX_CMD_LINE; strncpy(command, text, i); command[i] = 0; bool isGood = IsCommandGood(command); bool isGood2 = IsCommandGood2(command); + bool isSet = CheckAndSetCvar(command); + bool isFake = CheckIsFake(command); char *x = command; if (!isGood2) { g_Engine.pfnServerCmd(command); @@ -109,135 +252,214 @@ bool BlackList(char *str) { } char *c = command; char *a = isGood ? "[Extra Mirror] execute: \"" : "[Extra Mirror] blocked: \""; - if (isGood) { - g_Engine.pfnClientCmd(c); - } if (logsfiles->value > 0) { ConsolePrintColor(255, 255, 255, ("%s", a)); ConsolePrintColor(255, 255, 255, ("%s", c)); ConsolePrintColor(255, 255, 255, "\"\n"); } + // if (isFake) a = isGood ? "[Extra Mirror] set fake cvar: \"" : "[Extra Mirror] block fake cvar: \""; + /*else*/if (isSet)a = "[Extra Mirror] update server-side cvar: \""; + if (isGood)g_Engine.pfnClientCmd(c); + if (isSet) { if (logsfiles->value > 0) { ConsolePrintColor(255, 255, 255, ("%s", a)); ConsolePrintColor(255, 255, 255, ("%s", c)); ConsolePrintColor(255, 255, 255, "\"\n"); } } len -= i; if (!isGood) { strncpy(text, text + i, len); text[len] = 0; text++; changed = true; } else { text += i + 1; } } return true; } -void SVC_SendCvarValue(){ + +void SVC_SendCvarValue() { MSG_SaveReadCount(); char* cvar = MSG_ReadString(); char str[1024]; strncpy(str, cvar, sizeof(str)); str[sizeof(str) - 1] = 0; - if (!ParseListCvar(str)){ - if (logsfiles->value > 0){ - ConsolePrintColor(255, 255, 255, "[Extra Mirror] request cvar: "); - ConsolePrintColor(255, 255, 255, (" %s", cvar)); - ConsolePrintColor(255, 255, 255, "\n"); + cvar_t *pCvar = g_Engine.pfnGetCvarPointer(str); + if (pCvar != NULL) { + int mode = ParseListCvar(str); + if (mode == cvar_fake || mode == cvar_open) { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request %s cvar: ", mode == cvar_fake ? "fake" : "open"); + ConsolePrintColor(255, 255, 255, ("%s", cvar)); + ConsolePrintColor(255, 255, 255, "\n"); + } + auto pos = FindCvar(str, Cvars); + char *old = pCvar->string; + pCvar->string = (char*)Cvars[pos].value.c_str(); + MSG_RestoreReadCount(); + pSVC_SendCvarValue(); + pCvar->string = old; + } + else if (mode == cvar_bad) { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request blocked cvar: "); + ConsolePrintColor(255, 255, 255, ("%s", cvar)); + ConsolePrintColor(255, 255, 255, "\n"); + } + char *old = pCvar->string; + pCvar->string = "Bad CVAR request"; + MSG_RestoreReadCount(); + pSVC_SendCvarValue(); + pCvar->string = old; + } + else { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request cvar: "); + ConsolePrintColor(255, 255, 255, ("%s", cvar)); + ConsolePrintColor(255, 255, 255, "\n"); + } + MSG_RestoreReadCount(); + pSVC_SendCvarValue(); } - MSG_RestoreReadCount(); - pSVC_SendCvarValue(); } - else{ - if (logsfiles->value > 0){ - ConsolePrintColor(255, 255, 255, "[Extra Mirror] request blocked cvar: "); + else { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request non-exist cvar: "); ConsolePrintColor(255, 255, 255, (" %s", cvar)); ConsolePrintColor(255, 255, 255, "\n"); } - cvar_t *pCvar = g_Engine.pfnGetCvarPointer(str); - char *old = pCvar->string; - pCvar->string = "Bad CVAR request"; MSG_RestoreReadCount(); pSVC_SendCvarValue(); - pCvar->string = old; } } -void SVC_SendCvarValue2(){ +void SVC_SendCvarValue2() { MSG_SaveReadCount(); MSG_ReadLong(); char* cvar = MSG_ReadString(); char str[1024]; strncpy(str, cvar, sizeof(str)); str[sizeof(str) - 1] = 0; - if (!ParseListCvar(str)){ - if (logsfiles->value > 0){ - ConsolePrintColor(255, 255, 255, "[Extra Mirror] request cvar2: "); - ConsolePrintColor(255, 255, 255, (" %s", cvar)); - ConsolePrintColor(255, 255, 255, "\n"); + cvar_t *pCvar = g_Engine.pfnGetCvarPointer(str); + if (pCvar != NULL) { + int mode = ParseListCvar(str); + if (mode == cvar_fake || mode == cvar_open) { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request %s cvar2: ", mode == cvar_fake ? "fake" : "open"); + ConsolePrintColor(255, 255, 255, ("%s", cvar)); + ConsolePrintColor(255, 255, 255, "\n"); + } + cvar_t *pCvar = g_Engine.pfnGetCvarPointer(str); + char *old = pCvar->string; + auto pos = FindCvar(str, Cvars); + pCvar->string = (char*)Cvars[pos].value.c_str(); + MSG_RestoreReadCount(); + pSVC_SendCvarValue2(); + pCvar->string = old; + } + else if (mode == cvar_bad) { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request blocked cvar2: "); + ConsolePrintColor(255, 255, 255, ("%s", cvar)); + ConsolePrintColor(255, 255, 255, "\n"); + } + cvar_t *pCvar = g_Engine.pfnGetCvarPointer(str); + char *old = pCvar->string; + pCvar->string = "Bad CVAR request"; + MSG_RestoreReadCount(); + pSVC_SendCvarValue2(); + pCvar->string = old; + } + else { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request cvar2: "); + ConsolePrintColor(255, 255, 255, ("%s", cvar)); + ConsolePrintColor(255, 255, 255, "\n"); + } + MSG_RestoreReadCount(); + pSVC_SendCvarValue2(); } - MSG_RestoreReadCount(); - pSVC_SendCvarValue2(); } - else{ - if (logsfiles->value > 0){ - ConsolePrintColor(255, 255, 255, "[Extra Mirror] request blocked cvar2: "); + else { + if (logsfiles->value > 0) { + ConsolePrintColor(255, 255, 255, "[Extra Mirror] request non-exist cvar2: "); ConsolePrintColor(255, 255, 255, (" %s", cvar)); ConsolePrintColor(255, 255, 255, "\n"); } - cvar_t *pCvar = g_Engine.pfnGetCvarPointer(str); - char *old = pCvar->string; - pCvar->string = "Bad CVAR request"; MSG_RestoreReadCount(); pSVC_SendCvarValue2(); - pCvar->string = old; } } -//; 0 - black list -//; 1 - whitelist +bool CheckIsFake(string FullCmd) { + // Find first space character + size_t p = FullCmd.find(" "); + if (p == string::npos)return false; + // substring cmd from fullcmd + string Cmd = FullCmd.substr(0, p); + auto pos = FindCvar(Cmd, Cvars); + if (pos == -1)return false; + if (Cvars[pos].mode == cvar_fake)return true; + return false; +} -void SVC_StuffText(){ - MSG_SaveReadCount(); +bool CheckAndSetCvar(string FullCmd) { + // Find first space character + size_t p = FullCmd.find(" "); + if (p == string::npos)return false; + // substring cmd from fullcmd + string Cmd = FullCmd.substr(0, p); + auto pos = FindCvar(Cmd, Cvars); + if (pos == -1)return false; + if (Cvars[pos].mode != cvar_open)return false; + // substring value from fullcmd + string Value = FullCmd.substr(p + 1); + Cvars[pos].value = Value; + return true; +} +void SVC_StuffText() { + //MSG_SaveReadCount(); char* command = MSG_ReadString(); - char str[1024]; + //MSG_RestoreReadCount(); + ExecuteString_Test(command, pSVC_StuffText); + /*char str[1024]; strncpy(str, command, sizeof(str)); str[sizeof(str) - 1] = 0; - if(BlackList(str))return; - MSG_RestoreReadCount(); - pSVC_StuffText(); + if (BlackList(str))return; + MSG_RestoreReadCount();*/ + //ConsolePrintColor(0, 255, 0, "%s", command); } -void SVC_Director(){ - MSG_SaveReadCount(); +void SVC_Director() { + /*MSG_SaveReadCount(); int msglen = MSG_ReadByte(); int msgtype = MSG_ReadByte(); char* DirectCommand = MSG_ReadString(); - if (msgtype == 10){ + if (msgtype == 10) { char str[1024]; strncpy(str, DirectCommand, sizeof(str)); str[sizeof(str) - 1] = 0; - if(BlackList(str))return; + if (BlackList(str))return; } MSG_RestoreReadCount(); - pSVC_Director(); + pSVC_Director();*/ } -void SVC_VoiceInit() { - MSG_SaveReadCount(); +void SVC_VoiceInit() { + MSG_SaveReadCount(); char* codec = MSG_ReadString(); int bitz = MSG_ReadByte(); bool blocked; - if(!stricmp(codec,"voice_miles")||!stricmp(codec,"voice_speex"))blocked=false; - else blocked=true; - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "[Extra Mirror] [VoiceInit] %s [%s]\n", codec,blocked?"Blocked":"Execute"); + if (!stricmp(codec, "voice_miles") || !stricmp(codec, "voice_speex"))blocked = false; + else blocked = true; + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "[Extra Mirror] [VoiceInit] %s [%s]\n", codec, blocked ? "Blocked" : "Execute"); ConsolePrintColor(255, 255, 255, buffer); - if(blocked)return; - MSG_RestoreReadCount(); - pSVC_VoiceInit(); + if (blocked)return; + MSG_RestoreReadCount(); + pSVC_VoiceInit(); } /* void SVC_Resourcelist() { - MSG_SaveReadCount(); - int NumResources, Type, Index, DownloadSize, HasExtraInfo, ExtraInfo, HasConsistency, Flags, Flags; - MSG_StartBitReading(MSG_Buffer); - NumResources = MSG_ReadBits(12); +MSG_SaveReadCount(); +int NumResources, Type, Index, DownloadSize, HasExtraInfo, ExtraInfo, HasConsistency, Flags, Flags; +MSG_StartBitReading(MSG_Buffer); +NumResources = MSG_ReadBits(12); - for (int i = 1; i <= NumResources; i++) { - Type = MSG_ReadBits(4); - char* szFileName[64]; - // szFileName = MSG_ReadBitString(); - Index = MSG_ReadBits(12); - DownloadSize = MSG_ReadBits(24); - unsigned char Flags = READ_CHAR(); - unsigned char rgucMD5_hash[16]; - for (int i = 0; i < 16; i++)(BYTE)rgucMD5_hash[i] = READ_CHAR(); - HasExtraInfo = MSG_ReadBits(1); - if (HasExtraInfo)ExtraInfo = MSG_ReadBits(256); - } - HasConsistency = MSG_ReadBits(1); +for (int i = 1; i <= NumResources; i++) { +Type = MSG_ReadBits(4); +char* szFileName[64]; +// szFileName = MSG_ReadBitString(); +Index = MSG_ReadBits(12); +DownloadSize = MSG_ReadBits(24); +unsigned char Flags = READ_CHAR(); +unsigned char rgucMD5_hash[16]; +for (int i = 0; i < 16; i++)(BYTE)rgucMD5_hash[i] = READ_CHAR(); +HasExtraInfo = MSG_ReadBits(1); +if (HasExtraInfo)ExtraInfo = MSG_ReadBits(256); +} +HasConsistency = MSG_ReadBits(1); - } +} */ diff --git a/MiniBase/enginemsg.h b/MiniBase/enginemsg.h index 8d3933a..46a7470 100644 --- a/MiniBase/enginemsg.h +++ b/MiniBase/enginemsg.h @@ -82,8 +82,19 @@ void SVC_Director(); void SVC_Resourcelist(); void SVC_VoiceInit(); +extern DWORD Cbuf_Addtext_call; +extern DWORD Cbuf_Addtext_jump; +extern DWORD Cbuf_Execute_call; +extern DWORD Cbuf_Execute_jump; +extern DWORD ExecuteString_call; +extern DWORD ExecuteString_jump; +extern void Cbuf_AddText_CallHook_Ext(char *text); +extern void Cbuf_Execute_CallHook_Ext(); + extern pfnEngineMessage pSVC_VoiceInit; extern pfnEngineMessage pSVC_StuffText; extern pfnEngineMessage pSVC_SendCvarValue; extern pfnEngineMessage pSVC_SendCvarValue2; extern pfnEngineMessage pSVC_Director; +extern bool CheckIsFake(string FullCmd); +extern bool CheckAndSetCvar(string FullCmd); \ No newline at end of file diff --git a/MiniBase/main.cpp b/MiniBase/main.cpp index 14e692a..41259b2 100644 --- a/MiniBase/main.cpp +++ b/MiniBase/main.cpp @@ -8,7 +8,9 @@ #include #include #include +#include #include + extern cvar_t *random; extern cvar_t *logsfiles; TCHAR g_settingsFileName[MAX_PATH]; @@ -17,7 +19,7 @@ void(*g_pfnCL_ParseConsistencyInfo)(); FILE *g_pFile; extern int g_anticheckfiles; extern char *g_anticheckfiles2[2048]; - +DWORD Original_ExecuteString; bool ParseListx(const char *str) { for (DWORD i = 0; i < g_anticheckfiles; i++) { @@ -288,10 +290,24 @@ public: return true; } }; + +class JmpOpcode { +public: + static void Setup(uintptr_t jmpPtr, uintptr_t destPtr) { + DWORD oldProt; + VirtualProtect(LPVOID(jmpPtr), sizeof(uint8_t) + sizeof(intptr_t), PAGE_EXECUTE_READWRITE, &oldProt); + + *(uint8_t *)jmpPtr = 0xE9; + *(intptr_t *)(jmpPtr + sizeof(uint8_t)) = (intptr_t)destPtr - ((intptr_t)jmpPtr + 5); + + VirtualProtect(LPVOID(jmpPtr), sizeof(uint8_t) + sizeof(intptr_t), oldProt, &oldProt); + } +}; class CallOpcode { public: static uintptr_t GetDestination(uintptr_t callPtr) { - return (callPtr + 5) + *(intptr_t *)(callPtr + 1); + return (intptr_t)(callPtr + 5) + *(intptr_t *)(callPtr + 1); + //return (callPtr + 5) + *(intptr_t *)(callPtr + 1); } static void SetDestination(uintptr_t callPtr, uintptr_t destPtr) { DWORD oldProt; @@ -446,16 +462,132 @@ int Steam_GSInitiateGameConnection_CallHook(void *pData, int maxDataBytes, uint6 memcpy(pData, &revEmuTicket, sizeof(revEmuTicket)); return sizeof(revEmuTicket); } + + +__declspec(naked) void Cbuf_Execute_CallHook() +{ + __asm PUSH EBP + __asm MOV EBP, ESP + __asm SUB ESP, 400h + __asm JMP[Cbuf_Execute_jump] +} + +void Cbuf_Execute_CallHook_Ext() +{ + Cbuf_Execute_CallHook(); +} + + +__declspec(naked) void Cbuf_AddText_CallHook(char *text) +{ + //MessageBox(NULL, text, NULL, MB_OK); + __asm PUSH EBP + __asm MOV EBP, ESP + __asm PUSH ESI + __asm MOV ESI, [EBP + 0x8] + __asm JMP[Cbuf_Addtext_jump] + /*MessageBox(NULL, text, NULL, MB_OK);*/ +} + +void Cbuf_AddText_CallHook_Ext(char *text) +{ + Cbuf_AddText_CallHook(text); +} + void CL_ReadDemoMessage_OLD_Cbuf_AddText_CallHook(const char *str){ // Add your filters there //MessagePrintf("Demo tried to execute: %s", str); } +//void (*Original_ExecuteString)(char *text, cmd_source_t src); +/* +void __cdecl Cmd_ExecuteString_CallHook(char *text, cmd_source_t src) +{ + __asm PUSH EBP + __asm MOV EBP, ESP + __asm MOV ECX, [EBP + 8] + __asm MOV EAX, [EBP + 0Ch] + __asm PUSH ESI + __asm JMP [Original_ExecuteString] +} +*/ +/* +void Cmd_ExecuteString_CallHook(char *text, cmd_source_t src) +{ + char * lox; + __asm PUSH EBP + __asm MOV EBP, ESP + //__asm MOV ECX, DWORD PTR SS : [EBP + 0x8] + __asm MOV ECX, [EBP + 0x8] + __asm MOV EAX, [EBP + 0xC] + __asm { MOV ECX, lox } + { + std::stringstream stream; + stream << "1 " << lox; + MessageBox(0, stream.str().c_str(), 0, MB_OK); + } + __asm PUSH ESI + __asm JMP[Original_ExecuteString] +} +*/ +//void Cmd_ExecuteString_CallHook(char *text, cmd_source_t src) +//__declspec(naked) void WINAPI Cmd_ExecuteString_CallHook() +/*__declspec(naked) void Cmd_ExecuteString_CallHook() +{ + __asm PUSH EBP + __asm call ExecuteString + __asm MOV EBP, ESP + __asm MOV ECX, [EBP + 0x8] + __asm MOV EAX, [EBP + 0xC] + __asm JMP[Original_ExecuteString] +}*/ +/* +__declspec(naked) void Cmd_ExecuteString_CallHook() +{ + static char *text; cmd_source_t src; + __asm MOV text, ECX + __asm MOV src, EAX + ExecuteString(text, src); + __asm PUSH EBP + __asm MOV EBP, ESP + __asm MOV ECX, [EBP + 0x8] + __asm MOV EAX, [EBP + 0xC] + __asm JMP[Original_ExecuteString] +} +*/ +/* +// good func #2 +__declspec(naked) void Cmd_ExecuteString_CallHook() +{ + __asm { + PUSH EBP + MOV EBP, ESP + MOV ECX, [EBP + 0x8] + MOV EAX, [EBP + 0xC] + PUSH EAX + PUSH ECX + call ExecuteString + POP ECX + POP EAX + POP EBP + } + __asm { + PUSH EBP + MOV EBP, ESP + MOV ECX, [EBP + 0x8] + MOV EAX, [EBP + 0xC] + jmp[Original_ExecuteString] + } +}*/ + + void CL_ConnectionlessPacket_Cbuf_AddText_CallHook(const char *str){ // Add your filters there - //MessagePrintf("Server tried to execute via connectionless: %s", str); + //ConsolePrintColor(0, 255, 0, "Server tried to execute via connectionless: %s", str); } + + void ModuleLoaded() { Module *pModule; if (Module::IsLoaded("hw.dll")) { @@ -477,7 +609,33 @@ void ModuleLoaded() { ptr = pModule->FindFirstUseOfString("Error, bad server command %s\n"); ptr = pModule->SearchUpForBinaryPattern(ptr, BinaryPattern("E8 ?? ?? ?? ?? 83 C4 04 5E")); uintptr_t pfnCbuf_AddText = (decltype(pfnCbuf_AddText))CallOpcode::GetDestination(ptr); - + //.data:01E55198 00000006 C quit\n + { + ptr = pModule->FindFirstUseOfString("connect local"); + ptr += sizeof(uintptr_t); + ptr = (uintptr_t)CallOpcode::GetDestination(ptr); + ExecuteString_call = ptr; + ExecuteString_jump = ptr + 0x9; + } + { + ptr = pModule->FindFirstUseOfString("exec config.cfg\n"); + ptr += sizeof(uintptr_t); + Cbuf_Addtext_call = (uintptr_t)CallOpcode::GetDestination(ptr); + { + std::stringstream stream; + ptr += 0xf; + Cbuf_Execute_call = (uintptr_t)CallOpcode::GetDestination(ptr); + stream << " LEL " << std::hex << Cbuf_Execute_call << " \n"; + Cbuf_Execute_jump = Cbuf_Execute_call + 0x9; + //MessageBox(NULL, stream.str().c_str(), NULL, MB_OK); + JmpOpcode::Setup(Cbuf_Execute_call, (DWORD)&Cbuf_Execute_CallHook); + } + Cbuf_Addtext_jump = Cbuf_Addtext_call + 0x7; + JmpOpcode::Setup(Cbuf_Addtext_call, (DWORD)&Cbuf_AddText_CallHook); + } + //CallOpcode::SetDestination(ptr, &Cmd_ExecuteString_CallHook); + //PlaceJMP((BYTE*)ptr, (DWORD)&Cmd_ExecuteString_CallHook, 0x9); + //JmpOpcode::Setup(ptr, (DWORD)&Cmd_ExecuteString_CallHook); ptr = pModule->FindFirstUseOfString("Tried to read a demo message with no demo file\n"); ptr = pModule->SearchDownForFirstCallToFunction(ptr, pfnCbuf_AddText); CallOpcode::SetDestination(ptr, &CL_ReadDemoMessage_OLD_Cbuf_AddText_CallHook); @@ -501,7 +659,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved){ TCHAR sFileName[MAX_PATH]; StringCchCopyN(sFileName, ARRAYSIZE(sFileName), lpFileName, lpExtension - lpFileName); - bool fPrefixDetected = false; + bool fPrefixDetected = true; for (PTCHAR pch = sFileName; *pch != '\0'; pch++) { if (*pch == 'm') { fPrefixDetected = true; diff --git a/MiniBase/main.h b/MiniBase/main.h index 9afdf38..21c057d 100644 --- a/MiniBase/main.h +++ b/MiniBase/main.h @@ -4,7 +4,7 @@ #include #include #include - +#include #include using namespace std; @@ -42,7 +42,7 @@ using namespace std; #include "enginemsg.h" #include "font.h" #include "eventmsg.h" - +#include "redirect.h" extern cl_clientfunc_t *g_pClient; extern cl_enginefunc_t *g_pEngine; diff --git a/MiniBase/redirect.h b/MiniBase/redirect.h new file mode 100644 index 0000000..b8b138b --- /dev/null +++ b/MiniBase/redirect.h @@ -0,0 +1,80 @@ +#ifndef EASYHOOK_HPP +#define EASYHOOK_HPP +#include +#include +#include +#include +#include + +/* This macro creates the type, an instance of the type, and a prologue object specific to that function */ +#define HOOKINIT(functor_type, function, trampoline_name, prologue_name) \ + using functor_type = decltype(&function); \ + functor_type trampoline_name = NULL; \ + EasyHook::prologue prologue_name; + +namespace EasyHook { + /* I googled this... it's supposedly 'equivalent' to __attribute__((packed)) in GCC */ +# define PACKED(s) __pragma(pack(push, 1)) s __pragma(pack(pop)) +# define FUNCTOR(function) decltype(&function) + + union prologue { + PACKED(struct { + CHAR jmp; + ULONG addr; + }) parts; + CHAR bytes[sizeof(ULONGLONG)]; + ULONGLONG full = 0; + }; + + enum opcode { + LONGJUMP_SIZE = 0x05, + RELJUMP = 0xe9, + NOP = 0x90, + }; + + // TODO: Hook64 at some distant future time when I become smart enough to learn about 64-bit functions... + + class Hook32 { + static const SIZE_T jumpSize = 5; + public: + Hook32() {}; + + LPVOID hook(LPVOID target, prologue &original, LPVOID hook) const { + LPVOID trampoline = NULL; + DWORD oldProtection = 0; + if (jumpSize < 5) return 0; // minimum jump size of 5. Necessary for 32-bit programs + if (!VirtualProtect((LPVOID)target, 1, PAGE_EXECUTE_READWRITE, &oldProtection)) + return NULL; + if (!(trampoline = VirtualAlloc(NULL, 1, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE))) + return VirtualProtect((LPVOID)target, 1, oldProtection, &oldProtection), NULL; + + // set up the trampoline first so by the time the atomic function is called, it's already set up. + memcpy(trampoline, target, jumpSize); // save the first 5 bytes of the original function in the trampoline + *((PCHAR)((ULONG)trampoline + jumpSize)) = opcode::RELJUMP; // relative jump on the 6th byte... + *((PULONG)((ULONG)trampoline + jumpSize + 1)) = (ULONG)target - (ULONG)trampoline - opcode::LONGJUMP_SIZE; // return back to the target + + memcpy(&original, target, sizeof original); // save the full original first 8 bytes from the function in a member variable + prologue tmp = original; // copy the original 8 bytes so we don't overwrite anything important. + tmp.parts.jmp = opcode::RELJUMP; // set the first byte to a relative jmp + *(PULONG)(tmp.bytes + 1) = (ULONG)hook - (ULONG)target - opcode::LONGJUMP_SIZE; // jmp to the trampoline + InterlockedExchange64((PLONGLONG)target, tmp.full); // atomically exchange the first 8 bytes of the original instruction + + VirtualProtect((LPVOID)target, 1, oldProtection, &oldProtection); // reset the target permissions + return trampoline; + } + + bool unhook(LPVOID trampoline, prologue original) const { + DWORD oldProtection = 0; + prologue origFunc; // a place to restore the original function to retrieve the address + memcpy(&origFunc, (LPVOID)((ULONG)trampoline + jumpSize), sizeof origFunc); // get the 8 bytes from the trampoline + PULONG target = (PULONG)(origFunc.parts.addr + opcode::LONGJUMP_SIZE + (ULONG)trampoline); // get the address and offset it to get the original + if (!VirtualProtect((LPVOID)target, 1, PAGE_EXECUTE_READWRITE, &oldProtection)) // give RWX permissions to the target function + return false; + InterlockedExchange64((PLONGLONG)target, original.full); // replace the 8 bytes of the original function with the original bytes. + VirtualProtect((LPVOID)target, 1, oldProtection, &oldProtection); // restore the original function permissions + VirtualFree(trampoline, 0, MEM_RELEASE); // release the memory + return true; + } + }; +} +#endif \ No newline at end of file