mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-11-03 18:07:37 -10:00
Merge remote-tracking branch 'origin/GP-952_ryanmkurtz_pdata' (#3803)
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.pe;
|
||||
|
||||
import static ghidra.app.util.bin.format.pe.FileHeader.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
@@ -23,15 +25,18 @@ import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ExceptionDataDirectory extends DataDirectory {
|
||||
private final static String NAME = "IMAGE_DIRECTORY_ENTRY_EXCEPTION";
|
||||
|
||||
private ImageRuntimeFunctionEntries functionEntries;
|
||||
|
||||
ExceptionDataDirectory(NTHeader ntHeader, BinaryReader reader) throws IOException {
|
||||
processDataDirectory(ntHeader, reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDirectoryName() {
|
||||
@@ -39,11 +44,31 @@ public class ExceptionDataDirectory extends DataDirectory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse() {
|
||||
public boolean parse() throws IOException {
|
||||
int ptr = getPointer();
|
||||
if (ptr < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
reader.setPointerIndex(ptr);
|
||||
|
||||
functionEntries = switch (ntHeader.getFileHeader().getMachine() & IMAGE_FILE_MACHINE_MASK) {
|
||||
case IMAGE_FILE_MACHINE_I386:
|
||||
case IMAGE_FILE_MACHINE_IA64:
|
||||
case IMAGE_FILE_MACHINE_AMD64:
|
||||
yield new ImageRuntimeFunctionEntries_X86(reader, size, ntHeader);
|
||||
case IMAGE_FILE_MACHINE_ARM:
|
||||
case IMAGE_FILE_MACHINE_ARM64:
|
||||
case IMAGE_FILE_MACHINE_ARMNT:
|
||||
yield new ImageRuntimeFunctionEntries_ARM(reader, size, ntHeader);
|
||||
default:
|
||||
Msg.error(this, String.format("Exception Data unsupported architecture: 0x%02x",
|
||||
ntHeader.getFileHeader().getMachine()));
|
||||
yield null;
|
||||
};
|
||||
|
||||
reader.setPointerIndex(oldIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,17 +77,16 @@ public class ExceptionDataDirectory extends DataDirectory {
|
||||
NTHeader ntHeader)
|
||||
throws DuplicateNameException, CodeUnitInsertionException, IOException {
|
||||
|
||||
monitor.setMessage(program.getName()+": exceptions...");
|
||||
Address addr = PeUtils.getMarkupAddress(program, isBinary, ntHeader, virtualAddress);
|
||||
if (!program.getMemory().contains(addr)) {
|
||||
return;
|
||||
}
|
||||
createDirectoryBookmark(program, addr);
|
||||
if (functionEntries != null) {
|
||||
functionEntries.markup(program, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.util.bin.StructConverter#toDataType()
|
||||
*/
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException {
|
||||
StructureDataType struct = new StructureDataType(NAME, size);
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbolAux;
|
||||
import ghidra.program.model.data.*;
|
||||
@@ -179,9 +178,6 @@ public class FileHeader implements StructConverter {
|
||||
private SectionHeader[] sectionHeaders;
|
||||
private List<DebugCOFFSymbol> symbols = new ArrayList<>();
|
||||
|
||||
// TODO: This is x86-64 architecture-specific and needs to be generalized.
|
||||
private List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = new ArrayList<>();
|
||||
|
||||
private BinaryReader reader;
|
||||
private int startIndex;
|
||||
private NTHeader ntHeader;
|
||||
@@ -238,15 +234,6 @@ public class FileHeader implements StructConverter {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of RUNTIME_INFO entries, if any are present.
|
||||
* @return An array of _IMAGE_RUNTIME_FUNCTION_ENTRY. The array can be empty.
|
||||
* TODO: This is x86-64 architecture-specific and needs to be generalized.
|
||||
*/
|
||||
public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getImageRuntimeFunctionEntries() {
|
||||
return irfes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the section header that contains the specified virtual address.
|
||||
* @param virtualAddr the virtual address
|
||||
@@ -379,36 +366,6 @@ public class FileHeader implements StructConverter {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
}
|
||||
|
||||
void processImageRuntimeFunctionEntries() throws IOException {
|
||||
FileHeader fh = ntHeader.getFileHeader();
|
||||
SectionHeader[] sections = fh.getSectionHeaders();
|
||||
|
||||
// Look for an exception handler section for an array of
|
||||
// RUNTIME_FUNCTION structures, bail if one isn't found
|
||||
SectionHeader irfeHeader = null;
|
||||
for (SectionHeader header : sections) {
|
||||
if (header.getName().equals(".pdata")) {
|
||||
irfeHeader = header;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irfeHeader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
|
||||
int start = ntHeader.rvaToPointer(irfeHeader.getVirtualAddress());
|
||||
reader.setPointerIndex(start);
|
||||
|
||||
ImageRuntimeFunctionEntries entries =
|
||||
new ImageRuntimeFunctionEntries(reader, start, ntHeader);
|
||||
irfes = entries.getRuntimeFunctionEntries();
|
||||
|
||||
reader.setPointerIndex(oldIndex);
|
||||
}
|
||||
|
||||
void processSymbols() throws IOException {
|
||||
if (isLordPE()) {
|
||||
return;
|
||||
|
||||
@@ -16,160 +16,27 @@
|
||||
package ghidra.app.util.bin.format.pe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
* DWORD BeginAddress;
|
||||
* DWORD EndAddress;
|
||||
* union {
|
||||
* DWORD UnwindInfoAddress;
|
||||
* DWORD UnwindData;
|
||||
* } DUMMYUNIONNAME;
|
||||
* } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
*
|
||||
* #define UNW_FLAG_NHANDLER 0x0
|
||||
* #define UNW_FLAG_EHANDLER 0x1
|
||||
* #define UNW_FLAG_UHANDLER 0x2
|
||||
* #define UNW_FLAG_CHAININFO 0x4
|
||||
*
|
||||
* typedef struct _UNWIND_INFO {
|
||||
* UCHAR Version : 3;
|
||||
* UCHAR Flags : 5;
|
||||
* UCHAR SizeOfProlog;
|
||||
* UCHAR CountOfUnwindCodes;
|
||||
* UCHAR FrameRegister : 4;
|
||||
* UCHAR FrameOffset : 4;
|
||||
* UNWIND_CODE UnwindCode[1];
|
||||
*
|
||||
* //
|
||||
* // The unwind codes are followed by an optional DWORD aligned field that
|
||||
* // contains the exception handler address or the address of chained unwind
|
||||
* // information. If an exception handler address is specified, then it is
|
||||
* // followed by the language specified exception handler data.
|
||||
* //
|
||||
* // union {
|
||||
* // ULONG ExceptionHandler;
|
||||
* // ULONG FunctionEntry;
|
||||
* // };
|
||||
* //
|
||||
* // ULONG ExceptionData[];
|
||||
* //
|
||||
* } UNWIND_INFO, *PUNWIND_INFO;
|
||||
* An interface for working with function table entries used for exception handling, which are found
|
||||
* in the .pdata section. The actual implementations are architecture-specific.
|
||||
*/
|
||||
public class ImageRuntimeFunctionEntries {
|
||||
|
||||
private final static int UNWIND_INFO_SIZE = 0x0C;
|
||||
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> functionEntries = new ArrayList<>();
|
||||
|
||||
ImageRuntimeFunctionEntries(BinaryReader reader, long index, NTHeader ntHeader)
|
||||
throws IOException {
|
||||
int entryCount = 0;
|
||||
|
||||
// Find the exception handler data section. This is an unbounded array of
|
||||
// RUNTIME_INFO structures one after another and there's no count field
|
||||
// to tell us how many there are, so get the maximum number there could be
|
||||
// based on the size of the section.
|
||||
FileHeader fh = ntHeader.getFileHeader();
|
||||
for (SectionHeader section : fh.getSectionHeaders()) {
|
||||
if (section.getName().contentEquals(".pdata")) {
|
||||
entryCount = section.getSizeOfRawData() / UNWIND_INFO_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entryCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
reader.setPointerIndex(index);
|
||||
|
||||
for (int i = 0; i < entryCount; i++) {
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY entry = new _IMAGE_RUNTIME_FUNCTION_ENTRY();
|
||||
entry.beginAddress = reader.readNextUnsignedInt();
|
||||
entry.endAddress = reader.readNextUnsignedInt();
|
||||
entry.unwindInfoAddressOrData = reader.readNextUnsignedInt();
|
||||
|
||||
// When the size of the section is bigger than the number of structures
|
||||
// the structure data fields will all be null, signaling the end of the
|
||||
// array of structures. Break out here.
|
||||
if (entry.beginAddress == 0 && entry.endAddress == 0 &&
|
||||
entry.unwindInfoAddressOrData == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read and process the UNWIND_INFO structures the RUNTIME_INFO
|
||||
// structures point to
|
||||
entry.unwindInfo =
|
||||
PEx64UnwindInfo.readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader);
|
||||
|
||||
functionEntries.add(entry);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
}
|
||||
|
||||
public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getRuntimeFunctionEntries() {
|
||||
return functionEntries;
|
||||
}
|
||||
|
||||
public static void createData(Program program, Address headerStart,
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes) {
|
||||
// TODO: This is x86-64 architecture-specific and needs to be generalized.
|
||||
StructureDataType dt = new StructureDataType(".PDATA", 0);
|
||||
dt.setCategoryPath(new CategoryPath("/PE"));
|
||||
|
||||
// Lay an array of RUNTIME_INFO structure out over the data
|
||||
StructureDataType irfeStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0);
|
||||
irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "BeginAddress", null);
|
||||
irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "EndAddress", null);
|
||||
irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "UnwindInfoAddressOrData", null);
|
||||
|
||||
ArrayDataType irfeArray =
|
||||
new ArrayDataType(irfeStruct, irfes.size(), irfeStruct.getLength());
|
||||
|
||||
try {
|
||||
DataUtilities.createData(program, headerStart, irfeArray, irfeArray.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
catch (CodeUnitInsertionException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: change name to conform to Java naming standards
|
||||
// FIXME: If public visibility is required improved member protection is needed
|
||||
public static class _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
long beginAddress;
|
||||
long endAddress;
|
||||
long unwindInfoAddressOrData;
|
||||
PEx64UnwindInfo unwindInfo;
|
||||
|
||||
public void createData(Program program) {
|
||||
if (unwindInfoAddressOrData > 0) {
|
||||
try {
|
||||
DataType dt = unwindInfo.toDataType();
|
||||
Address start = program.getImageBase().add(unwindInfoAddressOrData);
|
||||
|
||||
DataUtilities.createData(program, start, dt, dt.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public interface ImageRuntimeFunctionEntries {
|
||||
|
||||
/**
|
||||
* Marks up an {@link ImageRuntimeFunctionEntries}
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @param start The start {@link Address}
|
||||
* @throws IOException If there was an IO-related error creating the data
|
||||
* @throws DuplicateNameException If a data type of the same name already exists
|
||||
* @throws CodeUnitInsertionException If data creation failed
|
||||
*/
|
||||
public void markup(Program program, Address start) throws CodeUnitInsertionException,
|
||||
IOException, DuplicateNameException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.pe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* typedef struct _IMAGE_ARM_RUNTIME_FUNCTION_ENTRY {
|
||||
* DWORD BeginAddress;
|
||||
* union {
|
||||
* DWORD UnwindData;
|
||||
* struct {
|
||||
* DWORD Flag : 2;
|
||||
* DWORD FunctionLength : 11;
|
||||
* DWORD Ret : 2;
|
||||
* DWORD H : 1;
|
||||
* DWORD Reg : 3;
|
||||
* DWORD R : 1;
|
||||
* DWORD L : 1;
|
||||
* DWORD C : 1;
|
||||
* DWORD StackAdjust : 10;
|
||||
* } DUMMYSTRUCTNAME;
|
||||
* } DUMMYUNIONNAME;
|
||||
* } IMAGE_ARM_RUNTIME_FUNCTION_ENTRY, * PIMAGE_ARM_RUNTIME_FUNCTION_ENTRY;
|
||||
* </pre>
|
||||
* @see <a href="https://github.com/MicrosoftDocs/cpp-docs/blob/main/docs/build/arm-exception-handling.md">arm-exception-handling.md</a>
|
||||
*/
|
||||
public class ImageRuntimeFunctionEntries_ARM implements ImageRuntimeFunctionEntries {
|
||||
|
||||
private final static int ENTRY_SIZE = 0x08;
|
||||
|
||||
private List<ImageRuntimeFunctionEntry_ARM> functionEntries = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ImageRuntimeFunctionEntries_X86}
|
||||
*
|
||||
* @param reader A {@link BinaryReader} positioned at the start of the exception data directory
|
||||
* @param size The size of the exception data directory
|
||||
* @param ntHeader The {@link NTHeader}
|
||||
* @throws IOException If there was an issue parsing the exception data directory
|
||||
*/
|
||||
ImageRuntimeFunctionEntries_ARM(BinaryReader reader, int size, NTHeader ntHeader)
|
||||
throws IOException {
|
||||
int numEntries = size / ENTRY_SIZE;
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
long beginAddress = reader.readNextUnsignedInt() & ~0x1L; // low bit is set when thumb
|
||||
int data = reader.readNextInt();
|
||||
if (beginAddress == 0 && data == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
functionEntries.add(new ImageRuntimeFunctionEntry_ARM(beginAddress, data));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(Program program, Address headerStart) throws CodeUnitInsertionException,
|
||||
IOException, DuplicateNameException {
|
||||
StructureDataType exceptionInfoStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0);
|
||||
exceptionInfoStruct.setPackingEnabled(true);
|
||||
exceptionInfoStruct.add(StructConverter.IBO32, "BeginAddress", null);
|
||||
exceptionInfoStruct.add(StructConverter.IBO32, "ExceptionInfo", null);
|
||||
|
||||
StructureDataType unwindDataStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY_2", 0);
|
||||
unwindDataStruct.setPackingEnabled(true);
|
||||
unwindDataStruct.add(StructConverter.IBO32, "BeginAddress", null);
|
||||
try {
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 2, "Flag", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 11, "FunctionLength", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 2, "Ret", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 1, "H", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 3, "Reg", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 1, "R", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 1, "L", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 1, "C", null);
|
||||
unwindDataStruct.addBitField(StructConverter.DWORD, 10, "StackAdjust", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
Address addr = headerStart;
|
||||
for (ImageRuntimeFunctionEntry_ARM entry : functionEntries) {
|
||||
DataType struct = entry.isExceptionInfoRVA() ? exceptionInfoStruct : unwindDataStruct;
|
||||
DataUtilities.createData(program, addr, struct, struct.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
addr = addr.add(ENTRY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ImageRuntimeFunctionEntries_ARM}
|
||||
*
|
||||
* @param beginAddress The RVA of the corresponding function
|
||||
* @param data The exception info RVA or the packed unwind data, depending on lower 2 bit flag
|
||||
*/
|
||||
record ImageRuntimeFunctionEntry_ARM(long beginAddress, int data) {
|
||||
|
||||
/**
|
||||
* Checks whether or not this entry is an exception info RVA or packed unwind data
|
||||
*
|
||||
* @return True if this entry is an exception info RVA, or false if it's packed unwind data
|
||||
*/
|
||||
public boolean isExceptionInfoRVA() {
|
||||
return (data & 0x3) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks up this entry
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @throws IOException If there was an IO-related error creating the data
|
||||
* @throws DuplicateNameException If a data type of the same name already exists
|
||||
* @throws CodeUnitInsertionException If data creation failed
|
||||
*/
|
||||
public void markup(Program program)
|
||||
throws DuplicateNameException, IOException, CodeUnitInsertionException {
|
||||
if (isExceptionInfoRVA()) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.pe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
* DWORD BeginAddress;
|
||||
* DWORD EndAddress;
|
||||
* union {
|
||||
* DWORD UnwindInfoAddress;
|
||||
* DWORD UnwindData;
|
||||
* } DUMMYUNIONNAME;
|
||||
* } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
*
|
||||
* #define UNW_FLAG_NHANDLER 0x0
|
||||
* #define UNW_FLAG_EHANDLER 0x1
|
||||
* #define UNW_FLAG_UHANDLER 0x2
|
||||
* #define UNW_FLAG_CHAININFO 0x4
|
||||
*
|
||||
* typedef struct _UNWIND_INFO {
|
||||
* UCHAR Version : 3;
|
||||
* UCHAR Flags : 5;
|
||||
* UCHAR SizeOfProlog;
|
||||
* UCHAR CountOfUnwindCodes;
|
||||
* UCHAR FrameRegister : 4;
|
||||
* UCHAR FrameOffset : 4;
|
||||
* UNWIND_CODE UnwindCode[1];
|
||||
*
|
||||
* //
|
||||
* // The unwind codes are followed by an optional DWORD aligned field that
|
||||
* // contains the exception handler address or the address of chained unwind
|
||||
* // information. If an exception handler address is specified, then it is
|
||||
* // followed by the language specified exception handler data.
|
||||
* //
|
||||
* // union {
|
||||
* // ULONG ExceptionHandler;
|
||||
* // ULONG FunctionEntry;
|
||||
* // };
|
||||
* //
|
||||
* // ULONG ExceptionData[];
|
||||
* //
|
||||
* } UNWIND_INFO, *PUNWIND_INFO;
|
||||
* </pre>
|
||||
*/
|
||||
public class ImageRuntimeFunctionEntries_X86 implements ImageRuntimeFunctionEntries {
|
||||
|
||||
private final static int ENTRY_SIZE = 0x0C;
|
||||
|
||||
private List<ImageRuntimeFunctionEntry_X86> functionEntries = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ImageRuntimeFunctionEntries_X86}
|
||||
*
|
||||
* @param reader A {@link BinaryReader} positioned at the start of the exception data directory
|
||||
* @param size The size of the exception data directory
|
||||
* @param ntHeader The {@link NTHeader}
|
||||
* @throws IOException If there was an issue parsing the exception data directory
|
||||
*/
|
||||
ImageRuntimeFunctionEntries_X86(BinaryReader reader, int size, NTHeader ntHeader)
|
||||
throws IOException {
|
||||
|
||||
int numEntries = size / ENTRY_SIZE;
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
long beginAddress = reader.readNextUnsignedInt();
|
||||
long endAddress = reader.readNextUnsignedInt();
|
||||
long unwindInfoAddressOrData = reader.readNextUnsignedInt();
|
||||
|
||||
if (beginAddress == 0 && endAddress == 0 && unwindInfoAddressOrData == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
PEx64UnwindInfo unwindInfo =
|
||||
PEx64UnwindInfo.readUnwindInfo(reader, unwindInfoAddressOrData, ntHeader);
|
||||
|
||||
ImageRuntimeFunctionEntry_X86 entry = new ImageRuntimeFunctionEntry_X86(beginAddress,
|
||||
endAddress, unwindInfoAddressOrData, unwindInfo);
|
||||
functionEntries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(Program program, Address headerStart) throws CodeUnitInsertionException,
|
||||
IOException, DuplicateNameException {
|
||||
StructureDataType struct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0);
|
||||
struct.add(StructConverter.IBO32, "BeginAddress", null);
|
||||
struct.add(StructConverter.IBO32, "EndAddress", null);
|
||||
struct.add(StructConverter.IBO32, "UnwindInfoAddressOrData", null);
|
||||
|
||||
ArrayDataType arr = new ArrayDataType(struct, functionEntries.size(), struct.getLength());
|
||||
|
||||
DataUtilities.createData(program, headerStart, arr, arr.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
|
||||
for (ImageRuntimeFunctionEntry_X86 entry : functionEntries) {
|
||||
entry.markup(program);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ImageRuntimeFunctionEntries_X86}
|
||||
*
|
||||
* @param beginAddress The RVA of the corresponding function
|
||||
* @param endAddress The RVA of the end of the function
|
||||
* @param unwindInfoAddressOrData The RVA of the unwind information
|
||||
* @param unwindInfo The parsed {@link PEx64UnwindInfo unwind information}
|
||||
*/
|
||||
record ImageRuntimeFunctionEntry_X86(long beginAddress, long endAddress,
|
||||
long unwindInfoAddressOrData, PEx64UnwindInfo unwindInfo) {
|
||||
|
||||
/**
|
||||
* Marks up this entry
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @throws IOException If there was an IO-related error creating the data
|
||||
* @throws DuplicateNameException If a data type of the same name already exists
|
||||
* @throws CodeUnitInsertionException If data creation failed
|
||||
*/
|
||||
public void markup(Program program)
|
||||
throws DuplicateNameException, IOException, CodeUnitInsertionException {
|
||||
if (unwindInfoAddressOrData > 0) {
|
||||
DataType dt = unwindInfo.toDataType();
|
||||
Address start = program.getImageBase().add(unwindInfoAddressOrData);
|
||||
|
||||
DataUtilities.createData(program, start, dt, dt.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,10 +254,6 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
|
||||
fileHeader.processSections(optionalHeader);
|
||||
fileHeader.processSymbols();
|
||||
if ((fileHeader.getMachine() &
|
||||
FileHeader.IMAGE_FILE_MACHINE_MASK) == FileHeader.IMAGE_FILE_MACHINE_AMD64) {
|
||||
fileHeader.processImageRuntimeFunctionEntries();
|
||||
}
|
||||
|
||||
if (advancedProcess) {
|
||||
optionalHeader.processDataDirectories(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries_X86.ImageRuntimeFunctionEntry_X86;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
@@ -48,7 +48,7 @@ class PEx64UnwindInfo implements StructConverter {
|
||||
byte frameOffset;
|
||||
UNWIND_CODE[] unwindCodes;
|
||||
int exceptionHandlerFunction;
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY unwindHandlerChainInfo;
|
||||
ImageRuntimeFunctionEntry_X86 unwindHandlerChainInfo;
|
||||
|
||||
long startOffset;
|
||||
|
||||
@@ -199,16 +199,15 @@ class PEx64UnwindInfo implements StructConverter {
|
||||
unwindInfo.exceptionHandlerFunction = reader.readNextInt();
|
||||
}
|
||||
else if (unwindInfo.hasChainedUnwindInfo()) {
|
||||
unwindInfo.unwindHandlerChainInfo =
|
||||
new _IMAGE_RUNTIME_FUNCTION_ENTRY();
|
||||
unwindInfo.unwindHandlerChainInfo.beginAddress = reader.readNextInt();
|
||||
unwindInfo.unwindHandlerChainInfo.endAddress = reader.readNextInt();
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData = reader.readNextInt();
|
||||
long beginAddress = reader.readNextUnsignedInt();
|
||||
long endAddress = reader.readNextUnsignedInt();
|
||||
long unwindInfoAddressOrData = reader.readNextUnsignedInt();
|
||||
|
||||
// Follow the chain to the referenced UNWIND_INFO structure until we
|
||||
// get to the end
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfo = readUnwindInfo(reader,
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData, ntHeader);
|
||||
PEx64UnwindInfo info = readUnwindInfo(reader, unwindInfoAddressOrData, ntHeader);
|
||||
unwindInfo.unwindHandlerChainInfo = new ImageRuntimeFunctionEntry_X86(beginAddress,
|
||||
endAddress, unwindInfoAddressOrData, info);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
|
||||
@@ -26,7 +26,6 @@ import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.mz.DOSHeader;
|
||||
import ghidra.app.util.bin.format.pe.*;
|
||||
import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugDirectoryParser;
|
||||
@@ -144,7 +143,6 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
processProperties(optionalHeader, program, monitor);
|
||||
processComments(program.getListing(), monitor);
|
||||
processSymbols(ntHeader, sectionToAddress, program, monitor, log);
|
||||
processImageRuntimeFunctionEntries(fileHeader, program, monitor, log);
|
||||
|
||||
processEntryPoints(ntHeader, program, monitor);
|
||||
String compiler = CompilerOpinion.getOpinion(pe, provider).toString();
|
||||
@@ -259,41 +257,6 @@ public class PeLoader extends AbstractPeDebugLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private void processImageRuntimeFunctionEntries(FileHeader fileHeader, Program program,
|
||||
TaskMonitor monitor, MessageLog log) {
|
||||
|
||||
// Check to see that we have exception data to process
|
||||
SectionHeader irfeHeader = null;
|
||||
for (SectionHeader header : fileHeader.getSectionHeaders()) {
|
||||
if (header.getName().contains(".pdata")) {
|
||||
irfeHeader = header;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irfeHeader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Address start = program.getImageBase().add(irfeHeader.getVirtualAddress());
|
||||
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = fileHeader.getImageRuntimeFunctionEntries();
|
||||
|
||||
if (irfes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This is x86-64 architecture-specific and needs to be generalized.
|
||||
ImageRuntimeFunctionEntries.createData(program, start, irfes);
|
||||
|
||||
// Each RUNTIME_INFO contains an address to an UNWIND_INFO structure
|
||||
// which also needs to be laid out. When they contain chaining data
|
||||
// they're recursive but the toDataType() function handles that.
|
||||
for (_IMAGE_RUNTIME_FUNCTION_ENTRY entry : irfes) {
|
||||
entry.createData(program);
|
||||
}
|
||||
}
|
||||
|
||||
private void processSymbols(NTHeader ntHeader, Map<SectionHeader, Address> sectionToAddress,
|
||||
Program program, TaskMonitor monitor, MessageLog log) {
|
||||
FileHeader fileHeader = ntHeader.getFileHeader();
|
||||
|
||||
Reference in New Issue
Block a user