Boot original Wii firmware through boot0, boot1, boot2 and IOS on an emulated ARM Starlet. Model the required IPC, memory, SD, USB and Bluetooth hardware behavior, including IOS reload into IOS58, and add focused tests, launch utilities and architecture documentation.
161 lines
6.2 KiB
PowerShell
161 lines
6.2 KiB
PowerShell
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<MEMORY_BASIC_INFORMATION>();
|
|
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
|