struct CNetworkSerializerPB : public VClass
{
const char* unscopedName;
std::uint32_t categoryMask;
void* protobufBinding;
const char* groupName;
int16_t messageID;
uint8_t groupID;
uint8_t defaultBufferType;
};
/*
sample class layout:
CNetMessagePB<101,class CUserMessageAchievementEvent,13,1,1>
client.dll + 0x37d13d0(hierarchy CNetMessagePB<101,class CUserMessageAchievementEvent,13,1,1>: CNetMessage, CUserMessageAchievementEvent, google::protobuf::Message, google::protobuf::MessageLite)
client.dll + 0x37d1400 MI offset 32(hierarchy CUserMessageAchievementEvent: google::protobuf::Message, google::protobuf::MessageLite)
^^ lea rax,qword ptr ds:[rcx+0x20]
*/
class CNetMessage : public VClass
{
/*
00007FFCEF88DAD0 | 33D2 | xor edx,edx
00007FFCEF88DAD2 | 48:8D41 20 | lea rax,qword ptr ds:[rcx+20]
00007FFCEF88DAD6 | 48:85C9 | test rcx,rcx
00007FFCEF88DAD9 | 48:0F44C2 | cmove rax,rdx
00007FFCEF88DADD | C3 | ret
*/
static inline constexpr auto GetPbMessage_VFTable_INDEX = 2;
/*
00007FFCEF890050 | 48:83EC 48 | sub rsp,48
00007FFCEF890054 | 48:8B05 8D3B1D00 | mov rax,qword ptr ds:[7FFCEFA63BE8]
00007FFCEF89005B | 48:8B0D C6CC1C00 | mov rcx,qword ptr ds:[7FFCEFA5CD28]
00007FFCEF890062 | 48:85C0 | test rax,rax
00007FFCEF890065 | 75 32 | jne networksystem.7FFCEF890099
00007FFCEF890067 | 48:8B01 | mov rax,qword ptr ds:[rcx]
00007FFCEF89006A | 4C:8D05 A7AB1900 | lea r8,qword ptr ds:[7FFCEFA2AC18]
00007FFCEF890071 | C64424 30 00 | mov byte ptr ss:[rsp+30],0
00007FFCEF890076 | 41:B9 38000000 | mov r9d,38
00007FFCEF89007C | C64424 28 01 | mov byte ptr ss:[rsp+28],1
00007FFCEF890081 | BA 00000040 | mov edx,40000000
00007FFCEF890086 | 48:C74424 20 00000000 | mov qword ptr ss:[rsp+20],0
00007FFCEF89008F | FF50 10 | call qword ptr ds:[rax+10]
00007FFCEF890092 | 48:8905 4F3B1D00 | mov qword ptr ds:[7FFCEFA63BE8],rax
00007FFCEF890099 | 48:83C4 48 | add rsp,48
00007FFCEF89009D | C3 | ret
*/
static inline constexpr auto GetSerializer_VFTable_INDEX = 3;
public:
auto GetPbMessage() const
{
return CallVFunc<GetPbMessage_VFTable_INDEX, google::protobuf::Message*>();
}
auto GetSerializer()
{
return CallVFunc<GetSerializer_VFTable_INDEX, CNetworkSerializerPB*>();
}
};
class CNetChan;
class INetworkMessageProcessingPreFilter
{
protected:
enum EFilterResult : bool
{
ALLOW_MESSAGE,
DROP_MESSAGE,
};
virtual EFilterResult FilterMessage(CNetMessage*, CNetChan*) = 0;
};
class CNetChan : public VClass
{
private:
/*
target function assembly:
00007FFCEF8898F0 | 48:896C24 18 | mov qword ptr ss:[rsp+18],rbp
00007FFCEF8898F5 | 57 | push rdi
00007FFCEF8898F6 | 41:56 | push r14
00007FFCEF8898F8 | 41:57 | push r15
00007FFCEF8898FA | 48:83EC 20 | sub rsp,20
00007FFCEF8898FE | 4C:63B9 C0750000 | movsxd r15,dword ptr ds:[rcx+75C0]
00007FFCEF889905 | 48:8DB9 C8750000 | lea rdi,qword ptr ds:[rcx+75C8]
00007FFCEF88990C | 45:33C0 | xor r8d,r8d
00007FFCEF88990F | 48:8BEA | mov rbp,rdx
00007FFCEF889912 | 4C:8BF1 | mov r14,rcx
00007FFCEF889915 | 45:85FF | test r15d,r15d
00007FFCEF889918 | 7E 26 | jle networksystem.7FFCEF889940
00007FFCEF88991A | 48:8B07 | mov rax,qword ptr ds:[rdi]
00007FFCEF88991D | 41:8BD0 | mov edx,r8d
00007FFCEF889920 | 48:3928 | cmp qword ptr ds:[rax],rbp
00007FFCEF889923 | 74 11 | je networksystem.7FFCEF889936
00007FFCEF889925 | 41:FFC0 | inc r8d
00007FFCEF889928 | 48:FFC2 | inc rdx
00007FFCEF88992B | 48:83C0 08 | add rax,8
00007FFCEF88992F | 49:3BD7 | cmp rdx,r15
00007FFCEF889932 | 7C EC | jl networksystem.7FFCEF889920
00007FFCEF889934 | EB 0A | jmp networksystem.7FFCEF889940
00007FFCEF889936 | 41:83F8 FF | cmp r8d,FFFFFFFF
00007FFCEF88993A | 0F85 D0000000 | jne networksystem.7FFCEF889A10
00007FFCEF889940 | 44:3BB9 D0750000 | cmp r15d,dword ptr ds:[rcx+75D0]
00007FFCEF889947 | 0F85 B5000000 | jne networksystem.7FFCEF889A02
00007FFCEF88994D | F747 0C 00000040 | test dword ptr ds:[rdi+C],40000000
00007FFCEF889954 | 0F85 A8000000 | jne networksystem.7FFCEF889A02
00007FFCEF88995A | 8B4F 08 | mov ecx,dword ptr ds:[rdi+8]
00007FFCEF88995D | 48:895C24 40 | mov qword ptr ss:[rsp+40],rbx
00007FFCEF889962 | 48:897424 48 | mov qword ptr ss:[rsp+48],rsi
00007FFCEF889967 | 81F9 FEFFFF7F | cmp ecx,dbghelp.7FFFFFFE
00007FFCEF88996D | 7E 0B | jle networksystem.7FFCEF88997A
00007FFCEF88996F | BA 01000000 | mov edx,1
00007FFCEF889974 | FF15 4E7F1300 | call qword ptr ds:[<&UtlMemory_FailedAllocation>]
00007FFCEF88997A | 8B4F 08 | mov ecx,dword ptr ds:[rdi+8]
00007FFCEF88997D | 41:B9 08000000 | mov r9d,8
invoker:
00007FFCF5D106A0 | 48:895C24 10 | mov qword ptr ss:[rsp+10],rbx
00007FFCF5D106A5 | 48:897424 18 | mov qword ptr ss:[rsp+18],rsi
00007FFCF5D106AA | 48:897C24 20 | mov qword ptr ss:[rsp+20],rdi
00007FFCF5D106AF | 55 | push rbp
00007FFCF5D106B0 | 41:56 | push r14
00007FFCF5D106B2 | 41:57 | push r15
00007FFCF5D106B4 | 48:8BEC | mov rbp,rsp
00007FFCF5D106B7 | 48:83EC 50 | sub rsp,50
00007FFCF5D106BB | 48:8BF9 | mov rdi,rcx
00007FFCF5D106BE | 48:8BDA | mov rbx,rdx
00007FFCF5D106C1 | 48:8B0D C8385200 | mov rcx,qword ptr ds:[7FFCF6233F90]
00007FFCF5D106C8 | 45:33FF | xor r15d,r15d
00007FFCF5D106CB | 48:85FF | test rdi,rdi
00007FFCF5D106CE | 48:8D57 18 | lea rdx,qword ptr ds:[rdi+18]
00007FFCF5D106D2 | 48:8B01 | mov rax,qword ptr ds:[rcx]
00007FFCF5D106D5 | 49:0F44D7 | cmove rdx,r15
00007FFCF5D106D9 | FF90 20010000 | call qword ptr ds:[rax+120]
00007FFCF5D106DF | 48:8B03 | mov rax,qword ptr ds:[rbx]
00007FFCF5D106E2 | 48:8BCB | mov rcx,rbx
00007FFCF5D106E5 | FF90 D0010000 | call qword ptr ds:[rax+1D0]
00007FFCF5D106EB | 48:893D AE044C00 | mov qword ptr ds:[7FFCF61D0BA0],rdi
00007FFCF5D106F2 | 48:8D15 9F044C00 | lea rdx,qword ptr ds:[7FFCF61D0B98]
00007FFCF5D106F9 | 48:8B03 | mov rax,qword ptr ds:[rbx]
00007FFCF5D106FC | 48:8BCB | mov rcx,rbx
00007FFCF5D106FF | FF90 18020000 | call qword ptr ds:[rax+218] <---- invocation here
00007FFCF5D10705 | 48:8D05 F0180200 | lea rax,qword ptr ds:[7FFCF5D31FFC]
00007FFCF5D1070C | 48:8BCF | mov rcx,rdi
00007FFCF5D1070F | 48:8945 F8 | mov qword ptr ss:[rbp-8],rax
00007FFCF5D10713 | E8 0877FFFF | call engine2.7FFCF5D07E20
00007FFCF5D10718 | 48:8B0D 79385200 | mov rcx,qword ptr ds:[7FFCF6233F98]
00007FFCF5D1071F | 48:8D77 28 | lea rsi,qword ptr ds:[rdi+28]
invoker xref:
00007FFCF5D12374 | 48:63DA | movsxd rbx,edx
00007FFCF5D12377 | 48:8D15 3ABC3F00 | lea rdx,qword ptr ds:[7FFCF610DFB8] | 00007FFCF610DFB8:"server"
00007FFCF5D1237E | C74424 30 01000000 | mov dword ptr ss:[rsp+30],1
00007FFCF5D12386 | C74424 28 02000000 | mov dword ptr ss:[rsp+28],2
00007FFCF5D1238E | 48:8B01 | mov rax,qword ptr ds:[rcx]
00007FFCF5D12391 | 48:895424 20 | mov qword ptr ss:[rsp+20],rdx
00007FFCF5D12396 | 41:8BD2 | mov edx,r10d
00007FFCF5D12399 | FF90 B0000000 | call qword ptr ds:[rax+B0]
00007FFCF5D1239F | 48:8BD0 | mov rdx,rax
00007FFCF5D123A2 | 48:8BCE | mov rcx,rsi
00007FFCF5D123A5 | 48:8BF8 | mov rdi,rax
00007FFCF5D123A8 | E8 F3E2FFFF | call engine2.7FFCF5D106A0 <---- calls invoker
00007FFCF5D123AD | 48:8D4B 0A | lea rcx,qword ptr ds:[rbx+A]
00007FFCF5D123B1 | 48:8D0C49 | lea rcx,qword ptr ds:[rcx+rcx*2]
00007FFCF5D123B5 | 48:893CCE | mov qword ptr ds:[rsi+rcx*8],rdi
00007FFCF5D123B9 | 48:8B0D D01B5200 | mov rcx,qword ptr ds:[7FFCF6233F90]
00007FFCF5D123C0 | C786 4C020000 FFFFFFFF | mov dword ptr ds:[rsi+24C],FFFFFFFF
00007FFCF5D123CA | 48:8B01 | mov rax,qword ptr ds:[rcx]
00007FFCF5D123CD | FF90 D8000000 | call qword ptr ds:[rax+D8]
00007FFCF5D123D3 | 8B0D F7B65300 | mov ecx,dword ptr ds:[7FFCF624DAD0]
00007FFCF5D123D9 | 0F57C9 | xorps xmm1,xmm1
00007FFCF5D123DC | F2:0F5AC8 | cvtsd2ss xmm1,xmm0
00007FFCF5D123E0 | BA 01000000 | mov edx,1
00007FFCF5D123E5 | F3:0F118E 50020000 | movss dword ptr ds:[rsi+250],xmm1
00007FFCF5D123ED | FF15 35F43800 | call qword ptr ds:[<&LoggingSystem_IsChannelEnabled>]
00007FFCF5D123F3 | 84C0 | test al,al
00007FFCF5D123F5 | 74 18 | je engine2.7FFCF5D1240F
00007FFCF5D123F7 | 8B0D D3B65300 | mov ecx,dword ptr ds:[7FFCF624DAD0]
00007FFCF5D123FD | 4C:8D05 CCBC3F00 | lea r8,qword ptr ds:[7FFCF610E0D0] | 00007FFCF610E0D0:"CL: CNetworkGameClientBase::Connect() calling SetSignonState( SIGNONSTATE_CONNECTED )\n"
00007FFCF5D12404 | BA 01000000 | mov edx,1
00007FFCF5D12409 | FF15 11F43800 | call qword ptr ds:[<&LoggingSystem_Log>]
00007FFCF5D1240F | 48:8B06 | mov rax,qword ptr ds:[rsi]
00007FFCF5D12412 | 45:33C9 | xor r9d,r9d
00007FFCF5D12415 | 48:8BCE | mov rcx,rsi
*/
static inline constexpr auto RegisterFilter_VFTable_INDEX = 67;
static inline constexpr auto UnregisterFilter_VFTable_INDEX = RegisterFilter_VFTable_INDEX + 1;
public:
void RegisterFilter(INetworkMessageProcessingPreFilter& filter_LIFETIME_MANGED_BY_INVOKER)
{
CallVFunc<RegisterFilter_VFTable_INDEX>(&filter_LIFETIME_MANGED_BY_INVOKER);
}
void UnregisterFilter(INetworkMessageProcessingPreFilter& filter_LIFETIME_MANGED_BY_INVOKER)
{
CallVFunc<UnregisterFilter_VFTable_INDEX>(&filter_LIFETIME_MANGED_BY_INVOKER);
}
};
struct MyFilter : INetworkMessageProcessingPreFilter
{
EFilterResult FilterMessage(CNetMessage* msg, CNetChan*) override
{
if(const auto ser = msg->GetSerializer(); ser)
{
cout_line(std::format("msg id {} {}", ser->messageID, ser->unscopedName));
}
return EFilterResult::ALLOW_MESSAGE;
}
};
MyFilter filter{};//static(simpler) or local but must be alive at least until it's unregistered, RegisterFilter doesn't manage ownership
...
channel->RegisterFilter(filter);