@@ -85,6 +85,30 @@ typedef GarrysMod::Lua::lua_State GState;
8585CLuaShared* current_shared;
8686CLuaInterface* current_interface;
8787
88+ int deferred_state_execute = -1 ;
89+ #ifdef __linux
90+ typedef CLuaInterface* (*RunStringEx_fn)(CLuaInterface* self, const char * fileName, const char * path, const char * stringToRun, bool run, bool showErrors, bool dontPushErrors, bool noReturns);
91+ RunStringEx_fn RunStringEx_o;
92+ bool RunStringEx_h (CLuaInterface* self, const char * fileName, const char * path, const char * stringToRun, bool run, bool showErrors, bool dontPushErrors, bool noReturns)
93+ #else
94+ typedef CLuaInterface* (__thiscall* RunStringEx_fn)(CLuaInterface* self, const char * fileName, const char * path, const char * stringToRun, bool run, bool showErrors, bool dontPushErrors, bool noReturns);
95+ RunStringEx_fn RunStringEx_o;
96+ bool __fastcall RunStringEx_h (CLuaInterface* self, const char * fileName, const char * path, const char * stringToRun, bool run, bool showErrors, bool dontPushErrors, bool noReturns)
97+ #endif
98+ {
99+ // Need to hook onto this since CreateLuaInterface is too early for openlibs.
100+ // I would have just hooked into luaL_loadbufferx, but this is just easier since its a vtable.
101+
102+ if (deferred_state_execute == -1 || deferred_state_execute != GarrysMod::Lua::State::CLIENT)
103+ return RunStringEx_o (self, fileName, path, stringToRun, run, showErrors, dontPushErrors, noReturns);
104+
105+ API::lua_State* L = (API::lua_State*)self->GetState ();
106+ Tracker::listen (L, " client" , true , (API::lua_State*)current_interface->GetState ());
107+ deferred_state_execute = -1 ;
108+
109+ return RunStringEx_o (self, fileName, path, stringToRun, run, showErrors, dontPushErrors, noReturns);
110+ }
111+
88112#ifdef __linux
89113typedef CLuaInterface* (*CreateLuaInterface_fn)(CLuaShared* self, unsigned char type, bool renew);
90114CreateLuaInterface_fn CreateLuaInterface_o;
@@ -96,27 +120,39 @@ CLuaInterface* __fastcall CreateLuaInterface_h(CLuaShared* self, unsigned char t
96120#endif
97121{
98122 CLuaInterface* state = CreateLuaInterface_o (self, type, renew);
99- API::lua_State* L = (API::lua_State*)state->GetState ();
100-
101- switch (type)
102- {
103- case GarrysMod::Lua::State::CLIENT:
104- Tracker::listen (L, " client" , true );
105- break ;
106- case GarrysMod::Lua::State::SERVER:
107- Tracker::listen (L, " server" , true );
108- break ;
109- case GarrysMod::Lua::State::MENU:
110- Tracker::listen (L, " menu" , true );
111- break ;
112- };
113123
114124 // Do note:
115125 // At this stage lua hasn't correctly initialized (missing some openlibs and stuff)
116126 // So you can't just grab things like the global table just yet, it isn't ready till luaopen_base is called.
117127 // If you need the API in the client-state, but you are in menu-state, just do L:api() L:pop(), this will create everything.
118128 // Be warned, exposing API's onto a state thats not entirely under your control leaves you open to high security risks.
119129
130+ deferred_state_execute = type;
131+
132+ if (type == GarrysMod::Lua::State::CLIENT) {
133+ auto vtable = Interface::VTable (state);
134+
135+ #ifdef _WIN32
136+ DWORD oldProtect;
137+ VirtualProtect (vtable, sizeof (void *) * 6 , PAGE_EXECUTE_READWRITE, &oldProtect);
138+ #elif __linux
139+ size_t page_size = sysconf (_SC_PAGE_SIZE);
140+ uintptr_t base = reinterpret_cast <uintptr_t >(vtable);
141+ uintptr_t aligned_base = base & ~(page_size - 1 );
142+ mprotect (reinterpret_cast <void *>(aligned_base), page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
143+ #endif
144+
145+ RunStringEx_o = (RunStringEx_fn)vtable[111 ];
146+ vtable[111 ] = (void *)RunStringEx_h;
147+
148+ #ifdef _WIN32
149+ DWORD __oldProtect;
150+ VirtualProtect (vtable, sizeof (void *) * 6 , oldProtect, &__oldProtect);
151+ #elif __linux
152+ mprotect (reinterpret_cast <void *>(aligned_base), page_size, PROT_READ | PROT_EXEC);
153+ #endif
154+ }
155+
120156 return state;
121157}
122158
@@ -132,6 +168,30 @@ void __fastcall CloseLuaInterface_h(CLuaShared* self, CLuaInterface* state)
132168{
133169 if (current_interface == state)
134170 return CloseLuaInterface_o (self, state);
171+
172+ if (state->IsClient () && RunStringEx_o) {
173+ auto vtable = Interface::VTable (state);
174+
175+ #ifdef _WIN32
176+ DWORD oldProtect;
177+ VirtualProtect (vtable, sizeof (void *) * 6 , PAGE_EXECUTE_READWRITE, &oldProtect);
178+ #elif __linux
179+ size_t page_size = sysconf (_SC_PAGE_SIZE);
180+ uintptr_t base = reinterpret_cast <uintptr_t >(vtable);
181+ uintptr_t aligned_base = base & ~(page_size - 1 );
182+ mprotect (reinterpret_cast <void *>(aligned_base), page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
183+ #endif
184+
185+ vtable[111 ] = (void *)RunStringEx_o;
186+
187+ #ifdef _WIN32
188+ DWORD __oldProtect;
189+ VirtualProtect (vtable, sizeof (void *) * 6 , oldProtect, &__oldProtect);
190+ #elif __linux
191+ mprotect (reinterpret_cast <void *>(aligned_base), page_size, PROT_READ | PROT_EXEC);
192+ #endif
193+ }
194+
135195 API::lua_State* L = (API::lua_State*)state->GetState ();
136196 Tracker::pre_remove (L);
137197 CloseLuaInterface_o (self, state);
@@ -259,6 +319,12 @@ int module_open() {
259319 Interstellar::Reflection::api ();
260320 std::cout << " [WARNING] Interstellar has reflection.* enabled, you have been warned." << std::endl;
261321 }
322+ else if (shared->GetLuaInterface (GarrysMod::Lua::State::MENU)) {
323+ // MENU should be fine to have this in, but you are responsible for what you use this for on servers.
324+ // Using this to execute malicious code onto a server will put you at risk.
325+ // Even though I think you should have full control over your client-state, I can't blame them.
326+ Interstellar::Reflection::api ();
327+ }
262328 Interstellar::Signal::api ();
263329 Interstellar::Coroutine::api ();
264330 Interstellar::Buffer::api ();
@@ -352,7 +418,7 @@ int module_open() {
352418 // API::lua::close = close_state;
353419
354420 #ifndef GMCL
355- if (shared-> GetLuaInterface (GarrysMod::Lua::State::MENU )) {
421+ if (lua_interface-> IsMenu ( )) {
356422 #ifdef _WIN32
357423 DWORD oldProtect;
358424 VirtualProtect (vtable, sizeof (void *) * 6 , PAGE_EXECUTE_READWRITE, &oldProtect);
@@ -449,9 +515,9 @@ int module_close() {
449515 return 1 ;
450516 }
451517
452- auto vtable = Interface::VTable (current_shared);
453-
454518 if (CreateLuaInterface_o && CloseLuaInterface_o) {
519+ auto vtable = Interface::VTable (current_shared);
520+
455521 #ifdef _WIN32
456522 DWORD oldProtect;
457523 VirtualProtect (vtable, sizeof (void *) * 6 , PAGE_EXECUTE_READWRITE, &oldProtect);
@@ -475,9 +541,7 @@ int module_close() {
475541
476542 auto list = Tracker::get_states ();
477543 for (auto & state : list) {
478- bool is_created = !Tracker::is_internal (state.second );
479544 Tracker::pre_remove (state.second );
480- if (is_created) Reflection::close (state.second );
481545 Tracker::post_remove (state.second );
482546 }
483547
0 commit comments