mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-955 Transitioned UNWIND_INFO to DynamicDataType. Corrected
DynamicDataType issues related to use of bitfields and trailing flex array.
This commit is contained in:
parent
6bb36e5f16
commit
73b07e4f48
4 changed files with 455 additions and 324 deletions
|
@ -19,10 +19,12 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataUtilities;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
|
@ -65,12 +67,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
* } UNWIND_INFO, *PUNWIND_INFO;
|
||||
*/
|
||||
public class ImageRuntimeFunctionEntries {
|
||||
private final static int UNWIND_INFO_VERSION_BITMASK = 0x07;
|
||||
private final static int UNWIND_INFO_FLAGS_SHIFT = 0x03;
|
||||
private final static int UNWIND_INFO_FRAME_REGISTER_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_FRAME_OFFSET_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_OPCODE_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_OPCODE_INFO_SHIFT = 0x04;
|
||||
|
||||
private final static int UNWIND_INFO_SIZE = 0x0C;
|
||||
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> functionEntries = new ArrayList<>();
|
||||
|
@ -132,7 +129,8 @@ public class ImageRuntimeFunctionEntries {
|
|||
|
||||
// Read and process the UNWIND_INFO structures the RUNTIME_INFO
|
||||
// structures point to
|
||||
entry.unwindInfo = readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader);
|
||||
entry.unwindInfo =
|
||||
PEx64UnwindInfo.readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader);
|
||||
|
||||
functionEntries.add(entry);
|
||||
}
|
||||
|
@ -140,317 +138,32 @@ public class ImageRuntimeFunctionEntries {
|
|||
reader.setPointerIndex(origIndex);
|
||||
}
|
||||
|
||||
private UNWIND_INFO readUnwindInfo(FactoryBundledWithBinaryReader reader, long offset,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
long pointer = ntHeader.rvaToPointer(offset);
|
||||
UNWIND_INFO unwindInfo = new UNWIND_INFO(pointer);
|
||||
|
||||
if (pointer < 0) {
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
reader.setPointerIndex(pointer);
|
||||
byte splitByte = reader.readNextByte();
|
||||
unwindInfo.version = (byte) (splitByte & UNWIND_INFO_VERSION_BITMASK);
|
||||
unwindInfo.flags = (byte) (splitByte >> UNWIND_INFO_FLAGS_SHIFT);
|
||||
|
||||
unwindInfo.sizeOfProlog = reader.readNextUnsignedByte();
|
||||
unwindInfo.countOfUnwindCodes = reader.readNextUnsignedByte();
|
||||
|
||||
splitByte = reader.readNextByte();
|
||||
unwindInfo.frameRegister = (byte) (splitByte & UNWIND_INFO_FRAME_REGISTER_MASK);
|
||||
unwindInfo.frameOffset = (byte) (splitByte >> UNWIND_INFO_FRAME_OFFSET_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes = new UNWIND_CODE[unwindInfo.countOfUnwindCodes];
|
||||
for (int i = 0; i < unwindInfo.countOfUnwindCodes; i++) {
|
||||
UNWIND_CODE code = new UNWIND_CODE();
|
||||
code.offsetInProlog = reader.readNextByte();
|
||||
|
||||
int opCodeData = reader.readNextUnsignedByte();
|
||||
code.opCode = UNWIND_CODE_OPCODE.fromInt((opCodeData & UNWIND_INFO_OPCODE_MASK));
|
||||
code.opInfoRegister =
|
||||
UNWIND_CODE_OPINFO_REGISTER.fromInt(opCodeData >> UNWIND_INFO_OPCODE_INFO_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes[i] = code;
|
||||
}
|
||||
|
||||
// You can have an exception handler or you can have chained exception handling info only.
|
||||
if (unwindInfo.hasExceptionHandler() || unwindInfo.hasUnwindHandler()) {
|
||||
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();
|
||||
|
||||
// Follow the chain to the referenced UNWIND_INFO structure until we
|
||||
// get to the end
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfo = readUnwindInfo(reader,
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData, ntHeader);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getRuntimeFunctionEntries() {
|
||||
return functionEntries;
|
||||
}
|
||||
|
||||
public class _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
public long beginAddress;
|
||||
public long endAddress;
|
||||
public long unwindInfoAddressOrData;
|
||||
public UNWIND_INFO unwindInfo;
|
||||
}
|
||||
// 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 enum UNWIND_CODE_OPCODE {
|
||||
UWOP_PUSH_NONVOL(0x00),
|
||||
UWOP_ALLOC_LARGE(0x01),
|
||||
UWOP_ALLOC_SMALL(0x02),
|
||||
UWOP_SET_FPREG(0x03),
|
||||
UWOP_SAVE_NONVOL(0x04),
|
||||
UWOP_SAVE_NONVOL_FAR(0x05),
|
||||
UWOP_SAVE_XMM(0x06),
|
||||
UWOP_SAVE_XMM_FAR(0x07),
|
||||
UWOP_SAVE_XMM128(0x08),
|
||||
UWOP_SAVE_XMM128_FAR(0x09),
|
||||
UWOP_PUSH_MACHFRAME(0x0A);
|
||||
public void createData(Program program) {
|
||||
if (unwindInfoAddressOrData > 0) {
|
||||
try {
|
||||
DataType dt = unwindInfo.toDataType();
|
||||
Address start = program.getImageBase().add(unwindInfoAddressOrData);
|
||||
|
||||
private final int id;
|
||||
|
||||
UNWIND_CODE_OPCODE(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPCODE fromInt(int id) {
|
||||
UNWIND_CODE_OPCODE[] values = UNWIND_CODE_OPCODE.values();
|
||||
for (UNWIND_CODE_OPCODE value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
DataUtilities.createData(program, start, dt, dt.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum UNWIND_CODE_OPINFO_REGISTER {
|
||||
UNWIND_OPINFO_REGISTER_RAX(0x00),
|
||||
UNWIND_OPINFO_REGISTER_RCX(0x01),
|
||||
UNWIND_OPINFO_REGISTER_RDX(0x02),
|
||||
UNWIND_OPINFO_REGISTER_RBX(0x03),
|
||||
UNWIND_OPINFO_REGISTER_RSP(0x04),
|
||||
UNWIND_OPINFO_REGISTER_RBP(0x05),
|
||||
UNWIND_OPINFO_REGISTER_RSI(0x06),
|
||||
UNWIND_OPINFO_REGISTER_RDI(0x07),
|
||||
UNWIND_OPINFO_REGISTER_R8(0x08),
|
||||
UNWIND_OPINFO_REGISTER_R9(0x09),
|
||||
UNWIND_OPINFO_REGISTER_R10(0x0A),
|
||||
UNWIND_OPINFO_REGISTER_R11(0x0B),
|
||||
UNWIND_OPINFO_REGISTER_R12(0x0C),
|
||||
UNWIND_OPINFO_REGISTER_R13(0x0D),
|
||||
UNWIND_OPINFO_REGISTER_R14(0x0E),
|
||||
UNWIND_OPINFO_REGISTER_R15(0x0F);
|
||||
|
||||
private final int id;
|
||||
|
||||
UNWIND_CODE_OPINFO_REGISTER(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPINFO_REGISTER fromInt(int id) {
|
||||
UNWIND_CODE_OPINFO_REGISTER[] values = UNWIND_CODE_OPINFO_REGISTER.values();
|
||||
for (UNWIND_CODE_OPINFO_REGISTER value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class UNWIND_CODE {
|
||||
public byte offsetInProlog;
|
||||
public UNWIND_CODE_OPCODE opCode;
|
||||
public UNWIND_CODE_OPINFO_REGISTER opInfoRegister;
|
||||
}
|
||||
|
||||
public class UNWIND_INFO implements StructConverter {
|
||||
private static final String NAME = "UNWIND_INFO";
|
||||
|
||||
private final static int UNW_FLAG_NHANDLER = 0x0;
|
||||
private final static int UNW_FLAG_EHANDLER = 0x1;
|
||||
private final static int UNW_FLAG_UHANDLER = 0x2;
|
||||
private final static int UNW_FLAG_CHAININFO = 0x4;
|
||||
|
||||
private final static int UNWIND_VERSION_FIELD_LENGTH = 0x03;
|
||||
private final static int UNWIND_FLAGS_FIELD_LENGTH = 0x05;
|
||||
private final static int UNWIND_FRAME_REGISTER_LENGTH = 0x04;
|
||||
private final static int UNWIND_FRAME_OFFSET_LENGTH = 0x04;
|
||||
private final static int UNWIND_OP_FIELD_LENGTH = 0x04;
|
||||
private final static int UNWIND_OP_INFO_FIELD_LENGTH = 0x04;
|
||||
|
||||
byte version;
|
||||
byte flags;
|
||||
int sizeOfProlog;
|
||||
int countOfUnwindCodes;
|
||||
byte frameRegister;
|
||||
byte frameOffset;
|
||||
UNWIND_CODE[] unwindCodes;
|
||||
int exceptionHandlerFunction;
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY unwindHandlerChainInfo;
|
||||
|
||||
long startOffset;
|
||||
|
||||
public UNWIND_INFO(long offset) {
|
||||
startOffset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
|
||||
StructureDataType struct = new StructureDataType(NAME + "_" + startOffset, 0);
|
||||
struct.setPackingEnabled(true);
|
||||
try {
|
||||
struct.addBitField(BYTE, UNWIND_VERSION_FIELD_LENGTH, "Version", null);
|
||||
struct.addBitField(defineFlagsEnum(), UNWIND_FLAGS_FIELD_LENGTH, "Flags", null);
|
||||
struct.add(BYTE, "SizeOfProlog", null);
|
||||
struct.add(BYTE, "CountOfUnwindCodes", null);
|
||||
struct.addBitField(BYTE, UNWIND_FRAME_REGISTER_LENGTH, "FrameRegister", null);
|
||||
struct.addBitField(BYTE, UNWIND_FRAME_OFFSET_LENGTH, "FrameOffset", null);
|
||||
|
||||
if (countOfUnwindCodes > 0) {
|
||||
ArrayDataType unwindInfoArray =
|
||||
new ArrayDataType(defineUnwindCodeStructure(), countOfUnwindCodes, -1);
|
||||
struct.add(unwindInfoArray, "UnwindCodes", null);
|
||||
}
|
||||
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new AssertException(e); // should never happen with byte bit-fields
|
||||
}
|
||||
|
||||
if (hasExceptionHandler() || hasUnwindHandler()) {
|
||||
struct.add(IBO32, "ExceptionHandler", null);
|
||||
if (hasUnwindHandler()) {
|
||||
struct.setFlexibleArrayComponent(UnsignedLongDataType.dataType, "ExceptionData",
|
||||
null);
|
||||
}
|
||||
}
|
||||
else if (hasChainedUnwindInfo()) {
|
||||
struct.add(IBO32, "FunctionStartAddress", null);
|
||||
struct.add(IBO32, "FunctionEndAddress", null);
|
||||
struct.add(IBO32, "FunctionUnwindInfoAddress", null);
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
public boolean hasExceptionHandler() {
|
||||
return (flags & UNW_FLAG_EHANDLER) == UNW_FLAG_EHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasUnwindHandler() {
|
||||
return (flags & UNW_FLAG_UHANDLER) == UNW_FLAG_UHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasChainedUnwindInfo() {
|
||||
return (flags & UNW_FLAG_CHAININFO) == UNW_FLAG_CHAININFO;
|
||||
}
|
||||
|
||||
private EnumDataType defineFlagsEnum() {
|
||||
EnumDataType flagsField = new EnumDataType("UNW_FLAGS", 1);
|
||||
flagsField.add("UNW_FLAG_NHANDLER", UNW_FLAG_NHANDLER);
|
||||
flagsField.add("UNW_FLAG_EHANDLER", UNW_FLAG_EHANDLER);
|
||||
flagsField.add("UNW_FLAG_UHANDLER", UNW_FLAG_UHANDLER);
|
||||
flagsField.add("UNW_FLAG_CHAININFO", UNW_FLAG_CHAININFO);
|
||||
return flagsField;
|
||||
}
|
||||
|
||||
private Structure defineUnwindCodeStructure() {
|
||||
StructureDataType unwindCode = new StructureDataType("UnwindCode", 0);
|
||||
unwindCode.setPackingEnabled(true);
|
||||
try {
|
||||
unwindCode.add(BYTE, "OffsetInProlog", null);
|
||||
unwindCode.addBitField(defineUnwindOpCodeField(), UNWIND_OP_FIELD_LENGTH,
|
||||
"UnwindOpCode", null);
|
||||
unwindCode.addBitField(defineUnwindCodeRegisterField(), UNWIND_OP_INFO_FIELD_LENGTH,
|
||||
"OpInfo", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new AssertException(e); // should never happen with byte bit-fields
|
||||
}
|
||||
return unwindCode;
|
||||
}
|
||||
|
||||
private EnumDataType defineUnwindOpCodeField() {
|
||||
EnumDataType unwindOpCodeField = new EnumDataType("UNWIND_CODE_OPCODE", 1);
|
||||
unwindOpCodeField.add("UWOP_PUSH_NONVOL", UNWIND_CODE_OPCODE.UWOP_PUSH_NONVOL.id);
|
||||
unwindOpCodeField.add("UWOP_ALLOC_LARGE", UNWIND_CODE_OPCODE.UWOP_ALLOC_LARGE.id);
|
||||
unwindOpCodeField.add("UWOP_ALLOC_SMALL", UNWIND_CODE_OPCODE.UWOP_ALLOC_SMALL.id);
|
||||
unwindOpCodeField.add("UWOP_SET_FPREG", UNWIND_CODE_OPCODE.UWOP_SET_FPREG.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_NONVOL", UNWIND_CODE_OPCODE.UWOP_SAVE_NONVOL.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_NONVOL_FAR",
|
||||
UNWIND_CODE_OPCODE.UWOP_SAVE_NONVOL_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM_FAR", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM128", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM128.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM128_FAR",
|
||||
UNWIND_CODE_OPCODE.UWOP_SAVE_XMM128_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_PUSH_MACHFRAME", UNWIND_CODE_OPCODE.UWOP_PUSH_MACHFRAME.id);
|
||||
|
||||
return unwindOpCodeField;
|
||||
}
|
||||
|
||||
private EnumDataType defineUnwindCodeRegisterField() {
|
||||
EnumDataType unwindCodeRegisterField =
|
||||
new EnumDataType("UNWIND_CODE_OPINFO_REGISTER", 1);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RAX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RAX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RCX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RCX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RDX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RDX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RBX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RBX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RSP",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RSP.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RBP",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RBP.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RSI",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RSI.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RDI",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RDI.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R8",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R8.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R9",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R9.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R10",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R10.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R11",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R11.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R12",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R12.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R13",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R13.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R14",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R14.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R15",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R15.id);
|
||||
|
||||
return unwindCodeRegisterField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/* ###
|
||||
* 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 ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
// TODO: If public visibility is required improved member protection is needed
|
||||
class PEx64UnwindInfo implements StructConverter {
|
||||
|
||||
final static int UNW_FLAG_NHANDLER = 0x0;
|
||||
final static int UNW_FLAG_EHANDLER = 0x1;
|
||||
final static int UNW_FLAG_UHANDLER = 0x2;
|
||||
final static int UNW_FLAG_CHAININFO = 0x4;
|
||||
|
||||
private final static int UNWIND_INFO_VERSION_MASK = 0x07;
|
||||
private final static int UNWIND_INFO_FLAGS_MASK = 0x1F;
|
||||
private final static int UNWIND_INFO_FLAGS_SHIFT = 0x03;
|
||||
private final static int UNWIND_INFO_FRAME_REGISTER_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_FRAME_OFFSET_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_OPCODE_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_OPCODE_INFO_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_OPCODE_INFO_MASK = 0x0F;
|
||||
|
||||
byte version;
|
||||
byte flags;
|
||||
int sizeOfProlog;
|
||||
int countOfUnwindCodes;
|
||||
byte frameRegister;
|
||||
byte frameOffset;
|
||||
UNWIND_CODE[] unwindCodes;
|
||||
int exceptionHandlerFunction;
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY unwindHandlerChainInfo;
|
||||
|
||||
long startOffset;
|
||||
|
||||
public PEx64UnwindInfo(long offset) {
|
||||
startOffset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
return PEx64UnwindInfoDataType.INSTANCE;
|
||||
}
|
||||
|
||||
public boolean hasExceptionHandler() {
|
||||
return (flags & UNW_FLAG_EHANDLER) == UNW_FLAG_EHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasUnwindHandler() {
|
||||
return (flags & UNW_FLAG_UHANDLER) == UNW_FLAG_UHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasChainedUnwindInfo() {
|
||||
return (flags & UNW_FLAG_CHAININFO) == UNW_FLAG_CHAININFO;
|
||||
}
|
||||
|
||||
// FIXME: change name to conform to Java naming standards
|
||||
public static enum UNWIND_CODE_OPCODE {
|
||||
UWOP_PUSH_NONVOL(0x00),
|
||||
UWOP_ALLOC_LARGE(0x01),
|
||||
UWOP_ALLOC_SMALL(0x02),
|
||||
UWOP_SET_FPREG(0x03),
|
||||
UWOP_SAVE_NONVOL(0x04),
|
||||
UWOP_SAVE_NONVOL_FAR(0x05),
|
||||
UWOP_SAVE_XMM(0x06),
|
||||
UWOP_SAVE_XMM_FAR(0x07),
|
||||
UWOP_SAVE_XMM128(0x08),
|
||||
UWOP_SAVE_XMM128_FAR(0x09),
|
||||
UWOP_PUSH_MACHFRAME(0x0A);
|
||||
|
||||
public final int id;
|
||||
|
||||
UNWIND_CODE_OPCODE(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPCODE fromInt(int id) {
|
||||
UNWIND_CODE_OPCODE[] values = UNWIND_CODE_OPCODE.values();
|
||||
for (UNWIND_CODE_OPCODE value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: change name to conform to Java naming standards
|
||||
// public static enum UNWIND_INFO_REGISTER {
|
||||
// RAX(0x00),
|
||||
// RCX(0x01),
|
||||
// RDX(0x02),
|
||||
// RBX(0x03),
|
||||
// RSP(0x04),
|
||||
// RBP(0x05),
|
||||
// RSI(0x06),
|
||||
// RDI(0x07),
|
||||
// R8(0x08),
|
||||
// R9(0x09),
|
||||
// R10(0x0A),
|
||||
// R11(0x0B),
|
||||
// R12(0x0C),
|
||||
// R13(0x0D),
|
||||
// R14(0x0E),
|
||||
// R15(0x0F);
|
||||
//
|
||||
// public final int id;
|
||||
//
|
||||
// UNWIND_INFO_REGISTER(int value) {
|
||||
// id = value;
|
||||
// }
|
||||
//
|
||||
// public int id() {
|
||||
// return id;
|
||||
// }
|
||||
//
|
||||
// public static UNWIND_INFO_REGISTER fromInt(int id) {
|
||||
// UNWIND_INFO_REGISTER[] values = UNWIND_INFO_REGISTER.values();
|
||||
// for (UNWIND_INFO_REGISTER value : values) {
|
||||
// if (value.id == id) {
|
||||
// return value;
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// FIXME: change name to conform to Java naming standards
|
||||
// TODO: If public visibility is required improved member protection is needed
|
||||
static class UNWIND_CODE {
|
||||
byte offsetInProlog;
|
||||
UNWIND_CODE_OPCODE opCode;
|
||||
byte opInfo; // encoding varies based upon opCode
|
||||
}
|
||||
|
||||
static PEx64UnwindInfo readUnwindInfo(FactoryBundledWithBinaryReader reader,
|
||||
long offset, NTHeader ntHeader) throws IOException {
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
long pointer = ntHeader.rvaToPointer(offset);
|
||||
PEx64UnwindInfo unwindInfo = new PEx64UnwindInfo(pointer);
|
||||
|
||||
if (pointer < 0) {
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
reader.setPointerIndex(pointer);
|
||||
byte splitByte = reader.readNextByte();
|
||||
unwindInfo.version = (byte) (splitByte & UNWIND_INFO_VERSION_MASK);
|
||||
unwindInfo.flags = (byte) ((splitByte >> UNWIND_INFO_FLAGS_SHIFT) & UNWIND_INFO_FLAGS_MASK);
|
||||
|
||||
unwindInfo.sizeOfProlog = reader.readNextUnsignedByte();
|
||||
unwindInfo.countOfUnwindCodes = reader.readNextUnsignedByte();
|
||||
|
||||
splitByte = reader.readNextByte();
|
||||
unwindInfo.frameRegister = (byte) (splitByte & UNWIND_INFO_FRAME_REGISTER_MASK);
|
||||
unwindInfo.frameOffset = (byte) (splitByte >> UNWIND_INFO_FRAME_OFFSET_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes = new UNWIND_CODE[unwindInfo.countOfUnwindCodes];
|
||||
for (int i = 0; i < unwindInfo.countOfUnwindCodes; i++) {
|
||||
UNWIND_CODE code = new UNWIND_CODE();
|
||||
code.offsetInProlog = reader.readNextByte();
|
||||
|
||||
int opCodeData = reader.readNextUnsignedByte();
|
||||
code.opCode = UNWIND_CODE_OPCODE.fromInt((opCodeData & UNWIND_INFO_OPCODE_MASK));
|
||||
code.opInfo = (byte) ((opCodeData >> UNWIND_INFO_OPCODE_INFO_SHIFT) &
|
||||
UNWIND_INFO_OPCODE_INFO_MASK);
|
||||
// code.opInfoRegister =
|
||||
// UNWIND_CODE_OPINFO_REGISTER.fromInt(opCodeData >> UNWIND_INFO_OPCODE_INFO_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes[i] = code;
|
||||
}
|
||||
|
||||
// You can have an exception handler or you can have chained exception handling info only.
|
||||
if (unwindInfo.hasExceptionHandler() || unwindInfo.hasUnwindHandler()) {
|
||||
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();
|
||||
|
||||
// Follow the chain to the referenced UNWIND_INFO structure until we
|
||||
// get to the end
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfo = readUnwindInfo(reader,
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData, ntHeader);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
/* ###
|
||||
* 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 ghidra.app.util.bin.format.pe.PEx64UnwindInfo.UNWIND_CODE_OPCODE;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class PEx64UnwindInfoDataType extends DynamicDataType {
|
||||
|
||||
public static final PEx64UnwindInfoDataType INSTANCE = new PEx64UnwindInfoDataType();
|
||||
|
||||
private final static int UNWIND_VERSION_FIELD_LENGTH = 0x03;
|
||||
private final static int UNWIND_FLAGS_FIELD_LENGTH = 0x05;
|
||||
private final static int UNWIND_FRAME_REGISTER_LENGTH = 0x04;
|
||||
private final static int UNWIND_FRAME_OFFSET_LENGTH = 0x04;
|
||||
private final static int UNWIND_OP_FIELD_LENGTH = 0x04;
|
||||
private final static int UNWIND_OP_INFO_FIELD_LENGTH = 0x04;
|
||||
|
||||
private final static DataType BYTE = ByteDataType.dataType;
|
||||
private final static DataType IBO32 = new ImageBaseOffset32DataType();
|
||||
|
||||
public PEx64UnwindInfoDataType() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public PEx64UnwindInfoDataType(DataTypeManager dtm) {
|
||||
super("PEx64_UnwindInfo", dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType clone(DataTypeManager dtm) {
|
||||
if (dtm == getDataTypeManager()) {
|
||||
return this;
|
||||
}
|
||||
return new PEx64UnwindInfoDataType(dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Dynamic structure for PE Exception UNWIND_INFO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMnemonic(Settings settings) {
|
||||
return "UNWIND_INFO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLabelPrefix() {
|
||||
return "UNWIND_INFO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(MemBuffer buf, Settings settings, int length) {
|
||||
return null; // TODO: Should we return filled-out structure? Caching?
|
||||
}
|
||||
|
||||
private Structure getStructure(MemBuffer buf) {
|
||||
|
||||
StructureDataType struct;
|
||||
try {
|
||||
byte flags = (byte) (buf.getByte(0) >> UNWIND_VERSION_FIELD_LENGTH);
|
||||
|
||||
struct = new StructureDataType("UNWIND_INFO", 0, dataMgr);
|
||||
struct.setPackingEnabled(true);
|
||||
try {
|
||||
struct.addBitField(BYTE, UNWIND_VERSION_FIELD_LENGTH, "Version", null);
|
||||
struct.addBitField(defineUnwindInfoFlags(), UNWIND_FLAGS_FIELD_LENGTH, "Flags",
|
||||
null);
|
||||
struct.add(BYTE, "SizeOfProlog", null);
|
||||
struct.add(BYTE, "CountOfUnwindCodes", null);
|
||||
struct.addBitField(BYTE, UNWIND_FRAME_REGISTER_LENGTH, "FrameRegister", null);
|
||||
struct.addBitField(BYTE, UNWIND_FRAME_OFFSET_LENGTH, "FrameOffset", null);
|
||||
|
||||
int countOfUnwindCodes = buf.getByte(2);
|
||||
if (countOfUnwindCodes > 0) {
|
||||
ArrayDataType unwindInfoArray =
|
||||
new ArrayDataType(defineUnwindCodeStructure(), countOfUnwindCodes, -1);
|
||||
struct.add(unwindInfoArray, "UnwindCodes", null);
|
||||
}
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new AssertException(e); // should never happen with byte bit-fields
|
||||
}
|
||||
|
||||
if (hasExceptionHandler(flags) || hasUnwindHandler(flags)) {
|
||||
struct.add(IBO32, "ExceptionHandler", null);
|
||||
if (hasUnwindHandler(flags)) {
|
||||
// NOTE: Dynamic structure does not reflect flex-array
|
||||
struct.setFlexibleArrayComponent(UnsignedLongDataType.dataType, "ExceptionData",
|
||||
null);
|
||||
}
|
||||
}
|
||||
else if (hasChainedUnwindInfo(flags)) {
|
||||
struct.add(IBO32, "FunctionStartAddress", null);
|
||||
struct.add(IBO32, "FunctionEndAddress", null);
|
||||
struct.add(IBO32, "FunctionUnwindInfoAddress", null);
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataTypeComponent[] getAllComponents(MemBuffer buf) {
|
||||
Structure struct = getStructure(buf);
|
||||
if (struct == null) {
|
||||
return null;
|
||||
}
|
||||
DataTypeComponent[] components = struct.getComponents();
|
||||
if (struct.hasFlexibleArrayComponent()) {
|
||||
DataTypeComponent[] newArray = new DataTypeComponent[components.length + 1];
|
||||
System.arraycopy(components, 0, newArray, 0, components.length);
|
||||
newArray[components.length] = struct.getFlexibleArrayComponent();
|
||||
components = newArray;
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
private boolean hasExceptionHandler(int flags) {
|
||||
return (flags & PEx64UnwindInfo.UNW_FLAG_EHANDLER) == PEx64UnwindInfo.UNW_FLAG_EHANDLER;
|
||||
}
|
||||
|
||||
private boolean hasUnwindHandler(int flags) {
|
||||
return (flags & PEx64UnwindInfo.UNW_FLAG_UHANDLER) == PEx64UnwindInfo.UNW_FLAG_UHANDLER;
|
||||
}
|
||||
|
||||
private boolean hasChainedUnwindInfo(int flags) {
|
||||
return (flags & PEx64UnwindInfo.UNW_FLAG_CHAININFO) == PEx64UnwindInfo.UNW_FLAG_CHAININFO;
|
||||
}
|
||||
|
||||
private Structure defineUnwindCodeStructure() {
|
||||
StructureDataType unwindCode = new StructureDataType("UnwindCode", 0);
|
||||
unwindCode.setPackingEnabled(true);
|
||||
try {
|
||||
unwindCode.add(BYTE, "OffsetInProlog", null);
|
||||
unwindCode.addBitField(defineUnwindOpCodeEnum(), UNWIND_OP_FIELD_LENGTH, "UnwindOpCode",
|
||||
null);
|
||||
// UnwindOpInfo encoding varies with UnwindOpCode
|
||||
unwindCode.addBitField(BYTE, UNWIND_OP_INFO_FIELD_LENGTH, "UnwindOpInfo", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
throw new AssertException(e); // should never happen with byte bit-fields
|
||||
}
|
||||
return unwindCode;
|
||||
}
|
||||
|
||||
private static EnumDataType unwindInfoFlagsEnum;
|
||||
|
||||
private EnumDataType defineUnwindInfoFlags() {
|
||||
if (unwindInfoFlagsEnum == null) {
|
||||
unwindInfoFlagsEnum = new EnumDataType("UNW_FLAGS", 1);
|
||||
unwindInfoFlagsEnum.add("UNW_FLAG_NHANDLER", PEx64UnwindInfo.UNW_FLAG_NHANDLER);
|
||||
unwindInfoFlagsEnum.add("UNW_FLAG_EHANDLER", PEx64UnwindInfo.UNW_FLAG_EHANDLER);
|
||||
unwindInfoFlagsEnum.add("UNW_FLAG_UHANDLER", PEx64UnwindInfo.UNW_FLAG_UHANDLER);
|
||||
unwindInfoFlagsEnum.add("UNW_FLAG_CHAININFO", PEx64UnwindInfo.UNW_FLAG_CHAININFO);
|
||||
}
|
||||
return unwindInfoFlagsEnum;
|
||||
}
|
||||
|
||||
private static EnumDataType unwindCodeOpcodeEnum;
|
||||
|
||||
private static synchronized EnumDataType defineUnwindOpCodeEnum() {
|
||||
if (unwindCodeOpcodeEnum == null) {
|
||||
unwindCodeOpcodeEnum = new EnumDataType("UNWIND_CODE_OPCODE", 1);
|
||||
for (UNWIND_CODE_OPCODE value : UNWIND_CODE_OPCODE.values()) {
|
||||
unwindCodeOpcodeEnum.add(value.name(), value.id);
|
||||
}
|
||||
}
|
||||
return unwindCodeOpcodeEnum;
|
||||
}
|
||||
|
||||
// private static EnumDataType unwindRegisterEnum;
|
||||
//
|
||||
// private static EnumDataType defineUnwindRegisterEnum() {
|
||||
// if (unwindRegisterEnum == null) {
|
||||
// unwindRegisterEnum = new EnumDataType("UNWIND_CODE_OPINFO_REGISTER", 1);
|
||||
// for (UNWIND_INFO_REGISTER value : UNWIND_INFO_REGISTER.values()) {
|
||||
// unwindRegisterEnum.add(value.name(), value.id);
|
||||
// }
|
||||
// }
|
||||
// return unwindRegisterEnum;
|
||||
// }
|
||||
|
||||
}
|
|
@ -304,18 +304,7 @@ public class PeLoader extends AbstractPeDebugLoader {
|
|||
// 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) {
|
||||
if (entry.unwindInfoAddressOrData > 0) {
|
||||
try {
|
||||
dt = (StructureDataType) entry.unwindInfo.toDataType();
|
||||
start = program.getImageBase().add(entry.unwindInfoAddressOrData);
|
||||
|
||||
DataUtilities.createData(program, start, dt, dt.getLength(), true,
|
||||
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
catch (CodeUnitInsertionException | DuplicateNameException | IOException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
entry.createData(program);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue