param( [Parameter(Mandatory = $true)] [int]$ProcessId, [Parameter(Mandatory = $true)] [string]$OutputPath ) $source = @' using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; public static class StarletSramScanner { [StructLayout(LayoutKind.Sequential)] private struct MEMORY_BASIC_INFORMATION { public IntPtr BaseAddress; public IntPtr AllocationBase; public uint AllocationProtect; public ushort PartitionId; public UIntPtr RegionSize; public uint State; public uint Protect; public uint Type; } [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr OpenProcess(uint access, bool inherit, int processId); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32.dll", SetLastError = true)] private static extern UIntPtr VirtualQueryEx(IntPtr process, IntPtr address, out MEMORY_BASIC_INFORMATION information, UIntPtr length); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool ReadProcessMemory(IntPtr process, IntPtr address, byte[] buffer, UIntPtr size, out UIntPtr bytesRead); private const uint PROCESS_VM_READ = 0x0010; private const uint PROCESS_QUERY_INFORMATION = 0x0400; private const uint MEM_COMMIT = 0x1000; private const uint PAGE_NOACCESS = 0x01; private const uint PAGE_GUARD = 0x100; private const int SRAM_SIZE = 0x18000; private const int SIGNATURE_OFFSET = 0x540; private static readonly byte[] Signature = { 0xe5, 0x93, 0x00, 0x00, 0xe1, 0x51, 0x08, 0x20, 0x0a, 0xff, 0xff, 0xfc }; private static int Find(byte[] haystack, int count) { for (int i = 0; i <= count - Signature.Length; ++i) { int j = 0; while (j < Signature.Length && haystack[i + j] == Signature[j]) ++j; if (j == Signature.Length) return i; } return -1; } public static long Dump(int processId, string outputPath) { IntPtr process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, processId); if (process == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcess failed"); try { ulong address = 0x10000; ulong maximum = 0x00007fffffff0000UL; int mbiSize = Marshal.SizeOf(); while (address < maximum) { MEMORY_BASIC_INFORMATION mbi; UIntPtr queried = VirtualQueryEx(process, new IntPtr(unchecked((long)address)), out mbi, new UIntPtr((uint)mbiSize)); if (queried == UIntPtr.Zero) break; ulong baseAddress = unchecked((ulong)mbi.BaseAddress.ToInt64()); ulong regionSize = mbi.RegionSize.ToUInt64(); if (regionSize == 0) break; bool readable = mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_NOACCESS) == 0 && (mbi.Protect & PAGE_GUARD) == 0; if (readable) { const int chunkSize = 4 * 1024 * 1024; ulong offset = 0; while (offset < regionSize) { int requested = (int)Math.Min((ulong)chunkSize, regionSize - offset); byte[] chunk = new byte[requested]; UIntPtr bytesRead; if (ReadProcessMemory(process, new IntPtr(unchecked((long)(baseAddress + offset))), chunk, new UIntPtr((uint)requested), out bytesRead)) { int hit = Find(chunk, checked((int)bytesRead.ToUInt64())); if (hit >= 0) { ulong signatureAddress = baseAddress + offset + (uint)hit; if (signatureAddress < SIGNATURE_OFFSET) break; ulong sramAddress = signatureAddress - SIGNATURE_OFFSET; byte[] sram = new byte[SRAM_SIZE]; UIntPtr sramRead; if (ReadProcessMemory(process, new IntPtr(unchecked((long)sramAddress)), sram, new UIntPtr(SRAM_SIZE), out sramRead) && sramRead.ToUInt64() == SRAM_SIZE && FindAt(sram, SIGNATURE_OFFSET)) { File.WriteAllBytes(outputPath, sram); return unchecked((long)sramAddress); } } } offset += (ulong)requested; } } address = baseAddress + regionSize; if (address <= baseAddress) break; } } finally { CloseHandle(process); } throw new InvalidOperationException("Starlet SRAM signature was not found"); } private static bool FindAt(byte[] bytes, int offset) { if (offset < 0 || offset + Signature.Length > bytes.Length) return false; for (int i = 0; i < Signature.Length; ++i) { if (bytes[offset + i] != Signature[i]) return false; } return true; } } '@ Add-Type -TypeDefinition $source -Language CSharp $resolvedOutput = [System.IO.Path]::GetFullPath($OutputPath) $address = [StarletSramScanner]::Dump($ProcessId, $resolvedOutput) "Starlet SRAM dumped from host address 0x{0:x16} to {1}" -f $address, $resolvedOutput