• 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!

Connecting to cip server 7.7 - authenticating with the game server fails

e.e

Divine Intellect
Joined
Sep 16, 2016
Messages
476
Solutions
1
Reaction score
226
Hello.

I'm trying to connect to a cip server 7.7 using OTC.
Connecting with cip client works.
I set OTSERV_RSA in modules/gamecore/const.lua to cip client 7.7's public RSA key which is
Code:
1429962396241639952007017738289889555079540334546615321747051608293473758277603888296721338620
4600674145392845853859217990626450972452084065728686565926568763097919597040472189120184779200
2125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762
033017096809910315212883967
I then get to char list, but upon trying to log in with a character OTC hangs and I see this error in console
Code:
ERROR: invalid decrypted network message
C++ stack traceback:
	[C++]: Protocol::xteaDecrypt
	./otclient(Protocol::xteaDecrypt(stdext::shared_object_ptr<InputMessage> const&)+0x234) [0x7ced52]
	./otclient(Protocol::internalRecvData(unsigned char*, unsigned short)+0x240) [0x7cf0ba]
	./otclient(std::_Function_handler<void (unsigned char*, unsigned short), std::_Bind<std::_Mem_fn<void (Protocol::*)(unsigned char*, unsigned short)> (stdext::shared_object_ptr<Protocol>, std::_Placeholder<1>, std::_Placeholder<2>)> >::_M_invoke(std::_Any_data const&, unsigned char*, unsigned short)+0x47) [0x7d045b]
	./otclient(Connection::onRecv(boost::system::error_code const&, unsigned long)+0x6a) [0x7c25d0]
	./otclient(boost::asio::detail::read_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, std::_Bind<std::_Mem_fn<void (Connection::*)(boost::system::error_code const&, unsigned long)> (stdext::shared_object_ptr<Connection>, std::_Placeholder<1>, std::_Placeholder<2>)> >::operator()(boost::system::error_code const&, unsigned long, int)+0x2fb) [0x7cb827]
	./otclient(boost::asio::detail::reactive_socket_recv_op<boost::asio::mutable_buffers_1, boost::asio::detail::read_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, std::_Bind<std::_Mem_fn<void (Connection::*)(boost::system::error_code const&, unsigned long)> (stdext::shared_object_ptr<Connection>, std::_Placeholder<1>, std::_Placeholder<2>)> > >::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long)+0x101) [0x7cb949]
	./otclient(boost::asio::detail::task_io_service::poll(boost::system::error_code&)+0x320) [0x7c5dbe]
	./otclient(Connection::poll()+0x50) [0x7bfd48]
ERROR: failed to decrypt message
C++ stack traceback:
	[C++]: Protocol::internalRecvData
	./otclient(Protocol::internalRecvData(unsigned char*, unsigned short)+0x283) [0x7cf0fd]
	./otclient(std::_Function_handler<void (unsigned char*, unsigned short), std::_Bind<std::_Mem_fn<void (Protocol::*)(unsigned char*, unsigned short)> (stdext::shared_object_ptr<Protocol>, std::_Placeholder<1>, std::_Placeholder<2>)> >::_M_invoke(std::_Any_data const&, unsigned char*, unsigned short)+0x47) [0x7d045b]
	./otclient(Connection::onRecv(boost::system::error_code const&, unsigned long)+0x6a) [0x7c25d0]
	./otclient(boost::asio::detail::read_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, std::_Bind<std::_Mem_fn<void (Connection::*)(boost::system::error_code const&, unsigned long)> (stdext::shared_object_ptr<Connection>, std::_Placeholder<1>, std::_Placeholder<2>)> >::operator()(boost::system::error_code const&, unsigned long, int)+0x2fb) [0x7cb827]
	./otclient(boost::asio::detail::reactive_socket_recv_op<boost::asio::mutable_buffers_1, boost::asio::detail::read_op<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >, boost::asio::mutable_buffers_1, boost::asio::detail::transfer_all_t, std::_Bind<std::_Mem_fn<void (Connection::*)(boost::system::error_code const&, unsigned long)> (stdext::shared_object_ptr<Connection>, std::_Placeholder<1>, std::_Placeholder<2>)> > >::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long)+0x101) [0x7cb949]
	./otclient(boost::asio::detail::task_io_service::poll(boost::system::error_code&)+0x320) [0x7c5dbe]
	./otclient(Connection::poll()+0x50) [0x7bfd48]
	./otclient(Application::poll()+0x9) [0x72a79d]
