summaryrefslogtreecommitdiff
path: root/plugins/workflow_objc/src/activities/alloc_init.rs
blob: 5c8bf0645c61176a57688edc41039e3e9ce6d79e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use binaryninja::{
    binary_view::{BinaryView, BinaryViewExt as _},
    medium_level_il::MediumLevelILLiftedInstruction,
    rc::Ref,
    types::Type,
    workflow::AnalysisContext,
};
use bstr::ByteSlice;

use super::util;
use crate::{error::ILLevel, metadata::GlobalState, workflow::Confidence, Error};

// j_ prefixes are for stub functions in the dyld shared cache.
// The prefix is added by Binary Ninja's shared cache workflow.
const ALLOC_INIT_FUNCTIONS: &[&[u8]] = &[
    b"_objc_alloc_init",
    b"_objc_alloc_initWithZone",
    b"_objc_alloc",
    b"_objc_allocWithZone",
    b"_objc_opt_new",
    b"j__objc_alloc_init",
    b"j__objc_alloc_initWithZone",
    b"j__objc_alloc",
    b"j__objc_allocWithZone",
    b"j__objc_opt_new",
];

fn return_type_for_alloc_call(call: &util::Call<'_>, view: &BinaryView) -> Option<Ref<Type>> {
    if call.call.params.is_empty() {
        return None;
    }

    let class_addr =
        util::match_constant_pointer_or_load_of_constant_pointer(&call.call.params[0])?;
    let class_symbol_name = view.symbol_by_address(class_addr)?.full_name();
    let class_name = util::class_name_from_symbol_name(class_symbol_name.to_bytes().as_bstr())?;

    let class_type = view.type_by_name(class_name.to_str_lossy())?;
    Some(Type::pointer(&call.target.arch(), &class_type))
}

fn process_instruction(instr: &MediumLevelILLiftedInstruction, view: &BinaryView) -> Option<()> {
    let call = util::match_call_to_function_named(instr, view, ALLOC_INIT_FUNCTIONS)?;

    util::adjust_return_type_of_call(
        &call,
        return_type_for_alloc_call(&call, view)?.as_ref(),
        Confidence::AllocInit as u8,
    );

    Some(())
}

pub fn process(ac: &AnalysisContext) -> Result<(), Error> {
    let bv = ac.view();
    if GlobalState::should_ignore_view(&bv) {
        return Ok(());
    }

    let mlil = ac.mlil_function().ok_or(Error::MissingIL {
        level: ILLevel::Medium,
        func_start: ac.function().start(),
    })?;
    let mlil_ssa = mlil.ssa_form();

    for block in &mlil_ssa.basic_blocks() {
        for instr in block.iter() {
            process_instruction(&instr.lift(), &bv);
        }
    }

    Ok(())
}