GP-0: PeLoader cleanup

This commit is contained in:
Ryan Kurtz
2026-07-06 11:33:07 -04:00
parent a3c3eb3da6
commit fbcd6cffff
8 changed files with 524 additions and 738 deletions

View File

@@ -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

View File

@@ -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 <code>winnt.h</code>.
* <br>
* <pre>
* 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;
* </pre>
*
*/
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 <code>IMAGE_FILE_HEADER</code> 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 &gt;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<DebugCOFFSymbol> 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());
}
}

View File

@@ -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}
*
* <pre>{@code
* typedef struct _IMAGE_NT_HEADERS {
* DWORD Signature;
* IMAGE_FILE_HEADER FileHeader;
* IMAGE_OPTIONAL_HEADER32 OptionalHeader;
* };
* }</pre>
*/
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;
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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.
* <p>
* Same as calling <code>createFileAlignedPortableExecutable(factory, bp, layout, true, false)</code>
* @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;
}
}

View File

@@ -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 <code>winnt.h</code>.
* <br>
* <pre>
* 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; *
* </pre>
* <br>
* <code>#define IMAGE_SIZEOF_SECTION_HEADER 40</code> *
*
*
*/
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}
* <p>
* 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}
* <p>
* 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 <code>getPointerToRawData()</code>.
* 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) {

View File

@@ -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;
}