How to hook CGameClient::ProcessVoiceData?

Please post any questions about developing your plugin here. Please use the search function before posting!
User avatar
Doldol
Senior Member
Posts: 201
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

How to hook CGameClient::ProcessVoiceData?

Postby Doldol » Tue Nov 04, 2014 6:04 pm

I found this in engine_srv.so, and hooking this should allow me to block voicechat and even modify the voice packets?
I'm actually not sure, but though I should give it a go, now I can't find a signature for it, I did find some offsets, but I have no clue where to start from. (CGameClient seems to be a parent class to even CBaseClient??)

So, I'm trying to hook:
CGameClient::ProcessVoiceData(CLC_VoiceData *)

Found this:

Code: Select all

// CGameClient
// Auto reconstructed from vtable block @ 0x0022CC40// from "engine_srv.so", by ida_vtables.idc
0    CGameClient::~CGameClient
1    CGameClient::~CGameClient
2    CBaseClient::FireGameEvent
3    CBaseClient::GetPlayerSlot
4    CBaseClient::GetUserID
5    CBaseClient::GetNetworkID
6    CBaseClient::GetClientName
7    CBaseClient::GetNetChannel
8    CBaseClient::GetServer
9    CBaseClient::GetUserSetting
10    CBaseClient::GetNetworkIDString
11    CGameClient::Connect
12    CGameClient::Inactivate
13    CGameClient::Reconnect
14    CGameClient: :D isconnect
15    CGameClient::SetRate
16    CBaseClient::GetRate
17    CGameClient::SetUpdateRate
18    CBaseClient::GetUpdateRate
19    CGameClient::Clear
20    CBaseClient: :D emoRestart
21    CBaseClient::GetMaxAckTickCount
22    CGameClient::ExecuteStringCommand
23    CGameClient::SendNetMsg
24    CBaseClient::ClientPrintf
25    CBaseClient::IsConnected
26    CBaseClient::IsSpawned
27    CBaseClient::IsActive
28    CBaseClient::IsFakeClient
29    CBaseClient::IsHLTV
30    CBaseClient::IsReplay
31    CGameClient::IsHearingClient
32    CGameClient::IsProximityHearingClient
33    CBaseClient::SetMaxRoutablePayloadSize
34    CBaseClient::IsSplitScreenUser
35    CBaseClient::ProcessTick
36    CBaseClient::ProcessStringCmd
37    CBaseClient::ProcessSetConVar
38    CBaseClient::ProcessSignonState
39    CGameClient::ProcessClientInfo
40    CBaseClient::ProcessBaselineAck
41    CBaseClient::ProcessListenEvents
42    CGameClient::ProcessCmdKeyValues
43    CBaseClient::ConnectionStart
44    CGameClient::UpdateAcknowledgedFramecount
45    CGameClient::ShouldSendMessages
46    CBaseClient::UpdateSendState
47    CBaseClient::FillUserInfo
48    CGameClient::UpdateUserSettings
49    CGameClient::SetSignonState
50    CGameClient::WriteGameSounds
51    CGameClient::GetDeltaFrame
52    CGameClient::SendSnapshot
53    CBaseClient::SendServerInfo
54    CGameClient::SendSignonData
55    CGameClient::SpawnPlayer
56    CGameClient::ActivatePlayer
57    CBaseClient::SetName
58    CBaseClient::SetUserCVar
59    CBaseClient::FreeBaselines
60    CGameClient::IgnoreTempEntity
61    CGameClient::ConnectionClosing
62    CGameClient::ConnectionCrashed
63    CGameClient::PacketStart
64    CGameClient::PacketEnd
65    CGameClient::FileReceived
66    CGameClient::FileRequested
67    CGameClient::FileDenied
68    CGameClient::FileSent
69    CGameClient::ProcessMove
70    CGameClient::ProcessVoiceData
71    CGameClient::ProcessRespondCvarValue
72    CGameClient::ProcessFileCRCCheck
73    CGameClient::ProcessFileMD5Check
74    CGameClient::ProcessSaveReplay


