Skip to content

Instantly share code, notes, and snippets.

@DartPower
Last active June 19, 2026 07:50
Show Gist options
  • Select an option

  • Save DartPower/70772a446598e73b969d60287599fc14 to your computer and use it in GitHub Desktop.

Select an option

Save DartPower/70772a446598e73b969d60287599fc14 to your computer and use it in GitHub Desktop.
XBox One some Emu proto...
// Program.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
[assembly: AssemblyCompany("XBEmu180626_2")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("XBEmu180626_2")]
[assembly: AssemblyTitle("XBEmu180626_2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace XBEmu180626_2
{
public static partial class Program
{
public static void LogDirect(string msg)
{
try
{
byte[] bytes = Encoding.ASCII.GetBytes(msg + "\r\n");
IntPtr hFile = NativeMethods.CreateFile("C:\\xbemu_thread.log", NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OPEN_ALWAYS, 0, IntPtr.Zero);
if (hFile != (IntPtr)(-1))
{
NativeMethods.SetFilePointer(hFile, 0, IntPtr.Zero, 2); // FILE_END
NativeMethods.WriteFile(hFile, bytes, (uint)bytes.Length, out _, IntPtr.Zero);
NativeMethods.CloseHandle(hFile);
}
}
catch { }
}
public static PeImageLoader CurrentLoader;
public static IntPtr CurrentEntryPoint;
public static GameThreadWrapperDelegate WrapperDelegate = new GameThreadWrapperDelegate(GameThreadWrapper); // ДОБАВЛЕНО
static readonly string CheckpointFile = "xbemu_checkpoint.bin";
static readonly string LogFile = $"xbemu_log_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
public static bool IsHeadless = false;
public static bool IsNoRender = false;
public static bool IsDebug = false;
public static string GamePath = "";
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void GameThreadWrapperDelegate(IntPtr param);
[Obsolete]
static void Main(string[] args)
{
SetupTraceListener();
UpdateTitle("Initializing...");
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
var ex = (Exception)e.ExceptionObject;
Trace.WriteLine($"[UNHANDLED] {ex.Message}\n{ex.StackTrace}");
};
try
{
Trace.WriteLine("[SYS] 1. Parsing arguments...");
ParseArgsRecursively(args);
AutoLoadState();
Trace.WriteLine("[SYS] 2. Initializing COM (MTA)...");
NativeMethods.CoInitializeEx(IntPtr.Zero, NativeMethods.COINIT.MULTITHREADED);
Trace.WriteLine("[SYS] 3. Initializing VEH...");
Diagnostics.InitVEH();
Trace.WriteLine("[SYS] 4. Initializing MemoryManager...");
MemoryManager.Init();
Trace.WriteLine("[SYS] 5. Initializing XboxExports...");
XboxExports.InitWinRTEvents();
Trace.WriteLine("[SYS] 6. Initializing StubManager...");
StubManager.Init();
Trace.WriteLine("[SYS] 7. Skipping RenderManager for debug...");
Trace.WriteLine("[SYS] 8. Setting up checkpoint timer...");
var checkpointTimer = new Timer(state => AutoSaveState(), null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
Trace.WriteLine("[SYS] 9. Checking game path...");
if (!string.IsNullOrEmpty(GamePath) && File.Exists(GamePath))
{
UpdateTitle("Loading PE...");
Trace.WriteLine("[SYS] 10. Loading PE Image...");
PeImageLoader loader = new PeImageLoader();
IntPtr entryPoint = loader.Load(GamePath);
if (entryPoint != IntPtr.Zero)
{
UpdateTitle("Running...");
Trace.WriteLine($"[SYS] 11. Entry Point found at: 0x{entryPoint.ToInt64():X}");
Trace.WriteLine("[SYS] 12. Creating game thread...");
CurrentLoader = loader;
CurrentEntryPoint = entryPoint;
// Увеличиваем стек до 32 МБ
IntPtr hThread = NativeMethods.CreateThread(IntPtr.Zero, 32 * 1024 * 1024, Marshal.GetFunctionPointerForDelegate(WrapperDelegate), IntPtr.Zero, 0, out uint threadId);
Trace.WriteLine($"[SYS] 13. Game thread created. ID: {threadId}");
if (hThread != IntPtr.Zero)
{
Trace.WriteLine("[SYS] 14. Entering main loop...");
while (NativeMethods.WaitForSingleObject(hThread, 100) == 258)
{
Thread.Sleep(10);
}
uint exitCode = 0;
NativeMethods.GetExitCodeThread(hThread, out exitCode);
Trace.WriteLine($"[SYS] 15. Game thread exited with code: 0x{exitCode:X}");
}
}
else
{
Trace.WriteLine("[ERROR] Failed to load PE or find entry point.");
}
}
else
{
Trace.WriteLine("[ERROR] Game path invalid or not provided. Use -p <path>");
PrintHelp();
}
}
catch (Exception ex)
{
Trace.WriteLine($"[FATAL] {ex.Message}\n{ex.StackTrace}");
}
finally
{
AutoSaveState();
UpdateTitle("Shutting down...");
NativeMethods.CoUninitialize();
Console.ResetColor();
Trace.WriteLine("[SYS] Emulator stopped. Press any key to exit.");
if (!IsHeadless) Console.ReadKey();
}
}
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
public static void GameThreadWrapper(IntPtr param)
{
LogDirect("[SYS] 12.5. Game thread started. Initializing TLS...");
try
{
if (CurrentLoader.TlsSize > 0)
{
IntPtr tlsBlock = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)CurrentLoader.TlsSize, 0x3000, 0x04);
if (CurrentLoader.TlsStartAddress != IntPtr.Zero)
{
for (int i = 0; i < CurrentLoader.TlsSize; i++)
Marshal.WriteByte(IntPtr.Add(tlsBlock, i), Marshal.ReadByte(IntPtr.Add(CurrentLoader.TlsStartAddress, i)));
}
NativeMethods.TlsSetValue((int)CurrentLoader.TlsIndex, tlsBlock);
}
foreach (var cb in CurrentLoader.TlsCallbacksList)
{
var cbDel = Marshal.GetDelegateForFunctionPointer<PeImageLoader.TlsCallbackDelegate>(cb);
cbDel(IntPtr.Zero, 1, IntPtr.Zero);
}
LogDirect("[SYS] 12.7. Calling game entry point...");
var entry = Marshal.GetDelegateForFunctionPointer<GameThreadWrapperDelegate>(CurrentEntryPoint);
entry(IntPtr.Zero);
LogDirect("[SYS] 12.8. Game entry point returned.");
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.WriteLine($"\n[FATAL] Game thread crashed: {ex.GetType().Name}");
Console.Error.WriteLine($"[FATAL] Message: {ex.Message}");
Console.Error.WriteLine($"[FATAL] Stack: {ex.StackTrace}");
Console.ResetColor();
LogDirect($"[FATAL] Game thread crashed: {ex.GetType().Name}\nMessage: {ex.Message}\nStack: {ex.StackTrace}");
}
}
#region Arguments and TUI
static void ParseArgsRecursively(string[] args, int index = 0)
{
if (index >= args.Length) return;
string arg = args[index].ToLowerInvariant();
switch (arg)
{
case "-p":
case "--path":
if (index + 1 < args.Length) GamePath = args[index + 1];
ParseArgsRecursively(args, index + 2);
return;
case "--headless":
IsHeadless = true; break;
case "--no-render":
IsNoRender = true; break;
case "--debug":
IsDebug = true; break;
case "--help":
PrintHelp(); Environment.Exit(0); break;
}
ParseArgsRecursively(args, index + 1);
}
static void PrintHelp()
{
Console.WriteLine("XBEmu180626_2 - Xbox One (Durango) High-Level Emulator");
Console.WriteLine("Usage: XBEmu180626_2 -p <path_to_game.exe> [options]");
Console.WriteLine("Options:");
Console.WriteLine(" -p, --path Path to Xbox One x64 PE executable.");
Console.WriteLine(" --headless Run without interactive console UI (CI/CD).");
Console.WriteLine(" --no-render Disable D3D11 window rendering (headless graphics).");
Console.WriteLine(" --debug Enable verbose diagnostic logging and VEH.");
}
public static void DrawProgressBar(int progress, string label)
{
if (IsHeadless) return;
Console.CursorLeft = 0;
Console.Write("[");
int pads = 30;
int filled = (progress * pads) / 100;
Console.Write(new string('#', filled) + new string('-', pads - filled));
Console.Write($"] {progress}% {label}");
}
#endregion
#region State Management
static void AutoSaveState()
{
try
{
using (var fs = new FileStream(CheckpointFile, FileMode.Create))
using (var sw = new StreamWriter(fs))
{
sw.WriteLine($"# XBEmu State Checkpoint - {DateTime.Now}");
sw.WriteLine($"GamePath={GamePath}");
}
}
catch { }
}
static void AutoLoadState()
{
if (!File.Exists(CheckpointFile)) return;
try
{
foreach (var line in File.ReadAllLines(CheckpointFile))
{
if (line.StartsWith("GamePath=") && string.IsNullOrEmpty(GamePath))
GamePath = line.Substring(9);
}
}
catch { }
}
static void UpdateTitle(string status) => Console.Title = $"XBEmu180626_2 | {status} | {(IsDebug ? "Debug" : "Release")}";
static void SetupTraceListener()
{
Trace.Listeners.Clear();
Trace.Listeners.Add(new EmulatorTraceListener(LogFile));
Trace.AutoFlush = true;
}
#endregion
}
public class EmulatorTraceListener : TraceListener
{
private StreamWriter _logFile;
private readonly object _lock = new object();
public EmulatorTraceListener(string logPath) : base()
{
_logFile = new StreamWriter(logPath, true) { AutoFlush = true };
}
private void WriteColored(string message)
{
string fullMsg = $"[{DateTime.Now:HH:mm:ss.fff}] {message}";
ConsoleColor color = ConsoleColor.Gray;
if (message.Contains("[ERROR]") || message.Contains("[FATAL]") || message.Contains("[VEH]")) color = ConsoleColor.Red;
else if (message.Contains("[WARN]")) color = ConsoleColor.Yellow;
else if (message.Contains("[GPU]")) color = ConsoleColor.Green;
else if (message.Contains("[PE]")) color = ConsoleColor.Cyan;
else if (message.Contains("[MEM]")) color = ConsoleColor.Magenta;
else if (message.Contains("[HLE]")) color = ConsoleColor.Blue;
lock (_lock)
{
try
{
var prevColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.Write(fullMsg);
Console.ForegroundColor = prevColor;
_logFile.Write(fullMsg);
_logFile.Flush();
}
catch { }
}
}
public override void Write(string message) => WriteColored(message);
public override void WriteLine(string message) => WriteColored(message + Environment.NewLine);
protected override void Dispose(bool disposing)
{
if (disposing) _logFile?.Dispose();
base.Dispose(disposing);
}
}
public static class MemoryManager
{
public static IntPtr GuestBase = IntPtr.Zero;
public static IntPtr EsramBase = IntPtr.Zero;
public const ulong GUEST_MEMORY_SIZE = 6442450944; // 6 GB
public const ulong ESRAM_SIZE = 33554432; // 32 MB
public static void Init()
{
Trace.WriteLine("[MEM] Reserving 6GB Guest Memory (Lazy Commit)...");
ulong[] prefBases = { 0x14000000000, 0x100000000, 0x200000000, 0x300000000 };
foreach (var baseAddr in prefBases)
{
// 0x2000 = MEM_RESERVE. Мы только резервируем адресное пространство!
GuestBase = NativeMethods.VirtualAlloc((IntPtr)baseAddr, (IntPtr)GUEST_MEMORY_SIZE, 0x2000, 0x40);
if (GuestBase != IntPtr.Zero) break;
}
if (GuestBase == IntPtr.Zero)
GuestBase = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)GUEST_MEMORY_SIZE, 0x2000, 0x40);
if (GuestBase == IntPtr.Zero)
throw new Exception("Failed to reserve 6GB Guest Memory.");
Trace.WriteLine($"[MEM] Guest Base: 0x{GuestBase.ToInt64():X}");
EsramBase = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)ESRAM_SIZE, 0x2000, 0x40);
Trace.WriteLine($"[MEM] ESRAM Base: 0x{EsramBase.ToInt64():X}");
}
public static void MapSection(IntPtr dest, byte[] data)
{
// 0x1000 = MEM_COMMIT. Физически выделяем память только для нужных секций!
NativeMethods.VirtualAlloc(dest, (IntPtr)data.Length, 0x1000, 0x40);
Marshal.Copy(data, 0, dest, data.Length);
}
}
public class PeImageLoader
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void TlsCallbackDelegate(IntPtr hModule, uint reason, IntPtr pvReserved);
public IntPtr Load(string path)
{
try
{
Trace.WriteLine($"[PE] Loading PE: {path}");
byte[] fileData = File.ReadAllBytes(path);
Trace.WriteLine($"[PE] File read. Size: {fileData.Length} bytes.");
int dosHeader = BitConverter.ToInt32(fileData, 0x3C);
ushort machine = BitConverter.ToUInt16(fileData, dosHeader + 4);
if (machine != 0x8664) throw new Exception("Not an x64 PE file.");
ulong imageBase = BitConverter.ToUInt64(fileData, dosHeader + 24 + 24);
uint sizeOfImage = BitConverter.ToUInt32(fileData, dosHeader + 24 + 56);
uint entryPointRva = BitConverter.ToUInt32(fileData, dosHeader + 24 + 16);
uint sizeOfHeaders = BitConverter.ToUInt32(fileData, dosHeader + 24 + 60);
Trace.WriteLine($"[PE] ImageBase: 0x{imageBase:X}, SizeOfImage: 0x{sizeOfImage:X}, EntryPointRVA: 0x{entryPointRva:X}");
Trace.WriteLine("[PE] Attempting VirtualAlloc...");
// 0x2000 = MEM_RESERVE
IntPtr imgBase = NativeMethods.VirtualAlloc((IntPtr)imageBase, (IntPtr)sizeOfImage, 0x2000, 0x40);
if (imgBase == IntPtr.Zero)
{
Trace.WriteLine("[PE] Preferred base failed. Allocating dynamically...");
imgBase = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)sizeOfImage, 0x2000, 0x40);
}
if (imgBase == IntPtr.Zero)
throw new Exception($"Failed to allocate memory for PE image. Win32 Error: {Marshal.GetLastWin32Error()}");
Trace.WriteLine($"[PE] Allocated Image Base: 0x{imgBase.ToInt64():X}");
ushort numSections = BitConverter.ToUInt16(fileData, dosHeader + 6);
ushort sizeOfOptHeader = BitConverter.ToUInt16(fileData, dosHeader + 20);
Trace.WriteLine("[PE] Mapping headers...");
byte[] headerData = new byte[sizeOfHeaders];
Array.Copy(fileData, 0, headerData, 0, sizeOfHeaders);
MemoryManager.MapSection(imgBase, headerData);
Trace.WriteLine("[PE] Mapping sections...");
int sectionOffset = dosHeader + 24 + sizeOfOptHeader;
for (int i = 0; i < numSections; i++)
{
int secOff = sectionOffset + (i * 40);
string name = Encoding.ASCII.GetString(fileData, secOff, 8).Trim('\0');
uint vAddr = BitConverter.ToUInt32(fileData, secOff + 12);
uint vSize = BitConverter.ToUInt32(fileData, secOff + 8);
uint rOff = BitConverter.ToUInt32(fileData, secOff + 20);
uint rSize = BitConverter.ToUInt32(fileData, secOff + 16);
if (vSize > 0)
{
// КРИТИЧЕСКИ ВАЖНО: Выделяем полный VirtualSize и заполняем нулями (для .bss)
int copySize = (int)Math.Min(rSize, vSize);
byte[] secData = new byte[vSize];
if (rSize > 0) Array.Copy(fileData, rOff, secData, 0, copySize);
IntPtr dest = IntPtr.Add(imgBase, (int)vAddr);
Trace.WriteLine($"[PE] Mapping {name} to 0x{dest.ToInt64():X} (VSize: 0x{vSize:X}, CSize: 0x{copySize:X})...");
MemoryManager.MapSection(dest, secData);
}
}
if ((ulong)imgBase.ToInt64() != imageBase)
{
Trace.WriteLine("[PE] Applying relocations...");
ApplyRelocations(fileData, dosHeader, imgBase, (IntPtr)imageBase);
}
// CRITICAL FIX: Process Imports (IAT)
ProcessImports(fileData, dosHeader, imgBase);
int dataDirOffset = dosHeader + 24 + 112;
uint exceptionRva = BitConverter.ToUInt32(fileData, dataDirOffset + (3 * 8));
uint exceptionSize = BitConverter.ToUInt32(fileData, dataDirOffset + (3 * 8) + 4);
if (exceptionSize > 0 && exceptionRva > 0)
{
IntPtr pdataPtr = IntPtr.Add(imgBase, (int)exceptionRva);
uint count = exceptionSize / 12;
NativeMethods.RtlAddFunctionTable(pdataPtr, count, imgBase);
Trace.WriteLine($"[PE] Registered {count} exception handlers.");
}
// Обработка TLS
ProcessTlsCallbacks(fileData, dosHeader, imgBase);
Trace.WriteLine("[PE] Scanning for INT3...");
PatchAntiDebug(imgBase, sizeOfImage);
return IntPtr.Add(imgBase, (int)entryPointRva);
}
catch (Exception ex)
{
Trace.WriteLine($"[PE] FATAL Load Error: {ex.Message}\n{ex.StackTrace}");
return IntPtr.Zero;
}
}
// CRITICAL FIX: Сохраняем TLS данные, чтобы настроить поток перед запуском
public uint TlsIndex = 0;
public IntPtr TlsIndexPtr = IntPtr.Zero;
public IntPtr TlsStartAddress = IntPtr.Zero;
public IntPtr TlsEndAddress = IntPtr.Zero;
public uint TlsSize = 0;
public List<IntPtr> TlsCallbacksList = new List<IntPtr>();
private void ProcessTlsCallbacks(byte[] fileData, int dosHeader, IntPtr imgBase)
{
try
{
int dataDirOffset = dosHeader + 24 + 112;
uint tlsRva = BitConverter.ToUInt32(fileData, dataDirOffset + (9 * 8));
if (tlsRva != 0)
{
Trace.WriteLine($"[PE] Found TLS Directory at RVA: 0x{tlsRva:X}");
IntPtr tlsDirPtr = IntPtr.Add(imgBase, (int)tlsRva);
TlsStartAddress = Marshal.ReadIntPtr(tlsDirPtr, 0);
TlsEndAddress = Marshal.ReadIntPtr(tlsDirPtr, 8);
TlsIndexPtr = Marshal.ReadIntPtr(tlsDirPtr, 16);
IntPtr callbacksPtr = Marshal.ReadIntPtr(tlsDirPtr, 24);
if (TlsIndexPtr != IntPtr.Zero)
{
TlsIndex = (uint)Marshal.ReadInt32(TlsIndexPtr);
if (TlsIndex == 0)
{
TlsIndex = (uint)NativeMethods.TlsAlloc();
Marshal.WriteInt32(TlsIndexPtr, (int)TlsIndex);
Trace.WriteLine($"[PE] Allocated TLS Slot: {TlsIndex}");
}
}
TlsSize = (uint)(TlsEndAddress.ToInt64() - TlsStartAddress.ToInt64());
TlsSize = (TlsSize + 15) & ~15u; // Выравнивание
if (callbacksPtr != IntPtr.Zero)
{
int i = 0;
while (true)
{
IntPtr callback = Marshal.ReadIntPtr(callbacksPtr, i * IntPtr.Size);
if (callback == IntPtr.Zero) break;
TlsCallbacksList.Add(callback);
i++;
}
}
}
}
catch (Exception ex)
{
Trace.WriteLine($"[PE] TLS Callback processing failed: {ex.Message}");
}
}
private void ProcessImports(byte[] fileData, int dosHeader, IntPtr imgBase)
{
Trace.WriteLine("[PE] Processing Imports (IAT)...");
int dataDirOffset = dosHeader + 24 + 112;
uint importRva = BitConverter.ToUInt32(fileData, dataDirOffset + (1 * 8)); // Import Directory is index 1
if (importRva == 0) return;
int importOffset = RvaToOffset(fileData, dosHeader, importRva);
int descriptorSize = 20;
int dllCount = 0;
while (true)
{
int descOffset = importOffset + (dllCount * descriptorSize);
if (descOffset + 20 > fileData.Length) break;
uint nameRva = BitConverter.ToUInt32(fileData, descOffset + 12);
if (nameRva == 0) break; // End of descriptors
// Read DLL Name
int nameOff = RvaToOffset(fileData, dosHeader, nameRva);
string dllName = "";
while (fileData[nameOff] != 0) { dllName += (char)fileData[nameOff]; nameOff++; }
uint originalFirstThunkRva = BitConverter.ToUInt32(fileData, descOffset);
uint firstThunkRva = BitConverter.ToUInt32(fileData, descOffset + 16);
// Use OriginalFirstThunk for reading names, FirstThunk for patching
uint readThunkRva = originalFirstThunkRva != 0 ? originalFirstThunkRva : firstThunkRva;
int readThunkOffset = RvaToOffset(fileData, dosHeader, readThunkRva);
IntPtr iatPtr = IntPtr.Add(imgBase, (int)firstThunkRva);
int thunkIndex = 0;
int funcCount = 0;
while (true)
{
if (readThunkOffset + (thunkIndex * 8) + 8 > fileData.Length) break;
ulong thunk = (ulong)BitConverter.ToInt64(fileData, readThunkOffset + (thunkIndex * 8));
if (thunk == 0) break;
string funcName = "";
if ((thunk & 0x8000000000000000UL) == 0) // Import by name
{
int nameRva2 = (int)thunk;
int nameOffset2 = RvaToOffset(fileData, dosHeader, (uint)nameRva2) + 2; // Skip 2-byte Hint
while (fileData[nameOffset2] != 0) { funcName += (char)fileData[nameOffset2]; nameOffset2++; }
}
else
{
funcName = $"Ordinal_{thunk & 0xFFFF}";
}
IntPtr stubPtr = StubManager.GetStub(dllName, funcName);
Marshal.WriteIntPtr(IntPtr.Add(iatPtr, thunkIndex * 8), stubPtr);
thunkIndex++;
funcCount++;
}
Trace.WriteLine($"[PE] Patched {funcCount} imports from {dllName}");
dllCount++;
}
Trace.WriteLine("[PE] IAT Patching complete.");
}
private int RvaToOffset(byte[] fileData, int dosHeader, uint rva)
{
ushort numSections = BitConverter.ToUInt16(fileData, dosHeader + 6);
ushort sizeOfOptHeader = BitConverter.ToUInt16(fileData, dosHeader + 20);
int sectionOffset = dosHeader + 24 + sizeOfOptHeader;
for (int i = 0; i < numSections; i++)
{
int secOff = sectionOffset + (i * 40);
uint vAddr = BitConverter.ToUInt32(fileData, secOff + 12);
uint vSize = BitConverter.ToUInt32(fileData, secOff + 8);
uint rOff = BitConverter.ToUInt32(fileData, secOff + 20);
uint rSize = BitConverter.ToUInt32(fileData, secOff + 16);
if (rva >= vAddr && rva < vAddr + Math.Max(vSize, rSize))
{
return (int)(rva - vAddr + rOff);
}
}
return (int)rva; // Fallback (usually headers)
}
private void ApplyRelocations(byte[] fileData, int dosHeader, IntPtr actualBase, IntPtr preferredBase)
{
int dataDirOffset = dosHeader + 24 + 112;
uint relocRva = BitConverter.ToUInt32(fileData, dataDirOffset + (5 * 8));
if (relocRva == 0) return;
long delta = actualBase.ToInt64() - preferredBase.ToInt64();
if (delta == 0) return;
int relocOffset = RvaToOffset(fileData, dosHeader, relocRva);
while (relocOffset < fileData.Length)
{
uint pageRva = BitConverter.ToUInt32(fileData, relocOffset);
uint blockSize = BitConverter.ToUInt32(fileData, relocOffset + 4);
if (blockSize == 0) break;
int entryCount = (int)((blockSize - 8) / 2);
for (int i = 0; i < entryCount; i++)
{
ushort entry = BitConverter.ToUInt16(fileData, relocOffset + 8 + (i * 2));
int type = entry >> 12;
int offset = entry & 0xFFF;
if (type == 10)
{
IntPtr patchAddr = IntPtr.Add(actualBase, (int)pageRva + offset);
long val = Marshal.ReadInt64(patchAddr);
Marshal.WriteInt64(patchAddr, val + delta);
}
}
relocOffset += (int)blockSize;
}
}
private unsafe void PatchAntiDebug(IntPtr imgBase, uint sizeOfImage)
{
// ПОЛНОСТЬЮ ОТКЛЮЧАЕМ. Слепой патчинг ломает инструкции игры.
return;
}
}
public static class Diagnostics
{
public static VectoredHandler VehHandlerDelegate = null!;
private static GCHandle _vehHandle;
public static void InitVEH()
{
VehHandlerDelegate = new VectoredHandler(VehHandler);
_vehHandle = GCHandle.Alloc(VehHandlerDelegate);
Trace.WriteLine("[DIAG] Vectored Exception Handler attached.");
NativeMethods.AddVectoredExceptionHandler(1, VehHandlerDelegate);
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int VectoredHandler(IntPtr exceptionInfo);
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
public static int VehHandler(IntPtr exceptionInfo)
{
try
{
IntPtr exRecord = Marshal.ReadIntPtr(exceptionInfo);
uint code = (uint)Marshal.ReadInt32(exRecord);
IntPtr addr = Marshal.ReadIntPtr(exRecord, 0x10); // ExceptionAddress на x64
// ИСПРАВЛЕНО: Явное приведение типов для STATUS_BREAKPOINT (0x80000003)
if (code == 0x80000003)
{
IntPtr ctxRecord = Marshal.ReadIntPtr(exceptionInfo, 8); // PCONTEXT
IntPtr ripPtr = IntPtr.Add(ctxRecord, 0xF8); // Смещение RIP в CONTEXT для x64
long rip = Marshal.ReadInt64(ripPtr);
Marshal.WriteInt64(ripPtr, rip + 1); // Сдвигаем указатель на 1 байт (размер int 3)
// Читаем 16 байт вокруг точки останова для отладки
byte[] bytes = new byte[16];
Marshal.Copy(addr, bytes, 0, 16);
string hex = BitConverter.ToString(bytes);
string msg = $"[VEH] Skipped BREAKPOINT at 0x{addr.ToInt64():X} -> 0x{rip + 1:X} | Bytes: {hex}\r\n";
byte[] msgBytes = Encoding.ASCII.GetBytes(msg);
IntPtr hFile = NativeMethods.CreateFile("C:\\xbemu_veh.log", NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OPEN_ALWAYS, 0, IntPtr.Zero);
if (hFile != (IntPtr)(-1))
{
NativeMethods.SetFilePointer(hFile, 0, IntPtr.Zero, 2);
NativeMethods.WriteFile(hFile, msgBytes, (uint)msgBytes.Length, out _, IntPtr.Zero);
NativeMethods.CloseHandle(hFile);
}
Trace.WriteLine(msg);
return -1; // EXCEPTION_CONTINUE_EXECUTION
}
string msg2 = $"[VEH] EXCEPTION 0x{code:X8} at 0x{addr.ToInt64():X}\r\n";
byte[] bytes2 = Encoding.ASCII.GetBytes(msg2);
IntPtr hFile2 = NativeMethods.CreateFile("C:\\xbemu_veh.log", NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OPEN_ALWAYS, 0, IntPtr.Zero);
if (hFile2 != (IntPtr)(-1))
{
NativeMethods.SetFilePointer(hFile2, 0, IntPtr.Zero, 2);
NativeMethods.WriteFile(hFile2, bytes2, (uint)bytes2.Length, out _, IntPtr.Zero);
NativeMethods.CloseHandle(hFile2);
}
Trace.WriteLine(msg2);
}
catch { }
return 0; // EXCEPTION_CONTINUE_SEARCH
}
}
public static class StubManager
{
public delegate int RaiseFailFastExceptionDelegate(IntPtr ex, IntPtr ctx, uint flags);
public delegate IntPtr SetUnhandledExceptionFilterDelegate(IntPtr filter);
static int RaiseFailFastExceptionImpl(IntPtr ex, IntPtr ctx, uint flags) => throw new Exception("[HLE] RaiseFailFastException API called!");
static IntPtr SetUnhandledExceptionFilterImpl(IntPtr filter) => IntPtr.Zero;
public delegate void TerminateProcessDelegate(IntPtr hProcess, uint exitCode);
public delegate void ExitProcessDelegate(uint exitCode);
static void TerminateProcessImpl(IntPtr hProcess, uint exitCode) => throw new Exception($"[HLE] TerminateProcess called! Code: 0x{exitCode:X}");
static void ExitProcessImpl(uint exitCode) => throw new Exception($"[HLE] ExitProcess called! Code: 0x{exitCode:X}");
public delegate int MapTitlePhysicalPagesDelegate(IntPtr process, IntPtr section, uint type, ref uint count, out IntPtr pages);
public delegate IntPtr ApuCreateHeapDelegate();
public delegate IntPtr ApuAllocDelegate(IntPtr heap, uint size);
public delegate IntPtr AcpHalCreateDelegate();
static int MapTitlePhysicalPagesImpl(IntPtr process, IntPtr section, uint type, ref uint count, out IntPtr pages)
{
Console.WriteLine($"[HLE] MapTitlePhysicalPages called. Allocating {count} pages.");
pages = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)(count * 4096), 0x3000, 0x04);
return 1;
}
static IntPtr ApuCreateHeapImpl() => new IntPtr(1);
static IntPtr ApuAllocImpl(IntPtr heap, uint size) => NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)size, 0x3000, 0x04);
static IntPtr AcpHalCreateImpl() => new IntPtr(1);
static IntPtr AcpHalAllocateShapeContextsImpl() => new IntPtr(1);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr HeapAllocateDelegate(ulong size);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void HeapFreeDelegate(IntPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int AllocateTitlePhysicalPagesDelegate(IntPtr process, uint type, ref uint count, out IntPtr pages);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void FailFastDelegate();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void TerminateDelegate();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void AmsgExitDelegate(int code);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void SetAppTypeDelegate(int type);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void LockDelegate(int locknum);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void TypeDtorDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void ThrowStdExceptionDelegate(IntPtr msg);
static IntPtr HeapAllocateImpl(ulong size) => NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)size, 0x3000, 0x04);
static void HeapFreeImpl(IntPtr ptr) { }
static int AllocateTitlePhysicalPagesImpl(IntPtr process, uint type, ref uint count, out IntPtr pages)
{
Trace.WriteLine($"[HLE] AllocateTitlePhysicalPages called. Allocating {count} pages.");
pages = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)(count * 4096), 0x3000, 0x04);
return 1; // TRUE
}
static void FailFastImpl() => throw new Exception("[HLE] Platform::__abi_FailFast called!");
static void TerminateImpl() => throw new Exception("[HLE] std::terminate() called!");
static void AmsgExitImpl(int code) => throw new Exception($"[HLE] _amsg_exit({code}) called!");
static void SetAppTypeImpl(int type) { }
static void LockImpl(int locknum) { }
static void UnlockImpl(int locknum) { }
static void TypeDtorImpl(IntPtr thisPtr) { }
static void OrphanAllImpl(IntPtr thisPtr) { }
static void ThrowStdExceptionImpl(IntPtr msg) => throw new Exception("[GAME] std::exception: " + Marshal.PtrToStringAnsi(msg));
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int PrintfDelegate(IntPtr format);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int FprintfDelegate(IntPtr file, IntPtr format);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr IobFuncDelegate();
static IntPtr _acmdlnPtr = IntPtr.Zero;
static IntPtr _fmodePtr = IntPtr.Zero;
static IntPtr _commodePtr = IntPtr.Zero;
static IntPtr _iobPtr = IntPtr.Zero;
static int PrintfImpl(IntPtr format)
{
try { Console.WriteLine("[GAME] " + Marshal.PtrToStringAnsi(format)); } catch { }
return 0;
}
static int FprintfImpl(IntPtr file, IntPtr format)
{
try { Console.WriteLine("[GAME] " + Marshal.PtrToStringAnsi(format)); } catch { }
return 0;
}
static IntPtr IobFuncImpl()
{
if (_iobPtr == IntPtr.Zero) _iobPtr = Marshal.AllocHGlobal(128); // Фейковый массив FILE*
return _iobPtr;
}
// Правильное выделение памяти под данные CRT
public static IntPtr GetDataStub(string name)
{
if (name == "_acmdln" || name == "_wcmdln")
{
if (_acmdlnPtr == IntPtr.Zero)
{
_acmdlnPtr = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(_acmdlnPtr, GetCommandLineA());
}
return _acmdlnPtr;
}
if (name == "_fmode")
{
if (_fmodePtr == IntPtr.Zero) { _fmodePtr = Marshal.AllocHGlobal(4); Marshal.WriteInt32(_fmodePtr, 0); }
return _fmodePtr;
}
if (name == "_commode")
{
if (_commodePtr == IntPtr.Zero) { _commodePtr = Marshal.AllocHGlobal(4); Marshal.WriteInt32(_commodePtr, 0); }
return _commodePtr;
}
// Безопасный фолбэк для неизвестных данных: выдаем указатель на 0
IntPtr genericPtr = Marshal.AllocHGlobal(8);
Marshal.WriteInt64(genericPtr, 0);
return genericPtr;
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr CallocDelegate(IntPtr count, IntPtr size);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetActivationFactoryDelegate(IntPtr classId, out IntPtr factory);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetActivationFactoryByPCWSTRDelegate(IntPtr classId, IntPtr iid, out IntPtr factory); // Было ref Guid
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetIidsFnDelegate(int count, IntPtr sizes, IntPtr guids, out IntPtr iidArray);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetProxyImplDelegate(IntPtr pUnk, ref Guid iid, ref Guid destIid, out IntPtr proxy);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr GetObjectContextDelegate();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int ReCreateFromExceptionDelegate(IntPtr ex);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr GetIBoxArrayVtableDelegate(IntPtr a);
static int GetActivationFactoryByPCWSTRImpl(IntPtr classId, ref Guid iid, out IntPtr factory)
{
Trace.WriteLine("[HLE] GetActivationFactoryByPCWSTR called. Returning fake COM object.");
// ИСПРАВЛЕНО: Вызываем функцию, она сама заполнит 'factory' через out, а мы просто вернем 0 (S_OK)
XboxExports.GetActivationFactoryImpl(classId, out factory);
return 0;
}
static int GetIidsFnImpl(int count, IntPtr sizes, IntPtr guids, out IntPtr iidArray)
{
Trace.WriteLine("[HLE] GetIidsFn called. Returning empty array.");
// ИСПРАВЛЕНО: Возвращаем E_NOTIMPL (0x80004001), чтобы C++/CX не пытался читать пустой массив
iidArray = IntPtr.Zero;
return unchecked((int)0x80004001);
}
static int GetProxyImplImpl(IntPtr pUnk, ref Guid iid, ref Guid destIid, out IntPtr proxy)
{
proxy = IntPtr.Zero;
return 0;
}
static IntPtr GetObjectContextImpl() => IntPtr.Zero;
static int ReCreateFromExceptionImpl(IntPtr ex) => 0;
static IntPtr GetIBoxArrayVtableImpl(IntPtr a) => IntPtr.Zero;
static IntPtr CallocImpl(IntPtr count, IntPtr size)
{
long total = count.ToInt64() * size.ToInt64();
if (total <= 0) return IntPtr.Zero;
return NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)total, 0x3000, 0x04);
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr GetCmdArgsDelegate(out int argc);
static IntPtr _wargvArray = IntPtr.Zero;
static IntPtr GetCmdArgsImpl(out int argc)
{
Trace.WriteLine("[HLE] Platform::GetCmdArguments called.");
if (_wargvArray == IntPtr.Zero)
{
IntPtr wargv0 = Marshal.StringToHGlobalUni("Emu.exe");
_wargvArray = Marshal.AllocHGlobal(16);
Marshal.WriteIntPtr(_wargvArray, wargv0);
Marshal.WriteIntPtr(_wargvArray, 8, IntPtr.Zero);
}
argc = 1;
return _wargvArray;
}
// CRITICAL FIX: Список для удержания делегатов в памяти, чтобы GC их не удалил
private static List<Delegate> _aliveDelegates = new List<Delegate>();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr NoArgDelegate();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr Arg1Delegate(IntPtr a);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr Arg4Delegate(IntPtr a, IntPtr b, IntPtr c, IntPtr d);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void InitTermDelegate(IntPtr start, IntPtr end);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetMainArgsDelegate(ref int argc, ref IntPtr argv, ref IntPtr envp, int dowildcard, IntPtr startinfo);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int InitTermEFuncDelegate();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int InitTermEDelegate(IntPtr start, IntPtr end);
// Делегаты для CRT
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe delegate IntPtr MemCpyDelegate(IntPtr dest, IntPtr src, IntPtr count);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe delegate IntPtr MemSetDelegate(IntPtr dest, int val, IntPtr count);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr StrLenDelegate(IntPtr str);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr MallocDelegate(IntPtr size);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void FreeDelegate(IntPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr ReturnThisDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void VoidDelegate();
static IntPtr ReturnThisImpl(IntPtr thisPtr) => thisPtr; // Конструктор возвращает указатель на созданный объект
static void VoidImpl() { } // Деструкторы и бросалки исключений ничего не возвращают
static IntPtr ReturnZero() => IntPtr.Zero; // Возвращаем S_OK (0) для неизвестных функций
static IntPtr ReturnOne() => new IntPtr(1);
static IntPtr ReturnMinusOne() => new IntPtr(-1);
static IntPtr ReturnMinusTwo() => new IntPtr(-2);
static IntPtr EncodePointerImpl(IntPtr ptr) => ptr;
static IntPtr DecodePointerImpl(IntPtr ptr) => ptr;
static IntPtr QueryPerformanceCounterImpl(IntPtr ptr)
{
try { Marshal.WriteInt64(ptr, Stopwatch.GetTimestamp()); } catch { }
return IntPtr.Zero;
}
static IntPtr GetSystemTimeAsFileTimeImpl(IntPtr ptr)
{
try { Marshal.WriteInt64(ptr, DateTime.Now.ToFileTime()); } catch { }
return IntPtr.Zero;
}
static IntPtr GetTickCount64Impl() => new IntPtr(Environment.TickCount);
[Obsolete]
static IntPtr GetCurrentThreadIdImpl() => new IntPtr(NativeMethods.GetCurrentThreadId());
static void InitTermImpl(IntPtr start, IntPtr end)
{
Trace.WriteLine("[HLE] _initterm called. Executing CRT initializers...");
while (start.ToInt64() < end.ToInt64())
{
IntPtr funcPtr = Marshal.ReadIntPtr(start);
if (funcPtr != IntPtr.Zero)
{
try
{
var func = Marshal.GetDelegateForFunctionPointer<NoArgDelegate>(funcPtr);
func();
}
catch (Exception ex) { Trace.WriteLine($"[HLE] _initterm func crashed: {ex.Message}"); }
}
start = IntPtr.Add(start, IntPtr.Size);
}
}
static int InitTermEImpl(IntPtr start, IntPtr end)
{
Trace.WriteLine("[HLE] _initterm_e called. Executing CRT initializers...");
while (start.ToInt64() < end.ToInt64())
{
IntPtr funcPtr = Marshal.ReadIntPtr(start);
if (funcPtr != IntPtr.Zero)
{
try
{
var func = Marshal.GetDelegateForFunctionPointer<InitTermEFuncDelegate>(funcPtr);
int ret = func();
if (ret != 0)
{
Trace.WriteLine($"[HLE] _initterm_e function returned {ret}, aborting initialization.");
return ret;
}
}
catch (Exception ex) { Trace.WriteLine($"[HLE] _initterm_e func crashed: {ex.Message}"); }
}
start = IntPtr.Add(start, IntPtr.Size);
}
return 0;
}
static int GetMainArgsImpl(ref int argc, ref IntPtr argv, ref IntPtr envp, int dowildcard, IntPtr startinfo)
{
Trace.WriteLine("[HLE] __getmainargs called.");
argc = 1;
IntPtr argvArray = Marshal.AllocHGlobal(16);
Marshal.WriteIntPtr(argvArray, GetCommandLineA());
Marshal.WriteIntPtr(argvArray, 8, IntPtr.Zero);
argv = argvArray;
IntPtr envpArray = Marshal.AllocHGlobal(8);
Marshal.WriteIntPtr(envpArray, IntPtr.Zero);
envp = envpArray;
return 0;
}
static IntPtr _cmdLineWPtr = IntPtr.Zero;
static IntPtr _cmdLineAPtr = IntPtr.Zero;
static IntPtr GetCommandLineW()
{
if (_cmdLineWPtr == IntPtr.Zero)
{
string cmd = "Emu.exe";
byte[] bytes = Encoding.Unicode.GetBytes(cmd + "\0");
_cmdLineWPtr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, _cmdLineWPtr, bytes.Length);
}
return _cmdLineWPtr;
}
static IntPtr GetCommandLineA()
{
if (_cmdLineAPtr == IntPtr.Zero)
{
string cmd = "Emu.exe";
byte[] bytes = Encoding.ASCII.GetBytes(cmd + "\0");
_cmdLineAPtr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, _cmdLineAPtr, bytes.Length);
}
return _cmdLineAPtr;
}
// Реальные реализации CRT
static unsafe IntPtr MemCpyImpl(IntPtr dest, IntPtr src, IntPtr count)
{
if (count == IntPtr.Zero) return dest;
Buffer.MemoryCopy((void*)src, (void*)dest, count.ToInt64(), count.ToInt64());
return dest;
}
static unsafe IntPtr MemSetImpl(IntPtr dest, int val, IntPtr count)
{
if (count == IntPtr.Zero) return dest;
byte* d = (byte*)dest;
byte v = (byte)val;
for (long i = 0; i < count.ToInt64(); i++) d[i] = v;
return dest;
}
static unsafe IntPtr StrLenImpl(IntPtr str)
{
byte* p = (byte*)str;
long len = 0;
while (*p != 0) { p++; len++; }
return new IntPtr(len);
}
static IntPtr MallocImpl(IntPtr size)
{
return NativeMethods.VirtualAlloc(IntPtr.Zero, size, 0x3000, 0x04); // PAGE_READWRITE
}
static void FreeImpl(IntPtr ptr) { /* Игнорируем для утечек памяти в эмуляторе */ }
public static Dictionary<string, IntPtr> KnownStubs = new Dictionary<string, IntPtr>();
public static IntPtr FallbackStub = IntPtr.Zero;
[Obsolete]
public static void Init()
{
Trace.WriteLine("[HLE] Initializing Stub Manager...");
FallbackStub = CreateJitStub("Unknown", new NoArgDelegate(ReturnZero));
KnownStubs["memcpy"] = CreateJitStub("memcpy", new MemCpyDelegate(MemCpyImpl));
KnownStubs["memset"] = CreateJitStub("memset", new MemSetDelegate(MemSetImpl));
KnownStubs["memmove"] = CreateJitStub("memmove", new MemCpyDelegate(MemCpyImpl));
KnownStubs["strlen"] = CreateJitStub("strlen", new StrLenDelegate(StrLenImpl));
KnownStubs["malloc"] = CreateJitStub("malloc", new MallocDelegate(MallocImpl));
KnownStubs["free"] = CreateJitStub("free", new FreeDelegate(FreeImpl));
// КРИТИЧЕСКИ ВАЖНО: C++ new/delete. Без них игра падает при первом же выделении памяти
KnownStubs["??2@YAPEAX_K@Z"] = CreateJitStub("operator_new", new MallocDelegate(MallocImpl));
KnownStubs["??_U@YAPEAX_K@Z"] = CreateJitStub("operator_new[]", new MallocDelegate(MallocImpl));
KnownStubs["??3@YAXPEAX@Z"] = CreateJitStub("operator_delete", new FreeDelegate(FreeImpl));
KnownStubs["??_V@YAXPEAX@Z"] = CreateJitStub("operator_delete[]", new FreeDelegate(FreeImpl));
KnownStubs["_calloc_crt"] = CreateJitStub("_calloc_crt", new CallocDelegate(CallocImpl));
KnownStubs["RoInitialize"] = CreateJitStub("RoInitialize", new NoArgDelegate(ReturnZero));
KnownStubs["CoTaskMemAlloc"] = CreateJitStub("CoTaskMemAlloc", new MallocDelegate(MallocImpl));
KnownStubs["WindowsCreateString"] = CreateJitStub("WindowsCreateString", new NoArgDelegate(ReturnZero));
// ИСПРАВЛЕНО: Правильная сигнатура для GetActivationFactory
KnownStubs["GetActivationFactory"] = CreateJitStub("GetActivationFactory", new GetActivationFactoryDelegate(XboxExports.GetActivationFactoryImpl));
KnownStubs["?GetActivationFactoryByPCWSTR@@YAJPEAXAEAVGuid@Platform@@PEAPEAX@Z"] = CreateJitStub("GetActivationFactoryByPCWSTR", new GetActivationFactoryByPCWSTRDelegate(XboxExports.GetActivationFactoryByPCWSTRImpl));
KnownStubs["GetCurrentProcess"] = CreateJitStub("GetCurrentProcess", new NoArgDelegate(ReturnMinusOne));
KnownStubs["GetCurrentThread"] = CreateJitStub("GetCurrentThread", new NoArgDelegate(ReturnMinusTwo));
KnownStubs["IsDebuggerPresent"] = CreateJitStub("IsDebuggerPresent", new NoArgDelegate(ReturnZero));
KnownStubs["GetLastError"] = CreateJitStub("GetLastError", new NoArgDelegate(ReturnZero));
KnownStubs["GetTickCount64"] = CreateJitStub("GetTickCount64", new NoArgDelegate(GetTickCount64Impl));
KnownStubs["GetCurrentThreadId"] = CreateJitStub("GetCurrentThreadId", new NoArgDelegate(GetCurrentThreadIdImpl));
KnownStubs["GetCommandLineW"] = CreateJitStub("GetCommandLineW", new NoArgDelegate(GetCommandLineW));
KnownStubs["GetCommandLineA"] = CreateJitStub("GetCommandLineA", new NoArgDelegate(GetCommandLineA));
KnownStubs["EncodePointer"] = CreateJitStub("EncodePointer", new Arg1Delegate(EncodePointerImpl));
KnownStubs["DecodePointer"] = CreateJitStub("DecodePointer", new Arg1Delegate(DecodePointerImpl));
KnownStubs["QueryPerformanceCounter"] = CreateJitStub("QueryPerformanceCounter", new Arg1Delegate(QueryPerformanceCounterImpl));
KnownStubs["GetSystemTimeAsFileTime"] = CreateJitStub("GetSystemTimeAsFileTime", new Arg1Delegate(GetSystemTimeAsFileTimeImpl));
// KnownStubs["VirtualProtect"] = CreateJitStub("VirtualProtect", new Arg4Delegate((a, b, c, d) => ReturnOne()));
KnownStubs["_initterm"] = CreateJitStub("_initterm", new InitTermDelegate(InitTermImpl));
KnownStubs["_initterm_e"] = CreateJitStub("_initterm_e", new InitTermEDelegate(InitTermEImpl));
KnownStubs["__getmainargs"] = CreateJitStub("__getmainargs", new GetMainArgsDelegate(GetMainArgsImpl));
KnownStubs["?GetCmdArguments@Details@Platform@@YAPEAPEA_WPEAH@Z"] = CreateJitStub("GetCmdArguments", new GetCmdArgsDelegate(GetCmdArgsImpl));
// Регистрируем новые HLE-заглушки
KnownStubs["?GetIidsFn@@YAJHPEAKPEBU__s_GUID@@PEAPEAVGuid@Platform@@@Z"] = CreateJitStub("GetIidsFn", new GetIidsFnDelegate(GetIidsFnImpl));
KnownStubs["?GetProxyImpl@Details@Platform@@YAJPEAUIUnknown@@AEBU_GUID@@0PEAPEAU3@@Z"] = CreateJitStub("GetProxyImpl", new GetProxyImplDelegate(GetProxyImplImpl));
KnownStubs["?GetObjectContext@Details@Platform@@YAPEAUIUnknown@@XZ"] = CreateJitStub("GetObjectContext", new GetObjectContextDelegate(GetObjectContextImpl));
KnownStubs["?ReCreateFromException@Details@Platform@@YAJPE$AAVException@2@@Z"] = CreateJitStub("ReCreateFromException", new ReCreateFromExceptionDelegate(ReCreateFromExceptionImpl));
KnownStubs["?GetIBoxArrayVtable@Details@Platform@@YAPEAXPEAX@Z"] = CreateJitStub("GetIBoxArrayVtable", new GetIBoxArrayVtableDelegate(GetIBoxArrayVtableImpl));
// Xbox One memory API
KnownStubs["AllocateTitlePhysicalPages"] = CreateJitStub("AllocateTitlePhysicalPages", new AllocateTitlePhysicalPagesDelegate(AllocateTitlePhysicalPagesImpl));
KnownStubs["?Allocate@Heap@Details@Platform@@SAPEAX_K@Z"] = CreateJitStub("Heap_Allocate", new HeapAllocateDelegate(HeapAllocateImpl));
KnownStubs["?AllocateException@Heap@Details@Platform@@SAPEAX_K@Z"] = CreateJitStub("Heap_AllocateException", new HeapAllocateDelegate(HeapAllocateImpl));
KnownStubs["?Free@Heap@Details@Platform@@SAXPEAX@Z"] = CreateJitStub("Heap_Free", new HeapFreeDelegate(HeapFreeImpl));
KnownStubs["?FreeException@Heap@Details@Platform@@SAXPEAX@Z"] = CreateJitStub("Heap_FreeException", new HeapFreeDelegate(HeapFreeImpl));
// Перехват критических ошибок CRT
KnownStubs["?__abi_FailFast@@YAXXZ"] = CreateJitStub("FailFast", new FailFastDelegate(FailFastImpl));
KnownStubs["?terminate@@YAXXZ"] = CreateJitStub("terminate", new TerminateDelegate(TerminateImpl));
KnownStubs["_amsg_exit"] = CreateJitStub("_amsg_exit", new AmsgExitDelegate(AmsgExitImpl));
KnownStubs["__set_app_type"] = CreateJitStub("__set_app_type", new SetAppTypeDelegate(SetAppTypeImpl));
KnownStubs["_lock"] = CreateJitStub("_lock", new LockDelegate(LockImpl));
KnownStubs["_unlock"] = CreateJitStub("_unlock", new LockDelegate(UnlockImpl));
KnownStubs["?_type_info_dtor_internal_method@type_info@@QEAAXXZ"] = CreateJitStub("type_info_dtor", new TypeDtorDelegate(TypeDtorImpl));
KnownStubs["?_Orphan_all@_Container_base0@std@@QEAAXXZ"] = CreateJitStub("Orphan_all", new TypeDtorDelegate(OrphanAllImpl));
KnownStubs["?_Xlength_error@std@@YAXPEBD@Z"] = CreateJitStub("Xlength_error", new ThrowStdExceptionDelegate(ThrowStdExceptionImpl));
KnownStubs["?_Xout_of_range@std@@YAXPEBD@Z"] = CreateJitStub("Xout_of_range", new ThrowStdExceptionDelegate(ThrowStdExceptionImpl));
KnownStubs["?_Xbad_alloc@std@@YAXXZ"] = CreateJitStub("Xbad_alloc", new FailFastDelegate(FailFastImpl));
// Xbox One memory & audio API
KnownStubs["MapTitlePhysicalPages"] = CreateJitStub("MapTitlePhysicalPages", new MapTitlePhysicalPagesDelegate(MapTitlePhysicalPagesImpl));
KnownStubs["ApuCreateHeap"] = CreateJitStub("ApuCreateHeap", new ApuCreateHeapDelegate(ApuCreateHeapImpl));
KnownStubs["ApuAlloc"] = CreateJitStub("ApuAlloc", new ApuAllocDelegate(ApuAllocImpl));
KnownStubs["AcpHalAllocateShapeContexts"] = CreateJitStub("AcpHalAllocateShapeContexts", new ApuCreateHeapDelegate(AcpHalAllocateShapeContextsImpl));
KnownStubs["AcpHalCreate"] = CreateJitStub("AcpHalCreate", new AcpHalCreateDelegate(AcpHalCreateImpl));
// Перехват убийства процесса
KnownStubs["TerminateProcess"] = CreateJitStub("TerminateProcess", new TerminateProcessDelegate(TerminateProcessImpl));
KnownStubs["ExitProcess"] = CreateJitStub("ExitProcess", new ExitProcessDelegate(ExitProcessImpl));
KnownStubs["RaiseFailFastException"] = CreateJitStub("RaiseFailFastException", new RaiseFailFastExceptionDelegate(RaiseFailFastExceptionImpl));
KnownStubs["SetUnhandledExceptionFilter"] = CreateJitStub("SetUnhandledExceptionFilter", new SetUnhandledExceptionFilterDelegate(SetUnhandledExceptionFilterImpl));
KnownStubs["printf"] = CreateJitStub("printf", new PrintfDelegate(PrintfImpl));
KnownStubs["fprintf"] = CreateJitStub("fprintf", new FprintfDelegate(FprintfImpl));
KnownStubs["__iob_func"] = CreateJitStub("__iob_func", new IobFuncDelegate(IobFuncImpl));
// Известные переменные CRT
KnownStubs["_acmdln"] = GetDataStub("_acmdln");
KnownStubs["_wcmdln"] = GetDataStub("_wcmdln");
KnownStubs["_fmode"] = GetDataStub("_fmode");
KnownStubs["_commode"] = GetDataStub("_commode");
// ИСПРАВЛЕНО: Явно возвращаем S_OK для инициализации C++/CX
KnownStubs["?InitializeData@Details@Platform@@YAJH@Z"] = CreateJitStub("InitializeData", new NoArgDelegate(ReturnZero));
KnownStubs["?UninitializeData@Details@Platform@@YAXH@Z"] = CreateJitStub("UninitializeData", new NoArgDelegate(ReturnZero));
}
public static IntPtr GetStub(string dllName, string funcName)
{
if (KnownStubs.TryGetValue(funcName, out IntPtr stub))
return stub;
IntPtr hModule = IntPtr.Zero;
string normDll = dllName.ToLowerInvariant();
if (normDll == "kernelx.dll" || normDll == "kernelbase.dll")
hModule = NativeMethods.GetModuleHandleA("kernel32.dll");
else if (normDll == "combase.dll")
hModule = NativeMethods.LoadLibraryA("combase.dll");
else if (normDll == "msvcr110.dll" || normDll == "msvcp110.dll")
hModule = NativeMethods.LoadLibraryA("ucrtbase.dll");
if (hModule != IntPtr.Zero)
{
IntPtr proc = NativeMethods.GetProcAddress(hModule, funcName);
if (proc != IntPtr.Zero)
{
KnownStubs[funcName] = proc;
return proc;
}
}
// ИСПРАВЛЕНО: Перехватываем функции вывода игры
if (funcName == "printf" || funcName == "wprintf")
{
var del = new PrintfDelegate(PrintfImpl);
_aliveDelegates.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
KnownStubs[funcName] = ptr;
return ptr;
}
if (funcName == "fprintf" || funcName == "fwprintf")
{
var del = new FprintfDelegate(FprintfImpl);
_aliveDelegates.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
KnownStubs[funcName] = ptr;
return ptr;
}
if (funcName == "__iob_func")
{
var del = new IobFuncDelegate(IobFuncImpl);
_aliveDelegates.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
KnownStubs[funcName] = ptr;
return ptr;
}
// ИСПРАВЛЕНО: Известные переменные CRT
if (funcName == "_acmdln" || funcName == "_wcmdln" || funcName == "_fmode" || funcName == "_commode" || funcName == "_pctype")
{
IntPtr dataPtr = GetDataStub(funcName);
KnownStubs[funcName] = dataPtr;
return dataPtr;
}
// ИСПРАВЛЕНИЕ: Автоматическая заглушка для конструкторов C++ (??0). Возвращает thisPtr (RCX)
if (funcName.StartsWith("??0"))
{
var del = new ReturnThisDelegate(ReturnThisImpl);
_aliveDelegates.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
KnownStubs[funcName] = ptr;
Trace.WriteLine($"[HLE] Auto-stubbed C++ constructor: {funcName}");
return ptr;
}
// ИСПРАВЛЕНИЕ: Автоматическая заглушка для деструкторов (ничего не делают)
if (funcName.StartsWith("??1"))
{
var del = new VoidDelegate(VoidImpl);
_aliveDelegates.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
KnownStubs[funcName] = ptr;
Trace.WriteLine($"[HLE] Auto-stubbed C++ destructor: {funcName}");
return ptr;
}
// ИСПРАВЛЕНИЕ: Raisers должны бросать исключения, чтобы игра не шла дальше
if (funcName.Contains("__abi_WinRTraise"))
{
var del = new VoidDelegate(ThrowWinRTExceptionImpl);
_aliveDelegates.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
KnownStubs[funcName] = ptr;
Trace.WriteLine($"[HLE] Auto-stubbed WinRT raiser: {funcName}");
return ptr;
}
Trace.WriteLine($"[HLE] WARN: Unimplemented import '{funcName}' from '{dllName}'. Using fallback.");
return FallbackStub;
}
static void ThrowWinRTExceptionImpl()
{
// ИСПРАВЛЕНО: Бросаем C# исключение, чтобы безопасно раскрутить стек и завершить поток,
// вместо того чтобы возвращаться в мусорный код (int 3).
throw new Exception("[HLE] WinRT Exception raised by game!");
}
public static IntPtr CreateJitStub(string name, Delegate impl)
{
_aliveDelegates.Add(impl); // CRITICAL FIX: Сохраняем ссылку
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(impl);
Trace.WriteLine($"[HLE] Generated stub for {name} at 0x{ptr.ToInt64():X}");
return ptr;
}
}
public static class RenderManager
{
static Thread renderThread = null!;
static volatile bool isRunning = false;
public static void InitWindow()
{
renderThread = new Thread(() =>
{
isRunning = true;
while (isRunning) Thread.Sleep(16);
});
renderThread.IsBackground = true;
renderThread.Start();
}
public static void RenderFrame()
{
int tick = Environment.TickCount;
if (Program.IsHeadless) return;
Program.DrawProgressBar((int)(50 + 50 * Math.Sin(tick * 0.005)), "Pulsing Xbox Green Frame...");
}
}
public static class XboxExports
{
public static void InitWinRTEvents()
{
IntPtr hEvent = NativeMethods.CreateEvent(IntPtr.Zero, true, false, "Global\\RoInitializationCompletedEvent");
if (hEvent != IntPtr.Zero) NativeMethods.SetEvent(hEvent);
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int AddRefDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int ReleaseDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetIidsDelegate(IntPtr thisPtr, out int count, out IntPtr iids);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetRuntimeClassNameDelegate(IntPtr thisPtr, out IntPtr className);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int GetTrustLevelDelegate(IntPtr thisPtr, out int trustLevel);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int ActivateInstanceDelegate(IntPtr thisPtr, out IntPtr instance);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int QueryInterfaceDelegate(IntPtr thisPtr, IntPtr iid, out IntPtr ppv);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int SafeComStubDelegate(IntPtr thisPtr, IntPtr arg1, IntPtr arg2, IntPtr arg3);
static QueryInterfaceDelegate _qi = QueryInterfaceImpl;
static AddRefDelegate _addRef = AddRefImpl;
static ReleaseDelegate _release = ReleaseImpl;
static GetIidsDelegate _getIids = GetIidsImpl;
static GetRuntimeClassNameDelegate _getRuntimeClassName = GetRuntimeClassNameImpl;
static GetTrustLevelDelegate _getTrustLevel = GetTrustLevelImpl;
static ActivateInstanceDelegate _activateInstance = ActivateInstanceImpl;
public static List<Delegate> _comSafeStubs = new List<Delegate>();
private static IntPtr _comVTable = IntPtr.Zero;
private static bool IsWritable(IntPtr address)
{
NativeMethods.MEMORY_BASIC_INFORMATION mbi = new NativeMethods.MEMORY_BASIC_INFORMATION();
if (NativeMethods.VirtualQuery(address, ref mbi, (IntPtr)Marshal.SizeOf(mbi)) != IntPtr.Zero)
{
// PAGE_READWRITE = 0x04, PAGE_EXECUTE_READWRITE = 0x40, PAGE_WRITECOPY = 0x08
return (mbi.Protect & 0x04) != 0 || (mbi.Protect & 0x40) != 0 || (mbi.Protect & 0x08) != 0;
}
return false;
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int VoidHresultDelegate(IntPtr thisPtr);
// ИСПРАВЛЕНО: Правильные сигнатуры COM-методов (возвращают HRESULT, объекты через out)
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int CreateViewDelegate(IntPtr thisPtr, out IntPtr view);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int InitializeDelegate(IntPtr thisPtr, IntPtr applicationView);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int SetWindowDelegate(IntPtr thisPtr, IntPtr window);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int LoadDelegate(IntPtr thisPtr, IntPtr entryPoint);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int RunDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int UninitializeDelegate(IntPtr thisPtr);
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
public static int CoreApplicationRunImpl(IntPtr viewSourcePtr)
{
try
{
if (viewSourcePtr == IntPtr.Zero) return 0;
Console.WriteLine("[HLE] Calling IFrameworkViewSource::CreateView...");
IntPtr vtable = Marshal.ReadIntPtr(viewSourcePtr);
// CreateView - это индекс 6 в VTable
IntPtr createViewPtr = Marshal.ReadIntPtr(vtable, 6 * IntPtr.Size);
var createViewDel = Marshal.GetDelegateForFunctionPointer<CreateViewDelegate>(createViewPtr);
int hr = createViewDel(viewSourcePtr, out IntPtr viewPtr);
Console.WriteLine($"[HLE] CreateView returned HR: 0x{hr:X}, View: 0x{viewPtr.ToInt64():X}");
if (hr == 0 && viewPtr != IntPtr.Zero)
{
IntPtr viewVtable = Marshal.ReadIntPtr(viewPtr);
// 6: Initialize(CoreApplicationView*)
Console.WriteLine("[HLE] Calling IFrameworkView::Initialize...");
IntPtr initializePtr = Marshal.ReadIntPtr(viewVtable, 6 * IntPtr.Size);
var initDel = Marshal.GetDelegateForFunctionPointer<InitializeDelegate>(initializePtr);
initDel(viewPtr, GenerateFakeComInstance(TYPE_CORE_APPLICATION_VIEW)); // ИСПРАВЛЕНО
// 7: SetWindow(CoreWindow*) - передаем фейковый CoreWindow
Console.WriteLine("[HLE] Calling IFrameworkView::SetWindow...");
IntPtr setWindowPtr = Marshal.ReadIntPtr(viewVtable, 7 * IntPtr.Size);
var setWindowDel = Marshal.GetDelegateForFunctionPointer<SetWindowDelegate>(setWindowPtr);
setWindowDel(viewPtr, GenerateFakeComInstance());
// 8: Load(HSTRING)
Console.WriteLine("[HLE] Calling IFrameworkView::Load...");
IntPtr loadPtr = Marshal.ReadIntPtr(viewVtable, 8 * IntPtr.Size);
var loadDel = Marshal.GetDelegateForFunctionPointer<LoadDelegate>(loadPtr);
loadDel(viewPtr, IntPtr.Zero);
// 9: Run()
Console.WriteLine("[HLE] Calling IFrameworkView::Run...");
IntPtr runPtr = Marshal.ReadIntPtr(viewVtable, 9 * IntPtr.Size);
var runDel = Marshal.GetDelegateForFunctionPointer<RunDelegate>(runPtr);
runDel(viewPtr);
// 10: Uninitialize()
Console.WriteLine("[HLE] Calling IFrameworkView::Uninitialize...");
IntPtr uninitPtr = Marshal.ReadIntPtr(viewVtable, 10 * IntPtr.Size);
var uninitDel = Marshal.GetDelegateForFunctionPointer<UninitializeDelegate>(uninitPtr);
uninitDel(viewPtr);
}
else
{
Console.WriteLine("[HLE] CreateView failed or returned null!");
}
}
catch (Exception ex)
{
Console.WriteLine($"[HLE] CoreApplicationRunImpl crashed: {ex.Message}\n{ex.StackTrace}");
}
return 0;
}
public static int GetActivationFactoryImpl(IntPtr classId, out IntPtr factory)
{
Console.WriteLine("[HLE] GetActivationFactory called. Returning fake COM object.");
factory = GenerateFakeComInstance();
return 0; // S_OK
}
public static int GetActivationFactoryByPCWSTRImpl(IntPtr classId, IntPtr iid, out IntPtr factory)
{
string className = "[null]";
try { if (classId != IntPtr.Zero) className = Marshal.PtrToStringUni(classId); } catch { }
Guid iidGuid = Guid.Empty;
try { if (iid != IntPtr.Zero) iidGuid = GuidFromPtr(iid); } catch { }
Console.WriteLine($"[HLE] GetActivationFactoryByPCWSTR called. Class: {className}, IID: {iidGuid}");
factory = GenerateFakeComInstance();
return 0; // S_OK
}
public static Guid GuidFromPtr(IntPtr ptr)
{
byte[] bytes = new byte[16];
Marshal.Copy(ptr, bytes, 0, 16);
return new Guid(bytes);
}
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static int QueryInterfaceImpl(IntPtr thisPtr, IntPtr iid, out IntPtr ppv)
{
Guid iidGuid = Guid.Empty;
try { if (iid != IntPtr.Zero) iidGuid = GuidFromPtr(iid); } catch { }
Console.WriteLine($"[HLE] COM QueryInterface called. IID: {iidGuid}");
ppv = thisPtr; // Возвращаем тот же фейковый объект
return 0; // S_OK
}
static int AddRefImpl(IntPtr thisPtr) => 1;
static int ReleaseImpl(IntPtr thisPtr) => 1;
static int GetIidsImpl(IntPtr thisPtr, out int count, out IntPtr iids)
{
count = 0;
iids = IntPtr.Zero;
return unchecked((int)0x80004001); // E_NOTIMPL
}
static int GetRuntimeClassNameImpl(IntPtr thisPtr, out IntPtr className)
{
className = IntPtr.Zero;
return unchecked((int)0x80004001); // E_NOTIMPL
}
static int GetTrustLevelImpl(IntPtr thisPtr, out int trustLevel)
{
trustLevel = 1; // BaseTrust
return 0; // S_OK
}
static int ActivateInstanceImpl(IntPtr thisPtr, out IntPtr instance)
{
Console.WriteLine("[HLE] IActivationFactory::ActivateInstance called.");
instance = GenerateFakeComInstance();
return 0; // S_OK
}
public const int TYPE_UNKNOWN = 0;
public const int TYPE_CORE_APPLICATION_VIEW = 1;
public const int TYPE_LAUNCH_ACTIVATED_EVENT_ARGS = 2;
public const int TYPE_CORE_WINDOW = 3;
private static IntPtr _comInstancePool = IntPtr.Zero;
private static int _comInstancePoolOffset = 0;
public static IntPtr GenerateFakeComInstance(int typeId = 0)
{
if (_comVTable == IntPtr.Zero)
{
_comVTable = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)1024, 0x3000, 0x40);
if (_comVTable == IntPtr.Zero) return IntPtr.Zero;
for (int i = 0; i < 128; i++)
{
var dm = new DynamicMethod($"ComStub_{i}", typeof(int), new[] { typeof(IntPtr), typeof(IntPtr), typeof(IntPtr), typeof(IntPtr) }, typeof(XboxExports).Module, true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Ldarg_3);
il.EmitCall(OpCodes.Call, typeof(XboxExports).GetMethod("SafeComStubImplWithIndex", BindingFlags.Static | BindingFlags.Public), null);
il.Emit(OpCodes.Ret);
var del = dm.CreateDelegate(typeof(SafeComStubDelegate));
_comSafeStubs.Add(del);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(del);
Marshal.WriteIntPtr(_comVTable, i * 8, ptr);
}
Marshal.WriteIntPtr(_comVTable, 0 * 8, Marshal.GetFunctionPointerForDelegate(_qi));
Marshal.WriteIntPtr(_comVTable, 1 * 8, Marshal.GetFunctionPointerForDelegate(_addRef));
Marshal.WriteIntPtr(_comVTable, 2 * 8, Marshal.GetFunctionPointerForDelegate(_release));
Marshal.WriteIntPtr(_comVTable, 3 * 8, Marshal.GetFunctionPointerForDelegate(_getIids));
Marshal.WriteIntPtr(_comVTable, 4 * 8, Marshal.GetFunctionPointerForDelegate(_getRuntimeClassName));
Marshal.WriteIntPtr(_comVTable, 5 * 8, Marshal.GetFunctionPointerForDelegate(_getTrustLevel));
}
// ИСПРАВЛЕНО: Используем пул памяти вместо VirtualAlloc для каждого объекта
if (_comInstancePool == IntPtr.Zero)
{
_comInstancePool = NativeMethods.VirtualAlloc(IntPtr.Zero, (IntPtr)(1024 * 1024), 0x3000, 0x04); // 1 MB pool
if (_comInstancePool == IntPtr.Zero)
{
Console.WriteLine("[HLE] FATAL: Failed to allocate COM instance pool!");
return IntPtr.Zero;
}
}
if (_comInstancePoolOffset >= 1024 * 1024 - 16)
{
Console.WriteLine("[HLE] FATAL: COM instance pool exhausted!");
return IntPtr.Zero;
}
IntPtr fakeInstance = IntPtr.Add(_comInstancePool, _comInstancePoolOffset);
_comInstancePoolOffset += 16; // 8 bytes for VTable, 8 bytes for TypeID
Marshal.WriteIntPtr(fakeInstance, _comVTable);
Marshal.WriteInt32(fakeInstance, 8, typeId);
return fakeInstance;
}
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
public static int SafeComStubImplWithIndex(int index, IntPtr thisPtr, IntPtr arg1, IntPtr arg2, IntPtr arg3)
{
int typeId = 0;
try { typeId = Marshal.ReadInt32(thisPtr, 8); } catch { }
Console.WriteLine($"[HLE] COM Method called. Type: {typeId}, VTable Index: {index}, Args: 0x{arg1.ToInt64():X}, 0x{arg2.ToInt64():X}, 0x{arg3.ToInt64():X}");
// ICoreApplication::Run(IFrameworkViewSource*) - индекс 13
if (index == 13 && typeId == TYPE_UNKNOWN)
{
Console.WriteLine("[HLE] ICoreApplication::Run called!");
return CoreApplicationRunImpl(arg1);
}
// CoreApplicationView (Type 1)
if (typeId == TYPE_CORE_APPLICATION_VIEW)
{
if (index == 6) // get_CoreWindow
{
Console.WriteLine("[HLE] CoreApplicationView::get_CoreWindow");
Marshal.WriteIntPtr(arg1, GenerateFakeComInstance(TYPE_CORE_WINDOW));
return 0;
}
if (index == 7) // get_LaunchActivatedEventArgs
{
Console.WriteLine("[HLE] CoreApplicationView::get_LaunchActivatedEventArgs");
Marshal.WriteIntPtr(arg1, GenerateFakeComInstance(TYPE_LAUNCH_ACTIVATED_EVENT_ARGS));
return 0;
}
}
// LaunchActivatedEventArgs (Type 2)
if (typeId == TYPE_LAUNCH_ACTIVATED_EVENT_ARGS)
{
if (index == 6) // get_Kind (ActivationKind enum, 4 байта)
{
Console.WriteLine("[HLE] LaunchActivatedEventArgs::get_Kind -> Launch");
Marshal.WriteInt32(arg1, 0); // 0 = Launch
return 0;
}
if (index == 7) // get_PreviousExecutionState (enum, 4 байта)
{
Console.WriteLine("[HLE] LaunchActivatedEventArgs::get_PreviousExecutionState -> NotRunning");
Marshal.WriteInt32(arg1, 1); // 1 = NotRunning
return 0;
}
if (index == 8) // get_SplashScreen
{
Console.WriteLine("[HLE] LaunchActivatedEventArgs::get_SplashScreen");
Marshal.WriteIntPtr(arg1, GenerateFakeComInstance(TYPE_UNKNOWN));
return 0;
}
if (index == 9) // get_Arguments (HSTRING)
{
Console.WriteLine("[HLE] LaunchActivatedEventArgs::get_Arguments");
Marshal.WriteIntPtr(arg1, IntPtr.Zero); // No arguments
return 0;
}
}
// ИСПРАВЛЕНО: Для всех остальных методов возвращаем S_OK, но НИЧЕГО не пишем в arg1!
// Это предотвратит затирание стека и мусорных указателей.
return 0; // S_OK
}
}
public static class NativeMethods
{
public const uint GENERIC_WRITE = 0x40000000;
public const uint OPEN_ALWAYS = 4;
[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)]
public static extern IntPtr VirtualQuery(IntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, IntPtr dwLength);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int TlsAlloc();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool TlsSetValue(int dwTlsIndex, IntPtr lpTlsValue);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibraryA(string lpFileName);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr GetModuleHandleA(string lpModuleName);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SetFilePointer(IntPtr hFile, int lDistanceToMove, IntPtr lpDistanceToMoveHigh, uint dwMoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool RtlAddFunctionTable(IntPtr FunctionTable, uint EntryCount, IntPtr BaseAddress);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr AddVectoredExceptionHandler(uint First, Diagnostics.VectoredHandler handler);
[DllImport("ole32.dll")]
public static extern int CoInitializeEx(IntPtr pvReserved, COINIT dwCoInit);
[DllImport("ole32.dll")]
public static extern void CoUninitialize();
[Flags]
public enum COINIT : uint { APARTMENTTHREADED = 0x2, MULTITHREADED = 0x0 }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment