diff --git a/CMakeLists.txt b/CMakeLists.txt index 03659ed..b8de32d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,8 +66,6 @@ add_library(Module SHARED Environment/Libraries/WebSocket.hpp Environment/Libraries/Http.cpp Environment/Libraries/Http.hpp - Environment/Libraries/Debugger.cpp - Environment/Libraries/Debugger.hpp # Disassembler abstraction Disassembler/Disassembler.cpp diff --git a/ClosureManager.cpp b/ClosureManager.cpp index 4e078a2..03d645b 100644 --- a/ClosureManager.cpp +++ b/ClosureManager.cpp @@ -78,7 +78,7 @@ int ClosureManager::newcclosure_handler(lua_State *L) { const auto callResult = lua_pcall(L, argc, LUA_MULTRET, 0); if (callResult != LUA_OK && callResult != LUA_YIELD && std::strcmp(luaL_optstring(L, -1, ""), "attempt to yield across metamethod/C-call boundary") == 0) { - return lua_yield(L, 0); + return lua_yield(L, LUA_MULTRET); } if (callResult == LUA_ERRRUN) @@ -362,8 +362,6 @@ int ClosureManager::newcclosure(lua_State *L) { clManager->FixClosure(L, closure); lua_pushcclosurek(L, ClosureManager::newcclosure_handler, functionName, 0, nullptr); const auto cclosure = lua_toclosure(L, closureIndex); - luaC_barrierfast(L, closure); - luaS_fix(cclosure); clManager->m_newcclosureMap[currentDataModel][cclosure] = closure; lua_remove(L, lua_gettop(L) - 1); // Balance lua stack. return 1; diff --git a/Debugger/DebuggerManager.cpp b/Debugger/DebuggerManager.cpp index 91e053d..081beae 100644 --- a/Debugger/DebuggerManager.cpp +++ b/Debugger/DebuggerManager.cpp @@ -8,7 +8,6 @@ #include "Communication/PacketSerdes.hpp" #include "Disassembler/Disassembler.hpp" -#include "Environment/Libraries/Debugger.hpp" #include "Luau/Compiler.h" #include "LuauManager.hpp" #include "RobloxManager.hpp" @@ -25,18 +24,18 @@ RbxStu::LuauFunctionDefinitions::luau_load original_rluau_load; RBX::Studio::FunctionTypes::luau_execute original_rluau_execute; void rluau_execute__detour(lua_State *L) { - const auto debugManager = DebuggerManager::GetSingleton(); - const auto currentCl = clvalue(L->ci->func); - if (!currentCl->isC) { - - if (debugManager->IsLocalPlayerScript(currentCl->l.p->source->data)) { - // printf("LocalScript Found: %s\n", currentCl->l.p->source->data); - } - - if (debugManager->IsServerScript(currentCl->l.p->source->data)) { - // printf("Server Script Found: %s\n", currentCl->l.p->source->data); - } - } + // const auto debugManager = DebuggerManager::GetSingleton(); + // const auto currentCl = clvalue(L->ci->func); + // if (!currentCl->isC) { + // + // if (debugManager->IsLocalPlayerScript(currentCl->l.p->source->data)) { + // // printf("LocalScript Found: %s\n", currentCl->l.p->source->data); + // } + // + // if (debugManager->IsServerScript(currentCl->l.p->source->data)) { + // // printf("Server Script Found: %s\n", currentCl->l.p->source->data); + // } + // } return original_rluau_execute(L); } @@ -83,25 +82,24 @@ void DebuggerManager::Initialize() { logger->PrintInformation(RbxStu::DebuggerManager, std::format("- '{}' at address {}.", funcName, funcAddress)); } - logger->PrintInformation(RbxStu::DebuggerManager, "Setting up hook on luau_load to track bytecode load-ins..."); - - const auto rLuauLoad = - reinterpret_cast(luauManager->GetFunction("luau_load")); + // logger->PrintInformation(RbxStu::DebuggerManager, "Setting up hook on luau_load to track bytecode load-ins..."); - MH_CreateHook(rLuauLoad, rluau_load__detour, reinterpret_cast(&original_rluau_load)); - MH_EnableHook(rLuauLoad); + // const auto rLuauLoad = + // reinterpret_cast(luauManager->GetFunction("luau_load")); - logger->PrintInformation(RbxStu::DebuggerManager, "Set hook. luau_load -> Instrument bytecode loading"); + // MH_CreateHook(rLuauLoad, rluau_load__detour, reinterpret_cast(&original_rluau_load)); + // MH_EnableHook(rLuauLoad); + // logger->PrintInformation(RbxStu::DebuggerManager, "Set hook. luau_load -> Instrument bytecode loading"); - logger->PrintInformation(RbxStu::DebuggerManager, "Setting up hook on luau_execute to track execution."); + // logger->PrintInformation(RbxStu::DebuggerManager, "Setting up hook on luau_execute to track execution."); - const auto rLuauExecute = - reinterpret_cast(luauManager->GetFunction("luau_execute")); + // const auto rLuauExecute = + // reinterpret_cast(luauManager->GetFunction("luau_execute")); - MH_CreateHook(rLuauExecute, rluau_execute__detour, reinterpret_cast(&original_rluau_execute)); - MH_EnableHook(rLuauExecute); + // MH_CreateHook(rLuauExecute, rluau_execute__detour, reinterpret_cast(&original_rluau_execute)); + // MH_EnableHook(rLuauExecute); - logger->PrintInformation(RbxStu::DebuggerManager, "Set hook. luau_execute -> Instrument script execution."); + // logger->PrintInformation(RbxStu::DebuggerManager, "Set hook. luau_execute -> Instrument script execution."); } void DebuggerManager::PushScriptTracking(const char *chunkname, lua_State *scriptState) { diff --git a/Environment/EnvironmentManager.cpp b/Environment/EnvironmentManager.cpp index 0d3a4ab..3ed14c0 100644 --- a/Environment/EnvironmentManager.cpp +++ b/Environment/EnvironmentManager.cpp @@ -12,7 +12,6 @@ #include "Libraries/Closures.hpp" #include "Libraries/Console.hpp" #include "Libraries/Debug.hpp" -#include "Libraries/Debugger.hpp" #include "Libraries/Filesystem.hpp" #include "Libraries/Globals.hpp" #include "Libraries/Http.hpp" @@ -59,7 +58,6 @@ std::shared_ptr EnvironmentManager::GetSingleton() { EnvironmentManager::pInstance->m_vLibraryList.push_back(new Instance{}); EnvironmentManager::pInstance->m_vLibraryList.push_back(new Input{}); EnvironmentManager::pInstance->m_vLibraryList.push_back(new Http{}); - EnvironmentManager::pInstance->m_vLibraryList.push_back(new Debugger{}); } return EnvironmentManager::pInstance; @@ -300,7 +298,8 @@ void EnvironmentManager::PushEnvironment(_In_ lua_State *L) { for (const auto &[bannedName, sound]: specificBlockage) { if (Utilities::ToLower(bannedName).find(instanceClassName) != std::string::npos) { for (const auto &func: sound) { - if (indexAsString.find(func) != std::string::npos) { + if (indexAsString.find(func) != std::string::npos && + strstr(indexAsString.c_str(), func.c_str()) == indexAsString.c_str()) { goto banned__index; } if (func == "BLOCK_ALL" && strcmp(loweredIndex.c_str(), "classname") != 0 && diff --git a/Environment/Libraries/Console.cpp b/Environment/Libraries/Console.cpp index a33a3c4..6bfe220 100644 --- a/Environment/Libraries/Console.cpp +++ b/Environment/Libraries/Console.cpp @@ -10,13 +10,15 @@ #include "Logger.hpp" #include "RobloxManager.hpp" +static std::string ConsoleWindowTitle{"-- RbxStu V2 --"}; + namespace RbxStu { namespace Console { HWND CreateIfNotCreated() { if (const auto hWnd = GetConsoleWindow(); !hWnd || hWnd == INVALID_HANDLE_VALUE) { AllocConsole(); Logger::GetSingleton()->OpenStandard(); - SetConsoleTitleA("-- RbxStu V2 --"); + SetConsoleTitleA(ConsoleWindowTitle.c_str()); Logger::GetSingleton()->PrintInformation(RbxStu::Anonymous, "-- roblox console created --"); } @@ -39,8 +41,8 @@ namespace RbxStu { int rconsolesettitle(lua_State *L) { CreateIfNotCreated(); - const auto wndName = luaL_checkstring(L, 1); - SetConsoleTitleA(wndName); + ConsoleWindowTitle = luaL_checkstring(L, 1); + SetConsoleTitleA(ConsoleWindowTitle.c_str()); return 0; } diff --git a/Environment/Libraries/Debugger.cpp b/Environment/Libraries/Debugger.cpp deleted file mode 100644 index f69ec1b..0000000 --- a/Environment/Libraries/Debugger.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// -// Created by Dottik on 15/9/2024. -// - -#include "Debugger.hpp" - -#include "Utilities.hpp" -#include "ldebug.h" - -namespace RbxStu::Debugger { - - int breakpoint_thread(lua_State *L) { - // TODO: fix. - - luaL_checktype(L, 1, lua_Type::LUA_TTHREAD); - const auto oL = lua_tothread(L, 1); - const bool crawlUntilLuaThread = luaL_optboolean(L, 2, true); - - auto currentCall = oL->ci; - - if (crawlUntilLuaThread) { - while (currentCall != oL->base_ci && clvalue(currentCall->func)->isC) { - currentCall--; - } - } - - const auto currentCallClosure = clvalue(currentCall->func); - - if (currentCallClosure->isC) - luaL_argerror(L, 1, "the target thread's current call stack must point to a lua closure"); - - if (currentCallClosure->l.p->lineinfo == nullptr) - luaL_argerror(L, 1, - "the target thread's current call stack bytecode was compiled without debug information " - "(level 2). Cannot set breakpoint."); - - luaG_breakpoint(oL, currentCallClosure->l.p, - luaG_getline(currentCallClosure->l.p, pcRel(currentCall->savedpc, currentCallClosure->l.p)), - true); - - return 0; - } - -}; // namespace RbxStu::Debugger - -std::string Debugger::GetLibraryName() { return "debugger"; } -luaL_Reg *Debugger::GetLibraryFunctions() { - const auto reg = new luaL_Reg[]{{"breakpoint_thread", RbxStu::Debugger::breakpoint_thread}, {nullptr, nullptr}}; - - return reg; -} diff --git a/Environment/Libraries/Debugger.hpp b/Environment/Libraries/Debugger.hpp deleted file mode 100644 index 2350feb..0000000 --- a/Environment/Libraries/Debugger.hpp +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Dottik on 15/9/2024. -// - -#pragma once -#include "Environment/EnvironmentManager.hpp" -#include "Roblox/TypeDefinitions.hpp" - - -class Debugger final : public Library { -public: - std::string GetLibraryName() override; - luaL_Reg *GetLibraryFunctions() override; -}; diff --git a/Environment/Libraries/Misc.cpp b/Environment/Libraries/Misc.cpp index 2677eac..45f745b 100644 --- a/Environment/Libraries/Misc.cpp +++ b/Environment/Libraries/Misc.cpp @@ -66,7 +66,6 @@ namespace RbxStu { }); })); - L->ci->flags |= 1; return lua_yield(L, 1); } diff --git a/LuauManager.cpp b/LuauManager.cpp index 2eed401..ea79bba 100644 --- a/LuauManager.cpp +++ b/LuauManager.cpp @@ -74,19 +74,20 @@ namespace RbxStu { } // namespace RbxStu static void luau__freeblock(lua_State *L, uint32_t sizeClass, void *block) { - if (reinterpret_cast(block) > 0x00007FF000000000) { - Logger::GetSingleton()->PrintWarning( - RbxStu::HookedFunction, - std::format("Suspicious address caught (non-heap range): {}. Deallocation blocked!", block)); - return; - } - if (!Utilities::IsPointerValid(static_cast(block)) || - !Utilities::IsPointerValid(reinterpret_cast(reinterpret_cast(block) - 8)) || - !Utilities::IsPointerValid(*reinterpret_cast(reinterpret_cast(block) - 8))) { - Logger::GetSingleton()->PrintWarning( - RbxStu::HookedFunction, std::format("Suspicious address caught: {}. Deallocation blocked!", block)); - return; - } + // This has been fixed, the pointer check no longer needs to exist. + // if (reinterpret_cast(block) > 0x00007FF000000000) { + // Logger::GetSingleton()->PrintWarning( + // RbxStu::HookedFunction, + // std::format("Suspicious address caught (non-heap range): {}. Deallocation blocked!", block)); + // return; + // } + // if (!Utilities::IsPointerValid(static_cast(block)) || + // !Utilities::IsPointerValid(reinterpret_cast(reinterpret_cast(block) - 8)) + // || !Utilities::IsPointerValid(*reinterpret_cast(reinterpret_cast(block) - + // 8))) { Logger::GetSingleton()->PrintWarning( + // RbxStu::HookedFunction, std::format("Suspicious address caught: {}. Deallocation blocked!", block)); + // return; + // } return (reinterpret_cast( LuauManager::GetSingleton()->GetHookOriginal("freeblock"))(L, sizeClass, block)); @@ -224,22 +225,23 @@ void LuauManager::Initialize() { logger->PrintInformation(RbxStu::LuauManager, "All cleaned up!"); logger->PrintInformation(RbxStu::LuauManager, "Hooking functions... [3/4]"); - - logger->PrintInformation(RbxStu::LuauManager, "- Installing pointer check hook into freeblock..."); - this->m_mapHookMap["freeblock"] = new void *(); - - // Error checking, because Dottik didn't add it. - // - MakeSureDudeDies - if (MH_CreateHook(this->m_mapLuauFunctions["freeblock"], luau__freeblock, &this->m_mapHookMap["freeblock"]) != - MH_OK) { - logger->PrintError(RbxStu::LuauManager, "Failed to create freeblock hook!"); - throw std::exception("Creating freeblock hook failed."); - } - - if (MH_EnableHook(this->m_mapLuauFunctions["freeblock"]) != MH_OK) { - logger->PrintError(RbxStu::LuauManager, "Failed to enable freeblock hook!"); - throw std::exception("Enabling freeblock hook failed."); - } + logger->PrintInformation(RbxStu::LuauManager, "Hooks completed! [3/4]"); + + // logger->PrintInformation(RbxStu::LuauManager, "- Installing pointer check hook into freeblock..."); + // this->m_mapHookMap["freeblock"] = new void *(); + + //// Error checking, because Dottik didn't add it. + //// - MakeSureDudeDies + // if (MH_CreateHook(this->m_mapLuauFunctions["freeblock"], luau__freeblock, &this->m_mapHookMap["freeblock"]) != + // MH_OK) { + // logger->PrintError(RbxStu::LuauManager, "Failed to create freeblock hook!"); + // throw std::exception("Creating freeblock hook failed."); + // } + + // if (MH_EnableHook(this->m_mapLuauFunctions["freeblock"]) != MH_OK) { + // logger->PrintError(RbxStu::LuauManager, "Failed to enable freeblock hook!"); + // throw std::exception("Enabling freeblock hook failed."); + // } // this->m_mapHookMap["luaE_newthread"] = new void *(); // MH_CreateHook(this->m_mapLuauFunctions["luaE_newthread"], luaE__newthread, diff --git a/RobloxManager.cpp b/RobloxManager.cpp index 70c2d08..436b3f7 100644 --- a/RobloxManager.cpp +++ b/RobloxManager.cpp @@ -105,29 +105,28 @@ void *rbx__scriptcontext__resumeWaitingThreads( const auto getDataModel = reinterpret_cast( robloxManager->GetRobloxFunction("RBX::ScriptContext::getDataModel")); - { - const auto dataModel = getDataModel(scriptContext); - if (const auto debuggerManager = DebuggerManager::GetSingleton(); - dataModel->m_bIsOpen && !debuggerManager->IsInitialized()) { - const auto globalState = robloxManager->GetGlobalState(scriptContext); - debuggerManager->RegisterCallbackCopy(lua_callbacks(globalState.value())); - } - - if (dataModel->m_bIsOpen) - robloxManager->SetScriptContext(robloxManager->GetDataModelType(dataModel), &scriptContext); - } if (!scheduler->IsInitialized() && luauManager->IsInitialized()) { // !scheduler->is_initialized() if (getDataModel == nullptr) { logger->PrintWarning(RbxStu::HookedFunction, "Initialization of Scheduler may be unstable! Cannot " "determine DataModel for the obtained ScriptContext!"); } else { const auto expectedDataModel = robloxManager->GetCurrentDataModel(scheduler->GetExecutionDataModel()); + if (!expectedDataModel.has_value() || getDataModel(scriptContext) != expectedDataModel.value() || !robloxManager->IsDataModelValid(scheduler->GetExecutionDataModel())) { goto __scriptContext_resumeWaitingThreads__cleanup; } } + if (const auto debuggerManager = DebuggerManager::GetSingleton(); !debuggerManager->IsInitialized()) { + const auto dataModel = getDataModel(scriptContext); + const auto globalState = robloxManager->GetGlobalState(scriptContext); + debuggerManager->RegisterCallbackCopy(lua_callbacks(globalState.value())); + + if (dataModel->m_bIsOpen) + robloxManager->SetScriptContext(robloxManager->GetDataModelType(dataModel), &scriptContext); + } + // HACK!: We do not want to initialize the scheduler on the // first resumptions of waiting threads. This will cause // us to access invalid memory, as the global state is not truly set up yet apparently, @@ -741,6 +740,7 @@ void *RobloxManager::GetRobloxFunction(const std::string &functionName) { if (this->m_mapRobloxFunctions.contains(functionName)) { return this->m_mapRobloxFunctions[functionName]; } + return nullptr; } diff --git a/Scheduler.cpp b/Scheduler.cpp index 145bf8d..f70c9b3 100644 --- a/Scheduler.cpp +++ b/Scheduler.cpp @@ -103,9 +103,11 @@ bool Scheduler::ExecuteSchedulerJob(lua_State *runOn, SchedulerJob *job) { if (robloxManager->GetRobloxTaskDefer().has_value()) { const auto defer = robloxManager->GetRobloxTaskDefer().value(); + logger->PrintInformation(RbxStu::Scheduler, "Scheduling via RBX::ScriptContext::task_defer..."); defer(L); } else if (robloxManager->GetRobloxTaskSpawn().has_value()) { const auto spawn = robloxManager->GetRobloxTaskSpawn().value(); + logger->PrintInformation(RbxStu::Scheduler, "Scheduling via RBX::ScriptContext::task_spawn..."); spawn(L); } else { logger->PrintError(RbxStu::Scheduler, diff --git a/Security.cpp b/Security.cpp index 124841b..c1ca9e4 100644 --- a/Security.cpp +++ b/Security.cpp @@ -36,40 +36,50 @@ std::unordered_map> allCapabilities {"Chat", {0x1b, true}}, {"Animation", {0x1c, true}}, {"Avatar", {0x1d, true}}, + {"Input", {0x1e, true}}, + {"Environment", {0x1f, true}}, + {"RemoteEvent", {0x1f, true}}, + {"PluginOrOpenCloud", {0x1f, true}}, {"Assistant", {0x3e, true}}}; std::unordered_map> identityCapabilities = { {3, - {"RunServerScript", "Plugin", "LocalUser", "RobloxScript", "RunClientScript", "AccessOutsideWrite", "Avatar"}}, + {"RunServerScript", "Plugin", "LocalUser", "RobloxScript", "RunClientScript", "AccessOutsideWrite", "Avatar", + "RemoteEvent", "Environment", "Input"}}, {2, {"CSG", "Chat", "Animation", "Avatar"}}, // These are needed for 'require' to work! {4, {"Plugin", "LocalUser", "Avatar"}}, {6, - {"RunServerScript", "Plugin", "LocalUser", "Avatar", "RobloxScript", "RunClientScript", "AccessOutsideWrite"}}, + {"RunServerScript", "Plugin", "LocalUser", "Avatar", "RobloxScript", "RunClientScript", "AccessOutsideWrite", + "Input", "Environment", "RemoteEvent", "PluginOrOpenCloud"}}, {8, - {"ScriptGlobals", - "RunServerScript", - "Plugin", - "Chat", - "CreateInstances", + {"Plugin", "LocalUser", - "RobloxEngine", "WritePlayer", "RobloxScript", - "CSG", + "RobloxEngine", "NotAccessible", "RunClientScript", + "RunServerScript", "AccessOutsideWrite", - "Physics", "Unassigned", "AssetRequire", - "Avatar", "LoadString", + "ScriptGlobals", + "CreateInstances", "Basic", "Audio", "DataStore", "Network", + "Physics", "UI", + "CSG", + "Chat", "Animation", + "Avatar", + "Input", + "Environment", + "RemoteEvent", + "PluginOrOpenCloud", "Assistant"}}}; std::shared_ptr Security::pInstance; @@ -87,7 +97,7 @@ void Security::PrintCapabilities(std::uint32_t capabilities) { logger->PrintInformation(RbxStu::Security, std::format("0x{:X} got these capabilities:", capabilities)); for (auto capability = allCapabilities.begin(); capability != allCapabilities.end(); ++capability) { if (capability->second.second == true) { - if ((capabilities) & (1 << capability->second.first)) { + if ((capabilities) & (1ull << capability->second.first)) { logger->PrintInformation(RbxStu::Security, capability->first); } } else { @@ -99,14 +109,15 @@ void Security::PrintCapabilities(std::uint32_t capabilities) { }; std::uint64_t Security::IdentityToCapabilities(const std::uint32_t identity) { - std::uint64_t capabilities = 0x3FFFF00 | (1ull << 48ull); // Basic capability | Checkcaller check + std::uint64_t capabilities = + 0x3FFFF00ull | (1ull << 48ull); // Basic capability | Checkcaller check, the capabilities work as flags. if (const auto capabilitiesForIdentity = identityCapabilities.find(identity); capabilitiesForIdentity != identityCapabilities.end()) { for (const auto &capability: capabilitiesForIdentity->second) { if (auto it = allCapabilities.find(capability); it != allCapabilities.end()) { if (it->second.second == true) { - capabilities |= (1 << it->second.first); + capabilities |= (1ull << it->second.first); continue; } @@ -126,8 +137,10 @@ void Security::SetThreadSecurity(lua_State *L, std::int32_t identity) { L->global->cb.userthread(L->global->mainthread, L); // If unallocated, then we must run the callback to create a valid RobloxExtraSpace - auto *plStateUd = static_cast(L->userdata); - auto capabilities = Security::GetSingleton()->IdentityToCapabilities(identity); + auto *plStateUd = + static_cast(L->userdata); // Const is applied to whats on the left first, if + // there is nothing, it applies to that on the right + const auto capabilities = Security::GetSingleton()->IdentityToCapabilities(identity); plStateUd->contextInformation = RBX::Security::ExtendedIdentity{identity, 0, nullptr}; plStateUd->capabilities = capabilities; } @@ -146,11 +159,11 @@ bool Security::IsOurThread(lua_State *L) { /// This way, we can set the bit 47th, used to describe NOTHING, to set it as /// our thread. Then we & it to validate it is present on the integer with an AND, which it shouldn't be ever if /// its anything normal, but we aren't normal! - /// When doing the bit shift, in C by default numbers are int32_t, we must use int64_t, thus we must post-fix the - /// the number with 'll' - const auto extraSpace = static_cast(L->userdata); + /// When doing the bit shift, in C++ by default numbers are std::int32_t, we must use std::uint64_t, thus we must + /// post-fix the the number with 'ull' + const auto extraSpace = static_cast(L->userdata); const auto logger = Logger::GetSingleton(); - const auto passed = (extraSpace->capabilities & (1ull << 48ll)) == (1ull << 48ull); + const auto passed = (extraSpace->capabilities & (1ull << 48ull)) == (1ull << 48ull); return passed; } @@ -166,7 +179,7 @@ bool Security::SetLuaClosureSecurity(Closure *lClosure, std::uint32_t identity) return true; } -void walk_proto_wipe(lua_State *L, Proto *proto) { +void walk_proto_wipe(lua_State *const L, Proto *const proto) { proto->debugname = nullptr; proto->source = luaS_new(L, ""); proto->linedefined = -1; diff --git a/main.cpp b/main.cpp index b3e4fd5..7b26bba 100644 --- a/main.cpp +++ b/main.cpp @@ -35,66 +35,71 @@ long exception_filter(PEXCEPTION_POINTERS pExceptionPointers) { const auto *pContext = pExceptionPointers->ContextRecord; printf("\r\n-- WARNING: Exception handler caught an exception\r\n"); - if (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { - printf("Exception Identified: EXCEPTION_ACCESS_VIOLATION\r\n"); - } + printf("Exception Code: %llx\r\n", pExceptionPointers->ExceptionRecord->ExceptionCode); + printf("Exception Address: %p\r\n", pExceptionPointers->ExceptionRecord->ExceptionAddress); + try { std::rethrow_exception(std::current_exception()); } catch (const std::exception &ex) { printf("\nIntercepted C++/Cxx exception!\nError Reason: '%s'\n. Resuming SEH handler!\n", ex.what()); } catch (...) { - printf("\nNo current C++/Cxx exception? Resuming SEH handler!\n"); + printf("\nNo current C++/Cxx exception? This should never happen but alas, Resuming SEH handler!\n"); } - printf("Exception Caught @ %p\r\n", pExceptionPointers->ContextRecord->Rip); - printf("Module.dll @ %p\r\n", reinterpret_cast(GetModuleHandleA("Module.dll"))); - printf("Rebased Module @ 0x%p\r\n", - pExceptionPointers->ContextRecord->Rip - reinterpret_cast(GetModuleHandleA("Module.dll"))); - printf("RobloxStudioBeta.exe @ %p\r\n", - reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe"))); + printf("Thread RI P @ %p\r\n", reinterpret_cast(pExceptionPointers->ContextRecord->Rip)); + printf("Module.dll @ %p\r\n", reinterpret_cast(GetModuleHandleA("Module.dll"))); + printf("Rebased Module @ %p\r\n", + reinterpret_cast(pExceptionPointers->ContextRecord->Rip - + reinterpret_cast(GetModuleHandleA("Module.dll")))); + printf("RobloxStudioBeta.exe @ %p\r\n", reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe"))); - printf("Rebased Studio @ 0x%p\r\n", - pContext->Rip - reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe"))); + printf("Rebased Studio @ %p\r\n", + reinterpret_cast(pContext->Rip - + reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe")))); printf("-- START REGISTERS STATE --\r\n\r\n"); - printf("-- START GP REGISTERS --\r\n"); - - printf("RAX: 0x%p\r\n", pContext->Rax); - printf("RBX: 0x%p\r\n", pContext->Rbx); - printf("RCX: 0x%p\r\n", pContext->Rcx); - printf("RDX: 0x%p\r\n", pContext->Rdx); - printf("RDI: 0x%p\r\n", pContext->Rdi); - printf("RSI: 0x%p\r\n", pContext->Rsi); - printf("-- R8 - R15 --\r\n"); - printf("R08: 0x%p\r\n", pContext->R8); - printf("R09: 0x%p\r\n", pContext->R9); - printf("R10: 0x%p\r\n", pContext->R10); - printf("R11: 0x%p\r\n", pContext->R11); - printf("R12: 0x%p\r\n", pContext->R12); - printf("R13: 0x%p\r\n", pContext->R13); - printf("R14: 0x%p\r\n", pContext->R14); - printf("R15: 0x%p\r\n", pContext->R15); + printf("-- START GENERAL PURPOSE REGISTERS --\r\n"); + + printf("RAX: 0x%llx\r\n", pContext->Rax); + printf("RBX: 0x%llx\r\n", pContext->Rbx); + printf("RCX: 0x%llx\r\n", pContext->Rcx); + printf("RDX: 0x%llx\r\n", pContext->Rdx); + printf("RDI: 0x%llx\r\n", pContext->Rdi); + printf("RSI: 0x%llx\r\n", pContext->Rsi); + printf("R08: 0x%llx\r\n", pContext->R8); + printf("R09: 0x%llx\r\n", pContext->R9); + printf("R10: 0x%llx\r\n", pContext->R10); + printf("R11: 0x%llx\r\n", pContext->R11); + printf("R12: 0x%llx\r\n", pContext->R12); + printf("R13: 0x%llx\r\n", pContext->R13); + printf("R14: 0x%llx\r\n", pContext->R14); + printf("R15: 0x%llx\r\n", pContext->R15); + printf("-- END GP REGISTERS --\r\n\r\n"); printf("-- START STACK POINTERS --\r\n"); - printf("RBP: 0x%p\r\n", pContext->Rbp); - printf("RSP: 0x%p\r\n", pContext->Rsp); + printf("RBP: 0x%llx\r\n", pContext->Rbp); + printf("RSP: 0x%llx\r\n", pContext->Rsp); printf("-- END STACK POINTERS --\r\n\r\n"); printf("-- END REGISTERS STATE --\r\n\r\n"); printf(" -- Stack Trace:\r\n"); - SymInitialize(GetCurrentProcess(), nullptr, TRUE); + + const auto hCurrentProcess = GetCurrentProcess(); + + SymInitialize(hCurrentProcess, nullptr, true); void *stack[256]; - const unsigned short frameCount = RtlCaptureStackBackTrace(0, 100, stack, nullptr); + const unsigned short frameCount = RtlCaptureStackBackTrace(0, 255, stack, nullptr); for (unsigned short i = 0; i < frameCount; ++i) { - auto address = reinterpret_cast(stack[i]); - char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + const auto address = reinterpret_cast(stack[i]); + char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(char)]; // Stack allocations do not get constantly + // reallocated, this ain't the heap. auto *symbol = reinterpret_cast(symbolBuffer); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; @@ -103,26 +108,20 @@ long exception_filter(PEXCEPTION_POINTERS pExceptionPointers) { DWORD *pValue = &value; if (SymFromAddr(GetCurrentProcess(), address, nullptr, symbol) && ((*pValue = symbol->Address - address)) && SymFromAddr(GetCurrentProcess(), address, reinterpret_cast(pValue), symbol)) { - printf(("[Stack Frame %d] Inside %s @ 0x%p; Studio Rebase: 0x%p\r\n"), i, symbol->Name, address, - address - reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe")) + 0x140000000); - } else { - printf(("[Stack Frame %d] Unknown Subroutine @ 0x%p; Studio Rebase: 0x%p\r\n"), i, symbol->Name, address, - address - reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe")) + 0x140000000); - } - } - std::cout << std::endl; - std::stringstream sstream{}; - for (unsigned short i = 0; i < frameCount; ++i) { - sstream << ("0x") << std::hex << reinterpret_cast(stack[i]); - if (i < frameCount) { - sstream << (" -> "); + printf(("[Stack Frame %d] Inside %s @ %p; Studio Rebase: %p\r\n"), i, symbol->Name, + reinterpret_cast(address), + reinterpret_cast(address - + reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe")) + + 0x140000000)); } else { - sstream << ("\r\n"); + printf(("[Stack Frame %d] Unknown Subroutine @ %p; Studio Rebase: %p\r\n"), i, + reinterpret_cast(address), + reinterpret_cast(address - + reinterpret_cast(GetModuleHandleA("RobloxStudioBeta.exe")) + + 0x140000000)); } } - std::cout << sstream.str(); - // Clean up SymCleanup(GetCurrentProcess()); MessageBoxA(nullptr, ("Studio Crash"), ("RbxStu exception filter has been run! Stacktrace on Studio's CLI."), @@ -170,13 +169,92 @@ int main() { std::thread(Communication::HandlePipe, "CommunicationPipe").detach(); logger->PrintInformation(RbxStu::MainThread, "-- Initializing DebuggerManager..."); - const auto debuggerManager = DebuggerManager::GetSingleton(); - debuggerManager->Initialize(); + auto debugManagerInitialize = std::thread([]() { + const auto debuggerManager = DebuggerManager::GetSingleton(); + debuggerManager->Initialize(); + }); logger->PrintInformation(RbxStu::MainThread, "Running mod initialization step..."); modManager->InitializeMods(); logger->PrintInformation(RbxStu::MainThread, "All mods have been initialized."); + logger->PrintWarning(RbxStu::MainThread, "Asserting if execution is possible (Evaluating found functions)..."); + + try { + auto hasFailedAnyCheck = false; + if (robloxManager->GetRobloxFunction("RBX::ScriptContext::task_defer") == nullptr || + robloxManager->GetRobloxFunction("RBX::ScriptContext::task_spawn") == nullptr) { + + if (robloxManager->GetRobloxFunction("RBX::ScriptContext::task_defer") == nullptr) + logger->PrintError(RbxStu::MainThread, + "Failed to find RBX::ScriptContext::task_defer; If RBX::ScriptContext::task_spawn " + "is found, scheduling will not be an issue!"); + if (robloxManager->GetRobloxFunction("RBX::ScriptContext::task_spawn") == nullptr) + logger->PrintError(RbxStu::MainThread, + "Failed to find RBX::ScriptContext::task_spawn; If RBX::ScriptContext::task_delay " + "is found, scheduling will not be an issue!"); + + hasFailedAnyCheck = true; + } else if (robloxManager->GetRobloxFunction("RBX::ScriptContext::task_defer") == nullptr || + robloxManager->GetRobloxFunction("RBX::ScriptContext::task_spawn") == nullptr) { + if (robloxManager->GetRobloxFunction("RBX::ScriptContext::task_spawn") == nullptr) + logger->PrintError(RbxStu::MainThread, "Execution is not possible! Cannot schedule Luau through the " + "ROBLOX scheduler! Refusing to continue!"); + + throw std::exception("RbxStu V2 cannot execute scripts; No method of scheduling through the ROBLOX " + "scheduler was found, AOBs must be updated!"); + } + + if (robloxManager->GetRobloxFunction("RBX::ScriptContext::resumeDelayedThreads") == nullptr) { + logger->PrintError(RbxStu::MainThread, + "Execution is not possible! Cannot safely hook into ROBLOX's scheduler via " + "RBX::ScriptContext::resumeDelayedThreads! This makes it impossible to guarantee safe " + "environment initialization! Refusing to continue!"); + + throw std::exception("RbxStu V2 cannot execute scripts; No way of initializing in a stable manner was " + "found, AOBs must be updated!"); + } + + if (robloxManager->GetRobloxFunction("RBX::ScriptContext::resume") == nullptr) { + logger->PrintError(RbxStu::MainThread, + "Execution, whilst possible, may face issues in some functions! Refusing to continue!"); + throw std::exception("RBX::ScriptContext::resume could not be found! Yielding safely for C closures is not " + "possible, AOBs must be updated!"); + } + + if (robloxManager->GetRobloxFunction("RBX::DataModel::getStudioGameStateType") == nullptr) { + logger->PrintError( + RbxStu::MainThread, + "Execution, whilst possible, will be unstable and unsafe as the DataModel that will be used for " + "execution may lead to security issues (As it cannot be determined for certain which it is!)"); + throw std::exception( + "RBX::DataModel::getStudioGameStateType could not be found; This makes initialization impossible"); + } + + if (!robloxManager->GetFastVariable("TaskSchedulerTargetFps").has_value()) { + logger->PrintError(RbxStu::MainThread, "setfpscap and getfpscap are unavailable and will error when " + "invoked! Failed to find required IntFastFlag!"); + hasFailedAnyCheck = true; + } + + if (hasFailedAnyCheck) { + logger->PrintWarning(RbxStu::MainThread, "RbxStu V2 may need to update its AOBs, some offset is missing, " + "but it should not have any major issues on execution!"); + } else { + logger->PrintWarning( + RbxStu::MainThread, + "RbxStu V2 has all the required offsets to work as expected, no further action is required."); + } + } catch (const std::exception &ex) { + logger->PrintError(RbxStu::MainThread, + "RbxStu V2 NEEDS to update! A significant offset is missing, which may hinder execution in " + "a significant manner! Please contact the developers, RbxStu V2 will now crash :'("); + throw; + } + + logger->PrintInformation(RbxStu::MainThread, "Waiting for DebuggerManager..."); + debugManagerInitialize.join(); + logger->PrintInformation(RbxStu::MainThread, "Main Thread will now close, as RbxStu V2's initialization has been completed."); @@ -210,7 +288,9 @@ BOOL WINAPI DllMain(const HINSTANCE hModule, const DWORD fdwReason, const LPVOID // Perform any necessary cleanup. break; - default:; + default: + + break; } return true; }