- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 754
- Reaction score
- 457
WarRock's packet system has finally evolved. While the old client used basic ASCII strings for game server communication, the recent structures have shifted to binary/uint8 formats. If you are building a private server or a specialized tool, you will need to handle the new packet alignment and the BattlEye heartbeat logic.
The Packet Shift
Previously, we looked at ASCII strings like TimeStamp PacketID 1 1 1 1 1 1 -1. The current protocol is structured differently:
BattlEye Heartbeat Data
Currently, Packet ID 31330 is used for the BE heartbeat. If you are monitoring the stream, you will see recurring blocks like this:
Hooking Send/Recv
To intercept these, a simple naked detour on the packet handler is sufficient. Here is a workable base using standard C++ structures and a basic detour function. Note that all packet structures are capped at 24576 bytes.
Hook Addresses & Logic
The following naked functions handle the ECX pointer to the packet class and log the ID/Timestamp. These offsets are for the current build, but the logic remains consistent.
Technical Implementation Notes
If you possess an older client from before the packet system overhaul, you can map the old ASCII packet logic to the new uint8 parameters fairly easily. The maximum class size is 24576, which is more than enough for any game action. This was originally part of a private project that I have since abandoned, so feel free to use it for your own reversing efforts.
Anyone else found significant changes in the RetCodes recently?
The Packet Shift
Previously, we looked at ASCII strings like TimeStamp PacketID 1 1 1 1 1 1 -1. The current protocol is structured differently:
- TimeStamp (DWORD)
- PacketID (__int16)
- RetCode (__int8)
- Parameters (typically uint8 sequences)
BattlEye Heartbeat Data
Currently, Packet ID 31330 is used for the BE heartbeat. If you are monitoring the stream, you will see recurring blocks like this:
[Structure Packet 57e4cbcc] Packet ID 31330 error code 264 vº¿Ãpå!n˜7nvíúNžt<*逥ù aå°ð2š;KÌÕ;jÕÚ¡(¼÷m0hQêN3fµã@y
S¥ëu˜ëè“’ëìÊO£Ô}¦êá £Äpæ—ƒ¢{“Eˆ+8 f‡¸Û¢ÆC÷í ÀMŒgD‰ŽÓx:Ûî*ëLöñü£T¢ öáHOðÿ¼NF>l¤‘O^.g-å©R¢6,ÀXÓôO…´ŒÏ“¢^Xóºå™ƒÞK :ôN4CgVÀzF ¤
S¥ëu˜ëè“’ëìÊO£Ô}¦êá £Äpæ—ƒ¢{“Eˆ+8 f‡¸Û¢ÆC÷í ÀMŒgD‰ŽÓx:Ûî*ëLöñü£T¢ öáHOðÿ¼NF>l¤‘O^.g-å©R¢6,ÀXÓôO…´ŒÏ“¢^Xóºå™ƒÞK :ôN4CgVÀzF ¤
Hooking Send/Recv
To intercept these, a simple naked detour on the packet handler is sufficient. Here is a workable base using standard C++ structures and a basic detour function. Note that all packet structures are capped at 24576 bytes.
Code:
class cSendPacket
{
public:
DWORD dwTimeStamp; //0x0000
__int16 PacketID; //0x0004
__int8 RetCode; //0x0006
};
class cRecvPacket
{
public:
DWORD dwTimeStamp; //0x0000
__int16 PacketID; //0x0004
__int8 RetCode; //0x0006
};
// Basic Detour implementation
void* DetourCreate(BYTE* src, const BYTE* dst)
{
int len = 5;
BYTE* jmp = (BYTE*)malloc(len + 5);
DWORD dwBack;
VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &dwBack);
memcpy(jmp, src, len);
jmp += len;
jmp[0] = 0xE9;
*(DWORD*)(jmp + 1) = (DWORD)(src + len - jmp) - 5;
src[0] = 0xE9;
*(DWORD*)(src + 1) = (DWORD)(dst - src) - 5;
VirtualProtect(src, len, dwBack, &dwBack);
return(jmp - len);
}
Hook Addresses & Logic
The following naked functions handle the ECX pointer to the packet class and log the ID/Timestamp. These offsets are for the current build, but the logic remains consistent.
Code:
static DWORD dwRecvECX = 0;
DWORD dwRecvJmp = 0x8608D2;
DWORD dwRecvCall = 0x86F4CC;
__declspec (naked) void HookRecv (void)
{
__asm mov dwRecvECX, ecx
pRecvPacket = (cRecvPacket*)dwRecvECX;
if (pRecvPacket)
{
// Access pRecvPacket->TimeStamp and pRecvPacket->PacketID here
}
__asm call[dwRecvCall]
__asm jmp[dwRecvJmp]
}
// Injection
DetourCreate((BYTE*)0x8608CD, (BYTE*)HookRecv);
DetourCreate((BYTE*)0x86076D, (BYTE*)HookSend);
Technical Implementation Notes
If you possess an older client from before the packet system overhaul, you can map the old ASCII packet logic to the new uint8 parameters fairly easily. The maximum class size is 24576, which is more than enough for any game action. This was originally part of a private project that I have since abandoned, so feel free to use it for your own reversing efforts.
Anyone else found significant changes in the RetCodes recently?