Skip to content

Commit a98b5ce

Browse files
committed
Add RunStringEX & fix crash from client-state
1 parent ef15eba commit a98b5ce

File tree

1 file changed

+83
-19
lines changed

1 file changed

+83
-19
lines changed

entry_point.cpp

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,30 @@ typedef GarrysMod::Lua::lua_State GState;
8585
CLuaShared* current_shared;
8686
CLuaInterface* 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
89113
typedef CLuaInterface* (*CreateLuaInterface_fn)(CLuaShared* self, unsigned char type, bool renew);
90114
CreateLuaInterface_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

Comments
 (0)