Skip to main content

Client>Server Communication

There is no singular function that is responsible for this task. In Apex, we rather have multiple of those working together. 

Let's get started with the most common function, R5AC::PushViolation:

void __fastcall R5AC::PushViolation(
        const __m128i *pszIdentifierStr,
        char Severity,
        __int64 ExtraDataPtr,
        __int64 ExtraDataLen);

In case of client-side abnormalities being detected, it will be invoked like this:

LBL_ON_ABNORMALITY_FOUND:
              v119 = v116();
              v120 = 0;
              v121 = 0i64;
              do
              {
                v122 = *(_BYTE *)(v121 + 41489128); // these are just data and key, and the RVA can be computed with analysis of a runtime dump.
                ++v120;
                v123 = *(_BYTE *)(v121 + 27928337);
                v188[++v121 + 15] = v123 ^ v122;
              }
              while ( v120 < 0xC ); // C-String encryption using simple xor
              sprintf_s_2(Buffer, 0x104ui64, Format, v119);
            }
          }
          R5AC::PushViolation(v200, 1, v177, (__int64)Buffer);

It's purpose is to enqueue a new violation record into a list that is fetched and emptied by numerous callers, all originating from r5apex_dx12.exe. These are to be considered slaves because they simply check if there is any messages pending, use CLC_AntiCheat virtual method table to setup a new netmessage in source engine, and then puts the data of the anti-cheat violation/message into the netmsg and sends it to the game server.

__int64 __fastcall R5AC::PopAnticheatMsg(_WORD_WORD* *a1,outBuffer, intint* *a2)outSize)
{
    __int64 acContext = qword_5D553D0;

    v2// =Lock qword_5D553D0;for thread safety
    MEMORY[0x7FFE8D5790A0](&R5AC::ViolationLock);

    // Pre-processing hook (originally v2->vtable[3])
    (*(void (__fastcall *__fastcall**)(__int64))(*(_QWORD *)v2acContext + 24i64)24))(v2)acContext);

    if ( R5AC::NumSecondaryVIolations )> 0)
    {
        v4 = 0;
    for (int iidx = 0; iidx < R5AC::NumSecondaryVIolations; ++i )idx)
        {
            if__int64 ( *(_DWORD *)(32i64 * i + R5AC::SecondaryViolationList + 8) )
      {
        v6 = 0;
        *a1 = 0;
        v7 = a1 + 1;
        v8 = R5AC::NumSecondaryVIolations;
        for ( j = (__m128i *)(a1 + 1); v6 < v8; ++v6 )
        {
          v10violationEntry = R5AC::SecondaryViolationList + 32i6432LL * v6;idx;
            if ( *(_DWORD *_DWORD*)(v10violationEntry + 8)) // valid violation
            {
                int bufferOffset = 0;
                *outBuffer = 0; // null-terminate start
                _WORD* bufferPtr = outBuffer + 1;

                for (int j = 0; j < R5AC::NumSecondaryVIolations; ++j)
                {
                    __int64 entryAddr = R5AC::SecondaryViolationList + 32LL * j;
                    if (*(_DWORD*)(entryAddr + 8))
                    {
                        v11int msgLen = *(_DWORD *_DWORD*)(*(_QWORD *_QWORD*)v10entryAddr - 4i64)4);
                        ifint (extraLen (unsigned int)(v11 += *(_DWORD *_DWORD*)(*(_QWORD *_QWORD*)(v10entryAddr + 24) - 4i64)4);
                        int totalLen = msgLen + 15)extraLen + 15;

                        if (totalLen > (char *)(a1 + 512)512 - (charbufferOffset) *)j// )check overflow
                            break;

                        // Copy first message segment
                        sse_memcpy(j,bufferPtr, *(const __m128i *__m128i**)v10,entryAddr, (unsigned int)(v11msgLen + 1));

                        // Append metadata
                        v12char* metaPtr = &j->m128i_i8[*(_DWORD *char*)(*(_QWORD *)v10 - 4i64)bufferPtr + 1];msgLen + 1;
                        *(_DWORD *_DWORD*)v12metaPtr = *(_DWORD *_DWORD*)(v10entryAddr + 8);
                        v12[metaPtr[4] = *(_BYTE *_BYTE*)(v10entryAddr + 12);
                        *(_QWORD *_QWORD*)(v12metaPtr + 5) = *(_QWORD *_QWORD*)(v10entryAddr + 16);

                        // Copy second message segment
                        sse_memcpy(
              (__m128i *__m128i*)(v12metaPtr + 13), *(const __m128i**)(entryAddr + 24), extraLen + 1);

                        // Reset original entry to mark consumed
                        *(const __m128i **_DWORD*)(v10 + 24),
              (unsigned int)(*(_DWORD *)(*(_QWORD *)(v10 + 24) - 4i64) + 1));
            v13 = (unsigned int)(*(_DWORD *)(*(_QWORD *)(v10 + 24) - 4i64) + 14);
            *(_DWORD *)(v10entryAddr + 8) = 0;

                        // Move buffer pointer
                        jbufferPtr = (__m128i__m128i*)(metaPtr *)&v12[v13]+ 13 + extraLen + 1);
                        v8bufferOffset += R5AC::NumSecondaryVIolations;totalLen;
                    }
                }

                v14// =Final (_DWORD)jbuffer - (_DWORD)a1;size
                *a2outSize = v14;
bufferOffset;

                dwEncryptionKey// =Simple 0x42A1110F96B5E116i64;XOR "encryption" (skip first 2 bytes)
                if ( v14bufferOffset != 2 )2)
                {
                    douint64_t xorKey = 0x42A1110F96B5E116ULL;
                    for (int k = 0; k < bufferOffset - 2; ++k)
                    {
                        v7 = (_WORD *)((char *char*)v7outBuffer)[k + 1);
            v15 = v4++ & 7;
            *((_BYTE *)v7 - 1)2] ^= *((_BYTE *char*)&dwEncryptionKeyxorKey)[k +& v15)7];
                    }
                while}

( v4 < v14 - 2 );
        }
        v16 = 1;
        goto LABEL_15;
      }
    }
  }
  v16 = 0;
LABEL_15:
  (*(void (__fastcall **)(__int64))(*(_QWORD *)v2 + 32i64))(v2);

                MEMORY[0x7FFE8D562C70](&R5AC::ViolationLock);
                return v16;1; // message popped successfully
            }
        }
    }

    MEMORY[0x7FFE8D562C70](&R5AC::ViolationLock);
    return 0; // no message
}

_BYTE *__fastcall R5::BuildAnticheatMsg3(__int64 a1)
{
  if ( !*(_QWORD *)a1 || !*result )
    return result;
  v3 = 0;
  v4 = gpNetChan;
  if ( gpNetChan )
  {
    v14 = vft::CLC_AntiCheatMsg;
    v15 = 0;
    v17 = 0i64;
    v5 = 5;
    v16 = 1;
    // send all of them in bulk
    for ( i = 0; (unsigned __int8)R5AC::PopAnticheatMsg(v20, &i); --v5 )
    {
      if ( !v5 )
        break;
      v18 = v20;
      v19 = i;
      C_NetChan::SendNetMsg(v4, &v14, 0, 0);
      C_NetChan::SendDatagram(v4, 0i64);
    }
  }