Solution for 'Fake R5 Client Checks on Mike CnR and other'

Hello UGBASE,
Iam releasing my solution for their new protection against 0.3.7 R5 Client Version spoof.
I was debugging both clients (R1 & R5) and discovered that on R5 there is 32 bits more sent via Clientjoin RPC also later if you bypass first part they checking client side via RPC_ClientCheck and obv u need to answer to that.
My solution code snippets:​
First part while sending ClientJoin:
C++:
    BitStream bsSend;
...
    bsSend.Write(iVersion);
    bsSend.Write(byteMod);
    bsSend.Write(static_cast<BYTE>(m_NickName.length()));
    bsSend.Write(m_NickName.c_str(), m_NickName.length());
    bsSend.Write(uiClientChallengeResponse);
    bsSend.Write(byteSerialLen);
    bsSend.Write(szSerial, byteSerialLen);
    bsSend.Write(byteClientVerLen);
    bsSend.Write(szClientVersion, byteClientVerLen);

// FIX R5
{
    unsigned char bits[] = { 0x6c, 0xb0, 0xa2, 0x70, 0x6f, 0x64, 0x5c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5c, 0x74, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x6e, 0x67, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0 };
    bsSend.WriteBits(bits, sizeof(bits));
}

    SendRPC(RPC_ClientJoin, bsSend, ...);

Second part after being connected (you need incomingRPC hook):
C++:
bool incomingRPC(unsigned char id, RPCParameters* rpcParams, void (*callback)(RPCParameters*))
{
...
    if (id == RPC_ClientCheck)
    {
        uint8_t type;
        uint32_t addr;
        uint16_t offset;
        uint16_t count;

        bsData.Read(type);
        bsData.Read(addr);
        bsData.Read(offset);
        bsData.Read(count);

        if (type == 0x5)
        {
            if (addr == 0x520190 || addr == 0x5e8606)
            {
                BitStream bs;
                bs.Write((uint8_t)0x5);
                bs.Write((uint32_t)addr);
                bs.Write((uint16_t)0xef38);
                bs.Write((uint16_t)0xc459);
                LocalClient->RPC(RPC_ClientCheck, &bs, HIGH_PRIORITY, RELIABLE, 0, false);
                return false;
            }
        }
    }
...
}

Thats it, all credits goes to me :)
#TeamExpl01T
Preview:
 

Expl01T3R

Active member
Joined
Nov 20, 2022
Messages
68
Reaction score
9
Location
Czech Republic
Important NOTE: This "FakeR5 client version spoof" doesn't work on samp.lsgyvenimas.lt:7777 (LSG).
They using some advanced ClientCheck protection or idk yet, gonna try to find solution also against their protection later. :cool:
 

Expl01T3R

Active member
Joined
Nov 20, 2022
Messages
68
Reaction score
9
Location
Czech Republic
Update for latest patch on Mike CnR:
~First part
C++:
bool CALLBACK outcomingRPC(stRakNetHookParams* params)
{
    if ( params->packetId == RPCEnumeration::RPC_ClientJoin)
    {
        int iVersion;
        byte byteMod;
        byte byteNameLen;
        char szNickName[32];
        uint uiChallengeResponse;
        byte byteAuthBSLen;
        char pszAuthBullshit[64];
        char cver[32];
        uint8_t cverlen;

        params->bitStream->ResetReadPointer();
        params->bitStream->Read(iVersion);
        params->bitStream->Read(byteMod);
        params->bitStream->Read(byteNameLen);
        params->bitStream->Read(szNickName, byteNameLen);
        params->bitStream->Read(uiChallengeResponse);
        params->bitStream->Read(byteAuthBSLen);
        params->bitStream->Read(pszAuthBullshit, byteAuthBSLen);
        params->bitStream->Read(cverlen);
        params->bitStream->Read(cver, cverlen);

        auto size = params->bitStream->GetNumberOfUnreadBits();
        unsigned char* bits = nullptr;
        if (size > 0)
        {
            bits = new unsigned char[size + 1];
            params->bitStream->ReadBits(bits, size);
        }

        params->bitStream->ResetReadPointer();
        
        // Modify values
        strcpy(cver, "0.3.7-R5");
        cverlen = strlen(cver);

        cver[cverlen] = '\0';
        pszAuthBullshit[byteAuthBSLen] = '\0';
        szNickName[byteNameLen] = '\0';

        params->bitStream->ResetWritePointer();
        params->bitStream->Write(iVersion);
        params->bitStream->Write(byteMod);
        params->bitStream->Write(byteNameLen);
        params->bitStream->Write(szNickName, byteNameLen);
        params->bitStream->Write(uiChallengeResponse);
        params->bitStream->Write(byteAuthBSLen);
        params->bitStream->Write(pszAuthBullshit, byteAuthBSLen);
        params->bitStream->Write(cverlen);
        params->bitStream->Write(cver, cverlen);

        if (size > 0)
        {
            params->bitStream->WriteBits(bits, size);
        }
        else
        {
            bits = new unsigned char[4] { 0x34, 0x83, 0x3, 0x2e };
            params->bitStream->WriteBits(bits, sizeof(bits));
        }

        SF->getSAMP()->getChat()->AddChatMessage(D3DCOLOR_XRGB(128, 235, 52), "[#TE] Spoofed client version to 0.3.7-R5 !");
    }
    return true;
}
 

