mirror of
https://git.crafterpika.cc/crafterpika/SplatTool-public.git
synced 2026-08-17 18:52:16 +02:00
420 lines
15 KiB
C#
420 lines
15 KiB
C#
|
|
// modules
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
|
||
|
|
namespace ProcessWrapper {
|
||
|
|
public interface IProcessWrapper {
|
||
|
|
byte[] ReadProcessMemory(int pid, ulong address, uint length);
|
||
|
|
bool WriteProcessMemory(int pid, ulong address, byte[] data);
|
||
|
|
int getuid();
|
||
|
|
ulong FindCemuBase(int pid, ulong minSize);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mac implementation
|
||
|
|
public class MacProcessWrapper : IProcessWrapper {
|
||
|
|
// vars
|
||
|
|
const int KERN_SUCCESS = 0;
|
||
|
|
const string LIBSYSTEM = "/usr/lib/libSystem.B.dylib";
|
||
|
|
const int VM_REGION_BASIC_INFO_64 = 9;
|
||
|
|
const int VM_REGION_BASIC_INFO_COUNT_64 = 10;
|
||
|
|
const int VM_PROT_READ = 0x01;
|
||
|
|
|
||
|
|
// Struct
|
||
|
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
|
public struct vm_region_basic_info_64 {
|
||
|
|
public int protection;
|
||
|
|
public int max_protection;
|
||
|
|
public int inheritance;
|
||
|
|
public int shared;
|
||
|
|
public int reserved;
|
||
|
|
public ulong offset;
|
||
|
|
public int behavior;
|
||
|
|
public short user_wired_count;
|
||
|
|
public short user_tag;
|
||
|
|
}
|
||
|
|
|
||
|
|
// https://github.com/attilathedud/macos_task_for_pid#overview
|
||
|
|
// Well documenated apis, thanks Apple.
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern int task_for_pid(IntPtr task,
|
||
|
|
int pid,
|
||
|
|
out IntPtr targetTask);
|
||
|
|
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern IntPtr mach_task_self();
|
||
|
|
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern int mach_vm_read(IntPtr target_task,
|
||
|
|
ulong address,
|
||
|
|
ulong size,
|
||
|
|
out IntPtr data,
|
||
|
|
out ulong data_count);
|
||
|
|
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern int mach_vm_write(IntPtr target_task,
|
||
|
|
ulong address,
|
||
|
|
byte[] data,
|
||
|
|
uint dataCnt);
|
||
|
|
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern int vm_deallocate(IntPtr task,
|
||
|
|
IntPtr address,
|
||
|
|
ulong size);
|
||
|
|
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern int mach_vm_region(IntPtr task,
|
||
|
|
ref ulong address,
|
||
|
|
out ulong size,
|
||
|
|
int flavor,
|
||
|
|
IntPtr info,
|
||
|
|
ref uint count,
|
||
|
|
out ulong object_name);
|
||
|
|
|
||
|
|
[DllImport(LIBSYSTEM, SetLastError = true)]
|
||
|
|
private static extern int geteuid();
|
||
|
|
|
||
|
|
public int getuid() {
|
||
|
|
return geteuid();
|
||
|
|
}
|
||
|
|
|
||
|
|
public byte[] ReadProcessMemory(int pid, ulong address, uint length) {
|
||
|
|
IntPtr task;
|
||
|
|
IntPtr localTask = mach_task_self();
|
||
|
|
|
||
|
|
int result = task_for_pid(localTask, pid, out task);
|
||
|
|
if (result != KERN_SUCCESS) {
|
||
|
|
Console.WriteLine($"task_for_pid failed with code {result}");
|
||
|
|
return new byte[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
IntPtr bufferPtr;
|
||
|
|
ulong size;
|
||
|
|
result = mach_vm_read(task, address, length, out bufferPtr, out size);
|
||
|
|
if (result != KERN_SUCCESS) {
|
||
|
|
//Console.WriteLine($"mach_vm_read failed with code {result}");
|
||
|
|
return new byte[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
byte[] buffer = new byte[size];
|
||
|
|
Marshal.Copy(bufferPtr, buffer, 0, (int)size);
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
vm_deallocate(localTask, bufferPtr, size);
|
||
|
|
|
||
|
|
return buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool WriteProcessMemory(int pid, ulong address, byte[] data) {
|
||
|
|
IntPtr task;
|
||
|
|
IntPtr localTask = mach_task_self();
|
||
|
|
|
||
|
|
int result = task_for_pid(localTask, pid, out task);
|
||
|
|
if (result != KERN_SUCCESS) {
|
||
|
|
//throw new Exception($"task_for_pid failed: {result}");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = mach_vm_write(task, address, data, (uint)data.Length);
|
||
|
|
if (result != KERN_SUCCESS) {
|
||
|
|
//throw new Exception($"mach_vm_write failed: {result}");
|
||
|
|
Console.WriteLine($"mach_vm_write failed: {result}");
|
||
|
|
return false;
|
||
|
|
} else {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Console.WriteLine($"Wrote {data.Length} bytes to 0x{address:X}");
|
||
|
|
}
|
||
|
|
|
||
|
|
public ulong FindCemuBase(int pid, ulong maxSize) {
|
||
|
|
IntPtr task;
|
||
|
|
IntPtr localTask = mach_task_self();
|
||
|
|
byte?[] patternBytes = new byte?[] { 0x02, 0xD4, 0xE7 };
|
||
|
|
|
||
|
|
int result = task_for_pid(localTask, pid, out task);
|
||
|
|
if (result != KERN_SUCCESS) {
|
||
|
|
Console.WriteLine($"task_for_pid failed: {result}");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
ulong address = 0;
|
||
|
|
while (true) {
|
||
|
|
ulong size;
|
||
|
|
uint count = VM_REGION_BASIC_INFO_COUNT_64;
|
||
|
|
IntPtr infoPtr = Marshal.AllocHGlobal(Marshal.SizeOf<vm_region_basic_info_64>());
|
||
|
|
ulong objectName;
|
||
|
|
|
||
|
|
result = mach_vm_region(task,
|
||
|
|
ref address,
|
||
|
|
out size,
|
||
|
|
VM_REGION_BASIC_INFO_64,
|
||
|
|
infoPtr,
|
||
|
|
ref count,
|
||
|
|
out objectName
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result != KERN_SUCCESS) {
|
||
|
|
Marshal.FreeHGlobal(infoPtr);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
var regionInfo = Marshal.PtrToStructure<vm_region_basic_info_64>(infoPtr);
|
||
|
|
Marshal.FreeHGlobal(infoPtr);
|
||
|
|
|
||
|
|
bool readable = (regionInfo.protection & VM_PROT_READ) != 0;
|
||
|
|
|
||
|
|
if (readable && size < maxSize || size == maxSize) {
|
||
|
|
var bytes = ReadProcessMemory(pid, (ulong)(address + 0xE000000), 20);
|
||
|
|
|
||
|
|
if (bytes != null && bytes.Length > 0) {
|
||
|
|
int patternLen = patternBytes.Length;
|
||
|
|
for (int i = 0, j = 0; i + patternLen <= 20; i++) {
|
||
|
|
if (patternBytes[j] == null || bytes[i] == patternBytes[j]) {
|
||
|
|
j++;
|
||
|
|
} else {
|
||
|
|
j = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (j >= patternLen) {
|
||
|
|
return address;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
address += size;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Linux implementation
|
||
|
|
public class LinuxProcessWrapper : IProcessWrapper {
|
||
|
|
// IOVec Struct
|
||
|
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
|
private struct IOVec {
|
||
|
|
public UIntPtr Base;
|
||
|
|
public UIntPtr Length;
|
||
|
|
}
|
||
|
|
|
||
|
|
[DllImport("libc", SetLastError = true)]
|
||
|
|
private static extern long process_vm_readv(int pid,
|
||
|
|
IOVec[] local_iov, ulong liovcnt,
|
||
|
|
IOVec[] remote_iov, ulong riovcnt,
|
||
|
|
ulong flags);
|
||
|
|
|
||
|
|
[DllImport("libc", SetLastError = true)]
|
||
|
|
private static extern long process_vm_writev(int pid,
|
||
|
|
IOVec[] local_iov, ulong liovcnt,
|
||
|
|
IOVec[] remote_iov, ulong riovcnt,
|
||
|
|
ulong flags);
|
||
|
|
|
||
|
|
[DllImport("libc", SetLastError = true)]
|
||
|
|
private static extern int geteuid();
|
||
|
|
|
||
|
|
public int getuid() {
|
||
|
|
return geteuid();
|
||
|
|
}
|
||
|
|
|
||
|
|
public byte[] ReadProcessMemory(int pid, ulong address, uint length) {
|
||
|
|
byte[] buffer = new byte[length];
|
||
|
|
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||
|
|
|
||
|
|
try {
|
||
|
|
var local = new IOVec {
|
||
|
|
Base = new UIntPtr((ulong)handle.AddrOfPinnedObject().ToInt64()),
|
||
|
|
Length = (UIntPtr)length
|
||
|
|
};
|
||
|
|
|
||
|
|
var remote = new IOVec {
|
||
|
|
Base = (UIntPtr)address,
|
||
|
|
Length = (UIntPtr)length
|
||
|
|
};
|
||
|
|
|
||
|
|
long nread = process_vm_readv(pid, new[] { local }, 1, new[] { remote }, 1, 0);
|
||
|
|
if (nread == -1) {
|
||
|
|
// Console.WriteLine($"Failed to read memory. Error code: {Marshal.GetLastWin32Error()}");
|
||
|
|
return new byte[0];
|
||
|
|
} else {
|
||
|
|
// Console.WriteLine($"Read {nread} bytes from process {pid} at 0x{address:X}");
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
handle.Free();
|
||
|
|
}
|
||
|
|
|
||
|
|
return buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool WriteProcessMemory(int pid, ulong address, byte[] data) {
|
||
|
|
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||
|
|
|
||
|
|
try {
|
||
|
|
var local = new IOVec {
|
||
|
|
Base = new UIntPtr((ulong)handle.AddrOfPinnedObject().ToInt64()),
|
||
|
|
Length = (UIntPtr)data.Length
|
||
|
|
};
|
||
|
|
|
||
|
|
var remote = new IOVec {
|
||
|
|
Base = (UIntPtr)address,
|
||
|
|
Length = (UIntPtr)data.Length
|
||
|
|
};
|
||
|
|
|
||
|
|
long nwritten = process_vm_writev(pid, new[] { local }, 1, new[] { remote }, 1, 0);
|
||
|
|
if (nwritten == -1) {
|
||
|
|
// Console.WriteLine($"Failed to write memory. Error code: {Marshal.GetLastWin32Error()}");
|
||
|
|
return false;
|
||
|
|
} else {
|
||
|
|
// Console.WriteLine($"Wrote {nwritten} bytes to process {pid} at 0x{address:X}");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
handle.Free();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public ulong FindCemuBase(int pid, ulong maxSize) {
|
||
|
|
// vars
|
||
|
|
string mapsPath = $"/proc/{pid}/maps";
|
||
|
|
byte?[] patternBytes = new byte?[] { 0x02, 0xD4, 0xE7 };
|
||
|
|
|
||
|
|
string[] mapsLines = File.ReadAllLines(mapsPath);
|
||
|
|
foreach (string line in mapsLines) {
|
||
|
|
var split = line.Split(" ");
|
||
|
|
var addrP = split[0].Split("-");
|
||
|
|
ulong min = Convert.ToUInt64(addrP[0], 16);
|
||
|
|
ulong max = Convert.ToUInt64(addrP[1], 16);
|
||
|
|
|
||
|
|
if (!split[1].Contains("r")) continue;
|
||
|
|
var len = (max - min);
|
||
|
|
if (len > maxSize || len == maxSize) {
|
||
|
|
var bytes = ReadProcessMemory(pid, (ulong)(min + 0xE000000), 20);
|
||
|
|
|
||
|
|
if (bytes != null && bytes.Length > 0) {
|
||
|
|
int patternLen = patternBytes.Length;
|
||
|
|
for (int i = 0, j = 0; i + patternLen <= 20; i++) {
|
||
|
|
if (patternBytes[j] == null || bytes[i] == patternBytes[j]) {
|
||
|
|
j++;
|
||
|
|
} else {
|
||
|
|
j = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (j >= patternLen) {
|
||
|
|
return min;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class WindowsProcessWrapper : IProcessWrapper {
|
||
|
|
// vars
|
||
|
|
const uint PROCESS_VM_READ = 0x0010;
|
||
|
|
const uint PROCESS_VM_WRITE = 0x0020;
|
||
|
|
const uint PROCESS_VM_OPERATION = 0x0008;
|
||
|
|
const uint PROCESS_QUERY_INFORMATION = 0x0400;
|
||
|
|
|
||
|
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
|
public struct MEMORY_BASIC_INFORMATION {
|
||
|
|
public IntPtr BaseAddress;
|
||
|
|
public IntPtr AllocationBase;
|
||
|
|
public uint AllocationProtect;
|
||
|
|
public IntPtr RegionSize;
|
||
|
|
public uint State;
|
||
|
|
public uint Protect;
|
||
|
|
public uint Type;
|
||
|
|
}
|
||
|
|
|
||
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||
|
|
private static extern IntPtr OpenProcess(uint dwDesiredAccess,
|
||
|
|
bool bInheritHandle,
|
||
|
|
int dwProcessId);
|
||
|
|
|
||
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||
|
|
private static extern bool ReadProcessMemory(IntPtr hProcess,
|
||
|
|
IntPtr lpBaseAddress,
|
||
|
|
[Out] byte[] lpBuffer,
|
||
|
|
int dwSize,
|
||
|
|
out int lpNumberOfBytesRead);
|
||
|
|
|
||
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||
|
|
private static extern bool WriteProcessMemory(IntPtr hProcess,
|
||
|
|
IntPtr lpBaseAddress,
|
||
|
|
byte[] lpBuffer,
|
||
|
|
int dwSize,
|
||
|
|
out int lpNumberOfBytesWritten);
|
||
|
|
|
||
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||
|
|
private static extern int VirtualQueryEx(IntPtr hProcess,
|
||
|
|
IntPtr lpAddress,
|
||
|
|
out MEMORY_BASIC_INFORMATION lpBuffer,
|
||
|
|
uint dwLength);
|
||
|
|
|
||
|
|
// Windows doesnt need this.
|
||
|
|
public int getuid() {
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
public byte[] ReadProcessMemory(int pid, ulong address, uint length) {
|
||
|
|
IntPtr hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, pid);
|
||
|
|
if (hProcess == IntPtr.Zero) {
|
||
|
|
return new byte[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
byte[] buffer = new byte[length];
|
||
|
|
if (ReadProcessMemory(hProcess, (IntPtr)address, buffer, (int)length, out int bytesRead) && bytesRead > 0) {
|
||
|
|
if (bytesRead < length) {
|
||
|
|
Array.Resize(ref buffer, bytesRead);
|
||
|
|
}
|
||
|
|
return buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
return new byte[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool WriteProcessMemory(int pid, ulong address, byte[] data) {
|
||
|
|
IntPtr hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, false, pid);
|
||
|
|
if (hProcess == IntPtr.Zero) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return WriteProcessMemory(hProcess, (IntPtr)address, data, data.Length, out int bytesWritten) && bytesWritten == data.Length;
|
||
|
|
}
|
||
|
|
|
||
|
|
public ulong FindCemuBase(int pid, ulong maxSize) {
|
||
|
|
byte?[] patternBytes = new byte?[] { 0x02, 0xD4, 0xE7 };
|
||
|
|
IntPtr address = IntPtr.Zero;
|
||
|
|
MEMORY_BASIC_INFORMATION mbi;
|
||
|
|
|
||
|
|
IntPtr hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, false, pid);
|
||
|
|
if (hProcess == IntPtr.Zero) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (VirtualQueryEx(hProcess, address, out mbi, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION))) != 0) {
|
||
|
|
bool readable = (mbi.State == 0x1000) && (mbi.Protect != 0x01);
|
||
|
|
ulong regionSize = (ulong)mbi.RegionSize.ToInt64();
|
||
|
|
if (readable && regionSize > maxSize || regionSize == maxSize) {
|
||
|
|
var bytes = ReadProcessMemory(pid, (ulong)(mbi.BaseAddress + 0xE000000), 20);
|
||
|
|
if (bytes != null && bytes.Length > 0) {
|
||
|
|
int patternLen = patternBytes.Length;
|
||
|
|
for (int i = 0, j = 0; i + patternLen <= 20; i++) {
|
||
|
|
if (patternBytes[j] == null || bytes[i] == patternBytes[j]) {
|
||
|
|
j++;
|
||
|
|
} else {
|
||
|
|
j = 0;
|
||
|
|
}
|
||
|
|
if (j >= patternLen) {
|
||
|
|
return (ulong)mbi.BaseAddress;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
address = new IntPtr(mbi.BaseAddress.ToInt64() + mbi.RegionSize.ToInt64());
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|