using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using RecRoom.AntiCheat; using System.Linq; using System.Runtime.InteropServices; [BepInPlugin("com.radicade.eacbypass", "Radicade August Client", "2.0.0")] public sealed class RadicadeEacBypassPlugin : BasePlugin { internal static ManualLogSource LogSource; internal static ConfigEntry ServerUrl, TlsAllowedHosts; internal static Il2CppSystem.Threading.SynchronizationContext MainThreadContext; internal static string ImageKeyId; public override void Load() { LogSource = Log; if (UnityEngine.Application.version != "20230825") throw new NotSupportedException("This plugin requires Rec Room 20230825."); MainThreadContext = Il2CppSystem.Threading.SynchronizationContext.Current; Log.LogInfo($"[August] main-thread sync context: {(MainThreadContext != null ? MainThreadContext.GetType().Name : "NULL")}"); ServerUrl = Config.Bind("Connection", "ServerUrl", "https://localhost:3030", "Private server URL."); if (!Uri.TryCreate(ServerUrl.Value, UriKind.Absolute, out var server) || server.Scheme != "https") throw new InvalidOperationException("The August client requires an HTTPS server URL."); TlsAllowedHosts = Config.Bind("TLS", "AllowedHosts", server.Host, "Exact private server hosts allowed by the embedded TLS integration."); if (!EmbeddedTlsPatches.IsHostAllowed(server.Host)) throw new InvalidOperationException("TLS AllowedHosts must include the configured private server host."); var metadata = Path.Combine(UnityEngine.Application.dataPath, "il2cpp_data", "Metadata", "global-metadata.interop.dat"); ImageKeyId = ImageSignatureRequest.DetectKeyId(File.ReadAllBytes(metadata)); if (ModuleHidingPolicy.Enabled) { var hidden = PebModuleHider.HideModule("winhttp.dll"); Log.LogInfo(hidden ? "[ModuleHide] winhttp.dll unlinked from PEB module lists (scanner-invisible)" : "[ModuleHide] WARNING: winhttp.dll not found in PEB lists - scanner will still see it"); var wiped = PeHeaderWiper.WipeSignatures("winhttp.dll"); Log.LogInfo(wiped ? "[ModuleHide] winhttp.dll PE signatures wiped (MZ+PE) - region scans cannot classify it" : "[ModuleHide] WARNING: PE signature wipe did not apply (signatures missing or VirtualProtect failed)"); } else { Log.LogInfo("[ModuleHide] disabled by default (RADICADE_MODULE_HIDE=1 re-arms runs 14/15): winhttp.dll keeps its loader-list entry and its PE headers, so a module scan has nothing to call obscured."); } Log.LogInfo(ModuleHidingPolicy.DescribeModuleList()); HileGate.Enabled = true; Log.LogInfo("[HileGate] armed - anti-cheat report POSTs are " + (HileGate.DropEnabled ? "dropped at the HTTP layer (RADICADE_HILE_GATE=drop)" : "captured in full and then allowed through, so the report body is on the record (RADICADE_HILE_GATE=drop restores the drop)") + "."); var harmony = new Harmony("com.radicade.eacbypass"); EmbeddedTlsPatches.Apply(harmony); harmony.PatchAll(typeof(NameserverConnectionPatches)); WebSocketRedirectPatches.Apply(harmony); harmony.PatchAll(typeof(AugustChallengePatches)); harmony.PatchAll(typeof(AugustLaunchDiagnostics)); harmony.PatchAll(typeof(AugustLaunchValidatorPatch)); RsaVerifyNeutralizerPatches.Apply(harmony); harmony.PatchAll(typeof(FileCheckEntryPatch)); harmony.PatchAll(typeof(ExitInitiatorTracerPatch)); Log.LogInfo("[ExitInit] armed - managed exit initiators are blocked once a hile report has been dropped, and traced."); try { harmony.PatchAll(typeof(ExitStateVetoPatch)); Log.LogInfo("[ExitVeto] patches applied; blockExitState enabled=" + ExitStateVetoPatch.Enabled + " (opt-in fallback only; a normal quit and server logouts work either way. RADICADE_EXIT_VETO=1 arms it)"); } catch (Exception vetoEx) { Log.LogError("[ExitVeto] failed to apply - continuing without it: " + vetoEx); } try { harmony.PatchAll(typeof(ExitSequenceTracePatch)); harmony.PatchAll(typeof(UnityQuitVetoPatch)); harmony.PatchAll(typeof(EacNativeClientStubPatch)); harmony.PatchAll(typeof(EacHealthQueryLiePatch)); harmony.PatchAll(typeof(CheatManagerTracePatch)); harmony.PatchAll(typeof(CheatScanSuppressPatch)); Log.LogInfo("[CheatScan] verdict suppression enabled=" + CheatScanSuppressPatch.Enabled); Log.LogInfo("[CheatType] cheat-detection suppression enabled=" + CheatDetectionSuppressPatch.Enabled + " (RADICADE_CHEAT_TYPE=0 restores the detection and the dorm drop that follows it)"); Log.LogInfo("[CheatDrop] dorm-drop guard enabled=" + CheatDormDropGuardPatch.Enabled + " (RADICADE_CHEAT_DROP=0 lets every drop run)"); try { harmony.PatchAll(typeof(CheatDetectionSuppressPatch)); Log.LogInfo("[CheatType] verdict handlers guarded=" + CheatDetectionSuppressPatch.Guarded + (CheatDetectionSuppressPatch.Unguarded == null ? " (all three)" : " - UNGUARDED, not found on CheatManager: " + CheatDetectionSuppressPatch.Unguarded)); } catch (Exception cheatTypeEx) { Log.LogError("[CheatType] verdict suppression NOT armed: " + cheatTypeEx.Message); } try { harmony.PatchAll(typeof(CheatDormDropGuardPatch)); Log.LogInfo("[CheatDrop] dorm drop (EFLCHFDDPJP) held back for the anti-cheat reason; " + "every other reason still drops the player."); } catch (Exception cheatDropEx) { Log.LogError("[CheatDrop] dorm-drop guard NOT armed: " + cheatDropEx.Message); } Log.LogInfo("[EacStub] EasyAntiCheat Hydra runtime neutralisation enabled=" + EacGate.Enabled + " (RADICADE_EAC_NEUTRALIZE=0 before launch leaves EAC alone)"); Log.LogInfo("[ExitTrace] shutdown-request stack dump armed; [QuitVeto] wantsToQuit veto enabled=" + UnityQuitVetoPatch.Enabled); } catch (Exception quitEx) { Log.LogError("[ExitTrace] failed to apply - continuing without them: " + quitEx); } try { CrashAddressMap.Start(); Log.LogInfo("[AddrMap] enabled=" + CrashAddressMap.Enabled + " target=0x" + CrashAddressMap.TargetRva.ToString("x") + " (RADICADE_ADDR_MAP=0 disables; RADICADE_ADDR_TARGET=0x... re-aims it)"); } catch (Exception mapEx) { Log.LogError("[AddrMap] failed to start: " + mapEx); } try { harmony.PatchAll(typeof(ToolCollisionResetGuardPatch)); Log.LogInfo("[ToolGuard] Tool.ResetToDefaultCollisionLayer skipped=" + ToolCollisionResetGuardPatch.Enabled + " (RADICADE_TOOL_RESET=0 before launch restores the original reset)"); } catch (Exception toolEx) { Log.LogError("[ToolGuard] failed to apply - the room crash is NOT guarded: " + toolEx); } Log.LogInfo($"[August] Connection patches loaded for {ServerUrl.Value}; image key {ImageKeyId ?? "unknown"}."); } } [HarmonyPatch] internal static class AugustLaunchDiagnostics { [HarmonyPrefix, HarmonyPatch(typeof(NDIIPBKGGIG), "FIBOEONALKB")] [HarmonyPatch(typeof(NDIIPBKGGIG), "IKHPLCCBENN")] private static void Trace(JKBPKDAGEHN __0) { if (__0 == JKBPKDAGEHN.LAUNCH_VALIDATION_FAILED) RadicadeEacBypassPlugin.LogSource.LogWarning("[August launch diagnostic] " + Il2CppSystem.Environment.StackTrace); } } [HarmonyPatch] internal static class AugustChallengePatches { [HarmonyPrefix, HarmonyPatch(typeof(EACManager), nameof(EACManager.GenerateChallengeResponse))] private static bool Challenge(ref string __result) { __result = "radicade-eac-bypass"; return false; } [HarmonyPrefix, HarmonyPatch(typeof(EACManager), "MGEAHKJIIMO")] private static bool Ready(ref bool __result) { __result = true; return false; } } [HarmonyPatch(typeof(ADJOODNBCKG.NFPDGJNDFOF), "GBAMNAKGEGD")] internal static class AugustLaunchValidatorPatch { static readonly System.Threading.CancellationTokenSource Pump = new(); [HarmonyPrefix] internal static bool Bypass(ADJOODNBCKG.NFPDGJNDFOF __instance) { var promise = __instance.promise; if (promise == null) return true; RadicadeEacBypassPlugin.LogSource.LogInfo("[August] Bypassing native launch validator (hash check cannot pass in a patched install)."); var context = RadicadeEacBypassPlugin.MainThreadContext; try { var before = Il2CppSystem.Threading.SynchronizationContext.Current; RadicadeEacBypassPlugin.LogSource.LogInfo( $"[August] validator thread context: {(before != null ? before.GetType().Name : "null")}; installing main-thread context"); if (context != null) Il2CppSystem.Threading.SynchronizationContext.SetSynchronizationContext(context); try { RadicadeEacBypassPlugin.LogSource.LogInfo( $"[August] resolving: done={SafeState(promise, "EPCPPDDJCMO")} handlers={SafeHandlerCount(promise)}"); promise.OLKGFACPIPC(); RadicadeEacBypassPlugin.LogSource.LogInfo("[August] Launch-validation promise resolved; boot continues."); } catch (Exception e) { RadicadeEacBypassPlugin.LogSource.LogError($"[August] Resolve failed: {e}"); } finally { Il2CppSystem.Threading.SynchronizationContext.SetSynchronizationContext(before); } } catch (Exception outer) { RadicadeEacBypassPlugin.LogSource.LogError($"[August] Context install failed: {outer}"); } return false; } static int SafeHandlerCount(PFDOLDLEIBA promise) { try { return promise.HFPMLNDOGLF?.Count ?? 0; } catch { return -1; } } static bool SafeState(PFDOLDLEIBA promise, string prop) { try { return prop switch { "EPCPPDDJCMO" => promise.EPCPPDDJCMO, "MPJBGFFEEOP" => promise.MPJBGFFEEOP, "NEIINCCHEPF" => promise.NEIINCCHEPF, _ => false, }; } catch { return false; } } } internal static class RsaVerifyNeutralizerPatches { static readonly byte[] P1KeyModulus = System.Convert.FromBase64String( "X07yXkxaaLcZ1wVXfkWjgFkkqdoLhDFm0GPODsF+Q47pSUlbLvtXGqStnyEJEIrQmgDiicAvCdGRq4lovr2l5sIPMaoyizsbVHBdwLUrCsji0RvSBnmvN+8KqQ8STnB4DP4pAsPilfD35def4WuX/xMCXB5+hQUVhv27HPV8Dj9XzHuJAijIM9UwDZmvUcECyiO4wv+TaZi2+ELBtaLCQR8Gm1ZPeDEwP62Ch6MJy0jx5pkvvD0KdF9Wye+3/Wx31Zn/Trdo9HL4sGFWPDM9H9kQhZd5wkTHuxpwGIIhlzIwvY2/pBGdZKP6fi1D2jROEmVkBDyhmYY9nO+s3/bndQ=="); static int p1Calls, totalCalls; internal static void Apply(Harmony harmony) { PatchVerify(harmony, typeof(Il2CppSystem.Security.Cryptography.RSACryptoServiceProvider), "VerifyHash", new[] { typeof(Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray), typeof(Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray), typeof(Il2CppSystem.Security.Cryptography.HashAlgorithmName), typeof(Il2CppSystem.Security.Cryptography.RSASignaturePadding) }); PatchVerify(harmony, typeof(Il2CppSystem.Security.Cryptography.RSACryptoServiceProvider), "VerifyHash", new[] { typeof(Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray), typeof(int), typeof(Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray) }); PatchVerify(harmony, typeof(Il2CppSystem.Security.Cryptography.RSACryptoServiceProvider), "VerifyData", new[] { typeof(Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray), typeof(Il2CppSystem.Object), typeof(Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray) }); } static void PatchVerify(Harmony harmony, Type target, string methodName, Type[] args) { try { var m = AccessTools.Method(target, methodName, args); if (m == null) { RadicadeEacBypassPlugin.LogSource.LogWarning( $"[RsaVerify] {target.Name}.{methodName}({args.Length} args) not found - skipped"); return; } harmony.Patch(m, prefix: new HarmonyMethod(typeof(RsaVerifyNeutralizerPatches).GetMethod(nameof(VerifyPrefix)))); RadicadeEacBypassPlugin.LogSource.LogInfo( $"[RsaVerify] patched {target.Name}.{methodName}({string.Join(", ", Array.ConvertAll(args, t => t.Name))})"); } catch (Exception e) { RadicadeEacBypassPlugin.LogSource.LogWarning( $"[RsaVerify] failed to patch {target.Name}.{methodName}: {e.GetType().Name}: {e.Message}"); } } public static bool VerifyPrefix( Il2CppSystem.Security.Cryptography.RSACryptoServiceProvider __instance, ref bool __result) { totalCalls++; var isP1 = false; try { var modulus = __instance.ExportParameters(false).Modulus; isP1 = modulus != null && modulus.Length == P1KeyModulus.Length && modulus.SequenceEqual(P1KeyModulus); if (isP1) p1Calls++; } catch { } __result = isP1; if (totalCalls <= 8) RadicadeEacBypassPlugin.LogSource.LogWarning( $"[ImageSig] RSA verify #{totalCalls} ({(isP1 ? "p1 key" : "game key")}) forced {(isP1 ? "true" : "false")}"); return false; } } [HarmonyPatch] internal static class FileCheckEntryPatch { private static System.Collections.Generic.IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(KKHDHILFOGO), "GGIFLDPHCIP"); yield return AccessTools.Method(typeof(KKHDHILFOGO), "GOCCLPJDJIC"); } [HarmonyPrefix] internal static bool Bypass(ref MCFFIFIPNKE __result) { var result = KKHDHILFOGO.BJGODLEIDGE.NOMPJGOIAEB("radicade: file check bypassed"); __result = PFDOLDLEIBA.PLJINLOCHGM(result); RadicadeEacBypassPlugin.LogSource.LogInfo("[FileCheck] entry bypassed - resolved success promise returned"); return false; } } internal static unsafe class PebModuleHider { [StructLayout(LayoutKind.Sequential)] private struct PROCESS_BASIC_INFORMATION { public IntPtr Reserved1; public IntPtr PebBaseAddress; public IntPtr Reserved2_0; public IntPtr Reserved2_1; public IntPtr UniqueProcessId; public IntPtr Reserved3; } [DllImport("ntdll.dll")] private static extern int NtQueryInformationProcess( IntPtr hProcess, int processInfoClass, ref PROCESS_BASIC_INFORMATION pbi, int cb, out int returnLength); private struct LIST_ENTRY { public IntPtr Flink, Blink; } private const int OffInMemoryOrder = 0x10; private const int OffInInitOrder = 0x20; private const int OffBaseDllName = 0x58; public static bool HideModule(string dllName) { var pbi = default(PROCESS_BASIC_INFORMATION); int status = NtQueryInformationProcess(new IntPtr(-1), 0, ref pbi, Marshal.SizeOf(), out _); if (status != 0 || pbi.PebBaseAddress == IntPtr.Zero) return false; byte* peb = (byte*)pbi.PebBaseAddress; byte* ldr = *(byte**)(peb + 0x18); if (ldr == null) return false; LIST_ENTRY* head = (LIST_ENTRY*)(ldr + 0x10); byte* target = null; for (LIST_ENTRY* n = (LIST_ENTRY*)head->Flink; n != head && target == null; n = (LIST_ENTRY*)n->Flink) { byte* entry = (byte*)n; ushort* us = (ushort*)(entry + OffBaseDllName); ushort lenBytes = *us; char* buf = *(char**)(entry + OffBaseDllName + 8); if (buf == null || lenBytes == 0) continue; string name = new string(buf, 0, lenBytes / 2); if (name.Equals(dllName, StringComparison.OrdinalIgnoreCase)) target = entry; } if (target == null) return false; Unlink((LIST_ENTRY*)target); Unlink((LIST_ENTRY*)(target + OffInMemoryOrder)); Unlink((LIST_ENTRY*)(target + OffInInitOrder)); return true; } private static void Unlink(LIST_ENTRY* e) { var flink = (LIST_ENTRY*)e->Flink; var blink = (LIST_ENTRY*)e->Blink; if (flink == e || blink == e) return; blink->Flink = e->Flink; flink->Blink = e->Blink; e->Flink = (IntPtr)e; e->Blink = (IntPtr)e; } } internal static unsafe class PeHeaderWiper { [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr GetModuleHandleW(string moduleName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool VirtualProtect(IntPtr address, UIntPtr size, uint newProtect, out uint oldProtect); public static bool WipeSignatures(string dllName) { IntPtr baseAddr = GetModuleHandleW(dllName); if (baseAddr == IntPtr.Zero) return false; byte* p = (byte*)baseAddr; int eLfanew = *(int*)(p + 0x3C); if (eLfanew <= 0 || eLfanew > 0x1000) return false; if (!VirtualProtect(baseAddr, (UIntPtr)0x1000, 0x04 , out uint old)) return false; bool wasMz = p[0] == (byte)'M' && p[1] == (byte)'Z'; bool wasPe = *(uint*)(p + eLfanew) == 0x00004550; *(ushort*)p = 0; *(uint*)(p + eLfanew) = 0; VirtualProtect(baseAddr, (UIntPtr)0x1000, old, out _); return wasMz && wasPe; } } [HarmonyPatch] internal static class ExitInitiatorTracerPatch { private static System.Collections.Generic.IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(CCKNJCHHBLL), "BEFDPLAELIH"); yield return AccessTools.Method(typeof(CCKNJCHHBLL), "GIEEBIOOFML"); yield return AccessTools.Method(typeof(CCKNJCHHBLL), "FNOEHNDLHHG"); } [HarmonyPrefix] internal static bool TraceAndBlock() { var stack = ""; try { stack = Environment.StackTrace; } catch { } var armed = HileGate.DroppedCount > 0; RadicadeEacBypassPlugin.LogSource.LogWarning(armed ? "[ExitInit] blocked managed exit initiator - caller trace:\n" + stack : "[ExitInit] allowed managed exit initiator (no report filed yet) - caller trace:\n" + stack); return !armed; } } [HarmonyPatch] [HarmonyPatch(typeof(BestHTTP.HTTPManager), "SendRequest", new[] { typeof(BestHTTP.HTTPRequest) })] internal static class ExitSequenceTracePatch { private static int _dumped; [HarmonyPriority(Priority.First)] private static void Prefix(BestHTTP.HTTPRequest request) { try { var path = request?.Uri?.AbsolutePath; if (path == null) return; if (path.IndexOf("/matchmaking/player/logout", StringComparison.OrdinalIgnoreCase) < 0 && path.IndexOf("/playersettings/playersettings", StringComparison.OrdinalIgnoreCase) < 0) return; if (++_dumped > 4) return; RadicadeEacBypassPlugin.LogSource.LogWarning( "[ExitTrace] shutdown request " + path + " - managed caller stack:" + ExitTraceHelper.SafeStack()); } catch (Exception traceEx) { RadicadeEacBypassPlugin.LogSource.LogWarning("[ExitTrace] dump failed: " + traceEx.Message); } } } [HarmonyPatch] internal static class UnityQuitVetoPatch { internal static bool Enabled => _enabled; private static readonly bool _enabled = IsEnabled(); private static int _blocks; private static bool IsEnabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_QUIT_VETO"); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "1" or "on" or "true" or "yes"; } catch { return false; } } private static System.Collections.Generic.IEnumerable TargetMethods() { var noArg = AccessTools.Method(typeof(CCKNJCHHBLL), "GBGMBMGDGGD", Type.EmptyTypes); if (noArg != null) yield return noArg; var flag = AccessTools.Method(typeof(CCKNJCHHBLL), "GBGMBMGDGGD", new[] { typeof(bool) }); if (flag != null) yield return flag; } [HarmonyPrefix] private static bool Veto(ref bool __result) { if (!Enabled) return true; __result = false; var count = ++_blocks; if (count <= 3 || count % 25 == 0) RadicadeEacBypassPlugin.LogSource.LogWarning( "[QuitVeto] wantsToQuit answered false (block #" + count + ") - Unity's quit is cancelled. " + "This is the anti-cheat shutdown, not a user quit; the veto is armed by RADICADE_QUIT_VETO=1, set it to 0 to disable." + ExitTraceHelper.SafeStack()); return false; } } internal static class ModuleHidingPolicy { internal static bool Enabled { get; } = ReadFlag("RADICADE_MODULE_HIDE"); private static bool ReadFlag(string name) { try { var raw = Environment.GetEnvironmentVariable(name); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "1" or "on" or "true" or "yes"; } catch { return false; } } internal static string DescribeModuleList() { try { using (var self = System.Diagnostics.Process.GetCurrentProcess()) { var seen = false; var path = string.Empty; foreach (System.Diagnostics.ProcessModule module in self.Modules) { if (!string.Equals(module.ModuleName, "winhttp.dll", StringComparison.OrdinalIgnoreCase)) continue; seen = true; path = module.FileName ?? string.Empty; break; } return seen ? "[ModuleList] winhttp.dll is enumerated by a Toolhelp module walk: " + path : "[ModuleList] winhttp.dll is NOT enumerated by a Toolhelp module walk - a loaded image the loader lists do not know about is the most literal definition of obscured."; } } catch (Exception ex) { return "[ModuleList] enumeration failed: " + ex.Message; } } } [HarmonyPatch] internal static class CheatScanSuppressPatch { internal static bool Enabled => !_disabled; private static readonly bool _disabled = IsDisabled(); private static int _skips; private static bool IsDisabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_CHEAT_SCAN"); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "0" or "off" or "false" or "no"; } catch { return false; } } private static System.Reflection.MethodBase TargetMethod() { var type = AccessTools.TypeByName("CheatManager"); if (type == null) throw new System.MissingMemberException("CheatManager could not be resolved."); var method = AccessTools.Method(type, "MJLAGDJLOBC", System.Type.EmptyTypes); if (method == null) throw new System.MissingMethodException("CheatManager.MJLAGDJLOBC() is gone - the scan moved or was renamed."); return method; } [HarmonyPrefix] private static bool Skip() { if (!Enabled) return true; var count = ++_skips; if (count <= 3 || count % 25 == 0) RadicadeEacBypassPlugin.LogSource.LogWarning("[CheatScan] skipped CheatManager.MJLAGDJLOBC() (#" + count + ") - this is the pass behind the Obscured/winhttp.dll verdict, so no report and no shutdown follow from it."); return false; } } [HarmonyPatch] internal static class CheatDetectionSuppressPatch { internal static bool Enabled => !_disabled; private static readonly bool _disabled = IsDisabled(); private static int _skips; private static bool IsDisabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_CHEAT_TYPE"); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "0" or "off" or "false" or "no"; } catch { return false; } } private static System.Collections.Generic.IEnumerable TargetMethods() { var type = AccessTools.TypeByName("CheatManager"); if (type == null) throw new System.MissingMemberException("CheatManager could not be resolved."); var found = new System.Collections.Generic.List(); var missing = new System.Collections.Generic.List(); foreach (var name in new[] { "OnObscuredTypeCheatDetected", "OnTimeCheatDetected", "OnAdvancedMovementCheatDetected" }) { var method = AccessTools.Method(type, name, System.Type.EmptyTypes); if (method == null) { missing.Add(name); continue; } found.Add(name); yield return method; } _guarded = found.Count; _unguarded = missing.Count == 0 ? null : string.Join(", ", missing); } internal static int Guarded => _guarded; internal static string Unguarded => _unguarded; private static int _guarded; private static string _unguarded; [HarmonyPrefix] private static bool Skip(System.Reflection.MethodBase __originalMethod) { if (!Enabled) return true; var count = ++_skips; if (count <= 5 || count % 25 == 0) RadicadeEacBypassPlugin.LogSource.LogWarning("[CheatType] skipped CheatManager." + __originalMethod.Name + "() (#" + count + ") - this is a verdict handler that ends in the dorm drop with EFLCHFDDPJP and the shutdown behind it, so the room survives the detection."); return false; } } [HarmonyPatch] internal static class CheatDormDropGuardPatch { private const string AntiCheatReason = "385120"; internal static bool Enabled => !_disabled; private static readonly bool _disabled = IsDisabled(); private static int _skips; private static bool IsDisabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_CHEAT_DROP"); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "0" or "off" or "false" or "no"; } catch { return false; } } private static System.Reflection.MethodBase TargetMethod() { var type = AccessTools.TypeByName("CheatManager"); if (type == null) throw new System.MissingMemberException("CheatManager could not be resolved."); var method = AccessTools.Method(type, "EFLCHFDDPJP", new[] { typeof(string) }); if (method == null) throw new System.MissingMethodException("CheatManager.EFLCHFDDPJP(string) is gone - the dorm drop moved or was renamed, so the guard is NOT armed."); return method; } [HarmonyPrefix] private static bool Skip(string JELLDPBCBLG) { if (!Enabled) return true; if (string.IsNullOrEmpty(JELLDPBCBLG) || JELLDPBCBLG.IndexOf(AntiCheatReason, StringComparison.Ordinal) < 0) return true; var count = ++_skips; if (count <= 5 || count % 25 == 0) RadicadeEacBypassPlugin.LogSource.LogWarning("[CheatDrop] skipped CheatManager.EFLCHFDDPJP('" + JELLDPBCBLG + "') (#" + count + ") - the anti-cheat's own drop, so the player stays where they are instead of being sent to the dormroom mid-session."); return false; } } [HarmonyPatch] internal static class CheatManagerTracePatch { private const int PerMethodLimit = 4; private const int LineLimit = 90; private static readonly System.Diagnostics.Stopwatch Clock = System.Diagnostics.Stopwatch.StartNew(); private static readonly System.Collections.Generic.Dictionary Seen = new System.Collections.Generic.Dictionary(); private static int _lines; private static System.Collections.Generic.IEnumerable TargetMethods() { var outer = AccessTools.TypeByName("CheatManager"); if (outer == null) { RadicadeEacBypassPlugin.LogSource.LogWarning("[CheatTrace] CheatManager not resolvable - nothing traced."); yield break; } var types = new System.Collections.Generic.List { outer }; foreach (var nested in outer.GetNestedTypes(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)) types.Add(nested); foreach (var type in types) { foreach (var method in type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly)) { if (method.IsAbstract || method.ContainsGenericParameters) continue; yield return method; } } } [HarmonyPrefix] private static void Trace(System.Reflection.MethodBase __originalMethod) { try { if (_lines >= LineLimit) return; var key = __originalMethod.DeclaringType.Name + "." + __originalMethod.Name; Seen.TryGetValue(key, out var count); if (count >= PerMethodLimit) return; Seen[key] = count + 1; _lines++; RadicadeEacBypassPlugin.LogSource.LogWarning("[CheatTrace] +" + Clock.ElapsedMilliseconds + "ms " + key); } catch { } } } internal static class ExitTraceHelper { internal static string SafeStack() { try { return "\n" + Environment.StackTrace; } catch { return "\n"; } } } internal static class EacGate { internal static bool Enabled => !_disabled; private static readonly bool _disabled = IsDisabled(); private static int _logCount; private static bool IsDisabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_EAC_NEUTRALIZE"); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "0" or "off" or "false" or "no"; } catch { return false; } } internal static System.Type Find(string fullName) { try { var byName = System.Type.GetType(fullName + ", EasyAntiCheat.Client"); if (byName != null) return byName; } catch { } try { var forced = typeof(EasyAntiCheat.Client.Hydra.Runtime).Assembly; var direct = forced.GetType(fullName); if (direct != null) return direct; } catch { } try { return AccessTools.TypeByName(fullName); } catch { return null; } } private const int LogLimit = 8; internal static void Log(string message) { try { if (++_logCount > LogLimit) return; RadicadeEacBypassPlugin.LogSource.LogWarning(message); } catch { } } } [HarmonyPatch] internal static class EacNativeClientStubPatch { private static System.Collections.Generic.IEnumerable TargetMethods() { var type = EacGate.Find("EasyAntiCheat.Client.Hydra.NativeClient"); if (type == null) { EacGate.Log("[EacStub] Hydra.NativeClient not resolvable - EAC runtime left untouched."); yield break; } foreach (var method in type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)) if (method.Name == "Initialize") yield return method; } [HarmonyPrefix] private static bool Skip(System.Reflection.MethodBase __originalMethod) { if (!EacGate.Enabled) return true; EacGate.Log("[EacStub] skipped " + __originalMethod.DeclaringType + "." + __originalMethod.Name + " - EasyAntiCheat's native client never starts, so its module scanner never runs."); return false; } } [HarmonyPatch] internal static class EacHealthQueryLiePatch { private static System.Collections.Generic.IEnumerable TargetMethods() { var native = EacGate.Find("EasyAntiCheat.Client.Hydra.NativeClient"); if (native != null) foreach (var method in native.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)) if (method.Name == "IsStartedWithEasyAntiCheat" && method.ReturnType == typeof(bool)) yield return method; var runtime = EacGate.Find("EasyAntiCheat.Client.Hydra.Runtime"); if (runtime != null) foreach (var method in runtime.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)) if ((method.Name == "IsActive" || method.Name == "get_Initialized") && method.ReturnType == typeof(bool)) yield return method; } [HarmonyPrefix] private static bool Lie(ref bool __result, System.Reflection.MethodBase __originalMethod) { if (!EacGate.Enabled) return true; __result = true; EacGate.Log("[EacStub] " + __originalMethod.DeclaringType + "." + __originalMethod.Name + " answered true."); return false; } } [HarmonyPatch] [HarmonyPatch] internal static class ToolCollisionResetGuardPatch { internal static bool Enabled => !_disabled; private static readonly bool _disabled = IsDisabled(); private static int _skips; private static bool IsDisabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_TOOL_RESET"); if (string.IsNullOrWhiteSpace(raw)) return false; return raw.Trim().ToLowerInvariant() is "0" or "off" or "false" or "no"; } catch { return false; } } private static System.Reflection.MethodBase TargetMethod() { var method = AccessTools.Method(typeof(Tool), "ResetToDefaultCollisionLayer", Type.EmptyTypes); if (method == null) throw new MissingMethodException( "Tool.ResetToDefaultCollisionLayer() not found - the room crash guard is NOT armed."); return method; } [HarmonyPrefix] private static bool Skip(Tool __instance) { if (!Enabled) return true; var count = ++_skips; if (count <= 5 || count % 50 == 0) RadicadeEacBypassPlugin.LogSource.LogWarning( "[ToolGuard] skipped Tool.ResetToDefaultCollisionLayer on " + Describe(__instance) + " (#" + count + ") - this method is the frame directly above the access violation " + "at GameAssembly.dll+0x470728d, which walks the tool's ECS collision-layer components. " + "Tool resets still apply; only the collision layer is left as it was."); return false; } private static string Describe(Tool tool) { try { if (tool == null) return ""; var owner = tool.gameObject; return (owner == null) ? "" : "'" + owner.name + "'"; } catch (Exception ex) { return ""; } } } internal static class ExitStateVetoPatch { internal static bool Enabled => _enabled; private static readonly bool _enabled = IsEnabled(); private static int _blocks; private static bool IsEnabled() { try { var raw = Environment.GetEnvironmentVariable("RADICADE_EXIT_VETO"); if (string.IsNullOrWhiteSpace(raw)) return false; raw = raw.Trim().ToLowerInvariant(); return raw is "1" or "on" or "true" or "yes"; } catch { return false; } } private static System.Collections.Generic.IEnumerable TargetMethods() { var property = AccessTools.PropertySetter(typeof(CCKNJCHHBLL), "GLNIKJNLCIB"); if (property != null) yield return property; var raw = AccessTools.Method(typeof(CCKNJCHHBLL), "KALEFEDAHCC", new[] { typeof(CCKNJCHHBLL.CCFIKLBABNB) }); if (raw != null) yield return raw; } [HarmonyPrefix] private static bool Block(CCKNJCHHBLL.CCFIKLBABNB __0) { if (!Enabled) return true; if (__0 == CCKNJCHHBLL.CCFIKLBABNB.Running) return true; var count = ++_blocks; var caller = count == 1 ? ExitTraceHelper.SafeStack() : string.Empty; if (count <= 3 || count % 25 == 0) RadicadeEacBypassPlugin.LogSource.LogWarning( $"[ExitVeto] blocked AppExitState -> {__0} (block #{count}); the session keeps running. " + "This is the anti-cheat-driven exit, not a user quit - the veto is armed by RADICADE_EXIT_VETO=1, set it to 0 to disable." + caller); return false; } }