Also found (but it's not relevant?):

Code: Select all

// CClientState
// Auto reconstructed from vtable block @ 0x002188E0// from "engine_srv.so", by ida_vtables.idc
0    CClientState::~CClientState
1    CClientState::~CClientState
2    CBaseClientState::ConnectionStart
3    CClientState::ConnectionClosing
4    CClientState::ConnectionCrashed
5    CClientState::PacketStart
6    CClientState::PacketEnd
7    CClientState::FileRequested
8    CClientState::FileReceived
9    CClientState::FileDenied
10    CClientState::FileSent
11    CClientState::ProcessConnectionlessPacket
12    CClientState::ProcessTick
13    CClientState::ProcessStringCmd
14    CBaseClientState::ProcessSetConVar
15    CBaseClientState::ProcessSignonState
16    CBaseClientState::ProcessPrint
17    CClientState::ProcessServerInfo
18    CBaseClientState::ProcessSendTable
19    CClientState::ProcessClassInfo
20    CClientState::ProcessSetPause
21    CBaseClientState::ProcessCreateStringTable
22    CBaseClientState::ProcessUpdateStringTable
23    CBaseClientState::ProcessSetView
24    CClientState::ProcessPacketEntities
25    CBaseClientState::ProcessMenu
26    CBaseClientState::ProcessGameEventList
27    CBaseClientState::ProcessGetCvarValue
28    CBaseClientState::ProcessCmdKeyValues
29    CBaseClientState::GetDemoProtocolVersion
30    CClientState::Clear
31    CClientState::FullConnect
32    CBaseClientState::Connect
33    CClientState::SetSignonState
34    CClientState: :D isconnect
35    CBaseClientState::SendConnectPacket
36    CClientState::GetCDKeyHash
37    CClientState::RunFrame
38    CBaseClientState::CheckForResend
39    CClientState::InstallStringTableCallback
40    CClientState::HookClientStringTable
41    CBaseClientState::LinkClasses
42    CBaseClientState::GetConnectionRetryNumber
43    CBaseClientState::GetClientName
44    CClientState::ReadEnterPVS
45    CClientState::ReadLeavePVS
46    CClientState::ReadDeltaEnt
47    CClientState::ReadPreserveEnt
48    CClientState::ReadDeletions
49    CClientState::ProcessVoiceInit
50    CClientState::ProcessVoiceData
51    CClientState::ProcessSounds
52    CClientState::ProcessFixAngle
53    CClientState::ProcessCrosshairAngle
54    CClientState::ProcessBSPDecal
55    CClientState::ProcessGameEvent
56    CClientState::ProcessUserMessage
57    CClientState::ProcessEntityMessage
58    CClientState::ProcessTempEntities
59    CClientState::ProcessPrefetch
User avatar
Ayuto
Project Leader
Posts: 2197
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Tue Nov 04, 2014 6:47 pm

If you just want to mute players, you can use this. http://forums.eventscripts.com/viewtopic.php?f=125&t=46569

But I can't tell you if that's the function to modify voice data. I will need to take a look at it.

You should start using this Python script to find the proper offsets for Windows as this also handles non-virtual thunks, which occur if it comes to multiple inheritance. And this is the case if you want to find offsets for CBaseClient, CClientState, etc.

Edit: Here is the output.

Code: Select all

Inheritance Tree:
 CGameClient
  CBaseClient
   IGameEventListener2
   IClient
    INetChannelHandler
   IClientMessageHandler
    INetMessageHandler
  CClientFrameManager

VTable for CGameClient: (0, 0)
 Lin  Win Function
   0    0 CGameClient::~CGameClient()
   1    0 CGameClient::~CGameClient()
   2    1 CBaseClient::FireGameEvent(IGameEvent *)
   3      CBaseClient::GetPlayerSlot(void)const
   4      CBaseClient::GetUserID(void)const
   5      CBaseClient::GetNetworkID(void)const
   6      CBaseClient::GetClientName(void)const
   7      CBaseClient::GetNetChannel(void)
   8      CBaseClient::GetServer(void)
   9      CBaseClient::GetUserSetting(char  const*)const
  10      CBaseClient::GetNetworkIDString(void)const
  11      CGameClient::Connect(char  const*, int, INetChannel *, bool, int)
  12      CGameClient::Inactivate(void)
  13      CGameClient::Reconnect(void)
  14      CGameClient: :D isconnect(char  const*, ...)
  15      CGameClient::SetRate(int, bool)
  16      CBaseClient::GetRate(void)const
  17      CGameClient::SetUpdateRate(int, bool)
  18      CBaseClient::GetUpdateRate(void)const
  19      CGameClient::Clear(void)
  20    2 CBaseClient: :D emoRestart(void)
  21      CBaseClient::GetMaxAckTickCount(void)const
  22      CGameClient::ExecuteStringCommand(char  const*)
  23      CGameClient::SendNetMsg(INetMessage &, bool)
  24      CBaseClient::ClientPrintf(char  const*, ...)
  25      CBaseClient::IsConnected(void)const
  26      CBaseClient::IsSpawned(void)const
  27      CBaseClient::IsActive(void)const
  28      CBaseClient::IsFakeClient(void)const
  29      CBaseClient::IsHLTV(void)const
  30      CBaseClient::IsReplay(void)const
  31      CGameClient::IsHearingClient(int)const
  32      CGameClient::IsProximityHearingClient(int)const
  33      CBaseClient::SetMaxRoutablePayloadSize(int)
  34    3 CBaseClient::IsSplitScreenUser(void)const
  35      CBaseClient::ProcessTick(NET_Tick *)
  36      CBaseClient::ProcessStringCmd(NET_StringCmd *)
  37      CBaseClient::ProcessSetConVar(NET_SetConVar *)
  38      CBaseClient::ProcessSignonState(NET_SignonState *)
  39      CGameClient::ProcessClientInfo(CLC_ClientInfo *)
  40      CBaseClient::ProcessBaselineAck(CLC_BaselineAck *)
  41      CBaseClient::ProcessListenEvents(CLC_ListenEvents *)
  42      CGameClient::ProcessCmdKeyValues(CLC_CmdKeyValues *)
  43      CBaseClient::ConnectionStart(INetChannel *)
  44    4 CGameClient::UpdateAcknowledgedFramecount(int)
  45    5 CGameClient::ShouldSendMessages(void)
  46    6 CBaseClient::UpdateSendState(void)
  47    7 CBaseClient::FillUserInfo(player_info_s &)
  48    8 CGameClient::UpdateUserSettings(void)
  49    9 CGameClient::SetSignonState(int, int)
  50   10 CGameClient::WriteGameSounds(bf_write &)
  51   11 CGameClient::GetDeltaFrame(int)
  52   12 CGameClient::SendSnapshot(CClientFrame *)
  53   13 CBaseClient::SendServerInfo(void)
  54   14 CGameClient::SendSignonData(void)
  55   15 CGameClient::SpawnPlayer(void)
  56   16 CGameClient::ActivatePlayer(void)
  57   17 CBaseClient::SetName(char  const*)
  58   18 CBaseClient::SetUserCVar(char  const*, char  const*)
  59   19 CBaseClient::FreeBaselines(void)
  60   20 CGameClient::IgnoreTempEntity(CEventInfo *)
  61      CGameClient::ConnectionClosing(char  const*)
  62      CGameClient::ConnectionCrashed(char  const*)
  63      CGameClient::PacketStart(int, int)
  64      CGameClient::PacketEnd(void)
  65      CGameClient::FileReceived(char  const*, unsigned int)
  66      CGameClient::FileRequested(char  const*, unsigned int)
  67      CGameClient::FileDenied(char  const*, unsigned int)
  68      CGameClient::FileSent(char  const*, unsigned int)
  69      CGameClient::ProcessMove(CLC_Move *)
  70      CGameClient::ProcessVoiceData(CLC_VoiceData *)
  71      CGameClient::ProcessRespondCvarValue(CLC_RespondCvarValue *)
  72      CGameClient::ProcessFileCRCCheck(CLC_FileCRCCheck *)
  73      CGameClient::ProcessFileMD5Check(CLC_FileMD5Check *)
  74      CGameClient::ProcessSaveReplay(CLC_SaveReplay *)

VTable for IClientMessageHandler: (1, 8)
 Lin  Win Function
T  0    0 CGameClient::~CGameClient()
T  1    0 CGameClient::~CGameClient()
T  2    1 CBaseClient::ProcessTick(NET_Tick *)
T  3    2 CBaseClient::ProcessStringCmd(NET_StringCmd *)
T  4    3 CBaseClient::ProcessSetConVar(NET_SetConVar *)
T  5    4 CBaseClient::ProcessSignonState(NET_SignonState *)
T  6    5 CGameClient::ProcessClientInfo(CLC_ClientInfo *)
T  7    6 CGameClient::ProcessMove(CLC_Move *)
T  8    7 CGameClient::ProcessVoiceData(CLC_VoiceData *)
T  9    8 CBaseClient::ProcessBaselineAck(CLC_BaselineAck *)
T 10    9 CBaseClient::ProcessListenEvents(CLC_ListenEvents *)
T 11   10 CGameClient::ProcessRespondCvarValue(CLC_RespondCvarValue *)
T 12   11 CGameClient::ProcessFileCRCCheck(CLC_FileCRCCheck *)
T 13   12 CGameClient::ProcessFileMD5Check(CLC_FileMD5Check *)
T 14   13 CGameClient::ProcessSaveReplay(CLC_SaveReplay *)
T 15   14 CGameClient::ProcessCmdKeyValues(CLC_CmdKeyValues *)

VTable for CClientFrameManager: (2, 160592)
 Lin  Win Function
T  0    0 CGameClient::~CGameClient()
T  1    0 CGameClient::~CGameClient()

VTable for IClient: (3, 4)
 Lin  Win Function
T  0    0 CGameClient::~CGameClient()
T  1    0 CGameClient::~CGameClient()
T  2    1 CBaseClient::ConnectionStart(INetChannel *)
T  3    2 CGameClient::ConnectionClosing(char  const*)
T  4    3 CGameClient::ConnectionCrashed(char  const*)
T  5    4 CGameClient::PacketStart(int, int)
T  6    5 CGameClient::PacketEnd(void)
T  7    6 CGameClient::FileRequested(char  const*, unsigned int)
T  8    7 CGameClient::FileReceived(char  const*, unsigned int)
T  9    8 CGameClient::FileDenied(char  const*, unsigned int)
T 10    9 CGameClient::FileSent(char  const*, unsigned int)
T 11   10 CGameClient::Connect(char  const*, int, INetChannel *, bool, int)
T 12   11 CGameClient::Inactivate(void)
T 13   12 CGameClient::Reconnect(void)
T 14   13 CGameClient: :D isconnect(char  const*, ...)
T 15   14 CBaseClient::GetPlayerSlot(void)const
T 16   15 CBaseClient::GetUserID(void)const
T 17   16 CBaseClient::GetNetworkID(void)const
T 18   17 CBaseClient::GetClientName(void)const
T 19   18 CBaseClient::GetNetChannel(void)
T 20   19 CBaseClient::GetServer(void)
T 21   20 CBaseClient::GetUserSetting(char  const*)const
T 22   21 CBaseClient::GetNetworkIDString(void)const
T 23   22 CGameClient::SetRate(int, bool)
T 24   23 CBaseClient::GetRate(void)const
T 25   24 CGameClient::SetUpdateRate(int, bool)
T 26   25 CBaseClient::GetUpdateRate(void)const
T 27   26 CGameClient::Clear(void)
T 28   27 CBaseClient::GetMaxAckTickCount(void)const
T 29   28 CGameClient::ExecuteStringCommand(char  const*)
T 30   29 CGameClient::SendNetMsg(INetMessage &, bool)
T 31   30 CBaseClient::ClientPrintf(char  const*, ...)
T 32   31 CBaseClient::IsConnected(void)const
T 33   32 CBaseClient::IsSpawned(void)const
T 34   33 CBaseClient::IsActive(void)const
T 35   34 CBaseClient::IsFakeClient(void)const
T 36   35 CBaseClient::IsHLTV(void)const
T 37   36 CBaseClient::IsReplay(void)const
T 38   37 CGameClient::IsHearingClient(int)const
T 39   38 CGameClient::IsProximityHearingClient(int)const
T 40   39 CBaseClient::SetMaxRoutablePayloadSize(int)
Edit 2: Here is my test code for Windows.

Syntax: Select all

# =============================================================================
# >> IMPORTS
# =============================================================================
# memory
import memory

from memory import Argument
from memory import Return

from memory.manager import manager
from memory.manager import CustomType

from memory.hooks import PreHook

# filters
from filters.players import PlayerIter

# core
from core import PLATFORM
if PLATFORM != 'windows':
raise ValueError('Unsupported platform.')


# =============================================================================
# >> CLASSES
# =============================================================================
class CBaseServer(CustomType, metaclass=manager):
GetClient = manager.virtual_function(6, [Argument.INT], Return.POINTER)

def get_client_message_handler(self, index):
# Convert the ICLient object to an IClientMessageHandler object
return memory.make_object(
IClientMessageHandler, self.GetClient(index) + 4)

sv = manager.global_pointer(CBaseServer, 'bin/engine',
b'\x55\x8B\xEC\x56\x8B\x75\x0C\x57\x85\xF6', 26)

'''
Inheritance Tree:
CGameClient
CBaseClient
IGameEventListener2
IClient
INetChannelHandler
IClientMessageHandler
INetMessageHandler
CClientFrameManager
'''
class IClientMessageHandler(CustomType, metaclass=manager):
ProcessVoiceData = manager.virtual_function(7, [Argument.POINTER])


'''
Inheritance Tree:
SVC_VoiceData
CNetMessage
INetMessage

VTable for SVC_VoiceData: (0, 0)
Lin Win Function
0 0 SVC_VoiceData::~SVC_VoiceData()
1 0 SVC_VoiceData::~SVC_VoiceData()
2 1 CNetMessage::SetNetChannel(INetChannel *)
3 2 CNetMessage::SetReliable(bool)
4 3 SVC_VoiceData::Process(void)
5 4 SVC_VoiceData::ReadFromBuffer(bf_read &)
6 5 SVC_VoiceData::WriteToBuffer(bf_write &)
7 6 CNetMessage::IsReliable(void)const
8 7 SVC_VoiceData::GetType(void)const
9 8 SVC_VoiceData::GetGroup(void)const
10 9 SVC_VoiceData::GetName(void)const
11 10 CNetMessage::GetNetChannel(void)const
12 11 SVC_VoiceData::ToString(void)const
'''
class CLC_VoiceData(CustomType, metaclass=manager):
ToString = manager.virtual_function(11, [], Return.STRING)


# =============================================================================
# >> TEST
# =============================================================================
index = None
for index in PlayerIter('human', return_types='index'): break
if index is None:
raise ValueError('No human player found!')

# Convert the entity index to a client index and use the result to get the
# player's message handler
handler = sv.get_client_message_handler(index - 1)

@PreHook(handler.ProcessVoiceData)
def process_voice_data(args):
this = memory.make_object(IClientMessageHandler, args[0])
data = memory.make_object(CLC_VoiceData, args[1])

print(data.ToString())
User avatar
Doldol
Senior Member
Posts: 201
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

Postby Doldol » Tue Nov 04, 2014 10:13 pm

No, I did not forget that post! :) I was reviewing it earlier. But I'm also interested in knowing when someone talks (which the ES code never did) and modifying (and saving) the data. Apart from that I got really curious how to do this since it totally wasn't working.

