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