Fix thread namespace collision and tighten executable stub allocation

Fully qualifies System.Threading.Thread in DllInjector after introducing the WhiteMagic.Thread namespace, and replaces the broken+too-small near-allocation loop with a symmetric +/-2 GiB search so the x64 call stub always lands within rel32 range.
This commit is contained in:
kbe
2026-07-22 16:04:41 +02:00
parent 9aef9c21e3
commit 8f988768fe
3 changed files with 35 additions and 38 deletions
+22 -26
View File
@@ -472,27 +472,22 @@ public sealed class RemoteThreadExecutor
nuint mask = AllocationGranularity - (nuint)1; nuint mask = AllocationGranularity - (nuint)1;
nuint aligned = (preferred + AllocationGranularity - (nuint)1) & ~mask; nuint aligned = (preferred + AllocationGranularity - (nuint)1) & ~mask;
for (int i = 0; i < NearAllocationAttempts; i++) for (long delta = 0; delta <= (long)0x7FFF; delta++)
{ {
nuint candidate; long signedOffset = delta * (long)AllocationGranularity;
if (i == 0)
{
candidate = aligned;
}
else if ((i & 1) == 1)
{
candidate = aligned + (nuint)i * AllocationGranularity;
}
else
{
nuint offset = (nuint)i * AllocationGranularity;
if (offset > aligned)
{
continue;
}
candidate = aligned - offset; // Try above, then below the target. Keep the original address as the first attempt.
} for (int sign = 0; sign < 2; sign++)
{
if (delta == 0 && sign != 0)
continue;
long offset = sign == 0 ? signedOffset : -signedOffset;
nuint candidate = (nuint)((long)aligned + offset);
// Avoid underflow to zero on below-target search.
if (offset < 0 && candidate >= aligned)
continue;
IntPtr result = NativeMethods.VirtualAllocEx( IntPtr result = NativeMethods.VirtualAllocEx(
handle, handle,
@@ -503,15 +498,16 @@ public sealed class RemoteThreadExecutor
if (result != IntPtr.Zero) if (result != IntPtr.Zero)
{ {
long distance = (long)(nuint)(nint)result - (long)(nuint)(nint)preferredAddress;
if (distance >= int.MinValue && distance <= int.MaxValue)
return result; return result;
// The allocator gave us a nearby candidate but on the wrong side
// of the 2 GiB boundary; treat it as unusable and keep searching.
NativeMethods.VirtualFreeEx(handle, result, 0, MemoryFreeType.Release);
}
} }
} }
return NativeMethods.VirtualAllocEx( return IntPtr.Zero; }
handle,
IntPtr.Zero,
size,
MemoryAllocationType.Commit | MemoryAllocationType.Reserve,
MemoryProtectionType.ExecuteReadWrite);
}
} }
+1 -1
View File
@@ -383,7 +383,7 @@ public sealed class DllInjector
if (value != IntPtr.Zero) if (value != IntPtr.Zero)
return value; return value;
Thread.Sleep(5); System.Threading.Thread.Sleep(5);
} }
return IntPtr.Zero; return IntPtr.Zero;
+3 -2
View File
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Thread = System.Threading.Thread;
using WhiteMagic; using WhiteMagic;
using WhiteMagic.Injection; using WhiteMagic.Injection;
using WhiteMagic.Native; using WhiteMagic.Native;
@@ -69,7 +70,7 @@ public class DllInjectorTests
int osThreadId = 0; int osThreadId = 0;
Exception? threadError = null; Exception? threadError = null;
var helper = new Thread(() => var helper = new System.Threading.Thread(() =>
{ {
try try
{ {
@@ -80,7 +81,7 @@ public class DllInjectorTests
// also be stopped once its original context is restored. // also be stopped once its original context is restored.
while (!stopEvent.IsSet) while (!stopEvent.IsSet)
{ {
Thread.Sleep(10); System.Threading.Thread.Sleep(10);
} }
} }
catch (Exception ex) catch (Exception ex)