Unverified Commit 008a45d2 by Enkelmann Committed by GitHub

Change non-returning extern calls to dead ends in the CFG (#319)

parent 242c5325
......@@ -37,6 +37,7 @@ impl Project {
self.calling_conventions
.get("__stdcall")
.or_else(|| self.calling_conventions.get("__cdecl"))
.or_else(|| self.calling_conventions.get("__thiscall")) // for x86_64 Microsoft Windows binaries.
}
/// Try to find a specific calling convention in the list of calling conventions in the project.
......@@ -98,14 +99,49 @@ impl Project {
}
}
/// Replace the return-to TID of calls to extern symbols that are marked as non-returning (e.g. `exit(..)`)
/// with the provided TID of an artificial sink block.
///
/// Returns `true` if at least one return target has been replaced this way.
fn retarget_calls_to_non_returning_symbols_to_artificial_sink(
&mut self,
dummy_blk_tid: &Tid,
) -> bool {
let mut has_at_least_one_jmp_been_retargeted = false;
for sub in self.program.term.subs.values_mut() {
for block in sub.term.blocks.iter_mut() {
for jmp in block.term.jmps.iter_mut() {
if let Jmp::Call {
target,
return_: Some(return_tid),
} = &mut jmp.term
{
if let Some(extern_symbol) = self.program.term.extern_symbols.get(target) {
if extern_symbol.no_return {
// Call to an extern symbol that does not return.
*return_tid = dummy_blk_tid.clone();
has_at_least_one_jmp_been_retargeted = true;
}
}
}
}
}
}
has_at_least_one_jmp_been_retargeted
}
/// Replace jumps to nonexisting TIDs with jumps to a dummy target
/// representing an artificial sink in the control flow graph.
/// Return a log message for each replaced jump target.
/// Also retarget the return address of extern symbol calls that are marked as non-returning to the artificial sink.
///
/// Nonexisting jump targets may be generated by the Ghidra backend
/// if the data at the target address is not a valid assembly instruction.
#[must_use]
fn remove_references_to_nonexisting_tids(&mut self) -> Vec<LogMessage> {
fn remove_references_to_nonexisting_tids_and_retarget_non_returning_calls(
&mut self,
) -> Vec<LogMessage> {
// Gather all existing jump targets
let mut jump_target_tids = HashSet::new();
for sub in self.program.term.subs.values() {
......@@ -139,8 +175,11 @@ impl Project {
}
}
}
// Also replace return targets of calls to non-returning extern symbols
let dummy_blk_needs_to_be_added =
self.retarget_calls_to_non_returning_symbols_to_artificial_sink(&dummy_blk_tid);
// If at least one dummy jump was inserted, add the corresponding dummy sub and block to the program.
if !log_messages.is_empty() {
if dummy_blk_needs_to_be_added || !log_messages.is_empty() {
let dummy_sub: Term<Sub> = Term {
tid: dummy_sub_tid,
term: Sub {
......@@ -181,13 +220,14 @@ impl Project {
///
/// Passes:
/// - Replace jumps to nonexisting TIDs with jumps to artificial sink targets in the CFG.
/// Also replace return addresses of non-returning external symbols with artificial sink targets.
/// - Duplicate blocks so that if a block is contained in several functions, each function gets its own unique copy.
/// - Propagate input expressions along variable assignments.
/// - Replace trivial expressions like `a XOR a` with their result.
/// - Remove dead register assignments
#[must_use]
pub fn normalize(&mut self) -> Vec<LogMessage> {
let logs = self.remove_references_to_nonexisting_tids();
let logs = self.remove_references_to_nonexisting_tids_and_retarget_non_returning_calls();
make_block_to_sub_mapping_unique(self);
self.propagate_input_expressions();
self.substitute_trivial_expressions();
......
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