Thanks for that snippet and the link to the tool, this has actually got me pretty exited! :)

Edit:
Great it's working wonderfully and seems to be stable (I wonder what performance will be like on a full server).
You're really amazing Ayuto!

There is probably no way to read/write from/to the buffer right? (SVC_VoiceData::ReadFromBuffer), I tried a ctypes buffer, but forgot that it isn't C++ compatible.

If I try with a string/int it crashes. I haven't tried writing. I'm using:
Python:
  1. class CLC_VoiceData(CustomType, metaclass=manager):
  2. ReadFromBuffer = manager.virtual_function(4, [Argument.POINTER], Return.VOID)
User avatar
Ayuto
Project Leader
Posts: 2197
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Wed Nov 05, 2014 7:45 pm

Hehe, thank you! :)

I will do some tests today. Exposing bf_read/bf_write is on our TODO list, but we were not really sure if it's really needed. In this case it would be very helpful. Otherwise you would have to reconstruct those two classes.
User avatar
Doldol
Senior Member
Posts: 201
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

Postby Doldol » Fri Nov 07, 2014 12:11 pm

Ayuto wrote:In this case it would be very helpful. Otherwise you would have to reconstruct those two classes.


I see, that would be great! I'll look into that in the meantime and try to figure something out!
User avatar
Ayuto
Project Leader
Posts: 2197
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Fri Nov 07, 2014 3:54 pm

User avatar
Doldol
Senior Member
Posts: 201
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

Postby Doldol » Fri Nov 07, 2014 6:43 pm

Well great! This seems to work!

I do get 2 different reported pointers to ReadFromBuffer? They alternate ech time the function gets called. I haven't looked into it further.
Also the bytes between what ToSting reports and what the buffer says are left seem to differ.

I'm not sure whether I did this 100% right or not.

Syntax: Select all

# ==================================================	===========================
# >> IMPORTS
# ================================================== ===========================
# memory
import memory


from memory import Argument
from memory import Return, Pointer


from memory.manager import manager
from memory.manager import CustomType


from memory.hooks import PreHook, HookType


# filters
from filters.players import PlayerIter
from players.entity import PlayerEntity
from players.helpers import index_from_userid


from _basetypes import bf_write, bf_read


# core
from core import PLATFORM
if PLATFORM != 'windows':
raise ValueError('Unsupported platform.')


'''
Inheritance Tree:
CGameClient
CBaseClient
IGameEventListener2
IClient
INetChannelHandler
IClientMessageHandler
INetMessageHandler
CClientFrameManager


Inheritance Tree:
SVC_VoiceData
CNetMessage
INetMessage


VTable for SVC_VoiceData: (0, 0)
Lin Win Function
0 0 SVC_VoiceData::~SVC_VoiceData()
1 0 SVC_VoiceData::~SVC_VoiceData()
2 1 CNetMessage::SetNetChannel(INetChannel *)
3 2 CNetMessage::SetReliable(bool)
4 3 SVC_VoiceData::Process(void)
5 4 SVC_VoiceData::ReadFromBuffer(bf_read &)
6 5 SVC_VoiceData::WriteToBuffer(bf_write &)
7 6 CNetMessage::IsReliable(void)const
8 7 SVC_VoiceData::GetType(void)const
9 8 SVC_VoiceData::GetGroup(void)const
10 9 SVC_VoiceData::GetName(void)const
11 10 CNetMessage::GetNetChannel(void)const
12 11 SVC_VoiceData::ToString(void)const


'''


