Fix 32-bit thread context API selection in DllInjector
Because DllInjector enforces matching host/target bitness, a 32-bit caller always handles a 32-bit target. The correct API is native GetThreadContext/SetThreadContext with Context32; the Wow64 APIs are only for 64-bit processes inspecting WOW64 targets, which never happens here. - Collapse the 32-bit path to always use GetThreadContext/SetThreadContext. - Remove the now-unused Wow64GetThreadContext/Wow64SetThreadContext declarations. - Update Context32 doc comment to describe the x86 usage. - ExternalReader access guard now also accepts QueryLimitedInformation. Tests: 207 passing, 4 skipped.
This commit is contained in:
@@ -37,10 +37,11 @@ public sealed class ExternalReader : MemoryBase
|
|||||||
public ExternalReader(System.Diagnostics.Process process, ProcessAccess desiredAccess = DefaultAccess)
|
public ExternalReader(System.Diagnostics.Process process, ProcessAccess desiredAccess = DefaultAccess)
|
||||||
{
|
{
|
||||||
_processId = process.Id;
|
_processId = process.Id;
|
||||||
if ((desiredAccess & ProcessAccess.QueryInformation) == 0)
|
const ProcessAccess queryAccess = ProcessAccess.QueryInformation | ProcessAccess.QueryLimitedInformation;
|
||||||
|
if ((desiredAccess & queryAccess) == 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"ExternalReader requires ProcessAccess.QueryInformation to determine target bitness.",
|
"ExternalReader requires ProcessAccess.QueryInformation or ProcessAccess.QueryLimitedInformation to determine target bitness.",
|
||||||
nameof(desiredAccess));
|
nameof(desiredAccess));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ public sealed class DllInjector
|
|||||||
{
|
{
|
||||||
IntPtr result;
|
IntPtr result;
|
||||||
|
|
||||||
if (Environment.Is64BitProcess)
|
if (_currentIs64Bit)
|
||||||
{
|
{
|
||||||
var originalContext = new Context64 { ContextFlags = ContextFlags.Amd64Full };
|
var originalContext = new Context64 { ContextFlags = ContextFlags.Amd64Full };
|
||||||
if (!NativeMethods.GetThreadContext(thread, ref originalContext))
|
if (!NativeMethods.GetThreadContext(thread, ref originalContext))
|
||||||
@@ -264,32 +264,23 @@ public sealed class DllInjector
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 32-bit process on either a 32-bit or 64-bit (WOW64) host.
|
// 32-bit process targeting a 32-bit process. The target context is
|
||||||
bool useWow64 = Environment.Is64BitOperatingSystem;
|
// a native x86 CONTEXT; the WOW64 APIs are for 64-bit callers only.
|
||||||
|
|
||||||
var originalContext = new Context32 { ContextFlags = ContextFlags.X86Full };
|
var originalContext = new Context32 { ContextFlags = ContextFlags.X86Full };
|
||||||
bool gotContext = useWow64
|
if (!NativeMethods.GetThreadContext(thread, ref originalContext))
|
||||||
? NativeMethods.Wow64GetThreadContext(thread, ref originalContext)
|
|
||||||
: NativeMethods.GetThreadContext(thread, ref originalContext);
|
|
||||||
if (!gotContext)
|
|
||||||
{
|
{
|
||||||
int error = Marshal.GetLastPInvokeError();
|
int error = Marshal.GetLastPInvokeError();
|
||||||
string api = useWow64 ? "Wow64GetThreadContext" : "GetThreadContext";
|
throw new InvalidOperationException($"GetThreadContext failed (error {error}).");
|
||||||
throw new InvalidOperationException($"{api} failed (error {error}).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var redirectContext = originalContext;
|
var redirectContext = originalContext;
|
||||||
redirectContext.Eip = (uint)(nint)remoteBase;
|
redirectContext.Eip = (uint)(nint)remoteBase;
|
||||||
redirectContext.Esp = (uint)(nint)stackTop;
|
redirectContext.Esp = (uint)(nint)stackTop;
|
||||||
|
|
||||||
bool setContext = useWow64
|
if (!NativeMethods.SetThreadContext(thread, ref redirectContext))
|
||||||
? NativeMethods.Wow64SetThreadContext(thread, ref redirectContext)
|
|
||||||
: NativeMethods.SetThreadContext(thread, ref redirectContext);
|
|
||||||
if (!setContext)
|
|
||||||
{
|
{
|
||||||
int error = Marshal.GetLastPInvokeError();
|
int error = Marshal.GetLastPInvokeError();
|
||||||
string api = useWow64 ? "Wow64SetThreadContext" : "SetThreadContext";
|
throw new InvalidOperationException($"SetThreadContext failed (error {error}).");
|
||||||
throw new InvalidOperationException($"{api} failed (error {error}).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NativeMethods.ResumeThread(thread) == 0xFFFFFFFF)
|
if (NativeMethods.ResumeThread(thread) == 0xFFFFFFFF)
|
||||||
@@ -306,14 +297,10 @@ public sealed class DllInjector
|
|||||||
throw new InvalidOperationException($"SuspendThread failed while capturing result (error {error}).");
|
throw new InvalidOperationException($"SuspendThread failed while capturing result (error {error}).");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool restoredContext = useWow64
|
if (!NativeMethods.SetThreadContext(thread, ref originalContext))
|
||||||
? NativeMethods.Wow64SetThreadContext(thread, ref originalContext)
|
|
||||||
: NativeMethods.SetThreadContext(thread, ref originalContext);
|
|
||||||
if (!restoredContext)
|
|
||||||
{
|
{
|
||||||
int error = Marshal.GetLastPInvokeError();
|
int error = Marshal.GetLastPInvokeError();
|
||||||
string api = useWow64 ? "Wow64SetThreadContext" : "SetThreadContext";
|
throw new InvalidOperationException($"SetThreadContext restore failed (error {error}).");
|
||||||
throw new InvalidOperationException($"{api} restore failed (error {error}).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NativeMethods.ResumeThread(thread) == 0xFFFFFFFF)
|
if (NativeMethods.ResumeThread(thread) == 0xFFFFFFFF)
|
||||||
|
|||||||
@@ -152,19 +152,6 @@ internal static partial class NativeMethods
|
|||||||
SafeMemoryHandle thread,
|
SafeMemoryHandle thread,
|
||||||
ref Context32 context);
|
ref Context32 context);
|
||||||
|
|
||||||
/// <summary>Sets a 32-bit (WOW64) thread context.</summary>
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static partial bool Wow64SetThreadContext(
|
|
||||||
SafeMemoryHandle thread,
|
|
||||||
ref Context32 context);
|
|
||||||
|
|
||||||
/// <summary>Gets a 32-bit (WOW64) thread context.</summary>
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static partial bool Wow64GetThreadContext(
|
|
||||||
SafeMemoryHandle thread,
|
|
||||||
ref Context32 context);
|
|
||||||
|
|
||||||
// ── Modules ──────────────────────────────────────────────────────────────
|
// ── Modules ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ public unsafe struct FloatingSaveArea32
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A 32-bit (x86/WOW64) thread context. Use it with
|
/// A 32-bit x86 thread context. Use it with <c>GetThreadContext</c> and
|
||||||
/// <c>Wow64GetThreadContext</c> and <c>Wow64SetThreadContext</c> to inspect a 32-bit thread.
|
/// <c>SetThreadContext</c> from a 32-bit process targeting a 32-bit thread.
|
||||||
/// The total size is 716 bytes.
|
/// The total size is 716 bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
|||||||
Reference in New Issue
Block a user