Skip to content

Commit 53c9868

Browse files
committed
Add reference program support for binary and raw pcode specifications
- Introduce ReferenceProgram struct representing steps and initial memory valuation - Implement loading reference programs from binary files and raw pcode strings - Add MemoryValuation helper for managing initial memory constraints - Refactor specification config to support binary files or raw pcode - Update dependency on jingle to 0.4.0 to support new features
1 parent 1961b53 commit 53c9868

File tree

12 files changed

+375
-254
lines changed

12 files changed

+375
-254
lines changed

crackers/src/config/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub struct CrackersConfig {
3535
impl CrackersConfig {
3636
pub fn resolve(&self) -> Result<SynthesisParams, CrackersError> {
3737
let library = self.library.build(&self.sleigh)?;
38+
let lang_id = library.language_id.clone();
3839
let mut b = SynthesisParamsBuilder::default();
3940
if let Some(c) = &self.constraint {
4041
b.preconditions(c.get_preconditions(&library.arch_info()).collect());
@@ -47,6 +48,7 @@ impl CrackersConfig {
4748
&self.specification,
4849
&self.sleigh,
4950
&self.library.operation_blacklist,
51+
&lang_id,
5052
)?);
5153
b.selection_strategy(self.synthesis.strategy);
5254
b.combine_instructions(self.synthesis.combine_instructions);

crackers/src/config/specification.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,22 @@ use crate::config::sleigh::SleighConfig;
1414

1515
#[derive(Clone, Debug, Deserialize, Serialize)]
1616
#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))]
17-
pub struct SpecificationConfig {
17+
pub struct BinaryFileSpecification {
1818
pub path: String,
1919
pub max_instructions: usize,
2020
pub base_address: Option<u64>,
2121
}
2222

23+
#[derive(Clone, Debug, Deserialize, Serialize)]
24+
#[cfg_attr(feature = "pyo3", pyclass)]
25+
pub enum SpecificationConfig {
26+
BinaryFile(BinaryFileSpecification),
27+
RawPcode(String),
28+
}
29+
2330
#[cfg(feature = "pyo3")]
2431
#[pymethods]
25-
impl SpecificationConfig {
32+
impl BinaryFileSpecification {
2633
#[new]
2734
fn new(path: String, max_instructions: usize, base_address: Option<u64>) -> Self {
2835
Self {
@@ -63,7 +70,7 @@ impl SpecificationConfig {
6370
}
6471
}
6572

66-
impl SpecificationConfig {
73+
impl BinaryFileSpecification {
6774
fn load_sleigh<'a>(
6875
&self,
6976
sleigh_config: &'a SleighConfig,

crackers/src/gadget/library/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod image;
1919
pub struct GadgetLibrary {
2020
pub(crate) gadgets: Vec<Gadget>,
2121
arch_info: SleighArchInfo,
22+
pub(crate) language_id: String,
2223
}
2324

2425
impl AsRef<SleighArchInfo> for GadgetLibrary {
@@ -52,6 +53,7 @@ impl GadgetLibrary {
5253
let mut lib: GadgetLibrary = GadgetLibrary {
5354
gadgets: vec![],
5455
arch_info: sleigh.arch_info().clone(),
56+
language_id: sleigh.get_language_id().to_string(),
5557
};
5658
event!(Level::INFO, "Loading gadgets from sleigh");
5759
for section in sleigh.get_sections().filter(|s| s.perms.exec) {
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
use super::ReferenceProgram;
2+
use crate::config::error::CrackersConfigError;
3+
use crate::config::error::CrackersConfigError::{
4+
SpecMissingStartSymbol, SpecMissingTextSection, UnrecognizedArchitecture,
5+
};
6+
use crate::config::sleigh::SleighConfig;
7+
use crate::config::specification::BinaryFileSpecification;
8+
use crate::error::CrackersError;
9+
use crate::error::CrackersError::ModelGenerationError;
10+
use crate::reference_program::MemoryValuation;
11+
use crate::reference_program::step::Step;
12+
use crate::synthesis::partition_iterator::Partition;
13+
use jingle::analysis::varnode::VarNodeSet;
14+
use jingle::modeling::{ModeledInstruction, ModelingContext};
15+
use jingle::sleigh::context::image::gimli::map_gimli_architecture;
16+
use jingle::sleigh::context::loaded::LoadedSleighContext;
17+
use jingle::sleigh::{GeneralizedVarNode, Instruction, OpCode, SleighArchInfo, VarNode};
18+
use jingle::varnode::ResolvedVarnode;
19+
use object::{File, Object, ObjectSymbol};
20+
use std::borrow::Borrow;
21+
use std::cmp::min;
22+
use std::collections::{HashMap, HashSet};
23+
use std::fs;
24+
use z3::{SatResult, Solver};
25+
26+
impl ReferenceProgram {
27+
pub fn try_load_binary(
28+
spec: &BinaryFileSpecification,
29+
sleigh_config: &SleighConfig,
30+
blacklist: &HashSet<OpCode>,
31+
) -> Result<Self, CrackersConfigError> {
32+
let mut blacklist = blacklist.clone();
33+
// We _do_ want to allow these in the reference program
34+
blacklist.remove(&OpCode::CPUI_BRANCH);
35+
blacklist.remove(&OpCode::CPUI_CALL);
36+
37+
let bytes = fs::read(&spec.path)?;
38+
let gimli_file = File::parse(&*bytes)?;
39+
let sleigh_context_builder = sleigh_config.context_builder()?;
40+
41+
let sym = gimli_file
42+
.symbol_by_name("_start")
43+
.ok_or(SpecMissingStartSymbol)?;
44+
let _section = gimli_file
45+
.section_by_name(".text")
46+
.ok_or(SpecMissingTextSection)?;
47+
let arch = map_gimli_architecture(&gimli_file).ok_or(UnrecognizedArchitecture(format!(
48+
"{:?}",
49+
gimli_file.architecture()
50+
)))?;
51+
let sleigh = sleigh_context_builder.build(arch)?;
52+
let mut sleigh = sleigh.initialize_with_image(&gimli_file)?;
53+
let mut addr = sym.address();
54+
if let Some(o) = spec.base_address {
55+
sleigh.set_base_address(o);
56+
addr = addr.wrapping_add(o);
57+
}
58+
let instrs: Vec<_> = sleigh
59+
.read_until_branch(addr, spec.max_instructions)
60+
.collect();
61+
for op in instrs.iter().flat_map(|i| i.ops.iter()) {
62+
if blacklist.contains(&op.opcode()) {
63+
return Err(CrackersConfigError::IllegalPcodeOperation(op.opcode()));
64+
}
65+
}
66+
67+
let steps: Vec<_> = instrs.into_iter().map(Step::from_instr).collect();
68+
let mut ref_program = Self {
69+
steps,
70+
initial_memory: Default::default(),
71+
};
72+
73+
ref_program.calc_initial_memory_valuation(sleigh);
74+
Ok(ref_program)
75+
}
76+
77+
fn calc_initial_memory_valuation(&mut self, image: LoadedSleighContext<'_>) {
78+
let steps = &self.steps;
79+
let mut covering_set = VarNodeSet::default();
80+
// initial direct pass
81+
for x in steps
82+
.iter()
83+
.flat_map(|step| step.instructions())
84+
.flat_map(|i| i.ops.clone())
85+
{
86+
for vn in x.inputs() {
87+
if let GeneralizedVarNode::Direct(vn) = vn {
88+
covering_set.insert(&vn);
89+
}
90+
}
91+
}
92+
93+
// now load indirect until it stablizes
94+
let mut stablized = false;
95+
while !stablized {
96+
stablized = true;
97+
for x in steps
98+
.iter()
99+
.flat_map(|step| step.instructions())
100+
.flat_map(|i| i.ops.clone())
101+
{
102+
for vn in x.inputs() {
103+
if let GeneralizedVarNode::Indirect(vn) = vn {
104+
if covering_set.covers(&vn.pointer_location) {
105+
let pointer_offset_bytes_le = if image.spaces()
106+
[image.arch_info().default_code_space_index()]
107+
.isBigEndian()
108+
{
109+
image.read_bytes(&vn.pointer_location).map(|mut f| {
110+
f.reverse();
111+
f
112+
})
113+
} else {
114+
image.read_bytes(&vn.pointer_location)
115+
};
116+
if let Some(pointer_offset_bytes_le) = pointer_offset_bytes_le {
117+
let mut buffer: [u8; 8] = [0; 8];
118+
let max = min(buffer.len(), pointer_offset_bytes_le.len());
119+
buffer[0..max].copy_from_slice(&pointer_offset_bytes_le[0..max]);
120+
let ptr = u64::from_le_bytes(buffer);
121+
let new_vn = VarNode {
122+
size: vn.access_size_bytes,
123+
space_index: vn.pointer_space_index,
124+
offset: ptr,
125+
};
126+
covering_set.insert(&new_vn);
127+
stablized = false;
128+
}
129+
}
130+
}
131+
}
132+
}
133+
}
134+
135+
self.initialize_valuation(&covering_set, &image);
136+
let extended_constraints = self
137+
.get_extended_constraints_from_indirect(image.arch_info())
138+
.unwrap();
139+
self.initialize_valuation(&extended_constraints, &image);
140+
}
141+
142+
fn initialize_valuation(&mut self, covering_set: &VarNodeSet, image: &LoadedSleighContext<'_>) {
143+
let mut valuation = HashMap::new();
144+
for x in covering_set.varnodes() {
145+
if let Some(b) = image.read_bytes(&x) {
146+
valuation.insert(x, b);
147+
}
148+
}
149+
self.initial_memory = MemoryValuation(valuation);
150+
}
151+
152+
pub fn partitions(&self) -> impl Iterator<Item = Self> {
153+
let init = self.initial_memory.clone();
154+
self.steps.partitions().map(move |steps| {
155+
let steps: Vec<_> = steps.into_iter().map(|s| Step::combine(s.iter())).collect();
156+
Self {
157+
steps,
158+
initial_memory: init.clone(),
159+
}
160+
})
161+
}
162+
163+
fn get_extended_constraints_from_indirect<T: Borrow<SleighArchInfo>>(
164+
&self,
165+
ctx: T,
166+
) -> Result<VarNodeSet, CrackersError> {
167+
let i: Vec<_> = self.instructions().cloned().collect();
168+
let i: Instruction = i.as_slice().try_into().unwrap();
169+
let modeled_instr = ModeledInstruction::new(i, ctx)?;
170+
let init_constraint = self.initial_memory.to_constraint();
171+
let constraint = init_constraint(modeled_instr.get_original_state())?;
172+
let solver = Solver::new();
173+
let mut vn_set = VarNodeSet::default();
174+
solver.assert(&constraint);
175+
match solver.check() {
176+
SatResult::Sat => {
177+
let model = solver.get_model().ok_or(ModelGenerationError)?;
178+
for x in modeled_instr.get_inputs() {
179+
match x {
180+
ResolvedVarnode::Direct(vn) => {
181+
vn_set.insert(&vn);
182+
}
183+
ResolvedVarnode::Indirect(ivn) => {
184+
vn_set.insert(&ivn.pointer_location);
185+
if let Some(res) =
186+
model.eval(&ivn.pointer, true).and_then(|f| f.as_u64())
187+
{
188+
let new_vn = VarNode {
189+
size: ivn.access_size_bytes,
190+
space_index: ivn.pointer_space_idx,
191+
offset: res,
192+
};
193+
vn_set.insert(&new_vn);
194+
}
195+
}
196+
}
197+
}
198+
}
199+
_ => {
200+
return Err(CrackersError::ModelGenerationError);
201+
}
202+
}
203+
Ok(vn_set)
204+
}
205+
206+
pub fn len(&self) -> usize {
207+
self.steps.len()
208+
}
209+
210+
fn instructions(&self) -> impl Iterator<Item = &Instruction> {
211+
self.steps.iter().flat_map(|step| step.instructions())
212+
}
213+
214+
pub fn is_empty(&self) -> bool {
215+
self.steps.is_empty()
216+
}
217+
218+
pub fn steps(&self) -> &[Step] {
219+
&self.steps
220+
}
221+
222+
pub fn initial_memory(&self) -> &MemoryValuation {
223+
&self.initial_memory
224+
}
225+
}

0 commit comments

Comments
 (0)