Someone then told me in a different post that since I got to character list that the problem I'm having is happening while connecting to the game server.
I only confirmed this yesterday when I did some network traffic analysis comparing successful login with cip client vs failed login with OTC client.
Here's a summary of what I noticed:
Connecting to the login server:
  • Although the data patterns are a bit different between cip client vs OTC client (see reference 1), the data size of the packets sent back and forth are the same between the clients, and the number of packets and TCP flags are the same.
  • It seems that both clients authenticates successfully as there's nothing too unexpected and the clients proceeds to connecting to the game server with same-sized data packets sent after handshake.
Connecting to the game server:
  1. The data patterns differs like before between the clients (see reference 2).
  2. Additionally the first data packet sent differs by 4 bytes between the two clients (cip 131 vs OTC 135) (see reference 2).
  3. The server responds with a data packet of size 1276 bytes to the cip client which successfully logs in, while it responds with a data packet of size 42 bytes to OTC.
  4. In OTC's case OTC then tries to send a 10 byte size packet that always starts with the hex data 0800 and then sends a FIN ACK which the server responds to with a ACK and then a RST ACK once I exit OTC.
    In cip client's case after the server sends a 1276 bytes sized data packet the server sends another 934 bytes and then it looks like the client is connected as the server and client are sending small, variable-sized data packets back and forth.
  5. One more strange anomaly I noticed is that the game server's 1276 byte packet sent to the cip client does not have a PUSH flag, just ACK. This can't be the cause of the problem with OTC connecting thus far though since it hasn't even gotten to the point of receiving any 1276 byte packet (or any other packets with any data and no PUSH flag for that matter).

References
1)
cip client connection #1 (first data packet to login server)
Code:
91000102000203335a9d43be529843d8c8504428df9dd16b89c20f579509c0a70a5c4a201bf851728cccaa476018d41984c0db22a36fce45f1bfd4c5588b66c0417da93b83b9a4ea0196dc66134640334ab2b0e5521be91518ac5aec2d02cf83c4d18f7e5777d7159479881feeffbef0ab87ba933774d58b65d94a60f65f48cb2594f28f117bc693e5a6af1674a8e8306105d3

cip client connection #2 (first data packet to login server)
Code:
91000102000203335a9d43be529843d8c85044c73394542460e57cdd6fbe34781e236f7b8748899753c5c4a23daac31377658d65fa33759de34dc42900a80f4adfb6307167a3c8bff4c5d2e0082d9131c64be8685fceeb7fd5d3da3d15763074e28c3602473b55b17b10a33414316ada07104b7eb5c00251157c08f27fd1b5861a90eb5b7ffe0e548b7f601f6649c6ab7c8001

OTC connection #1 (first data packet to login server)
Code:
9100010b000203335a9d43be529843e7ddc5563e7ad03363a21f5059f68d6f251af89781704a9baefb7ccfe40e578d97d2eeeef0961edbb54841c7dd00b987f27fbd81e57ec65eda55a8174a27fc4cbd297351327ff4953eeeedb7a5c0b6e7e5ce4eae9bc095ad30c8d537d9299fe2a58c202f08dacf650958eccba2024cabba6e3d94397c35523fbfe98f3a0bd458e0b1a547

OTC connection #2 (first data packet to login server)
Code:
9100010b000203335a9d43be529843e7ddc55659ee3e0ada2e84d77a420f0735dfc4c81b1bd3127dbc64579b5fc0250665b76916e47f0a6c57468a5a1ad78d2ea6021ceaf86e5829243ae93426af44f011035a9dc0e1438962d360e2ccb5949cf5b709b7c385353c762163e7c5d92074f511b57faf5a7517ece267d40f18005ca5402ce433004459cf73e9eb6932c5eba453f3

2)
cip client connection #1 (first data packet to game server):
Code:
81000a90837ced216f8fbdbf6ecea4075698c4457b0711ba60d49b92d30be5e8470c2efe1ad4d3cec945c18ca1360f02b2b471f3c879612fbe93e46b6002d34992751cec39305c6d7379a02481152a1fc9907e9541f33ab05eb1d55d76e78c5f8e94525461e814a6cc7f593581c5c1e6823250e449ade38ff419d3b7c98507039ba673
cip client connection #2 (first data packet to game server):
Code:
81000a648564e588bf5a50d13b17662cf7911c1252ae4888bde0a8f45dbdd3ec2879b194bbeab593fd8ff6eee2624a18d0120a1b9ff99cb095d3abb5bf4fa602e2cebd171b60392d680bfe0e1d65aaf8123c8afe1e339467f74a9c5692139c625162b8fd3468b0a83b1b637cd7ebfd52279db9e0a55ccd8d9ee38f1e855d1c9507cfc6
OTC connection #1 (first data packet to game server):
Code:
85000a0b00020342285b3113d3e3aa5b09c438dd1743a529f86d32aed6137890f059eb9ffc5f04655a9e7adec2588c5dc1344f0348314a84fb936451f8eeb21cad86ebfa7144a462d810cf146bbc486a8a7358880012a476cd239a253ffa2fca0320f34624f576274256a9eb79d8e9a9ce0c0ca2cb2cb09945f73d9ef21b7fbe94f2ee66ab688b
OTC connection #2 (first data packet to game server):
Code:
85000a0b0002032a1da88ec4de9cb4f2f2af50d7958cdceba1e0b3ec3bee55a4c993563f1119b72f514efb2be4dcdf38d29698e901aaee9e3d9dcc7af81604af29c378ca2323750560fa619485e8a20f485dd6fef8ae2ba57fa41305172a2ba0f21e6d7617ed648a6fd87129f98e57046adf94505665737c9223ec96129f61e6992e259fabb335



Interested in ideas, suggestions or solutions!
 
Last edited:
I'll bump once just to say that I forgot to mention earlier that I'll gladly pay for both helpful suggestions that I can confirm as helping me towards a solution, and for solutions/answers themselves.

I'm not rich, but I'm not stingy either.
In my first and last deal here on otland we agreed to a price of €5 just for some info he had readily available and some answers to some simple questions, and I paid him €15 anyway.

In general my motto is to be fair, so I'll pay whatever I think is fair respective to the quantity and quality of any help provided (relative to my wallet size, of course).

For this task I'm thinking of prizes between €1-5 for useful/spot-on (and sufficiently detailed) suggestions that I didn't already know and helps me towards a solution and €5-10 for confirmed partial or full solutions (up to €20 if it takes more work than I expected).
 
Last edited:
What is cip private RSA exponent (aka private key)?

To solve this you need to understand how it works.

OTCLIENT generates 4 xtea keys which are uint32_t and encrypts them like almost whole message using RSA.
Server decrypts these keys and sets them for a connection, which is no longer RSA encrypted, but XTEA encrypted. So the client generates them and server saves. If you get XTEA decrypt error on the client-side, the server has to answer with incorrectly encrypted XTEA message (different keys). Maybe OTCLIENT protocol for 7.7 first game packet differs from Cipsoft's (each byte does matter). There is no RSA error, so I guess it is set correctly.
 
@masteuszx

Both the Login Server and Game Server's private RSA keys are:
const char* p("12017580013707233233987537782574702577133548287527131234152948150506251412291888866940292054989907714155267326586216043845592229084368540020196135619327879");
const char* q("11898921368616868351880508246112101394478760265769325412746398405473436969889506919017477758618276066588858607419440134394668095105156501566867770737187273");

