/// <summary> /// Parse a PE. /// </summary> /// <param name="stream">A stream of the PE contents.</param> private void Parse(Stream stream) { rawData = new byte[stream.Length]; stream.Read(rawData, 0, (int)stream.Length); stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new BinaryReader(stream); dosHeader = PEUtility.FromBinaryReader <IMAGE_DOS_HEADER>(reader); int stubSize = (int)dosHeader.e_lfanew - Marshal.SizeOf(typeof(IMAGE_DOS_HEADER)); dosStub = reader.ReadBytes(stubSize); // Add 4 bytes to the offset stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); ntSignature = PEUtility.FromBinaryReader <IMAGE_NT_HEADERS>(reader); fileHeader = PEUtility.FromBinaryReader <IMAGE_FILE_HEADER>(reader); optionalHeader = PEUtility.FromBinaryReader <IMAGE_OPTIONAL_HEADER32>(reader); dataDirectories = PEUtility.FromBinaryReader <IMAGE_DATA_DIRECTORIES>(reader); sections = new List <PESection>(); for (int i = 0; i < fileHeader.NumberOfSections; i++) { IMAGE_SECTION_HEADER header = PEUtility.FromBinaryReader <IMAGE_SECTION_HEADER>(reader); PESection section = new PESection(header); section.Parse(ref rawData); sections.Add(section); } }
private void Serialize(uint totalSize) { /* Allocate enough space to contain the whole new file */ byte[] file = new byte[totalSize]; uint filePosition = SerializeHeader(ref file); Array.Copy(PEUtility.RawSerialize(dataDirectories), 0, file, filePosition, Marshal.SizeOf(typeof(IMAGE_DATA_DIRECTORIES))); filePosition += (uint)Marshal.SizeOf(typeof(IMAGE_DATA_DIRECTORIES)); // XXX: Sections must be sorted in layout order! foreach (PESection section in sections) { Array.Copy(PEUtility.RawSerialize(section.Header), 0, file, filePosition, Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER))); filePosition += (uint)Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER)); } /* Copy the section data */ filePosition = optionalHeader.SizeOfHeaders; sections.Sort(new SectionPhysicalComparer()); foreach (PESection s in sections) { Array.Copy(s.Data, 0, file, filePosition, s.Data.Length); filePosition += s.RawSize; break; } /* Overwrite the container data */ rawData = file; }
private void Parse(Stream stream) { rawData = new byte[stream.Length]; stream.Read(rawData, 0, (int)stream.Length); stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new BinaryReader(stream); fileHeader = PEUtility.FromBinaryReader <IMAGE_FILE_HEADER>(reader); // Read the sections sections = new List <PESection>(); for (int i = 0; i < fileHeader.NumberOfSections; i++) { IMAGE_SECTION_HEADER header; header = PEUtility.FromBinaryReader <IMAGE_SECTION_HEADER>(reader); PESection section = new PESection(this, header); section.Parse(ref rawData); sections.Add(section); } // Read the symbol table from fileHeader.PointerToSymbolTable symbolTable = new SymbolTable(fileHeader.NumberOfSymbols); stream.Seek(fileHeader.PointerToSymbolTable, SeekOrigin.Begin); for (int i = 0; i < fileHeader.NumberOfSymbols; i++) { IMAGE_SYMBOL symbol; symbol = PEUtility.FromBinaryReader <IMAGE_SYMBOL>(reader); symbolTable.AddSymbol(symbol, i); } uint pointerToStringTable = fileHeader.PointerToSymbolTable + (uint)(fileHeader.NumberOfSymbols * Marshal.SizeOf(typeof(IMAGE_SYMBOL))); stream.Seek(pointerToStringTable, SeekOrigin.Begin); uint stringTableSize = PEUtility.FromBinaryReader <UInt32>(reader); for (ushort i = (ushort)Marshal.SizeOf(typeof(UInt32)); i < stringTableSize;) { String stringEntry = PEUtility.StringFromBinaryReader(reader); symbolTable.AddString(stringEntry, i); i += (ushort)(stringEntry.Length + 1); // include NULL terminator } Console.WriteLine("Object File: {0}", sourceFile); Console.WriteLine(symbolTable.ToString()); Console.WriteLine("Sections:"); foreach (PESection s in sections) { Console.WriteLine(s.ToString()); } Console.WriteLine(); }
/// <summary> /// Updates the physical location of all sections, and updates the amount of initialized data /// </summary> public void UpdatePhysicalLayout() { uint fileAlignment, filePosition; switch (optionalStandard.Magic) { case IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_PE32: fileAlignment = optionalHeader32.FileAlignment; filePosition = optionalHeader32.SizeOfHeaders; break; case IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_ROM: throw new NotSupportedException(); case IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_PE32PLUS: fileAlignment = optionalHeader32plus.FileAlignment; filePosition = optionalHeader32plus.SizeOfHeaders; break; default: throw new ArgumentException(); } uint initializedDataSize = 0; /* Layout the sections in physical order */ foreach (var s in sections) { if (s.ContributesToFileSize) { //TODO use null coalecing operator somehow if (s.Data != null) { s.RawSize = PEUtility.AlignUp((uint)s.Data.Length, fileAlignment); } else { s.RawSize = PEUtility.AlignUp(s.RawSize, fileAlignment); } s.PhysicalAddress = filePosition; filePosition += s.RawSize; initializedDataSize += s.RawSize; } } optionalStandard.SizeOfInitializedData = initializedDataSize; }
void ParseRelocations(ref byte[] file) { relocations = new List <IMAGE_RELOCATION>(); if (!HasRelocations) { return; } MemoryStream stream = new MemoryStream(file); stream.Seek(header.PointerToRelocations, SeekOrigin.Begin); BinaryReader reader = new BinaryReader(stream); for (int i = 0; i < header.NumberOfRelocations; i++) { IMAGE_RELOCATION reloc; reloc = PEUtility.FromBinaryReader <IMAGE_RELOCATION>(reader); relocations.Add(reloc); } }
private uint SerializeHeader(ref byte[] file) { uint filePosition = 0; Array.Copy(PEUtility.RawSerialize(dosHeader), 0, file, filePosition, Marshal.SizeOf(typeof(IMAGE_DOS_HEADER))); filePosition += (uint)Marshal.SizeOf(typeof(IMAGE_DOS_HEADER)); Array.Copy(dosStub, 0, file, filePosition, dosStub.Length); filePosition += (uint)dosStub.Length; Array.Copy(PEUtility.RawSerialize(ntSignature), 0, file, filePosition, Marshal.SizeOf(typeof(IMAGE_NT_HEADERS))); filePosition += (uint)Marshal.SizeOf(typeof(IMAGE_NT_HEADERS)); Array.Copy(PEUtility.RawSerialize(fileHeader), 0, file, filePosition, Marshal.SizeOf(typeof(IMAGE_FILE_HEADER))); filePosition += (uint)Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)); Array.Copy(PEUtility.RawSerialize(optionalHeader), 0, file, filePosition, Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER32))); filePosition += (uint)Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER32)); return(filePosition); }
public void UpdateVirtualLayout(uint initialVirtualAddress = StartingVirtualAddress) { uint virtualAlignment; switch (optionalStandard.Magic) { case IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_PE32: virtualAlignment = optionalHeader32.SectionAlignment; break; case IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_ROM: throw new NotSupportedException(); case IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_PE32PLUS: virtualAlignment = optionalHeader32plus.SectionAlignment; break; default: throw new ArgumentException(); } /* * Fix up virtual addresses of the sections. * We start at 0x1000 (seems to be the convention) * Text should come first, then followed by data, then reloc * As we encounter certain sections, we need to update * special fields (data directory entries etc.). */ uint virtualPosition = initialVirtualAddress; bool dataSectionEncountered = false; foreach (var s in sections) { //update optional header switch (s.Name) { case TEXT: optionalStandard.BaseOfCode = virtualPosition; break; case RDATA: case DATA: //PE32 headers want to know where the data starts if (optionalStandard.Magic == IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_PE32 && !dataSectionEncountered) { dataSectionEncountered = true; optionalHeader32.BaseOfData = virtualPosition; } break; } //update virtual address if (s.VirtualAddress != virtualPosition) { switch (s.Name) { case RSRC: ResourceTable res; using (var ms = new MemoryStream(s.Data)) { //need to give it the original RVA 'cause that's important res = new ResourceTable(ms, s.VirtualAddress); } res.UpdateVirtualAddress(virtualPosition); //this is only safe because all I've done is changed the virtual address and reserialized the data //this means the data size can only go DOWN (because of alignment not being included) //do not repeat this anywhere else where the data gets changed s.Data = res.ToArray(); goto default; default: s.VirtualAddress = virtualPosition; break; } } //update size if (s.HasUninitializedData) { // Leave uninitialized data sizes untouched, their raw size is 0 } else if (s.HasInitializedData && s.HasCode) { //TODO really not sure why this is here... // It is possible for the virtual size to be greater than the size of raw data // Leave the virtual size untouched if this is the case if (s.VirtualSize <= s.RawSize) { s.VirtualSize = (uint)s.Data.Length; } } virtualPosition += PEUtility.AlignUp(s.VirtualSize, virtualAlignment); } /* Total virtual size is the final virtual address, which includes the initial virtual offset. */ if (optionalStandard.Magic == IMAGE_OPTIONAL_HEADER_STANDARD.MAGIC_PE32) { optionalHeader32.SizeOfImage = virtualPosition; } else { optionalHeader32plus.SizeOfImage = virtualPosition; } }
/// <summary> /// Layout contents of PE file, updating headers and order sections. /// </summary> /// <returns>Returns bool describing if layout succeeded.</returns> public bool Layout() { uint virtualAlignment = optionalHeader.SectionAlignment; uint fileAlignment = optionalHeader.FileAlignment; uint totalSize = 0; uint initializedDataSize = 0; totalSize += optionalHeader.SizeOfHeaders; /* Calculate total physical size required */ foreach (PESection s in sections) { totalSize += PEUtility.AlignUp((uint)s.Data.Length, fileAlignment); } /* Layout the sections in physical order */ uint filePosition = optionalHeader.SizeOfHeaders; sections.Sort(new SectionPhysicalComparer()); foreach (PESection s in sections) { if (s.ContributesToFileSize()) { s.RawSize = PEUtility.AlignUp((uint)s.Data.Length, fileAlignment); s.PhysicalAddress = filePosition; filePosition += s.RawSize; initializedDataSize += PEUtility.AlignUp((uint)s.Data.Length, fileAlignment); } break; } optionalHeader.SizeOfInitializedData = initializedDataSize; /* * Fix up virtual addresses of the sections. * We start at 0x1000 (seems to be the convention) * Text should come first, then followed by data, then reloc * As we encounter certain sections, we need to update * special fields (data directory entries etc.). */ uint virtAddr = 0x1000; bool dataSectionEncountered = false; sections.Sort(new SectionVirtualComparer()); foreach (PESection s in sections) { if (s.Name == ".text") { optionalHeader.BaseOfCode = virtAddr; } if (!dataSectionEncountered && ((s.Name == ".data") || (s.Name == ".rdata"))) { dataSectionEncountered = true; optionalHeader.BaseOfData = virtAddr; } if (s.Name == ".rdata") { dataDirectories.debug.VirtualAddress = virtAddr; } if (s.Name == ".reloc") { dataDirectories.baseReloc.VirtualAddress = virtAddr; } s.VirtualAddress = virtAddr; if (s.HasUninitializedData) { // Leave uninitialized data sizes untouched, their raw size is 0 virtAddr += PEUtility.AlignUp(s.VirtualSize, virtualAlignment); } else if (s.HasInitializedData && s.HasCode) { // It is possible for the virtual size to be greater than the size of raw data // Leave the virtual size untouched if this is the case if (s.VirtualSize > s.RawSize) { virtAddr += PEUtility.AlignUp(s.VirtualSize, virtualAlignment); } else { s.VirtualSize = (uint)s.Data.Length; virtAddr += PEUtility.AlignUp((uint)s.Data.Length, virtualAlignment); } } break; } /* Total virtual size is the final virtual address, which includes the initial virtual offset. */ optionalHeader.SizeOfImage = virtAddr; /* Serialize and write the header contents */ Serialize(totalSize); return(true); }