diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java index 2a9cfebdd2..7d1dd90681 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java @@ -22,6 +22,7 @@ import java.util.List; 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.app.util.bin.format.pe.debug.DebugCOFFSymbol; import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbolAux; import ghidra.program.model.data.*; @@ -45,7 +46,7 @@ import ghidra.util.exception.DuplicateNameException; * WORD Characteristics; // MANDATORY * } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; * - * + * */ public class FileHeader implements StructConverter { /** @@ -55,128 +56,131 @@ public class FileHeader implements StructConverter { /** * The size of the IMAGE_FILE_HEADER in bytes. */ - public final static int IMAGE_SIZEOF_FILE_HEADER = 20; + public final static int IMAGE_SIZEOF_FILE_HEADER = 20; /** * Relocation info stripped from file. */ - public final static int IMAGE_FILE_RELOCS_STRIPPED = 0x0001; - /** - * File is executable (no unresolved externel references). - */ - public final static int IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002; - /** - * Line nunbers stripped from file. - */ - public final static int IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004; - /** - * Local symbols stripped from file. - */ - public final static int IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008; - /** - * Agressively trim working set - */ - public final static int IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010; - /** - * App can handle >2gb addresses - */ - public final static int IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020; - /** - * Bytes of machine word are reversed. - */ - public final static int IMAGE_FILE_BYTES_REVERSED_LO = 0x0080; - /** - * 32 bit word machine. - */ - public final static int IMAGE_FILE_32BIT_MACHINE = 0x0100; - /** - * 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. - */ - public final static int IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400; - /** - * 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. - */ - public final static int IMAGE_FILE_SYSTEM = 0x1000; - /** - * File is a DLL. - */ - public final static int IMAGE_FILE_DLL = 0x2000; - /** - * 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. - */ - public final static int IMAGE_FILE_BYTES_REVERSED_HI = 0x8000; + public final static int IMAGE_FILE_RELOCS_STRIPPED = 0x0001; + /** + * File is executable (no unresolved externel references). + */ + public final static int IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002; + /** + * Line nunbers stripped from file. + */ + public final static int IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004; + /** + * Local symbols stripped from file. + */ + public final static int IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008; + /** + * Agressively trim working set + */ + public final static int IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010; + /** + * App can handle >2gb addresses + */ + public final static int IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020; + /** + * Bytes of machine word are reversed. + */ + public final static int IMAGE_FILE_BYTES_REVERSED_LO = 0x0080; + /** + * 32 bit word machine. + */ + public final static int IMAGE_FILE_32BIT_MACHINE = 0x0100; + /** + * 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. + */ + public final static int IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400; + /** + * 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. + */ + public final static int IMAGE_FILE_SYSTEM = 0x1000; + /** + * File is a DLL. + */ + public final static int IMAGE_FILE_DLL = 0x2000; + /** + * 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. + */ + public final static int IMAGE_FILE_BYTES_REVERSED_HI = 0x8000; - public final static String [] CHARACTERISTICS = { - "Relocation info stripped from file", - "File is executable (i.e. no unresolved externel references)", - "Line nunbers stripped from file", - "Local symbols stripped from file", - "Agressively trim working set", - "App can handle >2gb addresses", - "Bytes of machine word are reversed", - "32 bit word machine", - "Debugging info stripped from file in .DBG file", - "If Image is on removable media, copy and run from the swap file", - "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" - }; + /** + * 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. + */ + private final static int LORDPE_NUMBER_OF_SYMBOLS = 0x5D455064; - private short machine; - private short numberOfSections; - private int timeDateStamp; - private int pointerToSymbolTable; - private int numberOfSymbols; - private short sizeOfOptionalHeader; // delta between start of OptionalHeader and start of section table - private short characteristics; + public final static String[] CHARACTERISTICS = { "Relocation info stripped from file", + "File is executable (i.e. no unresolved externel references)", + "Line nunbers stripped from file", "Local symbols stripped from file", + "Agressively trim working set", "App can handle >2gb addresses", + "Bytes of machine word are reversed", "32 bit word machine", + "Debugging info stripped from file in .DBG file", + "If Image is on removable media, copy and run from the swap file", + "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" }; - private SectionHeader [] sectionHeaders; - private Listsymbols = new ArrayList<>(); + private short machine; + private short numberOfSections; + private int timeDateStamp; + private int pointerToSymbolTable; + private int numberOfSymbols; + private short sizeOfOptionalHeader; // delta between start of OptionalHeader and start of section table + private short characteristics; - private FactoryBundledWithBinaryReader reader; - private int startIndex; - private NTHeader ntHeader; + private SectionHeader[] sectionHeaders; + private List symbols = new ArrayList<>(); + private List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = new ArrayList<>(); - static FileHeader createFileHeader( - FactoryBundledWithBinaryReader reader, int startIndex, - NTHeader ntHeader) throws IOException { - FileHeader fileHeader = (FileHeader) reader.getFactory().create(FileHeader.class); - fileHeader.initFileHeader(reader, startIndex, ntHeader); - return fileHeader; - } + private FactoryBundledWithBinaryReader reader; + private int startIndex; + private NTHeader ntHeader; - /** - * DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD. - */ - public FileHeader() {} + static FileHeader createFileHeader(FactoryBundledWithBinaryReader reader, int startIndex, + NTHeader ntHeader) throws IOException { + FileHeader fileHeader = (FileHeader) reader.getFactory().create(FileHeader.class); + fileHeader.initFileHeader(reader, startIndex, ntHeader); + return fileHeader; + } - private void initFileHeader(FactoryBundledWithBinaryReader reader, int startIndex, NTHeader ntHeader) throws IOException { - this.reader = reader; - this.startIndex = startIndex; - this.ntHeader = ntHeader; + /** + * DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD. + */ + public FileHeader() { + } + + private void initFileHeader(FactoryBundledWithBinaryReader reader, int startIndex, + NTHeader ntHeader) throws IOException { + this.reader = reader; + this.startIndex = startIndex; + this.ntHeader = ntHeader; + + parse(); + } - parse(); - } - /** * Returns the architecture type of the computer. * @return the architecture type of the computer */ - public short getMachine() { + public short getMachine() { return machine; } @@ -184,131 +188,137 @@ public class FileHeader implements StructConverter { * Returns 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); - } - + public String getMachineName() { + return MachineName.getName(machine); + } + /** - * Returns the number of sections. + * Returns the number of sections. * Sections equate to Ghidra memory blocks. * @return the number of sections */ - public int getNumberOfSections() { - return numberOfSections; - } + public int getNumberOfSections() { + return numberOfSections; + } /** * Returns the array of section headers. * @return the array of section headers */ - public SectionHeader [] getSectionHeaders() { - if (sectionHeaders == null) { - return new SectionHeader[0]; - } - return sectionHeaders; - } + public SectionHeader[] getSectionHeaders() { + if (sectionHeaders == null) { + return new SectionHeader[0]; + } + return sectionHeaders; + } /** * Returns the array of symbols. * @return the array of symbols */ - public List getSymbols() { + public List getSymbols() { return symbols; } + public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getImageRuntimeFunctionEntries() { + return irfes; + } + /** * Returns 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; - } + 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; + } /** * Returns the section header at the specified position in the array. * @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) { + public SectionHeader getSectionHeader(int index) { if (index >= 0 && index < sectionHeaders.length) { - return sectionHeaders[index]; - } - return null; - } + return sectionHeaders[index]; + } + return null; + } /** * Returns the time stamp of the image. * @return the time stamp of the image */ - public int getTimeDateStamp() { - return timeDateStamp; - } + public int getTimeDateStamp() { + return timeDateStamp; + } /** * Returns the file offset of the COFF symbol table * @return the file offset of the COFF symbol table */ - public int getPointerToSymbolTable() { - return pointerToSymbolTable; - } + 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 */ - public int getNumberOfSymbols() { - return numberOfSymbols; - } + public int getNumberOfSymbols() { + return numberOfSymbols; + } /** * Returns the size of the optional header data * @return the size of the optional header, in bytes */ - public int getSizeOfOptionalHeader() { + public int getSizeOfOptionalHeader() { return sizeOfOptionalHeader; } /** - * Returns a set of bit flags indicating attributes of the file. + * Returns a set of bit flags indicating attributes of the file. * @return a set of bit flags indicating attributes */ - public int getCharacteristics() { - return characteristics; - } + public int getCharacteristics() { + return characteristics; + } /** * Returns the file pointer to the section headers. * @return the file pointer to the section headers */ - public int getPointerToSections() { - short sizeOptHdr = ntHeader.getFileHeader().sizeOfOptionalHeader; + public int getPointerToSections() { + short sizeOptHdr = ntHeader.getFileHeader().sizeOfOptionalHeader; int ptrToSections = startIndex + IMAGE_SIZEOF_FILE_HEADER + sizeOptHdr; - int testSize = ntHeader.getOptionalHeader().is64bit() - ? Constants.IMAGE_SIZEOF_NT_OPTIONAL64_HEADER - : Constants.IMAGE_SIZEOF_NT_OPTIONAL32_HEADER; - if (sizeOptHdr != testSize) { + int testSize = + ntHeader.getOptionalHeader().is64bit() ? Constants.IMAGE_SIZEOF_NT_OPTIONAL64_HEADER + : Constants.IMAGE_SIZEOF_NT_OPTIONAL32_HEADER; + if (sizeOptHdr != testSize) { Msg.warn(this, "Non-standard optional header size: " + sizeOptHdr + " bytes"); - } + } return ptrToSections; - } + } - void processSections(OptionalHeader optHeader) throws IOException { - long oldIndex = reader.getPointerIndex(); + void processSections(OptionalHeader optHeader) throws IOException { + long oldIndex = reader.getPointerIndex(); - int tmpIndex = getPointerToSections(); - if (numberOfSections < 0) { - Msg.error(this, "Number of sections = "+numberOfSections); - } else if (optHeader.getFileAlignment() == 0) { - Msg.error(this, "File alignment == 0: section processing skipped"); - } else { + int tmpIndex = getPointerToSections(); + if (numberOfSections < 0) { + Msg.error(this, "Number of sections = " + numberOfSections); + } + else if (optHeader.getFileAlignment() == 0) { + Msg.error(this, "File alignment == 0: section processing skipped"); + } + else { sectionHeaders = new SectionHeader[numberOfSections]; for (int i = 0; i < numberOfSections; ++i) { sectionHeaders[i] = SectionHeader.createSectionHeader(reader, tmpIndex); @@ -330,8 +340,8 @@ public class FileHeader implements StructConverter { optHeader.getSectionAlignment()); if (virtualAddress == alignedVirtualAddress) { if (sizeOfRawData > virtualSize) { - sectionHeaders[i].setVirtualSize( - Math.min(sizeOfRawData, alignedVirtualSize)); + sectionHeaders[i] + .setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize)); } } else { @@ -341,68 +351,101 @@ public class FileHeader implements StructConverter { } } - reader.setPointerIndex(oldIndex); - } + reader.setPointerIndex(oldIndex); + } - void processSymbols() throws IOException { - if (isLordPE()) { - return; - } + void processImageRuntimeFunctionEntries() throws IOException { + FileHeader fh = ntHeader.getFileHeader(); + SectionHeader[] sections = fh.getSectionHeaders(); - long oldIndex = reader.getPointerIndex(); + // 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; + } + } - int tmpIndex = getPointerToSymbolTable(); - if (!ntHeader.checkRVA(tmpIndex)) { - Msg.error(this, "Invalid file index "+Integer.toHexString(tmpIndex)); - return; - } + if (irfeHeader == null) { + return; + } - if ( numberOfSymbols < 0 || numberOfSymbols > reader.length()) { - Msg.error(this, "Invalid symbol count "+Integer.toHexString(numberOfSymbols)); - return; - } + long oldIndex = reader.getPointerIndex(); - int stringTableIndex = tmpIndex + DebugCOFFSymbol.IMAGE_SIZEOF_SYMBOL * numberOfSymbols; - - for (int i = 0; i < numberOfSymbols; ++i) { - if (!ntHeader.checkRVA(tmpIndex)) { - Msg.error(this, "Invalid file index "+Integer.toHexString(tmpIndex)); - break; - } + int start = irfeHeader.getPointerToRawData(); + reader.setPointerIndex(start); - DebugCOFFSymbol symbol = DebugCOFFSymbol.createDebugCOFFSymbol(reader, tmpIndex, stringTableIndex); + ImageRuntimeFunctionEntries entries = + ImageRuntimeFunctionEntries.createImageRuntimeFunctionEntries(reader, start, ntHeader); + irfes = entries.getRuntimeFunctionEntries(); - tmpIndex += DebugCOFFSymbol.IMAGE_SIZEOF_SYMBOL; + reader.setPointerIndex(oldIndex); + } - tmpIndex += (DebugCOFFSymbolAux.IMAGE_SIZEOF_AUX_SYMBOL * symbol.getNumberOfAuxSymbols()); + void processSymbols() throws IOException { + if (isLordPE()) { + return; + } - int numberOfAuxSymbols = symbol.getNumberOfAuxSymbols(); + long oldIndex = reader.getPointerIndex(); + + int tmpIndex = getPointerToSymbolTable(); + if (!ntHeader.checkRVA(tmpIndex)) { + Msg.error(this, "Invalid file index " + Integer.toHexString(tmpIndex)); + return; + } + + if (numberOfSymbols < 0 || numberOfSymbols > reader.length()) { + Msg.error(this, "Invalid symbol count " + Integer.toHexString(numberOfSymbols)); + return; + } + + int stringTableIndex = tmpIndex + DebugCOFFSymbol.IMAGE_SIZEOF_SYMBOL * numberOfSymbols; + + for (int i = 0; i < numberOfSymbols; ++i) { + if (!ntHeader.checkRVA(tmpIndex)) { + Msg.error(this, "Invalid file index " + Integer.toHexString(tmpIndex)); + break; + } + + DebugCOFFSymbol symbol = + DebugCOFFSymbol.createDebugCOFFSymbol(reader, tmpIndex, stringTableIndex); + + tmpIndex += DebugCOFFSymbol.IMAGE_SIZEOF_SYMBOL; + + tmpIndex += + (DebugCOFFSymbolAux.IMAGE_SIZEOF_AUX_SYMBOL * symbol.getNumberOfAuxSymbols()); + + int numberOfAuxSymbols = symbol.getNumberOfAuxSymbols(); i += numberOfAuxSymbols > 0 ? numberOfAuxSymbols : 0; - symbols.add( symbol ); - } + symbols.add(symbol); + } - reader.setPointerIndex(oldIndex); - } + reader.setPointerIndex(oldIndex); + } - public boolean isLordPE() { - if (getPointerToSymbolTable() == 0x726F4C5B && getNumberOfSymbols() == 0x5D455064) { - return true; - } - return false; - } + public boolean isLordPE() { + if (getPointerToSymbolTable() == LORDPE_SYMBOL_TABLE && + getNumberOfSymbols() == LORDPE_NUMBER_OF_SYMBOLS) { + return true; + } + return false; + } - private void parse() throws IOException { - reader.setPointerIndex(startIndex); + private void parse() throws IOException { + reader.setPointerIndex(startIndex); - machine = reader.readNextShort(); - numberOfSections = reader.readNextShort(); - timeDateStamp = reader.readNextInt (); - pointerToSymbolTable = reader.readNextInt (); - numberOfSymbols = reader.readNextInt (); - sizeOfOptionalHeader = reader.readNextShort(); - characteristics = reader.readNextShort(); - } + machine = reader.readNextShort(); + numberOfSections = reader.readNextShort(); + timeDateStamp = reader.readNextInt(); + pointerToSymbolTable = reader.readNextInt(); + numberOfSymbols = reader.readNextInt(); + sizeOfOptionalHeader = reader.readNextShort(); + characteristics = reader.readNextShort(); + } /** * @see ghidra.app.util.bin.StructConverter#toDataType() @@ -411,22 +454,22 @@ public class FileHeader implements StructConverter { 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); - struct.add(DWORD,4,"PointerToSymbolTable",null); - struct.add(DWORD,4,"NumberOfSymbols",null); - struct.add(WORD,2,"SizeOfOptionalHeader",null); - struct.add(WORD,2,"Characteristics",null); + struct.add(WORD, 2, "Machine", getMachineName()); + struct.add(WORD, 2, "NumberOfSections", null); + struct.add(DWORD, 4, "TimeDateStamp", null); + struct.add(DWORD, 4, "PointerToSymbolTable", null); + 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; } - private void setSectionHeaders(SectionHeader [] sectionHeaders) { + private void setSectionHeaders(SectionHeader[] sectionHeaders) { this.sectionHeaders = sectionHeaders; - numberOfSections = (short)sectionHeaders.length; + numberOfSections = (short) sectionHeaders.length; } void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { @@ -436,7 +479,7 @@ public class FileHeader implements StructConverter { raf.write(dc.getBytes(pointerToSymbolTable)); raf.write(dc.getBytes(numberOfSymbols)); raf.write(dc.getBytes(sizeOfOptionalHeader)); - raf.write(dc.getBytes(characteristics)); + raf.write(dc.getBytes(characteristics)); } /** @@ -449,61 +492,61 @@ public class FileHeader implements StructConverter { * @throws RuntimeException if the memory block is uninitialized */ public void addSection(MemoryBlock block, OptionalHeader optionalHeader) { - DataDirectory [] directories = optionalHeader.getDataDirectories(); - - DataDirectory [] dataDirectories = optionalHeader.getDataDirectories(); + DataDirectory[] directories = optionalHeader.getDataDirectories(); + DataDirectory[] dataDirectories = optionalHeader.getDataDirectories(); SecurityDataDirectory sdd = null; if (dataDirectories.length > OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY) { - sdd = (SecurityDataDirectory)dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY]; + sdd = + (SecurityDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY]; if (sdd != null && sdd.getSize() > 0) { - sdd.updatePointers( PortableExecutable.computeAlignment( (int)block.getSize( ), optionalHeader.getFileAlignment( ) ) ); + sdd.updatePointers(PortableExecutable.computeAlignment((int) block.getSize(), + optionalHeader.getFileAlignment())); } } - - int lastPos = computeAlignedNewPosition( optionalHeader, directories ); + int lastPos = computeAlignedNewPosition(optionalHeader, directories); SectionHeader newSection = new SectionHeader(block, optionalHeader, lastPos); - SectionHeader [] newSectionHeaders = new SectionHeader[sectionHeaders.length + 1]; - System.arraycopy(sectionHeaders, 0, newSectionHeaders, 0, sectionHeaders.length); + SectionHeader[] newSectionHeaders = new SectionHeader[sectionHeaders.length + 1]; + System.arraycopy(sectionHeaders, 0, newSectionHeaders, 0, sectionHeaders.length); newSectionHeaders[sectionHeaders.length] = newSection; setSectionHeaders(newSectionHeaders); int firstSectionStart = sectionHeaders[0].getPointerToRawData(); - int lastSectionEnd = sectionHeaders[sectionHeaders.length-1].getPointerToRawData() - +sectionHeaders[sectionHeaders.length-1].getSizeOfRawData(); + int lastSectionEnd = sectionHeaders[sectionHeaders.length - 1].getPointerToRawData() + + sectionHeaders[sectionHeaders.length - 1].getSizeOfRawData(); - for (int i = 0 ; i < directories.length ; i++) { - if (directories[i] == null || - directories[i].getSize() == 0 || + for (int i = 0; i < directories.length; i++) { + if (directories[i] == null || directories[i].getSize() == 0 || directories[i].isContainedInSection()) { continue; } if (directories[i].getVirtualAddress() < firstSectionStart) { if (i != OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) { - throw new RuntimeException("PE - Unexpected directory before sections: "+i); + throw new RuntimeException("PE - Unexpected directory before sections: " + i); } } if (directories[i].getVirtualAddress() > lastSectionEnd) { if (i != OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY) { - throw new RuntimeException("PE - Unexpected directory after sections: "+i); + throw new RuntimeException("PE - Unexpected directory after sections: " + i); } } } int offset = 0; - if (dataDirectories.length > OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) { - BoundImportDataDirectory bidd = (BoundImportDataDirectory)dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]; + BoundImportDataDirectory bidd = + (BoundImportDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]; if (bidd != null && bidd.getSize() > 0) { 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 = PortableExecutable.computeAlignment(endptr, + optionalHeader.getFileAlignment()); offset = alignedPtr - sectionHeaders[0].getPointerToRawData(); for (SectionHeader sectionHeader : sectionHeaders) { sectionHeader.updatePointers(offset); @@ -514,9 +557,9 @@ public class FileHeader implements StructConverter { } } - if (dataDirectories.length > OptionalHeader.IMAGE_DIRECTORY_ENTRY_DEBUG) { - DebugDataDirectory ddd = (DebugDataDirectory)dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_DEBUG]; + DebugDataDirectory ddd = + (DebugDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_DEBUG]; if (ddd != null && ddd.getSize() > 0) { if (ddd.getVirtualAddress() > newSection.getVirtualAddress()) { if (sdd != null && sdd.getSize() > 0) { @@ -530,12 +573,12 @@ public class FileHeader implements StructConverter { } if (block.isExecute()) { - optionalHeader.setSizeOfCode(optionalHeader.getSizeOfCode() + - newSection.getSizeOfRawData()); + optionalHeader + .setSizeOfCode(optionalHeader.getSizeOfCode() + newSection.getSizeOfRawData()); } else { - optionalHeader.setSizeOfInitializedData(optionalHeader.getSizeOfInitializedData() + - newSection.getSizeOfRawData()); + optionalHeader.setSizeOfInitializedData( + optionalHeader.getSizeOfInitializedData() + newSection.getSizeOfRawData()); } int soi = newSection.getVirtualAddress() + newSection.getSizeOfRawData(); @@ -543,7 +586,8 @@ public class FileHeader implements StructConverter { optionalHeader.setSizeOfImage(soi); } - private int computeAlignedNewPosition( OptionalHeader optionalHeader, DataDirectory [] directories ) { + private int computeAlignedNewPosition(OptionalHeader optionalHeader, + DataDirectory[] directories) { int lastPos = 0; for (SectionHeader sectionHeader : sectionHeaders) { if (sectionHeader.getPointerToRawData() + sectionHeader.getSizeOfRawData() > lastPos) { @@ -551,14 +595,13 @@ public class FileHeader implements StructConverter { } } for (DataDirectory directorie : directories) { - if (directorie == null || - directorie.getSize() == 0) { + if (directorie == null || directorie.getSize() == 0) { continue; } if (directorie.rvaToPointer() + directorie.getSize() > lastPos) { lastPos = directorie.rvaToPointer() + directorie.getSize(); } } - return PortableExecutable.computeAlignment( lastPos, optionalHeader.getFileAlignment( ) ); + return PortableExecutable.computeAlignment(lastPos, optionalHeader.getFileAlignment()); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java new file mode 100644 index 0000000000..7a372d9d19 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java @@ -0,0 +1,486 @@ +/* ### + * 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.StructConverter; +import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader; +import ghidra.program.model.data.*; +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; + */ +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<>(); + + static ImageRuntimeFunctionEntries createImageRuntimeFunctionEntries( + FactoryBundledWithBinaryReader reader, long index, NTHeader ntHeader) + throws IOException { + ImageRuntimeFunctionEntries imageRuntimeFunctionEntriesSection = + (ImageRuntimeFunctionEntries) reader.getFactory() + .create(ImageRuntimeFunctionEntries.class); + imageRuntimeFunctionEntriesSection.initImageRuntimeFunctionEntries(reader, index, ntHeader); + return imageRuntimeFunctionEntriesSection; + } + + /** + * DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD. + */ + public ImageRuntimeFunctionEntries() { + } + + private void initImageRuntimeFunctionEntries(FactoryBundledWithBinaryReader 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 = readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader); + + functionEntries.add(entry); + } + + 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.readNextByte(); + unwindInfo.countOfUnwindCodes = reader.readNextByte(); + + 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 and/or an unwind handler, or you + // can have chained exception handling info only. + if (unwindInfo.hasExceptionHandler() || unwindInfo.hasUnwindHandler()) { + if (unwindInfo.hasExceptionHandler()) { + unwindInfo.exceptionHandlerFunction = reader.readNextInt(); + } + if (unwindInfo.hasUnwindHandler()) { + unwindInfo.unwindHandlerFunction = 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; + } + + 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); + + 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; + } + } + 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_OP_FIELD_LENGTH = 0x04; + + byte version; + byte flags; + byte sizeOfProlog; + byte countOfUnwindCodes; + byte frameRegister; + byte frameOffset; + UNWIND_CODE[] unwindCodes; + int exceptionHandlerFunction; + int unwindHandlerFunction; + _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); + try { + StructureDataType vf = new StructureDataType("VersionFlags", 0); + vf.insertBitField(0, 1, 0, BYTE, UNWIND_VERSION_FIELD_LENGTH, "Version", null); + vf.insertBitField(0, 1, UNWIND_VERSION_FIELD_LENGTH, defineFlagsField(), + UNWIND_FLAGS_FIELD_LENGTH, "Flags", null); + + struct.add(vf, "Version + Flags", null); + } + catch (InvalidDataTypeException e) { + struct.add(BYTE, "Version + Flags", null); + } + + struct.add(BYTE, "SizeOfProlog", null); + struct.add(BYTE, "CountOfUnwindCodes", null); + + try { + StructureDataType fr = new StructureDataType("FrameRegisterAndOffset", 0); + fr.insertBitField(0, 1, 0, BYTE, UNWIND_FRAME_REGISTER_LENGTH, "FrameRegister", + null); + fr.insertBitField(0, 1, UNWIND_FRAME_REGISTER_LENGTH, BYTE, + UNWIND_FRAME_REGISTER_LENGTH, "FrameOffset", null); + struct.add(fr, "FrameRegister + FrameOffset", null); + } + catch (InvalidDataTypeException e) { + struct.add(BYTE, "FrameRegister + FrameOffset", null); + } + + for (int i = 0; i < countOfUnwindCodes; i++) { + StructureDataType unwindCode = new StructureDataType("UnwindCode", 0); + unwindCode.add(BYTE, "OffsetInProlog", null); + + StructureDataType unwindCodeInfo = new StructureDataType("UnwindCodeInfo", 0); + try { + if (unwindCodes[i].opCode != null) { + unwindCodeInfo.insertBitField(0, 1, 0, defineUnwindOpCodeField(), + UNWIND_OP_FIELD_LENGTH, "UnwindOpCode", null); + } + else { + unwindCodeInfo.insertBitField(0, 1, 0, BYTE, UNWIND_OP_FIELD_LENGTH, + "UnwindOpCode", null); + } + + if (unwindCodes[i].opInfoRegister != null) { + unwindCodeInfo.insertBitField(0, 1, UNWIND_OP_FIELD_LENGTH, + defineUnwindCodeRegisterField(), UNWIND_OP_FIELD_LENGTH, "OpInfo", + null); + } + else { + unwindCodeInfo.insertBitField(0, 1, UNWIND_OP_FIELD_LENGTH, BYTE, + UNWIND_OP_FIELD_LENGTH, "OpInfo", null); + } + } + catch (InvalidDataTypeException e) { + } + unwindCode.add(unwindCodeInfo, "UnwindCodeInfo", null); + + struct.add(unwindCode, "UnwindCode", null); + } + + if (hasExceptionHandler() || hasUnwindHandler()) { + if (hasExceptionHandler()) { + struct.add(IBO32, "ExceptionHandler", null); + } + if (hasUnwindHandler()) { + struct.add(IBO32, "UnwindHandler", 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 defineFlagsField() { + EnumDataType flagsField = new EnumDataType("Flags", 5); + 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 EnumDataType defineUnwindOpCodeField() { + EnumDataType unwindOpCodeField = new EnumDataType("UNWIND_CODE_OPCODE", 4); + 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", 4); + 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; + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java index 0912d75484..e88d1fe976 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java @@ -30,7 +30,7 @@ import ghidra.util.task.TaskMonitorAdapter; /** * A class to represent the IMAGE_NT_HEADERS32 and - * IMAGE_NT_HEADERS64 structs as defined in + * IMAGE_NT_HEADERS64 structs as defined in * winnt.h. *
  * typedef struct _IMAGE_NT_HEADERS {
@@ -39,8 +39,8 @@ import ghidra.util.task.TaskMonitorAdapter;
  *    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
  * };
  * 
