-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathUtilities.hpp
279 lines (229 loc) · 9.37 KB
/
Utilities.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
//
// Created by Dottik on 10/8/2024.
//
#pragma once
#include <Windows.h>
#include <filesystem>
#include <hex.h>
#include <regex>
#include <sha.h>
#include <sstream>
#include <string>
#include <tlhelp32.h>
#include <vector>
#include "Logger.hpp"
#include "lualib.h"
namespace RbxStu {
namespace Regexes {
static std::regex LUA_ERROR_CALLSTACK_REGEX;
}
} // namespace RbxStu
class Utilities final {
struct ThreadInformation {
bool bWasSuspended;
HANDLE hThread;
};
enum ThreadSuspensionState {
SUSPENDED,
RESUMED,
};
public:
class RobloxThreadSuspension {
std::vector<ThreadInformation> threadInformation;
ThreadSuspensionState state;
public:
explicit RobloxThreadSuspension(const bool suspendOnCreate) {
this->state = RESUMED;
if (suspendOnCreate)
this->SuspendThreads();
}
~RobloxThreadSuspension() {
const auto logger = Logger::GetSingleton();
logger->PrintInformation(RbxStu::ThreadManagement, "Cleaning up thread handles!");
for (auto &[_, hThread]: this->threadInformation) {
if (state == SUSPENDED)
ResumeThread(hThread);
CloseHandle(hThread);
}
}
void SuspendThreads() {
const auto logger = Logger::GetSingleton();
if (this->state != RESUMED) {
logger->PrintDebug(RbxStu::ThreadManagement,
"Trying to suspend threads while they are already suspended!");
return;
}
logger->PrintDebug(RbxStu::ThreadManagement, "Pausing roblox threads!");
const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE || hSnapshot == nullptr) {
throw std::exception("PauseRobloxThreads failed: Snapshot creation failed!");
}
THREADENTRY32 te{0};
te.dwSize = sizeof(THREADENTRY32);
if (!Thread32First(hSnapshot, &te)) {
CloseHandle(hSnapshot);
throw std::exception("PauseRobloxThreads failed: Thread32First failed!");
}
const auto currentPid = GetCurrentProcessId();
std::vector<ThreadInformation> thInfo;
do {
if (te.th32ThreadID != GetCurrentThreadId() && te.th32OwnerProcessID == currentPid) {
auto hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
auto th = ThreadInformation{false, nullptr};
if (SuspendThread(hThread) > 1)
th.bWasSuspended = true;
th.hThread = hThread;
thInfo.push_back(th);
}
} while (Thread32Next(hSnapshot, &te));
this->threadInformation = thInfo;
this->state = SUSPENDED;
}
void ResumeThreads() {
const auto logger = Logger::GetSingleton();
if (this->state != SUSPENDED) {
logger->PrintDebug(RbxStu::ThreadManagement,
"Attempting to resume threads while they are already resumed!");
return;
}
logger->PrintDebug(RbxStu::ThreadManagement, "Resuming roblox threads!");
for (auto &[bWasSuspended, hThread]: this->threadInformation) {
ResumeThread(hThread);
}
this->state = RESUMED;
}
};
static void Initialize();
__forceinline static std::pair<bool, std::string> getInstanceType(lua_State *L, int index) {
luaL_checktype(L, index, LUA_TUSERDATA);
lua_getglobal(L, "typeof");
lua_pushvalue(L, index);
lua_call(L, 1, 1);
if (const bool isInstance = (strcmp(lua_tostring(L, -1), "Instance") == 0); !isInstance) {
const auto str = lua_tostring(L, -1);
lua_pop(L, 1);
return {false, str};
}
lua_pop(L, 1);
lua_getfield(L, index, "ClassName");
auto className = lua_tostring(L, -1);
lua_pop(L, 1);
return {true, className};
}
static std::string StripLuaErrorMessage(const std::string &message);
__forceinline static void checkInstance(lua_State *L, int index, const char *expectedClassname) {
luaL_checktype(L, index, LUA_TUSERDATA);
lua_getglobal(L, "typeof");
lua_pushvalue(L, index);
lua_call(L, 1, 1);
const bool isInstance = (strcmp(lua_tostring(L, -1), "Instance") == 0);
lua_pop(L, 1);
if (!isInstance)
luaL_argerror(L, index, "expected an Instance");
if (strcmp(expectedClassname, "ANY") == 0)
return;
lua_getfield(L, index, "IsA");
lua_pushvalue(L, index);
lua_pushstring(L, expectedClassname);
lua_call(L, 2, 1);
const bool isExpectedClass = lua_toboolean(L, -1);
lua_pop(L, 1);
if (!isExpectedClass)
luaL_argerror(L, index, std::format("Expected to be {}", expectedClassname).c_str());
}
__forceinline static std::string GetDllDir() {
HMODULE hModule = nullptr;
if (char path[MAX_PATH];
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
"Module.dll", &hModule) &&
GetModuleFileNameA(hModule, path, sizeof(path))) {
const std::filesystem::path fullPath(path);
return fullPath.parent_path().string();
}
return "";
}
__forceinline static std::string ToLower(std::string target) {
for (auto &x: target) {
x = std::tolower(x); // NOLINT(*-narrowing-conversions)
}
return target;
}
/// @brief Splits the given std::string into a std::vector<std::string> using the given character as a
/// separator.
__forceinline static std::vector<std::string> SplitBy(const std::string &target, const char split) {
std::vector<std::string> splitted;
std::stringstream stream(target);
std::string temporal;
while (std::getline(stream, temporal, split)) {
splitted.push_back(temporal);
temporal.clear();
}
return splitted;
}
/// @return True if the DLL is running in a WINE powered environment underneath Linux.
__forceinline static bool IsWine() {
return GetProcAddress(GetModuleHandle("ntdll.dll"), "wine_get_version") != nullptr;
}
/// @brief Used to validate a pointer.
/// @remarks This template does NOT validate ANY data inside the pointer. It just validates that the pointer is
/// at LEAST of the size of the given type, and that the pointer is allocated in memory.
template<typename T>
__forceinline static bool IsPointerValid(T *tValue) { // Validate pointers.
const auto ptr = reinterpret_cast<const void *>(const_cast<const T *>(tValue));
auto buf = MEMORY_BASIC_INFORMATION{};
// Query a full page.
if (const auto read = VirtualQuery(ptr, &buf, sizeof(buf)); read != 0 && sizeof(buf) != read) {
// I honestly dont care.
} else if (read == 0) {
return false;
}
if (buf.RegionSize < sizeof(T)) {
return false; // Allocated region is too small to fit type T inside.
}
if (buf.State & MEM_FREE == MEM_FREE) {
return false; // The memory is not owned by the process, no need to do anything, we can already assume
// we cannot read it.
}
auto validProtections = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ |
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
if (buf.Protect & validProtections) {
return true;
}
if (buf.Protect & (PAGE_GUARD | PAGE_NOACCESS)) {
return false;
}
return true;
}
__forceinline static std::optional<const std::string> GetHwid() {
auto logger = Logger::GetSingleton();
HW_PROFILE_INFO hwProfileInfo;
if (!GetCurrentHwProfileA(&hwProfileInfo)) {
logger->PrintError(RbxStu::Anonymous,
"Failed to obtain Hardware Identifier from GetCurrentHwProfileA, returning empty!");
return {};
}
CryptoPP::SHA256 sha256;
unsigned char digest[CryptoPP::SHA256::DIGESTSIZE];
sha256.CalculateDigest(digest, reinterpret_cast<unsigned char *>(hwProfileInfo.szHwProfileGuid),
sizeof(hwProfileInfo.szHwProfileGuid));
CryptoPP::HexEncoder encoder;
std::string output;
encoder.Attach(new CryptoPP::StringSink(output));
encoder.Put(digest, sizeof(digest));
encoder.MessageEnd();
return output;
}
__forceinline static void GetService(lua_State *L, const std::string &serviceName) {
lua_getglobal(L, "game");
lua_getfield(L, -1, "GetService");
lua_pushvalue(L, -2);
lua_pushstring(L, serviceName.c_str());
lua_pcall(L, 2, 1, 0);
lua_remove(L, -2);
}
};
// Concepts are virtually T : where ... in C#, type constraints on templates.
namespace RbxStu::Concepts {
template<typename Derived, typename Base>
concept TypeConstraint = std::is_base_of_v<Base, Derived>;
}