(The login server's sources can also be found here: GitHub - HeavenIsLost/realotsloginserver)

Also it looks like the lines that are the most immediately relevant parts of the code in OTC that is triggering these errors are:
Code:
uint16 encryptedSize = inputMessage->getUnreadSize();
int sizeDelta = decryptedSize - encryptedSize;
if(sizeDelta > 0 || -sizeDelta > encryptedSize) {
        g_logger.traceError("invalid decrypted network message");
        return false;
    }
and
Code:
if(!xteaDecrypt(m_inputMessage)) {
            g_logger.traceError("failed to decrypt message");
            return;
        }
in otclient/src/framework/net/protocol.cpp

I agree that something seems to be going wrong with the first packet sent to the game server.
And like you say the protocol does seem practically to differ slightly in my opinion also, with both consistently different payload sizes (OTC 135 vs cip 131) and consistently different payload headers 85000a0b000203 vs 81000a.
In fact those headers are 4 bytes different in size, so they are definitely key.

No idea how these are generated or what they represent though.

Is it ProtocolGame::sendLoginPacket that is generating all of these auth-stage packets?
Do you know from which part of this function/lines packets are being sent to the game server?
 
Last edited:
A bit of self advertisement: Did you try to take a look at it in Wireshark's Tibia dissector?
Here's the RSA private key you can load for 7.70 (PEM format):
Code:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDLoibNahc41DeQhZUTth3FrhMNYek5xbQrlZ0O4BFr0LukdGY1
A3xHKAXt4d6Y539cQxpKqVPb+eSo44076c1PfL31BTDNFToN4uB3eUsOw3hROPSx
0Jkh72FAIhl6QynId0AHyrqeAgY59ZsTyXyJCSCWpFnl0WvK3Qf6nVBH/wIDAQAB
AoGAbaH7ahR4NCxtTe3deSNPnCJAN7BDNMtwWRbP3DZeqsFShRP2AwzBWD4AEzbL
9V6Nf0rRWOkZMRG1Pghk/aC+29reaole/CoTI+S0hKts4gXDkTHB5FfIzq7zXZVA
tHHH881l/ayhmGjlMqshM9YamT8jKMLZJlGfY+wKyehHecECQQDldLUeePo3RKZD
7+CVLFkTX/Yof8RLPOh1nukgftpvgaGcD1FXSwrIyKCHVAuwFnGgPfEr/2OAj60D
7Ya6XPeHAkEA4zC3U67ukzQxfyELrLTUBnsnKfkxejySsJ/M29SKCuHV2NOWxO8W
s9/c0RFvp0wI4uOxH00fIPYyI2h6TpxZyQJBAKQ0ZPktskKjCilMHPgkCIro/Yv2
A0+kgubJliP/I+rwZer8u0UxGsKdcOPnrYWSSjZWnaTS2y5Bo5tP/D6aETkCQDAc
AtZPtumpJcob/1LlP/jXX2W+BUIzTYTlcgYjLdA8HoK527V8Q7x3bVVAcfplWYRi
XwGX3T2npNpmp2+6IDECQBZuNbXiwHhDdsH7sSt36BGTFJNtVk3GJl/Bf50VrL6N
6NBNU3d4jQwArvSsanFYrZw+ayxS4rcJdZasO5mm+EI=
-----END RSA PRIVATE KEY-----
 
I confirmed that XTEA keys are encrypted correctly:
s8p7jEx.png


Something is wrong with setting the client. So I looked further....

I cut all packets to 128 bytes and attempted to decode

Cipsoft
Code:
90837ced216f8fbdbf6ecea4075698c4457b0711ba60d49b92d30be5e8470c2efe1ad4d3cec945c18ca1360f02b2b471f3c879612fbe93e46b6002d34992751cec39305c6d7379a02481152a1fc9907e9541f33ab05eb1d55d76e78c5f8e94525461e814a6cc7f593581c5c1e6823250e449ade38ff419d3b7c98507039ba673
-3 bytes

Cip RSA decrypted (so I did it okay):
Code:
0xcc 0xce 0x1c 0x22 0x6d 0xec 0xfd 0x0c 0x4b 0xbc 0x8f 0x93 0x40 0x7f 0x9c 0xa9 0x02 0x00 0x02 0x03 0x00 0x01 0x00 0x00 0x00 0x0c 0x00 0x42 0x75 0x73 0x74 0x65 0x72 0x64 0x72 0x61 0x67 0x6f 0x6e 0x00 0x00 0x69 0x8d 0xb9 0xd9 0x3d 0xbc 0x70 0x2d 0x19 0xe2 0x0c 0x6b 0x6a 0x0a 0x82 0x82 0xf4 0x38 0xd1 0x4a 0xc5 0x94 0x92 0xc2 0x67 0xcb 0xd1 0x9c 0x5a 0xb3 0x1b 0xfb 0xc1 0x2e 0xd8 0x7b 0x5c 0x50 0xdc 0x72 0xb6 0xc4 0x38 0x75 0xa0 0x6b 0x56 0x1e 0xdd 0x47 0x45 0x0d 0x1f 0x98 0x51 0x2d 0x33 0x44 0xd7 0x38 0x78 0x7c 0x57 0x7e 0x95 0x9d 0xcb 0xe2 0x6b 0x54 0x37 0x25 0x49 0x06 0x82 0x67 0x5f 0x6f 0x84 0xea 0x76 0x89 0x60 0x1a 0xde 0xaf

OTC game packet cut to 128bytes
Code:
42285b3113d3e3aa5b09c438dd1743a529f86d32aed6137890f059eb9ffc5f04655a9e7adec2588c5dc1344f0348314a84fb936451f8eeb21cad86ebfa7144a462d810cf146bbc486a8a7358880012a476cd239a253ffa2fca0320f34624f576274256a9eb79d8e9a9ce0c0ca2cb2cb09945f73d9ef21b7fbe94f2ee66ab688b
-7 bytes

rsa decrypt success:
Code:
0xae 0x57 0xbd 0x3c 0x6c 0x39 0xec 0x7e 0x91 0xbc 0x84 0x78 0xfb 0xd9 0x3f 0xad 0x00 0x01 0x00 0x00 0x00 0x0c 0x00 0x42 0x75 0x73 0x74 0x65 0x72 0x64 0x72 0x61 0x67 0x6f 0x6e 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

By the way, spot the differences that occurs further.... :)

