Unverified Commit 17022fdb by Melvin Klimke Committed by GitHub

Fix pointer to external function (#101)

parent a3a73e8d
...@@ -15,6 +15,7 @@ fn mock_extern_symbol(name: &str) -> ExternSymbol { ...@@ -15,6 +15,7 @@ fn mock_extern_symbol(name: &str) -> ExternSymbol {
let arg = Arg::Register(register("RDX")); let arg = Arg::Register(register("RDX"));
ExternSymbol { ExternSymbol {
tid: Tid::new("extern_".to_string() + name), tid: Tid::new("extern_".to_string() + name),
addresses: vec![],
name: name.into(), name: name.into(),
calling_convention: None, calling_convention: None,
parameters: vec![arg.clone()], parameters: vec![arg.clone()],
......
...@@ -276,6 +276,7 @@ fn clear_parameters_on_the_stack_on_extern_calls() { ...@@ -276,6 +276,7 @@ fn clear_parameters_on_the_stack_on_extern_calls() {
}; };
let extern_symbol = ExternSymbol { let extern_symbol = ExternSymbol {
tid: Tid::new("symbol"), tid: Tid::new("symbol"),
addresses: vec![],
name: "my_extern_symbol".into(), name: "my_extern_symbol".into(),
calling_convention: None, calling_convention: None,
parameters: vec![stack_param], parameters: vec![stack_param],
......
...@@ -217,6 +217,8 @@ pub enum Arg { ...@@ -217,6 +217,8 @@ pub enum Arg {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct ExternSymbol { pub struct ExternSymbol {
pub tid: Tid, pub tid: Tid,
/// Addresses of possibly multiple locations of the same extern symbol
pub addresses: Vec<String>,
/// The name of the extern symbol /// The name of the extern symbol
pub name: String, pub name: String,
/// The calling convention used for the extern symbol if known /// The calling convention used for the extern symbol if known
......
...@@ -294,7 +294,7 @@ impl From<Sub> for IrSub { ...@@ -294,7 +294,7 @@ impl From<Sub> for IrSub {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct ExternSymbol { pub struct ExternSymbol {
pub tid: Tid, pub tid: Tid,
pub address: String, pub addresses: Vec<String>,
pub name: String, pub name: String,
pub calling_convention: Option<String>, pub calling_convention: Option<String>,
pub arguments: Vec<Arg>, pub arguments: Vec<Arg>,
...@@ -337,6 +337,7 @@ impl From<ExternSymbol> for IrExternSymbol { ...@@ -337,6 +337,7 @@ impl From<ExternSymbol> for IrExternSymbol {
} }
IrExternSymbol { IrExternSymbol {
tid: symbol.tid, tid: symbol.tid,
addresses: symbol.addresses,
name: symbol.name, name: symbol.name,
calling_convention: symbol.calling_convention, calling_convention: symbol.calling_convention,
parameters, parameters,
...@@ -655,7 +656,9 @@ mod tests { ...@@ -655,7 +656,9 @@ mod tests {
"id": "sub_08048410", "id": "sub_08048410",
"address": "08048410" "address": "08048410"
}, },
"address": "08048410", "addresses": [
"08048410"
],
"name": "atoi", "name": "atoi",
"calling_convention": "__cdecl", "calling_convention": "__cdecl",
"arguments": [ "arguments": [
......
...@@ -76,6 +76,7 @@ impl From<ExternSymbol> for IrExternSymbol { ...@@ -76,6 +76,7 @@ impl From<ExternSymbol> for IrExternSymbol {
} }
IrExternSymbol { IrExternSymbol {
tid: symbol.tid, tid: symbol.tid,
addresses: vec![symbol.address],
name: symbol.name, name: symbol.name,
calling_convention: None, // We do not parse more than one calling convention from BAP at the moment. So we assume everything uses the standard one. calling_convention: None, // We do not parse more than one calling convention from BAP at the moment. So we assume everything uses the standard one.
parameters, parameters,
......
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
...@@ -56,7 +57,9 @@ public class PcodeExtractor extends GhidraScript { ...@@ -56,7 +57,9 @@ public class PcodeExtractor extends GhidraScript {
Term<Program> program = null; Term<Program> program = null;
FunctionManager funcMan; FunctionManager funcMan;
HashMap<String, Integer> functionEntryPoints; SymbolTable symTab;
HashMap<String, Tid> functionEntryPoints;
HashMap<String, ExternSymbol> externalSymbolMap;
ghidra.program.model.listing.Program ghidraProgram; ghidra.program.model.listing.Program ghidraProgram;
VarnodeContext context; VarnodeContext context;
String cpuArch; String cpuArch;
...@@ -79,11 +82,15 @@ public class PcodeExtractor extends GhidraScript { ...@@ -79,11 +82,15 @@ public class PcodeExtractor extends GhidraScript {
context = new VarnodeContext(ghidraProgram, ghidraProgram.getProgramContext(), ghidraProgram.getProgramContext()); context = new VarnodeContext(ghidraProgram, ghidraProgram.getProgramContext(), ghidraProgram.getProgramContext());
cpuArch = getCpuArchitecture(); cpuArch = getCpuArchitecture();
symTab = ghidraProgram.getSymbolTable();
externalSymbolMap = new HashMap<String, ExternSymbol>();
createExternalSymbolMap(symTab);
program = createProgramTerm(); program = createProgramTerm();
functionEntryPoints = new HashMap<String, Integer>(); functionEntryPoints = new HashMap<String, Tid>();
setFunctionEntryPoints(); setFunctionEntryPoints();
Project project = createProject(); Project project = createProject();
program = iterateFunctions(simpleBM, listing); program = iterateFunctions(simpleBM, listing);
program.getTerm().setExternSymbols(new ArrayList<ExternSymbol>(externalSymbolMap.values()));
String jsonPath = getScriptArgs()[0]; String jsonPath = getScriptArgs()[0];
Serializer ser = new Serializer(project, jsonPath); Serializer ser = new Serializer(project, jsonPath);
...@@ -98,15 +105,21 @@ public class PcodeExtractor extends GhidraScript { ...@@ -98,15 +105,21 @@ public class PcodeExtractor extends GhidraScript {
* This will later speed up the cast of indirect Calls. * This will later speed up the cast of indirect Calls.
*/ */
protected void setFunctionEntryPoints() { protected void setFunctionEntryPoints() {
// Add external symbols and internal function addresses to hash map // Add thunk addresses for external functions
int funcCounter = 0; for(ExternSymbol sym : externalSymbolMap.values()){
for(ExternSymbol sym : program.getTerm().getExternSymbols()){ for(String address : sym.getAddresses()) {
functionEntryPoints.put(sym.getAddress(), funcCounter); functionEntryPoints.put(address, sym.getTid());
funcCounter++;
} }
}
// Add internal function addresses
for(Function func : funcMan.getFunctionsNoStubs(true)) { for(Function func : funcMan.getFunctionsNoStubs(true)) {
functionEntryPoints.put(func.getEntryPoint().toString(), funcCounter); String address = func.getEntryPoint().toString();
funcCounter++; functionEntryPoints.put(address, new Tid(String.format("sub_%s", address), address));
}
// Add external addresses
for(Function ext : funcMan.getExternalFunctions()) {
String address = ext.getEntryPoint().toString();
functionEntryPoints.put(address, new Tid(String.format("sub_%s", address), address));
} }
} }
...@@ -625,8 +638,7 @@ public class PcodeExtractor extends GhidraScript { ...@@ -625,8 +638,7 @@ public class PcodeExtractor extends GhidraScript {
*/ */
protected Term<Program> createProgramTerm() { protected Term<Program> createProgramTerm() {
Tid progTid = new Tid(String.format("prog_%s", ghidraProgram.getMinAddress().toString()), ghidraProgram.getMinAddress().toString()); Tid progTid = new Tid(String.format("prog_%s", ghidraProgram.getMinAddress().toString()), ghidraProgram.getMinAddress().toString());
SymbolTable symTab = ghidraProgram.getSymbolTable(); return new Term<Program>(progTid, new Program(new ArrayList<Term<Sub>>(), addEntryPoints(symTab)));
return new Term<Program>(progTid, new Program(new ArrayList<Term<Sub>>(), addExternalSymbols(symTab), addEntryPoints(symTab)));
} }
...@@ -652,32 +664,65 @@ public class PcodeExtractor extends GhidraScript { ...@@ -652,32 +664,65 @@ public class PcodeExtractor extends GhidraScript {
/** /**
* *
* @param symTab: symbol table * @param symTab: symbol table
* @return: list of external symbols
* *
* Creates a list of external symbols to add to the program term * Creates a map of external symbols to add to the program term
*/ */
protected ArrayList<ExternSymbol> addExternalSymbols(SymbolTable symTab) { protected void createExternalSymbolMap(SymbolTable symTab) {
ArrayList<ExternSymbol> extSym = new ArrayList<ExternSymbol>(); HashMap<String, ArrayList<Function>> symbolMap = new HashMap<String, ArrayList<Function>>();
ArrayList<Symbol> externalSymbols = new ArrayList<Symbol>(); funcMan.getExternalFunctions().forEach(func -> {
ArrayList<Symbol> definedSymbols = new ArrayList<Symbol>(); ArrayList<Function> thunkFuncs = new ArrayList<Function>();
symTab.getExternalSymbols().forEachRemaining(externalSymbols::add); getThunkFunctions(func, thunkFuncs);
symTab.getDefinedSymbols().forEachRemaining(definedSymbols::add); if(thunkFuncs.size() > 0) {
for(Symbol def : definedSymbols) { for(Function thunk : thunkFuncs) {
for(Symbol ext : externalSymbols) { addToSymbolMap(symbolMap, thunk);
if(def.getName().equals(ext.getName()) && !def.getAddress().toString().startsWith("EXTERNAL:") && def.getSymbolType() == SymbolType.FUNCTION && notInReferences(def)) { }
extSym.add(createExternSymbol(def)); } else {
break; 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
*/
protected void getThunkFunctions(Function func, ArrayList<Function> thunkFuncs) {
Address[] thunks = func.getFunctionThunkAddresses();
if(thunks != null) {
for(Address thunkAddr : thunks) {
Function thunkFunction = getFunctionAt(thunkAddr);
thunkFuncs.add(funcMan.getFunctionAt(thunkAddr));
getThunkFunctions(thunkFunction, thunkFuncs);
} }
} }
} }
return extSym;
/**
*
* @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
*/
protected 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 sym: external symbol * @param func: function to get arguments
* @return: if same symbol name in references * @return: if same symbol name in references
* *
* Checks whether the same symbol name is in the references of the current symbol. * Checks whether the same symbol name is in the references of the current symbol.
...@@ -692,48 +737,40 @@ public class PcodeExtractor extends GhidraScript { ...@@ -692,48 +737,40 @@ public class PcodeExtractor extends GhidraScript {
* by some_function(). * by some_function().
* *
*/ */
protected Boolean notInReferences(Symbol sym) { protected Boolean notInReferences(Function func) {
for(Reference ref : sym.getReferences()) { for(Function calling : func.getCallingFunctions(getMonitor())) {
if(funcMan.getFunctionContaining(ref.getFromAddress()) != null) { if(calling.getName().equals(func.getName())) {
if(funcMan.getFunctionContaining(ref.getFromAddress()).getName().equals(sym.getName())) {
return false; return false;
} }
} }
}
return true; return true;
} }
/** /**
* @param symbol: External symbol * @param symbolMap: External symbol map
* @return: new ExternSymbol
* *
* Creates an external symbol with an unique TID, a calling convention and argument objects. * Creates external symbol map with an unique TID, a calling convention and argument objects.
*/ */
protected ExternSymbol createExternSymbol(Symbol symbol) { protected void createExternalSymbols(HashMap<String, ArrayList<Function>> symbolMap) {
Tid tid = new Tid(String.format("sub_%s", symbol.getAddress().toString()), symbol.getAddress().toString()); for(Map.Entry<String, ArrayList<Function>> functions : symbolMap.entrySet()) {
ArrayList<Arg> args = createArguments(symbol); ExternSymbol extSym = new ExternSymbol();
Boolean noReturn = funcMan.getFunctionAt(symbol.getAddress()).hasNoReturn(); extSym.setName(functions.getKey());
return new ExternSymbol(tid, symbol.getAddress().toString(), symbol.getName(), funcMan.getDefaultCallingConvention().getName(), args, noReturn); for(Function func : functions.getValue()) {
if(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(funcMan.getDefaultCallingConvention().toString());
}
if(!func.isExternal()) {
extSym.getAddresses().add(func.getEntryPoint().toString());
} }
/**
* @param def: Defined symbol
* @return: true if referencing function is thunk, else false
*
* Checks if current external symbol is referenced by a Thunk Function.
* If so, the Thunk Function is the internally called function.
*/
protected Boolean isThunkFunctionRef(Symbol def) {
Reference[] refs = def.getReferences();
if(refs.length == 0) {
return false;
} }
Address refAddr = def.getReferences()[0].getFromAddress(); externalSymbolMap.put(functions.getKey(), extSym);
return funcMan.getFunctionContaining(refAddr) != null && funcMan.getFunctionContaining(refAddr).isThunk(); }
} }
...@@ -743,14 +780,13 @@ public class PcodeExtractor extends GhidraScript { ...@@ -743,14 +780,13 @@ public class PcodeExtractor extends GhidraScript {
/** /**
* @param symbol: Symbol used to get corresponding function * @param func: function to get arguments
* @return: new Arg ArrayList * @return: new Arg ArrayList
* *
* Creates Arguments for the ExternSymbol object. * Creates Arguments for the ExternSymbol object.
*/ */
protected ArrayList<Arg> createArguments(Symbol symbol) { protected ArrayList<Arg> createArguments(Function func) {
ArrayList<Arg> args = new ArrayList<Arg>(); ArrayList<Arg> args = new ArrayList<Arg>();
Function func = funcMan.getFunctionAt(symbol.getAddress());
Parameter[] params = func.getParameters(); Parameter[] params = func.getParameters();
for (Parameter param : params) { for (Parameter param : params) {
args.add(specifyArg(param)); args.add(specifyArg(param));
...@@ -819,8 +855,7 @@ public class PcodeExtractor extends GhidraScript { ...@@ -819,8 +855,7 @@ public class PcodeExtractor extends GhidraScript {
* Creates a Sub Term with an unique TID consisting of the prefix sub and its entry address. * Creates a Sub Term with an unique TID consisting of the prefix sub and its entry address.
*/ */
protected Term<Sub> createSubTerm(Function func) { protected Term<Sub> createSubTerm(Function func) {
Tid subTid = new Tid(String.format("sub_%s", func.getEntryPoint().toString()), func.getEntryPoint().toString()); return new Term<Sub>(functionEntryPoints.get(func.getEntryPoint().toString()), new Sub(func.getName(), func.getBody()));
return new Term<Sub>(subTid, new Sub(func.getName(), func.getBody()));
} }
...@@ -1008,7 +1043,7 @@ public class PcodeExtractor extends GhidraScript { ...@@ -1008,7 +1043,7 @@ public class PcodeExtractor extends GhidraScript {
* Either returns an address to the memory if not resolved or an address to a symbol * Either returns an address to the memory if not resolved or an address to a symbol
*/ */
protected Label handleLabelsForIndirectCalls(PcodeOp pcodeOp) { protected Label handleLabelsForIndirectCalls(PcodeOp pcodeOp) {
Tid subTid = getTargetTid(pcodeOp.getInput(0)); Tid subTid = getTargetTid();
if (subTid != null) { if (subTid != null) {
return new Label(subTid); return new Label(subTid);
} }
...@@ -1023,13 +1058,62 @@ public class PcodeExtractor extends GhidraScript { ...@@ -1023,13 +1058,62 @@ public class PcodeExtractor extends GhidraScript {
* *
* Resolves the target id for an indirect jump * Resolves the target id for an indirect jump
*/ */
protected Tid getTargetTid(Varnode target) { protected Tid getTargetTid() {
Address[] flowDestinations = PcodeBlockData.instruction.getFlows(); Address[] flowDestinations = PcodeBlockData.instruction.getFlows();
if(flowDestinations.length == 1) { if(flowDestinations.length == 1) {
for(Address flow : flowDestinations) { Address flow = flowDestinations[0];
if(functionEntryPoints.containsKey(flow.toString())){ if(functionEntryPoints.containsKey(flow.toString())){
return new Tid(String.format("sub_%s", flow.toString()), flow.toString()); // In case a jump to an external address occured, check for fuction pointer
Function external = funcMan.getFunctionAt(flow);
if(flow.isExternalAddress()) {
return handleCallToFunctionPointer(flow, external);
} else {
return functionEntryPoints.get(flow.toString());
}
}
} }
return null;
}
/**
*
* @param flow: Flow address of indirect call
* @return: target tid of external symbol
*
* Tries to parse the function pointer address and if the external symbol TID
* is an external address overwrite the TID with the function pointer address.
* The function pointer address is always added to the address list of the external symbol.
*/
protected Tid handleCallToFunctionPointer(Address flow, Function external) {
Address funcPointer = parseFunctionPointerAddress();
Tid targetTid = null;
if(funcPointer != null && externalSymbolMap.containsKey(external.getName())) {
ExternSymbol symbol = externalSymbolMap.get(external.getName());
if(symbol.getTid().getId().startsWith("sub_EXTERNAL")) {
targetTid = new Tid(String.format("sub_%s", funcPointer.toString()), funcPointer.toString());
symbol.setTid(targetTid);
} else {
targetTid = symbol.getTid();
}
symbol.getAddresses().add(funcPointer.toString());
}
return targetTid;
}
/**
*
* @return Address of function pointer
*
* Parses the function pointer address out of an indirect call instruction
*/
protected Address parseFunctionPointerAddress() {
for(PcodeOp op : PcodeBlockData.ops) {
if(op.getOpcode() == PcodeOp.CALLIND && op.getInput(0).isAddress()) {
return op.getInput(0).getAddress();
} }
} }
return null; return null;
......
...@@ -11,8 +11,8 @@ public class ExternSymbol { ...@@ -11,8 +11,8 @@ public class ExternSymbol {
@SerializedName("tid") @SerializedName("tid")
private Tid tid; private Tid tid;
@SerializedName("address") @SerializedName("addresses")
private String address; private ArrayList<String> addresses;
@SerializedName("name") @SerializedName("name")
private String name; private String name;
@SerializedName("calling_convention") @SerializedName("calling_convention")
...@@ -23,11 +23,12 @@ public class ExternSymbol { ...@@ -23,11 +23,12 @@ public class ExternSymbol {
private Boolean noReturn; private Boolean noReturn;
public ExternSymbol() { public ExternSymbol() {
this.setAddresses(new ArrayList<String>());
} }
public ExternSymbol(Tid tid, String address, String name, String callingConvention, ArrayList<Arg> arguments, Boolean noReturn) { public ExternSymbol(Tid tid, ArrayList<String> addresses, String name, String callingConvention, ArrayList<Arg> arguments, Boolean noReturn) {
this.setTid(tid); this.setTid(tid);
this.setAddress(address); this.setAddresses(addresses);
this.setName(name); this.setName(name);
this.setCallingConvention(callingConvention); this.setCallingConvention(callingConvention);
this.setArguments(arguments); this.setArguments(arguments);
...@@ -42,12 +43,12 @@ public class ExternSymbol { ...@@ -42,12 +43,12 @@ public class ExternSymbol {
this.tid = tid; this.tid = tid;
} }
public String getAddress() { public ArrayList<String> getAddresses() {
return address; return addresses;
} }
public void setAddress(String address) { public void setAddresses(ArrayList<String> addresses) {
this.address = address; this.addresses = addresses;
} }
public String getName() { public String getName() {
......
...@@ -22,9 +22,8 @@ public class Program { ...@@ -22,9 +22,8 @@ public class Program {
this.setSubs(subs); this.setSubs(subs);
} }
public Program(ArrayList<Term<Sub>> subs, ArrayList<ExternSymbol> externSymbols, ArrayList<Tid> entryPoints) { public Program(ArrayList<Term<Sub>> subs, ArrayList<Tid> entryPoints) {
this.setSubs(subs); this.setSubs(subs);
this.setExternSymbols(externSymbols);
this.setEntryPoints(entryPoints); this.setEntryPoints(entryPoints);
} }
......
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