From 0c2b8cf26b077e921437423902f07920cb847d60 Mon Sep 17 00:00:00 2001 From: AdvDebug <90452585+AdvDebug@users.noreply.github.com> Date: Sat, 25 Jul 2026 11:33:14 +0300 Subject: [PATCH] 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. --- Brovan.Generators/VulkanForwardGenerator.cs | 12 +- .../OS/Windows/Devices/BrovVulkGenStruct.cs | 6 +- .../Win32k/NtUserEnumDisplayMonitors.cs | 47 ++++ .../Win32k/NtUserEnumDisplaySettings.cs | 75 ++++++ .../NtUserGetDisplayConfigBufferSizes.cs | 24 ++ .../Windows/Win32k/NtUserGetDpiForMonitor.cs | 22 ++ .../OS/Windows/WinHelperConstants.cs | 3 + .../Emulation/OS/Windows/WinSyscallsHelper.cs | 225 +++++++++++++++++- Brovan/GeneralHelper.cs | 3 + 9 files changed, 406 insertions(+), 11 deletions(-) create mode 100644 Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplayMonitors.cs create mode 100644 Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplaySettings.cs create mode 100644 Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDisplayConfigBufferSizes.cs create mode 100644 Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDpiForMonitor.cs diff --git a/Brovan.Generators/VulkanForwardGenerator.cs b/Brovan.Generators/VulkanForwardGenerator.cs index ae8120a..1575279 100644 --- a/Brovan.Generators/VulkanForwardGenerator.cs +++ b/Brovan.Generators/VulkanForwardGenerator.cs @@ -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 KindNum = new Dictionary @@ -816,7 +818,7 @@ namespace Brovan.Generators private static MDesc ClassifyMember(Model m, VkStruct owner, Member mem, Dictionary offsets, Dictionary 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) diff --git a/Brovan/Core/Emulation/OS/Windows/Devices/BrovVulkGenStruct.cs b/Brovan/Core/Emulation/OS/Windows/Devices/BrovVulkGenStruct.cs index 2df8d59..3152d9d 100644 --- a/Brovan/Core/Emulation/OS/Windows/Devices/BrovVulkGenStruct.cs +++ b/Brovan/Core/Emulation/OS/Windows/Devices/BrovVulkGenStruct.cs @@ -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) diff --git a/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplayMonitors.cs b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplayMonitors.cs new file mode 100644 index 0000000..fb6bf56 --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplayMonitors.cs @@ -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; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplaySettings.cs b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplaySettings.cs new file mode 100644 index 0000000..2b415c3 --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserEnumDisplaySettings.cs @@ -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; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDisplayConfigBufferSizes.cs b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDisplayConfigBufferSizes.cs new file mode 100644 index 0000000..1acf0c9 --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDisplayConfigBufferSizes.cs @@ -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; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDpiForMonitor.cs b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDpiForMonitor.cs new file mode 100644 index 0000000..d01f64a --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Win32k/NtUserGetDpiForMonitor.cs @@ -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; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs b/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs index 3591ceb..b504c83 100644 --- a/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs +++ b/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs @@ -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, diff --git a/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs b/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs index 14d8a16..bdb64fb 100644 --- a/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs +++ b/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs @@ -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; + } + + /// + /// Builds the page a guest callback returns to. Its code hands the stored result back through + /// NtCallbackReturn, which unwinds the frame pushed by BeginGuestCall. + /// + 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 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; + } + + /// + /// 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. + /// + 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 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 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)) diff --git a/Brovan/GeneralHelper.cs b/Brovan/GeneralHelper.cs index 34d4d79..edf1a50 100644 --- a/Brovan/GeneralHelper.cs +++ b/Brovan/GeneralHelper.cs @@ -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); }