Find CBaseEntity_StartTouch, CBaseEntity_Touch, and CBaseEntity_EndTouch
Locate these touch-related virtual functions in CS2 server.dll or libserver.so using IDA Pro MCP tools.
Method
1. Search for Debug String
Search for the debug string used in the touch event handler:
mcp__ida-pro-mcp__find_regex pattern="INVALID CGameEventStartTouchCollideAdaptor"
2. Find Cross-References
Get cross-references to the string:
mcp__ida-pro-mcp__xrefs_to addrs="<string_addr>"
This will lead to a large function that processes touch events.
3. Decompile and Analyze the Touch Event Handler
mcp__ida-pro-mcp__decompile addr="<function_addr>"
The verify first virtual func call (*(void (__fastcall **)(__int64 *, __int64 *, _QWORD))(v3 + 968))(a2, &v60, 0LL); is CVPhys2World_GetTouchingList, 968 is the vfunc_offset of CVPhys2World_GetTouchingList. vfunc_index = 968 / 8 = 121
Locate helper function that calls into StartTouch+Touch
Look for a call with 4 args in the decompiled code:
Windows:
sub_180XXXXXX(v12, v16, v18, v8);
Linux:
sub_1XXXXXX(v8, v13, (__int64)v45, (__int64)v6);
The sub_180XXXXXX or sub_1XXXXXX should match following pattern:
Windows:
double __fastcall sub_180D1AEE0(_QWORD *a1, _QWORD *a2, __int64 a3, __int64 a4)
{
__int64 v8; // rax
_BYTE v10[40]; // [rsp+20h] [rbp-28h] BYREF
v8 = (*(__int64 (__fastcall **)(_QWORD, _BYTE *))(**(_QWORD **)(a4 + 8) + 408LL))(*(_QWORD *)(a4 + 8), v10);
sub_18053B9A0(&unk_181BE1700, v8, a3);
qword_181BE1708 = (__int64)a2;
qword_181EC0DA0 = a4;
if ( a2 && (*(_DWORD *)(a1[2] + 48LL) & 0x200) == 0 && (*(_DWORD *)(a2[2] + 48LL) & 0x200) == 0 )
{
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a1 + 1176LL))(a1, a2); //StartTouch
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a1 + 1184LL))(a1, a2); //Touch
}
if ( a1 && (*(_DWORD *)(a2[2] + 48LL) & 0x200) == 0 && (*(_DWORD *)(a1[2] + 48LL) & 0x200) == 0 )
{
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a2 + 1176LL))(a2, a1); //StartTouch
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a2 + 1184LL))(a2, a1); //Touch
}
return sub_18128D070(&unk_181BE1700);
}
Linux:
__int64 __fastcall sub_177BE90(_QWORD *a1, _QWORD *a2, __int64 a3, __int64 a4)
{
_BYTE v7[80]; // [rsp+0h] [rbp-50h] BYREF
(*(void (__fastcall **)(_BYTE *))(**(_QWORD **)(a4 + 8) + 408LL))(v7);
sub_E31300(&xmmword_25A3B00, v7, a3);
*((_QWORD *)&xmmword_25A3B00 + 1) = a2;
qword_25A3AF8 = a4;
if ( a2 )
{
if ( (*(_BYTE *)(a1[2] + 49LL) & 2) != 0 || (*(_BYTE *)(a2[2] + 49LL) & 2) != 0 )
return sub_1B5AAC0(&xmmword_25A3B00);
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a1 + 1168LL))(a1, a2); //StartTouch
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a1 + 1176LL))(a1, a2); //Touch
}
if ( (*(_BYTE *)(a2[2] + 49LL) & 2) == 0 && (*(_BYTE *)(a1[2] + 49LL) & 2) == 0 )
{
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a2 + 1168LL))(a2, a1); //StartTouch
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a2 + 1176LL))(a2, a1); //Touch
}
return sub_1B5AAC0(&xmmword_25A3B00);
}
Pattern for EndTouch Helper Function
Find a call with comment or condition for "EndTouch":
Windows:
if ( (*(_BYTE *)v8 & 0x10) != 0 )
sub_180XXXXXX(v12, v16);
Linux:
LABEL_4:
if ( (v14 & 0x10) == 0 )
goto LABEL_5;
LABEL_43:
v5 += 256LL;
sub_1XXXXXX((__int64)v8, (__int64)v13);
Windows:
__int64 __fastcall sub_180D1AEA0(__int64 a1, __int64 a2)
{
(*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 1192LL))(a1); //EndTouch
return (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a2 + 1192LL))(a2, a1);
}
Linux:
__int64 __fastcall sub_1XXXXXX(__int64 a1, __int64 a2)
{
(*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 1184LL))(a1); // EndTouch
return (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a2 + 1184LL))(a2, a1);
}
4. Decompile StartTouch+Touch Helper Function
mcp__ida-pro-mcp__decompile addr="<starttouch_helper_addr>"
Look for these virtual function calls:
// Offset 1176 = CBaseEntity_StartTouch (vtable index 147)
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a1 + 1176LL))(a1, a2);
// Offset 1184 = CBaseEntity_Touch (vtable index 148)
(*(void (__fastcall **)(_QWORD *, _QWORD *))(*a1 + 1184LL))(a1, a2);
5. Decompile EndTouch Helper Function
mcp__ida-pro-mcp__decompile addr="<endtouch_helper_addr>"
Look for this virtual function call:
// Offset 1192 = CBaseEntity_EndTouch (vtable index 149)
(*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 1192LL))(a1);
6. Generate vfunc offset signatures for CBaseEntity_StartTouch, CBaseEntity_Touch, CBaseEntity_EndTouch, CVPhys2World_GetTouchingList
ALWAYS Use SKILL /generate-signature-for-vfuncoffset to generate a robust and unique signature for CBaseEntity_StartTouch, CBaseEntity_Touch, CBaseEntity_EndTouch, CVPhys2World_GetTouchingList, with inst_addr and vfunc_offset from previous steps.
7. Load CBaseEntity VTable information from yaml
ALWAYS Use SKILL /get-vtable-from-yaml with class_name=CBaseEntity.
If the skill returns an error, STOP and report to user.
Otherwise, extract these values for subsequent steps:
vtable_va: The vtable start address (use as<VTABLE_START>)vtable_numvfunc: The valid vtable entry count (last valid index = count - 1)vtable_entries: An array of virtual functions starting from vtable[0]
8. Resolve the virtual function addresses from the CBaseEntity vtable_entries
CBaseEntity_vtable.{platform}.yaml :
vtable_class: CBaseEntity
vtable_symbol: ??_7CBaseEntity@@6B@
#..................
vtable_entries:
#..................
147: '0x1803c81d0' # CBaseEntity_StartTouch, note that vfunc index can change on game update.
148: '0x1803c9710' # CBaseEntity_Touch, note that vfunc index can change on game update.
149: '0x1803af380' # CBaseEntity_EndTouch, note that vfunc index can change on game update.
- No need to resolve
CVPhys2World_GetTouchingListbecause it resides invphysics2instead ofserverdll.
9. Verify function body by decompiling
Decompile each function to verify it matches the expected pattern:
CBaseEntity_StartTouch Pattern
// Calls virtual function at offset 1176 on m_pParentEntity or similar
result = *(_QWORD **)(a1 + 632); // Get some entity pointer
v4 = result[7];
if ( v4 )
{
result = sub_XXXXXX(v4); // GetEntity or similar
if ( result )
{
// Check IsTouching (offset 0x4B0 = 1200)
result = (*(__int64 (__fastcall **)(_QWORD *))(*result + 1200LL))(result);
if ( result )
// Forward to StartTouch (offset 0x498 = 1176)
return (*(__int64 (__fastcall **)(_QWORD *, __int64))(*v5 + 1176LL))(v5, a2);
}
}
CBaseEntity_Touch Pattern
// Has optional callback call before the main logic
v2 = *(void (**)(void))(a1 + 696); // Optional touch callback
if ( v2 )
v2();
// Then similar forwarding pattern with offset 0x4A0 = 1184
CBaseEntity_EndTouch Pattern
// Similar to StartTouch but forwards to offset 0x4A8 = 1192
return (*(__int64 (__fastcall **)(_QWORD *, __int64))(*v5 + 1192LL))(v5, a2);
10. Rename the CBaseEntity Functions
mcp__ida-pro-mcp__rename batch={"func": [
{"addr": "<starttouch_addr>", "name": "CBaseEntity_StartTouch"},
{"addr": "<touch_addr>", "name": "CBaseEntity_Touch"},
{"addr": "<endtouch_addr>", "name": "CBaseEntity_EndTouch"}
]}
11. Write YAML Files
ALWAYS Use SKILL /write-vfunc-as-yaml to write the analysis results for each function.
Required parameters for each function:
CBaseEntity_StartTouch
func_name:CBaseEntity_StartTouchvtable_name:CBaseEntityvfunc_offset:0x498# can change on game updatevfunc_index:147# can change on game updatevfunc_sig: CBaseEntity_StartTouch's<vfunc_sig>from step 6
CBaseEntity_Touch
func_name:CBaseEntity_Touchvtable_name:CBaseEntityvfunc_offset:0x4A0# can change on game updatevfunc_index:148# can change on game updatevfunc_sig: CBaseEntity_Touch's<vfunc_sig>from step 6
CBaseEntity_EndTouch
func_name:CBaseEntity_EndTouchvtable_name:CBaseEntityvfunc_offset:0x4A8# can change on game updatevfunc_index:149# can change on game updatevfunc_sig: CBaseEntity_EndTouch's<vfunc_sig>from step 6
CVPhys2World_GetTouchingList
func_name:CVPhys2World_GetTouchingListvtable_name:CVPhys2Worldvfunc_offset:0x3C8# can change on game updatevfunc_index:121# can change on game updatevfunc_sig: CVPhys2World_GetTouchingList's<vfunc_sig>from step 3
Function Characteristics
CBaseEntity_StartTouch
- VTable: CBaseEntity
- VTable Index: 147 (offset 0x498 = 1176), can change on game update
- Prototype:
void CBaseEntity::StartTouch(CBaseEntity* pOther) - Purpose: Called when an entity starts touching another entity
- Implementation: Forwards the call to the parent entity's StartTouch if applicable
CBaseEntity_Touch
- VTable: CBaseEntity
- VTable Index: 148 (offset 0x4A0 = 1184), can change on game update
- Prototype:
void CBaseEntity::Touch(CBaseEntity* pOther) - Purpose: Called continuously while entities are touching
- Implementation: May call optional touch callback, then forwards to parent entity's Touch
CBaseEntity_EndTouch
- VTable: CBaseEntity
- VTable Index: 149 (offset 0x4A8 = 1192), can change on game update
- Prototype:
void CBaseEntity::EndTouch(CBaseEntity* pOther) - Purpose: Called when an entity stops touching another entity
- Implementation: Forwards the call to the parent entity's EndTouch if applicable
VTable Offset Reference
| Function | VTable | Byte Offset | Index | Virtual Call Pattern |
|---|---|---|---|---|
| CBaseEntity_StartTouch | CBaseEntity | 1176 (0x498) | 147 | 41 FF 90 98 04 00 00 |
| CBaseEntity_Touch | CBaseEntity | 1184 (0x4A0) | 148 | 41 FF 90 A0 04 00 00 |
| CBaseEntity_EndTouch | CBaseEntity | 1192 (0x4A8) | 149 | 41 FF 90 A8 04 00 00 |
Output YAML Format
The output YAML filenames depend on the platform:
server.dll->CBaseEntity_StartTouch.windows.yaml,CBaseEntity_Touch.windows.yaml,CBaseEntity_EndTouch.windows.yaml,CVPhys2World_GetTouchingList.windows.yamllibserver.so->CBaseEntity_StartTouch.linux.yaml,CBaseEntity_Touch.linux.yaml,CBaseEntity_EndTouch.linux.yaml,CVPhys2World_GetTouchingList.linux.yaml
DLL Information
- DLL:
server.dll(Windows) /libserver.so(Linux)
Notes
- The touch event handler function uses helper functions to call these virtual functions
- CBaseEntity_StartTouch and CBaseEntity_EndTouch have very similar bytecode - the distinguishing factor is the final virtual call offset (0x498 vs 0x4A8)
- CBaseEntity_Touch has a unique callback call pattern before the forwarding logic
- When analyzing Linux binaries, vtable offsets may differ slightly due to ABI differences