Unverified Commit 9fa65999 by Melvin Klimke Committed by GitHub

Better parameter parsing for external Symbols (#176)

parent 275c13aa
...@@ -428,7 +428,11 @@ impl<'a> Context<'a> { ...@@ -428,7 +428,11 @@ impl<'a> Context<'a> {
// We assume here that we do not know the parameters and approximate them by all possible parameter registers. // We assume here that we do not know the parameters and approximate them by all possible parameter registers.
// This approximation is wrong if the function is known but has neither parameters nor return values. // This approximation is wrong if the function is known but has neither parameters nor return values.
// We cannot distinguish these two cases yet. // We cannot distinguish these two cases yet.
for parameter_register_name in calling_conv.parameter_register.iter() { for parameter_register_name in calling_conv
.integer_parameter_register
.iter()
.chain(calling_conv.float_parameter_register.iter())
{
if let Some(register_value) = state.get_register_by_name(parameter_register_name) { if let Some(register_value) = state.get_register_by_name(parameter_register_name) {
possible_referenced_ids.append(&mut register_value.referenced_ids()); possible_referenced_ids.append(&mut register_value.referenced_ids());
} }
...@@ -469,7 +473,11 @@ impl<'a> Context<'a> { ...@@ -469,7 +473,11 @@ impl<'a> Context<'a> {
self.adjust_stack_register_on_extern_call(state_before_call, &mut new_state); self.adjust_stack_register_on_extern_call(state_before_call, &mut new_state);
let mut possible_referenced_ids = BTreeSet::new(); let mut possible_referenced_ids = BTreeSet::new();
for parameter_register_name in calling_conv.parameter_register.iter() { for parameter_register_name in calling_conv
.integer_parameter_register
.iter()
.chain(calling_conv.float_parameter_register.iter())
{
if let Some(register_value) = if let Some(register_value) =
state_before_call.get_register_by_name(parameter_register_name) state_before_call.get_register_by_name(parameter_register_name)
{ {
......
...@@ -84,7 +84,8 @@ fn mock_project() -> (Project, Config) { ...@@ -84,7 +84,8 @@ fn mock_project() -> (Project, Config) {
}; };
let cconv = CallingConvention { let cconv = CallingConvention {
name: "default".to_string(), name: "default".to_string(),
parameter_register: vec!["RDX".to_string()], integer_parameter_register: vec!["RDX".to_string()],
float_parameter_register: vec!["XMM0".to_string()],
return_register: vec!["RDX".to_string()], return_register: vec!["RDX".to_string()],
callee_saved_register: vec!["callee_saved_reg".to_string()], callee_saved_register: vec!["callee_saved_reg".to_string()],
}; };
......
...@@ -297,10 +297,9 @@ impl State { ...@@ -297,10 +297,9 @@ impl State {
pi_state_option: Option<&PointerInferenceState>, pi_state_option: Option<&PointerInferenceState>,
) -> bool { ) -> bool {
if let Some(calling_conv) = project.get_standard_calling_convention() { if let Some(calling_conv) = project.get_standard_calling_convention() {
self.check_register_list_for_taint( let mut all_parameters = calling_conv.integer_parameter_register.clone();
&calling_conv.parameter_register[..], all_parameters.append(&mut calling_conv.float_parameter_register.clone());
pi_state_option, self.check_register_list_for_taint(&all_parameters, pi_state_option)
)
} else { } else {
// No standard calling convention found. Assume everything may be parameters or referenced by parameters. // No standard calling convention found. Assume everything may be parameters or referenced by parameters.
!self.is_empty() !self.is_empty()
......
...@@ -385,8 +385,12 @@ impl State { ...@@ -385,8 +385,12 @@ impl State {
/// we approximate the parameters with all parameter registers of the standard calling convention of the project. /// we approximate the parameters with all parameter registers of the standard calling convention of the project.
pub fn remove_non_parameter_taints_for_generic_function(&mut self, project: &Project) { pub fn remove_non_parameter_taints_for_generic_function(&mut self, project: &Project) {
if let Some(calling_conv) = project.get_standard_calling_convention() { if let Some(calling_conv) = project.get_standard_calling_convention() {
let register_names: HashSet<String> = let register_names: HashSet<String> = calling_conv
calling_conv.parameter_register.iter().cloned().collect(); .integer_parameter_register
.iter()
.chain(calling_conv.float_parameter_register.iter())
.cloned()
.collect();
let taints = self.register_taint.clone(); let taints = self.register_taint.clone();
for (register, _) in taints.iter() { for (register, _) in taints.iter() {
if register_names.get(&register.name).is_none() { if register_names.get(&register.name).is_none() {
......
...@@ -423,8 +423,10 @@ pub struct CallingConvention { ...@@ -423,8 +423,10 @@ pub struct CallingConvention {
/// The name of the calling convention /// The name of the calling convention
#[serde(rename = "calling_convention")] #[serde(rename = "calling_convention")]
pub name: String, pub name: String,
/// A list of possible parameter register /// Possible integer parameter registers.
pub parameter_register: Vec<String>, pub integer_parameter_register: Vec<String>,
/// Possible float parameter registers.
pub float_parameter_register: Vec<String>,
/// A list of possible return register /// A list of possible return register
pub return_register: Vec<String>, pub return_register: Vec<String>,
/// A list of callee-saved register, /// A list of callee-saved register,
...@@ -616,7 +618,8 @@ mod tests { ...@@ -616,7 +618,8 @@ mod tests {
pub fn mock() -> CallingConvention { pub fn mock() -> CallingConvention {
CallingConvention { CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
parameter_register: vec!["RDI".to_string()], integer_parameter_register: vec!["RDI".to_string()],
float_parameter_register: vec!["XMMO".to_string()],
return_register: vec!["RAX".to_string()], return_register: vec!["RAX".to_string()],
callee_saved_register: vec!["RBP".to_string()], callee_saved_register: vec!["RBP".to_string()],
} }
......
...@@ -495,8 +495,10 @@ pub struct CallingConvention { ...@@ -495,8 +495,10 @@ pub struct CallingConvention {
/// The name of the calling convention. /// The name of the calling convention.
#[serde(rename = "calling_convention")] #[serde(rename = "calling_convention")]
pub name: String, pub name: String,
/// Possible parameter registers. /// Possible integer parameter registers.
parameter_register: Vec<String>, integer_parameter_register: Vec<String>,
/// Possible float parameter registers.
float_parameter_register: Vec<String>,
/// Possible return registers. /// Possible return registers.
return_register: Vec<String>, return_register: Vec<String>,
/// Callee-saved registers. /// Callee-saved registers.
...@@ -509,7 +511,8 @@ impl From<CallingConvention> for IrCallingConvention { ...@@ -509,7 +511,8 @@ impl From<CallingConvention> for IrCallingConvention {
fn from(cconv: CallingConvention) -> IrCallingConvention { fn from(cconv: CallingConvention) -> IrCallingConvention {
IrCallingConvention { IrCallingConvention {
name: cconv.name, name: cconv.name,
parameter_register: cconv.parameter_register, integer_parameter_register: cconv.integer_parameter_register,
float_parameter_register: cconv.float_parameter_register,
return_register: cconv.return_register, return_register: cconv.return_register,
callee_saved_register: cconv.unaffected_register, callee_saved_register: cconv.unaffected_register,
} }
......
...@@ -85,7 +85,8 @@ impl Setup { ...@@ -85,7 +85,8 @@ impl Setup {
"register_calling_convention": [ "register_calling_convention": [
{ {
"calling_convention": "default", "calling_convention": "default",
"parameter_register": [], "integer_parameter_register": [],
"float_parameter_register": [],
"return_register": [], "return_register": [],
"unaffected_register": [], "unaffected_register": [],
"killed_by_call_register": [] "killed_by_call_register": []
......
...@@ -254,7 +254,6 @@ public class PcodeExtractor extends GhidraScript { ...@@ -254,7 +254,6 @@ public class PcodeExtractor extends GhidraScript {
try { try {
HashMap<String, RegisterConvention> conventions = new HashMap<String, RegisterConvention>(); HashMap<String, RegisterConvention> conventions = new HashMap<String, RegisterConvention>();
ParseCspecContent.parseSpecs(currentProgram, conventions); ParseCspecContent.parseSpecs(currentProgram, conventions);
addParameterRegister(conventions);
project.setRegisterConvention(new ArrayList<RegisterConvention>(conventions.values())); project.setRegisterConvention(new ArrayList<RegisterConvention>(conventions.values()));
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
System.out.println(e); System.out.println(e);
...@@ -267,23 +266,6 @@ public class PcodeExtractor extends GhidraScript { ...@@ -267,23 +266,6 @@ public class PcodeExtractor extends GhidraScript {
/** /**
* Adds parameter register to the RegisterCallingConvention object
*/
protected void addParameterRegister(HashMap<String, RegisterConvention> conventions) {
PrototypeModel[] models = currentProgram.getCompilerSpec().getCallingConventions();
for(PrototypeModel model : models) {
String cconv = model.getName();
if(conventions.get(cconv) != null) {
ArrayList<String> parameters = conventions.get(cconv).getParameter();
for(VariableStorage storage : model.getPotentialInputRegisterStorage(currentProgram)) {
parameters.add(storage.getRegister().getName());
}
}
}
}
/**
* Adds all entry points of internal and external function to a global hash map * Adds all entry points of internal and external function to a global hash map
* This will later speed up the cast of indirect Calls. * This will later speed up the cast of indirect Calls.
*/ */
......
...@@ -267,10 +267,10 @@ public class ParseCspecContent { ...@@ -267,10 +267,10 @@ public class ParseCspecContent {
RegisterConvention convention = new RegisterConvention(); RegisterConvention convention = new RegisterConvention();
if(name.equals("default_proto")) { if(name.equals("default_proto")) {
parser.start(); parser.start();
getUnaffectedKilledByCallAndOutput(parser, convention); getCconvRegister(parser, convention);
parser.end(); parser.end();
} else if(name.equals("prototype")) { } else if(name.equals("prototype")) {
getUnaffectedKilledByCallAndOutput(parser, convention); getCconvRegister(parser, convention);
} }
// Using the hashmap this way will simplify the addition of parameter registers which are not parsed here // Using the hashmap this way will simplify the addition of parameter registers which are not parsed here
...@@ -286,17 +286,19 @@ public class ParseCspecContent { ...@@ -286,17 +286,19 @@ public class ParseCspecContent {
* *
* Sets the convention's unaffected, killed by call and return registers as well as the calling convention * Sets the convention's unaffected, killed by call and return registers as well as the calling convention
*/ */
public static void getUnaffectedKilledByCallAndOutput(XmlPullParser parser, RegisterConvention convention) { public static void getCconvRegister(XmlPullParser parser, RegisterConvention convention) {
XmlElement protoElement = parser.start(); XmlElement protoElement = parser.start();
String cconv = protoElement.getAttribute("name"); String cconv = protoElement.getAttribute("name");
convention.setCconv(cconv); convention.setCconv(cconv);
while(parser.peek().isStart()) { while(parser.peek().isStart()) {
XmlElement registers = parser.peek(); XmlElement entries = parser.peek();
if(registers.getName().equals("unaffected")) { if(entries.getName().equals("input")) {
parseInput(parser, convention);
} else if(entries.getName().equals("unaffected")) {
convention.setUnaffected(getRegisters(parser)); convention.setUnaffected(getRegisters(parser));
} else if (registers.getName().equals("killedbycall")) { } else if (entries.getName().equals("killedbycall")) {
convention.setKilledByCall(getRegisters(parser)); convention.setKilledByCall(getRegisters(parser));
} else if (registers.getName().equals("output")) { } else if (entries.getName().equals("output")) {
convention.setReturn(parseOutput(parser)); convention.setReturn(parseOutput(parser));
} else { } else {
discardSubTree(parser); discardSubTree(parser);
...@@ -305,6 +307,55 @@ public class ParseCspecContent { ...@@ -305,6 +307,55 @@ public class ParseCspecContent {
parser.end(protoElement); parser.end(protoElement);
} }
/**
*
* @param parser: parser for .cspec file
* @param convention: convention object for later serialization
*
* Parses the parameter registers for an external symbol.
* Differentiates between integer and float registers.
*/
public static void parseInput(XmlPullParser parser, RegisterConvention convention) {
ArrayList<String> integerRegisters = new ArrayList<String>();
ArrayList<String> floatRegisters = new ArrayList<String>();
parser.start("input");
while(parser.peek().isStart()) {
XmlElement pentry = parser.peek();
parser.start("pentry");
XmlElement entry = parser.peek();
if(entry.getName().equals("register")) {
parser.start("register");
if(isFloatRegister(pentry)) {
floatRegisters.add(entry.getAttribute("name"));
} else {
integerRegisters.add(entry.getAttribute("name"));
}
parser.end();
} else {
discardSubTree(parser);
}
parser.end();
}
parser.end();
convention.setFloatParameter(floatRegisters);
convention.setIntegerParameter(integerRegisters);
}
/**
*
* @param pentry: Parameter register entry
* @return: indicates whether the current entry is a float register.
*/
public static Boolean isFloatRegister(XmlElement pentry) {
if(pentry.hasAttribute("metatype")) {
if(pentry.getAttribute("metatype").equals("float")) {
return true;
}
}
return false;
}
/** /**
* *
......
package internal; package internal;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -9,8 +8,10 @@ public class RegisterConvention { ...@@ -9,8 +8,10 @@ public class RegisterConvention {
@SerializedName("calling_convention") @SerializedName("calling_convention")
private String cconv; private String cconv;
@SerializedName("parameter_register") @SerializedName("integer_parameter_register")
private ArrayList<String> parameter; private ArrayList<String> integerParameter;
@SerializedName("float_parameter_register")
private ArrayList<String> floatParameter;
@SerializedName("return_register") @SerializedName("return_register")
private ArrayList<String> return_; private ArrayList<String> return_;
@SerializedName("unaffected_register") @SerializedName("unaffected_register")
...@@ -19,15 +20,24 @@ public class RegisterConvention { ...@@ -19,15 +20,24 @@ public class RegisterConvention {
private ArrayList<String> killedByCall; private ArrayList<String> killedByCall;
public RegisterConvention() { public RegisterConvention() {
this.setParameter(new ArrayList<String>()); this.setIntegerParameter(new ArrayList<String>());
this.setFloatParameter(new ArrayList<String>());
this.setReturn(new ArrayList<String>()); this.setReturn(new ArrayList<String>());
this.setUnaffected(new ArrayList<String>()); this.setUnaffected(new ArrayList<String>());
this.setKilledByCall(new ArrayList<String>()); this.setKilledByCall(new ArrayList<String>());
} }
public RegisterConvention(String cconv, ArrayList<String> parameter, ArrayList<String> return_, ArrayList<String> unaffected, ArrayList<String> killedByCall) { public RegisterConvention(
String cconv,
ArrayList<String> integerParameter,
ArrayList<String> floatParameter,
ArrayList<String> return_,
ArrayList<String> unaffected,
ArrayList<String> killedByCall
) {
this.setCconv(cconv); this.setCconv(cconv);
this.setParameter(parameter); this.setIntegerParameter(integerParameter);
this.setFloatParameter(floatParameter);
this.setReturn(return_); this.setReturn(return_);
this.setUnaffected(unaffected); this.setUnaffected(unaffected);
this.setKilledByCall(killedByCall); this.setKilledByCall(killedByCall);
...@@ -41,12 +51,20 @@ public class RegisterConvention { ...@@ -41,12 +51,20 @@ public class RegisterConvention {
this.cconv = cconv; this.cconv = cconv;
} }
public ArrayList<String> getParameter() { public ArrayList<String> getIntegerParameter() {
return parameter; return integerParameter;
}
public void setIntegerParameter(ArrayList<String> integerParameter) {
this.integerParameter = integerParameter;
}
public ArrayList<String> getFloatParameter() {
return floatParameter;
} }
public void setParameter(ArrayList<String> parameter) { public void setFloatParameter(ArrayList<String> floatParameter) {
this.parameter = parameter; this.floatParameter = floatParameter;
} }
public ArrayList<String> getReturn() { public ArrayList<String> getReturn() {
......
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