clear upper 32 bits of 64bit regs, fix parity flag, add longdiv/mod, many other changes

This commit is contained in:
aemmitt committed 2022-06-09 23:36:42 -04:00
1 parent da6ed7b7eb
commit 294f2ad321
18 files changed
+248 -39

No files matched your search

+3 -1
View File
@@ -8,5 +8,7 @@ members = [
"examples/unbreakable",
"examples/dex",
"examples/babyre",
"examples/frida"
"examples/frida",
"examples/tracer",
"examples/ollvm"
]
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "ollvm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
radius2 = { path = "../../radius" }
+38
View File
@@ -0,0 +1,38 @@
use radius2::{Radius, State, Value, vc};
// output:
// FLAG: mirror_mirror_on_the_wall_whos_the_ugliest_handler_of_them_all?!
// target/release/ollvm 0.92s user 0.04s system 99% cpu 0.964 total
const HASHES:[u64; 8] = [
0x875cd4f2e18f8fc4, 0xbb093e17e5d3fa42, 0xada5dd034aae16b4, 0x97322728fea51225,
0x4124799d72188d0d, 0x2b3e3fbbb4d44981, 0xdfcac668321e4daa, 0xeac2137a35c8923a
];
fn main() {
let mut radius = Radius::new("tests/ollvm");
fn flag_hook(state: &mut State) -> bool {
state.registers.set("rax", state.context["flag"][0].clone());
true
}
radius.hook(0x00400899, flag_hook);
let main = radius.get_address("main").unwrap();
let mut state = radius.call_state(main);
radius.set_argv_env(&mut state, &[vc(0x58), vc(0x31)], &[]);
let flag = state.symbolic_value("flag", 64);
state.context.insert("flag".to_owned(), vec!(flag.clone()));
let mut end_state = radius.run(state, 1).unwrap();
let rcx = end_state.registers.get("rcx");
let mut solution = "".to_owned();
for hash in HASHES {
end_state.solver.push();
end_state.assert(&rcx.eq(&Value::Concrete(hash, 0)));
let reved = end_state.evaluate_string(&flag).unwrap();
solution += &reved.chars().rev().collect::<String>();
end_state.solver.pop();
}
println!("FLAG: {}", solution);
}
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "tracer"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
radius2 = { path = "../../radius" }
+35
View File
@@ -0,0 +1,35 @@
use radius2::{Radius, RadiusOption};
use radius2::state::StateStatus;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let options = [RadiusOption::Debug(true), RadiusOption::Sims(false)];
let mut radius = Radius::new_with_options(Some(args[1].to_owned()), &options);
let addr = radius.get_address(&args[2]).unwrap_or_default();
let mut state = radius.debug_state(addr, &args[3..]);
let mut prev = radius.r2api.cmd("dr").unwrap();
let mut cont = true;
while state.status == StateStatus::Active && cont {
for register in &state.registers.indexes {
let reg = &register.reg_info;
if reg.type_str == "gpr" || reg.type_str == "flg" {
let name = reg.name.to_owned();
let dbg_value = radius.r2api.get_register_value(&name).unwrap();
let rad_value = state.registers.get(&name).as_u64().unwrap();
if dbg_value != rad_value {
println!("{} {:x} {:x}", name, dbg_value, rad_value);
cont = reg.type_str == "flg"; // continue for flags
}
}
}
if cont {
prev = radius.r2api.cmd("dr;ds").unwrap();
radius.processor.step(&mut state);
} else {
println!("\nprevious:\n{}", prev);
println!("\ncurrent:\n{}", radius.r2api.cmd("dr").unwrap());
}
}
}
+1 -1
View File
@@ -16,4 +16,4 @@ pub mod value;
pub use crate::radius::{Radius, RadiusOption};
pub use crate::state::State;
pub use crate::value::Value;
pub use crate::value::{Value, vc};
+14 -3
View File
@@ -288,6 +288,18 @@ fn main() {
.multiple(true)
.help("Run r2 command on launch"),
)
.arg(
Arg::with_name("architecture")
.long("arch")
.takes_value(true)
.help("Set target arch (usually autodetected)"),
)
.arg(
Arg::with_name("bits")
.long("bits")
.takes_value(true)
.help("Set target bits (usually autodetected)"),
)
.arg(
Arg::with_name("evaluate")
.short("e")
@@ -451,7 +463,7 @@ fn main() {
if path.starts_with("frida:") {
radius.frida_state(addr)
} else if path.starts_with("gdb:") || path.starts_with("dbg:") {
radius.debug_state(addr)
radius.debug_state(addr, &[])
} else {
radius.call_state(addr)
}
@@ -540,7 +552,6 @@ fn main() {
} else {
&cons[2 * i + 1]
};
state.constrain_bytes_bv(bv, cons);
}
@@ -725,7 +736,7 @@ fn main() {
if do_json {
println!("{}", serde_json::to_string(&json_out).unwrap_or_default());
}
} else {
} else { // TODO this is temporary until I integrate a real testcase gen mode in processor
let mut pcs: HashMap<u64, usize> = HashMap::new();
let mut states = VecDeque::new();
let mut solutions: HashMap<Vec<u8>, usize> = HashMap::new();
+40 -2
View File
@@ -11,6 +11,7 @@ pub const SIZE: u64 = 64;
#[derive(Debug, Clone, PartialEq)]
pub enum Operations {
Trap,
Interrupt,
Syscall,
PcAddress,
If,
@@ -35,7 +36,9 @@ pub enum Operations {
Multiply,
LongMultiply,
Divide,
LongDivide,
Modulo,
LongModulo,
SignedDivide,
SignedModulo,
Not,
@@ -89,6 +92,8 @@ pub enum Operations {
PrintDebug, // Tool for cli hooks
Backtrace, // Tool for cli hooks
Constrain,
ConstraintPush,
ConstraintPop,
Terminate,
Discard,
@@ -113,7 +118,7 @@ impl Operations {
pub fn from_string(s: &str) -> Self {
match s {
"TRAP" => Operations::Trap,
"$" => Operations::Trap,
"$" => Operations::Interrupt,
"()" => Operations::Syscall,
"$$" => Operations::PcAddress, // this is whack
"?{" => Operations::If,
@@ -139,7 +144,9 @@ impl Operations {
"*" => Operations::Multiply,
"L*" => Operations::LongMultiply,
"/" => Operations::Divide,
"L/" => Operations::LongDivide,
"%" => Operations::Modulo,
"L%" => Operations::LongModulo,
"~/" => Operations::SignedDivide,
"~%" => Operations::SignedModulo,
"!" => Operations::Not,
@@ -201,6 +208,8 @@ impl Operations {
".." => Operations::PrintDebug,
"BT" => Operations::Backtrace,
"_" => Operations::Constrain,
"_+" => Operations::ConstraintPush,
"_-" => Operations::ConstraintPop,
"!!" => Operations::Terminate, // state.set_break()
"!_" => Operations::Discard, // state.set_inactive()
@@ -236,7 +245,14 @@ pub fn get_size(state: &mut State) -> u32 {
sz
}
#[inline]
pub fn pop_bv(state: &mut State, n: u32) -> Value {
let value = pop_value(state, false, false);
Value::Symbolic(state.solver.to_bv(&value, n), value.get_taint())
}
//#[inline]
pub fn pop_value(state: &mut State, set_size: bool, sign_ext: bool) -> Value {
let item = state.stack.pop().unwrap_or_default();
@@ -418,6 +434,7 @@ pub fn genmask(bits: u64) -> u64 {
pub fn do_operation(state: &mut State, operation: &Operations) {
match operation {
Operations::Trap => {}
Operations::Interrupt => {}
Operations::Syscall => {}
Operations::PcAddress => {
push_value(state, state.esil.prev_pc.clone());
@@ -545,6 +562,21 @@ pub fn do_operation(state: &mut State, operation: &Operations) {
}
}
}
Operations::LongDivide => {
let arg1 = pop_bv(state, 128);
let arg2 = pop_bv(state, 128);
let arg3 = pop_bv(state, 128);
push_value(state, ((arg2 << vc(64)) + arg1) / arg3);
}
Operations::LongModulo => {
let arg1 = pop_bv(state, 128);
let arg2 = pop_bv(state, 128);
let arg3 = pop_bv(state, 128);
//println!("{:?} {:?} {:?}", arg1, arg2, arg3);
push_value(state, ((arg2 << vc(64)) + arg1) % arg3);
}
Operations::Divide => {
binary_operation!(state, /);
}
@@ -900,6 +932,12 @@ pub fn do_operation(state: &mut State, operation: &Operations) {
let value = pop_value(state, false, false);
state.assert(&value);
}
Operations::ConstraintPush => {
state.solver.push();
}
Operations::ConstraintPop => {
state.solver.pop();
}
Operations::Terminate => {
state.set_break();
}
@@ -938,7 +976,7 @@ pub fn do_operation(state: &mut State, operation: &Operations) {
}
Operations::Parity => match &state.esil.current {
Value::Concrete(val, t) => {
let pf = Value::Concrete(!(val.count_ones() % 2) as u64, *t);
let pf = Value::Concrete(!((val & 0xff).count_ones() % 2) as u64, *t);
push_value(state, pf);
}
Value::Symbolic(_val, _t) => {
+10 -4
View File
@@ -40,15 +40,16 @@ pub enum RunMode {
Multiple,
}
pub type HookMethod = dyn Fn(&mut State) -> bool;
pub type HookMethod = fn(&mut State) -> bool;
#[derive(Clone)]
pub struct Processor {
pub instructions: BTreeMap<u64, InstructionEntry>,
pub hooks: HashMap<u64, Vec<Rc<HookMethod>>>,
pub hooks: HashMap<u64, Vec<HookMethod>>,
pub esil_hooks: HashMap<u64, Vec<String>>,
pub sims: HashMap<u64, SimMethod>,
pub traps: HashMap<u64, SimMethod>,
pub interrupts: HashMap<u64, SimMethod>,
pub syscalls: HashMap<u64, Syscall>,
pub breakpoints: HashSet<u64>,
pub mergepoints: HashSet<u64>,
@@ -100,6 +101,7 @@ impl Processor {
esil_hooks: HashMap::new(),
sims: HashMap::new(),
traps: HashMap::new(),
interrupts: HashMap::new(),
syscalls: HashMap::new(),
breakpoints: HashSet::new(),
mergepoints: HashSet::new(),
@@ -372,6 +374,9 @@ impl Processor {
state.esil.mode = ExecMode::Uncon;
break;
}
Operations::Interrupt => {
let _value = pop_value(state, false, false);
}
Operations::Trap => {
let trap = pop_concrete(state, false, false);
//let pc = state.registers.get_pc().as_u64().unwrap();
@@ -660,9 +665,9 @@ impl Processor {
return;
}
}
state.r2api.disassemble_bytes(pc_val, &data, 1).unwrap()
state.r2api.disassemble_bytes(pc_val, &data, 1).unwrap_or_default()
} else {
state.r2api.disassemble(pc_val, INSTR_NUM).unwrap()
state.r2api.disassemble(pc_val, INSTR_NUM).unwrap_or_default()
};
let mut prev: Option<u64> = None;
@@ -833,6 +838,7 @@ impl Processor {
let step = mode == RunMode::Step;
loop {
//println!("{} states", states.len());
if states.is_empty() {
if self.merges.is_empty() {
return results;
+16 -5
View File
@@ -793,7 +793,11 @@ impl R2Api {
// simple as that?
let ret = self.cmd("pae ret")?;
if ret.is_empty() {
Err("no ret instruction".to_owned())
if self.info.bin.arch == "bpf" {
Ok("8,sp,+=,sp,[8],pc,=".to_owned())
} else {
Err("no ret instruction".to_owned())
}
} else {
Ok(ret[0..ret.len() - 1].to_owned())
}
@@ -801,7 +805,7 @@ impl R2Api {
pub fn get_register_value(&mut self, reg: &str) -> R2Result<u64> {
let val = self.cmd(&format!("aer {}", reg))?;
Ok(u64::from_str_radix(&val[2..val.len() - 1], 16).unwrap())
Ok(u64::from_str_radix(&val[2..val.len() - 1], 16).unwrap_or_default())
}
pub fn set_register_value(&mut self, reg: &str, value: u64) {
@@ -847,12 +851,19 @@ impl R2Api {
match self.mode {
Mode::Debugger => self.cmd("dc"),
Mode::Frida => self.cmd(":dc"),
_ => Ok("idk".to_string()),
_ => self.cmd("aec"),
}
}
pub fn init_debug(&mut self, addr: u64) {
self.cmd(&format!("db {};dc", addr)).unwrap();
pub fn init_debug(&mut self, addr: u64, args: &[String]) {
(match self.mode {
Mode::Debugger => self.cmd(&format!("db {};dc", addr)),
Mode::Frida => panic!("can't enter debug from frida mode, also why would you?"),
_ => {
self.mode = Mode::Debugger;
self.cmd(&format!("doo {};db {};dc", args.join(" "), addr))
}
}).unwrap_or_default();
}
pub fn init_vm(&mut self) {
+30 -11
View File
@@ -7,7 +7,6 @@ use crate::sims::{get_sims, zero, SimMethod};
use crate::value::{vc, Value};
// use std::collections::VecDeque;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
@@ -213,11 +212,29 @@ impl Radius {
state
}
/**
* Initialized state at the provided function name with an initialized stack
* equivalent to `call_state(get_address(sym))`
*
* **Example**
*
* ```
* use radius2::radius::Radius;
* let mut radius = Radius::new("../tests/r100");
* let mut state = radius.callsym_state("sym.imp.printf");
* ```
*/
pub fn callsym_state<T: AsRef<str>>(&mut self, sym: T) -> State {
let addr = self.get_address(sym).unwrap_or_default();
self.call_state(addr)
}
/// Initialize state from a debugger breakpoint
/// the program will block until bp is hit
pub fn debug_state(&mut self, addr: u64) -> State {
self.r2api.seek(addr);
self.r2api.init_debug(addr);
pub fn debug_state(&mut self, addr: u64, args: &[String]) -> State {
// set cache to false to set breakpoint
self.r2api.set_option("io.cache", "false").unwrap();
self.r2api.init_debug(addr, args);
self.init_state()
}
@@ -249,8 +266,10 @@ impl Radius {
*/
pub fn entry_state(&mut self) -> State {
// get the entrypoint
let entry = self.r2api.get_entrypoints().unwrap()[0].vaddr;
self.r2api.seek(entry);
let entrypoints = self.r2api.get_entrypoints().unwrap_or_default();
if !entrypoints.is_empty() {
self.r2api.seek(entrypoints[0].vaddr);
}
self.r2api.init_vm();
let mut state = self.init_state();
state.memory.add_stack();
@@ -259,7 +278,7 @@ impl Radius {
let start_main_reloc = self.r2api.get_address("reloc.__libc_start_main").unwrap();
self.hook(start_main_reloc, Rc::new(__libc_start_main));
self.hook(start_main_reloc, __libc_start_main);
state
}
@@ -345,7 +364,7 @@ impl Radius {
}
/// Hook an address with a callback that is passed the `State`
pub fn hook(&mut self, addr: u64, hook_callback: Rc<HookMethod>) {
pub fn hook(&mut self, addr: u64, hook_callback: HookMethod) {
self.processor
.hooks
.entry(addr)
@@ -363,7 +382,7 @@ impl Radius {
}
/// Hook a symbol with a callback that is passed each state that reaches it
pub fn hook_symbol(&mut self, sym: &str, hook_callback: Rc<HookMethod>) {
pub fn hook_symbol(&mut self, sym: &str, hook_callback: HookMethod) {
let addr = self.get_address(sym).unwrap();
self.hook(addr, hook_callback);
}
@@ -548,8 +567,8 @@ impl Radius {
}
/// Get address of symbol
pub fn get_address(&mut self, symbol: &str) -> R2Result<u64> {
self.r2api.get_address(symbol)
pub fn get_address<T: AsRef<str>>(&mut self, symbol: T) -> R2Result<u64> {
self.r2api.get_address(symbol.as_ref())
}
/// Get all functions
+12 -6
View File
@@ -5,6 +5,7 @@ use crate::value::{Value, vc};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Bounds {
tstr: String,
start: u64,
end: u64,
size: u64,
@@ -53,6 +54,7 @@ impl Registers {
let mut bounds_map: HashMap<Bounds, usize> = HashMap::new();
for reg in reg_info.reg_info {
let mut bounds = Bounds {
tstr: reg.type_str.to_owned(),
start: reg.offset,
end: reg.offset.wrapping_add(reg.size),
size: reg.size,
@@ -61,7 +63,8 @@ impl Registers {
let old_bounds = bounds_map.keys();
let mut in_bounds = false;
for bound in old_bounds {
if bounds.start >= bound.start && bounds.end <= bound.end {
let inside = bounds.start >= bound.start && bounds.end <= bound.end;
if bound.tstr == bounds.tstr && inside {
in_bounds = true;
bounds = bound.clone();
}
@@ -144,7 +147,11 @@ impl Registers {
let t = r.reg.to_owned();
return self.set_value(self.regs[&t].index, value);
}
self.set_value(self.regs[alias].index, value)
// make set_with_alias more forgiving
if let Some(reg) = self.regs.get(alias) {
let creg = reg.clone();
self.set_value(creg.index, value);
}
}
/// Get the value of `PC`
@@ -178,7 +185,6 @@ impl Registers {
if register.reg_info.offset == -1i64 as u64 {
return Value::Concrete(0, 0); // this is a zero register
}
let value = &self.values[register.value_index];
if register.reg_info.size == register.bounds.size {
value.to_owned()
@@ -199,15 +205,15 @@ impl Registers {
let size = register.reg_info.size;
if size == register.bounds.size {
if size <= 64 {
self.values[register.value_index] = value;
self.values[register.value_index] = value.slice(size - 1, 0);
} else {
// need to prevent size > 64 from becoming a u64
let bv = self.solver.to_bv(&value, size as u32);
let v = Value::Symbolic(bv, value.get_taint());
self.values[register.value_index] = v;
}
//} else if self.clear_upper && size == 32 { // this sux
// self.values[register.value_index] = value.uext(&vc(32));
} else if size == 32 { // this sux
self.values[register.value_index] = value.slice(size - 1, 0).uext(&vc(32));
} else {
let bound_size = register.bounds.size as u32;
let offset = register.reg_info.offset - register.bounds.start;
+11
View File
@@ -532,6 +532,11 @@ pub fn strtol(state: &mut State, args: &[Value]) -> Value {
strtoll(state, args).slice(bits - 1, 0)
}
pub fn strtoul(state: &mut State, args: &[Value]) -> Value {
let bits = state.memory.bits;
strtoll(state, args).slice(bits - 1, 0)
}
pub fn itoa(state: &mut State, args: &[Value]) -> Value {
format::itoa_helper(state, &args[0], &args[1], &args[2], true, 32)
}
@@ -793,6 +798,12 @@ pub fn sleep(_state: &mut State, _args: &[Value]) -> Value {
vc(0)
}
pub fn __errno_location(state: &mut State, _args: &[Value]) -> Value {
let addr = state.memory_alloc(&vc(8));
state.memory_write_value(&addr, &vc(0), 8);
addr
}
pub fn open(state: &mut State, args: &[Value]) -> Value {
syscall::open(state, args)
}
+3 -1
View File
@@ -118,7 +118,8 @@ pub fn get_sims() -> Vec<Sim> {
make_sim("atol", libc::atoi, 1),
make_sim("itoa", libc::itoa, 3),
make_sim("strtol", libc::strtol, 3),
make_sim("strtoll", libc::strtol, 3),
make_sim("strtoul", libc::strtoul, 3),
make_sim("strtoll", libc::strtoll, 3),
make_sim("strtod", libc::strtol, 3),
make_sim("islower", libc::islower, 1),
make_sim("isupper", libc::isupper, 1),
@@ -164,5 +165,6 @@ pub fn get_sims() -> Vec<Sim> {
make_sim("__stack_chk_fail", ret, 0),
make_sim("__libc_start_main", libc::__libc_start_main, 5),
make_sim("__cfi_slowpath", zero, 0),
make_sim("__errno_location", libc::__errno_location, 0),
]
}
+7 -1
View File
@@ -72,7 +72,13 @@ impl Solver {
#[inline]
pub fn bvv(&self, v: u64, n: u32) -> BitVec {
BV::from_u64(self.btor.clone(), v, n)
if n <= 64 {
// boolector sign extends. even though its u64...
// what the fuck
BV::from_u64(self.btor.clone(), v, n)
} else {
BV::from_u64(self.btor.clone(), v, 64).uext(n-64)
}
}
pub fn translate(&self, bv: &BitVec) -> Option<BitVec> {
+10 -4
View File
@@ -620,7 +620,8 @@ impl Value {
}
(Value::Symbolic(a, t1), Value::Concrete(b, t2)) => {
//let bv = make_bv(&a, b, a.get_width());
Value::Symbolic(a.slice(*b as u32 - 1, 0).uext(64 - *b as u32), *t1 | *t2)
let bits = if *b <= 64 { *b } else { 64 };
Value::Symbolic(a.slice(*b as u32 - 1, 0).uext(64 - bits as u32), *t1 | *t2)
}
(Value::Concrete(a, t), Value::Symbolic(_b, _t)) => {
// uh hopefully this doesnt happen
@@ -643,7 +644,7 @@ impl Value {
}
(Value::Symbolic(a, t1), Value::Concrete(b, t2)) => {
//let bv = make_bv(&a, b, a.get_width());
Value::Symbolic(a.slice(*b as u32 - 1, 0).sext(64 - *b as u32), *t1 | *t2)
Value::Symbolic(a.slice(*b as u32 - 1, 0).sext(64 - (*b % 64) as u32), *t1 | *t2)
}
(Value::Concrete(a, t), Value::Symbolic(_b, _t)) => {
// uh hopefully this doesnt happen
@@ -661,7 +662,8 @@ impl Value {
pub fn slice(&self, high: u64, low: u64) -> Value {
match self {
Value::Concrete(a, t) => {
Value::Concrete((*a >> low) & ((1 << (high - low + 1)) - 1), *t)
let mask = if (high - low) < 63 { (1 << (high - low + 1)) - 1 } else { -1i64 as u64 };
Value::Concrete((*a >> low) & mask, *t)
}
Value::Symbolic(a, t) => Value::Symbolic(a.slice(high as u32, low as u32), *t),
}
@@ -737,7 +739,11 @@ impl Value {
#[inline]
pub fn is_symbolic(&self) -> bool {
matches!(self, Value::Symbolic(_, _))
if let Value::Symbolic(s, _) = self {
!s.is_const()
} else {
false
}
}
#[inline]
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.