CIPSOFT PACKET:
1gkMnLI.png



OTC PACKET:
Rjihud7.png



SO THE SOLUTION IS TO CUT 4 BYTES FROM GAME PACKET FROM THE BEGINNING

This will eliminate XTEA error. But you need to analyze each byte for what it is and recreate the packet. To be honest, I have done most work for you. You need to change bytes sequence and recompile OTC.

By the way, a bonus from me, the packet in Cip login 02 03 states for a version:
xhrDMFW.png
 
Last edited:
@masteuszx

I appreciate your input a lot!
Though we may not completely agree about that workload estimate.
I'll PM you later about payment after doing more testing if I can confirm that your advice is true.

I'm still not sure which parts of the sources are involved with communicating with the game-server, and this is most crucial.
But I'll take another look at protocolgamesend.cpp soon and experiment a little bit to see if it's there and then post an update here later.
 
Last edited:
yes it is there and it is in protocolgamesend.cpp in sendLoginPacket func

start with cutting
Code:
    msg->addU16(g_game.getOs());
    msg->addU16(g_game.getProtocolVersion());

    if(g_game.getFeature(Otc::GameClientVersion))
        msg->addU32(g_game.getClientVersion());

    if(g_game.getFeature(Otc::GameContentRevision))
        msg->addU16(g_things.getContentRevision());

    if(g_game.getFeature(Otc::GamePreviewState))
        msg->addU8(0);

As the protocol version is inside the rsa (past offset setting). You need basic networking knowledge to solve this...
 
@masteuszx That timing of your post! (I entered the thread about 10 minutes after to make this post)

I've been spending the last few hours trying to figure it out using @.: Strike :.'s wireshark Tibia protocol.
I've cut parts out and changed some values to make OTC send the exact same data (except for RSA) as cip client.
I no longer get the same problems as before, BUT I'm confronted with a new, probably deeper problem:
As I login with OTC the GUI presents a dialog box with the message "Login failed due to corrupt data." and the game server responds to OTC's char login packet with a dismissive 42 bytes of "random" (probably encrypted?) data followed up by ACK,FIN :(
Probably the same 42 bytes payload packet that it was sending earlier, only now the network session ends cleanly.

