Skip to content

Commit 1f56bbe

Browse files
authored
Add Python Type Annotations (#34)
* Some initial changes * More typing changes
1 parent 117a4fb commit 1f56bbe

File tree

10 files changed

+286
-49
lines changed

10 files changed

+286
-49
lines changed

crackers/src/config/meta.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use rand::random;
44
use serde::{Deserialize, Serialize};
55
use tracing::Level;
66

7-
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
7+
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
88
#[serde(rename_all = "UPPERCASE")]
9-
#[cfg_attr(feature = "pyo3", pyclass)]
9+
#[cfg_attr(feature = "pyo3", pyclass(eq, eq_int))]
1010
pub enum CrackersLogLevel {
1111
Trace,
1212
Debug,

crackers/src/config/synthesis.rs

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,44 +41,4 @@ impl SynthesisConfig {
4141
combine_instructions,
4242
}
4343
}
44-
45-
#[getter]
46-
fn strategy(&self) -> SynthesisSelectionStrategy {
47-
self.strategy
48-
}
49-
50-
#[setter]
51-
fn set_strategy(&mut self, strategy: SynthesisSelectionStrategy) {
52-
self.strategy = strategy;
53-
}
54-
55-
#[getter]
56-
fn max_candidates_per_slot(&self) -> usize {
57-
self.max_candidates_per_slot
58-
}
59-
60-
#[setter]
61-
fn set_max_candidates_per_slot(&mut self, max_candidates_per_slot: usize) {
62-
self.max_candidates_per_slot = max_candidates_per_slot
63-
}
64-
65-
#[getter]
66-
fn parallel(&self) -> usize {
67-
self.parallel
68-
}
69-
70-
#[setter]
71-
fn set_parallel(&mut self, parallel: usize) {
72-
self.parallel = parallel
73-
}
74-
75-
#[getter]
76-
fn combine_instructions(&self) -> bool {
77-
self.combine_instructions
78-
}
79-
80-
#[setter]
81-
fn set_combine_instructions(&mut self, combine_instructions: bool) {
82-
self.combine_instructions = combine_instructions
83-
}
8444
}

crackers/src/gadget/library/builder.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use std::collections::HashSet;
2-
31
use derive_builder::Builder;
42
use jingle::sleigh::OpCode;
53
#[cfg(feature = "pyo3")]
64
use pyo3::pyclass;
5+
use pyo3::pymethods;
76
use serde::{Deserialize, Serialize};
7+
use std::collections::HashSet;
88

