mirror of
https://github.com/AdvDebug/Brovan
synced 2026-08-09 11:57:17 +00:00
Add display Win32k stubs and callback plumbing
Introduces Win32k syscall handlers for monitor/display queries (`EnumDisplayMonitors`, `EnumDisplaySettings`, `GetDisplayConfigBufferSizes`, `GetDpiForMonitor`) and adds guest-callback trampoline support so kernel-side handlers can invoke guest callbacks safely. It also improves runtime robustness by adding PID generation fallback behavior, wiring primary monitor handle/rect helpers, and exposing display frequency via `EnumDisplaySettingsW`. On the Vulkan side, the generator now tracks optional members and emits that metadata into generated struct descriptors, and rebuild-time validation now rejects non-optional null arrays with non-zero declared counts.
This commit is contained in:
@@ -56,6 +56,7 @@ namespace Brovan.Generators
|
||||
public string AltLength;
|
||||
public int ArrayLen = 1;
|
||||
public string Values;
|
||||
public bool Optional;
|
||||
}
|
||||
|
||||
private sealed class VkStruct
|
||||
@@ -248,7 +249,7 @@ namespace Brovan.Generators
|
||||
continue;
|
||||
ParseTyped(mem, out string ty, out string nm, out int pd, out bool isc);
|
||||
int alen = pd == 0 ? ResolveArrayLen(mem, m.Constants) : 1;
|
||||
s.Members.Add(new Member { Name = nm, Type = ty, PtrDepth = pd, IsConst = isc, Length = (string)mem.Attribute("len"), AltLength = (string)mem.Attribute("altlen"), ArrayLen = alen, Values = (string)mem.Attribute("values") });
|
||||
s.Members.Add(new Member { Name = nm, Type = ty, PtrDepth = pd, IsConst = isc, Length = (string)mem.Attribute("len"), AltLength = (string)mem.Attribute("altlen"), ArrayLen = alen, Values = (string)mem.Attribute("values"), Optional = ((string)mem.Attribute("optional") ?? "").StartsWith("true", StringComparison.Ordinal) });
|
||||
}
|
||||
m.Structs[name] = s;
|
||||
}
|
||||
@@ -760,6 +761,7 @@ namespace Brovan.Generators
|
||||
public string MemName;
|
||||
public string LenName;
|
||||
public string SelName;
|
||||
public bool Optional;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, byte> KindNum = new Dictionary<string, byte>
|
||||
@@ -816,7 +818,7 @@ namespace Brovan.Generators
|
||||
|
||||
private static MDesc ClassifyMember(Model m, VkStruct owner, Member mem, Dictionary<string, int> offsets, Dictionary<string, Layout> cache)
|
||||
{
|
||||
MDesc d = new MDesc { Offset = offsets.TryGetValue(mem.Name, out int mo) ? mo : 0, MemName = mem.Name };
|
||||
MDesc d = new MDesc { Offset = offsets.TryGetValue(mem.Name, out int mo) ? mo : 0, MemName = mem.Name, Optional = mem.Optional };
|
||||
if (mem.Name == "pNext") { d.Kind = "PNext"; d.Size = 8; return d; }
|
||||
if (owner.Name == "VkWriteDescriptorSet" && (mem.Name == "pImageInfo" || mem.Name == "pBufferInfo" || mem.Name == "pTexelBufferView"))
|
||||
{
|
||||
@@ -1987,8 +1989,8 @@ namespace Brovan.Generators
|
||||
sm.AppendLine(" internal enum BvkMK : byte { Scalar, Handle, StructValue, StructPtr, StructArray, HandleArray, ScalarArray, StringZ, StringArray, PNext, Ignore, BlobPtr, SelectArray }");
|
||||
sm.AppendLine(" internal readonly struct BvkM");
|
||||
sm.AppendLine(" {");
|
||||
sm.AppendLine(" public readonly BvkMK Kind; public readonly int Offset; public readonly int Size; public readonly int Sub; public readonly int LenOffset; public readonly string HandleType; public readonly int SelOffset; public readonly uint SelMask;");
|
||||
sm.AppendLine(" public BvkM(BvkMK k, int o, int s, int sub, int lo, string ht, int so, uint sm) { Kind = k; Offset = o; Size = s; Sub = sub; LenOffset = lo; HandleType = ht; SelOffset = so; SelMask = sm; }");
|
||||
sm.AppendLine(" public readonly BvkMK Kind; public readonly int Offset; public readonly int Size; public readonly int Sub; public readonly int LenOffset; public readonly string HandleType; public readonly int SelOffset; public readonly uint SelMask; public readonly bool Optional;");
|
||||
sm.AppendLine(" public BvkM(BvkMK k, int o, int s, int sub, int lo, string ht, int so, uint sm, bool opt = false) { Kind = k; Offset = o; Size = s; Sub = sub; LenOffset = lo; HandleType = ht; SelOffset = so; SelMask = sm; Optional = opt; }");
|
||||
sm.AppendLine(" }");
|
||||
sm.AppendLine(" internal static class BrovVulkStructMeta");
|
||||
sm.AppendLine(" {");
|
||||
@@ -2032,7 +2034,7 @@ namespace Brovan.Generators
|
||||
if (d.Kind == "Scalar" && d.Size > 8 && d.MemName != null)
|
||||
ga.Append("BVK_SAME_SIZE(s").Append(si).Append('_').Append(d.MemName)
|
||||
.Append(", sizeof(((").Append(n).Append("*)0)->").Append(d.MemName).Append("), ").Append(d.Size).Append(");\n");
|
||||
sm.Append("new BvkM(BvkMK.").Append(d.Kind).Append(", ").Append(d.Offset).Append(", ").Append(d.Size).Append(", ").Append(sub).Append(", ").Append(d.LenOffset).Append(", \"").Append(d.HandleType).Append("\", ").Append(d.SelOffset).Append(", ").Append(d.SelMask).Append("u), ");
|
||||
sm.Append("new BvkM(BvkMK.").Append(d.Kind).Append(", ").Append(d.Offset).Append(", ").Append(d.Size).Append(", ").Append(sub).Append(", ").Append(d.LenOffset).Append(", \"").Append(d.HandleType).Append("\", ").Append(d.SelOffset).Append(", ").Append(d.SelMask).Append("u, ").Append(d.Optional ? "true" : "false").Append("), ");
|
||||
gs.Append("{").Append(KindNum[d.Kind]).Append(",").Append(GuestOffset(n, d.MemName, d.Offset)).Append(",").Append(d.Size)
|
||||
.Append(",").Append(GuestNativeSize(n, d))
|
||||
.Append(",").Append(sub)
|
||||
|
||||
@@ -24,8 +24,12 @@ namespace Brovan.Core.Emulation.OS.Windows
|
||||
|
||||
private static void VerifyArrayLen(IntPtr dst, in BvkM d, uint n)
|
||||
{
|
||||
if (n != 0 && n != *(uint*)(dst + d.LenOffset))
|
||||
uint Declared = *(uint*)(dst + d.LenOffset);
|
||||
if (n != 0 && n != Declared)
|
||||
throw new InvalidOperationException("BrovVulk generic: array length mismatch.");
|
||||
|
||||
if (n == 0 && Declared != 0 && !d.Optional)
|
||||
throw new InvalidOperationException("BrovVulk generic: required array is null but its count is non-zero.");
|
||||
}
|
||||
|
||||
public static IntPtr Rebuild(int sid, GenReader r, GenState st)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using static Brovan.Core.Helpers.BinaryHelpers;
|
||||
|
||||
namespace Brovan.Core.Emulation.OS.Windows.Win32k
|
||||
{
|
||||
internal class NtUserEnumDisplayMonitors : IWinSyscall
|
||||
{
|
||||
public NTSTATUS Handle(BinaryEmulator Instance)
|
||||
{
|
||||
ulong Hdc = Instance.WinHelper.GetArg(0);
|
||||
ulong EnumProc = Instance.WinHelper.GetArg(2);
|
||||
ulong CallbackData = Instance.WinHelper.GetArg(3);
|
||||
|
||||
ulong Monitor = Instance.WinHelper.GetPrimaryMonitorHandle();
|
||||
if (Monitor == 0 || !Instance.WinHelper.TryGetPrimaryMonitorRect(out int Left, out int Top, out int Right, out int Bottom))
|
||||
{
|
||||
Instance.SetRawSyscallReturn(0);
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (EnumProc == 0)
|
||||
{
|
||||
Instance.SetRawSyscallReturn(1);
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ulong Rect = Instance.WinHelper.EnsureGuestCallScratch();
|
||||
if (Rect == 0)
|
||||
{
|
||||
Instance.SetRawSyscallReturn(0);
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Instance._emulator.WriteMemory(Rect + 0x00, (uint)Left, 4);
|
||||
Instance._emulator.WriteMemory(Rect + 0x04, (uint)Top, 4);
|
||||
Instance._emulator.WriteMemory(Rect + 0x08, (uint)Right, 4);
|
||||
Instance._emulator.WriteMemory(Rect + 0x0C, (uint)Bottom, 4);
|
||||
|
||||
if (!Instance.WinHelper.BeginGuestCall(EnumProc, Monitor, Hdc, Rect, CallbackData, 1))
|
||||
{
|
||||
Instance.SetRawSyscallReturn(0);
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Text;
|
||||
using static Brovan.Core.Helpers.BinaryHelpers;
|
||||
|
||||
namespace Brovan.Core.Emulation.OS.Windows.Win32k
|
||||
{
|
||||
internal class NtUserEnumDisplaySettings : IWinSyscall
|
||||
{
|
||||
private const uint EnumCurrentSettings = 0xFFFFFFFF;
|
||||
private const uint EnumRegistrySettings = 0xFFFFFFFE;
|
||||
|
||||
private const int DevModeSize = 0xDC;
|
||||
private const int OffsetDeviceName = 0x00;
|
||||
private const int OffsetSpecVersion = 0x40;
|
||||
private const int OffsetDriverVersion = 0x42;
|
||||
private const int OffsetSize = 0x44;
|
||||
private const int OffsetFields = 0x48;
|
||||
private const int OffsetPositionX = 0x4C;
|
||||
private const int OffsetPositionY = 0x50;
|
||||
private const int OffsetDisplayOrientation = 0x54;
|
||||
private const int OffsetLogPixels = 0xA6;
|
||||
private const int OffsetBitsPerPel = 0xA8;
|
||||
private const int OffsetPelsWidth = 0xAC;
|
||||
private const int OffsetPelsHeight = 0xB0;
|
||||
private const int OffsetDisplayFlags = 0xB4;
|
||||
private const int OffsetDisplayFrequency = 0xB8;
|
||||
|
||||
private const uint DmPosition = 0x00000020;
|
||||
private const uint DmDisplayOrientation = 0x00000080;
|
||||
private const uint DmLogPixels = 0x00020000;
|
||||
private const uint DmBitsPerPel = 0x00040000;
|
||||
private const uint DmPelsWidth = 0x00080000;
|
||||
private const uint DmPelsHeight = 0x00100000;
|
||||
private const uint DmDisplayFlags = 0x00200000;
|
||||
private const uint DmDisplayFrequency = 0x00400000;
|
||||
|
||||
private const ushort SpecVersion = 0x0401;
|
||||
private const uint BitsPerPixel = 32;
|
||||
private const uint LogPixels = 96;
|
||||
private const string DeviceName = @"\\.\DISPLAY1";
|
||||
|
||||
public NTSTATUS Handle(BinaryEmulator Instance)
|
||||
{
|
||||
uint ModeNumber = (uint)Instance.WinHelper.GetArg(1);
|
||||
ulong DevModePtr = Instance.WinHelper.GetArg(2);
|
||||
|
||||
if (DevModePtr == 0 || !Instance.IsRegionMapped(DevModePtr, DevModeSize))
|
||||
return NTSTATUS.STATUS_INVALID_PARAMETER;
|
||||
|
||||
// Only the current mode is advertised, so an enumerating caller stops after index 0.
|
||||
if (ModeNumber != EnumCurrentSettings && ModeNumber != EnumRegistrySettings && ModeNumber != 0)
|
||||
return NTSTATUS.STATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!Instance.WinHelper.TryGetPrimaryMonitorRect(out int Left, out int Top, out int Right, out int Bottom))
|
||||
return NTSTATUS.STATUS_UNSUCCESSFUL;
|
||||
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetDeviceName, DeviceName + "\0", Encoding.Unicode);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetSpecVersion, SpecVersion, 2);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetDriverVersion, SpecVersion, 2);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetSize, (ushort)DevModeSize, 2);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetFields,
|
||||
DmPosition | DmDisplayOrientation | DmLogPixels | DmBitsPerPel | DmPelsWidth | DmPelsHeight | DmDisplayFlags | DmDisplayFrequency, 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetPositionX, (uint)Left, 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetPositionY, (uint)Top, 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetDisplayOrientation, 0u, 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetLogPixels, (ushort)LogPixels, 2);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetBitsPerPel, BitsPerPixel, 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetPelsWidth, (uint)(Right - Left), 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetPelsHeight, (uint)(Bottom - Top), 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetDisplayFlags, 0u, 4);
|
||||
Instance._emulator.WriteMemory(DevModePtr + OffsetDisplayFrequency, Instance.WinHelper.GetPrimaryDisplayFrequency(), 4);
|
||||
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using static Brovan.Core.Helpers.BinaryHelpers;
|
||||
|
||||
namespace Brovan.Core.Emulation.OS.Windows.Win32k
|
||||
{
|
||||
internal class NtUserGetDisplayConfigBufferSizes : IWinSyscall
|
||||
{
|
||||
public NTSTATUS Handle(BinaryEmulator Instance)
|
||||
{
|
||||
ulong PathCountPtr = Instance.WinHelper.GetArg(1);
|
||||
ulong ModeCountPtr = Instance.WinHelper.GetArg(2);
|
||||
|
||||
if (PathCountPtr == 0 || !Instance.IsRegionMapped(PathCountPtr, 4))
|
||||
return NTSTATUS.STATUS_INVALID_PARAMETER;
|
||||
|
||||
Instance._emulator.WriteMemory(PathCountPtr, 1u, 4);
|
||||
|
||||
if (ModeCountPtr != 0 && Instance.IsRegionMapped(ModeCountPtr, 4))
|
||||
Instance._emulator.WriteMemory(ModeCountPtr, 2u, 4);
|
||||
|
||||
Instance.SetRawSyscallReturn(0);
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using static Brovan.Core.Helpers.BinaryHelpers;
|
||||
|
||||
namespace Brovan.Core.Emulation.OS.Windows.Win32k
|
||||
{
|
||||
internal class NtUserGetDpiForMonitor : IWinSyscall
|
||||
{
|
||||
public NTSTATUS Handle(BinaryEmulator Instance)
|
||||
{
|
||||
ulong DpiXPtr = Instance.WinHelper.GetArg(2);
|
||||
ulong DpiYPtr = Instance.WinHelper.GetArg(3);
|
||||
|
||||
if (DpiXPtr != 0 && Instance.IsRegionMapped(DpiXPtr, 4))
|
||||
Instance._emulator.WriteMemory(DpiXPtr, Win32kHelper.DEFAULT_SCREEN_DPI, 4);
|
||||
|
||||
if (DpiYPtr != 0 && Instance.IsRegionMapped(DpiYPtr, 4))
|
||||
Instance._emulator.WriteMemory(DpiYPtr, Win32kHelper.DEFAULT_SCREEN_DPI, 4);
|
||||
|
||||
Instance.SetRawSyscallReturn(0);
|
||||
return NTSTATUS.STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,9 @@ namespace Brovan.Core.Emulation.OS.Windows
|
||||
STATUS_PRIVILEGED_INSTRUCTION = 0xC0000096,
|
||||
STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094,
|
||||
STATUS_INTEGER_OVERFLOW = 0xC0000095,
|
||||
STATUS_ARRAY_BOUNDS_EXCEEDED = 0xC000008C,
|
||||
STATUS_FLOAT_INVALID_OPERATION = 0xC0000090,
|
||||
STATUS_FLOAT_MULTIPLE_TRAPS = 0xC00002B5,
|
||||
STATUS_MEMORY_NOT_ALLOCATED = 0xC00000A0,
|
||||
STATUS_CANT_TERMINATE_SELF = 0xC00000DB,
|
||||
STATUS_DEBUGGER_INACTIVE = 0xC0000354,
|
||||
|
||||
@@ -954,15 +954,24 @@ namespace Brovan.Core.Emulation.OS.Windows
|
||||
|
||||
public uint GetArg32(int Index) => (uint)GetArg(Index);
|
||||
|
||||
private uint SequentialIdCursor = 30000;
|
||||
|
||||
public uint GenerateRandomPID()
|
||||
{
|
||||
uint PID = (uint)RandomGen.Next(500, 29999);
|
||||
if (PIDs.TryGetValue(PID, out _))
|
||||
for (int Attempt = 0; Attempt < 64; Attempt++)
|
||||
{
|
||||
return GenerateRandomPID();
|
||||
uint Candidate = (uint)RandomGen.Next(500, 29999);
|
||||
if (PIDs.TryAdd(Candidate, true))
|
||||
return Candidate;
|
||||
}
|
||||
|
||||
// Long-running guests exhaust the random pool, so hand out sequential ids instead of retrying.
|
||||
while (true)
|
||||
{
|
||||
uint Candidate = SequentialIdCursor++;
|
||||
if (Candidate != 0 && PIDs.TryAdd(Candidate, true))
|
||||
return Candidate;
|
||||
}
|
||||
PIDs.Add(PID, true);
|
||||
return PID;
|
||||
}
|
||||
|
||||
private User GenerateRandomSvchostUser()
|
||||
@@ -1196,7 +1205,15 @@ namespace Brovan.Core.Emulation.OS.Windows
|
||||
private ushort NextGdiHandleUniq = 0x77;
|
||||
private const int SmCxScreen = 0;
|
||||
private const int SmCyScreen = 1;
|
||||
private const int EnumCurrentSettings = -1;
|
||||
private const ushort DevModeSize = 0xDC;
|
||||
private const int DevModeOffsetSize = 0x44;
|
||||
private const int DevModeOffsetDisplayFrequency = 0xB8;
|
||||
private const uint DefaultDisplayFrequency = 60;
|
||||
private uint PrimaryDisplayFrequency;
|
||||
private const byte UserHandleTypeWindow = 1;
|
||||
private const byte UserHandleTypeMonitor = 0xC;
|
||||
private ulong UserPrimaryMonitorHandle;
|
||||
private ushort NextUserHandleIndex = 1;
|
||||
private ushort NextUserHandleUniq = 1;
|
||||
private ulong UserServerInfoAddress;
|
||||
@@ -2575,6 +2592,171 @@ namespace Brovan.Core.Emulation.OS.Windows
|
||||
return true;
|
||||
}
|
||||
|
||||
private ulong GuestCallTrampoline;
|
||||
private const ulong GuestCallTrampolineSize = 0x1000;
|
||||
private const int GuestCallResultOffset = 0x00;
|
||||
private const int GuestCallScratchOffset = 0x08;
|
||||
private const int GuestCallCodeOffset = 0x20;
|
||||
|
||||
private bool TryGetNtSyscallNumber(string Name, out uint Number)
|
||||
{
|
||||
Number = 0;
|
||||
WinModule Ntdll = WinModules.FirstOrDefault(m => m != null && m.Name != null && m.Name.Equals("ntdll.dll", StringComparison.OrdinalIgnoreCase));
|
||||
if (Ntdll == null)
|
||||
return false;
|
||||
|
||||
ulong Stub = GetExportAddress(Ntdll, Name);
|
||||
if (Stub == 0 || !Emulator.IsRegionMapped(Stub, 8))
|
||||
return false;
|
||||
|
||||
// mov r10, rcx ; mov eax, imm32
|
||||
if (Emulator.ReadMemoryUInt(Stub) != 0xB8D18B4Cu)
|
||||
return false;
|
||||
|
||||
Number = Emulator.ReadMemoryUInt(Stub + 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the page a guest callback returns to. Its code hands the stored result back through
|
||||
/// NtCallbackReturn, which unwinds the frame pushed by BeginGuestCall.
|
||||
/// </summary>
|
||||
private ulong EnsureGuestCallTrampoline()
|
||||
{
|
||||
if (GuestCallTrampoline != 0 && Emulator.IsRegionMapped(GuestCallTrampoline, GuestCallTrampolineSize))
|
||||
return GuestCallTrampoline;
|
||||
|
||||
if (!TryGetNtSyscallNumber("NtCallbackReturn", out uint CallbackReturn))
|
||||
return 0;
|
||||
|
||||
ulong Page = Emulator.MapUniqueAddress(GuestCallTrampolineSize, MemoryProtection.All);
|
||||
if (Page == 0 || !WriteZeroMemory(Page, (uint)GuestCallTrampolineSize))
|
||||
return 0;
|
||||
|
||||
Span<byte> Code = stackalloc byte[26];
|
||||
int At = 0;
|
||||
|
||||
Code[At++] = 0x48; Code[At++] = 0x8D; Code[At++] = 0x0D;
|
||||
int Displacement = GuestCallResultOffset - (GuestCallCodeOffset + 7);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(Code.Slice(At, 4), Displacement);
|
||||
At += 4;
|
||||
|
||||
Code[At++] = 0x49; Code[At++] = 0x89; Code[At++] = 0xCA;
|
||||
Code[At++] = 0xBA;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(Code.Slice(At, 4), 8u);
|
||||
At += 4;
|
||||
|
||||
Code[At++] = 0x45; Code[At++] = 0x31; Code[At++] = 0xC0;
|
||||
Code[At++] = 0xB8;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(Code.Slice(At, 4), CallbackReturn);
|
||||
At += 4;
|
||||
|
||||
Code[At++] = 0x0F; Code[At++] = 0x05;
|
||||
Code[At++] = 0xCC;
|
||||
|
||||
if (!Emulator._emulator.WriteMemory(Page + GuestCallCodeOffset, Code.Slice(0, At)))
|
||||
return 0;
|
||||
|
||||
GuestCallTrampoline = Page;
|
||||
return GuestCallTrampoline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls a guest function from inside a syscall and resumes the syscall's caller afterwards with
|
||||
/// ResultValue in RAX. Used for Win32 APIs whose kernel side calls back into user code.
|
||||
/// </summary>
|
||||
public ulong EnsureGuestCallScratch()
|
||||
{
|
||||
ulong Page = EnsureGuestCallTrampoline();
|
||||
return Page == 0 ? 0 : Page + GuestCallScratchOffset;
|
||||
}
|
||||
|
||||
public bool BeginGuestCall(ulong Function, ulong Arg0, ulong Arg1, ulong Arg2, ulong Arg3, ulong ResultValue)
|
||||
{
|
||||
if (PointerSize != 8 || Function == 0 || !Emulator.IsRegionMapped(Function, 1))
|
||||
return false;
|
||||
|
||||
EmulatedThread Thread = Emulator.CurrentThread;
|
||||
if (Thread == null)
|
||||
return false;
|
||||
|
||||
ulong Page = EnsureGuestCallTrampoline();
|
||||
if (Page == 0)
|
||||
return false;
|
||||
|
||||
Emulator._emulator.WriteMemory(Page + GuestCallResultOffset, ResultValue, 8);
|
||||
|
||||
ulong CurrentRsp = Emulator.ReadRegister(Registers.UC_X86_REG_RSP);
|
||||
WinUserCallbackFrame Frame = new WinUserCallbackFrame
|
||||
{
|
||||
SavedRsp = CurrentRsp,
|
||||
SavedReturnAddress = Emulator.ReadMemoryULong(CurrentRsp),
|
||||
};
|
||||
WinEmulatedThread.GetState(Thread).UserCallbackFrames.Push(Frame);
|
||||
|
||||
ulong CallRsp = ((CurrentRsp - 0x200) & ~0xFUL) - 8;
|
||||
Emulator._emulator.WriteMemory(CallRsp, Page + GuestCallCodeOffset, 8);
|
||||
|
||||
Emulator.WriteRegister(Registers.UC_X86_REG_RCX, Arg0);
|
||||
Emulator.WriteRegister(Registers.UC_X86_REG_RDX, Arg1);
|
||||
Emulator.WriteRegister(Registers.UC_X86_REG_R8, Arg2);
|
||||
Emulator.WriteRegister(Registers.UC_X86_REG_R9, Arg3);
|
||||
Emulator.WriteRegister(Registers.UC_X86_REG_RSP, CallRsp);
|
||||
Emulator.WriteRegister(Emulator.IPRegister, Function - 2);
|
||||
Emulator.SuppressSyscallStatusWrite = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public ulong GetPrimaryMonitorHandle()
|
||||
{
|
||||
ulong Monitor = EnsureUserPrimaryMonitor();
|
||||
if (Monitor == 0)
|
||||
return 0;
|
||||
|
||||
if (UserPrimaryMonitorHandle != 0)
|
||||
return UserPrimaryMonitorHandle;
|
||||
|
||||
if (!EnsureUserSharedInfo(out _, out ulong HandleTable, out uint EntrySize))
|
||||
return 0;
|
||||
|
||||
ulong Handle = AllocateUserHandle();
|
||||
if (Handle == 0)
|
||||
return 0;
|
||||
|
||||
ushort Index = (ushort)(Handle & 0xFFFF);
|
||||
ushort Uniq = (ushort)((Handle >> 16) & 0x7FFF);
|
||||
|
||||
ulong Entry = HandleTable + (ulong)Index * EntrySize;
|
||||
Span<byte> Data = Shared.GetSpan(0x20);
|
||||
Data.Slice(0, 0x20).Clear();
|
||||
WriteU64(Data, 0x00, Monitor);
|
||||
WriteU64(Data, 0x08, EnsureCurrentUserThreadInfo());
|
||||
WriteU64(Data, 0x10, UserDesktopOwnerAddress);
|
||||
Data[0x18] = UserHandleTypeMonitor;
|
||||
Data[0x19] = 0;
|
||||
WriteU16(Data, 0x1A, Uniq);
|
||||
|
||||
if (!Emulator._emulator.WriteMemory(Entry, Data.Slice(0, 0x20)))
|
||||
return 0;
|
||||
|
||||
UserPrimaryMonitorHandle = Handle;
|
||||
return UserPrimaryMonitorHandle;
|
||||
}
|
||||
|
||||
public bool TryGetPrimaryMonitorRect(out int Left, out int Top, out int Right, out int Bottom)
|
||||
{
|
||||
Left = Top = Right = Bottom = 0;
|
||||
ulong Monitor = EnsureUserPrimaryMonitor();
|
||||
if (Monitor == 0)
|
||||
return false;
|
||||
|
||||
Left = (int)Emulator.ReadMemoryUInt(Monitor + 0x1C);
|
||||
Top = (int)Emulator.ReadMemoryUInt(Monitor + 0x20);
|
||||
Right = (int)Emulator.ReadMemoryUInt(Monitor + 0x24);
|
||||
Bottom = (int)Emulator.ReadMemoryUInt(Monitor + 0x28);
|
||||
return Right > Left && Bottom > Top;
|
||||
}
|
||||
|
||||
public bool CompleteUserCallback(ulong ResultAddress, uint ResultLength)
|
||||
{
|
||||
EmulatedThread Thread = Emulator.CurrentThread;
|
||||
@@ -3424,6 +3606,39 @@ namespace Brovan.Core.Emulation.OS.Windows
|
||||
return (1920, 1080);
|
||||
}
|
||||
|
||||
public unsafe uint GetPrimaryDisplayFrequency()
|
||||
{
|
||||
if (PrimaryDisplayFrequency != 0)
|
||||
return PrimaryDisplayFrequency;
|
||||
|
||||
PrimaryDisplayFrequency = DefaultDisplayFrequency;
|
||||
|
||||
try
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
Span<byte> DevMode = stackalloc byte[DevModeSize];
|
||||
DevMode.Clear();
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(DevMode.Slice(DevModeOffsetSize), DevModeSize);
|
||||
|
||||
fixed (byte* Buffer = DevMode)
|
||||
{
|
||||
if (NativeWinImports.EnumDisplaySettingsW(null, EnumCurrentSettings, Buffer))
|
||||
{
|
||||
uint Frequency = BinaryPrimitives.ReadUInt32LittleEndian(DevMode.Slice(DevModeOffsetDisplayFrequency));
|
||||
if (Frequency > 1)
|
||||
PrimaryDisplayFrequency = Frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return PrimaryDisplayFrequency;
|
||||
}
|
||||
|
||||
public ulong EnsureUserDesktopInfo()
|
||||
{
|
||||
if (UserDesktopInfoAddress != 0 && Emulator.IsRegionMapped(UserDesktopInfoAddress, UserDesktopInfoSize))
|
||||
|
||||
@@ -138,6 +138,9 @@ namespace Brovan
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetSystemMetrics(int nIndex);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern unsafe bool EnumDisplaySettingsW(string lpszDeviceName, int iModeNum, byte* lpDevMode);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetSystemMetricsForDpi(int nIndex, uint dpi);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user