Fix InputSimulator wParam, PeHeaderParser double-parse, and other review issues
Bug fixes: - InputSimulator: Pass correct button state (MK_LBUTTON/MK_RBUTTON) in wParam for button-down messages instead of 0. - PeHeaderParser: ParseOptionalHeader now reads only the optional header, not section headers (fixes double-parse waste). - EntryPoint: Removed useless isPe32Plus branch (AddressOfEntryPoint is at offset 16 in both PE32 and PE32+). - RemoteWindow: Handle null foreground window case in Activate to avoid calling GetWindowThreadProcessId with HWND 0. - RemotePointer: Remove dead null-conditional operators (encoding ??) since encoding is non-nullable. Constants added: - SystemMethods: MkLButton (0x0001) and MkRButton (0x0002) for mouse button state flags. Tests: 199 passing, 4 integration/interactive skipped.
This commit is contained in:
@@ -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
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -35,15 +35,16 @@ public sealed class RemotePointer
|
||||
/// <summary>Reads a null-terminated string at <c>BaseAddress + offset</c>.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Writes a null-terminated string at <c>BaseAddress + offset</c>.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Returns a new <see cref="RemotePointer"/> with the offset added.</summary>
|
||||
public RemotePointer this[nint offset] => new RemotePointer(_memory, BaseAddress + offset);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user