# ================================================== ===========================
# >> CLASSES
# ================================================== ===========================
class CBaseServer(CustomType, metaclass=manager):
GetClient = manager.virtual_function(6, [Argument.INT], Return.POINTER)


def get_client_message_handler(self, index):
# Convert the ICLient object to an IClientMessageHandler object
return memory.make_object(IClientMessageHandler, self.GetClient(index) + 4)


sv = manager.global_pointer(CBaseServer, 'bin/engine', b'\x55\x8B\xEC\x56\x8B\x75\x0C\x57\x85\xF6', 26)


class IClientMessageHandler(CustomType, metaclass=manager):
ProcessVoiceData = manager.virtual_function(7, [Argument.POINTER])


class CLC_VoiceData(CustomType, metaclass=manager):
ToString = manager.virtual_function(11, [], Return.STRING)
ReadFromBuffer = manager.virtual_function(4, [Argument.POINTER], Return.VOID)


index = None
for index in PlayerIter('human', return_types='index'): break
if index is None:
raise ValueError('No human player found!')


# Convert the entity index to a client index and use the result to get the player's message handler
handler = sv.get_client_message_handler(index - 1)


def voice_buffer_read(args):
stream = memory.make_object(bf_read, args[1])
print(stream.get_num_bytes_read(), stream.get_num_bytes_left())


