mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-07-30 07:18:37 -09:00
GP-0: PeLoader cleanup
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 >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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user