Skip to content
This repository has been archived by the owner on Jun 21, 2024. It is now read-only.

Commit

Permalink
update signature, add extra security daemon check, refactor some code
Browse files Browse the repository at this point in the history
  • Loading branch information
axstin committed Nov 10, 2022
1 parent d62e0f2 commit d407166
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 122 deletions.
256 changes: 139 additions & 117 deletions Source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,184 +86,206 @@ HANDLE GetRobloxProcess()
return processes[selection - 1];
}

This comment was marked as spam.

Copy link
@willlamsfamily345

willlamsfamily345 Nov 12, 2022

can i play you game in roblox


size_t FindTaskSchedulerFrameDelayOffset(HANDLE process, const void *scheduler)
void NotifyError(const char* title, const char* error)
{
const size_t search_offset = 0x100; // ProcUtil::IsProcess64Bit(process) ? 0x200 : 0x100;
if (Settings::SilentErrors || Settings::NonBlockingErrors)
{
// lol
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO info{};
GetConsoleScreenBufferInfo(console, &info);

uint8_t buffer[0x100];
if (!ProcUtil::Read(process, (const uint8_t *)scheduler + search_offset, buffer, sizeof(buffer)))
return -1;
WORD color = (info.wAttributes & 0xFF00) | FOREGROUND_RED | FOREGROUND_INTENSITY;
SetConsoleTextAttribute(console, color);

printf("[ERROR] %s\n", error);

SetConsoleTextAttribute(console, info.wAttributes);

/* Find the frame delay variable inside TaskScheduler (ugly, but it should survive updates unless the variable is removed or shifted)
(variable was at +0x150 (32-bit) and +0x180 (studio 64-bit) as of 2/13/2020) */
for (int i = 0; i < sizeof(buffer) - sizeof(double); i += 4)
if (!Settings::SilentErrors)
{
UI::SetConsoleVisible(true);
}
}
else
{
static const double frame_delay = 1.0 / 60.0;
double difference = *(double *)(buffer + i) - frame_delay;
difference = difference < 0 ? -difference : difference;
if (difference < std::numeric_limits<double>::epsilon()) return search_offset + i;
MessageBoxA(UI::Window, error, title, MB_OK);
}

return -1;
}

const void *FindTaskScheduler(HANDLE process, const char **error = nullptr)
struct RobloxProcess
{
try
HANDLE handle = NULL;
ProcUtil::ModuleInfo main_module{};
const void *ts_ptr = nullptr; // task scheduler pointer
const void *fd_ptr = nullptr; // frame delay pointer

int retries_left = 0;

bool Attach(HANDLE process, int retry_count)
{
ProcUtil::ProcessInfo info;
handle = process;
retries_left = retry_count;

if (!BlockingLoadModuleInfo())
{
NotifyError("rbxfpsunlocker Error", "Failed to get process base! Restart Roblox FPS Unlocker or, if you are on a 64-bit operating system, make sure you are using the 64-bit version of Roblox FPS Unlocker.");
retries_left = -1;
return false;
}
else
{
printf("[%p] Process base: %p (size %zu)\n", handle, main_module.base, main_module.size);

// Small windows exist where we can attach to Roblox's security daemon while it isn't being debugged (see GetRobloxProcesses)
// As a secondary measure, check module size (daemon is about 1MB, client is about 80MB)
if (main_module.size < 1024 * 1024 * 10)
{
printf("[%p] Ignoring security daemon process\n", handle);
retries_left = -1;
return false;
}

Tick();

return ts_ptr != nullptr && fd_ptr != nullptr;
}
}

// TODO: remove this retry code? (see RobloxProcess::Tick)
bool BlockingLoadModuleInfo()
{
int tries = 5;
int wait_time = 100;

printf("[%p] Finding process base...\n", handle);

while (true)
{
info = ProcUtil::ProcessInfo(process);
ProcUtil::ProcessInfo info = ProcUtil::ProcessInfo(handle);

if (info.module.base != nullptr)
break;
{
main_module = info.module;
return true;
}

if (tries--)
{
printf("[%p] Retrying in %dms...\n", process, wait_time);
printf("[%p] Retrying in %dms...\n", handle, wait_time);
Sleep(wait_time);
wait_time *= 2;
}
else
} else
{
if (error) *error = "Failed to get process base! Restart Roblox FPS Unlocker or, if you are on a 64-bit operating system, make sure you are using the 64-bit version of Roblox FPS Unlocker.";
return nullptr;
return false;
}
}
}

auto start = (const uint8_t *)info.module.base;
auto end = start + info.module.size;