Another detail that makes this problem seem more difficult is that OTC is no longer producing any debug or error messages to STDOUT when this login failure happens, and I can't find anything about this error message in the sources or modules.
The only line I could fine that contains 'corrupt data' is this:
Code:
src/client/thingtype.cpp:        stdext::throw_exception(stdext::format("corrupt data (id: %d, category: %d, count: %d, lastAttr: %d)"

I don't know/understand how Xtea works or what exactly it is yet, but it seems to me now that everything else is taken care of that the problem must be with the 128 bytes of encrypted data/key negotiation or whatever it's called itself :/

Also I haven't been able to decrypt any packets yet so I can't take a look at what the server's response is to OTC and more.
I tried using the XTEA keys/segments together with wireshark + Tibia protocol extension, but the extension wouldn't/failed to decrypt packets ("XTEA-encrypted length exceeds packet" error no matter what with both cip client packets and OTC packets, with or without XTEA keys provided), I couldn't find out how to use the pem file as wireshark's SSL protocol has been changed and the Tibia protocol extension is missing parts, at least with my wireshark (v2.5.0), compared to screenshots provided by Strike in his thread such as the "RSA keys list..." option - an extension I'm otherwise loving I might add!
 
Last edited:
Everything else unchanged in this file:
Code:
void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRandom)
{
    OutputMessagePtr msg(new OutputMessage);

    msg->addU8(Proto::ClientPendingGame);
    //msg->addU16(g_game.getOs());
    //msg->addU16(g_game.getProtocolVersion());

    if(g_game.getFeature(Otc::GameClientVersion))
        msg->addU32(g_game.getClientVersion());

    if(g_game.getFeature(Otc::GameContentRevision))
        msg->addU16(g_things.getContentRevision());

    if(g_game.getFeature(Otc::GamePreviewState))
        msg->addU8(0);

    int offset = msg->getMessageSize();
    // first RSA byte must be 0
    msg->addU8(0);

    if(g_game.getFeature(Otc::GameLoginPacketEncryption)) {
        // xtea key
        generateXteaKey();
        msg->addU32(m_xteaKey[0]);
        msg->addU32(m_xteaKey[1]);
        msg->addU32(m_xteaKey[2]);
        msg->addU32(m_xteaKey[3]);
        msg->addU8(0); // is gm set?
    }

    if(g_game.getFeature(Otc::GameSessionKey)) {
        msg->addString(m_sessionKey);
        msg->addString(m_characterName);
    } else {
        if(g_game.getFeature(Otc::GameAccountNames))
            msg->addString(m_accountName);
        else
            msg->addU32(stdext::from_string<uint32>(m_accountName));

        msg->addString(m_characterName);
        msg->addString(m_accountPassword);

        if(g_game.getFeature(Otc::GameAuthenticator))
            msg->addString(m_authenticatorToken);
    }

    if(g_game.getFeature(Otc::GameChallengeOnLogin)) {
        msg->addU32(challengeTimestamp);
        msg->addU8(challengeRandom);
    }

    std::string extended = callLuaField<std::string>("getLoginExtendedData");
    if(!extended.empty())
        msg->addString(extended);

    // complete the bytes for rsa encryption with zeros
    int paddingBytes = g_crypt.rsaGetSize() - (msg->getMessageSize() - offset);
    assert(paddingBytes >= 0);
    msg->addPaddingBytes(paddingBytes);

    // encrypt with RSA
    if(g_game.getFeature(Otc::GameLoginPacketEncryption))
        msg->encryptRsa();

    if(g_game.getFeature(Otc::GameProtocolChecksum))
        enableChecksum();

    send(msg);

    if(g_game.getFeature(Otc::GameLoginPacketEncryption))
        enableXteaEncryption();
}
 
It will be plus-minus this:
C++:
void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRandom)
{
    OutputMessagePtr msg(new OutputMessage);
    msg->addU8(Proto::ClientPendingGame);
    int offset = msg->getMessageSize();
    // first RSA byte must be 0
    msg->addU8(0);
    generateXteaKey();
    msg->addU32(m_xteaKey[0]);
    msg->addU32(m_xteaKey[1]);
    msg->addU32(m_xteaKey[2]);
    msg->addU32(m_xteaKey[3]);
   
    msg->addU16(2); // windows OS 0x02 0x00
    // msg->addU16(g_game.getOs()); 0x0b 0x00
    msg->addU16(g_game.getProtocolVersion()); // 0x02 0x03
       
    msg->addU8(0); // is gm set? 0x00
   
    msg->addU32(stdext::from_string<uint32>(m_accountName));
    msg->addString(m_characterName);
   
    msg->addString(m_accountPassword);
   /*
   if(g_game.getFeature(Otc::GameChallengeOnLogin)) {
        msg->addU32(challengeTimestamp);
        msg->addU8(challengeRandom);
    }
    std::string extended = callLuaField<std::string>("getLoginExtendedData");
    if(!extended.empty())
        msg->addString(extended);
    */
   
    // complete the bytes for rsa encryption with zeros
    int paddingBytes = g_crypt.rsaGetSize() - (msg->getMessageSize() - offset);
    assert(paddingBytes >= 0);
    msg->addPaddingBytes(paddingBytes);
    // encrypt with RSA
    if(g_game.getFeature(Otc::GameLoginPacketEncryption))
        msg->encryptRsa();
    if(g_game.getFeature(Otc::GameProtocolChecksum))
        enableChecksum();
    send(msg);
    if(g_game.getFeature(Otc::GameLoginPacketEncryption))
        enableXteaEncryption();
}
 
