private unsafe void DoBackSpew() { var returnSizeStep = 65536; // reset it back to zero *(long *)(OriginalBundleDownload + 0x40) = 0; var rawPointer = myWriterStream.GetPointer() + 8; var currentPosition = 0; var totalLength = (int)myWriterStream.Length; while (currentPosition < totalLength) { var currentRead = Math.Min(returnSizeStep, totalLength - currentPosition); var bytesConsumed = BundleDownloadMethods.OriginalReceiveBytes(OriginalBundleDownload, (IntPtr)(rawPointer + currentPosition), currentRead); currentPosition += currentRead; if (bytesConsumed != currentRead) { // The thing refused to eat our data? break; } } myWriterStream.ReleasePointer(); }
internal void CompleteDownload() { if (myMemoryMap == null) { MelonDebug.Msg($"Did not succ any bytes for ptr {OriginalBundleDownload}, just completing"); BundleDownloadMethods.OriginalCompleteDownload(OriginalBundleDownload); return; } MelonDebug.Msg($"Succed {myWriterStream.Position} bytes out of declared {BundleDlInterceptor.GetTotalSize(OriginalBundleDownload)}; waiting for victim process"); var stopwatch = Stopwatch.StartNew(); var exitCode = myVerifierProcess.WaitForExit(TimeSpan.FromSeconds(BundleVerifierMod.TimeLimit.Value)); MelonDebug.Msg($"Process wait done after {stopwatch.ElapsedMilliseconds}ms extra wait"); if (exitCode != 0) { var cleanedUrl = BundleVerifierMod.SanitizeUrl(Url); MelonLogger.Msg($"Verifier process failed with exit code {exitCode} ({VerifierExitCodes.GetExitCodeDescription(exitCode)}) for bundle uid={cleanedUrl.Item1}+{cleanedUrl.Item2}"); BundleVerifierMod.BadBundleCache.Add(Url); MelonDebug.Msg("Reporting completion without data"); // feed some garbage into it, otherwise it dies unsafe { *(long *)(OriginalBundleDownload + 0x40) = 0; var stub = "UnityFS\0"; var bytes = Encoding.UTF8.GetBytes(stub); fixed(byte *bytesPtr = bytes) BundleDownloadMethods.OriginalReceiveBytes(OriginalBundleDownload, (IntPtr)bytesPtr, bytes.Length); } BundleDownloadMethods.OriginalCompleteDownload(OriginalBundleDownload); return; } MelonDebug.Msg("Bundle looks clean, spewing back..."); DoBackSpew(); MelonDebug.Msg("Back-spew done, completing"); BundleDownloadMethods.OriginalCompleteDownload(OriginalBundleDownload); MelonDebug.Msg("Completed!"); }
internal int ProcessBytes(byte[] bytes, int offset, int length) { try { myWriterStream.Write(bytes, offset, length); } catch (IOException ex) { MelonLogger.Error($"Received more bytes than declared for bundle URL {Url} (declared: {BundleDlInterceptor.GetTotalSize(OriginalBundleDownload)})"); MelonLogger.Error(ex.ToString()); DoBackSpew(); unsafe { fixed(byte *bytesPtr = bytes) BundleDownloadMethods.OriginalReceiveBytes(OriginalBundleDownload, (IntPtr)bytesPtr, length); } BundleDlInterceptor.CancelIntercept(this); } return(length); }
public static void OnApplicationStart(HarmonyLib.Harmony harmonyInstance) { var category = MelonPreferences.CreateCategory(SettingsCategory, "Advanced Safety - Bundles"); TimeLimit = category.CreateEntry("TimeLimit", 15, "Time limit (seconds)"); MemoryLimit = category.CreateEntry("MemLimit", 2048, "Memory limit (megabytes)"); ComponentLimit = category.CreateEntry("ComponentLimit", 10_000, "Component limit (0=unlimited)"); EnabledSetting = category.CreateEntry("Enabled", true, "Check for corrupted bundles"); OnlyPublics = category.CreateEntry("OnlyPublics", true, "Only check bundles in public worlds"); BadBundleCache = new BundleHashCache(Path.Combine(MelonUtils.UserDataDirectory, "BadBundleHashes.bin")); ForceAllowedCache = new BundleHashCache(null); var initSuccess = BundleDownloadMethods.Init(); if (!initSuccess) { return; } try { PrepareVerifierDir(); } catch (IOException ex) { MelonLogger.Error("Unable to extract bundle verifier app, the mod will not work"); MelonLogger.Error(ex.ToString()); return; } harmonyInstance.Patch(typeof(NetworkManager).GetMethod("OnLeftRoom"), new HarmonyMethod(typeof(BundleVerifierMod), nameof(OnLeftRoom))); harmonyInstance.Patch(typeof(NetworkManager).GetMethod("OnJoinedRoom"), new HarmonyMethod(typeof(BundleVerifierMod), nameof(OnJoinedRoom))); EnabledSetting.OnValueChangedUntyped += () => MelonCoroutines.Start(CheckInstanceType()); OnlyPublics.OnValueChangedUntyped += () => MelonCoroutines.Start(CheckInstanceType()); }