voice_buffer_read_hook = False


@PreHook(handler.ProcessVoiceData)
def process_voice_data(args):
global voice_buffer_read_hook
this = memory.make_object(IClientMessageHandler, args[0])
data = memory.make_object(CLC_VoiceData, args[1])
if not voice_buffer_read_hook:
# I do get 2 diffrent pointer addresses!?
# data.ReadFromBuffer.add_hook(HookType.PRE, voice_buffer_read_hook)
voice_buffer_read_hook = PreHook(data.ReadFromBuffer)
voice_buffer_read_hook(voice_buffer_read)
print(data.ToString())


This is so cool!
User avatar
Ayuto
Project Leader
Posts: 2197
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Fri Nov 07, 2014 6:58 pm

I haven't tested this, but this is how I would do it.

Syntax: Select all

# =============================================================================
# >> IMPORTS
# =============================================================================
# memory
import memory

from memory import Argument
from memory import Return

from memory.manager import manager
from memory.manager import CustomType

from memory.hooks import PreHook

# filters
from filters.players import PlayerIter

# _basetypes
from _basetypes import bf_read
from _basetypes import bf_write

# core
from core import PLATFORM
if PLATFORM != 'windows':
raise ValueError('Unsupported platform.')


# =============================================================================
# >> CLASSES
# =============================================================================
class CBaseServer(CustomType, metaclass=manager):
GetClient = manager.virtual_function(6, [Argument.INT], Return.POINTER)

