mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-24 11:48:39 +08:00
A dozen changes; new HLEs, AV fixes, ELF loader fixes, new return codes, etc.
This commit is contained in:
@@ -12,6 +12,12 @@ namespace SharpEmu.Core.Cpu;
|
||||
|
||||
public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
{
|
||||
private enum EntryFrameKind
|
||||
{
|
||||
ProcessEntry,
|
||||
ModuleInitializer,
|
||||
}
|
||||
|
||||
private const ulong StackBaseAddress = 0x7FFF_F000_0000UL;
|
||||
private const ulong StackSize = 0x0020_0000UL;
|
||||
private const ulong TlsBaseAddress = 0x7FFE_0000_0000UL;
|
||||
@@ -88,13 +94,44 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public OrbisGen2Result DispatchModuleInitializer(
|
||||
ulong entryPoint,
|
||||
Generation generation,
|
||||
IReadOnlyDictionary<ulong, string>? importStubs = null,
|
||||
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
|
||||
string moduleName = "module",
|
||||
CpuExecutionOptions executionOptions = default)
|
||||
{
|
||||
Console.Error.WriteLine("[DISPATCHER] === DispatchModuleInitializer START ===");
|
||||
Console.Error.WriteLine($"[DISPATCHER] moduleInit=0x{entryPoint:X16}, generation={generation}, module={moduleName}");
|
||||
|
||||
try
|
||||
{
|
||||
return DispatchEntryCore(
|
||||
entryPoint,
|
||||
generation,
|
||||
importStubs,
|
||||
runtimeSymbols,
|
||||
moduleName,
|
||||
executionOptions,
|
||||
EntryFrameKind.ModuleInitializer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"[DISPATCHER] FATAL EXCEPTION in DispatchModuleInitializer: {ex.GetType().Name}: {ex.Message}");
|
||||
Console.Error.WriteLine($"[DISPATCHER] Stack trace: {ex.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private OrbisGen2Result DispatchEntryCore(
|
||||
ulong entryPoint,
|
||||
Generation generation,
|
||||
IReadOnlyDictionary<ulong, string>? importStubs = null,
|
||||
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
|
||||
string processImageName = "eboot.bin",
|
||||
CpuExecutionOptions executionOptions = default)
|
||||
CpuExecutionOptions executionOptions = default,
|
||||
EntryFrameKind frameKind = EntryFrameKind.ProcessEntry)
|
||||
{
|
||||
Console.Error.WriteLine("[DISPATCHER] DispatchEntryCore STARTING...");
|
||||
|
||||
@@ -164,23 +201,33 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
var effectiveImportStubs = importStubs is null
|
||||
? new Dictionary<ulong, string>()
|
||||
: new Dictionary<ulong, string>(importStubs);
|
||||
var programExitHandlerStubAddress = TryMapDynlibFallbackStubRegion();
|
||||
if (programExitHandlerStubAddress == 0)
|
||||
var entryParamsConfigured = false;
|
||||
if (frameKind == EntryFrameKind.ProcessEntry)
|
||||
{
|
||||
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (!InitializeProcessEntryFrame(context, processImageName, programExitHandlerStubAddress))
|
||||
{
|
||||
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (ShouldInjectBootstrapPayload(entryPoint))
|
||||
{
|
||||
if (!TryInstallBootstrapPayload(context, effectiveImportStubs))
|
||||
var programExitHandlerStubAddress = TryMapDynlibFallbackStubRegion();
|
||||
if (programExitHandlerStubAddress == 0)
|
||||
{
|
||||
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (!InitializeProcessEntryFrame(context, processImageName, programExitHandlerStubAddress))
|
||||
{
|
||||
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
entryParamsConfigured = true;
|
||||
|
||||
if (ShouldInjectBootstrapPayload(entryPoint))
|
||||
{
|
||||
if (!TryInstallBootstrapPayload(context, effectiveImportStubs))
|
||||
{
|
||||
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!InitializeModuleInitializerFrame(context))
|
||||
{
|
||||
return FailEarly(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
var entryFrameDiagnostic = BuildEntryFrameDiagnostic(
|
||||
@@ -188,7 +235,7 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
context,
|
||||
sentinelEnabled: true,
|
||||
sentinelValue: returnToHostStubAddress,
|
||||
entryParamsConfigured: true);
|
||||
entryParamsConfigured: entryParamsConfigured);
|
||||
|
||||
if (executionOptions.CpuEngine != CpuExecutionEngine.NativeOnly)
|
||||
{
|
||||
@@ -360,6 +407,17 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool InitializeModuleInitializerFrame(CpuContext context)
|
||||
{
|
||||
context[CpuRegister.Rdi] = 0;
|
||||
context[CpuRegister.Rsi] = 0;
|
||||
context[CpuRegister.Rdx] = 0;
|
||||
context[CpuRegister.Rcx] = 0;
|
||||
context[CpuRegister.R8] = 0;
|
||||
context[CpuRegister.R9] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ulong AlignDown(ulong value, ulong alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
|
||||
@@ -34,4 +34,12 @@ public interface ICpuDispatcher
|
||||
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
|
||||
string processImageName = "eboot.bin",
|
||||
CpuExecutionOptions executionOptions = default);
|
||||
|
||||
OrbisGen2Result DispatchModuleInitializer(
|
||||
ulong entryPoint,
|
||||
Generation generation,
|
||||
IReadOnlyDictionary<ulong, string>? importStubs = null,
|
||||
IReadOnlyDictionary<string, ulong>? runtimeSymbols = null,
|
||||
string moduleName = "module",
|
||||
CpuExecutionOptions executionOptions = default);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using SharpEmu.Core.Cpu.Disasm;
|
||||
@@ -254,6 +255,9 @@ public sealed partial class DirectExecutionBackend
|
||||
Console.Error.WriteLine("[LOADER][ERROR] Could not read code at RIP");
|
||||
}
|
||||
DumpRecentImportTrace();
|
||||
DumpGuestDisasmDiagnostics(rip, rbp);
|
||||
DumpGuestReferenceDiagnostics();
|
||||
DumpGuestPointerWindowDiagnostics();
|
||||
break;
|
||||
case 2147483651u:
|
||||
Console.Error.WriteLine("[LOADER][WARNING] Type: Breakpoint (int3)");
|
||||
@@ -322,6 +326,239 @@ public sealed partial class DirectExecutionBackend
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpGuestDisasmDiagnostics(ulong rip, ulong rbp)
|
||||
{
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_DISASM"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (rip >= 0x20)
|
||||
{
|
||||
DumpGuestInstructionStream("fault-prelude", rip - 0x20, 24);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ulong frame = rbp;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (frame < 140733193388032L || frame > 140737488355327L)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ulong ret = (ulong)Marshal.ReadInt64((nint)(frame + 8));
|
||||
if (ret >= 0x40)
|
||||
{
|
||||
DumpGuestInstructionStream($"frame#{i}-ret-prelude", ret - 0x40, 24);
|
||||
}
|
||||
|
||||
ulong next = (ulong)Marshal.ReadInt64((nint)frame);
|
||||
if (next <= frame)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
frame = next;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.Error.WriteLine("[LOADER][WARNING] Could not dump disasm diagnostics.");
|
||||
}
|
||||
|
||||
var extraAddresses = Environment.GetEnvironmentVariable("SHARPEMU_LOG_DISASM_ADDRS");
|
||||
if (string.IsNullOrWhiteSpace(extraAddresses))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var token in extraAddresses.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
var normalized = token.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||
? token[2..]
|
||||
: token;
|
||||
if (!ulong.TryParse(normalized, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var address) || address < 0x20)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DumpGuestInstructionStream($"extra-0x{address:X16}", address, 48);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void DumpGuestReferenceDiagnostics()
|
||||
{
|
||||
var targetList = ParseDiagnosticAddresses(Environment.GetEnvironmentVariable("SHARPEMU_LOG_REFSCAN_ADDRS"));
|
||||
if (targetList.Count == 0 || _cpuContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const ulong scanBase = 0x0000000800000000UL;
|
||||
const ulong scanEnd = 0x0000000810000000UL;
|
||||
const int maxHitsPerTarget = 24;
|
||||
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][INFO] Ref scan targets: {string.Join(", ", targetList.ConvertAll(static addr => $"0x{addr:X16}"))}");
|
||||
|
||||
var hitCounts = new Dictionary<ulong, int>(targetList.Count);
|
||||
for (var i = 0; i < targetList.Count; i++)
|
||||
{
|
||||
hitCounts[targetList[i]] = 0;
|
||||
}
|
||||
|
||||
ulong address = scanBase;
|
||||
while (address < scanEnd)
|
||||
{
|
||||
if (VirtualQuery((void*)address, out var mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ulong regionBase = mbi.BaseAddress;
|
||||
ulong regionEnd = regionBase + mbi.RegionSize;
|
||||
if (regionEnd <= address)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (mbi.State == MEM_COMMIT &&
|
||||
IsReadableProtection(mbi.Protect) &&
|
||||
IsExecutableProtection(mbi.Protect))
|
||||
{
|
||||
ScanExecutableRegionForTargetReferences(regionBase, regionEnd, targetList, hitCounts, maxHitsPerTarget);
|
||||
}
|
||||
|
||||
var allTargetsSatisfied = true;
|
||||
for (var i = 0; i < targetList.Count; i++)
|
||||
{
|
||||
if (hitCounts[targetList[i]] < maxHitsPerTarget)
|
||||
{
|
||||
allTargetsSatisfied = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allTargetsSatisfied)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
address = regionEnd;
|
||||
}
|
||||
|
||||
for (var i = 0; i < targetList.Count; i++)
|
||||
{
|
||||
var target = targetList[i];
|
||||
if (!hitCounts.TryGetValue(target, out var count) || count == 0)
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Ref scan 0x{target:X16}: none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpGuestPointerWindowDiagnostics()
|
||||
{
|
||||
var targetList = ParseDiagnosticAddresses(Environment.GetEnvironmentVariable("SHARPEMU_LOG_POINTER_WINDOWS"));
|
||||
if (targetList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var windowSize = 0x80;
|
||||
var rawWindowSize = Environment.GetEnvironmentVariable("SHARPEMU_LOG_POINTER_WINDOW_SIZE");
|
||||
if (!string.IsNullOrWhiteSpace(rawWindowSize))
|
||||
{
|
||||
var normalized = rawWindowSize.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||
? rawWindowSize[2..]
|
||||
: rawWindowSize;
|
||||
if (int.TryParse(normalized, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var parsedWindowSize) &&
|
||||
parsedWindowSize > 0)
|
||||
{
|
||||
windowSize = parsedWindowSize;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var target in targetList)
|
||||
{
|
||||
DumpPointerWindow($"ptrwin-0x{target:X16}", target, windowSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScanExecutableRegionForTargetReferences(
|
||||
ulong regionBase,
|
||||
ulong regionEnd,
|
||||
IReadOnlyList<ulong> targets,
|
||||
IDictionary<ulong, int> hitCounts,
|
||||
int maxHitsPerTarget)
|
||||
{
|
||||
if (_cpuContext == null || regionEnd <= regionBase)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ulong rip = regionBase;
|
||||
while (rip < regionEnd)
|
||||
{
|
||||
if (!IcedDecoder.TryReadGuestBytes(_cpuContext.Memory, rip, maxLen: 15, out var bytes) ||
|
||||
!IcedDecoder.TryDecode(rip, bytes, out var instruction) ||
|
||||
instruction.Length <= 0)
|
||||
{
|
||||
rip++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instruction.MemoryAddress is { } memoryAddress)
|
||||
{
|
||||
for (var i = 0; i < targets.Count; i++)
|
||||
{
|
||||
var target = targets[i];
|
||||
if (memoryAddress != target ||
|
||||
!hitCounts.TryGetValue(target, out var count) ||
|
||||
count >= maxHitsPerTarget)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hitCounts[target] = count + 1;
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][INFO] Ref scan hit target=0x{target:X16} rip=0x{instruction.Rip:X16} text={instruction.Text} bytes={IcedDecoder.FormatBytes(instruction.Bytes)}");
|
||||
}
|
||||
}
|
||||
|
||||
rip += (ulong)instruction.Length;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ulong> ParseDiagnosticAddresses(string? rawValue)
|
||||
{
|
||||
var result = new List<ulong>();
|
||||
if (string.IsNullOrWhiteSpace(rawValue))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var token in rawValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
{
|
||||
var normalized = token.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||
? token[2..]
|
||||
: token;
|
||||
if (!ulong.TryParse(normalized, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var address))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result.Contains(address))
|
||||
{
|
||||
result.Add(address);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void DumpUnresolvedSentinelWindow(string name, ulong baseAddress, int size)
|
||||
{
|
||||
if (baseAddress < 0x10000 || size <= 0)
|
||||
|
||||
@@ -237,6 +237,7 @@ public sealed partial class DirectExecutionBackend
|
||||
try
|
||||
{
|
||||
OrbisGen2Result orbisGen2Result;
|
||||
bool dispatchResolved = true;
|
||||
if (string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
|
||||
{
|
||||
orbisGen2Result = DispatchBootstrapBridge();
|
||||
@@ -247,34 +248,31 @@ public sealed partial class DirectExecutionBackend
|
||||
}
|
||||
else
|
||||
{
|
||||
orbisGen2Result = _moduleManager.Dispatch(importStubEntry.Nid, _cpuContext);
|
||||
dispatchResolved = _moduleManager.TryDispatch(importStubEntry.Nid, _cpuContext, out orbisGen2Result);
|
||||
}
|
||||
switch (orbisGen2Result)
|
||||
if (!dispatchResolved)
|
||||
{
|
||||
case OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND:
|
||||
LastError = "Missing HLE export for NID: " + importStubEntry.Nid;
|
||||
Console.Error.WriteLine($"[LOADER][WARN] Import#{num} unresolved: nid={importStubEntry.Nid} ret=0x{num7:X16}");
|
||||
if (importStubEntry.Nid == "L-Q3LEjIbgA")
|
||||
LastError = "Missing HLE export for NID: " + importStubEntry.Nid;
|
||||
Console.Error.WriteLine($"[LOADER][WARN] Import#{num} unresolved: nid={importStubEntry.Nid} ret=0x{num7:X16}");
|
||||
if (importStubEntry.Nid == "L-Q3LEjIbgA")
|
||||
{
|
||||
string value18 = string.Join(" ", importStubEntry.Nid.Select(delegate (char c)
|
||||
{
|
||||
string value18 = string.Join(" ", importStubEntry.Nid.Select(delegate (char c)
|
||||
{
|
||||
int num10 = c;
|
||||
return num10.ToString("X2");
|
||||
}));
|
||||
Console.Error.WriteLine($"[LOADER][WARN] map_direct nid raw len={importStubEntry.Nid.Length} chars=[{value18}]");
|
||||
Delegate function;
|
||||
bool value19 = _moduleManager.TryGetFunction(importStubEntry.Nid, out function);
|
||||
ExportedFunction export2;
|
||||
bool value20 = _moduleManager.TryGetExport(importStubEntry.Nid, out export2);
|
||||
Console.Error.WriteLine($"[LOADER][WARN] map_direct lookup with import nid: function={value19}, export={value20}");
|
||||
Console.Error.WriteLine(_moduleManager.TryGetExport("L-Q3LEjIbgA", out ExportedFunction export3) ? $"[LOADER][WARN] Canonical map_direct exists as {export3.LibraryName}:{export3.Name}, target={export3.Target}, ctx_target={_cpuContext.TargetGeneration}" : "[LOADER][WARN] Canonical map_direct export lookup also missing");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Console.Error.WriteLine($"[LOADER][WARN] Import#{num} result: {orbisGen2Result} ({importStubEntry.Nid})");
|
||||
break;
|
||||
case OrbisGen2Result.ORBIS_GEN2_OK:
|
||||
break;
|
||||
int num10 = c;
|
||||
return num10.ToString("X2");
|
||||
}));
|
||||
Console.Error.WriteLine($"[LOADER][WARN] map_direct nid raw len={importStubEntry.Nid.Length} chars=[{value18}]");
|
||||
Delegate function;
|
||||
bool value19 = _moduleManager.TryGetFunction(importStubEntry.Nid, out function);
|
||||
ExportedFunction export2;
|
||||
bool value20 = _moduleManager.TryGetExport(importStubEntry.Nid, out export2);
|
||||
Console.Error.WriteLine($"[LOADER][WARN] map_direct lookup with import nid: function={value19}, export={value20}");
|
||||
Console.Error.WriteLine(_moduleManager.TryGetExport("L-Q3LEjIbgA", out ExportedFunction export3) ? $"[LOADER][WARN] Canonical map_direct exists as {export3.LibraryName}:{export3.Name}, target={export3.Target}, ctx_target={_cpuContext.TargetGeneration}" : "[LOADER][WARN] Canonical map_direct export lookup also missing");
|
||||
}
|
||||
}
|
||||
else if (orbisGen2Result != OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][WARN] Import#{num} result: {orbisGen2Result} ({importStubEntry.Nid})");
|
||||
}
|
||||
_cpuContext[CpuRegister.Rbx] = value3;
|
||||
_cpuContext[CpuRegister.Rbp] = value4;
|
||||
@@ -334,7 +332,12 @@ public sealed partial class DirectExecutionBackend
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RecordImportLoopSignature(BuildImportLoopSignature(nid, returnRip, arg0, arg1));
|
||||
if (!_importNidHashCache.TryGetValue(nid, out var value))
|
||||
{
|
||||
value = StableHash64(nid);
|
||||
_importNidHashCache[nid] = value;
|
||||
}
|
||||
RecordImportLoopSignature(value, returnRip, BuildImportLoopSignature(value, returnRip, arg0, arg1));
|
||||
if (!HasRepeatingImportLoopPattern())
|
||||
{
|
||||
if (_importLoopPatternHits > 0)
|
||||
@@ -347,21 +350,18 @@ public sealed partial class DirectExecutionBackend
|
||||
return _importLoopPatternHits >= 6;
|
||||
}
|
||||
|
||||
private ulong BuildImportLoopSignature(string nid, ulong returnRip, ulong arg0, ulong arg1)
|
||||
private ulong BuildImportLoopSignature(ulong nidHash, ulong returnRip, ulong arg0, ulong arg1)
|
||||
{
|
||||
if (!_importNidHashCache.TryGetValue(nid, out var value))
|
||||
{
|
||||
value = StableHash64(nid);
|
||||
_importNidHashCache[nid] = value;
|
||||
}
|
||||
ulong num = returnRip >> 2;
|
||||
ulong num2 = ((arg0 >> 4) * 11400714819323198485uL) ^ ((arg1 >> 4) * 14029467366897019727uL);
|
||||
return num ^ value * 11400714819323198485uL ^ num2;
|
||||
return num ^ nidHash * 11400714819323198485uL ^ num2;
|
||||
}
|
||||
|
||||
private void RecordImportLoopSignature(ulong signature)
|
||||
private void RecordImportLoopSignature(ulong nidHash, ulong returnRip, ulong signature)
|
||||
{
|
||||
_importLoopSignatures[_importLoopSignatureWriteIndex] = signature;
|
||||
_importLoopNidHashes[_importLoopSignatureWriteIndex] = nidHash;
|
||||
_importLoopReturnRips[_importLoopSignatureWriteIndex] = returnRip;
|
||||
_importLoopSignatureWriteIndex = (_importLoopSignatureWriteIndex + 1) % _importLoopSignatures.Length;
|
||||
if (_importLoopSignatureCount < _importLoopSignatures.Length)
|
||||
{
|
||||
@@ -405,7 +405,7 @@ public sealed partial class DirectExecutionBackend
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return IsSevereImportLoopPattern(num);
|
||||
}
|
||||
|
||||
private ulong GetImportLoopSignatureFromTail(int offset)
|
||||
@@ -418,6 +418,51 @@ public sealed partial class DirectExecutionBackend
|
||||
return _importLoopSignatures[num % _importLoopSignatures.Length];
|
||||
}
|
||||
|
||||
private bool IsSevereImportLoopPattern(int sampleCount)
|
||||
{
|
||||
int num = CountDistinctImportLoopValuesFromTail(_importLoopNidHashes, sampleCount, 3);
|
||||
if (num > 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int num2 = CountDistinctImportLoopValuesFromTail(_importLoopReturnRips, sampleCount, 3);
|
||||
return num2 <= 2;
|
||||
}
|
||||
|
||||
private int CountDistinctImportLoopValuesFromTail(ulong[] source, int sampleCount, int stopAfter)
|
||||
{
|
||||
int num = Math.Min(sampleCount, _importLoopSignatureCount);
|
||||
int num2 = 0;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
ulong importLoopValueFromTail = GetImportLoopValueFromTail(source, i);
|
||||
bool flag = false;
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
if (GetImportLoopValueFromTail(source, j) == importLoopValueFromTail)
|
||||
{
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag && ++num2 >= stopAfter)
|
||||
{
|
||||
return num2;
|
||||
}
|
||||
}
|
||||
return num2;
|
||||
}
|
||||
|
||||
private ulong GetImportLoopValueFromTail(ulong[] source, int offset)
|
||||
{
|
||||
int num = _importLoopSignatureWriteIndex - 1 - offset;
|
||||
while (num < 0)
|
||||
{
|
||||
num += source.Length;
|
||||
}
|
||||
return source[num % source.Length];
|
||||
}
|
||||
|
||||
private bool ShouldSuppressStrlenTrace(string nid)
|
||||
{
|
||||
return string.Equals(nid, "j4ViWNHEgww", StringComparison.Ordinal) && !_logStrlenImports;
|
||||
|
||||
@@ -89,6 +89,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
private const uint PAGE_EXECUTE_READ = 32u;
|
||||
|
||||
private const int TlsHandlerRegionSize = 4096;
|
||||
|
||||
private const ulong TlsModuleAllocStart = 140726751354880uL;
|
||||
|
||||
private const ulong TlsModuleAllocStride = 65536uL;
|
||||
@@ -101,6 +103,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
private bool _ownsTlsBaseAddress;
|
||||
|
||||
private int _tlsPatchStubOffset;
|
||||
|
||||
private nint _unresolvedReturnStub;
|
||||
|
||||
private nint _rawExceptionHandler;
|
||||
@@ -177,6 +181,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
private readonly ulong[] _importLoopSignatures = new ulong[192];
|
||||
|
||||
private readonly ulong[] _importLoopNidHashes = new ulong[192];
|
||||
|
||||
private readonly ulong[] _importLoopReturnRips = new ulong[192];
|
||||
|
||||
private int _importLoopSignatureCount;
|
||||
|
||||
private int _importLoopSignatureWriteIndex;
|
||||
@@ -509,6 +517,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
Console.Error.WriteLine($"[LOADER][DEBUG] TryResolveDirectImportTarget: {nid} not in HLE table, checking runtime symbols...");
|
||||
|
||||
if (TryResolveRuntimeSymbolAddress(nid, out var directValue) && IsDirectImportTargetUsable(directValue))
|
||||
{
|
||||
targetAddress = directValue;
|
||||
resolvedSymbol = nid;
|
||||
Console.Error.WriteLine($"[LOADER][DEBUG] TryResolveDirectImportTarget: {nid} -> runtime symbol 0x{targetAddress:X16}");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Aerolib.Instance.TryGetByNid(nid, out var symbolByNid))
|
||||
{
|
||||
if (!PreferLleForLibcExport(symbolByNid.ExportName))
|
||||
@@ -587,9 +603,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
{
|
||||
"_init_env" or
|
||||
"atexit" or
|
||||
"__cxa_guard_acquire" or
|
||||
"__cxa_guard_release" or
|
||||
"__cxa_guard_abort" or
|
||||
"strlen" or
|
||||
"strnlen" or
|
||||
"strcmp" or
|
||||
@@ -841,10 +854,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
private unsafe void CreateTlsHandler()
|
||||
{
|
||||
_tlsHandlerAddress = (nint)TryAllocateNearEntry(256u);
|
||||
_tlsHandlerAddress = (nint)TryAllocateNearEntry(TlsHandlerRegionSize);
|
||||
if (_tlsHandlerAddress == 0)
|
||||
{
|
||||
_tlsHandlerAddress = (nint)VirtualAlloc(null, 256u, 12288u, 64u);
|
||||
_tlsHandlerAddress = (nint)VirtualAlloc(null, TlsHandlerRegionSize, 12288u, 64u);
|
||||
}
|
||||
if (_tlsHandlerAddress == 0)
|
||||
{
|
||||
@@ -857,9 +870,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
*(long*)(tlsHandlerAddress + num) = _tlsBaseAddress;
|
||||
num += 8;
|
||||
tlsHandlerAddress[num++] = 195;
|
||||
_tlsPatchStubOffset = (num + 15) & ~15;
|
||||
uint num2 = default(uint);
|
||||
VirtualProtect((void*)_tlsHandlerAddress, 256u, 32u, &num2);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)_tlsHandlerAddress, 256u);
|
||||
VirtualProtect((void*)_tlsHandlerAddress, TlsHandlerRegionSize, 32u, &num2);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)_tlsHandlerAddress, TlsHandlerRegionSize);
|
||||
Console.Error.WriteLine($"[LOADER][INFO] TLS handler at 0x{_tlsHandlerAddress:X16}");
|
||||
}
|
||||
|
||||
@@ -939,6 +953,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
ulong num2 = num + MaxScanBytes;
|
||||
int num3 = 0;
|
||||
int num4 = 0;
|
||||
int num9 = 0;
|
||||
while (num < num2)
|
||||
{
|
||||
if (VirtualQuery((void*)num, out var lpBuffer, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0 || lpBuffer.RegionSize == 0)
|
||||
@@ -967,6 +982,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
num3++;
|
||||
PatchTlsInstruction(address);
|
||||
}
|
||||
else if (TryPatchTlsImmediateStoreInstruction(address, ptr + i))
|
||||
{
|
||||
num9++;
|
||||
}
|
||||
else if (TryPatchStackCanaryInstruction(address, ptr + i))
|
||||
{
|
||||
num4++;
|
||||
@@ -975,7 +994,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
}
|
||||
num = num6 > num ? num6 : num + 4096uL;
|
||||
}
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Patched {num3} TLS patterns, {num4} stack-canary accesses");
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Patched {num3} TLS loads, {num9} TLS stores, {num4} stack-canary accesses");
|
||||
}
|
||||
|
||||
private unsafe bool IsPatternMatch(byte* ptr, byte[] pattern)
|
||||
@@ -1089,6 +1108,115 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe bool TryPatchTlsImmediateStoreInstruction(nint address, byte* source)
|
||||
{
|
||||
if (source[0] != 100 || source[1] != 199 || source[2] != 4 || source[3] != 37)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int tlsOffset = *(int*)(source + 4);
|
||||
int immediateValue = *(int*)(source + 8);
|
||||
nint num = CreateTlsImmediateStoreHelper(tlsOffset, immediateValue);
|
||||
if (num == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return PatchCallSite(address, 12, num);
|
||||
}
|
||||
|
||||
private unsafe nint CreateTlsImmediateStoreHelper(int tlsOffset, int immediateValue)
|
||||
{
|
||||
nint num = AllocateTlsPatchStub(32);
|
||||
if (num == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
byte* ptr = (byte*)num;
|
||||
int num2 = 0;
|
||||
ptr[num2++] = 80;
|
||||
ptr[num2++] = 232;
|
||||
long num3 = _tlsHandlerAddress - (num + num2 + 4);
|
||||
if (num3 < int.MinValue || num3 > int.MaxValue)
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][WARNING] TLS store helper out of rel32 range at 0x{num:X16}");
|
||||
return 0;
|
||||
}
|
||||
*(int*)(ptr + num2) = (int)num3;
|
||||
num2 += 4;
|
||||
ptr[num2++] = 199;
|
||||
ptr[num2++] = 128;
|
||||
*(int*)(ptr + num2) = tlsOffset;
|
||||
num2 += 4;
|
||||
*(int*)(ptr + num2) = immediateValue;
|
||||
num2 += 4;
|
||||
ptr[num2++] = 88;
|
||||
ptr[num2++] = 195;
|
||||
while (num2 < 32)
|
||||
{
|
||||
ptr[num2++] = 144;
|
||||
}
|
||||
uint flNewProtect = default(uint);
|
||||
VirtualProtect((void*)num, 32u, 32u, &flNewProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)num, 32u);
|
||||
return num;
|
||||
}
|
||||
|
||||
private unsafe nint AllocateTlsPatchStub(int size)
|
||||
{
|
||||
if (_tlsHandlerAddress == 0 || size <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int num = (size + 15) & -16;
|
||||
if (_tlsPatchStubOffset + num > TlsHandlerRegionSize)
|
||||
{
|
||||
Console.Error.WriteLine("[LOADER][WARNING] TLS patch stub region exhausted.");
|
||||
return 0;
|
||||
}
|
||||
nint result = _tlsHandlerAddress + _tlsPatchStubOffset;
|
||||
_tlsPatchStubOffset += num;
|
||||
uint flNewProtect = default(uint);
|
||||
if (!VirtualProtect((void*)result, (nuint)num, 64u, &flNewProtect))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private unsafe bool PatchCallSite(nint address, int instructionLength, nint target)
|
||||
{
|
||||
if (instructionLength < 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint flNewProtect = default(uint);
|
||||
if (!VirtualProtect((void*)address, (nuint)instructionLength, 64u, &flNewProtect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
long num = target - (address + 5);
|
||||
if (num < int.MinValue || num > int.MaxValue)
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][WARNING] TLS patch out of rel32 range at 0x{address:X16}");
|
||||
return false;
|
||||
}
|
||||
*(byte*)address = 232;
|
||||
*(int*)(address + 1) = (int)num;
|
||||
for (int i = 5; i < instructionLength; i++)
|
||||
{
|
||||
*(byte*)(address + i) = 144;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
VirtualProtect((void*)address, (nuint)instructionLength, flNewProtect, &flNewProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)address, (nuint)instructionLength);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private unsafe void TryPreReservePrtAperture(ulong baseAddress, ulong size)
|
||||
{
|
||||
if (VirtualQuery((void*)baseAddress, out var lpBuffer, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) != 0 && lpBuffer.State != 65536)
|
||||
|
||||
Reference in New Issue
Block a user