Files
whitemagic/WhiteMagicTest/Discovery/PeHeaderParserTests.cs
kbe 3f0bea6bd4 Implement core diagnostic memory layer, execution helpers, and high-level facade slices
Implemented:
- Core: UTF-16 ReadString boundary/alignment fix, target bitness and process id on MemoryBase
- function interception: PatchManager, DetourManager, InstructionAnalyzer, MainThreadDispatcher
- Execution: BackgroundTaskExecutor, InProcessInvoker
- High-level: Magic facade, RemotePointer, async wrappers
- Discovery/external code loading/Window groundwork (PEB/TEB, pattern scanning, raw allocations, DLL external code loading, window/input)

Tests: 180 passing, 4 integration/interactive tests skipped.
2026-07-21 23:43:14 +02:00

157 lines
4.9 KiB
C#

using WhiteMagic;
using WhiteMagic.Discovery;
namespace WhiteMagicTest.Discovery;
/// <summary>
/// Tests for <see cref="PeHeaderParser"/>.
/// </summary>
public class PeHeaderParserTests
{
private static InProcessReader CreateReader()
{
return new InProcessReader();
}
[Fact]
public void EntryPoint_returns_nonzero_for_current_module()
{
using var reader = CreateReader();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var mainModule = currentProcess.MainModule;
Assert.NotNull(mainModule);
var parser = new PeHeaderParser(reader, mainModule.BaseAddress);
IntPtr entryPoint = parser.EntryPoint;
// Entry point should be a valid RVA (non-zero for a valid PE)
Assert.NotEqual(IntPtr.Zero, entryPoint);
// Entry point should be less than module size
Assert.True((nint)entryPoint < mainModule.ModuleMemorySize);
}
[Fact]
public void Sections_enumerates_at_least_text_section()
{
using var reader = CreateReader();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var mainModule = currentProcess.MainModule;
Assert.NotNull(mainModule);
var parser = new PeHeaderParser(reader, mainModule.BaseAddress);
var sections = parser.Sections.ToList();
Assert.NotEmpty(sections);
// Every PE file should have a .text section (or similar)
var textSection = sections.FirstOrDefault(s =>
s.Name.Equals(".text", StringComparison.OrdinalIgnoreCase) ||
s.Name.Equals("TEXT", StringComparison.OrdinalIgnoreCase));
// May not find ".text" exactly, but should have at least some sections
Assert.True(sections.Count >= 1);
}
[Fact]
public void Sections_have_valid_properties()
{
using var reader = CreateReader();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var mainModule = currentProcess.MainModule;
Assert.NotNull(mainModule);
var parser = new PeHeaderParser(reader, mainModule.BaseAddress);
var sections = parser.Sections.ToList();
foreach (var section in sections)
{
// Name should not be empty
Assert.False(string.IsNullOrWhiteSpace(section.Name));
// Virtual address should be within module bounds
Assert.True((nint)section.VirtualAddress < mainModule.ModuleMemorySize);
// Virtual size should be positive
Assert.True(section.VirtualSize > 0);
}
}
[Fact]
public void Sections_have_common_names()
{
using var reader = CreateReader();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var mainModule = currentProcess.MainModule;
Assert.NotNull(mainModule);
var parser = new PeHeaderParser(reader, mainModule.BaseAddress);
var sections = parser.Sections.Select(s => s.Name).ToList();
// At least some common section names should be present
var commonNames = new[] { ".text", ".data", ".rdata", ".bss" };
bool hasCommonSection = commonNames.Any(name =>
sections.Contains(name, StringComparer.OrdinalIgnoreCase));
// This might not always be true, but for managed EXEs it usually is
// We'll just verify sections were enumerated
Assert.NotEmpty(sections);
}
[Fact]
public void EntryPoint_is_consistent_across_calls()
{
using var reader = CreateReader();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var mainModule = currentProcess.MainModule;
Assert.NotNull(mainModule);
var parser = new PeHeaderParser(reader, mainModule.BaseAddress);
IntPtr first = parser.EntryPoint;
IntPtr second = parser.EntryPoint;
Assert.Equal(first, second);
}
[Fact]
public void Sections_are_consistent_across_calls()
{
using var reader = CreateReader();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
var mainModule = currentProcess.MainModule;
Assert.NotNull(mainModule);
var parser = new PeHeaderParser(reader, mainModule.BaseAddress);
var first = parser.Sections.ToList();
var second = parser.Sections.ToList();
Assert.Equal(first.Count, second.Count);
for (int i = 0; i < first.Count; i++)
{
Assert.Equal(first[i].Name, second[i].Name);
Assert.Equal(first[i].VirtualAddress, second[i].VirtualAddress);
Assert.Equal(first[i].VirtualSize, second[i].VirtualSize);
}
}
[Fact]
public void Constructor_throws_on_zero_base_address()
{
using var reader = CreateReader();
var ex = Assert.Throws<ArgumentException>(() =>
new PeHeaderParser(reader, IntPtr.Zero));
Assert.Contains("Base address cannot be zero", ex.Message);
}
}