From 129602cc8c1dba2141d409026f782accfca88f68 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 4 Jun 2025 14:01:33 -0400 Subject: [PATCH] GP-4881: Prevent PE Data Directory parse failures from making the import fail --- .../app/util/bin/format/mz/DOSHeader.java | 8 +- .../app/util/bin/format/pe/NTHeader.java | 28 +- .../util/bin/format/pe/OptionalHeader.java | 1077 ++++++++++++++--- .../bin/format/pe/OptionalHeaderImpl.java | 812 ------------- .../bin/format/pe/PortableExecutable.java | 17 +- .../ghidra/app/util/opinion/PeLoader.java | 2 +- 6 files changed, 906 insertions(+), 1038 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeaderImpl.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/mz/DOSHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/mz/DOSHeader.java index 07e8c9e526..012f715b16 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/mz/DOSHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/mz/DOSHeader.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -150,8 +150,8 @@ public class DOSHeader extends OldDOSHeader { if (e_lfanew >= 0 && e_lfanew <= 0x1000000) { try { NTHeader ntHeader = - new NTHeader(reader, e_lfanew, SectionLayout.FILE, false, false); - if (ntHeader != null && ntHeader.getOptionalHeader() != null) { + new NTHeader(reader, e_lfanew, SectionLayout.FILE, false); + if (ntHeader.getOptionalHeader() != null) { return true; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java index ff98b38043..b789e89dd1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,8 +25,6 @@ import ghidra.program.model.data.*; import ghidra.util.DataConverter; import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.NotYetImplementedException; -import ghidra.util.task.TaskMonitor; /** * A class to represent the IMAGE_NT_HEADERS32 and @@ -54,7 +52,6 @@ public class NTHeader implements StructConverter, OffsetValidator { private OptionalHeader optionalHeader; private BinaryReader reader; private int index; - private boolean advancedProcess = true; private boolean parseCliHeaders = false; private SectionLayout layout = SectionLayout.FILE; @@ -64,18 +61,16 @@ public class NTHeader implements StructConverter, OffsetValidator { * @param reader the binary reader * @param index the index into the reader to the start of the NT header * @param layout The {@link SectionLayout} - * @param advancedProcess if true, information outside of the base header will be processed * @param parseCliHeaders if true, CLI headers are parsed (if present) * @throws InvalidNTHeaderException if the bytes the specified index * @throws IOException if an IO-related exception occurred * do not constitute an accurate NT header. */ - public NTHeader(BinaryReader reader, int index, SectionLayout layout, boolean advancedProcess, - boolean parseCliHeaders) throws InvalidNTHeaderException, IOException { + public NTHeader(BinaryReader reader, int index, SectionLayout layout, boolean parseCliHeaders) + throws InvalidNTHeaderException, IOException { this.reader = reader; this.index = index; this.layout = layout; - this.advancedProcess = advancedProcess; this.parseCliHeaders = parseCliHeaders; parse(); @@ -264,14 +259,7 @@ public class NTHeader implements StructConverter, OffsetValidator { } tmpIndex += FileHeader.IMAGE_SIZEOF_FILE_HEADER; - // Process Optional Header. Abort load on failure. - try { - optionalHeader = new OptionalHeaderImpl(this, reader, tmpIndex); - } - catch (NotYetImplementedException e) {//TODO - Msg.error(this, "Unexpected Exception: " + e.getMessage()); - return; - } + optionalHeader = new OptionalHeader(this, reader, tmpIndex); // Process symbols. Allow parsing to continue on failure. boolean symbolsProcessed = false; @@ -286,12 +274,6 @@ public class NTHeader implements StructConverter, OffsetValidator { // Process sections. Resolving some sections names (i.e., "/21") requires symbols to have // been successfully processed. Resolving is optional though. fileHeader.processSections(optionalHeader, symbolsProcessed); - - // Perform advanced processing. If advanced processing is disabled, these things may be - // independently parsed later in the load if they are needed. - if (advancedProcess) { - optionalHeader.processDataDirectories(TaskMonitor.DUMMY); - } } void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeader.java index 03a0344389..48cf8620ad 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeader.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,12 +18,92 @@ package ghidra.app.util.bin.format.pe; import java.io.IOException; import java.io.RandomAccessFile; +import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOutOfBoundsException; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.MemoryAccessException; import ghidra.util.DataConverter; +import ghidra.util.Msg; +import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; -public interface OptionalHeader extends StructConverter { +/** + *
{@code
+ * typedef struct _IMAGE_OPTIONAL_HEADER {
+ *     WORD    Magic;									// MANDATORY
+ *     BYTE    MajorLinkerVersion;
+ *     BYTE    MinorLinkerVersion;
+ *     DWORD   SizeOfCode;
+ *     DWORD   SizeOfInitializedData;
+ *     DWORD   SizeOfUninitializedData;
+ *     DWORD   AddressOfEntryPoint;						// MANDATORY
+ *     DWORD   BaseOfCode;
+ *     DWORD   BaseOfData;
+ *     DWORD   ImageBase;								// MANDATORY
+ *     DWORD   SectionAlignment;						// MANDATORY
+ *     DWORD   FileAlignment;							// MANDATORY
+ *     WORD    MajorOperatingSystemVersion;				// MANDATORY
+ *     WORD    MinorOperatingSystemVersion;
+ *     WORD    MajorImageVersion;
+ *     WORD    MinorImageVersion;
+ *     WORD    MajorSubsystemVersion;
+ *     WORD    MinorSubsystemVersion;
+ *     DWORD   Win32VersionValue;
+ *     DWORD   SizeOfImage;								// MANDATORY
+ *     DWORD   SizeOfHeaders;							// MANDATORY
+ *     DWORD   CheckSum;
+ *     WORD    Subsystem;								// MANDATORY
+ *     WORD    DllCharacteristics;
+ *     DWORD   SizeOfStackReserve;
+ *     DWORD   SizeOfStackCommit;
+ *     DWORD   SizeOfHeapReserve;
+ *     DWORD   SizeOfHeapCommit;
+ *     DWORD   LoaderFlags;
+ *     DWORD   NumberOfRvaAndSizes;						// USED
+ *     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ * };
+ * 
+ * typedef struct _IMAGE_OPTIONAL_HEADER64 {
+ *     WORD        Magic;
+ *     BYTE        MajorLinkerVersion;
+ *     BYTE        MinorLinkerVersion;
+ *     DWORD       SizeOfCode;
+ *     DWORD       SizeOfInitializedData;
+ *     DWORD       SizeOfUninitializedData;
+ *     DWORD       AddressOfEntryPoint;
+ *     DWORD       BaseOfCode;
+ *     ULONGLONG   ImageBase;
+ *     DWORD       SectionAlignment;
+ *     DWORD       FileAlignment;
+ *     WORD        MajorOperatingSystemVersion;
+ *     WORD        MinorOperatingSystemVersion;
+ *     WORD        MajorImageVersion;
+ *     WORD        MinorImageVersion;
+ *     WORD        MajorSubsystemVersion;
+ *     WORD        MinorSubsystemVersion;
+ *     DWORD       Win32VersionValue;
+ *     DWORD       SizeOfImage;
+ *     DWORD       SizeOfHeaders;
+ *     DWORD       CheckSum;
+ *     WORD        Subsystem;
+ *     WORD        DllCharacteristics;
+ *     ULONGLONG   SizeOfStackReserve;
+ *     ULONGLONG   SizeOfStackCommit;
+ *     ULONGLONG   SizeOfHeapReserve;
+ *     ULONGLONG   SizeOfHeapCommit;
+ *     DWORD       LoaderFlags;
+ *     DWORD       NumberOfRvaAndSizes;
+ *     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ * };
+ * }
+ */ +public class OptionalHeader implements StructConverter { /** * ASLR with 64 bit address space. @@ -150,212 +230,717 @@ public interface OptionalHeader extends StructConverter { */ public final static byte IMAGE_DIRECTORY_ENTRY_COMHEADER = 14; - /** - * Returns true of this optional header is 64-bit. - * @return true of this optional header is 64-bit - */ - public boolean is64bit(); + protected short magic; + protected byte majorLinkerVersion; + protected byte minorLinkerVersion; + protected int sizeOfCode; + protected int sizeOfInitializedData; + protected int sizeOfUninitializedData; + protected int addressOfEntryPoint; + protected int baseOfCode; + protected int baseOfData; + protected long imageBase; + protected int sectionAlignment; + protected int fileAlignment; + protected short majorOperatingSystemVersion; + protected short minorOperatingSystemVersion; + protected short majorImageVersion; + protected short minorImageVersion; + protected short majorSubsystemVersion; + protected short minorSubsystemVersion; + protected int win32VersionValue; + protected int sizeOfImage; + protected int sizeOfHeaders; + protected int checkSum; + protected short subsystem; + protected short dllCharacteristics; + protected long sizeOfStackReserve; + protected long sizeOfStackCommit; + protected long sizeOfHeapReserve; + protected long sizeOfHeapCommit; + protected int loaderFlags; + protected int numberOfRvaAndSizes; + protected DataDirectory[] dataDirectory; - /** - * Return the major version number of the linker that built this binary. - * @return - */ - public byte getMajorLinkerVersion(); + protected NTHeader ntHeader; + protected BinaryReader reader; + protected int startIndex; + private long startOfDataDirs; - /** - * Return the minor version number of the linker that built this binary. - * @return - */ - public byte getMinorLinkerVersion(); + OptionalHeader(NTHeader ntHeader, BinaryReader reader, int startIndex) throws IOException { + this.ntHeader = ntHeader; + this.reader = reader; + this.startIndex = startIndex; - /** - * Return the major version number of the required operating system. - * @return - */ - public short getMajorOperatingSystemVersion(); + parse(); + } - /** - * Return the minor version number of the required operating system. - * @return - */ - public short getMinorOperatingSystemVersion(); + protected void parse() throws IOException { + reader.setPointerIndex(startIndex); - /** - * Get the major version number of the image. - * @return - */ - public short getMajorImageVersion(); + magic = reader.readNextShort(); + if (magic != Constants.IMAGE_ROM_OPTIONAL_HDR_MAGIC && + magic != Constants.IMAGE_NT_OPTIONAL_HDR32_MAGIC && + magic != Constants.IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + Msg.warn(this, "Unsupported magic value: 0x%x. Assuming 32-bit.".formatted(magic)); + } + majorLinkerVersion = reader.readNextByte(); + minorLinkerVersion = reader.readNextByte(); + sizeOfCode = reader.readNextInt(); + sizeOfInitializedData = reader.readNextInt(); + sizeOfUninitializedData = reader.readNextInt(); + addressOfEntryPoint = reader.readNextInt(); + // NB: 0 or negative addressOfEntryPoint is legal + if (addressOfEntryPoint < 0) { + Msg.warn(this, "Negative entry point " + Integer.toHexString(addressOfEntryPoint)); + } + if (addressOfEntryPoint == 0) { + int characteristics = ntHeader.getFileHeader().getCharacteristics(); + if ((characteristics & FileHeader.IMAGE_FILE_DLL) == 0) { + Msg.warn(this, "Zero entry point for non-DLL"); + } + } + baseOfCode = reader.readNextInt(); - /** - * Get the minor version number of the image. - * @return - */ - public short getMinorImageVersion(); + if (is64bit()) { + baseOfData = -1;//not used + imageBase = reader.readNextLong(); + } + else { + baseOfData = reader.readNextInt(); + imageBase = Integer.toUnsignedLong(reader.readNextInt()); + } - /** - * Get the major version number of the subsystem. - */ - public short getMajorSubsystemVersion(); + sectionAlignment = reader.readNextInt(); + fileAlignment = reader.readNextInt(); + if (fileAlignment < 0x200) { + Msg.warn(this, "Unusual file alignment: 0x" + Integer.toHexString(fileAlignment)); + } + majorOperatingSystemVersion = reader.readNextShort(); + minorOperatingSystemVersion = reader.readNextShort(); + majorImageVersion = reader.readNextShort(); + minorImageVersion = reader.readNextShort(); + majorSubsystemVersion = reader.readNextShort(); + minorSubsystemVersion = reader.readNextShort(); + win32VersionValue = reader.readNextInt(); + sizeOfImage = reader.readNextInt(); + sizeOfHeaders = reader.readNextInt(); + if (sizeOfHeaders >= sizeOfImage) { + Msg.warn(this, "Size of headers >= size of image: forced load"); + } + checkSum = reader.readNextInt(); + subsystem = reader.readNextShort(); + dllCharacteristics = reader.readNextShort(); - /** - * Get the minor version number of the subsystem. - * @return - */ - public short getMinorSubsystemVersion(); + if (is64bit()) { + sizeOfStackReserve = reader.readNextLong(); + sizeOfStackCommit = reader.readNextLong(); + sizeOfHeapReserve = reader.readNextLong(); + sizeOfHeapCommit = reader.readNextLong(); + } + else { + sizeOfStackReserve = reader.readNextUnsignedInt(); + sizeOfStackCommit = reader.readNextUnsignedInt(); + sizeOfHeapReserve = reader.readNextUnsignedInt(); + sizeOfHeapCommit = reader.readNextUnsignedInt(); + } - /** - * This value is reserved, and must be 0 - */ - public int getWin32VersionValue(); + loaderFlags = reader.readNextInt(); + numberOfRvaAndSizes = reader.readNextInt(); - /** - * Get the image file checksum. - * @return - */ - public int getChecksum(); + if (numberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) { + Msg.warn(this, "Non-standard # of data directories: " + numberOfRvaAndSizes); + if (numberOfRvaAndSizes > IMAGE_NUMBEROF_DIRECTORY_ENTRIES || numberOfRvaAndSizes < 0) { + Msg.warn(this, + "Forcing # of data directories to: " + IMAGE_NUMBEROF_DIRECTORY_ENTRIES); + numberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; + } + } - /** - * Get the subsystem that is required to run this image. - * @return - */ - public int getSubsystem(); - - /** - * Return flags that describe properties of and features of this binary. - * @see ghidra.app.util.bin.format.pe.DllCharacteristics - * @return - */ - public short getDllCharacteristics(); - - /** - * Return the size of the stack reservation - * @return - */ - public long getSizeOfStackReserve(); - - /** - * Return the size of the stack to commit - * @return - */ - public long getSizeOfStackCommit(); - - /** - * Return the size of the heap reservation - * @return - */ - public long getSizeOfHeapReserve(); - - /** - * Return the size of the heap to commit - * @return - */ - public long getSizeOfHeapCommit(); - - /** - * Return the flags passed to the loader. Obsolete. - * @return - */ - public int getLoaderFlags(); - - /** - * @return the RVA of the first code byte in the file that will be executed - */ - public long getAddressOfEntryPoint(); - - /** - * @return the preferred load address of this file in memory - */ - public long getImageBase(); - - /** - * @return the RVA that would be assigned to the next section following the last section - */ - public long getSizeOfImage(); - - /** - * @see #getSizeOfImage() - */ - public void setSizeOfImage(long size); - - /** - * @return the combined size of all headers - */ - public long getSizeOfHeaders(); - - /** - * @see #getSizeOfHeaders() - */ - public void setSizeOfHeaders(long size); - - /** - * Returns the combined total size of all sections with - * the IMAGE_SCN_CNT_CODE attribute. - * @return the combined total size of all sections with - * the IMAGE_SCN_CNT_CODE attribute. - */ - public long getSizeOfCode(); - - /** - * @see #getSizeOfCode() - */ - public void setSizeOfCode(long size); - - public long getNumberOfRvaAndSizes(); - - /** - * Returns the combined size of all initialized data sections. - * @return the combined size of all initialized data sections - */ - public long getSizeOfInitializedData(); - - /** - * @see #getSizeOfInitializedData() - */ - public void setSizeOfInitializedData(long size); - - /** - * Returns the size of all sections with the uninitialized - * data attributes. - * @return the size of all sections with the uninitialized data attributes - */ - public long getSizeOfUninitializedData(); - - /** - * @see #getSizeOfUninitializedData() - */ - public void setSizeOfUninitializedData(long size); - - /** - * Returns the RVA of the first byte of code when loaded in memory. - * @return the RVA of the first byte of code when loaded in memory - */ - public long getBaseOfCode(); - - /** - * @return the RVA of the first byte of data when loaded into memory - */ - public long getBaseOfData(); + startOfDataDirs = reader.getPointerIndex(); + } /** * This methods tells this optional header to process its data directories. + * + * @param log The log + * @param monitor The monitor */ - public void processDataDirectories(TaskMonitor monitor) throws IOException; + public void processDataDirectories(MessageLog log, TaskMonitor monitor) { + reader.setPointerIndex(startOfDataDirs); + + dataDirectory = new DataDirectory[numberOfRvaAndSizes]; + if (numberOfRvaAndSizes == 0) { + return; + } + + int ndata = 0; + monitor.setMessage("Parsing exports..."); + try { + dataDirectory[ndata] = new ExportDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(ExportDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing imports..."); + try { + dataDirectory[ndata] = new ImportDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(ImportDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing resources..."); + try { + dataDirectory[ndata] = new ResourceDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(ResourceDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing exceptions..."); + try { + dataDirectory[ndata] = new ExceptionDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(ExceptionDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing security..."); + try { + dataDirectory[ndata] = new SecurityDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(SecurityDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing relocations..."); + try { + dataDirectory[ndata] = new BaseRelocationDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(BaseRelocationDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing debug information..."); + try { + dataDirectory[ndata] = new DebugDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(DebugDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing architecture..."); + try { + dataDirectory[ndata] = new ArchitectureDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(ArchitectureDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing global pointer..."); + try { + dataDirectory[ndata] = new GlobalPointerDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(GlobalPointerDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing TLS data..."); + try { + dataDirectory[ndata] = new TLSDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(GlobalPointerDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing load config data..."); + try { + dataDirectory[ndata] = new LoadConfigDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(LoadConfigDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing bound imports..."); + try { + dataDirectory[ndata] = new BoundImportDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(BoundImportDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing import address table..."); + try { + dataDirectory[ndata] = new ImportAddressTableDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(ImportAddressTableDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing delay imports..."); + try { + dataDirectory[ndata] = new DelayImportDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(DelayImportDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + monitor.setMessage("Parsing COM descriptors..."); + try { + dataDirectory[ndata] = new COMDescriptorDataDirectory(ntHeader, reader); + } + catch (RuntimeException re) { + if (PortableExecutable.DEBUG) { + throw re; + } + } + catch (IOException e) { + log.appendMsg(COMDescriptorDataDirectory.class.getSimpleName(), e.getMessage()); + } + if (++ndata == numberOfRvaAndSizes) { + return; + } + + dataDirectory[ndata] = null; + } /** - * Returns the array of data directories. - * @return the array of data directories + * {@return true of this optional header is 64-bit.} */ - public DataDirectory[] getDataDirectories(); + public boolean is64bit() { + return magic == Constants.IMAGE_NT_OPTIONAL_HDR64_MAGIC; + } /** - * @return the section alignment + * {@return the major version number of the linker that built this binary.} */ - public int getSectionAlignment(); + public byte getMajorLinkerVersion() { + return majorLinkerVersion; + } /** - * @return the file alignment + * {@return the minor version number of the linker that built this binary.} */ - public int getFileAlignment(); + public byte getMinorLinkerVersion() { + return minorLinkerVersion; + } + + /** + * {@return the combined total size of all sections with IMAGE_SCN_CNT_CODE attribute.} + */ + public long getSizeOfCode() { + return sizeOfCode; + } + + /** + * Sets the combined total size of all sections with the IMAGE_SCN_CNT_CODE attribute. + * + * @param size The size to set + */ + public void setSizeOfCode(long size) { + this.sizeOfCode = (int) size; + } + + /** + * {@return the combined size of all initialized data sections.} + */ + public long getSizeOfInitializedData() { + return Integer.toUnsignedLong(sizeOfInitializedData); + } + + /** + * Sets the combined size of all initialized data sections} + * + * @param size The size to set + */ + public void setSizeOfInitializedData(long size) { + this.sizeOfInitializedData = (int) size; + } + + /** + * {@return the size of all sections with the uninitialized data attributes.} + */ + public long getSizeOfUninitializedData() { + return Integer.toUnsignedLong(sizeOfUninitializedData); + } + + /** + * Sets the size of all sections with the uninitialized data attributes.} + * + * @param size The size to set + */ + public void setSizeOfUninitializedData(long size) { + this.sizeOfUninitializedData = (int) size; + } + + /** + * {@return the RVA of the first code byte in the file that will be executed} + */ + public long getAddressOfEntryPoint() { + return Integer.toUnsignedLong(addressOfEntryPoint); + } + + /** + * {@return the RVA of the first byte of code when loaded in memory.} + */ + public long getBaseOfCode() { + return Integer.toUnsignedLong(baseOfCode); + } + + /** + * {@return the RVA of the first byte of data when loaded into memory.} + */ + public long getBaseOfData() { + return Integer.toUnsignedLong(baseOfData); + } + + /** + * {@return the preferred load address of this file in memory} + */ + public long getImageBase() { + return imageBase; + } + + /** + * {@return the section alignment} + */ + public int getSectionAlignment() { + return sectionAlignment; + } + + /** + * {@return the file alignment} + */ + public int getFileAlignment() { + return fileAlignment; + } + + /** + * {@return the major version number of the required operating system.} + */ + public short getMajorOperatingSystemVersion() { + return majorOperatingSystemVersion; + } + + /** + * {@return the minor version number of the required operating system.} + */ + public short getMinorOperatingSystemVersion() { + return minorOperatingSystemVersion; + } + + /** + * {@return the major version number of the image.} + */ + public short getMajorImageVersion() { + return majorImageVersion; + } + + /** + * {@return the minor version number of the image.} + */ + public short getMinorImageVersion() { + return minorImageVersion; + } + + /** + * {@return the major version number of the subsystem.} + */ + public short getMajorSubsystemVersion() { + return majorSubsystemVersion; + } + + /** + * {@return the minor version number of the subsystem.} + */ + public short getMinorSubsystemVersion() { + return minorSubsystemVersion; + } + + /** + * {@return the reserved value, which must be 0} + */ + public int getWin32VersionValue() { + return win32VersionValue; + } + + /** + * {@return the RVA that would be assigned to the next section following the last section} + */ + public long getSizeOfImage() { + return Integer.toUnsignedLong(sizeOfImage); + } + + /** + * Sets the RVA that would be assigned to the next section following the last section + * + * @param size The size to set + */ + public void setSizeOfImage(long size) { + this.sizeOfImage = (int) size; + } + + /** + * {@return the combined size of all headers} + */ + public long getSizeOfHeaders() { + return Integer.toUnsignedLong(sizeOfHeaders); + } + + /** + * Sets the combined size of all headers + * + * @param size The size to set + */ + public void setSizeOfHeaders(long size) { + this.sizeOfHeaders = (int) size; + } + + /** + * {@return the image file checksum.} + */ + public int getChecksum() { + return checkSum; + } + + /** + * {@return the subsystem that is required to run this image.} + */ + public int getSubsystem() { + return subsystem; + } + + /** + * {@return the flags that describe properties of and features of this binary.} + * @see ghidra.app.util.bin.format.pe.DllCharacteristics + */ + public short getDllCharacteristics() { + return dllCharacteristics; + } + + /** + * {@return the size of the stack reservation} + */ + public long getSizeOfStackReserve() { + return sizeOfStackReserve; + } + + /** + * {@return the size of the stack to commit} + */ + public long getSizeOfStackCommit() { + return sizeOfStackCommit; + } + + /** + * {@return the size of the heap reservation} + */ + public long getSizeOfHeapReserve() { + return sizeOfHeapReserve; + } + + /** + * {@return the size of the heap to commit} + */ + public long getSizeOfHeapCommit() { + return sizeOfHeapCommit; + } + + /** + * {@return the flags passed to the loader. Obsolete.} + */ + public int getLoaderFlags() { + return loaderFlags; + } + + /** + * {@return the number of data-directory entries in the remainder of the optional header.} + */ + public long getNumberOfRvaAndSizes() { + return Integer.toUnsignedLong(numberOfRvaAndSizes); + } + + /** + * {@return the array of data directories.} + */ + public DataDirectory[] getDataDirectories() { + return dataDirectory; + } + + @Override + public DataType toDataType() throws DuplicateNameException { + StructureDataType ddstruct = new StructureDataType(DataDirectory.TITLE, 0); + ddstruct.add(IBO32, "VirtualAddress", null); + ddstruct.add(DWORD, "Size", null); + ddstruct.setCategoryPath(new CategoryPath("/PE")); + + StructureDataType struct = new StructureDataType(getName(), 0); + + struct.add(WORD, "Magic", null); + struct.add(BYTE, "MajorLinkerVersion", null); + struct.add(BYTE, "MinorLinkerVersion", null); + struct.add(DWORD, "SizeOfCode", null); + struct.add(DWORD, "SizeOfInitializedData", null); + struct.add(DWORD, "SizeOfUninitializedData", null); + struct.add(IBO32, "AddressOfEntryPoint", null); + struct.add(IBO32, "BaseOfCode", null); + if (is64bit()) { + //BaseOfData does not exist in 64 bit + struct.add(new Pointer64DataType(), "ImageBase", null); + } + else { + struct.add(IBO32, "BaseOfData", null); + struct.add(new Pointer32DataType(), "ImageBase", null); + } + struct.add(DWORD, "SectionAlignment", null); + struct.add(DWORD, "FileAlignment", null); + struct.add(WORD, "MajorOperatingSystemVersion", null); + struct.add(WORD, "MinorOperatingSystemVersion", null); + struct.add(WORD, "MajorImageVersion", null); + struct.add(WORD, "MinorImageVersion", null); + struct.add(WORD, "MajorSubsystemVersion", null); + struct.add(WORD, "MinorSubsystemVersion", null); + struct.add(DWORD, "Win32VersionValue", null); + struct.add(DWORD, "SizeOfImage", null); + struct.add(DWORD, "SizeOfHeaders", null); + struct.add(DWORD, "CheckSum", null); + struct.add(WORD, "Subsystem", null); + struct.add(WORD, "DllCharacteristics", null); + if (is64bit()) { + struct.add(QWORD, "SizeOfStackReserve", null); + struct.add(QWORD, "SizeOfStackCommit", null); + struct.add(QWORD, "SizeOfHeapReserve", null); + struct.add(QWORD, "SizeOfHeapCommit", null); + } + else { + struct.add(DWORD, "SizeOfStackReserve", null); + struct.add(DWORD, "SizeOfStackCommit", null); + struct.add(DWORD, "SizeOfHeapReserve", null); + struct.add(DWORD, "SizeOfHeapCommit", null); + } + struct.add(DWORD, "LoaderFlags", null); + struct.add(DWORD, "NumberOfRvaAndSizes", null); + struct.add( + new ArrayDataType(ddstruct, numberOfRvaAndSizes, ddstruct.getLength()), + "DataDirectory", null); + + struct.setCategoryPath(new CategoryPath("/PE")); + return struct; + } /** * Writes this optional header to the specified random access file. @@ -363,14 +948,126 @@ public interface OptionalHeader extends StructConverter { * @param raf the random access file * @param dc the data converter * - * @throws IOException + * @throws IOException if an IO-related error occurred */ - public void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException; + public void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { + raf.write(dc.getBytes(magic)); + raf.write(new byte[] { majorLinkerVersion }); + raf.write(new byte[] { minorLinkerVersion }); + raf.write(dc.getBytes(sizeOfCode)); + raf.write(dc.getBytes(sizeOfInitializedData)); + raf.write(dc.getBytes(sizeOfUninitializedData)); + raf.write(dc.getBytes(addressOfEntryPoint)); + raf.write(dc.getBytes(baseOfCode)); + if (is64bit()) { + //BaseOfData does not exist in 64 bit + raf.write(dc.getBytes(imageBase)); + } + else { + raf.write(dc.getBytes(baseOfData)); + raf.write(dc.getBytes((int) imageBase)); + } + raf.write(dc.getBytes(sectionAlignment)); + raf.write(dc.getBytes(fileAlignment)); + raf.write(dc.getBytes(majorOperatingSystemVersion)); + raf.write(dc.getBytes(minorOperatingSystemVersion)); + raf.write(dc.getBytes(majorImageVersion)); + raf.write(dc.getBytes(minorImageVersion)); + raf.write(dc.getBytes(majorSubsystemVersion)); + raf.write(dc.getBytes(minorSubsystemVersion)); + raf.write(dc.getBytes(win32VersionValue)); + raf.write(dc.getBytes(sizeOfImage)); + raf.write(dc.getBytes(sizeOfHeaders)); + raf.write(dc.getBytes(checkSum)); + raf.write(dc.getBytes(subsystem)); + raf.write(dc.getBytes(dllCharacteristics)); + if (is64bit()) { + raf.write(dc.getBytes(sizeOfStackReserve)); + raf.write(dc.getBytes(sizeOfStackCommit)); + raf.write(dc.getBytes(sizeOfHeapReserve)); + raf.write(dc.getBytes(sizeOfHeapCommit)); + } + else { + raf.write(dc.getBytes((int) sizeOfStackReserve)); + raf.write(dc.getBytes((int) sizeOfStackCommit)); + raf.write(dc.getBytes((int) sizeOfHeapReserve)); + raf.write(dc.getBytes((int) sizeOfHeapCommit)); + } + raf.write(dc.getBytes(loaderFlags)); + raf.write(dc.getBytes(numberOfRvaAndSizes)); - public void validateDataDirectories(Program program); + //the last one is null ... + for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; ++i) { + if (dataDirectory[i] != null) { + raf.write(dc.getBytes(dataDirectory[i].getVirtualAddress())); + raf.write(dc.getBytes(dataDirectory[i].getSize())); + } + else { + raf.write(dc.getBytes(0)); + raf.write(dc.getBytes(0)); + } + } + } + + public void validateDataDirectories(Program program) { + Memory memory = program.getMemory(); + int sizeint = Integer.SIZE / 8; + Address addr = program.getImageBase().add(startOfDataDirs); + for (int i = 0; i < numberOfRvaAndSizes; i++) { + try { + int virtualAddress = memory.getInt(addr, false); + addr = addr.add(sizeint); + int size = memory.getInt(addr, false); + addr = addr.add(sizeint); + if (dataDirectory[i] != null && dataDirectory[i].hasParsedCorrectly()) { + if (dataDirectory[i].getVirtualAddress() != virtualAddress) { + Msg.warn(this, + "Correcting dataDirectory[" + i + "] va:" + + Integer.toHexString(dataDirectory[i].getVirtualAddress()) + "->" + + Integer.toHexString(virtualAddress)); + dataDirectory[i].setVirtualAddress(virtualAddress); + } + if (dataDirectory[i].getSize() != size) { + Msg.warn(this, + "Correcting dataDirectory[" + i + "] sz:" + + Integer.toHexString(dataDirectory[i].getSize()) + "->" + + Integer.toHexString(size)); + dataDirectory[i].setSize(size); + } + } + } + catch (MemoryAccessException | AddressOutOfBoundsException e) { + Msg.error(this, "Problem validating data directories", e); + } + } + } /** - * @return true if the PE uses predominantly CLI code; otherwise, false. + * {@return true if the PE uses predominantly CLI code; otherwise, false.} + * + * @throws IOException if an IO-related error occurred */ - public boolean isCLI() throws IOException; + public boolean isCLI() throws IOException { + long origPointerIndex = reader.getPointerIndex(); + + reader.setPointerIndex(startOfDataDirs + (DataDirectory.IMAGE_SIZEOF_IMAGE_DIRECTORY_ENTRY * + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)); + + ImageCor20Header cor20 = new COMDescriptorDataDirectory(ntHeader, reader).getHeader(); + + reader.setPointerIndex(origPointerIndex); + + if (cor20 == null) { + return false; + } + + boolean intermediateLanguageOnly = (cor20.getFlags() & + ImageCor20Flags.COMIMAGE_FLAGS_ILONLY) == ImageCor20Flags.COMIMAGE_FLAGS_ILONLY; + + return intermediateLanguageOnly && cor20.getManagedNativeHeader().getVirtualAddress() == 0; + } + + private String getName() { + return "IMAGE_OPTIONAL_HEADER" + (is64bit() ? "64" : "32"); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeaderImpl.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeaderImpl.java deleted file mode 100644 index 56a2e19e3d..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/OptionalHeaderImpl.java +++ /dev/null @@ -1,812 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.util.bin.format.pe; - -import java.io.IOException; -import java.io.RandomAccessFile; - -import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressOutOfBoundsException; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; -import ghidra.util.DataConverter; -import ghidra.util.Msg; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.task.TaskMonitor; - -/** - *
- * typedef struct _IMAGE_OPTIONAL_HEADER {
- *     WORD    Magic;									// MANDATORY
- *     BYTE    MajorLinkerVersion;
- *     BYTE    MinorLinkerVersion;
- *     DWORD   SizeOfCode;
- *     DWORD   SizeOfInitializedData;
- *     DWORD   SizeOfUninitializedData;
- *     DWORD   AddressOfEntryPoint;						// MANDATORY
- *     DWORD   BaseOfCode;
- *     DWORD   BaseOfData;
- *     DWORD   ImageBase;								// MANDATORY
- *     DWORD   SectionAlignment;						// MANDATORY
- *     DWORD   FileAlignment;							// MANDATORY
- *     WORD    MajorOperatingSystemVersion;				// MANDATORY
- *     WORD    MinorOperatingSystemVersion;
- *     WORD    MajorImageVersion;
- *     WORD    MinorImageVersion;
- *     WORD    MajorSubsystemVersion;
- *     WORD    MinorSubsystemVersion;
- *     DWORD   Win32VersionValue;
- *     DWORD   SizeOfImage;								// MANDATORY
- *     DWORD   SizeOfHeaders;							// MANDATORY
- *     DWORD   CheckSum;
- *     WORD    Subsystem;								// MANDATORY
- *     WORD    DllCharacteristics;
- *     DWORD   SizeOfStackReserve;
- *     DWORD   SizeOfStackCommit;
- *     DWORD   SizeOfHeapReserve;
- *     DWORD   SizeOfHeapCommit;
- *     DWORD   LoaderFlags;
- *     DWORD   NumberOfRvaAndSizes;						// USED
- *     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
- * };
- * 
- * - *
- * typedef struct _IMAGE_OPTIONAL_HEADER64 {
- *     WORD        Magic;
- *     BYTE        MajorLinkerVersion;
- *     BYTE        MinorLinkerVersion;
- *     DWORD       SizeOfCode;
- *     DWORD       SizeOfInitializedData;
- *     DWORD       SizeOfUninitializedData;
- *     DWORD       AddressOfEntryPoint;
- *     DWORD       BaseOfCode;
- *     ULONGLONG   ImageBase;
- *     DWORD       SectionAlignment;
- *     DWORD       FileAlignment;
- *     WORD        MajorOperatingSystemVersion;
- *     WORD        MinorOperatingSystemVersion;
- *     WORD        MajorImageVersion;
- *     WORD        MinorImageVersion;
- *     WORD        MajorSubsystemVersion;
- *     WORD        MinorSubsystemVersion;
- *     DWORD       Win32VersionValue;
- *     DWORD       SizeOfImage;
- *     DWORD       SizeOfHeaders;
- *     DWORD       CheckSum;
- *     WORD        Subsystem;
- *     WORD        DllCharacteristics;
- *     ULONGLONG   SizeOfStackReserve;
- *     ULONGLONG   SizeOfStackCommit;
- *     ULONGLONG   SizeOfHeapReserve;
- *     ULONGLONG   SizeOfHeapCommit;
- *     DWORD       LoaderFlags;
- *     DWORD       NumberOfRvaAndSizes;
- *     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
- * };
- * 
- * - * - */ -public class OptionalHeaderImpl implements OptionalHeader { - protected short magic; - protected byte majorLinkerVersion; - protected byte minorLinkerVersion; - protected int sizeOfCode; - protected int sizeOfInitializedData; - protected int sizeOfUninitializedData; - protected int addressOfEntryPoint; - protected int baseOfCode; - protected int baseOfData; - protected long imageBase; - protected int sectionAlignment; - protected int fileAlignment; - protected short majorOperatingSystemVersion; - protected short minorOperatingSystemVersion; - protected short majorImageVersion; - protected short minorImageVersion; - protected short majorSubsystemVersion; - protected short minorSubsystemVersion; - protected int win32VersionValue; - protected int sizeOfImage; - protected int sizeOfHeaders; - protected int checkSum; - protected short subsystem; - protected short dllCharacteristics; - protected long sizeOfStackReserve; - protected long sizeOfStackCommit; - protected long sizeOfHeapReserve; - protected long sizeOfHeapCommit; - protected int loaderFlags; - protected int numberOfRvaAndSizes; - protected DataDirectory[] dataDirectory; - - protected NTHeader ntHeader; - protected BinaryReader reader; - protected int startIndex; - private long startOfDataDirs; - - OptionalHeaderImpl(NTHeader ntHeader, BinaryReader reader, int startIndex) throws IOException { - this.ntHeader = ntHeader; - this.reader = reader; - this.startIndex = startIndex; - - parse(); - } - - private String getName() { - return "IMAGE_OPTIONAL_HEADER" + (is64bit() ? "64" : "32"); - } - - @Override - public boolean is64bit() { - return magic == Constants.IMAGE_NT_OPTIONAL_HDR64_MAGIC; - } - - @Override - public long getImageBase() { - return imageBase; - } - - @Override - public long getAddressOfEntryPoint() { - return Integer.toUnsignedLong(addressOfEntryPoint); - } - - @Override - public long getSizeOfCode() { - return sizeOfCode; - } - - @Override - public void setSizeOfCode(long size) { - this.sizeOfCode = (int) size; - } - - @Override - public long getSizeOfInitializedData() { - return Integer.toUnsignedLong(sizeOfInitializedData); - } - - @Override - public void setSizeOfInitializedData(long size) { - this.sizeOfInitializedData = (int) size; - } - - @Override - public long getSizeOfUninitializedData() { - return Integer.toUnsignedLong(sizeOfUninitializedData); - } - - @Override - public void setSizeOfUninitializedData(long size) { - this.sizeOfUninitializedData = (int) size; - } - - @Override - public long getBaseOfCode() { - return Integer.toUnsignedLong(baseOfCode); - } - - @Override - public long getBaseOfData() { - return Integer.toUnsignedLong(baseOfData); - } - - @Override - public long getSizeOfImage() { - return Integer.toUnsignedLong(sizeOfImage); - } - - @Override - public void setSizeOfImage(long size) { - this.sizeOfImage = (int) size; - } - - @Override - public long getSizeOfHeaders() { - return Integer.toUnsignedLong(sizeOfHeaders); - } - - @Override - public void setSizeOfHeaders(long size) { - this.sizeOfHeaders = (int) size; - } - - @Override - public long getNumberOfRvaAndSizes() { - return Integer.toUnsignedLong(numberOfRvaAndSizes); - } - - @Override - public short getMajorOperatingSystemVersion() { - return majorOperatingSystemVersion; - } - - @Override - public short getMinorOperatingSystemVersion() { - return minorOperatingSystemVersion; - } - - @Override - public void processDataDirectories(TaskMonitor monitor) throws IOException { - reader.setPointerIndex(startOfDataDirs); - - dataDirectory = new DataDirectory[numberOfRvaAndSizes]; - if (numberOfRvaAndSizes == 0) { - return; - } - - int ndata = 0; - monitor.setMessage("Parsing exports..."); - try { - dataDirectory[ndata] = new ExportDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing imports..."); - try { - dataDirectory[ndata] = new ImportDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing resources..."); - try { - dataDirectory[ndata] = new ResourceDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing exceptions..."); - try { - dataDirectory[ndata] = new ExceptionDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing security..."); - try { - dataDirectory[ndata] = new SecurityDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing relocations..."); - try { - dataDirectory[ndata] = new BaseRelocationDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing debug information..."); - try { - dataDirectory[ndata] = new DebugDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing architecture..."); - try { - dataDirectory[ndata] = new ArchitectureDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing global pointer..."); - try { - dataDirectory[ndata] = new GlobalPointerDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing TLS data..."); - try { - dataDirectory[ndata] = new TLSDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing load config data..."); - try { - dataDirectory[ndata] = new LoadConfigDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing bound imports..."); - try { - dataDirectory[ndata] = new BoundImportDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing import address table..."); - try { - dataDirectory[ndata] = new ImportAddressTableDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing delay imports..."); - try { - dataDirectory[ndata] = new DelayImportDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - monitor.setMessage("Parsing COM descriptors..."); - try { - dataDirectory[ndata] = new COMDescriptorDataDirectory(ntHeader, reader); - } - catch (RuntimeException re) { - if (PortableExecutable.DEBUG) { - throw re; - } - } - if (++ndata == numberOfRvaAndSizes) { - return; - } - - dataDirectory[ndata] = null; - } - - @Override - public DataDirectory[] getDataDirectories() { - return dataDirectory; - } - - @Override - public int getSectionAlignment() { - return sectionAlignment; - } - - @Override - public int getFileAlignment() { - return fileAlignment; - } - - protected void parse() throws IOException { - reader.setPointerIndex(startIndex); - - magic = reader.readNextShort(); - if (magic != Constants.IMAGE_ROM_OPTIONAL_HDR_MAGIC && - magic != Constants.IMAGE_NT_OPTIONAL_HDR32_MAGIC && - magic != Constants.IMAGE_NT_OPTIONAL_HDR64_MAGIC) { - Msg.warn(this, "Unsupported magic value: 0x%x. Assuming 32-bit.".formatted(magic)); - } - majorLinkerVersion = reader.readNextByte(); - minorLinkerVersion = reader.readNextByte(); - sizeOfCode = reader.readNextInt(); - sizeOfInitializedData = reader.readNextInt(); - sizeOfUninitializedData = reader.readNextInt(); - addressOfEntryPoint = reader.readNextInt(); - // NB: 0 or negative addressOfEntryPoint is legal - if (addressOfEntryPoint < 0) { - Msg.warn(this, "Negative entry point " + Integer.toHexString(addressOfEntryPoint)); - } - if (addressOfEntryPoint == 0) { - int characteristics = ntHeader.getFileHeader().getCharacteristics(); - if ((characteristics & FileHeader.IMAGE_FILE_DLL) == 0) { - Msg.warn(this, "Zero entry point for non-DLL"); - } - } - baseOfCode = reader.readNextInt(); - - if (is64bit()) { - baseOfData = -1;//not used - imageBase = reader.readNextLong(); - } - else { - baseOfData = reader.readNextInt(); - imageBase = Integer.toUnsignedLong(reader.readNextInt()); - } - - sectionAlignment = reader.readNextInt(); - fileAlignment = reader.readNextInt(); - if (fileAlignment < 0x200) { - Msg.warn(this, "Unusual file alignment: 0x" + Integer.toHexString(fileAlignment)); - } - majorOperatingSystemVersion = reader.readNextShort(); - minorOperatingSystemVersion = reader.readNextShort(); - majorImageVersion = reader.readNextShort(); - minorImageVersion = reader.readNextShort(); - majorSubsystemVersion = reader.readNextShort(); - minorSubsystemVersion = reader.readNextShort(); - win32VersionValue = reader.readNextInt(); - sizeOfImage = reader.readNextInt(); - sizeOfHeaders = reader.readNextInt(); - if (sizeOfHeaders >= sizeOfImage) { - Msg.warn(this, "Size of headers >= size of image: forced load"); - } - checkSum = reader.readNextInt(); - subsystem = reader.readNextShort(); - dllCharacteristics = reader.readNextShort(); - - if (is64bit()) { - sizeOfStackReserve = reader.readNextLong(); - sizeOfStackCommit = reader.readNextLong(); - sizeOfHeapReserve = reader.readNextLong(); - sizeOfHeapCommit = reader.readNextLong(); - } - else { - sizeOfStackReserve = reader.readNextUnsignedInt(); - sizeOfStackCommit = reader.readNextUnsignedInt(); - sizeOfHeapReserve = reader.readNextUnsignedInt(); - sizeOfHeapCommit = reader.readNextUnsignedInt(); - } - - loaderFlags = reader.readNextInt(); - numberOfRvaAndSizes = reader.readNextInt(); - - if (numberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) { - Msg.warn(this, "Non-standard # of data directories: " + numberOfRvaAndSizes); - if (numberOfRvaAndSizes > IMAGE_NUMBEROF_DIRECTORY_ENTRIES || numberOfRvaAndSizes < 0) { - Msg.warn(this, - "Forcing # of data directories to: " + IMAGE_NUMBEROF_DIRECTORY_ENTRIES); - numberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; - } - } - - startOfDataDirs = reader.getPointerIndex(); - } - - /** - * @see ghidra.app.util.bin.StructConverter#toDataType() - */ - @Override - public DataType toDataType() throws DuplicateNameException { - StructureDataType ddstruct = new StructureDataType(DataDirectory.TITLE, 0); - ddstruct.add(IBO32, "VirtualAddress", null); - ddstruct.add(DWORD, "Size", null); - ddstruct.setCategoryPath(new CategoryPath("/PE")); - - StructureDataType struct = new StructureDataType(getName(), 0); - - struct.add(WORD, "Magic", null); - struct.add(BYTE, "MajorLinkerVersion", null); - struct.add(BYTE, "MinorLinkerVersion", null); - struct.add(DWORD, "SizeOfCode", null); - struct.add(DWORD, "SizeOfInitializedData", null); - struct.add(DWORD, "SizeOfUninitializedData", null); - struct.add(IBO32, "AddressOfEntryPoint", null); - struct.add(IBO32, "BaseOfCode", null); - if (is64bit()) { - //BaseOfData does not exist in 64 bit - struct.add(new Pointer64DataType(), "ImageBase", null); - } - else { - struct.add(IBO32, "BaseOfData", null); - struct.add(new Pointer32DataType(), "ImageBase", null); - } - struct.add(DWORD, "SectionAlignment", null); - struct.add(DWORD, "FileAlignment", null); - struct.add(WORD, "MajorOperatingSystemVersion", null); - struct.add(WORD, "MinorOperatingSystemVersion", null); - struct.add(WORD, "MajorImageVersion", null); - struct.add(WORD, "MinorImageVersion", null); - struct.add(WORD, "MajorSubsystemVersion", null); - struct.add(WORD, "MinorSubsystemVersion", null); - struct.add(DWORD, "Win32VersionValue", null); - struct.add(DWORD, "SizeOfImage", null); - struct.add(DWORD, "SizeOfHeaders", null); - struct.add(DWORD, "CheckSum", null); - struct.add(WORD, "Subsystem", null); - struct.add(WORD, "DllCharacteristics", null); - if (is64bit()) { - struct.add(QWORD, "SizeOfStackReserve", null); - struct.add(QWORD, "SizeOfStackCommit", null); - struct.add(QWORD, "SizeOfHeapReserve", null); - struct.add(QWORD, "SizeOfHeapCommit", null); - } - else { - struct.add(DWORD, "SizeOfStackReserve", null); - struct.add(DWORD, "SizeOfStackCommit", null); - struct.add(DWORD, "SizeOfHeapReserve", null); - struct.add(DWORD, "SizeOfHeapCommit", null); - } - struct.add(DWORD, "LoaderFlags", null); - struct.add(DWORD, "NumberOfRvaAndSizes", null); - struct.add( - new ArrayDataType(ddstruct, numberOfRvaAndSizes, ddstruct.getLength()), - "DataDirectory", null); - - struct.setCategoryPath(new CategoryPath("/PE")); - return struct; - } - - @Override - public void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { - raf.write(dc.getBytes(magic)); - raf.write(new byte[] { majorLinkerVersion }); - raf.write(new byte[] { minorLinkerVersion }); - raf.write(dc.getBytes(sizeOfCode)); - raf.write(dc.getBytes(sizeOfInitializedData)); - raf.write(dc.getBytes(sizeOfUninitializedData)); - raf.write(dc.getBytes(addressOfEntryPoint)); - raf.write(dc.getBytes(baseOfCode)); - if (is64bit()) { - //BaseOfData does not exist in 64 bit - raf.write(dc.getBytes(imageBase)); - } - else { - raf.write(dc.getBytes(baseOfData)); - raf.write(dc.getBytes((int) imageBase)); - } - raf.write(dc.getBytes(sectionAlignment)); - raf.write(dc.getBytes(fileAlignment)); - raf.write(dc.getBytes(majorOperatingSystemVersion)); - raf.write(dc.getBytes(minorOperatingSystemVersion)); - raf.write(dc.getBytes(majorImageVersion)); - raf.write(dc.getBytes(minorImageVersion)); - raf.write(dc.getBytes(majorSubsystemVersion)); - raf.write(dc.getBytes(minorSubsystemVersion)); - raf.write(dc.getBytes(win32VersionValue)); - raf.write(dc.getBytes(sizeOfImage)); - raf.write(dc.getBytes(sizeOfHeaders)); - raf.write(dc.getBytes(checkSum)); - raf.write(dc.getBytes(subsystem)); - raf.write(dc.getBytes(dllCharacteristics)); - if (is64bit()) { - raf.write(dc.getBytes(sizeOfStackReserve)); - raf.write(dc.getBytes(sizeOfStackCommit)); - raf.write(dc.getBytes(sizeOfHeapReserve)); - raf.write(dc.getBytes(sizeOfHeapCommit)); - } - else { - raf.write(dc.getBytes((int) sizeOfStackReserve)); - raf.write(dc.getBytes((int) sizeOfStackCommit)); - raf.write(dc.getBytes((int) sizeOfHeapReserve)); - raf.write(dc.getBytes((int) sizeOfHeapCommit)); - } - raf.write(dc.getBytes(loaderFlags)); - raf.write(dc.getBytes(numberOfRvaAndSizes)); - - //the last one is null ... - for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; ++i) { - if (dataDirectory[i] != null) { - raf.write(dc.getBytes(dataDirectory[i].getVirtualAddress())); - raf.write(dc.getBytes(dataDirectory[i].getSize())); - } - else { - raf.write(dc.getBytes(0)); - raf.write(dc.getBytes(0)); - } - } - } - - @Override - public void validateDataDirectories(Program program) { - Memory memory = program.getMemory(); - int sizeint = Integer.SIZE / 8; - Address addr = program.getImageBase().add(startOfDataDirs); - for (int i = 0; i < numberOfRvaAndSizes; i++) { - try { - int virtualAddress = memory.getInt(addr, false); - addr = addr.add(sizeint); - int size = memory.getInt(addr, false); - addr = addr.add(sizeint); - if (dataDirectory[i] != null && dataDirectory[i].hasParsedCorrectly()) { - if (dataDirectory[i].getVirtualAddress() != virtualAddress) { - Msg.warn(this, - "Correcting dataDirectory[" + i + "] va:" + - Integer.toHexString(dataDirectory[i].getVirtualAddress()) + "->" + - Integer.toHexString(virtualAddress)); - dataDirectory[i].setVirtualAddress(virtualAddress); - } - if (dataDirectory[i].getSize() != size) { - Msg.warn(this, - "Correcting dataDirectory[" + i + "] sz:" + - Integer.toHexString(dataDirectory[i].getSize()) + "->" + - Integer.toHexString(size)); - dataDirectory[i].setSize(size); - } - } - } - catch (MemoryAccessException e) { - e.printStackTrace(); - } - catch (AddressOutOfBoundsException e) { - e.printStackTrace(); - } - } - } - - @Override - public boolean isCLI() throws IOException { - long origPointerIndex = reader.getPointerIndex(); - - reader.setPointerIndex(startOfDataDirs + (DataDirectory.IMAGE_SIZEOF_IMAGE_DIRECTORY_ENTRY * - IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)); - - ImageCor20Header cor20 = new COMDescriptorDataDirectory(ntHeader, reader).getHeader(); - - reader.setPointerIndex(origPointerIndex); - - if (cor20 == null) { - return false; - } - - boolean intermediateLanguageOnly = (cor20.getFlags() & - ImageCor20Flags.COMIMAGE_FLAGS_ILONLY) == ImageCor20Flags.COMIMAGE_FLAGS_ILONLY; - - return intermediateLanguageOnly && cor20.getManagedNativeHeader().getVirtualAddress() == 0; - } - - @Override - public byte getMajorLinkerVersion() { - return majorLinkerVersion; - } - - @Override - public byte getMinorLinkerVersion() { - return minorLinkerVersion; - } - - @Override - public short getMajorImageVersion() { - return majorImageVersion; - } - - @Override - public short getMinorImageVersion() { - return minorImageVersion; - } - - @Override - public short getMajorSubsystemVersion() { - return majorSubsystemVersion; - } - - @Override - public short getMinorSubsystemVersion() { - return minorSubsystemVersion; - } - - @Override - public int getWin32VersionValue() { - return win32VersionValue; - } - - @Override - public int getChecksum() { - return checkSum; - } - - @Override - public int getSubsystem() { - return subsystem; - } - - @Override - public short getDllCharacteristics() { - return dllCharacteristics; - } - - @Override - public long getSizeOfStackReserve() { - return sizeOfStackReserve; - } - - @Override - public long getSizeOfStackCommit() { - return sizeOfStackCommit; - } - - @Override - public long getSizeOfHeapReserve() { - return sizeOfHeapReserve; - } - - @Override - public long getSizeOfHeapCommit() { - return sizeOfHeapCommit; - } - - @Override - public int getLoaderFlags() { - return loaderFlags; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PortableExecutable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PortableExecutable.java index 010ee1b701..65892fc7a6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PortableExecutable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PortableExecutable.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,9 +21,10 @@ import java.io.RandomAccessFile; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.mz.DOSHeader; +import ghidra.app.util.importer.MessageLog; import ghidra.util.DataConverter; import ghidra.util.Msg; -import ghidra.util.exception.NotYetImplementedException; +import ghidra.util.task.TaskMonitor; /** * A class to manage loading Portable Executables (PE). @@ -86,15 +87,15 @@ public class PortableExecutable { } try { - ntHeader = new NTHeader(reader, dosHeader.e_lfanew(), layout, advancedProcess, - parseCliHeaders); + ntHeader = new NTHeader(reader, dosHeader.e_lfanew(), layout, parseCliHeaders); + if (advancedProcess) { + ntHeader.getOptionalHeader() + .processDataDirectories(new MessageLog(), TaskMonitor.DUMMY); + } } catch (InvalidNTHeaderException e) { Msg.debug(this, "Expected InvalidNTHeaderException, ignoring"); } - catch (NotYetImplementedException e) { - Msg.debug(this, "Expected NotYetImplementedException, ignoring"); - } catch (ArrayIndexOutOfBoundsException e) { Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java index 93b9d01f75..c42b9a74db 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java @@ -127,7 +127,7 @@ public class PeLoader extends AbstractPeDebugLoader { processMemoryBlocks(pe, program, fileBytes, monitor, log); monitor.setCancelEnabled(false); - optionalHeader.processDataDirectories(monitor); + optionalHeader.processDataDirectories(log, monitor); monitor.setCancelEnabled(true); optionalHeader.validateDataDirectories(program);