Ahhh, I get it now I think!
I thought the OS and protocol version bytes were bad and should just be excluded as pertaining to communication with this game server because these bytes added to the size of the payload, so I was thinking this was the problem, but it needs to be added after the encryption instead!
I only added this/made some similar modifications for communication with the login server (f.ex changing OS byte from OTCLinux to Windows).

I still don't completely understand how all of this work, but I've learned a little bit now :p

Thank you so much, and it works now!
I'll PM you within soon/within a day regarding payment.
 
Last edited:
@masteuszxI couldn't find out how to use the pem file as wireshark's SSL protocol has been changed and the Tibia protocol extension is missing parts, at least with my wireshark (v2.5.0), compared to screenshots provided by Strike in his thread such as the "RSA keys list..." option - an extension I'm otherwise loving I might add!
Did you compile it yourself? I just tested the 64 builds for macOS and Windows and both have GnuTLS bundled and therefore the RSA Key List option.

If you're on Linux and compiling yourself, you need to install GnuTLS before building. e.g. on debian with:
Code:
sudo apt install libgnutls28-dev

I tried using the XTEA keys/segments together with wireshark + Tibia protocol extension, but the extension wouldn't/failed to decrypt packets ("XTEA-encrypted length exceeds packet" error no matter what

As long XTEA key and payload are of correct size, decryption can't fail. We can check the reported length after decryption though and if it's bigger than the number of bytes in the packet for real, something is wrong. That's what the error message means.

The option works if you supply the XTEA key in the same byte order it was in the login packet. I would guess you ran into endianness issues. You can copy the XTEA key out of the login packet from Wireshark GUI and paste it direcrtly into the XTEA keys UAT and check if that indeed was the case, or if it was a bug that could be fixed.
 
Last edited:
So if I understood correctly, Tibia 7.7 encrypted everything except of the packet length with RSA?
They would have changed it in 7.71 or 7.72 then, because I tested 7.72 before for the dissector and that decoded normally.

If there are indeed problems with the dissector's handling of 7.7, I wold appreciate if you could record a PCAP, where you try a wrong password. Then a correct password and choose a character and log in, say Hello World then logout. This would help me fix 7.7 support.
 
So if I understood correctly, Tibia 7.7 encrypted everything except of the packet length with RSA?
They would have changed it in 7.71 or 7.72 then, because I tested 7.72 before for the dissector and that decoded normally.

If there are indeed problems with the dissector's handling of 7.7, I wold appreciate if you could record a PCAP, where you try a wrong password. Then a correct password and choose a character and log in, say Hello World then logout. This would help me fix 7.7 support.
No, it is not the whole message. Please spot the:
Code:
msg->addU8(Proto::ClientPendingGame); // 0x0a
 
In the 770 client rsa decrypt should be called before clientos and version check but only in protocolgame (ClientPendingGame)
 
Back
Top