public PEReader(BinaryReader reader) { // Reset reader position, just in case reader.BaseStream.Seek(0, SeekOrigin.Begin); // Read MS-DOS header section _dosHeader = MarshalBytesTo <IMAGE_DOS_HEADER>(reader); // MS-DOS magic number should read 'MZ' if (_dosHeader.e_magic != 0x5a4d) { throw new InvalidOperationException("File is not a portable executable."); } // Skip MS-DOS stub and seek reader to NT Headers reader.BaseStream.Seek(_dosHeader.e_lfanew, SeekOrigin.Begin); // Read NT Headers _ntHeaders.Signature = MarshalBytesTo <UInt32>(reader); // Make sure we have 'PE' in the pe signature if (_ntHeaders.Signature != 0x4550) { throw new InvalidOperationException("Invalid portable executable signature in NT header."); } _ntHeaders.FileHeader = MarshalBytesTo <IMAGE_FILE_HEADER>(reader); // Read optional headers if (Is32bitAssembly()) { Load32bitOptionalHeaders(reader); } else { Load64bitOptionalHeaders(reader); } // Read section data foreach (IMAGE_SECTION_HEADER header in _sectionHeaders) { // Skip to beginning of a section reader.BaseStream.Seek(header.PointerToRawData, SeekOrigin.Begin); // Read section data... and do something with it byte[] sectiondata = reader.ReadBytes((int)header.SizeOfRawData); } }
public PeFile(byte[] Buffer) { int P = 0; this.Buffer = Buffer; // Read MS-DOS header section DosHeader = MarshalBytesTo <IMAGE_DOS_HEADER>(Buffer, P); // MS-DOS magic number should read 'MZ' if (DosHeader.e_magic != 0x5a4d) { throw new InvalidOperationException("File is not a portable executable."); } // Read NT Headers P = (int)DosHeader.e_lfanew; NtHeaders.Signature = MarshalBytesTo <UInt32>(Buffer, P); // Make sure we have 'PE' in the pe signature if (NtHeaders.Signature != 0x4550) { throw new InvalidOperationException("Invalid portable executable signature in NT header."); } P += sizeof(UInt32); NtHeaders.FileHeader = MarshalBytesTo <IMAGE_FILE_HEADER>(Buffer, P); // Read optional headers P += Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)); if (Is32bitAssembly()) { Load32bitOptionalHeaders(Buffer, P); ImageBase = NtHeaders.OptionalHeader32.ImageBase; EntryPoint = NtHeaders.OptionalHeader32.AddressOfEntryPoint; ExportDirectoryVirtualOffset = NtHeaders.OptionalHeader32.DataDirectory[0].VirtualAddress; ImportDirectoryVirtualOffset = NtHeaders.OptionalHeader32.DataDirectory[1].VirtualAddress; RuntimeDirectoryVirtualOffset = NtHeaders.OptionalHeader32.DataDirectory[3].VirtualAddress; RuntimeDirectorySize = NtHeaders.OptionalHeader32.DataDirectory[3].Size; } else { Load64bitOptionalHeaders(Buffer, P); ImageBase = NtHeaders.OptionalHeader64.ImageBase; EntryPoint = NtHeaders.OptionalHeader64.AddressOfEntryPoint; ExportDirectoryVirtualOffset = NtHeaders.OptionalHeader64.DataDirectory[0].VirtualAddress; ImportDirectoryVirtualOffset = NtHeaders.OptionalHeader64.DataDirectory[1].VirtualAddress; RuntimeDirectoryVirtualOffset = NtHeaders.OptionalHeader64.DataDirectory[3].VirtualAddress; RuntimeDirectorySize = NtHeaders.OptionalHeader64.DataDirectory[3].Size; } // Read Sections _sectionHeaders.ToList().ForEach(s => { byte[] RawCode = new byte[s.SizeOfRawData]; System.Buffer.BlockCopy(Buffer, (int)s.PointerToRawData, RawCode, 0, (int)s.SizeOfRawData); Sections.Add(new Section { Header = s, Buffer = RawCode, VirtualAddress = s.VirtualAddress + (UInt32)ImageBase, VirtualSize = s.Misc.VirtualSize, IsCode = ((s.Characteristics & (uint)Constants.SectionFlags.IMAGE_SCN_CNT_CODE) != 0) }); }); // Read Exports // TODO: Proper support for 64-bit files if (ExportDirectoryVirtualOffset != 0) { IMAGE_EXPORT_DIRECTORY ExportDirectory = MarshalBytesTo <IMAGE_EXPORT_DIRECTORY>(Buffer, (int)ConvertVirtualOffsetToRawOffset((uint)(ExportDirectoryVirtualOffset))); if (ExportDirectory.AddressOfNames != 0) { Section ExportsSection = GetSectionForVirtualAddress((uint)(ImageBase + ExportDirectory.AddressOfNames)); UInt32 OffsetNames = (UInt32)(ImageBase + ExportDirectory.AddressOfNames - ExportsSection.VirtualAddress); UInt32 OffsetOrdinals = (UInt32)(ImageBase + ExportDirectory.AddressOfNameOrdinals - ExportsSection.VirtualAddress); UInt32 OffsetFunctions = (UInt32)(ImageBase + ExportDirectory.AddressOfFunctions - ExportsSection.VirtualAddress); string[] ExportNames = new string[ExportDirectory.NumberOfNames]; UInt16[] Ordinals = new UInt16[ExportDirectory.NumberOfNames]; UInt32[] VirtualAddresses = new UInt32[ExportDirectory.NumberOfFunctions]; for (int i = 0; i < ExportDirectory.NumberOfNames; i++) { UInt32 NamesRVA = ByteOperations.ReadUInt32(ExportsSection.Buffer, (UInt32)(OffsetNames + (i * sizeof(UInt32)))); UInt32 NameOffset = (UInt32)(NamesRVA + ImageBase - ExportsSection.VirtualAddress); ExportNames[i] = ByteOperations.ReadAsciiString(ExportsSection.Buffer, NameOffset); Ordinals[i] = ByteOperations.ReadUInt16(ExportsSection.Buffer, (UInt32)(OffsetOrdinals + (i * sizeof(UInt16)))); } for (int i = 0; i < ExportDirectory.NumberOfFunctions; i++) { VirtualAddresses[i] = ByteOperations.ReadUInt32(ExportsSection.Buffer, (UInt32)(OffsetFunctions + (i * sizeof(UInt32)))); VirtualAddresses[i] -= (VirtualAddresses[i] % 2); // Round down for Thumb2 } for (int i = 0; i < ExportDirectory.NumberOfNames; i++) { Exports.Add(new FunctionDescriptor() { Name = ExportNames[i], VirtualAddress = (UInt32)(ImageBase + VirtualAddresses[Ordinals[i]]) }); } } } // Read Imports // TODO: Proper support for 64-bit files if (ImportDirectoryVirtualOffset != 0) { Section ImportsSection = GetSectionForVirtualAddress((uint)(ImageBase + ImportDirectoryVirtualOffset)); IMAGE_IMPORT_DESCRIPTOR ImportDirectory; do { ImportDirectory = MarshalBytesTo <IMAGE_IMPORT_DESCRIPTOR>(ImportsSection.Buffer, (int)(ImportDirectoryVirtualOffset - (ImportsSection.VirtualAddress - ImageBase))); if (ImportDirectory.OriginalFirstThunk != 0) { // ImportDirectory.OriginalFirstThunk is the VirtualOffset to an array of VirtualOffsets. They point to a struct with a word-value, followed by a zero-terminated ascii-string, which is the name of the import. // ImportDirectory.FirstThunk points to an array pointers which is the actual import table. UInt32 NameArrayOffset = ImportDirectory.OriginalFirstThunk - (ImportsSection.VirtualAddress - (UInt32)ImageBase); UInt32 NameOffset; int i = 0; do { NameOffset = ByteOperations.ReadUInt32(ImportsSection.Buffer, NameArrayOffset); if ((NameOffset < (ImportsSection.VirtualAddress - ImageBase)) || (NameOffset >= (ImportsSection.VirtualAddress + ImportsSection.VirtualSize - ImageBase))) { NameOffset = 0; // ImportDirectory.OriginalFirstThunk seems to contain Characteristics, not an offset to an array. } NameArrayOffset += sizeof(UInt32); if (NameOffset != 0) { string Name = ByteOperations.ReadAsciiString(ImportsSection.Buffer, NameOffset + 2 - (ImportsSection.VirtualAddress - (UInt32)ImageBase)); Imports.Add(new FunctionDescriptor() { Name = Name, VirtualAddress = ImportDirectory.FirstThunk + (UInt32)ImageBase + (UInt32)(i * sizeof(UInt32)) }); i++; } }while (NameOffset != 0); ImportDirectoryVirtualOffset += (UInt64)Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR)); } }while (ImportDirectory.OriginalFirstThunk != 0); } // Read Runtime functions // TODO: Proper support for 64-bit files if (RuntimeDirectoryVirtualOffset != 0) { Section RuntimeSection = GetSectionForVirtualAddress((uint)(ImageBase + RuntimeDirectoryVirtualOffset)); RUNTIME_FUNCTION_32 RuntimeFunction; for (int i = 0; i < (RuntimeDirectorySize / Marshal.SizeOf(typeof(RUNTIME_FUNCTION_32))); i++) { RuntimeFunction = MarshalBytesTo <RUNTIME_FUNCTION_32>(RuntimeSection.Buffer, (int)(RuntimeDirectoryVirtualOffset - (RuntimeSection.VirtualAddress - ImageBase)) + (i * Marshal.SizeOf(typeof(RUNTIME_FUNCTION_32)))); RuntimeFunctions.Add(new FunctionDescriptor() { Name = null, VirtualAddress = (UInt32)(RuntimeFunction.RVAofBeginAddress + ImageBase) }); } } }