Hook ProcessVoiceData

Please post any questions about developing your plugin here. Please use the search function before posting!
Tank Missile
Junior Member
Posts: 4
Joined: Wed Jun 03, 2015 4:16 am

Hook ProcessVoiceData

Postby Tank Missile » Fri Apr 05, 2019 9:50 pm

I've been trying for a while to understand the ins and outs of Source.Python by reading the documentation that's available, and I must say I'm quite impressed with how much is already there. However, one powerful feature I'm trying to harness is hooking virtual functions. From reading the wiki, I need the use the memory module, but there isn't any examples on hooks. Specifically, I want to detect when a player speaks, and maybe even retrieve player voice packets. I have no idea if that's possible though. From looking at a sourcemod plugin that does the same task I want to do here, I need to hook CGameClient::ProcessVoiceData(CLC_VoiceData*). I found an old topic that seems to do what I want, but I can't seem to modernize it. https://forums.sourcepython.com/viewtopic.php?f=20&t=659 From reading the sourcemod wiki on virtual functions as well, I learned there are actually three ways to achieve what I'm after: rely on manual offsets into the class' vtable, modify the provided class headers by inserting dummy virtual functions to fix the offsets, or sigscanning. It seems like sigscanning, while the most tedious, is the most effective method. In fact, I managed to find the virtual function I was after by using objdump on engine_srv.so (my server is running linux, although making my plugins cross-platform is important). Below is the virtual function with the given bytes. I attempted to create a signature from those bytes and create a global pointer from that, but that ended up crashing my server. In conclusion, I don't exactly know what I'm doing at the moment.

Code: Select all

00183a70 <CGameClient::ProcessVoiceData(CLC_VoiceData*)>:
  183a70:   55                      push   %ebp
  183a71:   89 e5                   mov    %esp,%ebp
  183a73:   57                      push   %edi
  183a74:   56                      push   %esi
  183a75:   8d bd e4 ef ff ff       lea    -0x101c(%ebp),%edi
  183a7b:   53                      push   %ebx
  183a7c:   81 ec 3c 10 00 00       sub    $0x103c,%esp
  183a82:   65 a1 14 00 00 00       mov    %gs:0x14,%eax
  183a88:   89 45 e4                mov    %eax,-0x1c(%ebp)
  183a8b:   31 c0                   xor    %eax,%eax
  183a8d:   8b 75 0c                mov    0xc(%ebp),%esi
  183a90:   8b 5d 08                mov    0x8(%ebp),%ebx
  183a93:   8b 46 14                mov    0x14(%esi),%eax
  183a96:   89 7c 24 04             mov    %edi,0x4(%esp)
  183a9a:   c7 44 24 08 00 10 00    movl   $0x1000,0x8(%esp)
  183aa1:   00
  183aa2:   83 c3 04                add    $0x4,%ebx
  183aa5:   89 44 24 0c             mov    %eax,0xc(%esp)
  183aa9:   8d 46 18                lea    0x18(%esi),%eax
  183aac:   89 04 24                mov    %eax,(%esp)
  183aaf:   e8 ac 64 04 00          call   1c9f60 <bf_read::ReadBitsClamped_ptr(void*, unsigned int, unsigned int)>
  183ab4:   8b 56 48                mov    0x48(%esi),%edx
  183ab7:   8b 4e 4c                mov    0x4c(%esi),%ecx
  183aba:   83 c0 07                add    $0x7,%eax
  183abd:   89 7c 24 08             mov    %edi,0x8(%esp)
  183ac1:   c1 f8 03                sar    $0x3,%eax
  183ac4:   89 1c 24                mov    %ebx,(%esp)
  183ac7:   89 44 24 04             mov    %eax,0x4(%esp)
  183acb:   89 54 24 0c             mov    %edx,0xc(%esp)
  183acf:   89 4c 24 10             mov    %ecx,0x10(%esp)
  183ad3:   e8 b8 0f 01 00          call   194a90 <SV_BroadcastVoiceData(IClient*, int, char*, long long)>
  183ad8:   b8 01 00 00 00          mov    $0x1,%eax
  183add:   8b 7d e4                mov    -0x1c(%ebp),%edi
  183ae0:   65 33 3d 14 00 00 00    xor    %gs:0x14,%edi
  183ae7:   75 0b                   jne    183af4 <CGameClient::ProcessVoiceData(CLC_VoiceData*)+0x84>
  183ae9:   81 c4 3c 10 00 00       add    $0x103c,%esp
  183aef:   5b                      pop    %ebx
  183af0:   5e                      pop    %esi
  183af1:   5f                      pop    %edi
  183af2:   5d                      pop    %ebp
  183af3:   c3                      ret   
  183af4:   e8 fc ff ff ff          call   183af5 <CGameClient::ProcessVoiceData(CLC_VoiceData*)+0x85>
  183af9:   8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi
User avatar
Ayuto
Project Leader
Posts: 2018
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Hook ProcessVoiceData

Postby Ayuto » Tue Apr 09, 2019 7:52 pm

For which game do you need this?
User avatar
Kill
Member
Posts: 87
Joined: Wed Aug 31, 2016 10:05 pm

Re: Hook ProcessVoiceData

Postby Kill » Wed Apr 10, 2019 8:56 am

Ayuto wrote:For which game do you need this?

I'd say he's trying to do something with CSS.

Here's some code I was working on a while back, I checked if the indexes are still correct, according to https://github.com/Franc1sco/VoiceAnnou ... .games.txt, they still are.

Syntax: Select all

SIG = None
if GAME_NAME == "cstrike":
SIG = 7 if PLATFORM == 'windows' else 8
# if PLATFORM == "windows":
# SIG = b"\x55\x8B\xEC\xB8\x00\x10\x00\x00\xE8\x2A\x2A\x2A\x2A\x56\x8B\x75\x08"
# else:
# SIG = "_ZN11CGameClient16ProcessVoiceDataEP13CLC_VoiceData"
elif GAME_NAME == "csgo":
SIG = 8 if PLATFORM == 'windows' else 9
else:
# Unsupported game.
console_message("\n" +
" =========================================== \n" +
" [Warden] Error! This game is not supported.\n" +
" =========================================== \n")


def make_on_voice_transmit(pointer):
if GAME_NAME == 'csgo':
return pointer.make_virtual_function(
SIG,
Convention.THISCALL,
(DataType.POINTER,),
DataType.INT
)
return pointer.make_virtual_function(
SIG,
Convention.THISCALL,
(DataType.POINTER, DataType.POINTER), # Windows always has the this pointer.
DataType.VOID
)

@EntityPreHook(EntityCondition.is_player, make_on_voice_transmit)
def pre_on_voice_transmit(args):
player = None
if PLATFORM == "windows":
player = memory.make_object(Client, args[0])
else:
player = memory.make_function(Client, args[0])
player = Player.from_userid(player.userid)
if player.team != teams.CT or player.dead:
return None
Tank Missile
Junior Member
Posts: 4
Joined: Wed Jun 03, 2015 4:16 am

Re: Hook ProcessVoiceData

Postby Tank Missile » Wed Apr 10, 2019 11:02 pm

Apologies for not mentioning the game. I'm actually trying to do this in TF2. Also, that other plugin I was talking about was indeed VoiceAnnounceEX, and I did attempt to use the indexes provided by it.
User avatar
Ayuto
Project Leader
Posts: 2018
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Hook ProcessVoiceData

Postby Ayuto » Sat Apr 13, 2019 8:51 am

If you just need this for Linux/TF2, I would use the symbol. Then you will probably never need to care about updates again:

Syntax: Select all

import memory

from memory import DataType
from memory import Convention
from memory.hooks import PreHook

from core import PLATFORM

from players import Client

if PLATFORM != 'linux':
raise OSError('OS not supported.')

engine = memory.find_binary('engine')

ProcessVoiceData = engine['_ZN11CGameClient16ProcessVoiceDataEP13CLC_VoiceData'].make_function(
Convention.THISCALL,
[DataType.POINTER, DataType.POINTER],
DataType.BOOL)


@PreHook(ProcessVoiceData)
def pre_process_voice_data(args):
client = memory.make_object(Client, args[0])

# CLC_VoiceData
voice_data_ptr = args[1]

# Your code...

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 2 guests