diff --git a/WhiteMagic/Discovery/PeHeaderParser.cs b/WhiteMagic/Discovery/PeHeaderParser.cs index 723ba70..bb24d2d 100644 --- a/WhiteMagic/Discovery/PeHeaderParser.cs +++ b/WhiteMagic/Discovery/PeHeaderParser.cs @@ -64,21 +64,8 @@ public sealed class PeHeaderParser if (optionalHeader is null) return IntPtr.Zero; - // Entry point is at different offsets for PE32 vs PE32+ - bool isPe32Plus = IsPe32Plus(); - - if (isPe32Plus) - { - // PE32+: AddressOfEntryPoint is at offset 16 in OPTIONAL_HEADER (64-bit) - return (IntPtr)BitConverter.ToUInt32( - optionalHeader.AsSpan(16, 4)); - } - else - { - // PE32: AddressOfEntryPoint is at offset 16 in OPTIONAL_HEADER (32-bit) - return (IntPtr)BitConverter.ToUInt32( - optionalHeader.AsSpan(16, 4)); - } + // Entry point RVA is at offset 16 in the optional header (both PE32 and PE32+) + return (IntPtr)BitConverter.ToUInt32(optionalHeader.AsSpan(16, 4)); } } @@ -185,7 +172,46 @@ public sealed class PeHeaderParser /// private (byte[]? OptionalHeader, byte[][]? SectionHeaders) ParseOptionalHeader() { - return ParseOptionalHeaderAndSectionHeaders(); + // Read DOS header (first 64 bytes) + byte[] dosHeader = _memory.ReadBytes(_baseAddress, 64); + if (dosHeader.Length < 64) + throw new InvalidDataException("Failed to read DOS header."); + + // Verify DOS signature "MZ" + if (dosHeader[0] != 0x4D || dosHeader[1] != 0x5A) + throw new InvalidDataException("Invalid DOS signature (not a PE file)."); + + // PE header offset is at 0x3C in DOS header + int peOffset = BitConverter.ToInt32(dosHeader, 0x3C); + if (peOffset < 0 || peOffset > 0x1000) // Sanity check + throw new InvalidDataException($"Invalid PE offset: {peOffset}"); + + // Read PE signature (4 bytes: "PE\0\0") + IntPtr peSigAddr = _baseAddress + peOffset; + byte[] peSignature = _memory.ReadBytes(peSigAddr, 4); + if (peSignature.Length < 4) + throw new InvalidDataException("Failed to read PE signature."); + + if (peSignature[0] != 0x50 || peSignature[1] != 0x45 || + peSignature[2] != 0x00 || peSignature[3] != 0x00) + throw new InvalidDataException("Invalid PE signature."); + + // COFF header follows PE signature (20 bytes) + IntPtr coffAddr = peSigAddr + 4; + byte[] coffHeader = _memory.ReadBytes(coffAddr, 20); + if (coffHeader.Length < 20) + throw new InvalidDataException("Failed to read COFF header."); + + // SizeOfOptionalHeader is at offset 16 in COFF header + ushort sizeOfOptionalHeader = BitConverter.ToUInt16(coffHeader, 16); + + // Optional header follows COFF header + IntPtr optAddr = coffAddr + 20; + byte[] optionalHeader = _memory.ReadBytes(optAddr, sizeOfOptionalHeader); + if (optionalHeader.Length < sizeOfOptionalHeader) + throw new InvalidDataException("Failed to read optional header."); + + return (optionalHeader, null); } /// diff --git a/WhiteMagic/Input/InputSimulator.cs b/WhiteMagic/Input/InputSimulator.cs index 667bf6d..197513c 100644 --- a/WhiteMagic/Input/InputSimulator.cs +++ b/WhiteMagic/Input/InputSimulator.cs @@ -53,16 +53,17 @@ public sealed class InputSimulator nint lParam = MakeLong(x, y); - (uint down, uint up) = button switch + (uint down, uint downWParam) = button switch { - MouseButton.Left => (NativeMethods.WmLButtonDown, NativeMethods.WmLButtonUp), - MouseButton.Right => (NativeMethods.WmRButtonDown, NativeMethods.WmRButtonUp), + MouseButton.Left => (NativeMethods.WmLButtonDown, NativeMethods.MkLButton), + MouseButton.Right => (NativeMethods.WmRButtonDown, NativeMethods.MkRButton), _ => throw new ArgumentOutOfRangeException(nameof(button)), }; - if (!NativeMethods.PostMessageW(hWnd, down, 0, lParam)) + if (!NativeMethods.PostMessageW(hWnd, down, downWParam, lParam)) return false; + uint up = button == MouseButton.Left ? NativeMethods.WmLButtonUp : NativeMethods.WmRButtonUp; return NativeMethods.PostMessageW(hWnd, up, 0, lParam); } diff --git a/WhiteMagic/Native/SystemMethods.cs b/WhiteMagic/Native/SystemMethods.cs index aec66d0..77637e2 100644 --- a/WhiteMagic/Native/SystemMethods.cs +++ b/WhiteMagic/Native/SystemMethods.cs @@ -114,6 +114,10 @@ internal static partial class NativeMethods internal const uint WmRButtonDown = 0x0204; internal const uint WmRButtonUp = 0x0205; + // Mouse button state flags for WM_*BUTTONDOWN messages + internal const uint MkLButton = 0x0001; + internal const uint MkRButton = 0x0002; + internal static readonly IntPtr HwndTop = IntPtr.Zero; internal const uint SwpShowWindow = 0x0040; diff --git a/WhiteMagic/RemotePointer.cs b/WhiteMagic/RemotePointer.cs index fae0bf8..1d5301a 100644 --- a/WhiteMagic/RemotePointer.cs +++ b/WhiteMagic/RemotePointer.cs @@ -35,15 +35,16 @@ public sealed class RemotePointer /// Reads a null-terminated string at BaseAddress + offset. public string ReadString(Encoding encoding, int maxLength = 512, nint offset = 0) { - return _memory.ReadString(BaseAddress + offset, encoding ?? Encoding.UTF8, maxLength); + return _memory.ReadString(BaseAddress + offset, encoding, maxLength); } /// Writes a null-terminated string at BaseAddress + offset. public bool WriteString(string value, Encoding encoding, nint offset = 0) { - return _memory.WriteString(BaseAddress + offset, value, encoding ?? Encoding.UTF8); + return _memory.WriteString(BaseAddress + offset, value, encoding); } + /// Returns a new with the offset added. public RemotePointer this[nint offset] => new RemotePointer(_memory, BaseAddress + offset); } diff --git a/WhiteMagic/Windows/RemoteWindow.cs b/WhiteMagic/Windows/RemoteWindow.cs index 7d8aad7..fbfead4 100644 --- a/WhiteMagic/Windows/RemoteWindow.cs +++ b/WhiteMagic/Windows/RemoteWindow.cs @@ -72,11 +72,16 @@ public sealed class RemoteWindow { IntPtr foreground = NativeMethods.GetForegroundWindow(); uint targetThread = NativeMethods.GetWindowThreadProcessId(Handle, out _); - uint foregroundThread = NativeMethods.GetWindowThreadProcessId(foreground, out _); if (targetThread == 0) return false; + // If there's no foreground window, or we're already in the foreground thread, just set it + if (foreground == IntPtr.Zero) + return NativeMethods.SetForegroundWindow(Handle); + + uint foregroundThread = NativeMethods.GetWindowThreadProcessId(foreground, out _); + if (targetThread == foregroundThread) return NativeMethods.SetForegroundWindow(Handle);