From 73dd49e8d11f91142f81cc0e25c575c1428c89c9 Mon Sep 17 00:00:00 2001 From: PixieCore <41352111+PixieCore@users.noreply.github.com> Date: Sat, 30 Apr 2022 03:00:24 +0200 Subject: [PATCH] Start processing of unknown commands print. * UserMessages need to be properly read like in sub_14028E890 * Started building bf_read * Clean up will follow later. --- r5dev/common/netmessages.cpp | 26 +++- r5dev/common/netmessages.h | 44 ++++-- r5dev/tier1/bitbuf.cpp | 255 +++++++++++++++++++++++++++++++++++ r5dev/tier1/bitbuf.h | 52 +++++++ 4 files changed, 364 insertions(+), 13 deletions(-) diff --git a/r5dev/common/netmessages.cpp b/r5dev/common/netmessages.cpp index 98c770e5..5b190197 100644 --- a/r5dev/common/netmessages.cpp +++ b/r5dev/common/netmessages.cpp @@ -22,13 +22,35 @@ bool SVC_Print::Process() return true; // Original just return true also. } +bool SVC_UserMessage::Process() +{ + bf_read buf = m_DataIn; + UserMessages type = (UserMessages)buf.ReadByte(); + + if (type == UserMessages::TextMsg) + { + char text[256]; + buf.ReadString(text, sizeof(text)); + if (strnlen_s(text, 256) > 0) + { + DevMsg(eDLL_T::SERVER, text); + } + } + + return SVC_UserMessage_Process(this); // Need to return original. +} + void CNetMessages_Attach() { auto SVCPrint = &SVC_Print::Process; - CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VTable, (LPVOID&)SVCPrint, (LPVOID*)&SVC_Print_Process, 3); + auto SVCUserMessage = &SVC_UserMessage::Process; + CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VTable, (LPVOID&)SVCPrint, (LPVOID*)&SVC_Print_Process, 3); + //CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VTable, (LPVOID&)SVCUserMessage, (LPVOID*)&SVC_UserMessage_Process, 3); } void CNetMessages_Detach() { - CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VTable, (LPVOID)p_SVC_Print_Process, (LPVOID*)&SVC_Print_Process, 3); + void* hkRestore = nullptr; + CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VTable, (LPVOID)SVC_Print_Process, (LPVOID*)&hkRestore, 3); + //CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VTable, (LPVOID)SVC_UserMessage_Process, (LPVOID*)&hkRestore, 3); } \ No newline at end of file diff --git a/r5dev/common/netmessages.h b/r5dev/common/netmessages.h index 44397b3e..46c8a40b 100644 --- a/r5dev/common/netmessages.h +++ b/r5dev/common/netmessages.h @@ -1,4 +1,10 @@ #pragma once +#include "tier1/bitbuf.h" + +enum class UserMessages : int +{ + TextMsg = 0x2 +}; class CNetMessage { @@ -10,17 +16,30 @@ public: void* m_NetChannel; }; -class SVC_Print : CNetMessage +class SVC_Print : public CNetMessage { public: + bool Process(); + void* m_pMessageHandler; char padding[8]; const char* m_szText; +private: + char m_szTextBuffer[2048]; +}; + +class SVC_UserMessage : public CNetMessage +{ +public: bool Process(); -private: - char m_szTextBuffer[2048]; + void* m_pMessageHandler; + char padding[8]; + int m_nMsgType; + int m_nLength; // data length in bits + bf_read m_DataIn; + bf_write m_DataOut; }; struct VecNetMessages @@ -49,11 +68,15 @@ inline CMemory MM_Heartbeat__ToString; // server HeartBeat? (baseserver.cpp). //------------------------------------------------------------------------- // SVC_Print //------------------------------------------------------------------------- -inline CMemory p_SVC_Print_Process; -inline auto SVC_Print_Process = p_SVC_Print_Process.RCast(); - +inline auto SVC_Print_Process = CMemory().RCast(); inline void* g_pSVC_Print_VTable; +//------------------------------------------------------------------------- +// SVC_UserMessage +//------------------------------------------------------------------------- +inline auto SVC_UserMessage_Process = CMemory().RCast(); +inline void* g_pSVC_UserMessage_VTable; + void CNetMessages_Attach(); void CNetMessages_Detach(); @@ -63,23 +86,22 @@ class HMM_Heartbeat : public IDetour virtual void GetAdr(void) const { std::cout << "| FUN: MM_Heartbeat::ToString : 0x" << std::hex << std::uppercase << MM_Heartbeat__ToString.GetPtr() << std::setw(nPad) << " |" << std::endl; - std::cout << "| FUN: SVC_Print_Process : 0x" << std::hex << std::uppercase << p_SVC_Print_Process.GetPtr() << std::setw(nPad) << " |" << std::endl; std::cout << "| VAR: SVC_Print_VTable : 0x" << std::hex << std::uppercase << g_pSVC_Print_VTable << std::setw(nPad) << " |" << std::endl; + std::cout << "| VAR: SVC_UserMessage_VTable : 0x" << std::hex << std::uppercase << g_pSVC_UserMessage_VTable << std::setw(nPad) << " |" << std::endl; std::cout << "+----------------------------------------------------------------+" << std::endl; } virtual void GetFun(void) const { MM_Heartbeat__ToString = g_mGameDll.FindPatternSIMD(reinterpret_cast("\x48\x83\xEC\x38\xE8\x00\x00\x00\x00\x3B\x05\x00\x00\x00\x00"), "xxxxx????xx????"); // 0x1402312A0 // 48 83 EC 38 E8 ? ? ? ? 3B 05 ? ? ? ? - p_SVC_Print_Process = g_mGameDll.FindPatternSIMD(reinterpret_cast("\x48\x8B\xD1\x48\x8B\x49\x18\x48\x8B\x01\x48\xFF\x60\x28"), "xxxxxxxxxxxxxx"); - // 0x1402D0810 // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 28 - - SVC_Print_Process = p_SVC_Print_Process.RCast(); } virtual void GetVar(void) const { // We get the actual address of the vtable here, not the class instance. g_pSVC_Print_VTable = g_mGameDll.FindPatternSIMD(reinterpret_cast("\x74\x1E\x48\x8D\x05\x00\x00\x00\x00\x89\x5F\x08"), "xxxxx????xxx").OffsetSelf(0x2).ResolveRelativeAddressSelf(0x3, 0x7); + // 0x1402D21F6 74 1E 48 8D 05 ? ? ? ? 89 5F 08 + g_pSVC_UserMessage_VTable = g_mGameDll.FindPatternSIMD(reinterpret_cast("\xE8\x00\x00\x00\x00\x48\x85\xFF\x74\x65"), "x????xxxxx").OffsetSelf(0xD).ResolveRelativeAddressSelf(0x3, 0x7); + // 0x1402D295E E8 ? ? ? ? 48 85 FF 74 65 } virtual void GetCon(void) const { } virtual void Attach(void) const { } diff --git a/r5dev/tier1/bitbuf.cpp b/r5dev/tier1/bitbuf.cpp index 97ea23ce..be2e08ac 100644 --- a/r5dev/tier1/bitbuf.cpp +++ b/r5dev/tier1/bitbuf.cpp @@ -8,6 +8,261 @@ #include "core/stdafx.h" #include "tier1/bitbuf.h" +const uint32 s_nMaskTable[33] = { + 0, + (1 << 1) - 1, + (1 << 2) - 1, + (1 << 3) - 1, + (1 << 4) - 1, + (1 << 5) - 1, + (1 << 6) - 1, + (1 << 7) - 1, + (1 << 8) - 1, + (1 << 9) - 1, + (1 << 10) - 1, + (1 << 11) - 1, + (1 << 12) - 1, + (1 << 13) - 1, + (1 << 14) - 1, + (1 << 15) - 1, + (1 << 16) - 1, + (1 << 17) - 1, + (1 << 18) - 1, + (1 << 19) - 1, + (1 << 20) - 1, + (1 << 21) - 1, + (1 << 22) - 1, + (1 << 23) - 1, + (1 << 24) - 1, + (1 << 25) - 1, + (1 << 26) - 1, + (1 << 27) - 1, + (1 << 28) - 1, + (1 << 29) - 1, + (1 << 30) - 1, + 0x7fffffff, + 0xffffffff, +}; + + +CBitBuffer::CBitBuffer(void) +{ + m_bOverflow = false; + m_pDebugName = NULL; + m_nDataBits = -1; + m_nDataBytes = 0; +} + +void CBitBuffer::SetDebugName(const char* pName) +{ + m_pDebugName = pName; +} + +const char* CBitBuffer::GetDebugName() +{ + return m_pDebugName; +} + +bool CBitBuffer::IsOverflowed() +{ + return m_bOverflow; +} + +void CBitBuffer::SetOverflowFlag() +{ + m_bOverflow = true; +} + +void CBitRead::GrabNextDWord(bool bOverFlowImmediately) +{ + if (m_pDataIn == m_pBufferEnd) + { + m_nBitsAvail = 1; + m_nInBufWord = 0; + + m_pDataIn++; + + if (bOverFlowImmediately) + SetOverflowFlag(); + } + else + { + if (m_pDataIn > m_pBufferEnd) + { + SetOverflowFlag(); + m_nInBufWord = 0; + } + else + { + assert(reinterpret_cast(m_pDataIn) + 3 < reinterpret_cast(m_pBufferEnd)); + m_nInBufWord = LittleDWord(*(m_pDataIn++)); + } + } +} + +void CBitRead::FetchNext() +{ + m_nBitsAvail = 32; + GrabNextDWord(false); +} + +uint32 CBitRead::ReadUBitLong(int numbits) +{ + if (m_nBitsAvail >= numbits) + { + unsigned int nRet = m_nInBufWord & s_nMaskTable[numbits]; + m_nBitsAvail -= numbits; + if (m_nBitsAvail) + { + m_nInBufWord >>= numbits; + } + else + { + FetchNext(); + } + return nRet; + } + else + { + uint32 nRet = m_nInBufWord; + numbits -= m_nBitsAvail; + GrabNextDWord(true); + + if (IsOverflowed()) + return 0; + + nRet |= ((m_nInBufWord & s_nMaskTable[numbits]) << m_nBitsAvail); + m_nBitsAvail = 32 - numbits; + m_nInBufWord >>= numbits; + + return nRet; + } +} + +int CBitRead::ReadSBitLong(int numbits) +{ + int nRet = ReadUBitLong(numbits); + return (nRet << (32 - numbits)) >> (32 - numbits); +} + +int CBitRead::ReadByte() +{ + return ReadSBitLong(sizeof(unsigned char) << 3); +} + +int CBitRead::ReadChar() +{ + return ReadSBitLong(sizeof(char) << 3); +} + +bool CBitRead::ReadString(char* pStr, int maxLen, bool bLine, int* pOutNumChars) +{ + assert(maxLen != 0); + + bool bTooSmall = false; + int iChar = 0; + while (1) + { + char val = ReadChar(); + if (val == 0) + break; + else if (bLine && val == '\n') + break; + + if (iChar < (maxLen - 1)) + { + pStr[iChar] = val; + ++iChar; + } + else + { + bTooSmall = true; + } + } + + // Make sure it's null-terminated. + pStr[iChar] = 0; + + if (pOutNumChars) + *pOutNumChars = iChar; + + return !IsOverflowed() && !bTooSmall; +} + +bool CBitRead::Seek(int nPosition) +{ + bool bSucc = true; + if (nPosition < 0 || nPosition > m_nDataBits) + { + SetOverflowFlag(); + bSucc = false; + nPosition = m_nDataBits; + } + int nHead = m_nDataBytes & 3; // non-multiple-of-4 bytes at head of buffer. We put the "round off" + // at the head to make reading and detecting the end efficient. + + int nByteOfs = nPosition / 8; + if ((m_nDataBytes < 4) || (nHead && (nByteOfs < nHead))) + { + // partial first dword + uint8 const* pPartial = (uint8 const*)m_pData; + if (m_pData) + { + m_nInBufWord = *(pPartial++); + if (nHead > 1) + m_nInBufWord |= (*pPartial++) << 8; + if (nHead > 2) + m_nInBufWord |= (*pPartial++) << 16; + } + m_pDataIn = (uint32 const*)pPartial; + m_nInBufWord >>= (nPosition & 31); + m_nBitsAvail = (nHead << 3) - (nPosition & 31); + } + else + { + int nAdjPosition = nPosition - (nHead << 3); + m_pDataIn = reinterpret_cast ( + reinterpret_cast(m_pData) + ((nAdjPosition / 32) << 2) + nHead); + if (m_pData) + { + m_nBitsAvail = 32; + GrabNextDWord(); + } + else + { + m_nInBufWord = 0; + m_nBitsAvail = 1; + } + m_nInBufWord >>= (nAdjPosition & 31); + m_nBitsAvail = min(m_nBitsAvail, 32 - (nAdjPosition & 31)); // in case grabnextdword overflowed + } + return bSucc; +} + +void CBitRead::StartReading(const void* pData, int nBytes, int iStartBit, int nBits) +{ + // Make sure it's dword aligned and padded. + assert(((unsigned long)pData & 3) == 0); + m_pData = (uint32*)pData; + m_pDataIn = m_pData; + m_nDataBytes = nBytes; + + if (nBits == -1) + { + m_nDataBits = nBytes << 3; + } + else + { + assert(nBits <= nBytes * 8); + m_nDataBits = nBits; + } + m_bOverflow = false; + m_pBufferEnd = reinterpret_cast (reinterpret_cast (m_pData) + nBytes); + if (m_pData) + Seek(iStartBit); + +} + inline int BitByte(int bits) { // return PAD_NUMBER( bits, 8 ) >> 3; diff --git a/r5dev/tier1/bitbuf.h b/r5dev/tier1/bitbuf.h index b69d70b5..3a288adb 100644 --- a/r5dev/tier1/bitbuf.h +++ b/r5dev/tier1/bitbuf.h @@ -9,9 +9,61 @@ typedef enum BITBUFERROR_NUM_ERRORS } BitBufErrorType; +#define LittleDWord(val) (val) + //----------------------------------------------------------------------------- // Used for serialization //----------------------------------------------------------------------------- +#pragma pack(push,1) +class CBitBuffer +{ +public: + CBitBuffer(void); + void SetDebugName(const char* pName); + const char* GetDebugName(); + bool IsOverflowed(); + void SetOverflowFlag(); + + //////////////////////////////////// + const char* m_pDebugName; + uint8_t m_bOverflow; + char gap_0x11[7]; + size_t m_nDataBits; + size_t m_nDataBytes; +}; +#pragma pack(pop) + +class CBitRead : public CBitBuffer +{ +public: + void GrabNextDWord(bool bOverFlowImmediately = false); + void FetchNext(); + + int ReadSBitLong(int numbits); + uint32 ReadUBitLong(int numbits); + + int ReadByte(); + int ReadChar(); + bool ReadString(char* pStr, int bufLen, bool bLine = false, int* pOutNumChars = nullptr); + + void StartReading(const void* pData, int nBytes, int iStartBit = 0, int nBits = -1); + + bool Seek(int nPosition); + + //////////////////////////////////// + uint32_t m_nInBufWord; + uint32_t m_nBitsAvail; + const uint32* m_pDataIn; + const uint32* m_pBufferEnd; + const uint32* m_pData; +}; + +class bf_read : public CBitRead +{ +public: + +}; + struct bf_write { public: