GP-2826: PeLoader now handles sections partially or fully outside of the

file better
This commit is contained in:
Ryan Kurtz
2022-11-14 03:39:34 -05:00
parent 81fd59389d
commit 322a4e81a8
4 changed files with 56 additions and 41 deletions
@@ -332,32 +332,43 @@ public class FileHeader implements StructConverter {
long stringTableOffset = getStringTableOffset();
sectionHeaders = new SectionHeader[numberOfSections];
for (int i = 0; i < numberOfSections; ++i) {
sectionHeaders[i] =
SectionHeader section =
SectionHeader.readSectionHeader(reader, tmpIndex, stringTableOffset);
sectionHeaders[i] = section;
int pointerToRawData = section.getPointerToRawData();
int sizeOfRawData = section.getSizeOfRawData();
// Ensure PointerToRawData + SizeOfRawData doesn't exceed the length of the file
int pointerToRawData = sectionHeaders[i].getPointerToRawData();
int sizeOfRawData = (int) Math.min(reader.length() - pointerToRawData,
sectionHeaders[i].getSizeOfRawData());
if (pointerToRawData >= reader.length()) {
sizeOfRawData = 0;
Msg.warn(this, "Section " + section.getName() + " begins after end of file!");
}
else if (pointerToRawData + sizeOfRawData > reader.length()) {
sizeOfRawData = (int) (reader.length() - pointerToRawData);
Msg.warn(this,
String.format("Section %s exceeds file length...truncating from %d to %d",
section.getName(), section.getSizeOfRawData(), sizeOfRawData));
}
section.setSizeOfRawData(sizeOfRawData);
// Ensure VirtualSize is large enough to accommodate SizeOfRawData, but do not
// exceed the next alignment boundary. We can only do this if the VirtualAddress is
// already properly aligned, since we currently don't support moving sections to
// different addresses to enforce alignment.
int virtualAddress = sectionHeaders[i].getVirtualAddress();
int virtualSize = sectionHeaders[i].getVirtualSize();
int virtualAddress = section.getVirtualAddress();
int virtualSize = section.getVirtualSize();
int alignedVirtualAddress = PortableExecutable.computeAlignment(virtualAddress,
optHeader.getSectionAlignment());
int alignedVirtualSize = PortableExecutable.computeAlignment(virtualSize,
optHeader.getSectionAlignment());
if (virtualAddress == alignedVirtualAddress) {
if (sizeOfRawData > virtualSize) {
sectionHeaders[i]
.setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize));
section.setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize));
}
}
else {
Msg.warn(this, "Section " + sectionHeaders[i].getName() + " is not aligned!");
Msg.warn(this, "Section " + section.getName() + " is not aligned!");
}
tmpIndex += SectionHeader.IMAGE_SIZEOF_SECTION_HEADER;
}
@@ -22,10 +22,11 @@ import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
import ghidra.program.model.data.*;
import ghidra.util.*;
import ghidra.util.DataConverter;
import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.NotYetImplementedException;
import ghidra.util.task.TaskMonitorAdapter;
import ghidra.util.task.TaskMonitor;
/**
* A class to represent the <b><code>IMAGE_NT_HEADERS32</code></b> and
@@ -62,9 +63,11 @@ public class NTHeader implements StructConverter, OffsetValidator {
* Constructs a new NT header.
* @param reader the binary reader
* @param index the index into the reader to the start of the NT header
* @param advancedProcess if true, information rafside of the base header will be processed
* @param layout The {@link SectionLayout}
* @param advancedProcess if true, information outside of the base header will be processed
* @param parseCliHeaders if true, CLI headers are parsed (if present)
* @throws InvalidNTHeaderException if the bytes the specified index
* @throws IOException if an IO-related exception occurred
* do not constitute an accurate NT header.
*/
public NTHeader(BinaryReader reader, int index, SectionLayout layout, boolean advancedProcess,
@@ -124,22 +127,26 @@ public class NTHeader implements StructConverter, OffsetValidator {
/**
* Converts a relative virtual address (RVA) into a pointer.
* @see #rvaToPointer(long)
*
* @param rva the relative virtual address
* @return the pointer into binary image, 0 if not valid
*/
public int rvaToPointer(int rva) {
return (int) rvaToPointer(rva & Conv.INT_MASK);
return (int) rvaToPointer(Integer.toUnsignedLong(rva));
}
/**
* Converts a relative virtual address (RVA) into a pointer.
* @param rva the relative virtual address
* @return the pointer into binary image, 0 if not valid
*/
public long rvaToPointer(long rva) {
SectionHeader[] sections = fileHeader.getSectionHeaders();
for (SectionHeader section : sections) {
long sectionVA = section.getVirtualAddress() & Conv.INT_MASK;
long rawSize = section.getSizeOfRawData() & Conv.INT_MASK;
long rawPtr = section.getPointerToRawData() & Conv.INT_MASK;
long sectionVA = Integer.toUnsignedLong(section.getVirtualAddress());
long rawSize = Integer.toUnsignedLong(section.getSizeOfRawData());
long rawPtr = Integer.toUnsignedLong(section.getPointerToRawData());
switch (layout) {
case MEMORY:
@@ -169,10 +176,10 @@ public class NTHeader implements StructConverter, OffsetValidator {
public boolean checkPointer(long ptr) {
SectionHeader[] sections = fileHeader.getSectionHeaders();
for (SectionHeader section : sections) {
long virtPtr = section.getVirtualAddress() & Conv.INT_MASK;
long virtSize = section.getVirtualSize() & Conv.INT_MASK;
long rawSize = section.getSizeOfRawData() & Conv.INT_MASK;
long rawPtr = section.getPointerToRawData() & Conv.INT_MASK;
long virtPtr = Integer.toUnsignedLong(section.getVirtualAddress());
long virtSize = Integer.toUnsignedLong(section.getVirtualSize());
long rawSize = Integer.toUnsignedLong(section.getSizeOfRawData());
long rawPtr = Integer.toUnsignedLong(section.getPointerToRawData());
long sectionBasePtr = layout == SectionLayout.MEMORY ? virtPtr : rawPtr;
long sectionSize = layout == SectionLayout.MEMORY ? virtSize : rawSize;
@@ -199,13 +206,17 @@ public class NTHeader implements StructConverter, OffsetValidator {
/**
* Converts a virtual address (VA) into a pointer.
* @see #vaToPointer(long)
*
* @param va the virtual address
* @return the pointer into binary image, 0 if not valid
*/
public int vaToPointer(int va) {
return (int) vaToPointer(va & Conv.INT_MASK);
return (int) vaToPointer(Integer.toUnsignedLong(va));
}
/**
* Converts a virtual address (VA) into a pointer.
*
* @param va the virtual address
* @return the pointer into binary image, 0 if not valid
*/
@@ -213,10 +224,6 @@ public class NTHeader implements StructConverter, OffsetValidator {
return rvaToPointer(va - getOptionalHeader().getImageBase());
}
/**
* @throws InvalidNTHeaderException
* @throws IOException
*/
private void parse() throws InvalidNTHeaderException, IOException {
if (index < 0 || index > reader.length()) {
@@ -229,6 +236,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
signature = reader.readInt(tmpIndex);
}
catch (IndexOutOfBoundsException ioobe) {
// Handled below
}
// if not correct signature, then return...
@@ -256,7 +264,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
fileHeader.processSymbols();
if (advancedProcess) {
optionalHeader.processDataDirectories(TaskMonitorAdapter.DUMMY_MONITOR);
optionalHeader.processDataDirectories(TaskMonitor.DUMMY);
}
}
@@ -20,7 +20,6 @@ import java.io.*;
import ghidra.app.util.bin.*;
import ghidra.program.model.data.*;
import ghidra.program.model.mem.*;
import ghidra.util.Conv;
import ghidra.util.DataConverter;
import ghidra.util.exception.DuplicateNameException;
@@ -535,7 +534,7 @@ public class SectionHeader implements StructConverter, ByteArrayConverter {
EnumDataType characteristicsEnum = new EnumDataType("SectionFlags", 4);
characteristicsEnum.setCategoryPath(new CategoryPath("/PE"));
for (SectionFlags flag : SectionFlags.values()) {
characteristicsEnum.add(flag.name(), Conv.intToLong(flag.getMask()));
characteristicsEnum.add(flag.name(), Integer.toUnsignedLong(flag.getMask()));
}
struct.add(characteristicsEnum, "Characteristics", null);
struct.setCategoryPath(new CategoryPath("/PE"));
@@ -16,7 +16,6 @@
package ghidra.app.util.opinion;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import ghidra.app.util.MemoryBlockUtils;
@@ -36,8 +35,6 @@ import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
@@ -109,7 +106,6 @@ public class PeLoader extends AbstractPeDebugLoader {
return;
}
OptionalHeader optionalHeader = ntHeader.getOptionalHeader();
FileHeader fileHeader = ntHeader.getFileHeader();
monitor.setMessage("Completing PE header parsing...");
FileBytes fileBytes = createFileBytes(provider, program, monitor);
@@ -406,7 +402,7 @@ public class PeLoader extends AbstractPeDebugLoader {
//than 32bit. On WindowsCE some sections are
//declared to roll over.
if (!optionalHeader.is64bit()) {
addr &= Conv.INT_MASK;
addr &= 0x00000000ffffffffL;
}
Address address = space.getAddress(addr);
@@ -686,6 +682,11 @@ public class PeLoader extends AbstractPeDebugLoader {
address = space.getAddress(addr);
String sectionName = sections[i].getReadableName();
if (sectionName.isBlank()) {
sectionName = "SECTION." + i;
}
r = ((sections[i].getCharacteristics() &
SectionFlags.IMAGE_SCN_MEM_READ.getMask()) != 0x0);
w = ((sections[i].getCharacteristics() &
@@ -707,10 +708,6 @@ public class PeLoader extends AbstractPeDebugLoader {
Msg.warn(this, "OptionalHeader.SizeOfImage < size of " +
sections[i].getName() + " section");
}
String sectionName = sections[i].getReadableName();
if (sectionName.isBlank()) {
sectionName = "SECTION." + i;
}
MemoryBlockUtils.createInitializedBlock(prog, false, sectionName, address,
fileBytes, rawDataPtr, dataSize, "", "", r, w, x, log);
sectionToAddress.put(sections[i], address);
@@ -740,8 +737,8 @@ public class PeLoader extends AbstractPeDebugLoader {
else {
int dataSize = (virtualSize > 0 || rawDataSize < 0) ? virtualSize : 0;
if (dataSize > 0) {
MemoryBlockUtils.createUninitializedBlock(prog, false,
sections[i].getReadableName(), address, dataSize, "", "", r, w, x, log);
MemoryBlockUtils.createUninitializedBlock(prog, false, sectionName, address,
dataSize, "", "", r, w, x, log);
sectionToAddress.put(sections[i], address);
}
}