/// <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; }
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); }