Merge branch 'GP-6991_ryanmkurtz_mz-fix'

This commit is contained in:
Ryan Kurtz
2026-06-23 12:22:25 -04:00
4 changed files with 89 additions and 172 deletions
@@ -19,11 +19,6 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.ne.InvalidWindowsHeaderException;
import ghidra.app.util.bin.format.ne.WindowsHeader;
import ghidra.app.util.bin.format.pe.InvalidNTHeaderException;
import ghidra.app.util.bin.format.pe.NTHeader;
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
import ghidra.program.model.data.*;
import ghidra.util.DataConverter;
import ghidra.util.exception.DuplicateNameException;
@@ -62,7 +57,8 @@ import ghidra.util.exception.DuplicateNameException;
public class DOSHeader extends OldDOSHeader {
/** The name to use when converting into a structure data type. */
public final static String NAME = "IMAGE_DOS_HEADER";
@SuppressWarnings("hiding")
public final static String NAME = "IMAGE_DOS_HEADER";
public final static int SIZEOF_DOS_HEADER = 64;
@@ -75,7 +71,8 @@ public class DOSHeader extends OldDOSHeader {
private byte [] stubBytes;
/**
* Constructs a new DOS header.
* Constructs a new {@link DOSHeader}
*
* @param reader the binary reader
* @throws IOException if there was an IO-related error
*/
@@ -84,87 +81,40 @@ public class DOSHeader extends OldDOSHeader {
}
/**
* Returns the reserved words.
* @return the reserved words
*/
* {@return the reserved words}
*/
public short [] e_res() {
return e_res;
}
/**
* Returns the OEM identifier (for e_oeminfo).
* @return the OEM identifier (for e_oeminfo)
*/
/**
* {@return the OEM identifier (for e_oeminfo)}
*/
public short e_oemid() {
return e_oemid;
}
/**
* Returns the OEM information; e_oemid specific.
* @return the OEM information; e_oemid specific
*/
/**
* {@return the OEM information; e_oemid specific}
*/
public short e_oeminfo() {
return e_oeminfo;
}
/**
* Returns the reserved words (2).
* @return the reserved words (2)
*/
/**
* {@return the reserved words (2)}
*/
public short [] e_res2() {
return e_res2;
}
/**
* Returns the file address of new EXE header.
* @return the file address of new EXE header
*/
/**
* {@return the file address of new EXE header}
*/
public int e_lfanew() {
return e_lfanew;
}
/**
* Returns true if a new EXE header exists.
* @return true if a new EXE header exists
*/
@Override
public boolean hasNewExeHeader() {
if (e_lfanew >= 0 && e_lfanew <= 0x10000) {
if (e_lfarlc() == 0x40) {
// There are some non-NE files out there than may have e_lfarlc == 0x40, so we need
// to actually read the bytes at e_lfanew and check for the required NE signature.
try {
new WindowsHeader(reader, null, (short) e_lfanew);
return true;
}
catch (InvalidWindowsHeaderException | IOException e) {
return false;
}
}
}
return false;
}
/**
* Returns true if a PE header exists.
* @return true if a PE header exists
*/
@Override
public boolean hasPeHeader() {
if (e_lfanew >= 0 && e_lfanew <= 0x1000000) {
try {
NTHeader ntHeader =
new NTHeader(reader, e_lfanew, SectionLayout.FILE, false);
if (ntHeader.getOptionalHeader() != null) {
return true;
}
}
catch (InvalidNTHeaderException | IOException e) {
// Fall through and return false
}
}
return false;
}
/**
* @see ghidra.app.util.bin.StructConverter#toDataType()
*/
@Override
public DataType toDataType() throws DuplicateNameException {
StructureDataType struct = (StructureDataType)super.toDataType();
@@ -199,24 +149,16 @@ public class DOSHeader extends OldDOSHeader {
return struct;
}
/**
* Helper to override the value of name
* @return The name of the header
*/
@Override
public String getName() {
return NAME;
}
/**
* Returns the length (in bytes) of the DOS
* program.
* <p>
* In other words:
* <code>e_lfanew() - SIZEOF_DOS_HEADER</code>
*
* @return the length (in bytes)
*/
* {@return the length (in bytes) of the DOS program}
* <p>
* In other words: {@code e_lfanew() - SIZEOF_DOS_HEADER}
*/
public int getProgramLen() {
return stubBytes == null ? 0 : stubBytes.length;
}
@@ -261,9 +203,6 @@ public class DOSHeader extends OldDOSHeader {
}
}
/**
* @see ghidra.app.util.bin.format.Writeable#write(java.io.RandomAccessFile, ghidra.util.DataConverter)
*/
@Override
public void write(RandomAccessFile raf, DataConverter dc) throws IOException {
super.write(raf, dc);
@@ -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.
@@ -72,7 +72,8 @@ public class OldDOSHeader implements StructConverter, Writeable {
protected BinaryReader reader;
/**
* Constructs a new DOS header.
* Constructs a new {@link OldDOSHeader}
*
* @param reader the binary reader
* @throws IOException if there was an IO-related error
*/
@@ -82,32 +83,28 @@ public class OldDOSHeader implements StructConverter, Writeable {
}
/**
* Returns the processor name.
* @return the processor name
* {@return the processor name}
*/
public String getProcessorName() {
return "x86";
}
/**
* Returns the magic number.
* @return the magic number
* {@return the magic number}
*/
public short e_magic() {
return e_magic;
}
/**
* Returns the number of bytes on the last page of file.
* @return the number of bytes on the last page of the file
* {@return the number of bytes on the last page of the file}
*/
public short e_cblp() {
return e_cblp;
}
/**
* Returns the number of pages in the file.
* @return the number of pages in the file
* {@return the number of pages in the file}
*/
public short e_cp() {
return e_cp;
@@ -122,104 +119,77 @@ public class OldDOSHeader implements StructConverter, Writeable {
}
/**
* Returns the size of header in paragraphs.
* @return the size of header in paragraphs
* {@return the size of header in paragraphs}
*/
public short e_cparhdr() {
return e_cparhdr;
}
/**
* Returns the minimum extra paragraphs needed.
* @return the minimum extra paragraphs needed
* {@return the minimum extra paragraphs needed}
*/
public short e_minalloc() {
return e_minalloc;
}
/**
* Returns the maximum extra paragraphs needed.
* @return the maximum extra paragraphs needed
* {@return the maximum extra paragraphs needed}
*/
public short e_maxalloc() {
return e_maxalloc;
}
/**
* Returns the initial (relative) SS value.
* @return the initial (relative) SS value
* {@return the initial (relative) SS value}
*/
public short e_ss() {
return e_ss;
}
/**
* Returns the initial SP value.
* @return the initial SP value
* {@return the initial SP value}
*/
public short e_sp() {
return e_sp;
}
/**
* Returns the checksum.
* @return the checksum
* {@return the checksum}
*/
public short e_csum() {
return e_csum;
}
/**
* Returns the initial IP value.
* @return the initial IP value
* {@return the initial IP value}
*/
public short e_ip() {
return e_ip;
}
/**
* Returns the initial (relative) CS value.
* @return the initial (relative) CS value
* {@return the initial (relative) CS value}
*/
public short e_cs() {
return e_cs;
}
/**
* Returns the file address of relocation table.
* @return the file address of relocation table
* {@return the file address of relocation table}
*/
public short e_lfarlc() {
return e_lfarlc;
}
/**
* Returns the overlay number.
* @return the overlay number
* {@return the overlay number}
*/
public short e_ovno() {
return e_ovno;
}
/**
* Returns true if a new EXE header exists.
* @return true if a new EXE header exists
*/
public boolean hasNewExeHeader() {
return false;
}
/**
* Returns true if a PE header exists.
* @return true if a PE header exists
*/
public boolean hasPeHeader() {
return false;
}
/**
* Returns true if the DOS magic number is correct
* @return true if the DOS magic number is correct
* {@return true if the DOS magic number is correct}
*/
public boolean isDosSignature() {
return e_magic == IMAGE_DOS_SIGNATURE;
@@ -268,10 +238,6 @@ public class OldDOSHeader implements StructConverter, Writeable {
return struct;
}
/**
* Helper to override the value of name
* @return The name of the header
*/
protected String getName() {
return NAME;
}
@@ -317,5 +283,4 @@ public class OldDOSHeader implements StructConverter, Writeable {
raf.write(dc.getBytes(e_lfarlc));
raf.write(dc.getBytes(e_ovno));
}
}
@@ -27,18 +27,16 @@ import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
/**
* A class to represent the <b><code>IMAGE_NT_HEADERS32</code></b> and
* IMAGE_NT_HEADERS64 structs as defined in
* <code>winnt.h</code>.
* <pre>
* A class to represent the {@code IMAGE_NT_HEADERS32} and {@code IMAGE_NT_HEADERS64} structs as
* defined in {@code winnt.h}
*
* <pre>{@code
* typedef struct _IMAGE_NT_HEADERS {
* DWORD Signature;
* IMAGE_FILE_HEADER FileHeader;
* IMAGE_OPTIONAL_HEADER32 OptionalHeader;
* };
* </pre>
*
*
* }</pre>
*/
public class NTHeader implements StructConverter, OffsetValidator {
/**
@@ -58,13 +56,14 @@ public class NTHeader implements StructConverter, OffsetValidator {
/**
* Constructs a new NT header.
*
* @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 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.
* do not constitute an accurate NT header.
*/
public NTHeader(BinaryReader reader, int index, SectionLayout layout, boolean parseCliHeaders)
throws InvalidNTHeaderException, IOException {
@@ -77,8 +76,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
}
/**
* Returns the name to use when converting into a structure data type.
* @return the name to use when converting into a structure data type
* {@return the name to use when converting into a structure data type}
*/
public String getName() {
return "IMAGE_NT_HEADERS" + (optionalHeader.is64bit() ? "64" : "32");
@@ -89,24 +87,19 @@ public class NTHeader implements StructConverter, OffsetValidator {
}
/**
* Returns the file header.
* @return the file header
* {@return the file header}
*/
public FileHeader getFileHeader() {
return fileHeader;
}
/**
* Returns the optional header.
* @return the optional header
* {@return the optional header}
*/
public OptionalHeader getOptionalHeader() {
return optionalHeader;
}
/**
* @see ghidra.app.util.bin.StructConverter#toDataType()
*/
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getName(), 0);
@@ -121,20 +114,20 @@ public class NTHeader implements StructConverter, OffsetValidator {
}
/**
* Converts a relative virtual address (RVA) into a pointer.
* {@return the given relative virtual address (RVA) converted into a pointer into the binary
* image, or -1 if not valid}
*
* @param rva the relative virtual address
* @return the pointer into binary image, 0 if not valid
*/
public int rvaToPointer(int rva) {
return (int) rvaToPointer(Integer.toUnsignedLong(rva));
}
/**
* Converts a relative virtual address (RVA) into a pointer.
* {@return the given relative virtual address (RVA) converted into a pointer into the binary
* image, or -1 if not valid}
* @param rva the relative virtual address
* @return the pointer into binary image, -1 if not valid
*/
public long rvaToPointer(long rva) {
SectionHeader[] sections = fileHeader.getSectionHeaders();
@@ -212,31 +205,26 @@ public class NTHeader implements StructConverter, OffsetValidator {
}
/**
* Converts a virtual address (VA) into a pointer.
* {@return the given virtual address (VA) converted into a pointer into the binary
* image, or -1 if not valid}
*
* @param va the virtual address
* @return the pointer into binary image, 0 if not valid
*/
public int vaToPointer(int va) {
return (int) vaToPointer(Integer.toUnsignedLong(va));
}
/**
* Converts a virtual address (VA) into a pointer.
* {@return the given virtual address (VA) converted into a pointer into the binary
* image, or -1 if not valid}
*
* @param va the virtual address
* @return the pointer into binary image, 0 if not valid
*/
public long vaToPointer(long va) {
return rvaToPointer(va - getOptionalHeader().getImageBase());
}
private void parse() throws InvalidNTHeaderException, IOException {
if (index < 0 || index > reader.length()) {
return;
}
int tmpIndex = index;
try {
@@ -23,6 +23,9 @@ import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.mz.*;
import ghidra.app.util.bin.format.ne.NewExecutable;
import ghidra.app.util.bin.format.pe.PortableExecutable;
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.database.module.TreeManager;
@@ -65,7 +68,7 @@ public class MzLoader extends AbstractLibrarySupportLoader {
}
MzExecutable mz = new MzExecutable(provider);
OldDOSHeader header = mz.getHeader();
if (header.isDosSignature() && !header.hasNewExeHeader() && !header.hasPeHeader()) {
if (header.isDosSignature() && !isPeOrNe(provider)) {
List<QueryResult> results =
QueryOpinionService.query(getName(), "" + header.e_magic(), null);
for (QueryResult result : results) {
@@ -127,6 +130,28 @@ public class MzLoader extends AbstractLibrarySupportLoader {
return 60; // we are less priority than PE! Important for ProgramLoader
}
/**
* {@return true if the given {@link ByteProvider} contains a PE or NE binary}
*
* @param provider The {@link ByteProvider} to check
*/
private boolean isPeOrNe(ByteProvider provider) {
try {
new PortableExecutable(provider, SectionLayout.FILE, false, false);
return true;
}
catch (IOException e) {
try {
new NewExecutable(provider, null);
return true;
}
catch (IOException e2) {
// fall through
}
}
return false;
}
/**
* Stores a relocation's fixup information
*