/// <summary> /// Locate the <see cref="IMAGE_SECTION_HEADER"/> that corresponds to the given RVA /// </summary> /// <param name="rva">The address to map to a header.</param> /// <returns></returns> public IMAGE_SECTION_HEADER GetSectionFromRva(uint rva) { IMAGE_SECTION_HEADER section = new IMAGE_SECTION_HEADER(); var sections = ImageSectionHeaders.ToArray(); int i = 0; while (i < FileHeader.NumberOfSections) { if (rva < sections[i].VirtualAddress + AlignTo(sections[i].VirtualSize, SectionAlignment)) { if (rva < sections[i].VirtualAddress) { return(section); } else { return(sections[i]); } } i++; } return(section); }
public PeHeaderReader(RemoteProcess process, IntPtr moduleBase) : base(process, moduleBase) { DosHeader = new ImageDosHeader(process, moduleBase); NtHeader = new ImageNtHeader(process, moduleBase + (int)DosHeader.e_lfanew, Is64Bit); var numSections = (int)NtHeader.FileHeader.NumberOfSections; var secHeaderStart = DosHeader.e_lfanew + NtHeader.FileHeader.SizeOfOptionalHeader + 0x18; SectionHeaders = new ImageSectionHeaders(process, moduleBase + (int)secHeaderStart, numSections); var exportDirVa = NtHeader.OptionalHeader.DataDirectory[0].VirtualAddress; if (exportDirVa != 0) { ExportDirectory = new ImageExportDirectory(process, moduleBase + (int)exportDirVa); } }
/// <summary> /// Add imports to the PE file. /// </summary> /// <param name="additionalImports">List with additional imports.</param> public void AddImports(List <AdditionalImport> additionalImports) { if (ImageNtHeaders is null || ImageSectionHeaders is null || _dataDirectoryParsers is null) { throw new Exception("NT Headers, Section Headers and Data Directory must not be null."); } const int sizeOfImpDesc = 0x14; var sizeOfThunkData = Is32Bit ? 4 : 8; var numAddImpDescs = additionalImports.Count; var importRva = ImageNtHeaders.OptionalHeader.DataDirectory[(int)DataDirectoryType.Import].VirtualAddress; var importSize = ImageNtHeaders.OptionalHeader.DataDirectory[(int)DataDirectoryType.Import].Size; ImageSectionHeader GetImportSection() => ImageSectionHeaders.First(sh => sh.VirtualAddress + sh.VirtualSize >= importRva); int EstimateAdditionalNeededSpace() => additionalImports.Select(ai => ai.Functions).Count() * 64; var impSection = GetImportSection(); var newUnalignedRawSecSize = EstimateAdditionalNeededSpace(); // First copy the current import descriptor array to the start of the new section to have enough space to // add additional import descriptors. AddSection(".addImp", (int)(impSection !.SizeOfRawData + newUnalignedRawSecSize), (ScnCharacteristicsType)0xC0000000); var newImpSec = ImageSectionHeaders.First(sh => sh.Name == ".addImp"); var oldImpDescBytes = RawFile.AsSpan(importRva.RvaToOffset(ImageSectionHeaders), importSize); RawFile.WriteBytes(newImpSec.PointerToRawData, oldImpDescBytes); // Set the import data directory to the new import section and adjust the size ImageNtHeaders.OptionalHeader.DataDirectory[(int)DataDirectoryType.Import].VirtualAddress = newImpSec.VirtualAddress; ImageNtHeaders.OptionalHeader.DataDirectory[(int)DataDirectoryType.Import].Size = (uint)(importSize + (sizeOfImpDesc * numAddImpDescs)); var newImportRva = ImageNtHeaders.OptionalHeader.DataDirectory[(int)DataDirectoryType.Import].VirtualAddress; var newImportSize = ImageNtHeaders.OptionalHeader.DataDirectory[(int)DataDirectoryType.Import].Size; var paAdditionalSpace = newImpSec.PointerToRawData + newImportSize; // Update import descriptors and imported functions to reflect the new // position in the new section. _dataDirectoryParsers.ReparseImportDescriptors(ImageSectionHeaders); _dataDirectoryParsers.ReparseImportedFunctions(); uint AddModName(ref uint offset, string module) { var tmp = Encoding.ASCII.GetBytes(module); var mName = new byte[tmp.Length + 1]; Array.Copy(tmp, mName, tmp.Length); var paName = offset; RawFile.WriteBytes(offset, mName); offset = (uint)(offset + mName.Length); return(paName); } List <uint> AddImpByNames(ref uint offset, List <string> funcs) { var adrList = new List <uint>(); foreach (var f in funcs) { var ibn = new ImageImportByName(RawFile, offset) { Hint = 0, Name = f }; adrList.Add(offset); offset += (uint)ibn.Name.Length + 2; } // Add zero DWORD to end array RawFile.WriteUInt(offset + 1, 0); offset += 5; return(adrList); } uint AddThunkDatas(ref uint offset, List <uint> adrList) { var paThunkStart = offset; foreach (var adr in adrList) { _ = new ImageThunkData(RawFile, offset, Is64Bit) { AddressOfData = adr.OffsetToRva(ImageSectionHeaders !) }; offset += (uint)sizeOfThunkData; } // End array with empty thunk data _ = new ImageThunkData(RawFile, offset, Is64Bit) { AddressOfData = 0 }; offset += (uint)sizeOfThunkData; return(paThunkStart); } void AddImportWithNewImpDesc(ref uint tmpOffset, ref long paIdesc, AdditionalImport ai) { var paName = AddModName(ref tmpOffset, ai.Module); var funcAdrs = AddImpByNames(ref tmpOffset, ai.Functions); var thunkAdrs = AddThunkDatas(ref tmpOffset, funcAdrs); _ = new ImageImportDescriptor(RawFile, paIdesc) { Name = paName.OffsetToRva(ImageSectionHeaders), OriginalFirstThunk = 0, FirstThunk = thunkAdrs.OffsetToRva(ImageSectionHeaders), ForwarderChain = 0, TimeDateStamp = 0 }; paIdesc += (uint)sizeOfImpDesc; } var paIdesc = newImportRva.RvaToOffset(ImageSectionHeaders) + ImageImportDescriptors !.Length * sizeOfImpDesc; var tmpOffset = paAdditionalSpace; // Add new imports foreach (var ai in additionalImports) { AddImportWithNewImpDesc(ref tmpOffset, ref paIdesc, ai); } // End with zero filled idesc _ = new ImageImportDescriptor(RawFile, paIdesc) { Name = 0, OriginalFirstThunk = 0, FirstThunk = 0, ForwarderChain = 0, TimeDateStamp = 0 }; // Reparse imports _dataDirectoryParsers.ReparseImportDescriptors(ImageSectionHeaders); _dataDirectoryParsers.ReparseImportedFunctions(); } }
/// <summary> /// Remove a section from the PE file. /// </summary> /// <param name="name">Name of the section to remove.</param> /// <param name="removeContent">Flag if the content should be removed or only the section header entry.</param> public void RemoveSection(string name, bool removeContent = true) { var sectionToRemove = ImageSectionHeaders.First(s => s.Name == name); // Remove section from list of sections var newSections = ImageSectionHeaders.Where(s => s.Name != name).ToArray(); // Change number of sections in the file header ImageNtHeaders !.FileHeader.NumberOfSections--; if (removeContent) { // Reloc the physical address of all sections foreach (var s in newSections) { if (s.PointerToRawData > sectionToRemove.PointerToRawData) { s.PointerToRawData -= sectionToRemove.SizeOfRawData; } } // Remove section content RawFile.RemoveRange(sectionToRemove.PointerToRawData, sectionToRemove.SizeOfRawData); } // Fix virtual size for (var i = 1; i < newSections.Count(); i++) { if (newSections[i - 1].VirtualAddress < sectionToRemove.VirtualAddress) { newSections[i - 1].VirtualSize = newSections[i].VirtualAddress - newSections[i - 1].VirtualAddress; } } // Replace old section headers with new section headers var sectionHeaderOffset = ImageDosHeader !.E_lfanew + ImageNtHeaders !.FileHeader.SizeOfOptionalHeader + 0x18; var sizeOfSection = 0x28; var newRawSections = new byte[newSections.Count() * sizeOfSection]; for (var i = 0; i < newSections.Count(); i++) { Array.Copy(newSections[i].ToArray(), 0, newRawSections, i * sizeOfSection, sizeOfSection); } // Null the data directory entry if any available var de = ImageNtHeaders .OptionalHeader .DataDirectory .FirstOrDefault(d => d.VirtualAddress == sectionToRemove.VirtualAddress && d.Size == sectionToRemove.VirtualSize); if (de != null) { de.Size = 0; de.VirtualAddress = 0; } // Null the old section headers RawFile.WriteBytes(sectionHeaderOffset, new byte[ImageSectionHeaders.Count() * sizeOfSection]); // Write the new sections headers RawFile.WriteBytes(sectionHeaderOffset, newRawSections); // Reparse section header _nativeStructureParsers.ReparseSectionHeaders(); }
/// <summary> /// Add a new section to the PE file. /// </summary> /// <param name="name">Name of the section to add. At max. 8 characters.</param> /// <param name="size">Size in bytes of the new section.</param> /// <param name="characteristics">Section characteristics.</param> public void AddSection(string name, int size, ScnCharacteristicsType characteristics) { if (ImageNtHeaders is null) { throw new Exception("IMAGE_NT_HEADERS must not be null."); } if (ImageDosHeader is null) { throw new Exception("IMAGE_DOS_HEADER must not be null"); } uint getNewSizeOfImage() { var factor = size / (double)ImageNtHeaders.OptionalHeader.SectionAlignment; var additionalSize = (uint)Math.Ceiling(factor) * ImageNtHeaders !.OptionalHeader.SectionAlignment; return(ImageNtHeaders.OptionalHeader.SizeOfImage + additionalSize); } uint getNewSecHeaderOffset() { var sizeOfSection = 0x28; var x = (uint)ImageNtHeaders !.FileHeader.SizeOfOptionalHeader + 0x18; var startOfSectionHeader = ImageDosHeader.E_lfanew + x; return((uint)(startOfSectionHeader + (ImageNtHeaders.FileHeader.NumberOfSections * sizeOfSection))); } uint getNewSecVA() { var lastSec = ImageSectionHeaders.OrderByDescending(sh => sh.VirtualAddress).First(); var vaLastSecEnd = lastSec.VirtualAddress + lastSec.VirtualSize; var factor = vaLastSecEnd / (double)ImageNtHeaders.OptionalHeader.SectionAlignment; return((uint)(Math.Ceiling(factor) * ImageNtHeaders.OptionalHeader.SectionAlignment)); } // Append new section to end of file var paNewSec = RawFile.AppendBytes(new Byte[size]); // Add new entry in section table var newSection = new ImageSectionHeader(RawFile, getNewSecHeaderOffset(), ImageNtHeaders.OptionalHeader.ImageBase) { Name = name, VirtualSize = (uint)size, VirtualAddress = getNewSecVA(), SizeOfRawData = (uint)size, PointerToRawData = (uint)paNewSec, PointerToRelocations = 0, PointerToLinenumbers = 0, NumberOfRelocations = 0, NumberOfLinenumbers = 0, Characteristics = characteristics }; // Increase number of sections ImageNtHeaders.FileHeader.NumberOfSections = (ushort)(ImageNtHeaders.FileHeader.NumberOfSections + 1); // Adjust image size by image alignment ImageNtHeaders.OptionalHeader.SizeOfImage = getNewSizeOfImage(); // Reparse section headers _nativeStructureParsers.ReparseSectionHeaders(); }