1mod call_analyzer;
2mod heap_analyzer;
3mod jump_analyzer;
4pub mod locals_analyzer;
5pub mod reaching_defs;
6mod stack_analyzer;
7use crate::ir::types::{Binopcode, IRBlock, IRMap, Stmt, Unopcode, Value};
8use crate::lattices::reachingdefslattice::LocIdx;
9use crate::lattices::{Lattice, VarState};
10use std::collections::{HashMap, VecDeque};
11use yaxpeax_core::analyses::control_flow::VW_CFG;
12
13pub use self::call_analyzer::CallAnalyzer;
15pub use self::heap_analyzer::HeapAnalyzer;
16pub use self::jump_analyzer::SwitchAnalyzer;
17pub use self::stack_analyzer::StackAnalyzer;
18
19pub type AnalysisResult<T> = HashMap<u64, T>;
20
21pub trait AbstractAnalyzer<State: Lattice + VarState + Clone> {
22 fn init_state(&self) -> State {
23 Default::default()
24 }
25 fn process_branch(
26 &self,
27 _irmap: &IRMap,
28 in_state: &State,
29 succ_addrs: &Vec<u64>,
30 _addr: &u64,
31 ) -> Vec<(u64, State)> {
32 succ_addrs
33 .into_iter()
34 .map(|addr| (addr.clone(), in_state.clone()))
35 .collect()
36 }
37 fn aexec_unop(
38 &self,
39 in_state: &mut State,
40 _opcode: &Unopcode,
41 dst: &Value,
42 _src: &Value,
43 _loc_idx: &LocIdx,
44 ) -> () {
45 in_state.set_to_bot(dst)
46 }
47 fn aexec_binop(
48 &self,
49 in_state: &mut State,
50 opcode: &Binopcode,
51 dst: &Value,
52 _src1: &Value,
53 _src2: &Value,
54 _loc_idx: &LocIdx,
55 ) -> () {
56 match opcode {
57 Binopcode::Cmp => (),
58 Binopcode::Test => (),
59 _ => in_state.set_to_bot(dst),
60 }
61 }
62
63 fn aexec(&self, in_state: &mut State, ir_instr: &Stmt, loc_idx: &LocIdx) -> () {
64 match ir_instr {
65 Stmt::Clear(dst, _srcs) => in_state.set_to_bot(dst),
66 Stmt::Unop(opcode, dst, src) => self.aexec_unop(in_state, opcode, &dst, &src, loc_idx),
67 Stmt::Binop(opcode, dst, src1, src2) => {
68 self.aexec_binop(in_state, opcode, dst, src1, src2, loc_idx);
69 in_state.adjust_stack_offset(opcode, dst, src1, src2)
70 }
71 Stmt::Call(_) => in_state.on_call(),
72 _ => (),
73 }
74 }
75
76 fn analyze_block(&self, state: &State, irblock: &IRBlock) -> State {
77 let mut new_state = state.clone();
78 for (addr, instruction) in irblock.iter() {
79 for (idx, ir_insn) in instruction.iter().enumerate() {
80 log::debug!(
81 "Analyzing insn @ 0x{:x}: {:?}: state = {:?}",
82 addr,
83 ir_insn,
84 new_state
85 );
86 self.aexec(
87 &mut new_state,
88 ir_insn,
89 &LocIdx {
90 addr: *addr,
91 idx: idx as u32,
92 },
93 );
94 }
95 }
96 new_state
97 }
98}
99
100fn align_succ_addrs(addr: u64, succ_addrs: Vec<u64>) -> Vec<u64> {
101 if succ_addrs.len() != 2 {
102 return succ_addrs;
103 }
104 let a1 = succ_addrs[0];
105 let a2 = succ_addrs[1];
106 if a1 < addr {
107 return vec![a2, a1];
108 }
109 if a2 < addr {
110 return vec![a1, a2];
111 }
112 if a1 < a2 {
113 return vec![a1, a2];
114 }
115 if a1 >= a2 {
116 return vec![a2, a1];
117 }
118 panic!("Unreachable");
119}
120
121pub fn run_worklist<T: AbstractAnalyzer<State>, State: VarState + Lattice + Clone>(
122 cfg: &VW_CFG,
123 irmap: &IRMap,
124 analyzer: &T,
125) -> AnalysisResult<State> {
126 let mut statemap: HashMap<u64, State> = HashMap::new();
127 let mut worklist: VecDeque<u64> = VecDeque::new();
128 worklist.push_back(cfg.entrypoint);
129 statemap.insert(cfg.entrypoint, analyzer.init_state());
130
131 while !worklist.is_empty() {
132 let addr = worklist.pop_front().unwrap();
133 let irblock = irmap.get(&addr).unwrap();
134 let state = statemap.get(&addr).unwrap();
135 let new_state = analyzer.analyze_block(state, irblock);
136 let succ_addrs_unaligned: Vec<u64> = cfg.graph.neighbors(addr).collect();
137 let succ_addrs: Vec<u64> = align_succ_addrs(addr, succ_addrs_unaligned);
138 log::debug!("Processing Block: 0x{:x} -> {:?}", addr, succ_addrs);
139 for (succ_addr, branch_state) in
140 analyzer.process_branch(irmap, &new_state, &succ_addrs, &addr)
141 {
142 let has_change = if statemap.contains_key(&succ_addr) {
143 let old_state = statemap.get(&succ_addr).unwrap();
144 let merged_state = old_state.meet(&branch_state, &LocIdx { addr: addr, idx: 0 });
145
146 if merged_state > *old_state {
147 log::debug!("{:?} {:?}", merged_state, old_state);
148 panic!("Meet monoticity error");
149 }
150 let has_change = *old_state != merged_state;
151 log::debug!(
152 "At block 0x{:x}: merged input {:?}",
153 succ_addr,
154 merged_state
155 );
156 statemap.insert(succ_addr, merged_state);
157 has_change
158 } else {
159 log::debug!("At block 0x{:x}: new input {:?}", succ_addr, branch_state);
160 statemap.insert(succ_addr, branch_state);
161 true
162 };
163
164 if has_change && !worklist.contains(&succ_addr) {
165 worklist.push_back(succ_addr);
166 }
167 }
168 }
169 statemap
170}