- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 765
- Reaction score
- 457
Anyone digging into Valorant asset modification knows the drill — Vanguard's signature checks and the inevitable "Corrupt Game Data" popup are the primary gatekeepers. If you are trying to load custom PAKs for chams, skinchangers, or map mods, simply calling a mount function isn't enough anymore because the game module will trigger a fatal exception or a forced exit the moment it sees an unsigned archive.
The Logic
This approach works by intercepting the communication between the engine and the user. Since the game relies on user32.dll and kernel32.dll to report corruption and terminate the process, we can hook these at the user-mode level (where Vanguard's protection is less aggressive regarding VirtualProtect calls) to suppress the alerts and block the exit signals.
Core Tech Stack:
The Implementation
Troubleshooting & Risks
While this method suppresses the visual and local triggers, it does not magically make the PAK itself "safe." If the game performs server-side checks on specific assets (like weapon stats or player heights), you'll still catch a manual ban or a developer kick. This is strictly for client-side visual modifications.
Before running this, make sure your injector is properly hidden and your offsets are updated for the current build. If your PAKs are not visible even after a successful mount log, check your file structure and ensure your .ucas and .utoc companions are in the same directory as your .pak.
While others are catching bans from broken free injectors and outdated asset loaders, Infocheats users are building robust solutions that handle the engine's internal checks.
Who else is testing this on the current build, and are you seeing any issues with the mount status returning false?
The Logic
This approach works by intercepting the communication between the engine and the user. Since the game relies on user32.dll and kernel32.dll to report corruption and terminate the process, we can hook these at the user-mode level (where Vanguard's protection is less aggressive regarding VirtualProtect calls) to suppress the alerts and block the exit signals.
Core Tech Stack:
- MessageBoxW/A Hooking — Suppresses the "Corrupt Game Data" dialogs before they even appear.
- Exit/Terminate Blocking — Prevents the process from killing itself when a signature mismatch is detected.
- PAK Signing Bypass — Zeroes out the signature check offset in the game module.
- Sacrificial Mount Thread — Executes the mount function in a separate thread to prevent main thread blocking or crashes.
The Implementation
Code:
#pragma once
#include <windows.h>
#include <cstdint>
#include <thread>
#include <fstream>
#include <intrin.h>
#include <tlhelp32.h>
#include <psapi.h>
#include "memory.hpp"
#include "offsets.hpp"
namespace pak_bypass
{
inline void log(const char* msg)
{
std::ofstream f("C:\\mods\\pak_debug.log", std::ios::app);
if (f.is_open()) { f << msg << std::endl; f.close(); }
}
inline void log_hex(const char* label, uintptr_t val)
{
char buf[256];
sprintf_s(buf, "%s: 0x%llX", label, (unsigned long long)val);
log(buf);
}
inline bool safe_zero(uintptr_t addr, size_t size)
{
__try { memset((void*)addr, 0, size); return true; }
__except (EXCEPTION_EXECUTE_HANDLER) { return false; }
}
inline __int64 safe_read_ptr(uintptr_t addr)
{
__try {
if (addr < 0x10000 || addr > 0x7FFFFFFFFFFF) return 0;
return *(__int64*)addr;
}
__except (EXCEPTION_EXECUTE_HANDLER) { return 0; }
}
inline __int64 get_fpak()
{
if (!memory::module_base) return 0;
auto delegate = safe_read_ptr(memory::module_base + offsets::fpak_platform_file);
if (!delegate) return 0;
return safe_read_ptr((uintptr_t)(delegate + 24));
}
// ── MessageBoxW Hook ──
// Hook user32!MessageBoxW to suppress "Corrupt Game Data" dialog
// user32.dll is NOT protected by Vanguard, so VirtualProtect works
// Original function bytes (saved for restoration)
inline uint8_t g_original_bytes[14] = {};
inline uintptr_t g_msgbox_addr = 0;
// Our replacement — suppress corruption dialogs, pass through others
inline int WINAPI hooked_MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
// Check if this is the corruption dialog
if (lpText && (wcsstr(lpText, L"Corrupt") || wcsstr(lpText, L"corrupt"))) {
log("[hook] Suppressed corruption MessageBox");
return IDOK; // Pretend user clicked OK
}
if (lpCaption && (wcsstr(lpCaption, L"Corrupt") || wcsstr(lpCaption, L"corrupt"))) {
log("[hook] Suppressed corruption MessageBox (caption)");
return IDOK;
}
// For other MessageBoxes, call original
// Unhook temporarily, call original, rehook
DWORD old_prot;
VirtualProtect((LPVOID)g_msgbox_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot);
memcpy((void*)g_msgbox_addr, g_original_bytes, 14);
VirtualProtect((LPVOID)g_msgbox_addr, 14, old_prot, &old_prot);
int result = MessageBoxW(hWnd, lpText, lpCaption, uType);
// Rehook
VirtualProtect((LPVOID)g_msgbox_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot);
// mov rax, addr; jmp rax (14 bytes)
*(uint16_t*)(g_msgbox_addr) = 0xB848;
*(uintptr_t*)(g_msgbox_addr + 2) = (uintptr_t)hooked_MessageBoxW;
*(uint16_t*)(g_msgbox_addr + 10) = 0xE0FF;
VirtualProtect((LPVOID)g_msgbox_addr, 14, old_prot, &old_prot);
return result;
}
inline bool hook_messagebox()
{
HMODULE user32 = GetModuleHandleA("user32.dll");
if (!user32) user32 = LoadLibraryA("user32.dll");
if (!user32) { log("[hook] user32.dll not found"); return false; }
// Hook MessageBoxW
g_msgbox_addr = (uintptr_t)GetProcAddress(user32, "MessageBoxW");
if (g_msgbox_addr) {
log_hex("[hook] MessageBoxW at", g_msgbox_addr);
memcpy(g_original_bytes, (void*)g_msgbox_addr, 14);
DWORD old_prot;
if (VirtualProtect((LPVOID)g_msgbox_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot)) {
*(uint16_t*)(g_msgbox_addr) = 0xB848;
*(uintptr_t*)(g_msgbox_addr + 2) = (uintptr_t)hooked_MessageBoxW;
*(uint16_t*)(g_msgbox_addr + 10) = 0xE0FF;
VirtualProtect((LPVOID)g_msgbox_addr, 14, old_prot, &old_prot);
log("[hook] MessageBoxW HOOKED");
}
}
// Hook MessageBoxA too
uintptr_t msgboxA = (uintptr_t)GetProcAddress(user32, "MessageBoxA");
if (msgboxA) {
log_hex("[hook] MessageBoxA at", msgboxA);
DWORD old_prot;
if (VirtualProtect((LPVOID)msgboxA, 14, PAGE_EXECUTE_READWRITE, &old_prot)) {
// Redirect MessageBoxA → always return IDOK (suppress all popups)
// xor eax, eax; mov al, 1; ret = 31 C0 B0 01 C3
*(uint8_t*)(msgboxA) = 0x31;
*(uint8_t*)(msgboxA + 1) = 0xC0;
*(uint8_t*)(msgboxA + 2) = 0xB0;
*(uint8_t*)(msgboxA + 3) = 0x01;
*(uint8_t*)(msgboxA + 4) = 0xC3;
VirtualProtect((LPVOID)msgboxA, 14, old_prot, &old_prot);
log("[hook] MessageBoxA HOOKED (ret IDOK)");
}
}
// Also hook MessageBoxExW and MessageBoxExA
uintptr_t msgboxExW = (uintptr_t)GetProcAddress(user32, "MessageBoxExW");
if (msgboxExW) {
DWORD old_prot;
if (VirtualProtect((LPVOID)msgboxExW, 14, PAGE_EXECUTE_READWRITE, &old_prot)) {
*(uint8_t*)(msgboxExW) = 0x31;
*(uint8_t*)(msgboxExW + 1) = 0xC0;
*(uint8_t*)(msgboxExW + 2) = 0xB0;
*(uint8_t*)(msgboxExW + 3) = 0x01;
*(uint8_t*)(msgboxExW + 4) = 0xC3;
VirtualProtect((LPVOID)msgboxExW, 14, old_prot, &old_prot);
log("[hook] MessageBoxExW HOOKED");
}
}
uintptr_t msgboxExA = (uintptr_t)GetProcAddress(user32, "MessageBoxExA");
if (msgboxExA) {
DWORD old_prot;
if (VirtualProtect((LPVOID)msgboxExA, 14, PAGE_EXECUTE_READWRITE, &old_prot)) {
*(uint8_t*)(msgboxExA) = 0x31;
*(uint8_t*)(msgboxExA + 1) = 0xC0;
*(uint8_t*)(msgboxExA + 2) = 0xB0;
*(uint8_t*)(msgboxExA + 3) = 0x01;
*(uint8_t*)(msgboxExA + 4) = 0xC3;
VirtualProtect((LPVOID)msgboxExA, 14, old_prot, &old_prot);
log("[hook] MessageBoxExA HOOKED");
}
}
return true;
}
// ── Hook ExitProcess, TerminateProcess, AND RaiseException ──
inline uint8_t g_exit_original[14] = {};
inline uintptr_t g_exit_addr = 0;
inline uint8_t g_raise_original[14] = {};
inline uintptr_t g_raise_addr = 0;
inline uint8_t g_terminate_original[14] = {};
inline uintptr_t g_terminate_addr = 0;
inline bool g_allow_exit = false;
inline void WINAPI hooked_ExitProcess(UINT uExitCode)
{
if (!g_allow_exit) {
log("[hook] Blocked ExitProcess");
return;
}
DWORD old_prot;
VirtualProtect((LPVOID)g_exit_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot);
memcpy((void*)g_exit_addr, g_exit_original, 14);
VirtualProtect((LPVOID)g_exit_addr, 14, old_prot, &old_prot);
ExitProcess(uExitCode);
}
inline BOOL WINAPI hooked_TerminateProcess(HANDLE hProcess, UINT uExitCode)
{
if (!g_allow_exit && (hProcess == GetCurrentProcess() || hProcess == (HANDLE)-1)) {
log("[hook] Blocked TerminateProcess");
return TRUE;
}
DWORD old_prot;
VirtualProtect((LPVOID)g_terminate_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot);
memcpy((void*)g_terminate_addr, g_terminate_original, 14);
VirtualProtect((LPVOID)g_terminate_addr, 14, old_prot, &old_prot);
return TerminateProcess(hProcess, uExitCode);
}
// RaiseException hook — blocks UE fatal error crashes from ApplicationRepairManager
inline void WINAPI hooked_RaiseException(DWORD code, DWORD flags, DWORD nArgs, const ULONG_PTR* args)
{
// Check if this is from the game module (ApplicationRepairManager crash)
void* ret_addr = _ReturnAddress();
uintptr_t ret = (uintptr_t)ret_addr;
if (memory::module_base && ret >= memory::module_base &&
ret <= (memory::module_base + 0x10000000))
{
char buf[256];
sprintf_s(buf, "[hook] Blocked RaiseException from game module (ret=0x%llX, code=0x%lX)",
(unsigned long long)ret, code);
log(buf);
return; // Don't crash — just return
}
// Not from game module — call original
DWORD old_prot;
VirtualProtect((LPVOID)g_raise_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot);
memcpy((void*)g_raise_addr, g_raise_original, 14);
VirtualProtect((LPVOID)g_raise_addr, 14, old_prot, &old_prot);
RaiseException(code, flags, nArgs, args);
// Rehook after call (if it returns)
VirtualProtect((LPVOID)g_raise_addr, 14, PAGE_EXECUTE_READWRITE, &old_prot);
*(uint16_t*)(g_raise_addr) = 0xB848;
*(uintptr_t*)(g_raise_addr + 2) = (uintptr_t)hooked_RaiseException;
*(uint16_t*)(g_raise_addr + 10) = 0xE0FF;
VirtualProtect((LPVOID)g_raise_addr, 14, old_prot, &old_prot);
}
inline void install_trampoline(uintptr_t target, uintptr_t hook, uint8_t* backup)
{
memcpy(backup, (void*)target, 14);
DWORD old_prot;
if (VirtualProtect((LPVOID)target, 14, PAGE_EXECUTE_READWRITE, &old_prot)) {
*(uint16_t*)(target) = 0xB848;
*(uintptr_t*)(target + 2) = hook;
*(uint16_t*)(target + 10) = 0xE0FF;
VirtualProtect((LPVOID)target, 14, old_prot, &old_prot);
}
}
inline bool hook_crash_functions()
{
HMODULE k32 = GetModuleHandleA("kernel32.dll");
// Hook ExitProcess
g_exit_addr = (uintptr_t)GetProcAddress(k32, "ExitProcess");
if (g_exit_addr) {
install_trampoline(g_exit_addr, (uintptr_t)hooked_ExitProcess, g_exit_original);
log("[hook] ExitProcess HOOKED");
}
// Hook TerminateProcess
g_terminate_addr = (uintptr_t)GetProcAddress(k32, "TerminateProcess");
if (g_terminate_addr) {
install_trampoline(g_terminate_addr, (uintptr_t)hooked_TerminateProcess, g_terminate_original);
log("[hook] TerminateProcess HOOKED");
}
// Hook RaiseException
g_raise_addr = (uintptr_t)GetProcAddress(k32, "RaiseException");
if (g_raise_addr) {
install_trampoline(g_raise_addr, (uintptr_t)hooked_RaiseException, g_raise_original);
log("[hook] RaiseException HOOKED");
}
return true;
}
// ── Mount in sacrificial thread ──
struct MountArgs {
__int64 fpak; const wchar_t* path; int priority;
uintptr_t mount_addr; volatile LONG result;
};
inline DWORD WINAPI mount_worker(LPVOID param)
{
__try {
auto* a = (MountArgs*)param;
typedef bool(__fastcall* fn_mount)(__int64, const wchar_t*, int, const wchar_t*, bool, bool);
auto fn = (fn_mount)(a->mount_addr);
bool ret = fn(a->fpak, a->path, a->priority, nullptr, true, false);
InterlockedExchange(&a->result, ret ? 1 : 2); // 1=true, 2=false(but no crash)
return ret ? 1 : 0;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
auto* a = (MountArgs*)param;
InterlockedExchange(&a->result, -1); // -1=crash
return 0;
}
}
inline int mount_pak_ex(__int64 fpak, const wchar_t* path, int priority)
{
MountArgs args = {};
args.fpak = fpak; args.path = path; args.priority = priority;
args.mount_addr = memory::module_base + offsets::mount; args.result = 0;
HANDLE h = CreateThread(nullptr, 0, mount_worker, &args, 0, nullptr);
if (!h) return -2;
DWORD w = WaitForSingleObject(h, 10000);
if (w == WAIT_TIMEOUT) { TerminateThread(h, 0); CloseHandle(h); return -3; }
CloseHandle(h);
return (int)args.result; // 1=success, 2=returned false, -1=crash
}
// ── Main thread ──
inline DWORD WINAPI pak_thread(LPVOID)
{
{
std::ofstream c("C:\\mods\\pak_debug.log", std::ios::trunc);
c.close();
}
log("=== PAK BYPASS ===");
// Wait for module_base
for (int i = 0; i < 120; i++) {
if (memory::module_base != 0) break;
Sleep(500);
}
if (!memory::module_base) { log("module_base FAILED"); return 0; }
log_hex("module_base", memory::module_base);
// Hook all crash/exit/dialog functions FIRST
hook_messagebox();
hook_crash_functions();
Sleep(5000);
// Get FPakPlatformFile
__int64 fpak = 0;
for (int i = 0; i < 30; i++) {
fpak = get_fpak();
if (fpak) break;
Sleep(500);
}
if (!fpak) { log("fpak not found"); Beep(300, 300); return 0; }
log_hex("fpak", (uintptr_t)fpak);
// Bypass PAK signing
safe_zero(memory::module_base + offsets::bypass_pak_signing, 0x20);
log("PAK signing zeroed");
Beep(800, 100);
Sleep(500);
// Mount PAKs
log("Mounting PAKs...");
char buf[512];
int mounted = 0;
const wchar_t* paks[] = {
L"C:\\mods\\pakchunk1-Windows_P.pak",
L"C:\\mods\\pakchunk2-Windows_P.pak",
L"C:\\mods\\pakchunk3-Windows_P.pak"
};
for (int i = 0; i < 3; i++) {
WIN32_FILE_ATTRIBUTE_DATA fdata = {};
if (!GetFileAttributesExW(paks[i], GetFileExInfoStandard, &fdata)) {
sprintf_s(buf, "pak%d NOT FOUND", i+1); log(buf);
continue;
}
ULONGLONG pak_size = ((ULONGLONG)fdata.nFileSizeHigh << 32) | fdata.nFileSizeLow;
sprintf_s(buf, "pak%d size: %llu bytes", i+1, pak_size);
log(buf);
// Also check for .ucas/.utoc companions
wchar_t ucas_path[256], utoc_path[256];
wcscpy_s(ucas_path, paks[i]);
wcscpy_s(utoc_path, paks[i]);
// Replace .pak with .ucas / .utoc
wchar_t* ext_ucas = wcsstr(ucas_path, L".pak");
wchar_t* ext_utoc = wcsstr(utoc_path, L".pak");
if (ext_ucas) wcscpy_s(ext_ucas, 6, L".ucas");
if (ext_utoc) wcscpy_s(ext_utoc, 6, L".utoc");
bool has_ucas = (GetFileAttributesW(ucas_path) != INVALID_FILE_ATTRIBUTES);
bool has_utoc = (GetFileAttributesW(utoc_path) != INVALID_FILE_ATTRIBUTES);
sprintf_s(buf, "pak%d companions: ucas=%s utoc=%s", i+1,
has_ucas ? "YES" : "NO", has_utoc ? "YES" : "NO");
log(buf);
int result = mount_pak_ex(fpak, paks[i], 10000 + i);
// 1=mount returned true, 2=mount returned false, -1=crash, -2=thread fail, -3=timeout
sprintf_s(buf, "pak%d mount result: %d (%s)", i+1, result,
result == 1 ? "TRUE" :
result == 2 ? "FALSE (mount rejected)" :
result == -1 ? "CRASH" :
result == -3 ? "TIMEOUT" : "ERROR");
log(buf);
if (result == 1) mounted++;
}
sprintf_s(buf, "Total: %d actually mounted", mounted);
log(buf);
if (mounted > 0) { Beep(1000, 100); Sleep(100); Beep(1200, 100); }
log("=== V12 DONE ===");
// Keep blocking exits for 5 minutes, then allow
Sleep(300000);
g_allow_exit = true;
return (DWORD)mounted;
}
// ── Dialog killer thread — finds and closes corruption popups ──
inline BOOL CALLBACK enum_windows_callback(HWND hwnd, LPARAM)
{
wchar_t title[256] = {};
GetWindowTextW(hwnd, title, 255);
if (wcsstr(title, L"Corrupt") || wcsstr(title, L"corrupt") ||
wcsstr(title, L"Repair") || wcsstr(title, L"Fatal"))
{
char buf[512];
sprintf_s(buf, "[killer] Found window: '%ls' — closing", title);
log(buf);
// Try multiple ways to close it
PostMessageW(hwnd, WM_CLOSE, 0, 0);
EndDialog(hwnd, IDOK);
return TRUE;
}
return TRUE;
}
inline DWORD WINAPI dialog_killer_thread(LPVOID)
{
log("[killer] Dialog killer thread started");
for (int i = 0; i < 600; i++) { // Run for 60 seconds
EnumWindows(enum_windows_callback, 0);
Sleep(100);
}
log("[killer] Dialog killer thread done");
return 0;
}
inline void init()
{
CreateThread(nullptr, 0, pak_thread, nullptr, 0, nullptr);
CreateThread(nullptr, 0, dialog_killer_thread, nullptr, 0, nullptr);
}
}
Code:
// Verified Offsets
inline std::uintptr_t bypass_pak_signing = 0xC528638;
inline std::uintptr_t fpak_platform_file = 0xC1CCBB8;
inline std::uintptr_t mount = 0x28FC420;
Troubleshooting & Risks
While this method suppresses the visual and local triggers, it does not magically make the PAK itself "safe." If the game performs server-side checks on specific assets (like weapon stats or player heights), you'll still catch a manual ban or a developer kick. This is strictly for client-side visual modifications.
Before running this, make sure your injector is properly hidden and your offsets are updated for the current build. If your PAKs are not visible even after a successful mount log, check your file structure and ensure your .ucas and .utoc companions are in the same directory as your .pak.
While others are catching bans from broken free injectors and outdated asset loaders, Infocheats users are building robust solutions that handle the engine's internal checks.
Who else is testing this on the current build, and are you seeing any issues with the mount status returning false?