Created
March 15, 2026 07:09
-
-
Save artchen/07a14bbc1d589196d0da65169a81bca3 to your computer and use it in GitHub Desktop.
Extract reverse 1999 summon url
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 | |
| Add-Type -AssemblyName System.Web | |
| $ProgressPreference = 'SilentlyContinue' | |
| Write-Output "Preparing to extract Reverse: 1999 Summon URL from memory..." | |
| $processName = "reverse1999" | |
| $process = Get-Process -Name $processName -ErrorAction SilentlyContinue | |
| if (-Not $process) { | |
| Write-Output "Reverse: 1999 is not running!" | |
| Write-Output "Please open the game and navigate to the 'Summon Review' history page first." | |
| return | |
| } | |
| $process = $process[0] | |
| Write-Output "Game process found (PID: $($process.Id))." | |
| Write-Output "Warning: This script will temporarily create a very large memory dump file (several gigabytes)." | |
| Write-Output "Please ensure you have sufficient free space on your C: drive." | |
| # Dynamically generate class name so we can re-run script in same window | |
| $ScannerClassName = "MemoryScanner_$([Guid]::NewGuid().ToString('N'))" | |
| $MemoryScannerCode = @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| using System.IO; | |
| public class $ScannerClassName { | |
| [DllImport("Dbghelp.dll", SetLastError = true)] | |
| public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, IntPtr hFile, int DumpType, IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam); | |
| public static int IndexOf(byte[] haystack, int haystackLength, byte[] needle, int startIndex) { | |
| if (needle.Length == 0 || haystackLength == 0 || haystackLength < needle.Length || startIndex >= haystackLength) return -1; | |
| int[] badCharSkip = new int[256]; | |
| for (int i = 0; i < 256; i++) badCharSkip[i] = needle.Length; | |
| for (int i = 0; i < needle.Length - 1; i++) badCharSkip[needle[i]] = needle.Length - 1 - i; | |
| int offset = startIndex; | |
| int maxOffset = haystackLength - needle.Length; | |
| while (offset <= maxOffset) { | |
| int scan = needle.Length - 1; | |
| while (haystack[offset + scan] == needle[scan]) { | |
| if (scan == 0) return offset; | |
| scan--; | |
| } | |
| offset += badCharSkip[haystack[offset + needle.Length - 1]]; | |
| } | |
| return -1; | |
| } | |
| } | |
| "@ | |
| try { | |
| Add-Type -TypeDefinition $MemoryScannerCode | |
| } catch { | |
| Write-Output "Failed to compile memory scanner tools." | |
| return | |
| } | |
| $dumpPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), "reverse1999_summon_dump.dmp") | |
| Write-Output "Creating process memory dump to $dumpPath ..." | |
| Write-Output "Depending on your hardware, this may take 10-30 seconds..." | |
| $fsDump = $null | |
| $dumpSuccess = $false | |
| try { | |
| $fsDump = [System.IO.File]::Create($dumpPath) | |
| $hFile = $fsDump.SafeFileHandle.DangerousGetHandle() | |
| $MiniDumpWithFullMemory = 2 | |
| $handle = $process.Handle | |
| $type = ("$ScannerClassName" -as [type]) | |
| $dumpSuccess = $type::MiniDumpWriteDump($handle, $process.Id, $hFile, $MiniDumpWithFullMemory, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero) | |
| } catch { | |
| Write-Output "" | |
| Write-Output "[ERROR] Access Denied: Could not read Reverse: 1999 process memory." | |
| Write-Output "Please reopen PowerShell by right clicking and selecting 'Run as Administrator', then run the script again." | |
| return | |
| } finally { | |
| if ($fsDump -ne $null) { | |
| $fsDump.Close() | |
| $fsDump.Dispose() | |
| } | |
| } | |
| if (-Not $dumpSuccess) { | |
| Write-Output "Failed to create memory dump." | |
| Write-Output "Please ensure you run this script as Administrator." | |
| return | |
| } | |
| Write-Output "Memory dump created successfully!" | |
| Write-Output "Scanning binary dump for the summon URL... this may take a moment." | |
| $fs = $null | |
| try { | |
| $fs = [System.IO.FileStream]::new($dumpPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) | |
| $bufferSize = 50 * 1024 * 1024 # 50 MB chunk sizes | |
| $buffer = New-Object byte[] $bufferSize | |
| # Depending on how the game stores strings in RAM, it could be ASCII/UTF-8 or UTF-16 (Unicode). | |
| # We will search for both encodings. | |
| $targetString = "com/query/summon" | |
| $patternAscii = [System.Text.Encoding]::UTF8.GetBytes($targetString) | |
| $patternUnicode = [System.Text.Encoding]::Unicode.GetBytes($targetString) | |
| $overlap = [Math]::Max($patternAscii.Length, $patternUnicode.Length) | |
| $bytesRead = 0 | |
| $foundUrl = $null | |
| while (($bytesRead = $fs.Read($buffer, 0, $bufferSize)) -gt 0) { | |
| $matchIdx = 0 | |
| while ($matchIdx -lt $bytesRead -and $matchIdx -ne -1) { | |
| $type = ("$ScannerClassName" -as [type]) | |
| $matchIdxA = $type::IndexOf($buffer, $bytesRead, $patternAscii, $matchIdx) | |
| $matchIdxU = $type::IndexOf($buffer, $bytesRead, $patternUnicode, $matchIdx) | |
| $matchIdxTmp = -1 | |
| $isUnicode = $false | |
| # Find the earliest match in the chunk | |
| if ($matchIdxA -ne -1 -and ($matchIdxU -eq -1 -or $matchIdxA -lt $matchIdxU)) { | |
| $matchIdxTmp = $matchIdxA | |
| } elseif ($matchIdxU -ne -1) { | |
| $matchIdxTmp = $matchIdxU | |
| $isUnicode = $true | |
| } | |
| if ($matchIdxTmp -ne -1) { | |
| # Expand a window around the match | |
| $startWindow = [Math]::Max(0, $matchIdxTmp - 1000) | |
| $endWindow = [Math]::Min($bytesRead, $matchIdxTmp + 2000) | |
| $windowLen = $endWindow - $startWindow | |
| $windowBytes = New-Object byte[] $windowLen | |
| [Array]::Copy($buffer, $startWindow, $windowBytes, 0, $windowLen) | |
| if ($isUnicode) { | |
| $textWindow = [System.Text.Encoding]::Unicode.GetString($windowBytes) | |
| } else { | |
| $textWindow = [System.Text.Encoding]::UTF8.GetString($windowBytes) | |
| } | |
| # Regex capture for full https url terminating at whitespace/null | |
| # Use single quotes to prevent PowerShell from trying to parse $ or other symbols inside the regex | |
| if ($textWindow -match '(https?://[a-zA-Z0-9\-\.]*com/query/summon[a-zA-Z0-9\-\._~:/?#\[\]@!$&''()*+,;="%]+)') { | |
| $foundUrl = $matches[1] | |
| break | |
| } else { | |
| # Print a bit of the window for debugging if we found the anchor but regex failed | |
| $cleanWindow = $textWindow -replace "[\0\r\n\t]", " " | |
| $idx = $cleanWindow.IndexOf("com/query/summon") | |
| if ($idx -gt 0) { | |
| $startPrint = [Math]::Max(0, $idx - 100) | |
| $lenPrint = [Math]::Min($cleanWindow.Length - $startPrint, 250) | |
| Write-Output "[DEBUG] Found anchor but regex failed. Context:" | |
| Write-Output $cleanWindow.Substring($startPrint, $lenPrint) | |
| } | |
| } | |
| # Advance index past this match if it was a false positive | |
| if ($isUnicode) { | |
| $matchIdx = $matchIdxTmp + $patternUnicode.Length | |
| } else { | |
| $matchIdx = $matchIdxTmp + $patternAscii.Length | |
| } | |
| } else { | |
| break | |
| } | |
| } | |
| if ($foundUrl) { break } | |
| if ($bytesRead -eq $bufferSize) { | |
| $fs.Seek(-$overlap, [System.IO.SeekOrigin]::Current) | Out-Null | |
| } | |
| } | |
| if ($foundUrl) { | |
| try { | |
| # Strip Unity HTML escape characters if present in raw memory | |
| $foundUrl = $foundUrl -replace "&", "&" | |
| $uri = [Uri]$foundUrl | |
| $query = [Web.HttpUtility]::ParseQueryString($uri.Query) | |
| $keys = @($query.AllKeys) | |
| foreach ($key in $keys) { | |
| if ($key -eq "userId") { continue } | |
| if ($key -eq "token") { continue } | |
| $query.Remove($key) | |
| } | |
| $latest_url = $uri.Scheme + "://" + $uri.Host + $uri.AbsolutePath + "?" + $query.ToString() | |
| Write-Output "`n[SUCCESS] Summon History URL Found!" | |
| Write-Output $latest_url | |
| Set-Clipboard -Value $latest_url | |
| Write-Output "The URL has been cleanly formatted and saved to your clipboard!" | |
| } catch { | |
| Write-Output "Found a matching URL but failed to parse it: $foundUrl" | |
| } | |
| } else { | |
| Write-Output "`nCould not locate Summon History URL in process memory." | |
| Write-Output "Please make sure you have opened the 'Summon Review' page IN-GAME right before running this script." | |
| } | |
| } finally { | |
| if ($fs -ne $null) { | |
| $fs.Close() | |
| $fs.Dispose() | |
| } | |
| if (Test-Path $dumpPath) { | |
| Write-Output "Cleaning up multi-gigabyte temporary dump file..." | |
| Remove-Item -Path $dumpPath -Force -ErrorAction SilentlyContinue | |
| } | |
| } |
Comments are disabled for this gist.