public void SealImportsAndTakeXorSnapshot() { if (_everythingSealed) { throw new InvalidOperationException("PeWrapper already sealed!"); } // save import values, then zero them all (for hash purposes), then take our snapshot, then load them again, // then set the .idata area to read only byte[] impData = null; if (_imports != null) { impData = new byte[_imports.Size]; Marshal.Copy(Z.US(_imports.Start), impData, 0, (int)_imports.Size); WaterboxUtils.ZeroMemory(Z.US(_imports.Start), (long)_imports.Size); } Memory.SaveXorSnapshot(); if (_imports != null) { Marshal.Copy(impData, 0, Z.US(_imports.Start), (int)_imports.Size); _imports.W = false; Memory.Protect(_imports.Start, _imports.Size, _imports.Prot); } if (_sealed != null) { _sealed.W = false; Memory.Protect(_sealed.Start, _sealed.Size, _sealed.Prot); } _everythingSealed = true; }
public void SealImportsAndTakeXorSnapshot() { if (_everythingSealed) { throw new InvalidOperationException($"{nameof(ElfLoader)} already sealed!"); } // save import values, then zero them all (for hash purposes), then take our snapshot, then load them again, // then set the .wbxsyscall area to read only byte[] impData = null; impData = new byte[_imports.Size]; Marshal.Copy(Z.US(_imports.LoadAddress), impData, 0, (int)_imports.Size); WaterboxUtils.ZeroMemory(Z.US(_imports.LoadAddress), (long)_imports.Size); byte[] invData = null; if (_invisible != null) { invData = new byte[_invisible.Size]; Marshal.Copy(Z.US(_invisible.LoadAddress), invData, 0, (int)_invisible.Size); WaterboxUtils.ZeroMemory(Z.US(_invisible.LoadAddress), (long)_invisible.Size); } Memory.Seal(); Marshal.Copy(impData, 0, Z.US(_imports.LoadAddress), (int)_imports.Size); if (_invisible != null) { Marshal.Copy(invData, 0, Z.US(_invisible.LoadAddress), (int)_invisible.Size); } _everythingSealed = true; Protect(); }
private void ProtectInternal(int startPage, int numPages, MemoryBlockBase.Protection prot, bool wasUsed) { for (var i = startPage; i < startPage + numPages; i++) { _pages[i] = prot; } ulong start = GetStartAddr(startPage); ulong length = ((ulong)numPages) << WaterboxUtils.PageShift; if (prot == FREE) { Memory.Protect(start, length, MemoryBlockBase.Protection.RW); WaterboxUtils.ZeroMemory(Z.US(start), (long)length); Memory.Protect(start, length, MemoryBlockBase.Protection.None); Used -= length; Console.WriteLine($"Freed {length} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)"); } else { Memory.Protect(start, length, prot); if (wasUsed) { Console.WriteLine($"Set protection for {length} bytes on {Name} to {prot}"); } else { Used += length; Console.WriteLine($"Allocated {length} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)"); } } }
public void LoadStateBinary(BinaryReader br) { var name = br.ReadString(); if (name != Name) { // probable cause: internal error throw new InvalidOperationException($"Name did not match for heap {Name}"); } var used = br.ReadUInt64(); if (used > Memory.Size) { throw new InvalidOperationException($"Heap {Name} used {used} larger than available {Memory.Size}"); } if (!Sealed) { var hash = br.ReadBytes(Memory.XorHash.Length); if (!hash.SequenceEqual(Memory.XorHash)) { throw new InvalidOperationException($"Hash did not match for heap {Name}. Is this the same rom with the same SyncSettings?"); } var oldUsedAligned = WaterboxUtils.AlignUp(Used); var usedAligned = WaterboxUtils.AlignUp(used); if (usedAligned > oldUsedAligned) { // grow var s = Memory.Start + oldUsedAligned; var l = usedAligned - oldUsedAligned; Memory.Protect(s, l, MemoryBlock.Protection.RW); } else if (usedAligned < oldUsedAligned) { // shrink var s = Memory.Start + usedAligned; var l = oldUsedAligned - usedAligned; // like elsewhere, we zero on free to avoid nondeterminism if later reallocated WaterboxUtils.ZeroMemory(Z.US(s), (long)l); Memory.Protect(s, l, MemoryBlock.Protection.None); } var ms = Memory.GetXorStream(Memory.Start, usedAligned, true); WaterboxUtils.CopySome(br.BaseStream, ms, (long)usedAligned); Used = used; } else { var hash = br.ReadBytes(_hash.Length); if (!hash.SequenceEqual(_hash)) { throw new InvalidOperationException($"Hash did not match for heap {Name}. Is this the same rom with the same SyncSettings?"); } } }
private void SettingsQuery(string name, IntPtr dest) { var val = SettingsQuery(name); var bytes = Encoding.UTF8.GetBytes(val); if (bytes.Length > 255) { throw new InvalidOperationException($"Value {val} for setting {name} was too long"); } WaterboxUtils.ZeroMemory(dest, 256); Marshal.Copy(bytes, 0, dest, bytes.Length); }
private void SettingsQuery(string name, IntPtr dest) { if (!_syncSettingsActual.MednafenValues.TryGetValue(name, out var val)) { if (SettingsInfo.SettingsByKey.TryGetValue(name, out var info)) { val = info.DefaultValue; } else { throw new InvalidOperationException($"Core asked for setting {name} which was not found in the defaults"); } } var bytes = Encoding.UTF8.GetBytes(val); if (bytes.Length > 255) { throw new InvalidOperationException($"Value {val} for setting {name} was too long"); } WaterboxUtils.ZeroMemory(dest, 256); Marshal.Copy(bytes, 0, dest, bytes.Length); }
public PeWrapper(string moduleName, byte[] fileData, ulong destAddress) { ModuleName = moduleName; _pe = new PeFile(fileData); Size = _pe.ImageNtHeaders.OptionalHeader.SizeOfImage; Start = destAddress; if (Size < _pe.ImageSectionHeaders.Max(s => (ulong)s.VirtualSize + s.VirtualAddress)) { throw new InvalidOperationException("Image not Big Enough"); } _fileHash = WaterboxUtils.Hash(fileData); foreach (var s in _pe.ImageSectionHeaders) { ulong start = Start + s.VirtualAddress; ulong length = s.VirtualSize; MemoryBlockBase.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 ? MemoryBlockBase.Protection.RX : w ? MemoryBlockBase.Protection.RW : MemoryBlockBase.Protection.R; var section = new Section { // chop off possible null padding from name Name = Encoding.ASCII.GetString(s.Name, 0, (s.Name.Select((v, i) => new { v, i }).FirstOrDefault(a => a.v == 0) ?? new { v = (byte)0, i = s.Name.Length }).i), Start = start, Size = length, SavedSize = WaterboxUtils.AlignUp(length), R = r, W = w, X = x, Prot = prot, DiskStart = s.PointerToRawData, DiskSize = s.SizeOfRawData }; _sections.Add(section); _sectionsByName.Add(section.Name, section); } _sectionsByName.TryGetValue(".idata", out _imports); _sectionsByName.TryGetValue(".sealed", out _sealed); _sectionsByName.TryGetValue(".invis", out _invisible); // OK, NOW MOUNT LoadOffset = (long)Start - (long)_pe.ImageNtHeaders.OptionalHeader.ImageBase; Memory = MemoryBlockBase.CallPlatformCtor(Start, Size); Memory.Activate(); Memory.Protect(Start, Size, MemoryBlockBase.Protection.RW); // copy headers Marshal.Copy(fileData, 0, Z.US(Start), (int)_pe.ImageNtHeaders.OptionalHeader.SizeOfHeaders); // copy sections foreach (var s in _sections) { ulong datalength = Math.Min(s.Size, s.DiskSize); Marshal.Copy(fileData, (int)s.DiskStart, Z.US(s.Start), (int)datalength); WaterboxUtils.ZeroMemory(Z.US(s.Start + datalength), (long)(s.SavedSize - datalength)); } // apply relocations var n32 = 0; var n64 = 0; 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); n32++; 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); n64++; break; } } } } if (IntPtr.Size == 8 && n32 > 0) { // check mcmodel, etc throw new InvalidOperationException("32 bit relocations found in 64 bit dll! This will fail."); } Console.WriteLine($"Processed {n32} 32 bit and {n64} 64 bit relocations"); ProtectMemory(); // 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 foreach (var import in _pe.ImportedFunctions) { Dictionary <string, IntPtr> module; if (!ImportsByModule.TryGetValue(import.DLL, out module)) { module = new Dictionary <string, IntPtr>(); ImportsByModule.Add(import.DLL, module); } var dest = Start + import.Thunk; if (_imports == null || dest >= _imports.Start + _imports.Size || dest < _imports.Start) { throw new InvalidOperationException("Import record outside of .idata!"); } module.Add(import.Name, Z.US(dest)); } Section midipix; if (_sectionsByName.TryGetValue(".midipix", out midipix)) { var dataOffset = midipix.DiskStart; CtorList = Z.SS(BitConverter.ToInt64(fileData, (int)(dataOffset + 0x30)) + LoadOffset); DtorList = Z.SS(BitConverter.ToInt64(fileData, (int)(dataOffset + 0x38)) + LoadOffset); } Console.WriteLine($"Mounted `{ModuleName}` @{Start:x16}"); foreach (var s in _sections.OrderBy(s => s.Start)) { Console.WriteLine(" @{0:x16} {1}{2}{3} `{4}` {5} bytes", s.Start, s.R ? "R" : " ", s.W ? "W" : " ", s.X ? "X" : " ", s.Name, s.Size); } Console.WriteLine("GDB Symbol Load:"); var symload = $"add-sym {ModuleName} {_sectionsByName[".text"].Start}"; if (_sectionsByName.ContainsKey(".data")) { symload += $" -s .data {_sectionsByName[".data"].Start}"; } if (_sectionsByName.ContainsKey(".bss")) { symload += $" -s .bss {_sectionsByName[".bss"].Start}"; } Console.WriteLine(symload); }
/// <summary> /// load the PE into memory /// </summary> /// <param name="org">start address</param> private void Mount() { LoadOffset = (long)Start - (long)_pe.ImageNtHeaders.OptionalHeader.ImageBase; Memory = new MemoryBlock(Start, Size); Memory.Activate(); Memory.Protect(Start, Size, MemoryBlock.Protection.RW); // copy headers Marshal.Copy(_fileData, 0, Z.US(Start), (int)_pe.ImageNtHeaders.OptionalHeader.SizeOfHeaders); // copy sections foreach (var s in _sections) { ulong datalength = Math.Min(s.Size, s.DiskSize); Marshal.Copy(_fileData, (int)s.DiskStart, Z.US(s.Start), (int)datalength); WaterboxUtils.ZeroMemory(Z.US(s.Start + datalength), (long)(s.SavedSize - datalength)); } // apply relocations var n32 = 0; var n64 = 0; 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); n32++; 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); n64++; break; } } } } if (IntPtr.Size == 8 && n32 > 0) { // check mcmodel, etc throw new InvalidOperationException("32 bit relocations found in 64 bit dll! This will fail."); } Console.WriteLine($"Processed {n32} 32 bit and {n64} 64 bit relocations"); ProtectMemory(); // 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 foreach (var import in _pe.ImportedFunctions) { Dictionary <string, IntPtr> module; if (!ImportsByModule.TryGetValue(import.DLL, out module)) { module = new Dictionary <string, IntPtr>(); ImportsByModule.Add(import.DLL, module); } var dest = Start + import.Thunk; if (_imports == null || dest >= _imports.Start + _imports.Size || dest < _imports.Start) { throw new InvalidOperationException("Import record outside of .idata!"); } module.Add(import.Name, Z.US(dest)); } Section midipix; if (_sectionsByName.TryGetValue(".midipix", out midipix)) { var dataOffset = midipix.DiskStart; CtorList = Z.SS(BitConverter.ToInt64(_fileData, (int)(dataOffset + 0x30)) + LoadOffset); DtorList = Z.SS(BitConverter.ToInt64(_fileData, (int)(dataOffset + 0x38)) + LoadOffset); } Console.WriteLine($"Mounted `{ModuleName}` @{Start:x16}"); foreach (var s in _sections.OrderBy(s => s.Start)) { Console.WriteLine(" @{0:x16} {1}{2}{3} `{4}` {5} bytes", s.Start, s.R ? "R" : " ", s.W ? "W" : " ", s.X ? "X" : " ", s.Name, s.Size); } Console.WriteLine("GDB Symbol Load:"); var symload = $"add-sym {ModuleName} {_sectionsByName[".text"].Start}"; if (_sectionsByName.ContainsKey(".data")) { symload += $" -s .data {_sectionsByName[".data"].Start}"; } if (_sectionsByName.ContainsKey(".bss")) { symload += $" -s .bss {_sectionsByName[".bss"].Start}"; } Console.WriteLine(symload); }