mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 21:55:32 +08:00
GP-2826: PeLoader now handles sections partially or fully outside of the
file better
This commit is contained in:
@@ -332,32 +332,43 @@ public class FileHeader implements StructConverter {
|
|||||||
long stringTableOffset = getStringTableOffset();
|
long stringTableOffset = getStringTableOffset();
|
||||||
sectionHeaders = new SectionHeader[numberOfSections];
|
sectionHeaders = new SectionHeader[numberOfSections];
|
||||||
for (int i = 0; i < numberOfSections; ++i) {
|
for (int i = 0; i < numberOfSections; ++i) {
|
||||||
sectionHeaders[i] =
|
SectionHeader section =
|
||||||
SectionHeader.readSectionHeader(reader, tmpIndex, stringTableOffset);
|
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
|
// Ensure PointerToRawData + SizeOfRawData doesn't exceed the length of the file
|
||||||
int pointerToRawData = sectionHeaders[i].getPointerToRawData();
|
if (pointerToRawData >= reader.length()) {
|
||||||
int sizeOfRawData = (int) Math.min(reader.length() - pointerToRawData,
|
sizeOfRawData = 0;
|
||||||
sectionHeaders[i].getSizeOfRawData());
|
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
|
// 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
|
// 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
|
// already properly aligned, since we currently don't support moving sections to
|
||||||
// different addresses to enforce alignment.
|
// different addresses to enforce alignment.
|
||||||
int virtualAddress = sectionHeaders[i].getVirtualAddress();
|
int virtualAddress = section.getVirtualAddress();
|
||||||
int virtualSize = sectionHeaders[i].getVirtualSize();
|
int virtualSize = section.getVirtualSize();
|
||||||
int alignedVirtualAddress = PortableExecutable.computeAlignment(virtualAddress,
|
int alignedVirtualAddress = PortableExecutable.computeAlignment(virtualAddress,
|
||||||
optHeader.getSectionAlignment());
|
optHeader.getSectionAlignment());
|
||||||
int alignedVirtualSize = PortableExecutable.computeAlignment(virtualSize,
|
int alignedVirtualSize = PortableExecutable.computeAlignment(virtualSize,
|
||||||
optHeader.getSectionAlignment());
|
optHeader.getSectionAlignment());
|
||||||
if (virtualAddress == alignedVirtualAddress) {
|
if (virtualAddress == alignedVirtualAddress) {
|
||||||
if (sizeOfRawData > virtualSize) {
|
if (sizeOfRawData > virtualSize) {
|
||||||
sectionHeaders[i]
|
section.setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize));
|
||||||
.setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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;
|
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.StructConverter;
|
||||||
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
||||||
import ghidra.program.model.data.*;
|
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.DuplicateNameException;
|
||||||
import ghidra.util.exception.NotYetImplementedException;
|
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
|
* 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.
|
* Constructs a new NT header.
|
||||||
* @param reader the binary reader
|
* @param reader the binary reader
|
||||||
* @param index the index into the reader to the start of the NT header
|
* @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)
|
* @param parseCliHeaders if true, CLI headers are parsed (if present)
|
||||||
* @throws InvalidNTHeaderException if the bytes the specified index
|
* @throws InvalidNTHeaderException if the bytes the specified index
|
||||||
|
* @throws IOException if an IO-related exception occurred
|
||||||
* do not constitute an accurate NT header.
|
* do not constitute an accurate NT header.
|
||||||
*/
|
*/
|
||||||
public NTHeader(BinaryReader reader, int index, SectionLayout layout, boolean advancedProcess,
|
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.
|
* 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) {
|
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
|
* @param rva the relative virtual address
|
||||||
* @return the pointer into binary image, 0 if not valid
|
* @return the pointer into binary image, 0 if not valid
|
||||||
*/
|
*/
|
||||||
public long rvaToPointer(long rva) {
|
public long rvaToPointer(long rva) {
|
||||||
SectionHeader[] sections = fileHeader.getSectionHeaders();
|
SectionHeader[] sections = fileHeader.getSectionHeaders();
|
||||||
for (SectionHeader section : sections) {
|
for (SectionHeader section : sections) {
|
||||||
long sectionVA = section.getVirtualAddress() & Conv.INT_MASK;
|
long sectionVA = Integer.toUnsignedLong(section.getVirtualAddress());
|
||||||
long rawSize = section.getSizeOfRawData() & Conv.INT_MASK;
|
long rawSize = Integer.toUnsignedLong(section.getSizeOfRawData());
|
||||||
long rawPtr = section.getPointerToRawData() & Conv.INT_MASK;
|
long rawPtr = Integer.toUnsignedLong(section.getPointerToRawData());
|
||||||
|
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
case MEMORY:
|
case MEMORY:
|
||||||
@@ -169,10 +176,10 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
|||||||
public boolean checkPointer(long ptr) {
|
public boolean checkPointer(long ptr) {
|
||||||
SectionHeader[] sections = fileHeader.getSectionHeaders();
|
SectionHeader[] sections = fileHeader.getSectionHeaders();
|
||||||
for (SectionHeader section : sections) {
|
for (SectionHeader section : sections) {
|
||||||
long virtPtr = section.getVirtualAddress() & Conv.INT_MASK;
|
long virtPtr = Integer.toUnsignedLong(section.getVirtualAddress());
|
||||||
long virtSize = section.getVirtualSize() & Conv.INT_MASK;
|
long virtSize = Integer.toUnsignedLong(section.getVirtualSize());
|
||||||
long rawSize = section.getSizeOfRawData() & Conv.INT_MASK;
|
long rawSize = Integer.toUnsignedLong(section.getSizeOfRawData());
|
||||||
long rawPtr = section.getPointerToRawData() & Conv.INT_MASK;
|
long rawPtr = Integer.toUnsignedLong(section.getPointerToRawData());
|
||||||
|
|
||||||
long sectionBasePtr = layout == SectionLayout.MEMORY ? virtPtr : rawPtr;
|
long sectionBasePtr = layout == SectionLayout.MEMORY ? virtPtr : rawPtr;
|
||||||
long sectionSize = layout == SectionLayout.MEMORY ? virtSize : rawSize;
|
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.
|
* 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) {
|
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
|
* @param va the virtual address
|
||||||
* @return the pointer into binary image, 0 if not valid
|
* @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());
|
return rvaToPointer(va - getOptionalHeader().getImageBase());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws InvalidNTHeaderException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
private void parse() throws InvalidNTHeaderException, IOException {
|
private void parse() throws InvalidNTHeaderException, IOException {
|
||||||
|
|
||||||
if (index < 0 || index > reader.length()) {
|
if (index < 0 || index > reader.length()) {
|
||||||
@@ -229,6 +236,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
|||||||
signature = reader.readInt(tmpIndex);
|
signature = reader.readInt(tmpIndex);
|
||||||
}
|
}
|
||||||
catch (IndexOutOfBoundsException ioobe) {
|
catch (IndexOutOfBoundsException ioobe) {
|
||||||
|
// Handled below
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not correct signature, then return...
|
// if not correct signature, then return...
|
||||||
@@ -256,7 +264,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
|||||||
fileHeader.processSymbols();
|
fileHeader.processSymbols();
|
||||||
|
|
||||||
if (advancedProcess) {
|
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.app.util.bin.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.mem.*;
|
import ghidra.program.model.mem.*;
|
||||||
import ghidra.util.Conv;
|
|
||||||
import ghidra.util.DataConverter;
|
import ghidra.util.DataConverter;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
||||||
@@ -535,7 +534,7 @@ public class SectionHeader implements StructConverter, ByteArrayConverter {
|
|||||||
EnumDataType characteristicsEnum = new EnumDataType("SectionFlags", 4);
|
EnumDataType characteristicsEnum = new EnumDataType("SectionFlags", 4);
|
||||||
characteristicsEnum.setCategoryPath(new CategoryPath("/PE"));
|
characteristicsEnum.setCategoryPath(new CategoryPath("/PE"));
|
||||||
for (SectionFlags flag : SectionFlags.values()) {
|
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.add(characteristicsEnum, "Characteristics", null);
|
||||||
struct.setCategoryPath(new CategoryPath("/PE"));
|
struct.setCategoryPath(new CategoryPath("/PE"));
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
package ghidra.app.util.opinion;
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.util.MemoryBlockUtils;
|
import ghidra.app.util.MemoryBlockUtils;
|
||||||
@@ -36,8 +35,6 @@ import ghidra.program.database.function.OverlappingFunctionException;
|
|||||||
import ghidra.program.database.mem.FileBytes;
|
import ghidra.program.database.mem.FileBytes;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
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.listing.*;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
@@ -109,7 +106,6 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
OptionalHeader optionalHeader = ntHeader.getOptionalHeader();
|
OptionalHeader optionalHeader = ntHeader.getOptionalHeader();
|
||||||
FileHeader fileHeader = ntHeader.getFileHeader();
|
|
||||||
|
|
||||||
monitor.setMessage("Completing PE header parsing...");
|
monitor.setMessage("Completing PE header parsing...");
|
||||||
FileBytes fileBytes = createFileBytes(provider, program, monitor);
|
FileBytes fileBytes = createFileBytes(provider, program, monitor);
|
||||||
@@ -406,7 +402,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||||||
//than 32bit. On WindowsCE some sections are
|
//than 32bit. On WindowsCE some sections are
|
||||||
//declared to roll over.
|
//declared to roll over.
|
||||||
if (!optionalHeader.is64bit()) {
|
if (!optionalHeader.is64bit()) {
|
||||||
addr &= Conv.INT_MASK;
|
addr &= 0x00000000ffffffffL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Address address = space.getAddress(addr);
|
Address address = space.getAddress(addr);
|
||||||
@@ -686,6 +682,11 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||||||
|
|
||||||
address = space.getAddress(addr);
|
address = space.getAddress(addr);
|
||||||
|
|
||||||
|
String sectionName = sections[i].getReadableName();
|
||||||
|
if (sectionName.isBlank()) {
|
||||||
|
sectionName = "SECTION." + i;
|
||||||
|
}
|
||||||
|
|
||||||
r = ((sections[i].getCharacteristics() &
|
r = ((sections[i].getCharacteristics() &
|
||||||
SectionFlags.IMAGE_SCN_MEM_READ.getMask()) != 0x0);
|
SectionFlags.IMAGE_SCN_MEM_READ.getMask()) != 0x0);
|
||||||
w = ((sections[i].getCharacteristics() &
|
w = ((sections[i].getCharacteristics() &
|
||||||
@@ -707,10 +708,6 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||||||
Msg.warn(this, "OptionalHeader.SizeOfImage < size of " +
|
Msg.warn(this, "OptionalHeader.SizeOfImage < size of " +
|
||||||
sections[i].getName() + " section");
|
sections[i].getName() + " section");
|
||||||
}
|
}
|
||||||
String sectionName = sections[i].getReadableName();
|
|
||||||
if (sectionName.isBlank()) {
|
|
||||||
sectionName = "SECTION." + i;
|
|
||||||
}
|
|
||||||
MemoryBlockUtils.createInitializedBlock(prog, false, sectionName, address,
|
MemoryBlockUtils.createInitializedBlock(prog, false, sectionName, address,
|
||||||
fileBytes, rawDataPtr, dataSize, "", "", r, w, x, log);
|
fileBytes, rawDataPtr, dataSize, "", "", r, w, x, log);
|
||||||
sectionToAddress.put(sections[i], address);
|
sectionToAddress.put(sections[i], address);
|
||||||
@@ -740,8 +737,8 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||||||
else {
|
else {
|
||||||
int dataSize = (virtualSize > 0 || rawDataSize < 0) ? virtualSize : 0;
|
int dataSize = (virtualSize > 0 || rawDataSize < 0) ? virtualSize : 0;
|
||||||
if (dataSize > 0) {
|
if (dataSize > 0) {
|
||||||
MemoryBlockUtils.createUninitializedBlock(prog, false,
|
MemoryBlockUtils.createUninitializedBlock(prog, false, sectionName, address,
|
||||||
sections[i].getReadableName(), address, dataSize, "", "", r, w, x, log);
|
dataSize, "", "", r, w, x, log);
|
||||||
sectionToAddress.put(sections[i], address);
|
sectionToAddress.put(sections[i], address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user