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(); }
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; }
private void ProtectInternal(int startPage, int numPages, MemoryBlock.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, MemoryBlock.Protection.RW); WaterboxUtils.ZeroMemory(Z.US(start), (long)length); Memory.Protect(start, length, MemoryBlock.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?"); } } }
public PeWrapper(string moduleName, byte[] fileData, ulong destAddress) { ModuleName = moduleName; _fileData = fileData; _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; 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; 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); Mount(); }
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); }
public MapHeap(ulong start, ulong size, string name) { size = WaterboxUtils.AlignUp(size); Memory = new MemoryBlock(start, size); Name = name; _pagesAsBytes = new byte[size >> WaterboxUtils.PageShift]; _pages = (MemoryBlock.Protection[])(object) _pagesAsBytes; for (var i = 0; i < _pages.Length; i++) { _pages[i] = FREE; } Console.WriteLine("Created mapheap `{1}` at {0:x16}:{2:x16}", start, name, start + size); }
public void Seal() { if (!Sealed) { Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.R); _hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false)); Sealed = true; } else { throw new InvalidOperationException(string.Format("Attempt to reseal heap {0}", Name)); } }
public MapHeap(ulong start, ulong size, string name) { size = WaterboxUtils.AlignUp(size); Memory = MemoryBlock.Create(start, size); Name = name; _pagesAsBytes = new byte[size >> WaterboxUtils.PageShift]; _pages = (MemoryBlock.Protection[])(object) _pagesAsBytes; for (var i = 0; i < _pages.Length; i++) { _pages[i] = FREE; } Console.WriteLine($"Created {nameof(MapHeap)} `{name}` at {start:x16}:{start + size:x16}"); }
public void Seal() { if (!Sealed) { Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.R); _hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, WaterboxUtils.AlignUp(Used), false)); Sealed = true; } else { throw new InvalidOperationException($"Attempt to reseal heap {Name}"); } }
public void LoadStateBinary(BinaryReader br) { if (!_everythingSealed) { // operations happening in the wrong order. probable cause: internal logic error. make sure frontend calls Seal throw new InvalidOperationException(".wbxsyscall section must be closed before loading state"); } if (br.ReadUInt64() != MAGIC) { // file id is missing. probable cause: garbage savestate throw new InvalidOperationException("Savestate corrupted!"); } var elfHash = br.ReadBytes(_elfHash.Length); if (_skipCoreConsistencyCheck) { throw new InvalidOperationException("We decided that the core consistency check should always run"); } else { if (!elfHash.SequenceEqual(_elfHash)) { // the .dll file that is loaded now has a different hash than the .dll that created the savestate throw new InvalidOperationException("Core consistency check failed. Is this a savestate from a different version?"); } } var xorHash = br.ReadBytes(Memory.XorHash.Length); if (!_skipMemoryConsistencyCheck) { if (!xorHash.SequenceEqual(Memory.XorHash)) { // the post-Seal memory state is different. probable cause: different rom or different version of rom, // different syncsettings throw new InvalidOperationException("Memory consistency check failed. Is this savestate from different SyncSettings?"); } } var len = Memory.EndExclusive - _saveStart; if (br.ReadUInt64() != len) { throw new InvalidOperationException("Unexpected saved length"); } var ms = Memory.GetXorStream(_saveStart, len, true); WaterboxUtils.CopySome(br.BaseStream, ms, (long)len); }
public void LoadStateBinary(BinaryReader br) { Enter(); try { if (br.ReadUInt64() != MAGIC) { throw new InvalidOperationException("Magic not magic enough!"); } if (!br.ReadBytes(_elfhash.Length).SequenceEqual(_elfhash)) { throw new InvalidOperationException("Elf changed disguise!"); } if (br.ReadInt64() != _loadoffset) { throw new InvalidOperationException("Trickys elves moved on you!"); } foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Writable) != 0)) { var len = br.ReadInt64(); if (sec.Size != len) { throw new InvalidOperationException("Unexpected section size for " + sec.Name); } var ms = _base.GetStream((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, true); WaterboxUtils.CopySome(br.BaseStream, ms, len); } if (_heap != null) { _heap.LoadStateBinary(br); } if (_sealedheap != null) { _sealedheap.LoadStateBinary(br); } if (br.ReadUInt64() != MAGIC) { throw new InvalidOperationException("Magic not magic enough!"); } // the syscall trampolines were overwritten in loadstate (they're in .bss), and if we're cross-session, // are no longer valid. cores must similiarly resend any external pointers they gave the core. ConnectAllClibPatches(); } finally { Exit(); } }
public void LoadStateBinary(BinaryReader br) { if (!_everythingSealed) { // operations happening in the wrong order. probable cause: internal logic error. make sure frontend calls Seal throw new InvalidOperationException(".idata sections must be closed before loading state"); } if (br.ReadUInt64() != MAGIC) { // file id is missing. probable cause: garbage savestate throw new InvalidOperationException("Savestate corrupted!"); } if (!br.ReadBytes(_fileHash.Length).SequenceEqual(_fileHash)) { // the .dll file that is loaded now has a different hash than the .dll that created the savestate throw new InvalidOperationException("Core consistency check failed. Is this a savestate from a different version?"); } if (!br.ReadBytes(Memory.XorHash.Length).SequenceEqual(Memory.XorHash)) { // the post-Seal memory state is different. probable cause: different rom or different version of rom, // different syncsettings throw new InvalidOperationException("Memory consistency check failed. Is this savestate from different SyncSettings?"); } if (br.ReadUInt64() != Start) { // dll loaded somewhere else. probable cause: internal logic error. // unlikely to get this far if the previous checks pssed throw new InvalidOperationException("Trickys elves moved on you!"); } Memory.Protect(Memory.AddressRange.Start, Memory.Size, MemoryBlockBase.Protection.RW); foreach (var s in _sections) { if (!s.W || s == _invisible) { continue; } if (br.ReadUInt64() != s.SavedSize) { throw new InvalidOperationException("Unexpected section size for " + s.Name); } var ms = Memory.GetXorStream(s.Start, s.SavedSize, true); WaterboxUtils.CopySome(br.BaseStream, ms, (long)s.SavedSize); } ProtectMemory(); }
public void LoadStateBinary(BinaryReader br) { var name = br.ReadString(); if (name != Name) { throw new InvalidOperationException($"Name did not match for {nameof(MapHeap)} {Name}"); } var size = br.ReadUInt64(); if (size != Memory.Size) { throw new InvalidOperationException($"Size did not match for {nameof(MapHeap)} {Name}"); } var used = br.ReadUInt64(); var hash = br.ReadBytes(Memory.XorHash.Length); if (!hash.SequenceEqual(Memory.XorHash)) { throw new InvalidOperationException($"Hash did not match for {nameof(MapHeap)} {Name}. Is this the same rom?"); } if (br.BaseStream.Read(_pagesAsBytes, 0, _pagesAsBytes.Length) != _pagesAsBytes.Length) { throw new InvalidOperationException("Unexpected error reading!"); } Used = 0; Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW); var dsts = Memory.GetXorStream(Memory.Start, Memory.Size, true); for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize) { if (_pages[i] != FREE) { dsts.Seek(addr, SeekOrigin.Begin); WaterboxUtils.CopySome(br.BaseStream, dsts, WaterboxUtils.PageSize); Used += (uint)WaterboxUtils.PageSize; } } if (Used != used) { throw new InvalidOperationException("Internal savestate error"); } if (br.ReadUInt64() != MAGIC) { throw new InvalidOperationException("Savestate internal error"); } RefreshAllProtections(); }
public void SaveStateBinary(BinaryWriter bw) { bw.Write(Name); bw.Write(Used); if (!Sealed) { bw.Write(Memory.XorHash); var ms = Memory.GetXorStream(Memory.Start, WaterboxUtils.AlignUp(Used), false); ms.CopyTo(bw.BaseStream); } else { bw.Write(_hash); } }
public void Seal() { if (!Sealed) { //RTC_Hijack : Change the protect level to RW instead of R Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.RW); _hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false)); Sealed = true; } else { throw new InvalidOperationException(string.Format("Attempt to reseal heap {0}", Name)); } }
public bool Protect(ulong start, ulong size, MemoryBlockBase.Protection prot) { if (start < Memory.Start || start + size > Memory.End || size == 0) { return(false); } var startPage = GetPage(start); var numPages = WaterboxUtils.PagesNeeded(size); if (!EnsureMapped(startPage, numPages)) { return(false); } ProtectInternal(startPage, numPages, prot, true); return(true); }
public ulong Map(ulong size, MemoryBlock.Protection prot) { if (size == 0) { return(0); } int numPages = WaterboxUtils.PagesNeeded(size); int startPage = FindConsecutiveFreePages(numPages); if (startPage == -1) { return(0); } var ret = GetStartAddr(startPage); ProtectInternal(startPage, numPages, prot, false); return(ret); }
public void StoreSaveRam(byte[] data) { using (_exe.EnterExit()) { if (data.Length != _saveramSize) { throw new InvalidOperationException("Saveram size mismatch"); } using (_exe.EnterExit()) { var source = new MemoryStream(data, false); foreach (var area in _saveramAreas) { WaterboxUtils.CopySome(source, new MemoryDomainStream(area), area.Size); } } } }
public bool Protect(ulong start, ulong size, MemoryBlock.Protection prot) { // TODO: eliminate copy+pasta between unmap and protect if (start < Memory.Start || start + size > Memory.EndExclusive || size == 0) { return(false); } var startPage = GetPage(start); var numPages = WaterboxUtils.PagesNeeded(size); if (!EnsureMappedNonStack(startPage, numPages)) { return(false); } ProtectInternal(startPage, numPages, prot, true); return(true); }
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 void LoadStateBinary(BinaryReader br) { var name = br.ReadString(); if (name != Name) { // probable cause: internal error throw new InvalidOperationException(string.Format("Name did not match for heap {0}", Name)); } var used = br.ReadUInt64(); if (used > Memory.Size) { throw new InvalidOperationException(string.Format("Heap {0} used {1} larger than available {2}", Name, used, Memory.Size)); } if (!Sealed) { var hash = br.ReadBytes(Memory.XorHash.Length); if (!hash.SequenceEqual(Memory.XorHash)) { throw new InvalidOperationException(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name)); } var usedAligned = WaterboxUtils.AlignUp(used); Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.None); Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW); 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(string.Format("Hash did not match for heap {0}. Is this the same rom with the same SyncSettings?", Name)); } } }
public void SaveStateBinary(BinaryWriter bw) { bw.Write(Name); bw.Write(Memory.Size); bw.Write(Used); bw.Write(Memory.XorHash); bw.Write(_pagesAsBytes); Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.R); var srcs = Memory.GetXorStream(Memory.Start, Memory.Size, false); for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize) { if (_pages[i] != FREE) { srcs.Seek(addr, SeekOrigin.Begin); WaterboxUtils.CopySome(srcs, bw.BaseStream, WaterboxUtils.PageSize); } } bw.Write(MAGIC); RefreshAllProtections(); }
/// <summary> /// Set memory protections, pre or post seal /// </summary> private void Protect() { var writeStart = _everythingSealed ? _postSealWriteStart : _writeStart; Memory.Protect(Memory.Start, _execEnd - Memory.Start, MemoryBlock.Protection.RX); Memory.Protect(_execEnd, writeStart - _execEnd, MemoryBlock.Protection.R); Memory.Protect(writeStart, Memory.EndExclusive - writeStart, MemoryBlock.Protection.RW); if (_invisible != null) { if (!WaterboxUtils.Aligned(_invisible.LoadAddress)) { throw new InvalidOperationException("Invisible section start not aligned! Check linker script"); } var end = WaterboxUtils.AlignUp(_invisible.LoadAddress + _invisible.Size); if (_sectionsByName.Values.Any(s => s != _invisible && s.LoadAddress < end && s.LoadAddress >= _invisible.LoadAddress)) { throw new InvalidOperationException("Invisible section end not aligned, or not padded! Check linker script"); } Memory.Protect( _invisible.LoadAddress, _invisible.Size, MemoryBlock.Protection.RW_Invisible); } }
public ElfLoader(string moduleName, byte[] fileData, ulong assumedStart, bool skipCoreConsistencyCheck, bool skipMemoryConsistencyCheck) { ModuleName = moduleName; _skipCoreConsistencyCheck = skipCoreConsistencyCheck; _skipMemoryConsistencyCheck = skipMemoryConsistencyCheck; _elfHash = WaterboxUtils.Hash(fileData); _elf = ELFReader.Load <ulong>(new MemoryStream(fileData, false), true); var loadsegs = _elf.Segments.Where(s => s.Type == SegmentType.Load); var start = loadsegs.Min(s => s.Address); start = WaterboxUtils.AlignDown(start); var end = loadsegs.Max(s => s.Address + s.Size); end = WaterboxUtils.AlignUp(end); var size = end - start; if (start != assumedStart) { throw new InvalidOperationException($"{nameof(assumedStart)} did not match actual origin in elf file"); } if (_elf.Sections.Any(s => s.Name.StartsWith(".rel"))) { throw new InvalidOperationException("Elf has relocations!"); } _allSymbols = ((ISymbolTable)_elf.GetSection(".symtab")) .Entries .Cast <SymbolEntry <ulong> >() .ToList(); _sectionsByName = _elf.Sections .ToDictionary(s => s.Name); _sectionsByName.TryGetValue(".wbxsyscall", out _imports); if (_imports == null) { // Likely cause: This is a valid elf file, but it was not compiled by our toolchain at all throw new InvalidOperationException("Missing .wbxsyscall section!"); } _sectionsByName.TryGetValue(".sealed", out _sealed); _sectionsByName.TryGetValue(".invis", out _invisible); _visibleSymbols = _allSymbols .Where(s => s.Binding == SymbolBinding.Global && s.Visibility == SymbolVisibility.Default) .ToDictionary(s => s.Name); _importSymbols = _allSymbols // TODO: No matter what attributes I provide, I seem to end up with Local and/or Hidden symbols in // .wbxsyscall a lot of the time on heavily optimized release builds. // Fortunately, there's nothing else in .wbxsyscall so we can just not filter at all. .Where(s => s.PointedSection == _imports) .ToList(); Memory = MemoryBlock.Create(start, size); Memory.Activate(); Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW); foreach (var seg in loadsegs) { var data = seg.GetFileContents(); Marshal.Copy(data, 0, Z.US(seg.Address), Math.Min((int)seg.Size, (int)seg.FileSize)); } { // Compute RW boundaries var allocated = _elf.Sections .Where(s => (s.Flags & SectionFlags.Allocatable) != 0); var writable = allocated .Where(s => (s.Flags & SectionFlags.Writable) != 0); var postSealWritable = writable .Where(s => !IsSpecialReadonlySection(s)); var saveable = postSealWritable .Where(s => s != _invisible); var executable = allocated .Where(s => (s.Flags & SectionFlags.Executable) != 0); _writeStart = WaterboxUtils.AlignDown(writable.Min(s => s.LoadAddress)); _postSealWriteStart = WaterboxUtils.AlignDown(postSealWritable.Min(s => s.LoadAddress)); _execEnd = WaterboxUtils.AlignUp(executable.Max(s => s.LoadAddress + s.Size)); // validate; this may require linkscript cooperation // due to the segment limitations, the only thing we'd expect to catch is a custom eventually readonly section // in the wrong place (because the linkscript doesn't know "eventually readonly") if (_execEnd > _writeStart) { throw new InvalidOperationException($"ElfLoader: Executable data to {_execEnd:X16} overlaps writable data from {_writeStart}"); } } PrintSections(); PrintGdbData(); PrintTopSavableSymbols(); Protect(); }
public ulong Remap(ulong start, ulong oldSize, ulong newSize, bool canMove) { // TODO: what is the expected behavior when everything requested for remap is allocated, // but with different protections? if (start < Memory.Start || start + oldSize > Memory.EndExclusive || oldSize == 0 || newSize == 0) { return(0); } var oldStartPage = GetPage(start); var oldNumPages = WaterboxUtils.PagesNeeded(oldSize); if (!EnsureMappedNonStack(oldStartPage, oldNumPages)) { return(0); } var oldProt = _pages[oldStartPage]; int newNumPages = WaterboxUtils.PagesNeeded(newSize); if (!canMove) { if (newNumPages <= oldNumPages) { if (newNumPages < oldNumPages) { ProtectInternal(oldStartPage + newNumPages, oldNumPages - newNumPages, FREE, true); } return(start); } else if (newNumPages > oldNumPages) { for (var i = oldStartPage + oldNumPages; i < oldStartPage + newNumPages; i++) { if (_pages[i] != FREE) { return(0); } } ProtectInternal(oldStartPage + oldNumPages, newNumPages - oldNumPages, oldProt, false); return(start); } } // if moving is allowed, we always move to simplify and defragment when possible int newStartPage = FindConsecutiveFreePagesAssumingFreed(newNumPages, oldStartPage, oldNumPages); if (newStartPage == -1) { return(0); } var copyDataLen = Math.Min(oldSize, newSize); var copyPageLen = Math.Min(oldNumPages, newNumPages); var data = new byte[copyDataLen]; Memory.Protect(start, copyDataLen, MemoryBlock.Protection.RW); Marshal.Copy(Z.US(start), data, 0, (int)copyDataLen); var pages = new MemoryBlock.Protection[copyPageLen]; Array.Copy(_pages, oldStartPage, pages, 0, copyPageLen); ProtectInternal(oldStartPage, oldNumPages, FREE, true); ProtectInternal(newStartPage, newNumPages, MemoryBlock.Protection.RW, false); var ret = GetStartAddr(newStartPage); Marshal.Copy(data, 0, Z.US(ret), (int)copyDataLen); Array.Copy(pages, 0, _pages, newStartPage, copyPageLen); RefreshProtections(newStartPage, copyPageLen); if (newNumPages > oldNumPages) { ProtectInternal(newStartPage + oldNumPages, newNumPages - oldNumPages, oldProt, true); } return(ret); }
/// <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); }
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); }
public ReadonlyFirmware(byte[] data, string name) { _data = data; _hash = WaterboxUtils.Hash(data); Name = name; }
public ElfRunner(string filename, long heapsize, long sealedheapsize, long invisibleheapsize) { using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { _elfhash = WaterboxUtils.Hash(fs); } // todo: hack up this baby to take Streams _elf = ELFReader.Load <long>(filename); var loadsegs = _elf.Segments.Where(s => s.Type == SegmentType.Load); long orig_start = loadsegs.Min(s => s.Address); orig_start &= ~(Environment.SystemPageSize - 1); long orig_end = loadsegs.Max(s => s.Address + s.Size); if (HasRelocations()) { _base = new MemoryBlock((ulong)(orig_end - orig_start)); _loadoffset = (long)_base.Start - orig_start; Initialize(0); } else { Initialize((ulong)orig_start); _base = new MemoryBlock((ulong)orig_start, (ulong)(orig_end - orig_start)); _loadoffset = 0; Enter(); } try { _disposeList.Add(_base); AddMemoryBlock(_base, "elf"); _base.Activate(); _base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.RW); foreach (var seg in loadsegs) { var data = seg.GetContents(); Marshal.Copy(data, 0, Z.SS(seg.Address + _loadoffset), data.Length); } RegisterSymbols(); ProcessRelocations(); _base.Protect(_base.Start, _base.Size, MemoryBlock.Protection.R); foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0)) { if ((sec.Flags & SectionFlags.Executable) != 0) { _base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlock.Protection.RX); } else if ((sec.Flags & SectionFlags.Writable) != 0) { _base.Protect((ulong)(sec.LoadAddress + _loadoffset), (ulong)sec.Size, MemoryBlock.Protection.RW); } } ulong end = _base.End; if (heapsize > 0) { _heap = new Heap(GetHeapStart(end), (ulong)heapsize, "sbrk-heap"); _heap.Memory.Activate(); end = _heap.Memory.End; _disposeList.Add(_heap); AddMemoryBlock(_heap.Memory, "sbrk - heap"); } if (sealedheapsize > 0) { _sealedheap = new Heap(GetHeapStart(end), (ulong)sealedheapsize, "sealed-heap"); _sealedheap.Memory.Activate(); end = _sealedheap.Memory.End; _disposeList.Add(_sealedheap); AddMemoryBlock(_sealedheap.Memory, "sealed-heap"); } if (invisibleheapsize > 0) { _invisibleheap = new Heap(GetHeapStart(end), (ulong)invisibleheapsize, "invisible-heap"); _invisibleheap.Memory.Activate(); end = _invisibleheap.Memory.End; _disposeList.Add(_invisibleheap); AddMemoryBlock(_invisibleheap.Memory, "invisible-heap"); } ConnectAllClibPatches(); Console.WriteLine("Loaded {0}@{1:X16}", filename, _base.Start); foreach (var sec in _elf.Sections.Where(s => s.LoadAddress != 0)) { Console.WriteLine(" {0}@{1:X16}, size {2}", sec.Name.PadLeft(20), sec.LoadAddress + _loadoffset, sec.Size.ToString().PadLeft(12)); } PrintTopSavableSymbols(); } catch { Dispose(); throw; } finally { Exit(); } }