• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!
  • 2026 staff recruitment is open! Check it out and consider applying!
  • New resources must be posted under Resources tab. A discussion thread will be created automatically, you can't open threads manually anymore.
Resource icon

Challange-response between server and Tibianic-dll client

Gover

Well-Known Member
Joined
Sep 3, 2009
Messages
129
Reaction score
67
Gover submitted a new resource:

Challance-response between server and Tibianic-dll client - tibianic-dll, challance-response

Hi,
I would like to share my approach to forcing players to use my version of game client.
Since we have tibianic-dll we can actually do that, and prevent players from using otclient's etc.
I'm not a skilled programmer, so it can have some bugs.
It would be nice to hear your opinions on doing something like this on my server. Some code corrections are always really welcome.

So here it is.
main.cpp file in tibianic-dll: (above quest log for example)
C++:
     /* Client protection -...

I'm not sure if thats a good place to post this. If not please move it to correct place on the forum.

Read more about this resource...
 
I analyze few servers which use tibianic-dll and I see that they have this security even before characters list appear.
So I believe it can be rewriten to protocollogin.cpp - I will try to do it later and will post an update if I make it with success.
Post automatically merged:

The new approach, similar to other servers out there.

Server side:

protocollogin.h
C++:
        bool clientSignatureValid = false;

protocollogin.cpp before RSA_decrypt
C++:
    // Client verification: Check for signature in raw buffer BEFORE RSA decryption
    //
    // Server buffer structure (INITIAL_BUFFER_POSITION = 2 bytes for length header):
    // [2 length][1 type][2 OS][2 version][12 signatures][128 RSA][2 siglen][18 signature]
    // Signature position: 2 + 1 + 2 + 2 + 12 + 128 = 147
    //
    // msg.getLength() returns total packet size including length header
    // Official client: 167 bytes (2 + 1 + 2 + 2 + 12 + 128 + 2 + 18)
    // Unofficial client: 147 bytes (2 + 1 + 2 + 2 + 12 + 128)
 
    const uint8_t* buffer = msg.getBuffer();
    const size_t signaturePos = 2 + 1 + 2 + 2 + 12 + 128; // = 147
    const size_t expectedLengthWithSignature = 2 + 1 + 2 + 2 + 12 + 128 + 2 + 18; // = 167
 
    clientSignatureValid = false;
 
    // Check if packet has the expected length for signature
    if (msg.getLength() >= expectedLengthWithSignature) {
        // Read signature: 2 bytes length + 18 bytes "OfficialClient2025"
        uint16_t sigLen = buffer[signaturePos] | (buffer[signaturePos + 1] << 8);
    
        if (sigLen == 18) {
            std::string signature(reinterpret_cast<const char*>(&buffer[signaturePos + 2]), 18);
        
            if (signature == "OfficialClient2025") {
                clientSignatureValid = true;
            }
        }
    }

add information (also protocollogin.cpp)
C++:
    // Client verification: Check signature AFTER encryption is set up
    // This ensures the client can receive and decrypt the error message
    if (!clientSignatureValid) {
        disconnectClient("Client is outdated. Please download latest client.");
        return;
    }


Client side:
main.cpp inside case 0x01: { // Login packet before break
C++:
        // Append client signature AFTER the RSA block
        // Packet structure: [2 header][1 type][2 OS][2 version][12 signatures][128 RSA][2 siglen][18 signature]
        // RSA block ends at: header(2) + type(1) + OS(2) + version(2) + signatures(12) + RSA(128) = 147
        const int rsaBlockEnd = NetworkMessage::header_length + 1 + 2 + 2 + 12 + 128;
    
        // Append our signature: 2 bytes length + "OfficialClient2025"
        const char* signature = "OfficialClient2025";
        const int signatureLen = 18;
    
        *(uint16_t*)&buff[rsaBlockEnd] = signatureLen;
        memcpy(&buff[rsaBlockEnd + 2], signature, signatureLen);
    
        // Update packet length in header (excludes the 2-byte header itself)
        // Content: type(1) + OS(2) + version(2) + signatures(12) + RSA(128) + siglen(2) + signature(18) = 165
        const int contentLen = 1 + 2 + 2 + 12 + 128 + 2 + signatureLen;
        *(uint16_t*)buff = contentLen;
    
        // Update total buffer length (includes header)
        len = NetworkMessage::header_length + contentLen;

If you plan to set longer or shoter signature you can of course use:
const int signatureLen = strlen(signature); // Automatically calculate length

and edit the server
C++:
    const std::string expectedSignature = "OfficialClient2025";
    const uint8_t* buffer = msg.getBuffer();
    const size_t signaturePos = 2 + 1 + 2 + 2 + 12 + 128; // = 147
    const size_t minLengthWithSignature = signaturePos + 2; // At least position + 2 bytes for length field
   
    clientSignatureValid = false;
   
    // Check if packet has at least the signature length field
    if (msg.getLength() >= minLengthWithSignature) {
        // Read signature length (2 bytes)
        uint16_t sigLen = buffer[signaturePos] | (buffer[signaturePos + 1] << 8);
       
        // Check if the signature length matches our expected signature
        if (sigLen == expectedSignature.length() && msg.getLength() >= signaturePos + 2 + sigLen) {
            std::string signature(reinterpret_cast<const char*>(&buffer[signaturePos + 2]), sigLen);
           
            if (signature == expectedSignature) {


I have tested it on otclient and tibianic-dll client (without this code) and both returns. Any other official tibia client will crash anyway because of additional packets sent from server, so I was not bothering with testing them.

1771722628994.webp

1771722665046.webp

Any suggestions are very welcome.

Regards,
Gover
 
Last edited:
This is tibianic and server piece of code which restrict users to use your version of the client. Any other client like otclient, classic Tibia client with ip changed - will not work. It will return the message that client is outdated.
 
Last edited:
This is tibianic space waves and server piece of code which restrict users to use your version of the client. Any other client like otclient, classic Tibia client with ip changed - will not work. It will return the message that client is outdated.
this system of answering the player by distance seems to be interesting, but I think it needs to have a storage or count to repeat, imagine a player leaving and entering the screen, or several players arriving at the same time, or a war happening xD
 
this system of answering the player by distance seems to be interesting, but I think it needs to have a storage or count to repeat, imagine a player leaving and entering the screen, or several players arriving at the same time, or a war happening xD
The updated version in the post (not in resource) verify the signature during player login (before characters list appear in enter-game window). I do not think it would hurt server, if even 100 players would log-in at the same time. But I might be wrong - I did not tested it in bigger scale.
 
Back
Top