Address review: forwarder split, CreateDelegate guard, API-set docs
- PeHeaderParser: split export forwarders on the FIRST dot (IndexOf), not the last. A forwarder is "Module.Function" and the module name has no extension, so the last-dot split misparsed export names that themselves contain a dot. - PeHeaderParser: document that API-set (api-ms-win-*/ext-ms-*) and ordinal forwarders are unsupported and should be resolved via the OS loader. - RemoteFunction.CreateDelegate now throws InvalidOperationException unless the session is in-process; an external target's address is not host-mapped and a delegate to it would access-violate on invocation. Tests cover both paths. - Reword the SSE-payload comment: the 16-byte scratch sits below the saved return address, which the aligned store leaves intact (it never overwrote it). Tests: 223 passing, 4 skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,8 +38,9 @@ public sealed class RemoteThreadExecutorTests
|
||||
// Five-arg callee that also executes an alignment-sensitive SSE instruction, proving
|
||||
// the stub delivers a 16-byte-aligned stack the CPU actually accepts (movaps #GPs on a
|
||||
// misaligned address) alongside correct register+stack argument placement.
|
||||
// sub rsp, 24 ; entry rsp ≡ 8 (mod 16) -> rsp ≡ 0 (16-aligned), 16-byte
|
||||
// ; scratch at [rsp..rsp+16) that clears the return slot ([rsp+24])
|
||||
// sub rsp, 24 ; entry rsp ≡ 8 (mod 16) -> rsp ≡ 0 (16-aligned), giving a
|
||||
// ; 16-byte aligned scratch at [rsp..rsp+16) below the saved
|
||||
// ; return address ([rsp+24]) so the store leaves it intact
|
||||
// movaps [rsp], xmm0 ; aligned 16-byte store — faults unless rsp is 16-aligned
|
||||
// add rsp, 24 ; restore
|
||||
// mov eax, ecx
|
||||
|
||||
@@ -79,6 +79,32 @@ public class ModuleFunctionTests
|
||||
Assert.Throws<InvalidOperationException>(() => magic["kernel32"]["NoSuchExport_ZZZ"]);
|
||||
}
|
||||
|
||||
private delegate uint GetCurrentProcessIdDelegate();
|
||||
|
||||
[Fact]
|
||||
public void CreateDelegate_throws_for_external_session()
|
||||
{
|
||||
Load("kernel32.dll");
|
||||
|
||||
// External reader (even to self): the address is not treated as host-mapped, so a
|
||||
// delegate to it is rejected rather than handed back to AV on invocation.
|
||||
using var magic = Magic.Open(Process.GetCurrentProcess());
|
||||
RemoteFunction fn = magic["kernel32"]["GetCurrentProcessId"];
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => fn.CreateDelegate<GetCurrentProcessIdDelegate>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDelegate_invokes_function_in_process()
|
||||
{
|
||||
Load("kernel32.dll");
|
||||
|
||||
using var magic = Magic.OpenInProcess();
|
||||
var getPid = magic["kernel32"]["GetCurrentProcessId"].CreateDelegate<GetCurrentProcessIdDelegate>();
|
||||
|
||||
Assert.Equal((uint)Process.GetCurrentProcess().Id, getPid());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolved_function_executes_via_remote_thread()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user