From 7a949f6bb610af0693ea5d3f84d4501033897dfc Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Wed, 4 Aug 2021 10:29:11 -0400 Subject: [PATCH] GP-1179 modified PR to simplify function markup without needing an analyzer, moved markAsFunction to AbstractProgramLoader --- .../util/bin/format/pe/ControlFlowGuard.java | 170 +++++++++++++++--- .../bin/format/pe/LoadConfigDirectory.java | 18 ++ .../util/opinion/AbstractProgramLoader.java | 31 +++- .../ghidra/app/util/opinion/CoffLoader.java | 43 ++--- 4 files changed, 203 insertions(+), 59 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java index 8e9c62bea4..370f3e3fc9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java @@ -15,11 +15,15 @@ */ package ghidra.app.util.bin.format.pe; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.cmd.data.CreateArrayCmd; import ghidra.app.util.bin.format.pe.LoadConfigDirectory.GuardFlags; import ghidra.app.util.importer.MessageLog; +import ghidra.app.util.opinion.AbstractProgramLoader; import ghidra.program.model.address.*; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.ImageBaseOffset32DataType; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; @@ -37,6 +41,9 @@ import ghidra.util.exception.InvalidInputException; * Creator's update. */ public class ControlFlowGuard { + public static String GuardCFFunctionTableName = "GuardCFFunctionTable"; + public static String GuardCFAddressTakenIatTableName = "GuardCFAddressTakenIatTable"; + public static String GuardCfgTableEntryName = "GuardCfgTableEntry"; /** * Perform markup on the supported ControlFlowGuard and ReturnFlowGuard functions and @@ -56,28 +63,29 @@ public class ControlFlowGuard { SymbolTable symbolTable = program.getSymbolTable(); // ControlFlowGuard - markupCfgCheckFunction(lcd, is64bit, space, mem, symbolTable); - markupCfgDispatchFunction(lcd, is64bit, space, mem, symbolTable); + markupCfgCheckFunction(lcd, program, is64bit, space, mem, symbolTable); + markupCfgDispatchFunction(lcd, program, is64bit, space, mem, symbolTable); markupCfgFunctionTable(lcd, program, log); - + markupCfgAddressTakenIatEntryTable(lcd, program, log); + // ReturnFlowGuard - markupRfgFailureRoutine(lcd, space, symbolTable); - markupRfgDefaultFailureRoutine(lcd, is64bit, space, mem, symbolTable); - markupRfgDefaultStackPointerFunction(lcd, is64bit, space, mem, symbolTable); + markupRfgFailureRoutine(lcd, program, space, symbolTable); + markupRfgDefaultFailureRoutine(lcd, program, is64bit, space, mem, symbolTable); + markupRfgDefaultStackPointerFunction(lcd, program, is64bit, space, mem, symbolTable); } /** * Performs markup on the ControlFlowGuard check function, if it exists. * * @param lcd The PE LoadConfigDirectory. + * @param program The program. * @param is64bit True if the PE is 64-bit; false if it's 32-bit. * @param space The program's address space. * @param mem The program's memory. * @param symbolTable The program's symbol table. */ - private static void markupCfgCheckFunction(LoadConfigDirectory lcd, boolean is64bit, - AddressSpace space, - Memory mem, SymbolTable symbolTable) { + private static void markupCfgCheckFunction(LoadConfigDirectory lcd, Program program, + boolean is64bit, AddressSpace space, Memory mem, SymbolTable symbolTable) { if (lcd.getCfgCheckFunctionPointer() == 0) { return; @@ -88,6 +96,8 @@ public class ControlFlowGuard { Address functionAddr = space.getAddress( is64bit ? mem.getLong(functionPointerAddr) : mem.getInt(functionPointerAddr)); symbolTable.createLabel(functionAddr, "_guard_check_icall", SourceType.IMPORTED); + + AbstractProgramLoader.markAsFunction(program, null, functionAddr); } catch (MemoryAccessException | AddressOutOfBoundsException | InvalidInputException e) { Msg.warn(ControlFlowGuard.class, "Unable to label ControlFlowGuard check function.", e); @@ -98,13 +108,14 @@ public class ControlFlowGuard { * Performs markup on the ControlFlowGuard dispatch function, if it exists. * * @param lcd The PE LoadConfigDirectory. + * @param program The program. * @param is64bit True if the PE is 64-bit; false if it's 32-bit. * @param space The program's address space. * @param mem The program's memory. * @param symbolTable The program's symbol table. */ - private static void markupCfgDispatchFunction(LoadConfigDirectory lcd, boolean is64bit, - AddressSpace space, Memory mem, SymbolTable symbolTable) { + private static void markupCfgDispatchFunction(LoadConfigDirectory lcd, Program program, + boolean is64bit, AddressSpace space, Memory mem, SymbolTable symbolTable) { if (lcd.getCfgDispatchFunctionPointer() == 0) { return; @@ -115,6 +126,8 @@ public class ControlFlowGuard { Address functionAddr = space.getAddress( is64bit ? mem.getLong(functionPointerAddr) : mem.getInt(functionPointerAddr)); symbolTable.createLabel(functionAddr, "_guard_dispatch_icall", SourceType.IMPORTED); + + AbstractProgramLoader.markAsFunction(program, null, functionAddr); } catch (MemoryAccessException | AddressOutOfBoundsException | InvalidInputException e) { Msg.warn(ControlFlowGuard.class, "Unable to label ControlFlowGuard dispatch function.", @@ -127,6 +140,7 @@ public class ControlFlowGuard { * * @param lcd The PE LoadConfigDirectory. * @param program The program. + * @param program The program. * @param log The log. */ private static void markupCfgFunctionTable(LoadConfigDirectory lcd, Program program, @@ -142,22 +156,110 @@ public class ControlFlowGuard { return; } + Address tableAddr = + program.getAddressFactory().getDefaultAddressSpace().getAddress(tablePointer); + + // Label the start of the table + try { + program.getSymbolTable() + .createLabel(tableAddr, GuardCFFunctionTableName, SourceType.IMPORTED); + } + catch (InvalidInputException e) { + Msg.warn(ControlFlowGuard.class, "Unable to label ControlFlowGuard function table.", e); + } + + // Each table entry is an RVA (32-bit image base offset), followed by 'n' extra bytes + GuardFlags guardFlags = lcd.getCfgGuardFlags(); + int n = (guardFlags.getFlags() & + IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT; + + // Pre-define base data types used to define table entry data type + DataType ibo32 = new ImageBaseOffset32DataType(); + DataType byteType = ByteDataType.dataType; + + CategoryPath categoryPath = new CategoryPath(CategoryPath.ROOT, "CFG"); + StructureDataType GuardCfgTableEntryType = (StructureDataType) program.getDataTypeManager() + .getDataType(categoryPath, GuardCfgTableEntryName); + + if (GuardCfgTableEntryType == null) { + GuardCfgTableEntryType = new StructureDataType(categoryPath, GuardCfgTableEntryName, 0); + GuardCfgTableEntryType.setPackingEnabled(false); + GuardCfgTableEntryType.add(ibo32, "Offset", ""); + if (n > 0) { + ArrayDataType padType = + new ArrayDataType(byteType, n / byteType.getLength(), byteType.getLength()); + GuardCfgTableEntryType.add(padType, "Pad", ""); + } + } + + CreateArrayCmd cmd = new CreateArrayCmd(tableAddr, (int) functionCount, + GuardCfgTableEntryType, GuardCfgTableEntryType.getLength()); + cmd.applyTo(program); + + Data tableData = program.getListing().getDataAt(tableAddr); + createCfgFunctions(program, tableData, log); + } + + private static void createCfgFunctions(Program program, Data tableData, MessageLog log) { + if (tableData == null) { + Msg.warn(ControlFlowGuard.class, "Couldn't find Control Flow Guard tables."); + return; + } + + if (!tableData.isArray() || (tableData.getNumComponents() < 1)) { + Msg.warn(ControlFlowGuard.class, "Control Flow Guard table seems to be empty."); + return; + } + + for (Address target : getFunctionAddressesFromTable(program, tableData)) { + AbstractProgramLoader.markAsFunction(program, null, target); + } + } + + private static List
getFunctionAddressesFromTable(Program program, Data table) { + List
list = new ArrayList
(); + + // use the array and ibo data in structure to get a list of functions + for (int i = 0; i < table.getNumComponents(); i++) { + Data entry = table.getComponent(i); + Data iboData = entry.getComponent(0); + Object value = iboData.getValue(); + if (value instanceof Address) { + list.add((Address) value); + } + } + return list; + } + + /** + * Performs markup on the ControlFlowGuard address taken IAT table, if it exists. + * + * @param lcd The PE LoadConfigDirectory. + * @param program The program. + * @param log The log. + */ + private static void markupCfgAddressTakenIatEntryTable(LoadConfigDirectory lcd, Program program, + MessageLog log) { + + long tablePointer = lcd.getGuardAddressIatTableTablePointer(); + long functionCount = lcd.getGuardAddressIatTableCount(); + + if (tablePointer == 0 || functionCount <= 0) { + return; + } + try { Address tableAddr = program.getAddressFactory().getDefaultAddressSpace().getAddress(tablePointer); // Label the start of the table - program.getSymbolTable().createLabel(tableAddr, "GuardCFFunctionTable", - SourceType.IMPORTED); - - // Each table entry is an RVA (32-bit image base offset), followed by 'n' extra bytes - GuardFlags guardFlags = lcd.getCfgGuardFlags(); - int n = (guardFlags.getFlags() & - IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT; + program.getSymbolTable() + .createLabel(tableAddr, GuardCFAddressTakenIatTableName, SourceType.IMPORTED); + // Each table entry is an RVA (32-bit image base offset) DataType ibo32 = new ImageBaseOffset32DataType(); for (long i = 0; i < functionCount; i++) { - Data d = PeUtils.createData(program, tableAddr.add(i * (ibo32.getLength() + n)), - ibo32, log); + Data d = + PeUtils.createData(program, tableAddr.add(i * ibo32.getLength()), ibo32, log); if (d == null) { // If we failed to create data on a table entry, just assume the rest will fail break; @@ -165,7 +267,7 @@ public class ControlFlowGuard { } } catch (AddressOutOfBoundsException | InvalidInputException e) { - Msg.warn(ControlFlowGuard.class, "Unable to label ControlFlowGuard function table.", e); + Msg.warn(ControlFlowGuard.class, "Unable to label ControlFlowGuard IAT table.", e); } } @@ -173,11 +275,12 @@ public class ControlFlowGuard { * Performs markup on the ReturnFlowGuard failure routine, if it exists. * * @param lcd The PE LoadConfigDirectory. + * @param program The program * @param space The program's address space. * @param symbolTable The program's symbol table. */ - private static void markupRfgFailureRoutine(LoadConfigDirectory lcd, AddressSpace space, - SymbolTable symbolTable) { + private static void markupRfgFailureRoutine(LoadConfigDirectory lcd, Program program, + AddressSpace space, SymbolTable symbolTable) { if (lcd.getRfgFailureRoutine() == 0) { return; @@ -186,6 +289,8 @@ public class ControlFlowGuard { try { Address routineAddr = space.getAddress(lcd.getRfgFailureRoutine()); symbolTable.createLabel(routineAddr, "_guard_ss_verify_failure", SourceType.IMPORTED); + + AbstractProgramLoader.markAsFunction(program, null, routineAddr); } catch (AddressOutOfBoundsException | InvalidInputException e) { Msg.warn(ControlFlowGuard.class, "Unable to label ReturnFlowGuard failure routine.", e); @@ -196,13 +301,14 @@ public class ControlFlowGuard { * Performs markup on the ReturnFlowGuard "default" failure routine function, if it exists. * * @param lcd The PE LoadConfigDirectory. + * @param program The program * @param is64bit True if the PE is 64-bit; false if it's 32-bit. * @param space The program's address space. * @param mem The program's memory. * @param symbolTable The program's symbol table. */ - private static void markupRfgDefaultFailureRoutine(LoadConfigDirectory lcd, boolean is64bit, - AddressSpace space, Memory mem, SymbolTable symbolTable) { + private static void markupRfgDefaultFailureRoutine(LoadConfigDirectory lcd, Program program, + boolean is64bit, AddressSpace space, Memory mem, SymbolTable symbolTable) { if (lcd.getRfgFailureRoutineFunctionPointer() == 0) { return; @@ -215,6 +321,9 @@ public class ControlFlowGuard { is64bit ? mem.getLong(functionPointerAddr) : mem.getInt(functionPointerAddr)); symbolTable.createLabel(functionAddr, "_guard_ss_verify_failure_default", SourceType.IMPORTED); + + AbstractProgramLoader.markAsFunction(program, null, functionAddr); + } catch (MemoryAccessException | AddressOutOfBoundsException | InvalidInputException e) { Msg.warn(ControlFlowGuard.class, @@ -226,13 +335,15 @@ public class ControlFlowGuard { * Performs markup on the ReturnFlowGuard verify stack pointer function, if it exists. * * @param lcd The PE LoadConfigDirectory. + * @param program The program * @param is64bit True if the PE is 64-bit; false if it's 32-bit. * @param space The program's address space. * @param mem The program's memory. * @param symbolTable The program's symbol table. */ private static void markupRfgDefaultStackPointerFunction(LoadConfigDirectory lcd, - boolean is64bit, AddressSpace space, Memory mem, SymbolTable symbolTable) { + Program program, boolean is64bit, AddressSpace space, Memory mem, + SymbolTable symbolTable) { if (lcd.getRfgVerifyStackPointerFunctionPointer() == 0) { return; @@ -245,6 +356,9 @@ public class ControlFlowGuard { is64bit ? mem.getLong(functionPointerAddr) : mem.getInt(functionPointerAddr)); symbolTable.createLabel(functionAddr, "_guard_ss_verify_sp_default", SourceType.IMPORTED); + + AbstractProgramLoader.markAsFunction(program, null, functionAddr); + } catch (MemoryAccessException | AddressOutOfBoundsException | InvalidInputException e) { Msg.warn(ControlFlowGuard.class, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDirectory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDirectory.java index 040ec04be7..2b36be05b3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDirectory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDirectory.java @@ -254,6 +254,24 @@ public class LoadConfigDirectory implements StructConverter { return guardCfFunctionCount; } + /** + * Gets the ControlFlowGuard IAT table pointer address. + * + * @return The ControlFlowGuard IAT table function pointer address. Could be 0 if ControlFlowGuard is not being used + */ + public long getGuardAddressIatTableTablePointer() { + return guardAddressTakenIatEntryTable; + } + + /** + * Gets the ControlFlowGuard IAT entries count. + * + * @return The ControlFlowGuard IAT entries count. Could be 0 if ControlFlowGuard is not being used + */ + public long getGuardAddressIatTableCount() { + return guardAddressTakenIatEntryCount; + } + /** * Gets the ReturnFlowGuard failure routine address. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java index f125963ea4..d3dcd239fc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractProgramLoader.java @@ -29,10 +29,10 @@ import ghidra.framework.model.DomainFolder; import ghidra.framework.model.DomainObject; import ghidra.framework.store.LockException; import ghidra.program.database.ProgramDB; +import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryConflictException; import ghidra.program.model.symbol.*; import ghidra.program.model.util.AddressLabelInfo; @@ -347,6 +347,33 @@ public abstract class AbstractProgramLoader implements Loader { } } + /** + * Mark this address as a function by creating a one byte function. The single byte body + * function is picked up by the function analyzer, disassembled, and the body fixed. + * Marking the function this way keeps disassembly and follow on analysis out of the loaders. + * + * @param program the program + * @param name name of function, null if name not known + * @param funcStart starting address of the function + */ + public static void markAsFunction(Program program, String name, Address funcStart) { + FunctionManager functionMgr = program.getFunctionManager(); + + if (functionMgr.getFunctionAt(funcStart) != null) { + return; + } + try { + functionMgr.createFunction(name, funcStart, new AddressSet(funcStart, funcStart), + SourceType.IMPORTED); + } + catch (InvalidInputException e) { + // ignore + } + catch (OverlappingFunctionException e) { + // ignore + } + } + /** * Gets the {@link Loader}'s language service. *

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java index 1157da24c1..98c01c0c43 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java @@ -28,7 +28,6 @@ import ghidra.app.util.bin.format.coff.relocation.CoffRelocationHandler; import ghidra.app.util.bin.format.coff.relocation.CoffRelocationHandlerFactory; import ghidra.app.util.importer.MessageLog; import ghidra.framework.model.DomainObject; -import ghidra.program.database.function.OverlappingFunctionException; import ghidra.program.database.mem.FileBytes; import ghidra.program.model.address.*; import ghidra.program.model.data.*; @@ -153,7 +152,8 @@ public class CoffLoader extends AbstractLibrarySupportLoader { } @Override - public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List