- * - * + * + * */ public class NTHeader implements StructConverter, OffsetValidator { /** @@ -82,8 +82,8 @@ public class NTHeader implements StructConverter, OffsetValidator { public NTHeader() { } - private void initNTHeader(FactoryBundledWithBinaryReader reader, int index, SectionLayout layout, - boolean advancedProcess, boolean parseCliHeaders) + private void initNTHeader(FactoryBundledWithBinaryReader reader, int index, + SectionLayout layout, boolean advancedProcess, boolean parseCliHeaders) throws InvalidNTHeaderException, IOException { this.reader = reader; this.index = index; @@ -172,9 +172,9 @@ public class NTHeader implements StructConverter, OffsetValidator { //low alignment mode? // if (optionalHeader != null) { - if (optionalHeader.getFileAlignment() == optionalHeader.getSectionAlignment() - && optionalHeader.getSectionAlignment() < 800 - && optionalHeader.getFileAlignment() > 1) { + if (optionalHeader.getFileAlignment() == optionalHeader.getSectionAlignment() && + optionalHeader.getSectionAlignment() < 800 && + optionalHeader.getFileAlignment() > 1) { return rva; } } @@ -270,6 +270,7 @@ public class NTHeader implements StructConverter, OffsetValidator { fileHeader.processSections(optionalHeader); fileHeader.processSymbols(); + fileHeader.processImageRuntimeFunctionEntries(); if (advancedProcess) { optionalHeader.processDataDirectories(TaskMonitorAdapter.DUMMY_MONITOR); @@ -278,7 +279,7 @@ public class NTHeader implements StructConverter, OffsetValidator { void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException { - raf.seek( index ); + raf.seek(index); raf.write(dc.getBytes(signature)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java index f7f1141135..ad4317876c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java @@ -28,6 +28,7 @@ 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; @@ -148,6 +149,7 @@ public class PeLoader extends AbstractPeDebugLoader { processProperties(optionalHeader, program, monitor); processComments(program.getListing(), monitor); processSymbols(fileHeader, sectionToAddress, program, monitor, log); + processImageRuntimeFunctionEntries(fileHeader, program, monitor, log); processEntryPoints(ntHeader, program, monitor); String compiler = CompilerOpinion.getOpinion(pe, provider).toString(); @@ -248,24 +250,74 @@ public class PeLoader extends AbstractPeDebugLoader { setComment(CodeUnit.EOL_COMMENT, start, section.getName()); start = start.add(dt.getLength()); } - -// for (int i = 0; i < datadirs.length; ++i) { -// if (datadirs[i] == null || datadirs[i].getSize() == 0) { -// continue; -// } -// -// if (datadirs[i].hasParsedCorrectly()) { -// start = datadirs[i].getMarkupAddress(program, true); -// dt = datadirs[i].toDataType(); -// DataUtilities.createData(program, start, dt, true, DataUtilities.ClearDataMode.CHECK_FOR_SPACE); -// } -// } } catch (Exception e1) { Msg.error(this, "Error laying down header structures " + e1); } } + 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 == null) { + return; + } + + 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, start, irfeArray, irfeArray.getLength(), true, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + catch (CodeUnitInsertionException e) { + return; + } + + // 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) { + 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; + } + } + } + } + private void processSymbols(FileHeader fileHeader, Map sectionToAddress, Program program, TaskMonitor monitor, MessageLog log) { List symbols = fileHeader.getSymbols(); @@ -493,7 +545,7 @@ public class PeLoader extends AbstractPeDebugLoader { break; } - // Get address of current position in the import address table + // Get address of current position in the import address table Address iatAddr = iatBaseAddr.add(offset); Data iatData = listing.getDataAt(iatAddr); if (iatData == null || !(iatData.getValue() instanceof Address)) { @@ -506,8 +558,7 @@ public class PeLoader extends AbstractPeDebugLoader { importInfo.getName(), null, SourceType.IMPORTED, 0, RefType.DATA); } catch (DuplicateNameException | InvalidInputException e) { - log.appendMsg( - "Failed to create Delay Load external function at: " + iatAddr); + log.appendMsg("Failed to create Delay Load external function at: " + iatAddr); } // Create delay load proxy function