99
use crate::config::error::CrackersConfigError;
1010
use crate::config::object::load_sleigh;
@@ -67,3 +67,52 @@ fn default_blacklist() -> HashSet<OpCode> {
6767
OpCode::CPUI_MULTIEQUAL,
6868
])
6969
}
70+
71+
/**
72+
73+
pub sample_size: Option<usize>,
74+
pub base_address: Option<u64>,
75+
*/
76+
77+
#[pymethods]
78+
impl GadgetLibraryConfig {
79+
#[getter]
80+
pub fn get_max_gadget_length(&self) -> usize {
81+
self.max_gadget_length
82+
}
83+
84+
#[setter]
85+
pub fn set_max_gadget_length(&mut self, l: usize) {
86+
self.max_gadget_length = l;
87+
}
88+
89+
#[getter]
90+
pub fn get_path(&self) -> &str {
91+
self.path.as_str()
92+
}
93+
94+
#[setter]
95+
pub fn set_path(&mut self, l: String) {
96+
self.path = l;
97+
}
98+
99+
#[getter]
100+
pub fn get_sample_size(&self) -> Option<usize> {
101+
self.sample_size
102+
}
103+
104+
#[setter]
105+
pub fn set_sample_size(&mut self, l: Option<usize>) {
106+
self.sample_size = l;
107+
}
108+
109+
#[getter]
110+
pub fn get_base_address(&self) -> Option<u64> {
111+
self.base_address
112+
}
113+
114+
#[setter]
115+
pub fn set_base_address(&mut self, l: Option<u64>) {
116+
self.base_address = l;
117+
}
118+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import z3
2+
from .crackers import *
3+
4+
__doc__ = crackers.__doc__
5+
if hasattr(crackers, "__all__"):
6+
__all__ = crackers.__all__
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
from typing import Any, Union, List, Optional, Iterable, Callable
2+
import z3 # Assuming Z3 is imported for type annotations
3+
4+
5+
class SpaceInfo:
6+
# Placeholder for SpaceInfo class
7+
...
8+
9+
class VarNode:
10+
def __init__(self, space_index: int, offset: int, size: int) -> None: ...
11+
12+
class RawVarNodeDisplay:
13+
def __init__(self, offset: int, size: int, space_info: SpaceInfo) -> None: ...
14+
15+
class VarNodeDisplay:
16+
def __init__(self, raw: RawVarNodeDisplay = ..., register: tuple[str, VarNode] = ...) -> None: ...
17+
# Represents the enum variants Raw and Register
18+
raw: RawVarNodeDisplay
19+
register: tuple[str, VarNode]
20+
21+
22+
class ResolvedIndirectVarNode:
23+
def __init__(self, pointer: Any, pointer_space_info: SpaceInfo, access_size_bytes: int) -> None: ...
24+
25+
def pointer_bv(self) -> z3.BitVecRef: ...
26+
def space_name(self) -> str: ...
27+
def access_size(self) -> int: ...
28+
29+
30+
class ResolvedVarNode:
31+
"""
32+
Represents the PythonResolvedVarNode enum with two variants:
33+
- Direct: Contains a VarNodeDisplay
34+
- Indirect: Contains a ResolvedIndirectVarNode
35+
"""
36+
def __init__(self, value: Union[VarNodeDisplay, ResolvedIndirectVarNode]) -> None: ...
37+
value: Union[VarNodeDisplay, ResolvedIndirectVarNode]
38+
39+
40+
class PcodeOperation:
41+
pass
42+
43+
44+
class Instruction:
45+
"""
46+
Represents a Python wrapper for a Ghidra instruction.
47+
"""
48+
disassembly: str
49+
def pcode(self) -> List[PcodeOperation]: ...
50+
51+
class State:
52+
def __init__(self, jingle: JingleContext) -> State: ...
53+
54+
def varnode(self, varnode: ResolvedVarNode) -> z3.BitVecRef: ...
55+
def register(self, name: str) -> z3.BitVecRef: ...
56+
def ram(self, offset: int, length: int) -> z3.BitVecRef: ...
57+
58+
class ModeledInstruction:
59+
original_state: State
60+
final_state: State
61+
62+
def get_input_vns(self) -> Iterable[ResolvedVarNode]: ...
63+
def get_output_vns(self) -> Iterable[ResolvedVarNode]: ...
64+
65+
class ModeledBlock:
66+
original_state: State
67+
final_state: State
68+
def get_input_vns(self) -> Iterable[ResolvedVarNode]: ...
69+
def get_output_vns(self) -> Iterable[ResolvedVarNode]: ...
70+
71+
class JingleContext:
72+
def __init__(self, binary_path: str, ghidra: str) -> JingleContext: ...
73+
def model_instruction_at(self, offset: int) -> ModeledInstruction: ...
74+
def model_block_at(self, offset: int, max_instrs: int) -> ModeledBlock: ...
75+
76+
class SleighContext:
77+
"""
78+
Represents a Sleigh context in python.
79+
"""
80+
def __init__(self, binary_path: str, ghidra: str) -> SleighContext: ...
81+
base_address: int
82+
def instruction_at(self, offset: int) -> Optional[Instruction]: ...
83+
def make_jingle_context(self) -> JingleContext: ...
84+
85+
86+
"""
87+
Begin crackers types
88+
"""
89+
90+
class CrackersLogLevel:
91+
Debug = CrackersLogLevel.Debug
92+
Error = CrackersLogLevel.Error
93+
Info = CrackersLogLevel.Info
94+
Trace = CrackersLogLevel.Trace
95+
Warn = CrackersLogLevel.Warn
96+
97+
class MetaConfig:
98+
seed: int
99+
log_level: CrackersLogLevel
100+
101+
class SpecificationConfig:
102+
path: str
103+
max_instructions: int
104+
base_address: Optional[int]
105+
106+
class GadgetLibraryConfig:
107+
max_gadget_length: int
108+
path: str
109+
sample_size: Optional[int]
110+
base_address: Optional[int]
111+
112+
class SleighConfig:
113+
ghidra_path: str
114+
115+
class SynthesisSelectionStrategy:
116+
SatStrategy = SynthesisSelectionStrategy.SatStrategy
117+
OptimizeStrategy = SynthesisSelectionStrategy.OptimizeStrategy
118+
119+
class SynthesisConfig:
120+
strategy: SynthesisSelectionStrategy
121+
max_candidates_per_slot: int
122+
parallel: int
123+
combine_instructions: bool
124+
125+
class MemoryEqualityConstraint:
126+
space: str
127+
address: int
128+
size: int
129+
value: int
130+
131+
class StateEqualityConstraint:
132+
register: Optional[dict[str, int]]
133+
pointer: Optional[dict[str, str]]
134+
memory: Optional[MemoryEqualityConstraint]
135+
136+
class PointerRange:
137+
min: int
138+
max: int
139+
class PointerRangeConstraints:
140+
read: Optional[list[PointerRange]]
141+
class ConstraintConfig:
142+
precondition: Optional[StateEqualityConstraint]
143+
postcondition: Optional[StateEqualityConstraint]
144+
pointer: Optional[PointerRangeConstraints]
145+
146+
147+
class CrackersConfig:
148+
meta: MetaConfig
149+
spec: SpecificationConfig
150+
library: GadgetLibraryConfig
151+
sleigh: SleighConfig
152+
synthesis: SynthesisConfig
153+
constraint: ConstraintConfig
154+
155+
@classmethod
156+
def from_toml(cls, path: str) -> CrackersConfig: ...
157+
158+
def resolve_config(self) -> SynthesisParams: ...
159+
160+
class AssignmentModel:
161+
def eval_bv(self, bv: z3.BitVecRef, model_completion: bool) -> z3.BitVecRef: ...
162+
def initial_state(self) -> Optional[State]: ...
163+
def final_state(self) -> Optional[State]: ...
164+
def gadgets(self) -> list[ModeledBlock]: ...
165+
def inputs(self) -> Iterable[ResolvedVarNode]: ...
166+
def outputs(self) -> Iterable[ResolvedVarNode]: ...
167+
def input_summary(self, model_completion: bool): Iterable[tuple[str, z3.BitVecRef]]
168+
def output_summary(self, model_completion: bool): Iterable[tuple[str, z3.BitVecRef]]
169+
170+
171+
class PythonDecisionResult_AssignmentFound(DecisionResult):
172+
_0: AssignmentModel
173+
__match_args__ = ('_0',)
174+
175+
176+
class SelectionFailure:
177+
indices: list[int]
178+
179+
class PythonDecisionResult_Unsat(DecisionResult):
180+
_0: SelectionFailure
181+
pass
182+
183+
class DecisionResult:
184+
AssignmentFound : PythonDecisionResult_AssignmentFound
185+
Unsat : PythonDecisionResult_Unsat
186+
187+
DecisionResultType = Union[DecisionResult.AssignmentFound, DecisionResult.Unsat]
188+
189+
type StateConstraintGenerator = Callable[[State, int], z3.BitVecRef]
190+
type TransitionConstraintGenerator = Callable[[ModeledBlock], z3.BitVecRef]
191+
class SynthesisParams:
192+
def run(self) -> DecisionResultType: ...
193+
def add_precondition(self, fn: StateConstraintGenerator): ...
194+
def add_postcondition(self, fn: StateConstraintGenerator): ...
195+
def add_transition_constraint(self, fn: TransitionConstraintGenerator): ...
196+
pass

crackers_python/crackers/py.typed

Whitespace-only changes.

crackers_python/src/decision/assignment_model/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use pyo3::{Py, PyAny, PyResult, pyclass, pymethods};
1515
use std::rc::Rc;
1616
use z3::ast::BV;
1717

18-
#[pyclass(unsendable)]
18+
#[pyclass(unsendable, name = "AssignmentModel")]
1919
#[derive(Clone)]
2020
pub struct PythonAssignmentModel {
2121
pub inner: Rc<AssignmentModel<'static, ModeledBlock<'static>>>,

crackers_python/src/decision/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use pyo3::pyclass;
44

55
pub mod assignment_model;
66

7-
#[pyclass(unsendable)]
7+
#[pyclass(unsendable, name = "DecisionResult")]
88
pub enum PythonDecisionResult {
99
AssignmentFound(PythonAssignmentModel),
1010
Unsat(SelectionFailure),

crackers_python/src/lib.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ mod decision;
33
mod synthesis;
44

55
use crate::config::PythonCrackersConfig;
6+
use crate::decision::PythonDecisionResult;
7+
use crate::synthesis::PythonSynthesisParams;
68
use ::crackers::config::constraint::{
79
ConstraintConfig, MemoryEqualityConstraint, PointerRange, PointerRangeConstraints,
810
StateEqualityConstraint,
@@ -13,14 +15,38 @@ use ::crackers::config::specification::SpecificationConfig;
1315
use ::crackers::config::synthesis::SynthesisConfig;
1416
use ::crackers::gadget::library::builder::GadgetLibraryConfig;
1517
use ::crackers::synthesis::builder::SynthesisSelectionStrategy;
18+
use ::jingle::python::instruction::PythonInstruction;
19+
use ::jingle::python::jingle_context::PythonJingleContext;
20+
use ::jingle::python::modeled_block::PythonModeledBlock;
21+
use ::jingle::python::modeled_instruction::PythonModeledInstruction;
22+
use ::jingle::python::sleigh_context::LoadedSleighContextWrapper;
23+
use ::jingle::python::state::PythonState;
24+
use ::jingle::sleigh::{IndirectVarNode, PcodeOperation, VarNode};
1625
use pyo3::prelude::*;
17-
use std::ffi::CString;
1826

27+
#[pymodule]
28+
#[pyo3(submodule)]
29+
fn jingle(m: &Bound<'_, PyModule>) -> PyResult<()> {
30+
m.add_class::<VarNode>()?;
31+
m.add_class::<IndirectVarNode>()?;
32+
m.add_class::<PcodeOperation>()?;
33+
m.add_class::<PythonInstruction>()?;
34+
m.add_class::<LoadedSleighContextWrapper>()?;
35+
m.add_class::<PythonJingleContext>()?;
36+
m.add_class::<PythonState>()?;
37+
m.add_class::<PythonModeledInstruction>()?;
38+
m.add_class::<PythonModeledBlock>()?;
39+
Ok(())
40+
}
1941
/// A Python module implemented in Rust.
2042
#[pymodule]
2143
fn crackers(m: &Bound<'_, PyModule>) -> PyResult<()> {
22-
m.py().run(&CString::new("import z3")?, None, None)?;
44+
let j = PyModule::new(m.py(), "jingle")?;
45+
jingle(&j)?;
46+
m.add_submodule(&j)?;
2347
m.add_class::<PythonCrackersConfig>()?;
48+
m.add_class::<PythonDecisionResult>()?;
49+
m.add_class::<PythonSynthesisParams>()?;
2450
m.add_class::<MetaConfig>()?;
2551
m.add_class::<SpecificationConfig>()?;
2652
m.add_class::<SleighConfig>()?;

0 commit comments

Comments
 (0)