Unverified Commit 387405d4 by Melvin Klimke Committed by GitHub

Refactored PcodeExtractor into multiple files for better readability (#109)

parent 0c38cac5
package internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import bil.Variable;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressIterator;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.util.VarnodeContext;
import ghidra.util.task.TaskMonitor;
import term.*;
public final class HelperFunctions {
// private constructor for non-instantiable classes
public static VarnodeContext context;
public static ghidra.program.model.listing.Program ghidraProgram;
public static FunctionManager funcMan;
public static HashMap<String, Tid> functionEntryPoints = new HashMap<String, Tid>();
public static TaskMonitor monitor;
private HelperFunctions() {
throw new UnsupportedOperationException();
}
/**
*
* @param op: call pcode operation
* @return: Address of function pointer
*
* Parses the function pointer address out of an call instruction
*/
public static String parseCallTargetAddress(PcodeOp op) {
if(op.getInput(0).isAddress()) {
return op.getInput(0).getAddress().toString();
}
return null;
}
/**
* @param address: Virtual register address
* @return: Prefixed virtual register name
*
* Prefixes virtual register with $U.
*/
public static String renameVirtualRegister(String address) {
return "$U" + address.replaceFirst("^(unique:0+(?!$))", "");
}
/**
* @param node: Register Varnode
* @return: Register mnemonic
*
* Gets register mnemonic.
*/
public static String getRegisterMnemonic(Varnode node) {
return context.getRegister(node).getName();
}
/**
* @param constant: Constant value
* @return: Constant value without prefix
*
* Removes the consts prefix from the constant.
*/
public static String removeConstantPrefix(String constant) {
return constant.replaceFirst("^(const:)", "");
}
/**
*
* @param param: stack parameter
* @return: stack parameter without stack prefix
*
* Removes stack prefix from stack parameter. e.g. Stack[0x4] => 0x4
*/
public static String removeStackPrefix(String param) {
Matcher matcher = Pattern.compile("^Stack\\[([a-zA-Z0-9]*)\\]$").matcher(param);
if(matcher.find()) {
return matcher.group(1);
}
return "";
}
/**
*
* @param call: indirect call
* @param mnemonic: call mnemonic
* @return: direkt call or indirekt call
*
* Checks whether the indirect call could have been resolved and casts it into a direct call
*/
public static String resolveCallMenmonic(Call call, String mnemonic) {
if (mnemonic.equals("CALLIND") && call.getTarget().getIndirect() == null) {
return "CALL";
}
return mnemonic;
}
/**
*
* @param var: register variable
* @param node: varnode containing half register
* @param isArgument: check if register is an argument
* @return: full register variable
*
* Casts half registers to full registers
*/
public static Variable checkForParentRegister(Varnode node) {
Variable var = new Variable();
Register reg = context.getRegister(node);
Register parent = reg.getBaseRegister();
if(parent != null) {
Varnode parent_node = context.getRegisterVarnode(parent);
var.setName(parent.getName());
var.setSize(parent_node.getSize());
} else {
var.setName(reg.getName());
var.setSize(node.getSize());
}
var.setIsVirtual(false);
return var;
}
public static Boolean hasVoidReturn(Function func) {
return func.hasNoReturn() || func.getReturn().getDataType().getName().equals("void");
}
/**
*
* @param func: function to get arguments
* @return: if same symbol name in references
*
* Checks whether the same symbol name is in the references of the current symbol.
* If so, the current symbol is not internally called by other functions
*
* e.g. some_function() -> system() -> system() -> external_system()
*
* In this Example some_function() only calls the leftmost system() function
* and if we have the one in the middle as parameter of notInReferences(),
* the leftmost will be in the references. As a consequence, the middle function
* of the chain is not taken into the external symbol list as it is not called
* by some_function().
*
*/
public static Boolean notInReferences(Function func) {
for(Function calling : func.getCallingFunctions(monitor)) {
if(calling.getName().equals(func.getName())) {
return false;
}
}
return true;
}
/**
*
* @param block: block term
* @return: boolean whether block ends on definition
*
* Checks whether the current block term ends on a definition
*/
public static Boolean lastInstructionIsDef(Term<Blk> block) {
ArrayList<Term<Jmp>> jumps = block.getTerm().getJmps();
ArrayList<Term<Def>> defs = block.getTerm().getDefs();
if(defs.size() > 0 && jumps.size() == 0) {
return true;
}
return false;
}
/**
*
* @param symTab: symbol table
* @return: list of program entry points
*
* Creates a list of program entry points to add to the program term
*/
public static ArrayList<Tid> addEntryPoints(SymbolTable symTab) {
ArrayList<Tid> entryTids = new ArrayList<Tid>();
AddressIterator entryPoints = symTab.getExternalEntryPointIterator();
while (entryPoints.hasNext()) {
Address entry = entryPoints.next();
entryTids.add(new Tid(String.format("sub_%s", entry.toString()), entry.toString()));
}
return entryTids;
}
/**
*
* @return: CPU architecture as string.
*
* Uses Ghidra's language id to extract the CPU arch as "arch-bits" e.g. x86_64, x86_32 etc.
*/
public static String getCpuArchitecture() {
String langId = ghidraProgram.getCompilerSpec().getLanguage().getLanguageID().getIdAsString();
String[] arch = langId.split(":");
return arch[0] + "_" + arch[2];
}
}
package symbol;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import bil.Expression;
import bil.Variable;
import internal.HelperFunctions;
import internal.TermCreator;
import term.Arg;
import term.Tid;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Parameter;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SymbolTable;
public class ExternSymbolCreator {
public static HashMap<String, ExternSymbol> externalSymbolMap = new HashMap<String, ExternSymbol>();
// private constructor for non-instantiable classes
private ExternSymbolCreator() {
throw new UnsupportedOperationException();
}
/**
*
* @param symTab: symbol table
*
* Creates a map of external symbols to add to the program term
*/
public static void createExternalSymbolMap(SymbolTable symTab) {
HashMap<String, ArrayList<Function>> symbolMap = new HashMap<String, ArrayList<Function>>();
HelperFunctions.funcMan.getExternalFunctions().forEach(func -> {
ArrayList<Function> thunkFuncs = new ArrayList<Function>();
getThunkFunctions(func, thunkFuncs);
if(thunkFuncs.size() > 0) {
for(Function thunk : thunkFuncs) {
addToSymbolMap(symbolMap, thunk);
}
} else {
addToSymbolMap(symbolMap, func);
}
});
createExternalSymbols(symbolMap);
}
/**
*
* @param func: Function for which thunk functions are to be found
* @param thunkFuncs: List of found thunk functions
*
* Recursively find thunk functions in symbol chain
*/
public static void getThunkFunctions(Function func, ArrayList<Function> thunkFuncs) {
Address[] thunks = func.getFunctionThunkAddresses();
if(thunks != null) {
for(Address thunkAddr : thunks) {
Function thunkFunction = HelperFunctions.funcMan.getFunctionAt(thunkAddr);
thunkFuncs.add(thunkFunction);
getThunkFunctions(thunkFunction, thunkFuncs);
}
}
}
/**
*
* @param symbolMap: Maps symbol names to multiple function declarations
* @param func: Function to be added to symbol map
*
* Either adds a function to a given symbol name or creates a new entry in the symbol map
*/
public static void addToSymbolMap(HashMap<String, ArrayList<Function>> symbolMap, Function func) {
if(symbolMap.containsKey(func.getName())) {
symbolMap.get(func.getName()).add(func);
} else {
symbolMap.put(func.getName(), new ArrayList<Function>(){{add(func);}});
}
}
/**
* @param symbolMap: External symbol map
*
* Creates external symbol map with an unique TID, a calling convention and argument objects.
*/
public static void createExternalSymbols(HashMap<String, ArrayList<Function>> symbolMap) {
for(Map.Entry<String, ArrayList<Function>> functions : symbolMap.entrySet()) {
ExternSymbol extSym = new ExternSymbol();
extSym.setName(functions.getKey());
for(Function func : functions.getValue()) {
if(HelperFunctions.notInReferences(func)) {
extSym.setTid(new Tid(String.format("sub_%s", func.getEntryPoint().toString()), func.getEntryPoint().toString()));
extSym.setNoReturn(func.hasNoReturn());
extSym.setArguments(createArguments(func));
extSym.setCallingConvention(HelperFunctions.funcMan.getDefaultCallingConvention().toString());
}
if(!func.isExternal()) {
extSym.getAddresses().add(func.getEntryPoint().toString());
}
}
externalSymbolMap.put(functions.getKey(), extSym);
}
}
/**
*
* @param flow: flow from instruction to target
* @param targetAddress: address of target
* @param funcMan: function manager
*
* Adds function pointer address to external symbol and updates the TID.
*/
public static Tid updateExternalSymbolLocations(Address flow, String targetAddress, FunctionManager funcMan) {
Function external = funcMan.getFunctionAt(flow);
ExternSymbol symbol = externalSymbolMap.get(external.getName());
symbol.getAddresses().add(targetAddress);
if(symbol.getTid().getId().startsWith("sub_EXTERNAL")) {
Tid targetTid = new Tid(String.format("sub_%s", targetAddress), targetAddress);
HelperFunctions.functionEntryPoints.put(targetAddress, targetTid);
symbol.setTid(targetTid);
return targetTid;
}
return symbol.getTid();
}
/**
* @param param: Function parameter
* @return: new Arg
*
* Specifies if the argument is a stack variable or a register.
*/
public static Arg specifyArg(Parameter param) {
Arg arg = new Arg();
if (param.isStackVariable()) {
Variable stackVar = TermCreator.createVariable(param.getFirstStorageVarnode());
arg.setLocation(new Expression("LOAD", stackVar));
} else if (param.isRegisterVariable()) {
arg.setVar(HelperFunctions.checkForParentRegister(param.getFirstStorageVarnode()));
}
arg.setIntent("INPUT");
return arg;
}
/**
* @param func: function to get arguments
* @return: new Arg ArrayList
*
* Creates Arguments for the ExternSymbol object.
*/
public static ArrayList<Arg> createArguments(Function func) {
ArrayList<Arg> args = new ArrayList<Arg>();
Parameter[] params = func.getParameters();
for (Parameter param : params) {
args.add(specifyArg(param));
}
if (!HelperFunctions.hasVoidReturn(func)) {
for(Varnode node : func.getReturn().getVariableStorage().getVarnodes()) {
args.add(new Arg(HelperFunctions.checkForParentRegister(node), "OUTPUT"));
}
}
return args;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment