diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 0ff66a8a21..9b121889f9 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1285,6 +1285,7 @@ + diff --git a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs new file mode 100644 index 0000000000..9bab031cfd --- /dev/null +++ b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs @@ -0,0 +1,157 @@ +using PeNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace BizHawk.Emulation.Cores.Waterbox +{ + public class PeRunner + { + private static readonly ulong CanonicalStart = 0x0000036f00000000; + + public class PeWrapper + { + public Dictionary ExportsByOrdinal { get; } = new Dictionary(); + /// + /// ordinal only exports will not show up in this list! + /// + public Dictionary ExportsByName { get; } = new Dictionary(); + + public string ModuleName { get; } + + private readonly byte[] _fileData; + private readonly PeFile _pe; + + public ulong Size { get; } + public ulong Start { get; private set; } + + public long LoadOffset { get; private set; } + + public MemoryBlock Memory { get; private set; } + + public IntPtr EntryPoint { get; private set; } + + public PeWrapper(string moduleName, byte[] fileData) + { + ModuleName = moduleName; + _fileData = fileData; + _pe = new PeFile(fileData); + Size = _pe.ImageNtHeaders.OptionalHeader.SizeOfImage; + + if (Size < _pe.ImageSectionHeaders.Max(s => (ulong)s.VirtualSize + s.VirtualAddress)) + { + throw new InvalidOperationException("Image not Big Enough"); + } + } + + /// + /// set memory protections, finishing the Mount process + /// + public void FinishMount() + { + foreach (var s in _pe.ImageSectionHeaders) + { + ulong start = Start + s.VirtualAddress; + ulong length = s.VirtualSize; + + MemoryBlock.Protection prot; + var r = (s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_MEM_READ) != 0; + var w = (s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_MEM_WRITE) != 0; + var x = (s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_MEM_EXECUTE) != 0; + if (w && x) + { + throw new InvalidOperationException("Write and Execute not allowed"); + } + + prot = x ? MemoryBlock.Protection.RX : w ? MemoryBlock.Protection.RW : MemoryBlock.Protection.R; + + Memory.Protect(start, length, prot); + } + } + + /// + /// load the PE into memory + /// + /// start address + public void Mount(ulong org) + { + Start = org; + LoadOffset = (long)Start - (long)_pe.ImageNtHeaders.OptionalHeader.ImageBase; + Memory = new MemoryBlock(Start, Size); + Memory.Activate(); + + // copy headers + { + ulong length = _pe.ImageNtHeaders.OptionalHeader.SizeOfHeaders; + Memory.Protect(Start, length, MemoryBlock.Protection.RW); + Marshal.Copy(_fileData, 0, Z.US(Start), (int)length); + Memory.Protect(Start, length, MemoryBlock.Protection.R); + } + + // copy sections + foreach (var s in _pe.ImageSectionHeaders) + { + ulong start = Start + s.VirtualAddress; + ulong length = s.VirtualSize; + + Memory.Protect(start, length, MemoryBlock.Protection.RW); + Marshal.Copy(_fileData, (int)s.PointerToRawData, Z.US(start), (int)s.SizeOfRawData); + WaterboxUtils.ZeroMemory(Z.US(start + s.SizeOfRawData), (long)(length - s.SizeOfRawData)); + } + + // apply relocations + foreach (var rel in _pe.ImageRelocationDirectory) + { + foreach (var to in rel.TypeOffsets) + { + ulong address = Start + rel.VirtualAddress + to.Offset; + + switch (to.Type) + { + // there are many other types of relocation specified, + // but the only that are used is 0 (does nothing), 3 (32 bit standard), 10 (64 bit standard) + + case 3: // IMAGE_REL_BASED_HIGHLOW + { + byte[] tmp = new byte[4]; + Marshal.Copy(Z.US(address), tmp, 0, 4); + uint val = BitConverter.ToUInt32(tmp, 0); + tmp = BitConverter.GetBytes((uint)(val + LoadOffset)); + Marshal.Copy(tmp, 0, Z.US(address), 4); + break; + } + + case 10: // IMAGE_REL_BASED_DIR64 + { + byte[] tmp = new byte[8]; + Marshal.Copy(Z.US(address), tmp, 0, 8); + long val = BitConverter.ToInt64(tmp, 0); + tmp = BitConverter.GetBytes(val + LoadOffset); + Marshal.Copy(tmp, 0, Z.US(address), 8); + break; + } + } + } + } + + // publish exports + EntryPoint = Z.US(Start + _pe.ImageNtHeaders.OptionalHeader.AddressOfEntryPoint); + foreach (var export in _pe.ExportedFunctions) + { + if (export.Name != null) + ExportsByName.Add(export.Name, Z.US(Start + export.Address)); + ExportsByOrdinal.Add(export.Ordinal, Z.US(Start + export.Address)); + } + + // collect information about imports + // NB: Hints are not the same as Ordinals. Off by 1?? + foreach (var import in _pe.ImportedFunctions) + { + + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Waterbox/WaterboxUtils.cs b/BizHawk.Emulation.Cores/Waterbox/WaterboxUtils.cs index 74540e47ef..9b01f29da2 100644 --- a/BizHawk.Emulation.Cores/Waterbox/WaterboxUtils.cs +++ b/BizHawk.Emulation.Cores/Waterbox/WaterboxUtils.cs @@ -42,5 +42,14 @@ namespace BizHawk.Emulation.Cores.Waterbox } } + public static unsafe void ZeroMemory(IntPtr mem, long length) + { + byte* p = (byte*)mem; + byte* end = p + length; + while (p < end) + { + *p++ = 0; + } + } } } diff --git a/References/PeNet.dll b/References/PeNet.dll index f2c887f476..f4657450e7 100644 Binary files a/References/PeNet.dll and b/References/PeNet.dll differ