diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/Writeable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/Writeable.java index 97140f586d..51cbe16fc6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/Writeable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/Writeable.java @@ -16,19 +16,19 @@ */ package ghidra.app.util.bin.format; -import ghidra.util.DataConverter; - import java.io.IOException; import java.io.RandomAccessFile; +import ghidra.util.DataConverter; + /** - * An interface for writing out class state information. - * + * An interface for writing out class state information */ public interface Writeable { /** - * Writes this object to the specified random access file using - * the data converter to handle endianness. + * Writes this object to the specified random access file using the data converter to handle + * endianness + * * @param raf the random access file * @param dc the data converter * @throws IOException if an I/O error occurs diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java index 65a3275d5a..951d13198f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java @@ -17,11 +17,11 @@ package ghidra.app.util.bin.format.pe; import java.io.IOException; import java.io.RandomAccessFile; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.format.Writeable; import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol; import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbolAux; import ghidra.program.model.data.*; @@ -30,101 +30,75 @@ import ghidra.util.DataConverter; import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; -/** - * A class to represent the IMAGE_FILE_HEADER struct as - * defined in winnt.h. - *
- *
- * typedef struct _IMAGE_FILE_HEADER {
- *     WORD    Machine;								// MANDATORY
- *     WORD    NumberOfSections;					// USED
- *     DWORD   TimeDateStamp;
- *     DWORD   PointerToSymbolTable;
- *     DWORD   NumberOfSymbols;
- *     WORD    SizeOfOptionalHeader;				// USED
- *     WORD    Characteristics;						// MANDATORY
- * } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
- * 
- * - */ -public class FileHeader implements StructConverter { - /** - * The name to use when converting into a structure data type. - */ +/// A class to represent the `IMAGE_FILE_HEADER` struct as defined in `winnt.h` +/// +/// ```c +/// typedef struct _IMAGE_FILE_HEADER { +/// WORD Machine; // MANDATORY +/// WORD NumberOfSections; // USED +/// DWORD TimeDateStamp; +/// DWORD PointerToSymbolTable; +/// DWORD NumberOfSymbols; +/// WORD SizeOfOptionalHeader; // USED +/// WORD Characteristics; // MANDATORY +/// } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; +/// ``` +public class FileHeader implements StructConverter, Writeable { + /// The name to use when converting into a structure data type public final static String NAME = "IMAGE_FILE_HEADER"; - /** - * The size of the IMAGE_FILE_HEADER in bytes. - */ + + /// The size of the {@code IMAGE_FILE_HEADER} in bytes public final static int IMAGE_SIZEOF_FILE_HEADER = 20; - /** - * Relocation info stripped from file. - */ + /// Relocation info stripped from file public final static int IMAGE_FILE_RELOCS_STRIPPED = 0x0001; - /** - * File is executable (no unresolved externel references). - */ + + /// File is executable (no unresolved external references) public final static int IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002; - /** - * Line nunbers stripped from file. - */ + + /// Line numbers stripped from file public final static int IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004; - /** - * Local symbols stripped from file. - */ + + /// Local symbols stripped from file public final static int IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008; - /** - * Agressively trim working set - */ + + /// Aggressively trim working set public final static int IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010; - /** - * App can handle >2gb addresses - */ + + /// App can handle >2gb addresses public final static int IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020; - /** - * Bytes of machine word are reversed. - */ + + /// Bytes of machine word are reversed public final static int IMAGE_FILE_BYTES_REVERSED_LO = 0x0080; - /** - * 32 bit word machine. - */ + + /// 32 bit word machine public final static int IMAGE_FILE_32BIT_MACHINE = 0x0100; - /** - * Debugging info stripped from file in .DBG file - */ + + /// Debugging info stripped from file in .DBG file public final static int IMAGE_FILE_DEBUG_STRIPPED = 0x0200; - /** - * If Image is on removable media, copy and run from the swap file. - */ + + /// If Image is on removable media, copy and run from the swap file public final static int IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400; - /** - * If Image is on Net, copy and run from the swap file. - */ + + /// If Image is on Net, copy and run from the swap file public final static int IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800; - /** - * System File. - */ + + /// System File public final static int IMAGE_FILE_SYSTEM = 0x1000; - /** - * File is a DLL. - */ + + /// File is a DLL public final static int IMAGE_FILE_DLL = 0x2000; - /** - * File should only be run on a UP machine. - */ + + /// File should only be run on a UP machine public final static int IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000; - /** - * Bytes of machine word are reversed. - */ + + /// Bytes of machine word are reversed public final static int IMAGE_FILE_BYTES_REVERSED_HI = 0x8000; - /** - * Magic value in LordPE's Symbol Table pointer field. - */ + /// Magic value in LordPE's Symbol Table pointer field private final static int LORDPE_SYMBOL_TABLE = 0x726F4C5B; - /** - * Magic value in LordPE's Number of Symbols field. - */ + + /// Magic value in LordPE's Number of Symbols field private final static int LORDPE_NUMBER_OF_SYMBOLS = 0x5D455064; public final static String[] CHARACTERISTICS = { "Relocation info stripped from file", @@ -137,9 +111,7 @@ public class FileHeader implements StructConverter { "If Image is on Net, copy and run from the swap file", "System file", "File is a DLL", "File should only be run on a UP machine", "Bytes of machine word are reversed" }; - /** - * Values for the Machine field indicating the intended processor architecture - */ + // Values for the Machine field indicating the intended processor architecture public final static int IMAGE_FILE_MACHINE_MASK = 0xFFFF; public final static int IMAGE_FILE_MACHINE_UNKNOWN = 0x0; // The content of this field is assumed to be applicable to any machine type public final static int IMAGE_FILE_MACHINE_AM33 = 0x1d3; // Matsushita AM33 @@ -182,25 +154,39 @@ public class FileHeader implements StructConverter { private int startIndex; private NTHeader ntHeader; + /** + * Creates a new {@link FileHeader} + * + * @param reader A {@link BinaryReader} + * @param startIndex The {@link BinaryReader} index of the start of the header + * @param ntHeader The associated {@link NTHeader} + * @throws IOException if an IO-related error occurred + */ FileHeader(BinaryReader reader, int startIndex, NTHeader ntHeader) throws IOException { this.reader = reader; this.startIndex = startIndex; this.ntHeader = ntHeader; - parse(); + reader.setPointerIndex(startIndex); + + machine = reader.readNextShort(); + numberOfSections = reader.readNextUnsignedShort(); + timeDateStamp = reader.readNextInt(); + pointerToSymbolTable = reader.readNextInt(); + numberOfSymbols = reader.readNextInt(); + sizeOfOptionalHeader = reader.readNextShort(); + characteristics = reader.readNextShort(); } /** - * Returns the architecture type of the computer. - * @return the architecture type of the computer + * {@return the architecture type of the computer} */ public short getMachine() { return machine; } /** - * Returns a string representation of the architecture type of the computer. - * @return a string representation of the architecture type of the computer + * {@return a string representation of the architecture type of the computer} */ public String getMachineName() { return MachineName.getName(machine); @@ -234,118 +220,97 @@ public class FileHeader implements StructConverter { } /** - * Returns the number of sections. - * Sections equate to Ghidra memory blocks. - * @return the number of sections + * {@return the number of sections} */ public int getNumberOfSections() { return numberOfSections; } /** - * Returns the array of section headers. - * @return the array of section headers + * {@return the array of section headers} */ public SectionHeader[] getSectionHeaders() { - if (sectionHeaders == null) { - return new SectionHeader[0]; - } - return sectionHeaders; + return sectionHeaders != null ? sectionHeaders : new SectionHeader[0]; } /** - * Returns the array of symbols. - * @return the array of symbols + * {@return the array of symbols} */ public List getSymbols() { return symbols; } /** - * Returns the section header that contains the specified virtual address. + * {@return the section header that contains the specified virtual address} + * * @param virtualAddr the virtual address - * @return the section header that contains the specified virtual address */ public SectionHeader getSectionHeaderContaining(int virtualAddr) { - for (SectionHeader sectionHeader : sectionHeaders) { - int start = sectionHeader.getVirtualAddress(); - int end = sectionHeader.getVirtualAddress() + sectionHeader.getVirtualSize() - 1; - if (virtualAddr >= start && virtualAddr <= end) { - return sectionHeader; - } - } - return null; + return Arrays.stream(sectionHeaders) + .filter(e -> virtualAddr >= e.getVirtualAddress() && + virtualAddr < e.getVirtualAddress() + e.getVirtualSize()) + .findFirst() + .orElse(null); } /** - * Returns the section header at the specified position in the array. + * {@return the section header at the specified position in the array, or null if invalid} + * * @param index index of section header to return - * @return the section header at the specified position in the array, or null if invalid */ public SectionHeader getSectionHeader(int index) { - if (index >= 0 && index < sectionHeaders.length) { - return sectionHeaders[index]; - } - return null; + return index >= 0 && index < sectionHeaders.length ? sectionHeaders[index] : null; } /** - * Get the first section header defined with the specified name + * {@return the first section header defined with the specified name, or null if not found} + * * @param name section name - * @return first section header defined with the specified name or null if not found */ public SectionHeader getSectionHeader(String name) { - for (SectionHeader element : sectionHeaders) { - if (element.getName().equals(name)) { - return element; - } - } - return null; + return Arrays.stream(sectionHeaders) + .filter(e -> e.getName().equals(name)) + .findFirst() + .orElse(null); } /** - * Returns the time stamp of the image. - * @return the time stamp of the image + * {@return the time stamp of the image} */ public int getTimeDateStamp() { return timeDateStamp; } /** - * Returns the file offset of the COFF symbol table - * @return the file offset of the COFF symbol table + * {@return the file offset of the COFF symbol table} */ public int getPointerToSymbolTable() { return pointerToSymbolTable; } /** - * Returns the number of symbols in the COFF symbol table - * @return the number of symbols in the COFF symbol table + * {@return the number of symbols in the COFF symbol table} */ public int getNumberOfSymbols() { return numberOfSymbols; } /** - * Returns the size of the optional header data - * @return the size of the optional header, in bytes + * {@return the size of the optional header, in bytes} */ public int getSizeOfOptionalHeader() { return sizeOfOptionalHeader; } /** - * Returns a set of bit flags indicating attributes of the file. - * @return a set of bit flags indicating attributes + * {@return a set of bit flags indicating attributes} */ public int getCharacteristics() { return characteristics; } /** - * Returns the file pointer to the section headers. - * @return the file pointer to the section headers + * {@return the file pointer to the section headers} */ public int getPointerToSections() { short sizeOptHdr = ntHeader.getFileHeader().sizeOfOptionalHeader; @@ -370,8 +335,7 @@ public class FileHeader implements StructConverter { long stringTableOffset = symbolsProcessed ? getStringTableOffset() : -1; sectionHeaders = new SectionHeader[numberOfSections]; for (int i = 0; i < numberOfSections; ++i) { - SectionHeader section = - SectionHeader.readSectionHeader(reader, tmpIndex, stringTableOffset); + SectionHeader section = new SectionHeader(reader, tmpIndex, stringTableOffset); sectionHeaders[i] = section; int pointerToRawData = section.getPointerToRawData(); @@ -396,10 +360,10 @@ public class FileHeader implements StructConverter { // different addresses to enforce alignment. int virtualAddress = section.getVirtualAddress(); int virtualSize = section.getVirtualSize(); - int alignedVirtualAddress = PortableExecutable.computeAlignment(virtualAddress, - optHeader.getSectionAlignment()); - int alignedVirtualSize = PortableExecutable.computeAlignment(virtualSize, - optHeader.getSectionAlignment()); + int alignedVirtualAddress = + PeUtils.align(virtualAddress, optHeader.getSectionAlignment()); + int alignedVirtualSize = + PeUtils.align(virtualSize, optHeader.getSectionAlignment()); if (virtualAddress == alignedVirtualAddress) { if (sizeOfRawData > virtualSize) { section.setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize)); @@ -490,25 +454,9 @@ public class FileHeader implements StructConverter { return false; } - private void parse() throws IOException { - reader.setPointerIndex(startIndex); - - machine = reader.readNextShort(); - numberOfSections = reader.readNextUnsignedShort(); - timeDateStamp = reader.readNextInt(); - pointerToSymbolTable = reader.readNextInt(); - numberOfSymbols = reader.readNextInt(); - sizeOfOptionalHeader = reader.readNextShort(); - characteristics = reader.readNextShort(); - } - - /** - * @see ghidra.app.util.bin.StructConverter#toDataType() - */ @Override public DataType toDataType() throws DuplicateNameException { StructureDataType struct = new StructureDataType(NAME, 0); - struct.add(WORD, 2, "Machine", getMachineName()); struct.add(WORD, 2, "NumberOfSections", null); struct.add(DWORD, 4, "TimeDateStamp", null); @@ -516,9 +464,7 @@ public class FileHeader implements StructConverter { struct.add(DWORD, 4, "NumberOfSymbols", null); struct.add(WORD, 2, "SizeOfOptionalHeader", null); struct.add(WORD, 2, "Characteristics", null); - struct.setCategoryPath(new CategoryPath("/PE")); - return struct; } @@ -527,7 +473,8 @@ public class FileHeader implements StructConverter { numberOfSections = sectionHeaders.length; } - void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { + @Override + public void write(RandomAccessFile raf, DataConverter dc) throws IOException { raf.write(dc.getBytes(machine)); raf.write(dc.getBytes(numberOfSections)); raf.write(dc.getBytes(timeDateStamp)); @@ -556,7 +503,7 @@ public class FileHeader implements StructConverter { sdd = (SecurityDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY]; if (sdd != null && sdd.getSize() > 0) { - sdd.updatePointers(PortableExecutable.computeAlignment((int) block.getSize(), + sdd.updatePointers(PeUtils.align((int) block.getSize(), optionalHeader.getFileAlignment())); } } @@ -600,8 +547,8 @@ public class FileHeader implements StructConverter { bidd.updatePointers(SectionHeader.IMAGE_SIZEOF_SECTION_HEADER); int endptr = bidd.getVirtualAddress() + bidd.getSize() - 1; if (endptr >= sectionHeaders[0].getPointerToRawData()) { - int alignedPtr = PortableExecutable.computeAlignment(endptr, - optionalHeader.getFileAlignment()); + int alignedPtr = + PeUtils.align(endptr, optionalHeader.getFileAlignment()); offset = alignedPtr - sectionHeaders[0].getPointerToRawData(); for (SectionHeader sectionHeader : sectionHeaders) { sectionHeader.updatePointers(offset); @@ -637,7 +584,7 @@ public class FileHeader implements StructConverter { } int soi = newSection.getVirtualAddress() + newSection.getSizeOfRawData(); - soi = PortableExecutable.computeAlignment(soi, optionalHeader.getSectionAlignment()); + soi = PeUtils.align(soi, optionalHeader.getSectionAlignment()); optionalHeader.setSizeOfImage(soi); } @@ -657,6 +604,6 @@ public class FileHeader implements StructConverter { lastPos = directorie.rvaToPointer() + directorie.getSize(); } } - return PortableExecutable.computeAlignment(lastPos, optionalHeader.getFileAlignment()); + return PeUtils.align(lastPos, optionalHeader.getFileAlignment()); } } 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 dcd5331435..20a3c7098d 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 @@ -20,42 +20,39 @@ import java.io.RandomAccessFile; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.format.Writeable; import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout; import ghidra.program.model.data.*; import ghidra.util.DataConverter; import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; -/** - * A class to represent the {@code IMAGE_NT_HEADERS32} and {@code IMAGE_NT_HEADERS64} structs as - * defined in {@code winnt.h} - * - *
{@code
- * typedef struct _IMAGE_NT_HEADERS {
- *    DWORD Signature;
- *    IMAGE_FILE_HEADER FileHeader;
- *    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
- * };
- * }
- */ -public class NTHeader implements StructConverter, OffsetValidator { - /** - * The size of the NT header signature. - */ +/// A class to represent the `IMAGE_NT_HEADERS32` and `IMAGE_NT_HEADERS64` structures as defined in +/// `winnt.h` +/// ```c +/// typedef struct _IMAGE_NT_HEADERS { +/// DWORD Signature; +/// IMAGE_FILE_HEADER FileHeader; +/// IMAGE_OPTIONAL_HEADER32 OptionalHeader; +/// }; +/// ``` +public class NTHeader implements StructConverter, OffsetValidator, Writeable { + + /// The size of the NT header signature. public final static int SIZEOF_SIGNATURE = BinaryReader.SIZEOF_INT; + public final static int MAX_SANE_COUNT = 0x10000; private int signature; private FileHeader fileHeader; private OptionalHeader optionalHeader; - private BinaryReader reader; private int index; private boolean parseCliHeaders = false; private SectionLayout layout = SectionLayout.FILE; /** - * Constructs a new NT header. + * Constructs a new NT header * * @param reader the binary reader * @param index the index into the reader to the start of the NT header @@ -63,16 +60,50 @@ public class NTHeader implements StructConverter, OffsetValidator { * @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 parseCliHeaders) throws InvalidNTHeaderException, IOException { - this.reader = reader; this.index = index; this.layout = layout; this.parseCliHeaders = parseCliHeaders; - parse(); + int tmpIndex = index; + + try { + signature = reader.readInt(tmpIndex); + } + catch (IndexOutOfBoundsException ioobe) { + // Handled below + } + + // if not correct signature, then return... + if (signature != Constants.IMAGE_NT_SIGNATURE) { + throw new InvalidNTHeaderException(); + } + + tmpIndex += 4; + + fileHeader = new FileHeader(reader, tmpIndex, this); + if (fileHeader.getSizeOfOptionalHeader() == 0) { + Msg.warn(this, "Section headers overlap optional header"); + } + tmpIndex += FileHeader.IMAGE_SIZEOF_FILE_HEADER; + + optionalHeader = new OptionalHeader(this, reader, tmpIndex); + + // Process symbols. Allow parsing to continue on failure. + boolean symbolsProcessed = false; + try { + fileHeader.processSymbols(); + symbolsProcessed = true; + } + catch (Exception e) { + Msg.error(this, "Failed to process symbols: " + e.getMessage()); + } + + // Process sections. Resolving some sections names (i.e., "/21") requires symbols to have + // been successfully processed. Resolving is optional though. + fileHeader.processSections(optionalHeader, symbolsProcessed); } /** @@ -205,8 +236,8 @@ public class NTHeader implements StructConverter, OffsetValidator { } /** - * {@return the given virtual address (VA) converted into a pointer into the binary - * image, or -1 if not valid} + * {@return the given virtual address (VA) converted into a pointer into the binary image, or -1 + * if not valid} * * @param va the virtual address */ @@ -215,8 +246,8 @@ public class NTHeader implements StructConverter, OffsetValidator { } /** - * {@return the given virtual address (VA) converted into a pointer into the binary - * image, or -1 if not valid} + * {@return the given virtual address (VA) converted into a pointer into the binary image, or + * -1 if not valid} * * @param va the virtual address */ @@ -224,62 +255,26 @@ public class NTHeader implements StructConverter, OffsetValidator { return rvaToPointer(va - getOptionalHeader().getImageBase()); } - private void parse() throws InvalidNTHeaderException, IOException { - int tmpIndex = index; - - try { - signature = reader.readInt(tmpIndex); - } - catch (IndexOutOfBoundsException ioobe) { - // Handled below - } - - // if not correct signature, then return... - if (signature != Constants.IMAGE_NT_SIGNATURE) { - throw new InvalidNTHeaderException(); - } - - tmpIndex += 4; - - fileHeader = new FileHeader(reader, tmpIndex, this); - if (fileHeader.getSizeOfOptionalHeader() == 0) { - Msg.warn(this, "Section headers overlap optional header"); - } - tmpIndex += FileHeader.IMAGE_SIZEOF_FILE_HEADER; - - optionalHeader = new OptionalHeader(this, reader, tmpIndex); - - // Process symbols. Allow parsing to continue on failure. - boolean symbolsProcessed = false; - try { - fileHeader.processSymbols(); - symbolsProcessed = true; - } - catch (Exception e) { - Msg.error(this, "Failed to process symbols: " + e.getMessage()); - } - - // Process sections. Resolving some sections names (i.e., "/21") requires symbols to have - // been successfully processed. Resolving is optional though. - fileHeader.processSections(optionalHeader, symbolsProcessed); - } - - void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { + @Override + public void write(RandomAccessFile raf, DataConverter dc) throws IOException { raf.seek(index); raf.write(dc.getBytes(signature)); - fileHeader.writeHeader(raf, dc); + fileHeader.write(raf, dc); optionalHeader.writeHeader(raf, dc); SectionHeader[] sections = fileHeader.getSectionHeaders(); for (SectionHeader section : sections) { - section.writeHeader(raf, dc); + section.write(raf, dc); } } + /** + * {@return whether or not CLI headers should be parsed} + */ boolean shouldParseCliHeaders() { return parseCliHeaders; } 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 b71c838922..5f3aaf3207 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 @@ -105,129 +105,88 @@ import ghidra.util.task.TaskMonitor; */ public class OptionalHeader implements StructConverter { - /** - * ASLR with 64 bit address space. - */ + /// ASLR with 64 bit address space public final static int IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020; - /** - * The DLL can be relocated at load time. - */ + /// The DLL can be relocated at load time public final static int IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040; - /** - * Code integrity checks are forced. - */ + /// Code integrity checks are forced public final static int IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080; - /** - * The image is compatible with data execution prevention (DEP) - */ + /// The image is compatible with data execution prevention (DEP) public final static int IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100; - /** - * The image is isolation aware, but should not be isolated. - */ + /// The image is isolation aware, but should not be isolated public final static int IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200; - /** - * The image does not use structured exception handling (SEH). - */ + /// The image does not use structured exception handling (SEH) public final static int IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400; - /** - * Do not bind the image. - */ + /// Do not bind the image public final static int IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800; - /** - * Image should execute in an AppContainer. - */ + /// Image should execute in an AppContainer public final static int IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000; - /** - * A WDM driver. - */ + /// A WDM driver public final static int IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000; - /** - * Image supports Control Flow Guard. - */ + /// Image supports Control Flow Guard public final static int IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000; - /** - * The image is terminal server aware. - */ + /// The image is terminal server aware public final static int IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000; - /** - * The count of data directories in the optional header. - */ + /// The count of data directories in the optional header public final static byte IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; - /** - * Export directory index - */ + /// Export directory index public final static byte IMAGE_DIRECTORY_ENTRY_EXPORT = 0; - /** - * Import directory index - */ + + /// Import directory index public final static byte IMAGE_DIRECTORY_ENTRY_IMPORT = 1; - /** - * Resource directory index - */ + + /// Resource directory index public final static byte IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; - /** - * Exception directory index - */ + + /// Exception directory index public final static byte IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; - /** - * Security directory index - */ + + /// Security directory index public final static byte IMAGE_DIRECTORY_ENTRY_SECURITY = 4; - /** - * Base Relocation Table directory index - */ + + /// Base Relocation Table directory index public final static byte IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; - /** - * Debug directory index - */ + + /// Debug directory index public final static byte IMAGE_DIRECTORY_ENTRY_DEBUG = 6; - /** - * Architecture Specific Data directory index - */ + + /// Architecture Specific Data directory index public final static byte IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7; - /** - * Global Pointer directory index - */ - public final static byte IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8;//RVA of GP - /** - * TLS directory index - */ + + /// Global Pointer directory index + public final static byte IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; //RVA of GP + + /// TLS directory index public final static byte IMAGE_DIRECTORY_ENTRY_TLS = 9; - /** - * Load Configuration directory index - */ + + /// Load Configuration directory index public final static byte IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; - /** - * Bound Import directory index - */ + + /// Bound Import directory index public final static byte IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; - /** - * Import Address Table directory index - */ + + /// Import Address Table directory index public final static byte IMAGE_DIRECTORY_ENTRY_IAT = 12; - /** - * Delay Load Import Descriptors directory index - */ + + /// Delay Load Import Descriptors directory index public final static byte IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13; - /** - * COM Runtime Descriptor directory index - */ + + /// COM Runtime Descriptor directory index public final static byte IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14; - /** - * New name for the COM Descriptor directory index - */ + + /// New name for the COM Descriptor directory index public final static byte IMAGE_DIRECTORY_ENTRY_COMHEADER = 14; protected short magic; @@ -272,10 +231,6 @@ public class OptionalHeader implements StructConverter { this.reader = reader; this.startIndex = startIndex; - parse(); - } - - protected void parse() throws IOException { reader.setPointerIndex(startIndex); magic = reader.readNextShort(); @@ -620,35 +575,35 @@ public class OptionalHeader implements StructConverter { } /** - * {@return true of this optional header is 64-bit.} + * {@return true of this optional header is 64-bit} */ public boolean is64bit() { return magic == Constants.IMAGE_NT_OPTIONAL_HDR64_MAGIC; } /** - * {@return the major version number of the linker that built this binary.} + * {@return the major version number of the linker that built this binary} */ public byte getMajorLinkerVersion() { return majorLinkerVersion; } /** - * {@return the minor version number of the linker that built this binary.} + * {@return the minor version number of the linker that built this binary} */ public byte getMinorLinkerVersion() { return minorLinkerVersion; } /** - * {@return the combined total size of all sections with IMAGE_SCN_CNT_CODE attribute.} + * {@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. + * Sets the combined total size of all sections with the IMAGE_SCN_CNT_CODE attribute * * @param size The size to set */ @@ -657,7 +612,7 @@ public class OptionalHeader implements StructConverter { } /** - * {@return the combined size of all initialized data sections.} + * {@return the combined size of all initialized data sections} */ public long getSizeOfInitializedData() { return Integer.toUnsignedLong(sizeOfInitializedData); @@ -673,14 +628,14 @@ public class OptionalHeader implements StructConverter { } /** - * {@return the size of all sections with the uninitialized data attributes.} + * {@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.} + * Sets the size of all sections with the uninitialized data attributes} * * @param size The size to set */ @@ -696,14 +651,14 @@ public class OptionalHeader implements StructConverter { } /** - * {@return 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 Integer.toUnsignedLong(baseOfCode); } /** - * {@return the RVA of the first byte of data when loaded into memory.} + * {@return the RVA of the first byte of data when loaded into memory} */ public long getBaseOfData() { return Integer.toUnsignedLong(baseOfData); @@ -731,42 +686,42 @@ public class OptionalHeader implements StructConverter { } /** - * {@return the major version number of the required operating system.} + * {@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.} + * {@return the minor version number of the required operating system} */ public short getMinorOperatingSystemVersion() { return minorOperatingSystemVersion; } /** - * {@return the major version number of the image.} + * {@return the major version number of the image} */ public short getMajorImageVersion() { return majorImageVersion; } /** - * {@return the minor version number of the image.} + * {@return the minor version number of the image} */ public short getMinorImageVersion() { return minorImageVersion; } /** - * {@return the major version number of the subsystem.} + * {@return the major version number of the subsystem} */ public short getMajorSubsystemVersion() { return majorSubsystemVersion; } /** - * {@return the minor version number of the subsystem.} + * {@return the minor version number of the subsystem} */ public short getMinorSubsystemVersion() { return minorSubsystemVersion; @@ -812,22 +767,21 @@ public class OptionalHeader implements StructConverter { } /** - * {@return the image file checksum.} + * {@return the image file checksum} */ public int getChecksum() { return checkSum; } /** - * {@return the subsystem that is required to run this image.} + * {@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 + * {@return the flags that describe properties of and features of this binary} */ public short getDllCharacteristics() { return dllCharacteristics; @@ -862,21 +816,21 @@ public class OptionalHeader implements StructConverter { } /** - * {@return the flags passed to the loader. Obsolete.} + * {@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.} + * {@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.} + * {@return the array of data directories} */ public DataDirectory[] getDataDirectories() { return dataDirectory; @@ -911,7 +865,8 @@ public class OptionalHeader implements StructConverter { ddstruct.add(DWORD, "Size", null); ddstruct.setCategoryPath(new CategoryPath("/PE")); - StructureDataType struct = new StructureDataType(getName(), 0); + String name = "IMAGE_OPTIONAL_HEADER" + (is64bit() ? "64" : "32"); + StructureDataType struct = new StructureDataType(name, 0); struct.add(WORD, "Magic", null); struct.add(BYTE, "MajorLinkerVersion", null); @@ -970,7 +925,6 @@ public class OptionalHeader implements StructConverter { * * @param raf the random access file * @param dc the data converter - * * @throws IOException if an IO-related error occurred */ public void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { @@ -1066,7 +1020,7 @@ public class OptionalHeader implements StructConverter { } /** - * {@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 */ @@ -1089,8 +1043,4 @@ public class OptionalHeader implements StructConverter { 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/PeUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PeUtils.java index ec17b7bf1d..fe8e6b5853 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PeUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PeUtils.java @@ -78,4 +78,17 @@ public class PeUtils { } } + /** + * {@return the given value rounded up to the nearest given alignment} + * + * @param value the value to align + * @param alignment the alignment value + */ + public static int align(int value, int alignment) { + if (alignment == 0) { + return value; + } + return Math.ceilDiv(value, alignment) * alignment; + } + } 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 6cc66a8f13..92e074162c 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 @@ -20,6 +20,7 @@ import java.io.RandomAccessFile; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.Writeable; import ghidra.app.util.bin.format.mz.DOSHeader; import ghidra.app.util.importer.MessageLog; import ghidra.util.DataConverter; @@ -28,23 +29,18 @@ import ghidra.util.task.TaskMonitor; /** * A class to manage loading Portable Executables (PE). - * - * */ -public class PortableExecutable { +public class PortableExecutable implements Writeable { public static final String NAME = "PORTABLE_EXECUTABLE"; public static boolean DEBUG = false; /** - * Indicates how sections of this PE are laid out in the underlying ByteProvider. - * Use {@link SectionLayout#FILE} when loading from a file, and {@link SectionLayout#MEMORY} when - * loading from a memory model (like an already-loaded program in Ghidra). + * Indicates how sections of this PE are laid out in the underlying {@link ByteProvider}. + * Use {@link SectionLayout#FILE} when loading from a file, and {@link SectionLayout#MEMORY} + * when loading from a memory model (like an already-loaded program in Ghidra). */ public static enum SectionLayout { - /** Indicates the sections of this PE are laid out as stored in a file. **/ - FILE, - /** Indicates the sections of this PE are laid out as loaded into memory **/ - MEMORY + FILE, MEMORY } private BinaryReader reader; @@ -52,28 +48,25 @@ public class PortableExecutable { private RichHeader richHeader; private NTHeader ntHeader; - //private FileHeader fileHeader; - /** - * Constructs a new Portable Executable using the specified byte provider and layout. - *

- * Same as calling createFileAlignedPortableExecutable(factory, bp, layout, true, false) - * @param bp the byte provider + * Constructs a new {@link PortableExecutable} using the specified byte provider and layout + * + * @param bp the {@link ByteProvider} * @param layout specifies the layout of the underlying provider and governs RVA resolution - * @throws IOException if an I/O error occurs. - * @see #PortableExecutable(ByteProvider, SectionLayout, boolean, boolean) - **/ + * @throws IOException if an I/O error occurs + */ public PortableExecutable(ByteProvider bp, SectionLayout layout) throws IOException { this(bp, layout, true, false); } /** - * Constructs a new Portable Executable using the specified byte provider and layout. - * @param bp the byte provider + * Constructs a new {@link PortableExecutable} using the specified byte provider and layout + * + * @param bp the {@link ByteProvider} * @param layout specifies the layout of the underlying provider and governs RVA resolution * @param advancedProcess if true, the data directories are also processed * @param parseCliHeaders if true, CLI headers are parsed (if present) - * @throws IOException if an I/O error occurs. + * @throws IOException if an I/O error occurs */ public PortableExecutable(ByteProvider bp, SectionLayout layout, boolean advancedProcess, boolean parseCliHeaders) throws IOException { @@ -101,56 +94,38 @@ public class PortableExecutable { } return; } - - //fileHeader = new FileHeader(reader); - // - //if (fileHeader.getMachineName() != null) { - // if (fileHeader.getSizeOfOptionalHeader() == 0) { - // Err.debug(this, "This is a .OBJ file..."); - // } - // else if (fileHeader.getSizeOfOptionalHeader() == Constants.IMAGE_SIZEOF_ROM_OPTIONAL_HEADER) { - // Err.debug(this, "This is a ROM image..."); - // } - //} - //if (isValidLibrary(reader)) { - // Err.debug(this, "This is a library/archive file..."); - //} } /** - * Returns the DOS header from the PE image. - * @return the DOS header from the PE image + * {@return the DOS header from the PE image} */ public DOSHeader getDOSHeader() { return dosHeader; } /** - * Returns the Rich header from the PE image. - * @return the Rich header from the PE image + * {@return the rich header from the PE image} */ public RichHeader getRichHeader() { return richHeader; } /** - * Returns the NT header from the PE image. - * @return the NT header from the PE image + * {@return the NT header from the PE image} */ public NTHeader getNTHeader() { return ntHeader; } - //private boolean isValidLibrary(BinaryReader reader) throws IOException { - // String s = reader.readAsciiString(0); - // if (s != null && s.length() >= Constants.IMAGE_ARCHIVE_START_SIZE) { - // return s.substring(0, Constants.IMAGE_ARCHIVE_START_SIZE).equals(Constants.IMAGE_ARCHIVE_START); - // } - // return false; - //} - - public void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { + /** + * {@return the length of the {@link PortableExecutable} file in bytes + */ + public long getFileLength() { + return reader != null ? reader.length() : 0; + } + @Override + public void write(RandomAccessFile raf, DataConverter dc) throws IOException { raf.seek(0); if (dosHeader != null) { dosHeader.write(raf, dc); @@ -159,19 +134,7 @@ public class PortableExecutable { richHeader.write(raf, dc); } if (ntHeader != null) { - ntHeader.writeHeader(raf, dc); + ntHeader.write(raf, dc); } } - - public static int computeAlignment(int value, int alignment) { - if (alignment == 0 || (value % alignment) == 0) { - return value; - } - int a = ((value + alignment) / alignment) * alignment; - return a; - } - - public long getFileLength() { - return reader != null ? reader.length() : 0; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SectionHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SectionHeader.java index 74a311c189..4d5358e7a1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SectionHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SectionHeader.java @@ -18,211 +18,179 @@ package ghidra.app.util.bin.format.pe; import java.io.*; import ghidra.app.util.bin.*; +import ghidra.app.util.bin.format.Writeable; import ghidra.program.model.data.*; import ghidra.program.model.mem.*; import ghidra.util.DataConverter; import ghidra.util.exception.DuplicateNameException; -/** - * A class to the represent the IMAGE_SECTION_HEADER - * struct as defined in winnt.h. - *
- *

- * typedef struct _IMAGE_SECTION_HEADER {
- *    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
- *    union {
- *            DWORD   PhysicalAddress;
- *            DWORD   VirtualSize;			// MANDATORY
- *    } Misc;
- *    DWORD   VirtualAddress;				// MANDATORY
- *    DWORD   SizeOfRawData;				// MANDATORY
- *    DWORD   PointerToRawData;				// MANDATORY
- *    DWORD   PointerToRelocations;
- *    DWORD   PointerToLinenumbers;
- *    WORD    NumberOfRelocations;
- *    WORD    NumberOfLinenumbers;
- *    DWORD   Characteristics;				// MANDATORY
- * } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; * 
- * 
- *
- * #define IMAGE_SIZEOF_SECTION_HEADER 40 * - * - * - */ -public class SectionHeader implements StructConverter, ByteArrayConverter { - /** - * The name to use when converting into a structure data type. - */ +/// A class to the represent the `IMAGE_SECTION_HEADER` struct as defined in `winnt.h` +/// +/// ```c +/// typedef struct _IMAGE_SECTION_HEADER { +/// BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; +/// union { +/// DWORD PhysicalAddress; +/// DWORD VirtualSize; // MANDATORY +/// } Misc; +/// DWORD VirtualAddress; // MANDATORY +/// DWORD SizeOfRawData; // MANDATORY +/// DWORD PointerToRawData; // MANDATORY +/// DWORD PointerToRelocations; +/// DWORD PointerToLinenumbers; +/// WORD NumberOfRelocations; +/// WORD NumberOfLinenumbers; +/// DWORD Characteristics; // MANDATORY +/// } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; +/// +/// #define IMAGE_SIZEOF_SECTION_HEADER 40 +/// ``` +public class SectionHeader implements StructConverter, ByteArrayConverter, Writeable { + /// The name to use when converting into a structure data type public final static String NAME = "IMAGE_SECTION_HEADER"; - /** - * The size of the section header short name. - */ + + /// The size of the section header short name public final static int IMAGE_SIZEOF_SHORT_NAME = 8; - /** - * The size of the section header. - */ + + /// The size of the section header public final static int IMAGE_SIZEOF_SECTION_HEADER = 40; -// public final static int IMAGE_SCN_TYPE_REG = 0x00000000; -// public final static int IMAGE_SCN_TYPE_DSECT = 0x00000001; -// public final static int IMAGE_SCN_TYPE_NOLOAD = 0x00000002; -// public final static int IMAGE_SCN_TYPE_GROUP = 0x00000004; -// public final static int IMAGE_SCN_TYPE_NO_PAD = 0x00000008; -// public final static int IMAGE_SCN_TYPE_COPY = 0x00000010; - /** - * Section contains code. - */ - public final static int IMAGE_SCN_CNT_CODE = 0x00000020; - /** - * Section contains initialized data. - */ - public final static int IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040; - /** - * Section contains uninitialized data. - */ - public final static int IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080; -// public final static int IMAGE_SCN_LNK_OTHER = 0x00000100; - /** - * Section contains information for use by the linker. - * Only exists in OBJs. - */ - public final static int IMAGE_SCN_LNK_INFO = 0x00000200; -// public final static int IMAGE_SCN_TYPE_OVER = 0x00000400; - /** - * Section contents will not become part of the image. - * This only appears in OBJ files. - */ - public final static int IMAGE_SCN_LNK_REMOVE = 0x00000800; - /** - * Section contents is communal data (comdat). - * Communal data is data (or code) that can be - * defined in multiple OBJs. The linker will select - * one copy to include in the executable. Comdats - * are vital for support of C++ template functions - * and function-level linking. Comdat sections only - * appear in OBJ files. - */ - public final static int IMAGE_SCN_LNK_COMDAT = 0x00001000; -// Reserved. = 0x00002000; -// public final static int IMAGE_SCN_MEM_PROTECTED - Obsolete = 0x00004000; - /** - * Reset speculative exceptions handling bits in the TLB entries for this section. - */ - public final static int IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000; - /** - * Section content can be accessed relative to GP. - */ - public final static int IMAGE_SCN_GPREL = 0x00008000; -// public final static int IMAGE_SCN_MEM_FARDATA = 0x00008000; -// public final static int IMAGE_SCN_MEM_SYSHEAP - Obsolete = 0x00010000; -// public final static int IMAGE_SCN_MEM_PURGEABLE = 0x00020000; -// public final static int IMAGE_SCN_MEM_16BIT = 0x00020000; -// public final static int IMAGE_SCN_MEM_LOCKED = 0x00040000; -// public final static int IMAGE_SCN_MEM_PRELOAD = 0x00080000; - /** - * Align on 1-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_1BYTES = 0x00100000; - /** - * Align on 2-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_2BYTES = 0x00200000; - /** - * Align on 4-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_4BYTES = 0x00300000; - /** - * Align on 8-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_8BYTES = 0x00400000; - /** - * Align on 16-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_16BYTES = 0x00500000; - /** - * Align on 32-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_32BYTES = 0x00600000; - /** - * Align on 64-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_64BYTES = 0x00700000; - /** - * Align on 128-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_128BYTES = 0x00800000; - /** - * Align on 256-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_256BYTES = 0x00900000; - /** - * Align on 512-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_512BYTES = 0x00A00000; - /** - * Align on 1024-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000; - /** - * Align on 2048-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000; - /** - * Align on 4096-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000; - /** - * Align on 8192-byte boundary. - */ - public final static int IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000; - /** - * Mask for alignment flags - */ - public final static int IMAGE_SCN_ALIGN_MASK = 0x00F00000; - /** - * Section contains extended relocations. - */ - public final static int IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000; - /** - * The section can be discarded from the final executable. - * Used to hold information for the linker's use, - * including the .debug$ sections. - */ - public final static int IMAGE_SCN_MEM_DISCARDABLE = 0x02000000; - /** - * Section is not cachable. - */ - public final static int IMAGE_SCN_MEM_NOT_CACHED = 0x04000000; - /** - * The section is not pageable, so it should - * always be physically present in memory. - * Often used for kernel-mode drivers. - */ - public final static int IMAGE_SCN_MEM_NOT_PAGED = 0x08000000; - /** - * Section is shareable. The physical pages containing this - * section's data will be shared between all processes - * that have this executable loaded. Thus, every process - * will see the exact same values for data in this section. - * Useful for making global variables shared between all - * instances of a process. To make a section shared, - * use the /section:name,S linker switch. - */ - public final static int IMAGE_SCN_MEM_SHARED = 0x10000000; - /** - * Section is executable. - */ - public final static int IMAGE_SCN_MEM_EXECUTE = 0x20000000; - /** - * Section is readable. - */ - public final static int IMAGE_SCN_MEM_READ = 0x40000000; - /** - * Section is writeable. - */ - public final static int IMAGE_SCN_MEM_WRITE = 0x80000000; + /// Reserved for future use + public final static int IMAGE_SCN_RESERVED1 = 0x00000000; - public final static int NOT_SET = -1; + /// Reserved for future use + public final static int IMAGE_SCN_RESERVED2 = 0x00000001; + + /// Reserved for future use + public final static int IMAGE_SCN_RESERVED3 = 0x00000002; + + /// Reserved for future use + public final static int IMAGE_SCN_RESERVED4 = 0x00000004; + + public final static int IMAGE_SCN_TYPE_NO_PAD = 0x00000008; + + /// Reserved for future use + public final static int IMAGE_SCN_RESERVED5 = 0x00000010; + + /// Section contains code + public final static int IMAGE_SCN_CNT_CODE = 0x00000020; + + /// Section contains initialized data + public final static int IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040; + + /// Section contains uninitialized data + public final static int IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080; + + /// Reserved for future use + public final static int IMAGE_SCN_LNK_OTHER = 0x00000100; + + /// Section contains information for use by the linker (only exists in OBJs) + public final static int IMAGE_SCN_LNK_INFO = 0x00000200; + + /// Reserved for future use + public final static int IMAGE_SCN_RESERVED6 = 0x00000400; + + /// Section contents will not become part of the image (this only appears in OBJ files) + public final static int IMAGE_SCN_LNK_REMOVE = 0x00000800; + + /// Section contents is communal data (comdat). Communal data is data (or code) that can be + /// defined in multiple OBJs. The linker will select one copy to include in the executable. + /// Comdats are vital for support of C++ template functions and function-level linking. Comdat + /// sections only appear in OBJ files. + public final static int IMAGE_SCN_LNK_COMDAT = 0x00001000; + + /// Reset speculative exceptions handling bits in the TLB entries for this section + public final static int IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000; + + /// Section content can be accessed relative to GP + public final static int IMAGE_SCN_GPREL = 0x00008000; + + /// Reserved for future use + public final static int IMAGE_SCN_MEM_PURGEABLE = 0x00020000; + + /// Reserved for future use + public final static int IMAGE_SCN_MEM_16BIT = 0x00020000; + + /// Reserved for future use + public final static int IMAGE_SCN_MEM_LOCKED = 0x00040000; + + /// Reserved for future use + public final static int IMAGE_SCN_MEM_PRELOAD = 0x00080000; + + /// Align on 1-byte boundary + public final static int IMAGE_SCN_ALIGN_1BYTES = 0x00100000; + + /// Align on 2-byte boundary + public final static int IMAGE_SCN_ALIGN_2BYTES = 0x00200000; + + /// Align on 4-byte boundary + public final static int IMAGE_SCN_ALIGN_4BYTES = 0x00300000; + + /// Align on 8-byte boundary + public final static int IMAGE_SCN_ALIGN_8BYTES = 0x00400000; + + /// Align on 16-byte boundary + public final static int IMAGE_SCN_ALIGN_16BYTES = 0x00500000; + + /// Align on 32-byte boundary + public final static int IMAGE_SCN_ALIGN_32BYTES = 0x00600000; + + /// Align on 64-byte boundary + public final static int IMAGE_SCN_ALIGN_64BYTES = 0x00700000; + + /// Align on 128-byte boundary + public final static int IMAGE_SCN_ALIGN_128BYTES = 0x00800000; + + /// Align on 256-byte boundary + public final static int IMAGE_SCN_ALIGN_256BYTES = 0x00900000; + + /// Align on 512-byte boundary + public final static int IMAGE_SCN_ALIGN_512BYTES = 0x00A00000; + + /// Align on 1024-byte boundary + public final static int IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000; + + /// Align on 2048-byte boundary + public final static int IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000; + + /// Align on 4096-byte boundary + public final static int IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000; + + /// Align on 8192-byte boundary + public final static int IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000; + + /// Mask for alignment flags + public final static int IMAGE_SCN_ALIGN_MASK = 0x00F00000; + + /// Section contains extended relocations + public final static int IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000; + + /// The section can be discarded from the final executable. Used to hold information for the + /// linker's use, including the .debug$ sections. + public final static int IMAGE_SCN_MEM_DISCARDABLE = 0x02000000; + + /// Section is not cachable + public final static int IMAGE_SCN_MEM_NOT_CACHED = 0x04000000; + + /// The section is not pageable, so it should always be physically present in memory. Often used + /// for kernel-mode drivers. + public final static int IMAGE_SCN_MEM_NOT_PAGED = 0x08000000; + + /// Section is shareable. The physical pages containing this section's data will be shared + /// between all processes that have this executable loaded. Thus, every process will see the + /// exact same values for data in this section. Useful for making global variables shared + /// between all instances of a process. To make a section shared, use the /section:name,S + /// linker switch. + public final static int IMAGE_SCN_MEM_SHARED = 0x10000000; + + /// Section is executable + public final static int IMAGE_SCN_MEM_EXECUTE = 0x20000000; + + /// Section is readable + public final static int IMAGE_SCN_MEM_READ = 0x40000000; + + /// Section is writeable + public final static int IMAGE_SCN_MEM_WRITE = 0x80000000; private String name; private int physicalAddress; @@ -239,58 +207,55 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { private BinaryReader reader; /** - * Read a {@link SectionHeader} from the specified stream starting at {@code index}. + * Creates a new {@link SectionHeader} from the specified stream starting at {@code index} * * @param reader {@link BinaryReader} to read from * @param index long offset in the reader where the section header starts * @param stringTableOffset offset of the string table, or -1 if not available - * @return new {@link SectionHeader} * @throws IOException if error reading data */ - public static SectionHeader readSectionHeader(BinaryReader reader, long index, - long stringTableOffset) throws IOException { - SectionHeader result = new SectionHeader(); + public SectionHeader(BinaryReader reader, long index, long stringTableOffset) + throws IOException { + this.reader = reader; - result.reader = reader; - - result.name = reader.readAsciiString(index, IMAGE_SIZEOF_SHORT_NAME).trim(); - if (result.name.startsWith("/") && stringTableOffset != -1) { + name = reader.readAsciiString(index, IMAGE_SIZEOF_SHORT_NAME).trim(); + if (name.startsWith("/") && stringTableOffset != -1) { try { - int nameOffset = Integer.parseInt(result.name.substring(1)); - result.name = reader.readAsciiString(stringTableOffset + nameOffset); + int nameOffset = Integer.parseInt(name.substring(1)); + name = reader.readAsciiString(stringTableOffset + nameOffset); } catch (NumberFormatException | IOException nfe) { // ignore format or out-of-bounds errors...section name will remain as it was } } - // we need to skip IMAGE_SIZEOF_SHORT_NAME chars no matter what, - // since those bytes are always allocated + // We need to skip IMAGE_SIZEOF_SHORT_NAME chars no matter what since those bytes are always + // allocated reader.setPointerIndex(index + IMAGE_SIZEOF_SHORT_NAME); - result.physicalAddress = result.virtualSize = reader.readNextInt(); - result.virtualAddress = reader.readNextInt(); - result.sizeOfRawData = reader.readNextInt(); - result.pointerToRawData = reader.readNextInt(); - result.pointerToRelocations = reader.readNextInt(); - result.pointerToLinenumbers = reader.readNextInt(); - result.numberOfRelocations = reader.readNextShort(); - result.numberOfLinenumbers = reader.readNextShort(); - result.characteristics = reader.readNextInt(); - - return result; - } - - private SectionHeader() { - // empty + physicalAddress = virtualSize = reader.readNextInt(); + virtualAddress = reader.readNextInt(); + sizeOfRawData = reader.readNextInt(); + pointerToRawData = reader.readNextInt(); + pointerToRelocations = reader.readNextInt(); + pointerToLinenumbers = reader.readNextInt(); + numberOfRelocations = reader.readNextShort(); + numberOfLinenumbers = reader.readNextShort(); + characteristics = reader.readNextInt(); } + /** + * Creates a new {@link SectionHeader} from the given {@link MemoryBlock} + * + * @param block The {@link MemoryBlock} + * @param optHeader The {@link OptionalHeader} + * @param ptr The pointer to raw data + */ SectionHeader(MemoryBlock block, OptionalHeader optHeader, int ptr) { name = block.getName(); physicalAddress = virtualSize = (int) block.getSize(); virtualAddress = (int) block.getStart().getOffset() - (int) optHeader.getImageBase(); - sizeOfRawData = - PortableExecutable.computeAlignment(virtualSize, optHeader.getFileAlignment()); + sizeOfRawData = PeUtils.align(virtualSize, optHeader.getFileAlignment()); pointerToRawData = ptr; pointerToLinenumbers = 0; pointerToRelocations = 0; @@ -308,8 +273,7 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { SectionFlags.IMAGE_SCN_CNT_CODE.getMask(); } if (block.isExecute()) { - characteristics |= - SectionHeader.IMAGE_SCN_MEM_EXECUTE | SectionHeader.IMAGE_SCN_CNT_CODE; + characteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE; } else if (block.getType() == MemoryBlockType.DEFAULT) {//not executable, then must be data... if (block.isInitialized()) { @@ -322,91 +286,67 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { } /** - * Returns the ASCII name of the section. A - * section name is not guaranteed to be - * null-terminated. If you specify a section name - * longer than eight characters, the linker - * truncates it to eight characters in the - * executable. A mechanism exists for allowing - * longer section names in OBJ files. Section - * names often start with a period, but this is - * not a requirement. Section names with a $ in - * the name get special treatment from the linker. - * Sections with identical names prior to the $ - * character are merged. The characters following - * the $ provide an alphabetic ordering for how the - * merged sections appear in the final section. - * There's quite a bit more to the subject of sections - * with $ in the name and how they're combined, but - * the details are outside the scope of this article - * - * @return the ASCII name of the section + * {@return the ASCII name of the section} + *

+ * A section name is not guaranteed to be null-terminated. If you specify a section name + * longer than eight characters, the linker truncates it to eight characters in the executable. + * A mechanism exists for allowing longer section names in OBJ files. Section names often start + * with a period, but this is not a requirement. Section names with a $ in the name get special + * treatment from the linker. Sections with identical names prior to the $ character are merged. + * The characters following the $ provide an alphabetic ordering for how the merged sections + * appear in the final section. There's quite a bit more to the subject of sections with $ in + * the name and how they're combined, but the details are outside the scope of this article. */ public String getName() { return name; } /** - * Returns a readable ascii version of the name. - * All non-readable characters - * are replaced with underscores. - * @return a readable ascii version of the name + * {@return a readable ascii version of the name (all non-readable characters are replaced with + * underscores} */ public String getReadableName() { - StringBuffer buffer = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < name.length(); ++i) { char ch = name.charAt(i); if (ch >= 0x20 && ch <= 0x7e) {//is readable ascii? - buffer.append(ch); + sb.append(ch); } else { - buffer.append('_'); + sb.append('_'); } } - return buffer.toString(); + return sb.toString(); } /** - * Returns the physical (file) address of this section. - * @return the physical (file) address of this section + * {@return the physical (file) address of this section} */ public int getPhysicalAddress() { return physicalAddress; } /** - * In executables, returns the RVA where - * the section begins in memory. Should be set to 0 in OBJs. - * this section should be loaded into memory. - * @return the RVA where the section begins in memory. + * {@return the RVA where the section begins in memory} */ public int getVirtualAddress() { return virtualAddress; } /** - * Returns the actual, used size of the section. - * This field may be larger or - * smaller than the SizeOfRawData field. - * If the VirtualSize is larger, the - * SizeOfRawData field is the size of the - * initialized data from the executable, - * and the remaining bytes up to the VirtualSize - * should be zero-padded. This field is set - * to 0 in OBJ files. - * @return the actual, used size of the section + * {@return the actual, used size of the section} + *

+ * This field may be larger or smaller than the SizeOfRawData field. If the VirtualSize is + * larger, the SizeOfRawData field is the size of the initialized data from the executable, and + * the remaining bytes up to the VirtualSize should be zero-padded. This field is set to 0 in + * OBJ files. */ public int getVirtualSize() { - if (virtualSize == 0) { - return sizeOfRawData; - } - return virtualSize; + return virtualSize != 0 ? virtualSize : sizeOfRawData; } /** - * Returns the size (in bytes) of data stored for the section - * in the executable or OBJ. - * @return the size (in bytes) of data stored for the section + * {@return the size (in bytes) of data stored for the section} */ public int getSizeOfRawData() { return sizeOfRawData; @@ -438,28 +378,22 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { return pointerToRawData; } - /** - * Returns the file offset of relocations for this section. - * @return the file offset of relocations for this section + * {@return the file offset of relocations for this section} */ public int getPointerToRelocations() { return pointerToRelocations; } /** - * Returns the number of relocations pointed - * to by the PointerToRelocations field. - * @return the number of relocations + * {@return the number of relocations pointed to by the {@code PointerToRelocations} field} */ public short getNumberOfRelocations() { return numberOfRelocations; } /** - * Return the file offset for COFF-style line - * numbers for this section. - * @return the file offset for COFF-style line numbers for this section + * {@return the file offset for COFF-style line numbers for this section} */ public int getPointerToLinenumbers() { return pointerToLinenumbers; @@ -477,9 +411,7 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { } /** - * Returns the number of line numbers pointed to by the - * NumberOfRelocations field. - * @return the number of line numbers + * {@return the number of line numbers pointed to by the {@code NumberOfRelocations} field */ public short getNumberOfLinenumbers() { return numberOfLinenumbers; @@ -491,26 +423,22 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { } /** - * Returns an input stream to underlying bytes of this section. - * @return an input stream to underlying bytes of this section - * @throws IOException if an i/o error occurs. + * {@return an {@link InputStream} to underlying bytes of this section} + * + * @throws IOException if an IO-related error occurred */ public InputStream getDataStream() throws IOException { return reader.getByteProvider().getInputStream(getPointerToRawData()); } /** - * Returns a ByteProvider to underlying bytes of this section. - * @return a ByteProvider to underlying bytes of this section + * {@return a {@link ByteProvider} to underlying bytes of this section} */ - public ByteProvider getDataByteProvider() { + public ByteProvider getDataByteProvider() { return new ByteProviderWrapper(reader.getByteProvider(), getPointerToRawData(), getSizeOfRawData()); } - /** - * @see java.lang.Object#toString() - */ @Override public String toString() { StringBuffer buff = new StringBuffer(); @@ -562,13 +490,8 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { return struct; } - /** - * Writes this section header to the specified random access file. - * @param raf the random access file - * @param dc the data converter - * @throws IOException if an I/O error occurs - */ - public void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { + @Override + public void write(RandomAccessFile raf, DataConverter dc) throws IOException { byte[] paddedName = new byte[IMAGE_SIZEOF_SHORT_NAME]; byte[] nameBytes = name.getBytes(); System.arraycopy(nameBytes, 0, paddedName, 0, @@ -587,17 +510,15 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { } /** - * Writes the bytes from this section into the specified random access file. - * The bytes will be written starting at the byte position - * specified by getPointerToRawData(). + * Writes the bytes from this section into the specified random access file. The bytes will be + * written starting at the byte position specified by {@link #getPointerToRawData()} * - * @param raf the random access file - * @param rafIndex the index into the RAF where the bytes will be written - * @param dc the data converter - * @param block the memory block corresponding to this section - * @param useBlockBytes if true, then use the bytes from the memory block, - * otherwise use the bytes from this section. - * + * @param raf the random access file + * @param rafIndex the index into the RAF where the bytes will be written + * @param dc the data converter + * @param block the memory block corresponding to this section + * @param useBlockBytes if true, then use the bytes from the memory block, otherwise use the + * bytes from this section. * @throws IOException if there are errors writing to the file * @throws MemoryAccessException if the byte from the memory block cannot be accesses */ @@ -610,8 +531,6 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { raf.seek(rafIndex); - //if ((block.getType() == MemoryBlock.INITIALIZED) || (block.getType() == MemoryBlock.LIVE)) { - if (useBlockBytes) { byte[] blockBytes = new byte[(int) block.getSize()]; block.getBytes(block.getStart(), blockBytes); @@ -625,7 +544,6 @@ public class SectionHeader implements StructConverter, ByteArrayConverter { if (padLength > 0) { raf.write(new byte[padLength]); } - //} } void updatePointers(int offset) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SeparateDebugHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SeparateDebugHeader.java index ee0200b5e7..4961595e57 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SeparateDebugHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/SeparateDebugHeader.java @@ -110,7 +110,7 @@ public class SeparateDebugHeader implements OffsetValidator { sections = new SectionHeader[numberOfSections]; for (int i = 0; i < numberOfSections; ++i) { - sections[i] = SectionHeader.readSectionHeader(reader, ptr, -1); + sections[i] = new SectionHeader(reader, ptr, -1); ptr += SectionHeader.IMAGE_SIZEOF_SECTION_HEADER; }