Expl01T3R

Active member
Joined
Nov 20, 2022
Messages
68
Reaction score
9
Location
Czech Republic
Just found bypass for LSG :cool:
(but not alone -> inspired by blast hack thread)
Globally init:
C++:
DWORD dwGTAModule;
unsigned char* ClearSAMPModule = nullptr;
unsigned char* ClearGTAModule = nullptr;
Then inside hook:
C++:
bool incomingRPC(unsigned char id, RPCParameters* rpcParams, void (*callback)(RPCParameters*))
{
    ...
    if (id == RPC_ClientCheck)
    {
        ...
        //samp.lsgyvenimas.lt:7777
        {
            if (type == 0x2)
            {
                BitStream bs;
                bs.Write((uint8_t)type);
                bs.Write((uint32_t)0x10000212);
                bs.Write((uint8_t)0x1);
                SF->getRakNet()->SendRPC(RPC_ClientCheck, &bs, HIGH_PRIORITY, RELIABLE, 0, false);
                return false;
            }

            if (ClearSAMPModule)
            {
                uint8_t result = 0;
                switch (type)
                {
                    case 0x5:
                    {
                        if (addr >= 0x400000 && addr <= 0x856E00)
                        {
                            result = readMemory(addr + offset - dwGTAModule + reinterpret_cast<DWORD>(ClearGTAModule), count);
                        }
                        break;
                    }

                    case 0x45:
                    {
                        if (addr <= 0xC3500)
                        {
                            result = readMemory(addr + offset + reinterpret_cast<DWORD>(ClearSAMPModule), count);
                        }
                        break;
                    }
                }

                if (result != 0)
                {
                    BitStream bs;
                    bs.Write((uint8_t)type);
                    bs.Write((uint32_t)addr);
                    bs.Write((uint8_t)result);
                    SF->getRakNet()->SendRPC(RPC_ClientCheck, &bs, HIGH_PRIORITY, RELIABLE, 0, false);
                    return false;
                }
            }
        }
    }
    ...
}
Into Dllmain or some init func / after game is launched (ALSO YOU NEED TO HAVE STORED 0.3.7-R5 SAMP.DLL SOMEWHERE):
C++:
dwGTAModule = reinterpret_cast<DWORD>(GetModuleHandleA("gta_sa.exe"));
ClearGTAModule = new unsigned char[0x856000 + 256 - 0x400000];
memcpy(ClearGTAModule, reinterpret_cast<void*>(dwGTAModule), 0x856000 + 256 - 0x400000);
ClearSAMPModule = reinterpret_cast<unsigned char*>(LoadLibraryExA("SAMPFUNCS\\FakeR5\\r5.dll"/*file path to 0.3.7-R5 samp.dll*/, NULL, DONT_RESOLVE_DLL_REFERENCES));
readMemory method:
C++:
unsigned char readMemory(int address, unsigned __int16 readSize)
{
    unsigned char result = 0;
    int i = 0;
    if (readSize)
    {
        do
            result ^= *(BYTE*)(i++ + address) & 0xCC;
        while (i != readSize);
    }
    return result;
}

Profit?
 
Top