Newbie HvHer
- User ID
- 138768
- Messages
- 2
- Reactions
- 4
- Level
- 2
I remembered Salvatore's lua for luminus lights in csgo. Got me interested how it's done so here's another entry in my reversing blog. I'll be using Moneybot and their Lua api for this.
I started looking how lights are added in hammer and watched a couple of videos
(timestamps unrelated)
From the Hammer tutorials we can see that lights have the classname `light_spot` / `light_env`. Might try getting these entities with entity_list.get_all (
Sadly, using the api method didn't work. What else can we do? What if we iterate over all entities and see what classnames they have?
(classnames are gotten from `BaseEntity`'s vtable (code below), all entities are inheriting GetClassName from `BaseEntitiy` class so we can use the same method for every entity)
Figure 1. Entity "dump".
What do we see? Lights have the classname `env_sprite`
(bottom left, in under)
Figure 2. Light entity.
Okay, getting entities by classname didn't work... what if we iterate over every entity and check if the name is "env_sprite"?
...
It works!
We have our light entities, now we need to reverse the light sprite methods. Investigating the freeed source code of csgo from 2018 we can find "SetRenderColor" method for entities in "game/shared/props_chares.cpp -> PropBreakableCreateAll( ... ) -> void` "
Figure 3. PropBreakableCreateAll.
Since there exists a string there, why not search for the string's xrefs in IDA?
Figure 4. Pseudo-reversed string xref block.
Exactly one match for this specific string! Can we sig this method? Yes! So Lua code:
What do you know, it works!
Figure 5. Red light in palace.
Figure 6. Green light in palace.
Figure 7. Colorful colored lights in connector.
Okay now if I remember correctly, Salvatore also had different light modes, probably not made in Lua (as in managing the flickering brightness in lua) but using game functions, so why not try and add that aswell? Looking at the freeed source code again, we can find this method and a string (possible to search for string xrefs again!) in "game\client\cstrike15\fx_cs_christmaslights.cpp -> CreateHolidayLight( const HolidayLightData_t &holidayLight ) -> void"
Figure 8. CreateHolidayLight.
So lets find this string in IDA...
Figure 9. IDA string xref.
Only 1 hit again, which means there is a high chance we are at the correct place. After further reversing we can conclude that we are in the correct method. What do we have here?
Figure 10. CreateHolidayLight pseudocode.
We already reversed "set_render_color", matching it with the source free, we can find "SetRenderMode" right next to it. Comparing the pseudocode from IDA to source free, it seems this might be the correct method.
Figure 11. IDA view.
Figure 12. Source free.
Accesses a property from "this" (first arg), checks if it has changed, sets the same prop to a new value and calls a method from "this" (probably SetRenderFX). Let's sig "SetRenderMode" and remember the offset where "m_nRenderMode" resides.
It works! Adding a few menu elements we get this amazing output:
(some modes and fx didn't match, didn't know at the time so the first 15 seconds of the gif are useless)
Figure 13. Final result.
I started looking how lights are added in hammer and watched a couple of videos
You must be registered for see links
You must be registered for see links
(timestamps unrelated)
From the Hammer tutorials we can see that lights have the classname `light_spot` / `light_env`. Might try getting these entities with entity_list.get_all (
You must be registered for see links
).Sadly, using the api method didn't work. What else can we do? What if we iterate over all entities and see what classnames they have?
(classnames are gotten from `BaseEntity`'s vtable (code below), all entities are inheriting GetClassName from `BaseEntitiy` class so we can use the same method for every entity)

Figure 1. Entity "dump".
Lua code:
local index_GetClassName = 139 -- offset 556, /4 = idx
local getclassname_typedef = 'char*(__thiscall*)(void*)'
-- ...entity loop
local entptr = get_client_entity( index )
local vtbl = get_vtbl( entptr )
-- you should probably cache this
local get_class_name = ffi.cast( getclassname_typedef, vtbl[ index_GetClassName ] )
local name = ffi.string( get_class_name( entptr ) )
What do we see? Lights have the classname `env_sprite`
(bottom left, in under)

Figure 2. Light entity.
Okay, getting entities by classname didn't work... what if we iterate over every entity and check if the name is "env_sprite"?
...
It works!
We have our light entities, now we need to reverse the light sprite methods. Investigating the freeed source code of csgo from 2018 we can find "SetRenderColor" method for entities in "game/shared/props_chares.cpp -> PropBreakableCreateAll( ... ) -> void` "

Figure 3. PropBreakableCreateAll.
Since there exists a string there, why not search for the string's xrefs in IDA?

Figure 4. Pseudo-reversed string xref block.
Exactly one match for this specific string! Can we sig this method? Yes! So Lua code:
Lua code:
-- ida f5-> void __thiscall SetRenderColor(_BYTE *this, char a2, char a3, char a4)
local set_render_color = ffi.cast( set_render_color_typedef, client.find_sig( 'client.dll', set_render_color_sig ) )
...
for ... do
-- ...
if name == 'env_sprite' then
set_render_color( entptr, 255, 0, 0 )
end
-- ...
end
What do you know, it works!

Figure 5. Red light in palace.

Figure 6. Green light in palace.

Figure 7. Colorful colored lights in connector.
Okay now if I remember correctly, Salvatore also had different light modes, probably not made in Lua (as in managing the flickering brightness in lua) but using game functions, so why not try and add that aswell? Looking at the freeed source code again, we can find this method and a string (possible to search for string xrefs again!) in "game\client\cstrike15\fx_cs_christmaslights.cpp -> CreateHolidayLight( const HolidayLightData_t &holidayLight ) -> void"

Figure 8. CreateHolidayLight.
So lets find this string in IDA...

Figure 9. IDA string xref.
Only 1 hit again, which means there is a high chance we are at the correct place. After further reversing we can conclude that we are in the correct method. What do we have here?

Figure 10. CreateHolidayLight pseudocode.
We already reversed "set_render_color", matching it with the source free, we can find "SetRenderMode" right next to it. Comparing the pseudocode from IDA to source free, it seems this might be the correct method.

Figure 11. IDA view.

Figure 12. Source free.
Accesses a property from "this" (first arg), checks if it has changed, sets the same prop to a new value and calls a method from "this" (probably SetRenderFX). Let's sig "SetRenderMode" and remember the offset where "m_nRenderMode" resides.
Lua code:
-- offset=598 => m_nRenderFX
-- ida pseudocode typedef -> int __thiscall SetRenderMode(int this, int a2, int a3)
local set_render_mode = ffi.cast( set_render_mode_typedef, client.find_sig( 'client.dll', set_render_mode_sig ) )
if name == 'env_sprite' then
set_render_mode( entptr, mode_to_idx[ new_mode ], false )
local m_nRenderFX = ffi.cast( 'int32_t*', ffi.cast( 'uintptr_t', entptr ) + 598 )
m_nRenderFX[ 0 ] = 7
set_render_color( entptr, new_color[ 1 ], new_color[ 2 ], new_color[ 3 ] )
end
It works! Adding a few menu elements we get this amazing output:
(some modes and fx didn't match, didn't know at the time so the first 15 seconds of the gif are useless)
Figure 13. Final result.