CounterStrikeSharp
CounterStrikeSharp copied to clipboard
Hook virtual functions by offset
With these changes, we will be able to hook virtual functions based on offset.
Example
Testing StartTouch, Touch, EndTouch hooks on the soccerball in de_dust2
{
"StartTouch": {
"offsets": {
"windows": 143,
"linux": 142
}
},
"Touch": {
"offsets": {
"windows": 144,
"linux": 143
}
},
"EndTouch": {
"offsets": {
"windows": 145,
"linux": 144
}
}
}
public CPhysicsPropMultiplayer? SoccerBall;
public VirtualFunctionVoid<CBaseEntity, CBaseEntity>? OnStartTouch;
public VirtualFunctionVoid<CBaseEntity, CBaseEntity>? OnTouch;
public VirtualFunctionVoid<CBaseEntity, CBaseEntity>? OnEndTouch;
public override void Load(bool hotReload)
{
RegisterEventHandler<EventRoundStart>((@event, info) =>
{
IEnumerable<CPhysicsPropMultiplayer> list = Utilities.FindAllEntitiesByDesignerName<CPhysicsPropMultiplayer>("prop_physics_multiplayer");
if (!list.Any())
{
return HookResult.Continue;
}
SoccerBall = list.First();
if (OnStartTouch == null) (OnStartTouch = new VirtualFunctionVoid<CBaseEntity, CBaseEntity>(SoccerBall, GameData.GetOffset("StartTouch"))).Hook(HookOnStartTouch, HookMode.Pre);
if (OnTouch == null) (OnTouch = new VirtualFunctionVoid<CBaseEntity, CBaseEntity>(SoccerBall, GameData.GetOffset("Touch"))).Hook(HookOnTouch, HookMode.Pre);
if (OnEndTouch == null) (OnEndTouch = new VirtualFunctionVoid<CBaseEntity, CBaseEntity>(SoccerBall, GameData.GetOffset("EndTouch"))).Hook(HookOnEndTouch, HookMode.Pre);
return HookResult.Continue;
});
}
public override void Unload(bool hotReload)
{
OnStartTouch?.Unhook(HookOnStartTouch, HookMode.Pre);
OnTouch?.Unhook(HookOnTouch, HookMode.Pre);
OnEndTouch?.Unhook(HookOnEndTouch, HookMode.Pre);
}
private HookResult HookOnStartTouch(DynamicHook h)
{
CBaseEntity entity = h.GetParam<CBaseEntity>(0);
CBaseEntity otherEntity = h.GetParam<CBaseEntity>(1);
if (entity != SoccerBall && otherEntity != SoccerBall)
return HookResult.Continue;
Console.WriteLine($"[OnStartTouch] {otherEntity.DesignerName} ({otherEntity.Index}) started touching {entity.DesignerName} ({entity.Index})");
return HookResult.Continue;
}
private HookResult HookOnTouch(DynamicHook h)
{
CBaseEntity entity = h.GetParam<CBaseEntity>(0);
CBaseEntity otherEntity = h.GetParam<CBaseEntity>(1);
if (entity != SoccerBall && otherEntity != SoccerBall)
return HookResult.Continue;
Console.WriteLine($"[OnTouch] {otherEntity.DesignerName} ({otherEntity.Index}) is touching {entity.DesignerName} ({entity.Index})");
return HookResult.Continue;
}
private HookResult HookOnEndTouch(DynamicHook h)
{
CBaseEntity entity = h.GetParam<CBaseEntity>(0);
CBaseEntity otherEntity = h.GetParam<CBaseEntity>(1);
if (entity != SoccerBall && otherEntity != SoccerBall)
return HookResult.Continue;
Console.WriteLine($"[OnEndTouch] {otherEntity.DesignerName} ({otherEntity.Index}) is no longer touching {entity.DesignerName} ({entity.Index})");
return HookResult.Continue;
}
Debug output:
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_142
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7f66a15159c0
[Info] - Trampoline - Allocated trampoline at 0x7f6721514000 (using 1 attempts)
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_143
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7f66a1050e60
[Info] - Trampoline - Allocated trampoline at 0x7f672104f000 (using 1 attempts)
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_144
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7f66a1050a40
[Info] - Trampoline - Allocated trampoline at 0x7f66d0e97000 (using 1 attempts)
[OnStartTouch] worldent (0) started touching prop_physics_multiplayer (269)
[OnTouch] worldent (0) is touching prop_physics_multiplayer (269)
[OnTouch] prop_physics_multiplayer (269) is touching worldent (0)
Developers should be aware that they are hooking the virtual function, which is shared between instances (same as with signatures)
@roflmuffin +1 on this
@roflmuffin +1 on this
my second review has to be resolved before this pr is ready
@roflmuffin +1 on this
my second review has to be resolved before this pr is ready
what is second pr? can community help you somehow?
@roflmuffin +1 on this
my second review has to be resolved before this pr is ready
what is second pr? can community help you somehow?
https://github.com/roflmuffin/CounterStrikeSharp/pull/617#discussion_r1793274716
this is the second review
virtual functions that has been created by an offset are now stored as {typeof(TArg1).Name}_{OffsetNum}, for e.g.:
10:52:04 [INFO] (cssharp:Core) Creating function CBaseEntity_142
TArg1 is meant to represent the originating class of the function
@roflmuffin Unsure of the interaction if someone hooks a CBaseEntity and a CCSPlayerPawn with the same offset.
12:40:05 [INFO] (cssharp:Core) Creating function CBaseEntity_142
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7fc5331159c0
[Info] - Trampoline - Allocated trampoline at 0x7fc5b3114000 (using 1 attempts)
12:40:05 [INFO] (cssharp:Core) Creating function CPhysicsPropMultiplayer_142
12:40:05 [INFO] (cssharp:Core) Creating function CBaseEntity_143
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7fc532c50e60
[Info] - Trampoline - Allocated trampoline at 0x7fc5b2c4f000 (using 1 attempts)
12:40:05 [INFO] (cssharp:Core) Creating function CBaseEntity_144
[Info] - Trampoline - Bounds of relative addresses accessed [0xffffffffffffffff, (nil)]
[Info] - Trampoline - Attempting to allocate trampoline within +-2GB range of 0x7fc532c50a40
the hook itself is reused, but currently only the latter is called, I believe this could also happen if the same function is hooked with 2 different signatures? (tested it and yes)
is there any plans to add this to the main branch?
is there any plans to add this to the main branch?
Hi, I plan to finish it in general, just need to find a better way to store these functions as the behaviour I've mentioned in my previous comment is not really viable in my opinion
Looking forward for it