printf("[%p] Process Base: %p\n", process, start);

if (ProcUtil::IsProcess64Bit(process))
const void *FindTaskScheduler() const
{
try
{
// 40 53 48 83 EC 20 0F B6 D9 E8 ?? ?? ?? ?? 86 58 04 48 83 C4 20 5B C3
if (auto result = (const uint8_t *)ProcUtil::ScanProcess(process, "\x40\x53\x48\x83\xEC\x20\x0F\xB6\xD9\xE8\x00\x00\x00\x00\x86\x58\x04\x48\x83\xC4\x20\x5B\xC3", "xxxxxxxxxx????xxxxxxxxx", start, end))
const auto start = (const uint8_t *)main_module.base;
const auto end = start + main_module.size;

if (ProcUtil::IsProcess64Bit(handle))
{
auto gts_fn = result + 14 + ProcUtil::Read<int32_t>(process, result + 10);
// 40 53 48 83 EC 20 0F B6 D9 E8 ?? ?? ?? ?? 86 58 04 48 83 C4 20 5B C3
if (auto result = (const uint8_t *)ProcUtil::ScanProcess(handle, "\x40\x53\x48\x83\xEC\x20\x0F\xB6\xD9\xE8\x00\x00\x00\x00\x86\x58\x04\x48\x83\xC4\x20\x5B\xC3", "xxxxxxxxxx????xxxxxxxxx", start, end))
{
auto gts_fn = result + 14 + ProcUtil::Read<int32_t>(handle, result + 10);

printf("[%p] GetTaskScheduler: %p\n", process, gts_fn);
printf("[%p] GetTaskScheduler (sig 0): %p\n", handle, gts_fn);

uint8_t buffer[0x100];
if (ProcUtil::Read(process, gts_fn, buffer, sizeof(buffer)))
{
if (auto inst = sigscan::scan("\x48\x8B\x05\x00\x00\x00\x00\x48\x83\xC4\x28", "xxx????xxxx", (uintptr_t)buffer, (uintptr_t)buffer + 0x100)) // mov eax, <TaskSchedulerPtr>; mov ecx, [ebp-0Ch])
uint8_t buffer[0x100];
if (ProcUtil::Read(handle, gts_fn, buffer, sizeof(buffer)))
{
const uint8_t *remote = gts_fn + (inst - buffer);
return remote + 7 + *(int32_t *)(inst + 3);
if (auto inst = sigscan::scan("\x48\x8B\x05\x00\x00\x00\x00\x48\x83\xC4\x28", "xxx????xxxx", (uintptr_t)buffer, (uintptr_t)buffer + 0x100)) // mov eax, <TaskSchedulerPtr>; mov ecx, [ebp-0Ch])
{
const uint8_t *remote = gts_fn + (inst - buffer);
return remote + 7 + *(int32_t *)(inst + 3);
}
}
}
}
}
else
{
// 55 8B EC 83 E4 F8 83 EC 08 E8 ?? ?? ?? ?? 8D 0C 24
if (auto result = (const uint8_t *)ProcUtil::ScanProcess(process, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x08\xE8\x00\x00\x00\x00\x8D\x0C\x24", "xxxxxxxxxx????xxx", start, end))
} else
{
auto gts_fn = result + 14 + ProcUtil::Read<int32_t>(process, result + 10);
// old signature (LTCG): 55 8B EC 83 E4 F8 83 EC 08 E8 ?? ?? ?? ?? 8D 0C 24
// 55 8B EC 83 EC 10 56 E8 ?? ?? ?? ?? 8B F0 8D 45 F0
if (auto result = (const uint8_t *)ProcUtil::ScanProcess(handle, "\x55\x8B\xEC\x83\xEC\x10\x56\xE8\x00\x00\x00\x00\x8B\xF0\x8D\x45\xF0", "xxxxxxxx????xxxxx", start, end))
{
auto gts_fn = result + 12 + ProcUtil::Read<int32_t>(handle, result + 8);

printf("[%p] GetTaskScheduler: %p\n", process, gts_fn);
printf("[%p] GetTaskScheduler (sig 1): %p\n", handle, gts_fn);

uint8_t buffer[0x100];
if (ProcUtil::Read(process, gts_fn, buffer, sizeof(buffer)))
{
if (auto inst = sigscan::scan("\xA1\x00\x00\x00\x00\x8B\x4D\xF4", "x????xxx", (uintptr_t)buffer, (uintptr_t)buffer + 0x100)) // mov eax, <TaskSchedulerPtr>; mov ecx, [ebp-0Ch])
uint8_t buffer[0x100];
if (ProcUtil::Read(handle, gts_fn, buffer, sizeof(buffer)))
{
//printf("[%p] Inst: %p\n", process, gts_fn + (inst - buffer));
return (const void *)(*(uint32_t *)(inst + 1));
if (auto inst = sigscan::scan("\xA1\x00\x00\x00\x00\x8B\x4D\xF4", "x????xxx", (uintptr_t)buffer, (uintptr_t)buffer + 0x100)) // mov eax, <TaskSchedulerPtr>; mov ecx, [ebp-0Ch])
{
//printf("[%p] Inst: %p\n", process, gts_fn + (inst - buffer));
return (const void *)(*(uint32_t *)(inst + 1));
}
}
}
}
// 55 8B EC 83 E4 F8 83 EC 14 56 E8 ?? ?? ?? ?? 8D 4C 24 10
else if (auto result = (const uint8_t*)ProcUtil::ScanProcess(process, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x14\x56\xE8\x00\x00\x00\x00\x8D\x4C\x24\x10", "xxxxxxxxxxx????xxxx", start, end))
{
auto gts_fn = result + 15 + ProcUtil::Read<int32_t>(process, result + 11);
// 55 8B EC 83 E4 F8 83 EC 14 56 E8 ?? ?? ?? ?? 8D 4C 24 10
else if (auto result = (const uint8_t *)ProcUtil::ScanProcess(handle, "\x55\x8B\xEC\x83\xE4\xF8\x83\xEC\x14\x56\xE8\x00\x00\x00\x00\x8D\x4C\x24\x10", "xxxxxxxxxxx????xxxx", start, end))
{
auto gts_fn = result + 15 + ProcUtil::Read<int32_t>(handle, result + 11);

printf("[%p] GetTaskScheduler: %p\n", process, gts_fn);
printf("[%p] GetTaskScheduler (sig 2): %p\n", handle, gts_fn);

uint8_t buffer[0x100];
if (ProcUtil::Read(process, gts_fn, buffer, sizeof(buffer)))
{
if (auto inst = sigscan::scan("\xA1\x00\x00\x00\x00\x8B\x4D\xF4", "x????xxx", (uintptr_t)buffer, (uintptr_t)buffer + 0x100)) // mov eax, <TaskSchedulerPtr>; mov ecx, [ebp-0Ch])
uint8_t buffer[0x100];
if (ProcUtil::Read(handle, gts_fn, buffer, sizeof(buffer)))
{
return (const void*)(*(uint32_t*)(inst + 1));
if (auto inst = sigscan::scan("\xA1\x00\x00\x00\x00\x8B\x4D\xF4", "x????xxx", (uintptr_t)buffer, (uintptr_t)buffer + 0x100)) // mov eax, <TaskSchedulerPtr>; mov ecx, [ebp-0Ch])
{
return (const void *)(*(uint32_t *)(inst + 1));
}
}
}
}
}
}
catch (ProcUtil::WindowsException& e)
{
}
catch (ProcUtil::WindowsException &e)
{
}

return nullptr;
}
return nullptr;
}

void NotifyError(const char* title, const char* error)
{
if (Settings::SilentErrors || Settings::NonBlockingErrors)
size_t FindTaskSchedulerFrameDelayOffset(const void *scheduler) const
{
// lol
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO info{};
GetConsoleScreenBufferInfo(console, &info);

WORD color = (info.wAttributes & 0xFF00) | FOREGROUND_RED | FOREGROUND_INTENSITY;
SetConsoleTextAttribute(console, color);
const size_t search_offset = 0x100; // ProcUtil::IsProcess64Bit(process) ? 0x200 : 0x100;

printf("[ERROR] %s\n", error);
uint8_t buffer[0x100];
if (!ProcUtil::Read(handle, (const uint8_t *)scheduler + search_offset, buffer, sizeof(buffer)))
return -1;

SetConsoleTextAttribute(console, info.wAttributes);

if (!Settings::SilentErrors)
/* Find the frame delay variable inside TaskScheduler (ugly, but it should survive updates unless the variable is removed or shifted)
(variable was at +0x150 (32-bit) and +0x180 (studio 64-bit) as of 2/13/2020) */
for (int i = 0; i < sizeof(buffer) - sizeof(double); i += 4)
{
UI::SetConsoleVisible(true);
static const double frame_delay = 1.0 / 60.0;
double difference = *(double *)(buffer + i) - frame_delay;
difference = difference < 0 ? -difference : difference;
if (difference < std::numeric_limits<double>::epsilon()) return search_offset + i;
}
}
else
{
MessageBoxA(UI::Window, error, title, MB_OK);
}
}

struct RobloxProcess
{
HANDLE handle = NULL;
const void *ts_ptr = nullptr; // task scheduler pointer
const void *fd_ptr = nullptr; // frame delay pointer

int retries_left = 0;

bool Attach(HANDLE process, int retry_count)
{
handle = process;
retries_left = retry_count;

Tick();

return ts_ptr != nullptr && fd_ptr != nullptr;
return -1;
}

void Tick()
{
if (retries_left < 0) return; // we tried
if (retries_left < 0)
return; // we tried

if (!ts_ptr)
{
const char* error = nullptr;
ts_ptr = FindTaskScheduler(handle, &error);
ts_ptr = FindTaskScheduler();

if (!ts_ptr)
{
if (error) retries_left = 0; // if FindTaskScheduler returned an error it already retried 5 times. TODO: remove
if (retries_left-- <= 0)
NotifyError("rbxfpsunlocker Error", error ? error : "Unable to find TaskScheduler! This is probably due to a Roblox update-- watch the github for any patches or a fix.");
NotifyError("rbxfpsunlocker Error", "Unable to find TaskScheduler! This is probably due to a Roblox update-- watch the github for any patches or a fix.");
return;
}
}
Expand All @@ -276,15 +298,15 @@ struct RobloxProcess
{
printf("[%p] Scheduler: %p\n", handle, scheduler);

size_t delay_offset = FindTaskSchedulerFrameDelayOffset(handle, scheduler);
size_t delay_offset = FindTaskSchedulerFrameDelayOffset(scheduler);
if (delay_offset == -1)
{
if (retries_left-- <= 0)
NotifyError("rbxfpsunlocker Error", "Variable scan failed! Make sure your framerate is at ~60.0 FPS (press Shift+F5 in-game) before using Roblox FPS Unlocker.");
return;
}

printf("[%p] Frame Delay Offset: %zu (%zx)\n", handle, delay_offset, delay_offset);
printf("[%p] Frame delay offset: %zu (0x%zx)\n", handle, delay_offset, delay_offset);

fd_ptr = scheduler + delay_offset;

Expand Down Expand Up @@ -377,7 +399,7 @@ DWORD WINAPI WatchThread(LPVOID)

if (code != STILL_ACTIVE)
{
printf("Purging dead process %p (pid %d) (code %X)\n", process, GetProcessId(process), code);
printf("Purging dead process %p (pid %d, code %X)\n", process, GetProcessId(process), code);
it = AttachedProcesses.erase(it);
CloseHandle(process);
printf("New size: %zu\n", AttachedProcesses.size());
Expand Down
8 changes: 4 additions & 4 deletions Source/procutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ ProcUtil::ModuleInfo ProcUtil::GetModuleInfo(HANDLE process, HMODULE module)

bool found;

printf("ProcUtil::GetModuleInfo: buffer = %s\n", buffer);
printf("[ProcUtil] QueryFullProcessImageName(%p) returned %s\n", process, buffer);

try
{
found = FindModuleInfo(process, buffer, result);
}
catch (WindowsException& e)
{
printf("[%p] ProcUtil::GetModuleInfo failed: %s (%X)\n", process, e.what(), e.GetLastError());
printf("[ProcUtil] GetModuleInfo(%p, NULL) failed: %s (%X)\n", process, e.what(), e.GetLastError());
found = false;
}

Expand Down Expand Up @@ -122,15 +122,15 @@ ProcUtil::ModuleInfo ProcUtil::GetModuleInfo(HANDLE process, HMODULE module)

bool ProcUtil::FindModuleInfo(HANDLE process, const std::filesystem::path& path, ModuleInfo& out)
{
printf("ProcUtil::FindModuleInfo: path = %s\n", path.string().c_str());
printf("[ProcUtil] FindModuleInfo(%p, %s)\n", process, path.string().c_str());

for (auto module : GetProcessModules(process))
{
try
{
ModuleInfo info = GetModuleInfo(process, module);

printf("ProcUtil::FindModuleInfo: info.path = %s\n", path.string().c_str());
printf("\tbase=%p, size=%zu, path=%s\n", info.base, info.size, info.path.string().c_str());

if (std::filesystem::equivalent(info.path, path))
{
Expand Down
2 changes: 1 addition & 1 deletion Source/rfu.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#define RFU_VERSION "4.4.2"
#define RFU_VERSION "4.4.3"
#define RFU_GITHUB_REPO "axstin/rbxfpsunlocker"

bool CheckForUpdates();
Expand Down

0 comments on commit d407166

Please sign in to comment.