1
// Function for outputting the buffer to the console
void print(const char* buffer, unsigned int length, bool sending)
{
    if (sending)
    {
        cout << "[Client > Server] ";
    }
    else
    {
        cout << "[Server > Client] ";
    }

    for (unsigned int i = 0; i < length; i++)
    {
        printf("%02X ", buffer[i]);
    }
        
    cout << endl;
}

/*
    VarInt read function
    https://wiki.vg/index.php?title=Protocol#VarInt_and_VarLong
*/

int readVarInt(char* buffer, unsigned int& offset)
{
    int numRead = 0;
    int result = 0;

    do
    {
        int value = (buffer[offset] & 0b01111111);
        result |= (value << (7 * numRead));
    }

    while ((buffer[offset++] & 0b10000000) != 0);

    return result;
}

// Intercepted WSARecv function
int WSAAPI __WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
    // Calling the original WSARecv function to fill the buffer
    int result = _WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpOverlapped, lpCompletionRoutine);

    for (unsigned int i = 0; i < dwBufferCount; i++)
    {
        unsigned int offset = 0;
        unsigned int length = readVarInt(lpBuffers[i].buf, offset);

        print(lpBuffers[i].buf, lpBuffers[i].len, false);
        print(lpBuffers[i].buf, length, false);
        cout << "Length: " << length << endl << endl;
    }

    return result;
}

// Intercepted WSASend function
int WSAAPI __WSASend(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
    for (unsigned int i = 0; i < dwBufferCount; i++)
    {
        unsigned int offset = 0;
        unsigned int length = readVarInt(lpBuffers[i].buf, offset);

        print(lpBuffers[i].buf, lpBuffers[i].len, true);
        print(lpBuffers[i].buf, length, true);
        cout << "Length: " << length << endl << endl;
    }

    // Calling the original WSASend function
    return _WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpOverlapped, lpCompletionRoutine);
}

Output https://pastebin.com/2VqYP7ZU (Sorry, there are many symbols)

As you can see, in the first two requests the length obtained by reading VarInt truncated only 1 byte (only one byte was not displayed) But in the third request (Encryption Request) a very large part was cut off. Why is that? Where is my mistake?

What does FFFFFF mean which is sometimes displayed?

Encryption and compression are not enabled yet, as this is the moment of authorization.

  • Regarding the unexpected FFs, [sign extension](https://en.wikipedia.org/wiki/Sign_extension). If you convert a character to a signed integer and that character happens to be negative, that negativity has to carry through to the integer. Quick solution: Use an unsigned type like `uint8_t`. – user4581301 May 13 '21 at 18:31
  • But this only affects the output in the console? Do you mean to change the type of the parameter in the print function from const char * to const unsigned char * ? – August Vishnevsky May 13 '21 at 18:39
  • Code looks a bit odd. It appears you are using overlapped IO and then processing the buffer without waiting for the completion function to be called. – user4581301 May 13 '21 at 18:46
  • Or do you mean that before reading (not outputting) the buffer, I need to convert it to unsigned char * ? – August Vishnevsky May 13 '21 at 18:46
  • I'd process `uint8_t` end to end or as close to it as possible. When dealing with raw binary information, sign should be ignored because sign is an interpretation of the data and you haven't reached the point where you want to interpret yet. – user4581301 May 13 '21 at 18:48
  • I just intercepted the send / receive functions that the game uses – August Vishnevsky May 13 '21 at 18:50
  • I do not understand what do you mean. :( I need to convert the buffer itself to unsigned char * before reading VarInt? – August Vishnevsky May 13 '21 at 18:51
  • For this usage I would replace `void print(const char* buffer, unsigned int length, bool sending)` with `void print(const unsigned char* buffer, unsigned int length, bool sending)` and cast as necessary in the calls Eg: `print((unsigned char*)lpBuffers[i].buf, lpBuffers[i].len, false);` – user4581301 May 13 '21 at 18:54
  • Totally unrelated side note: [Don't use two underscores in a row anywhere in an identifier unless you're writing "implementation" (compiler or Standard Library) code](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). – user4581301 May 13 '21 at 18:59
  • So I understood you correctly. Thank you. <3 – August Vishnevsky May 13 '21 at 18:59

0 Answers0