def get_client_message_handler(self, index):
# Convert the ICLient object to an IClientMessageHandler object
return memory.make_object(
IClientMessageHandler, self.GetClient(index) + 4)

sv = manager.global_pointer(CBaseServer, 'bin/engine',
b'\x55\x8B\xEC\x56\x8B\x75\x0C\x57\x85\xF6', 26)

'''
Inheritance Tree:
CGameClient
CBaseClient
IGameEventListener2
IClient
INetChannelHandler
IClientMessageHandler
INetMessageHandler
CClientFrameManager
'''
class IClientMessageHandler(CustomType, metaclass=manager):
ProcessVoiceData = manager.virtual_function(7, [Argument.POINTER])


'''
Inheritance Tree:
SVC_VoiceData
CNetMessage
INetMessage

VTable for SVC_VoiceData: (0, 0)
Lin Win Function
0 0 SVC_VoiceData::~SVC_VoiceData()
1 0 SVC_VoiceData::~SVC_VoiceData()
2 1 CNetMessage::SetNetChannel(INetChannel *)
3 2 CNetMessage::SetReliable(bool)
4 3 SVC_VoiceData::Process(void)
5 4 SVC_VoiceData::ReadFromBuffer(bf_read &)
6 5 SVC_VoiceData::WriteToBuffer(bf_write &)
7 6 CNetMessage::IsReliable(void)const
8 7 SVC_VoiceData::GetType(void)const
9 8 SVC_VoiceData::GetGroup(void)const
10 9 SVC_VoiceData::GetName(void)const
11 10 CNetMessage::GetNetChannel(void)const
12 11 SVC_VoiceData::ToString(void)const
'''
class CLC_VoiceData(CustomType, metaclass=manager):
ToString = manager.virtual_function(11, [], Return.STRING)
WriteToBuffer = manager.virtual_function(5, [Argument.POINTER])


# =============================================================================
# >> TEST
# =============================================================================
index = None
for index in PlayerIter('human', return_types='index'): break
if index is None:
raise ValueError('No human player found!')

# Convert the entity index to a client index and use the result to get the
# player's message handler
handler = sv.get_client_message_handler(index - 1)

@PreHook(handler.ProcessVoiceData)
def process_voice_data(args):
this = memory.make_object(IClientMessageHandler, args[0])
data = memory.make_object(CLC_VoiceData, args[1])

wbuffer = bf_write(2048)

# Write the data to the buffer
data.WriteToBuffer(wbuffer)

# Do something with the buffer...
# E.g. reading it...
rbuffer = bf_read(wbuffer)
8guawong
Senior Member
Posts: 148
Joined: Sat Sep 20, 2014 3:06 am

Postby 8guawong » Thu Nov 20, 2014 6:57 am

is this to detect if some1 is using voice chat???????
Vitamin
Junior Member
Posts: 4
Joined: Tue Dec 06, 2016 12:58 pm

Re: How to hook CGameClient::ProcessVoiceData?

Postby Vitamin » Mon Apr 03, 2023 9:37 pm

Anybody has solution about CLC_VoiceData and how parse this data from server-side ? Game CS: Source. I used codec CELT, but i don't understand what is struct CLC_VoiceData send to server

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 4 guests