IOS: add Starlet LLE milestone through HBC
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.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)] [int] $ProcessId,
|
||||
[Parameter(Mandatory = $true)] [UInt64] $Address,
|
||||
[Parameter(Mandatory = $true)] [int] $Length,
|
||||
[Parameter(Mandatory = $true)] [string] $OutputPath
|
||||
)
|
||||
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static class ProcessMemoryDumpNative
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr OpenProcess(uint access, bool inheritHandle, int processId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern bool ReadProcessMemory(IntPtr process, UIntPtr address, byte[] buffer,
|
||||
UIntPtr size, out UIntPtr bytesRead);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool CloseHandle(IntPtr handle);
|
||||
}
|
||||
'@
|
||||
|
||||
$handle = [ProcessMemoryDumpNative]::OpenProcess(0x410, $false, $ProcessId)
|
||||
if ($handle -eq [IntPtr]::Zero) {
|
||||
throw "OpenProcess failed: $([Runtime.InteropServices.Marshal]::GetLastWin32Error())"
|
||||
}
|
||||
|
||||
try {
|
||||
$buffer = [byte[]]::new($Length)
|
||||
$bytesRead = [UIntPtr]::Zero
|
||||
if (-not [ProcessMemoryDumpNative]::ReadProcessMemory(
|
||||
$handle, [UIntPtr]::new($Address), $buffer, [UIntPtr]::new([UInt64]$Length), [ref]$bytesRead)) {
|
||||
throw "ReadProcessMemory failed: $([Runtime.InteropServices.Marshal]::GetLastWin32Error())"
|
||||
}
|
||||
if ($bytesRead.ToUInt64() -ne [UInt64]$Length) {
|
||||
throw "Short read: requested $Length bytes, got $($bytesRead.ToUInt64())"
|
||||
}
|
||||
[IO.File]::WriteAllBytes((Join-Path (Get-Location) $OutputPath), $buffer)
|
||||
}
|
||||
finally {
|
||||
[void][ProcessMemoryDumpNative]::CloseHandle($handle)
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
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
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a LetterBomb tree directly in Dolphin's virtual SD folder."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import hmac
|
||||
import shutil
|
||||
import zipfile
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path, PurePosixPath
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_SOURCE = REPO_ROOT / ".starlet_check" / "letterbomb-web" / "public"
|
||||
|
||||
|
||||
def parse_mac(text: str) -> bytes:
|
||||
compact = text.replace(":", "").replace("-", "")
|
||||
if len(compact) != 12:
|
||||
raise argparse.ArgumentTypeError("MAC must contain exactly 12 hexadecimal digits")
|
||||
try:
|
||||
return bytes.fromhex(compact)
|
||||
except ValueError as error:
|
||||
raise argparse.ArgumentTypeError("MAC contains a non-hexadecimal character") from error
|
||||
|
||||
|
||||
def safe_extract(archive: zipfile.ZipFile, destination: Path) -> None:
|
||||
for member in archive.infolist():
|
||||
relative = PurePosixPath(member.filename)
|
||||
if relative.is_absolute() or ".." in relative.parts:
|
||||
raise RuntimeError(f"Unsafe archive member: {member.filename!r}")
|
||||
target = destination.joinpath(*relative.parts)
|
||||
if member.is_dir():
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
continue
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
with archive.open(member) as source, target.open("wb") as output:
|
||||
shutil.copyfileobj(source, output)
|
||||
|
||||
|
||||
def generate(template: bytes, mac: bytes, message_time: datetime) -> tuple[bytes, Path]:
|
||||
payload = bytearray(template)
|
||||
key = hashlib.sha1(mac + b"uyy").digest()
|
||||
payload[8:16] = key[:8]
|
||||
payload[176:196] = bytes(20)
|
||||
|
||||
epoch = datetime(2000, 1, 1)
|
||||
timestamp = int((message_time - epoch).total_seconds())
|
||||
payload[124:128] = timestamp.to_bytes(4, "big")
|
||||
payload[128:138] = f"{timestamp:010d}".encode("ascii")
|
||||
payload[176:196] = hmac.new(key[8:], payload, hashlib.sha1).digest()
|
||||
|
||||
relative_path = Path(
|
||||
"private",
|
||||
"wii",
|
||||
"title",
|
||||
"HAEA",
|
||||
key[:4].hex().upper(),
|
||||
key[4:8].hex().upper(),
|
||||
f"{message_time.year:04d}",
|
||||
f"{message_time.month - 1:02d}",
|
||||
f"{message_time.day:02d}",
|
||||
f"{message_time.hour:02d}",
|
||||
f"{message_time.minute:02d}",
|
||||
"HABA_#1",
|
||||
"txt",
|
||||
f"{timestamp:08X}.000",
|
||||
)
|
||||
return bytes(payload), relative_path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--mac", required=True, type=parse_mac)
|
||||
parser.add_argument("--region", required=True, choices=("E", "U", "J", "K"))
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
parser.add_argument("--source", type=Path, default=DEFAULT_SOURCE)
|
||||
parser.add_argument(
|
||||
"--date",
|
||||
help="message date as YYYY-MM-DD; defaults to yesterday in local Wii time",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
source = args.source.resolve()
|
||||
output = args.output.resolve()
|
||||
template_path = source / f"template{args.region}.bin"
|
||||
bundle_path = source / "hackmii.zip"
|
||||
if not template_path.is_file() or not bundle_path.is_file():
|
||||
raise RuntimeError(f"LetterBomb assets are missing from {source}")
|
||||
|
||||
if args.date:
|
||||
message_time = datetime.strptime(args.date, "%Y-%m-%d")
|
||||
else:
|
||||
message_time = datetime.now().replace(second=0, microsecond=0) - timedelta(days=1)
|
||||
|
||||
payload, relative_path = generate(template_path.read_bytes(), args.mac, message_time)
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
target = output / relative_path
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(payload)
|
||||
with zipfile.ZipFile(bundle_path) as archive:
|
||||
safe_extract(archive, output)
|
||||
|
||||
mac_text = ":".join(f"{byte:02X}" for byte in args.mac)
|
||||
(output / "LETTERBOMB-INFO.txt").write_text(
|
||||
"LetterBomb virtual Wii test card\n"
|
||||
f"System Menu: 4.3{args.region}\n"
|
||||
f"Emulated Wi-Fi MAC: {mac_text}\n"
|
||||
f"Message date: {message_time:%Y-%m-%d %H:%M}\n"
|
||||
f"Message file: {relative_path.as_posix()}\n"
|
||||
"Close Dolphin before changing files in this folder.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
print(f"LetterBomb generated at {target}")
|
||||
print(f"Bundled HackMii Installer extracted to {output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,82 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$ProcessId,
|
||||
[int64]$MinimumSize = 0x1000000
|
||||
)
|
||||
|
||||
$source = @'
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static class ProcessMemoryRegions
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Region
|
||||
{
|
||||
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 Region information, UIntPtr length);
|
||||
|
||||
public static Region[] List(int processId, long minimumSize)
|
||||
{
|
||||
IntPtr process = OpenProcess(0x0400, false, processId);
|
||||
if (process == IntPtr.Zero)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcess failed");
|
||||
var result = new List<Region>();
|
||||
try
|
||||
{
|
||||
ulong address = 0x10000;
|
||||
const ulong maximum = 0x00007fffffff0000UL;
|
||||
int size = Marshal.SizeOf<Region>();
|
||||
while (address < maximum)
|
||||
{
|
||||
Region region;
|
||||
if (VirtualQueryEx(process, new IntPtr(unchecked((long)address)), out region,
|
||||
new UIntPtr((uint)size)) == UIntPtr.Zero)
|
||||
break;
|
||||
ulong baseAddress = unchecked((ulong)region.BaseAddress.ToInt64());
|
||||
ulong regionSize = region.RegionSize.ToUInt64();
|
||||
if (regionSize == 0)
|
||||
break;
|
||||
if (region.State == 0x1000 && regionSize >= (ulong)minimumSize)
|
||||
result.Add(region);
|
||||
address = baseAddress + regionSize;
|
||||
if (address <= baseAddress)
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(process);
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
Add-Type -TypeDefinition $source -Language CSharp
|
||||
[ProcessMemoryRegions]::List($ProcessId, $MinimumSize) | ForEach-Object {
|
||||
[pscustomobject]@{
|
||||
Base = '0x{0:x16}' -f [uint64]$_.BaseAddress.ToInt64()
|
||||
AllocationBase = '0x{0:x16}' -f [uint64]$_.AllocationBase.ToInt64()
|
||||
Size = '0x{0:x}' -f $_.RegionSize.ToUInt64()
|
||||
Protect = '0x{0:x}' -f $_.Protect
|
||||
Type = '0x{0:x}' -f $_.Type
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$ProcessId,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[uint64]$Address,
|
||||
[int]$Length = 64
|
||||
)
|
||||
|
||||
$source = @'
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public static class ProcessMemoryReader
|
||||
{
|
||||
[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 bool ReadProcessMemory(IntPtr process, IntPtr address, byte[] buffer,
|
||||
UIntPtr size, out UIntPtr bytesRead);
|
||||
|
||||
public static byte[] Read(int processId, ulong address, int length)
|
||||
{
|
||||
IntPtr process = OpenProcess(0x0010 | 0x0400, false, processId);
|
||||
if (process == IntPtr.Zero)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcess failed");
|
||||
try
|
||||
{
|
||||
byte[] bytes = new byte[length];
|
||||
UIntPtr read;
|
||||
if (!ReadProcessMemory(process, new IntPtr(unchecked((long)address)), bytes,
|
||||
new UIntPtr((uint)length), out read) ||
|
||||
read.ToUInt64() != (ulong)length)
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), "ReadProcessMemory failed");
|
||||
return bytes;
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(process);
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
Add-Type -TypeDefinition $source -Language CSharp
|
||||
$bytes = [ProcessMemoryReader]::Read($ProcessId, $Address, $Length)
|
||||
for ($offset = 0; $offset -lt $bytes.Length; $offset += 16) {
|
||||
$count = [Math]::Min(16, $bytes.Length - $offset)
|
||||
$hex = ($bytes[$offset..($offset + $count - 1)] | ForEach-Object { '{0:x2}' -f $_ }) -join ' '
|
||||
'0x{0:x16}: {1}' -f ($Address + [uint64]$offset), $hex
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import ctypes
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
REPOSITORY_ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPOSITORY_ROOT / "capstone_local"))
|
||||
|
||||
from capstone import CS_ARCH_ARM, CS_ARCH_PPC, CS_MODE_32, CS_MODE_ARM, CS_MODE_BIG_ENDIAN, Cs
|
||||
|
||||
|
||||
PROCESS_VM_READ = 0x0010
|
||||
PROCESS_QUERY_INFORMATION = 0x0400
|
||||
|
||||
|
||||
def read_process_memory(process_id: int, address: int, length: int) -> bytes:
|
||||
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
||||
kernel32.OpenProcess.argtypes = [ctypes.c_uint32, ctypes.c_bool, ctypes.c_uint32]
|
||||
kernel32.OpenProcess.restype = ctypes.c_void_p
|
||||
kernel32.ReadProcessMemory.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_size_t,
|
||||
ctypes.POINTER(ctypes.c_size_t),
|
||||
]
|
||||
kernel32.ReadProcessMemory.restype = ctypes.c_bool
|
||||
kernel32.CloseHandle.argtypes = [ctypes.c_void_p]
|
||||
kernel32.CloseHandle.restype = ctypes.c_bool
|
||||
|
||||
process = kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, process_id)
|
||||
if not process:
|
||||
raise ctypes.WinError(ctypes.get_last_error())
|
||||
|
||||
try:
|
||||
buffer = ctypes.create_string_buffer(length)
|
||||
bytes_read = ctypes.c_size_t()
|
||||
if not kernel32.ReadProcessMemory(
|
||||
process, ctypes.c_void_p(address), buffer, length, ctypes.byref(bytes_read)
|
||||
):
|
||||
raise ctypes.WinError(ctypes.get_last_error())
|
||||
if bytes_read.value != length:
|
||||
raise RuntimeError(f"short read: requested {length} bytes, got {bytes_read.value}")
|
||||
return buffer.raw
|
||||
finally:
|
||||
kernel32.CloseHandle(process)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Disassemble big-endian 32-bit PowerPC code from a live Windows process."
|
||||
)
|
||||
parser.add_argument("process_id", type=int)
|
||||
parser.add_argument("host_address", type=lambda value: int(value, 0))
|
||||
parser.add_argument("guest_address", type=lambda value: int(value, 0))
|
||||
parser.add_argument("length", type=lambda value: int(value, 0))
|
||||
parser.add_argument("--arch", choices=("ppc", "arm"), default="ppc")
|
||||
args = parser.parse_args()
|
||||
|
||||
code = read_process_memory(args.process_id, args.host_address, args.length)
|
||||
if args.arch == "arm":
|
||||
disassembler = Cs(CS_ARCH_ARM, CS_MODE_ARM | CS_MODE_BIG_ENDIAN)
|
||||
else:
|
||||
disassembler = Cs(CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN)
|
||||
for instruction in disassembler.disasm(code, args.guest_address):
|
||||
operands = f" {instruction.op_str}" if instruction.op_str else ""
|
||||
print(f"{instruction.address:08x}: {instruction.bytes.hex(' '):11} "
|
||||
f"{instruction.mnemonic}{operands}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user