use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::sync::Arc;
use crate::memory::{Memory, Region};
use crate::regex::Regex;
mod console;
pub use console::{Console, ConsoleData, LogCallback};
mod time;
pub use time::Time;
#[allow(clippy::cast_precision_loss)]
mod math;
pub use math::Math;
mod string;
pub use string::String_;
#[cfg(feature = "hash")]
mod hash;
#[cfg(feature = "hash")]
pub use hash::Hash;
#[cfg(feature = "object")]
mod dotnet;
#[cfg(feature = "object")]
pub use dotnet::Dotnet;
#[cfg(feature = "object")]
pub(crate) mod elf;
#[cfg(feature = "object")]
pub use elf::Elf;
#[cfg(feature = "object")]
mod macho;
#[cfg(feature = "object")]
pub use macho::MachO;
#[cfg(feature = "object")]
mod pe;
#[cfg(feature = "object")]
pub use pe::{Pe, PeData};
#[cfg(feature = "object")]
mod dex;
#[cfg(feature = "object")]
pub use dex::Dex;
#[cfg(feature = "magic")]
mod magic;
#[cfg(feature = "magic")]
pub use self::magic::Magic;
#[cfg(feature = "cuckoo")]
mod cuckoo;
#[cfg(feature = "cuckoo")]
pub use self::cuckoo::{Cuckoo, CuckooData};
pub trait Module: Send + Sync + UnwindSafe + RefUnwindSafe {
fn get_name(&self) -> &'static str;
fn get_static_values(&self) -> HashMap<&'static str, StaticValue>;
fn get_dynamic_types(&self) -> HashMap<&'static str, Type> {
HashMap::new()
}
fn setup_new_scan(&self, data_map: &mut ModuleDataMap) {
let _ = data_map;
}
fn get_dynamic_values(
&self,
_ctx: &mut ScanContext,
_values: &mut HashMap<&'static str, Value>,
) {
}
}
impl std::fmt::Debug for Box<dyn Module> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Module")
.field("name", &self.get_name())
.finish()
}
}
pub struct ScanContext<'a, 'b, 'c> {
pub region: &'a Region<'b>,
pub module_data: &'a mut ModuleDataMap<'c>,
pub process_memory: bool,
}
impl std::fmt::Debug for ScanContext<'_, '_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScanContext").finish()
}
}
pub struct EvalContext<'a, 'b, 'c> {
pub mem: &'b mut Memory<'a>,
pub module_data: &'b ModuleDataMap<'c>,
pub process_memory: bool,
}
impl std::fmt::Debug for EvalContext<'_, '_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EvalContext").finish()
}
}
#[doc(hidden)]
#[derive(Default, Clone)]
pub struct ModuleUserData(
pub HashMap<TypeId, Arc<dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe>>,
);
impl std::fmt::Debug for ModuleUserData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ModuleUserData").finish()
}
}
pub struct ModuleDataMap<'scanner> {
private_data: HashMap<TypeId, Box<dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe>>,
user_data: &'scanner ModuleUserData,
}
impl std::fmt::Debug for ModuleDataMap<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ModuleDataMap").finish()
}
}
pub trait ModuleData: Module {
type PrivateData: Any + Send + Sync + UnwindSafe + RefUnwindSafe;
type UserData: Any + Send + Sync + UnwindSafe + RefUnwindSafe;
}
impl<'scanner> ModuleDataMap<'scanner> {
#[doc(hidden)]
#[must_use]
pub fn new(user_data: &'scanner ModuleUserData) -> Self {
Self {
private_data: HashMap::new(),
user_data,
}
}
pub fn insert<T: Module + ModuleData + 'static>(&mut self, data: T::PrivateData) {
let _r = self.private_data.insert(TypeId::of::<T>(), Box::new(data));
}
#[must_use]
pub fn get<T: Module + ModuleData + 'static>(&self) -> Option<&T::PrivateData> {
self.private_data.get(&TypeId::of::<T>()).and_then(|v| {
<dyn Any>::downcast_ref(&**v)
})
}
#[must_use]
pub fn get_mut<T: Module + ModuleData + 'static>(&mut self) -> Option<&mut T::PrivateData> {
self.private_data.get_mut(&TypeId::of::<T>()).and_then(|v| {
<dyn Any>::downcast_mut(&mut **v)
})
}
#[must_use]
pub fn get_user_data<T: Module + ModuleData + 'static>(&self) -> Option<&T::UserData> {
self.user_data.0.get(&TypeId::of::<T>()).and_then(|v| {
<dyn Any>::downcast_ref(&**v)
})
}
}
pub(crate) fn add_default_modules<F: FnMut(Box<dyn Module>)>(mut cb: F) {
cb(Box::new(Time));
cb(Box::new(Math));
cb(Box::new(String_));
#[cfg(feature = "hash")]
cb(Box::new(Hash));
#[cfg(feature = "object")]
cb(Box::new(Pe));
#[cfg(feature = "object")]
cb(Box::new(Elf));
#[cfg(feature = "object")]
cb(Box::new(MachO));
#[cfg(feature = "object")]
cb(Box::new(Dotnet));
#[cfg(feature = "object")]
cb(Box::new(Dex));
#[cfg(feature = "magic")]
cb(Box::new(Magic));
#[cfg(feature = "cuckoo")]
cb(Box::new(Cuckoo));
}
#[derive(Clone)]
pub enum Value {
Integer(i64),
Float(f64),
Bytes(Vec<u8>),
Regex(Regex),
Boolean(bool),
Object(HashMap<&'static str, Value>),
Array(Vec<Value>),
Dictionary(HashMap<Vec<u8>, Value>),
Function(
#[allow(clippy::type_complexity)]
Arc<dyn Fn(&mut EvalContext, Vec<Value>) -> Option<Value> + Send + Sync>,
),
Undefined,
}
impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(arg0) => f.debug_tuple("Integer").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Bytes(arg0) => {
let mut t = f.debug_tuple("Bytes");
match std::str::from_utf8(arg0) {
Ok(v) => t.field(&v).finish(),
Err(_) => t.field(arg0).finish(),
}
}
Self::Regex(arg0) => f.debug_tuple("Regex").field(arg0).finish(),
Self::Boolean(arg0) => f.debug_tuple("Boolean").field(arg0).finish(),
Self::Object(arg0) => f.debug_tuple("Object").field(arg0).finish(),
Self::Array(arg0) => f.debug_tuple("Array").field(arg0).finish(),
Self::Dictionary(arg0) => f.debug_tuple("Dictionary").field(arg0).finish(),
Self::Function(_) => f.debug_struct("Function").finish(),
Self::Undefined => write!(f, "Undefined"),
}
}
}
pub(crate) type StaticFunction = fn(&mut EvalContext, Vec<Value>) -> Option<Value>;
#[derive(Clone)]
pub enum StaticValue {
Integer(i64),
Float(f64),
Bytes(Vec<u8>),
Boolean(bool),
Object(HashMap<&'static str, StaticValue>),
Function {
fun: StaticFunction,
arguments_types: Vec<Vec<Type>>,
return_type: Type,
},
}
impl std::fmt::Debug for StaticValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(arg0) => f.debug_tuple("Integer").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Bytes(arg0) => f.debug_tuple("Bytes").field(arg0).finish(),
Self::Boolean(arg0) => f.debug_tuple("Boolean").field(arg0).finish(),
Self::Object(arg0) => f.debug_tuple("Object").field(arg0).finish(),
Self::Function {
fun,
arguments_types,
return_type,
} => f
.debug_struct("Function")
.field("fun", &(*fun as usize))
.field("arguments_types", arguments_types)
.field("return_type", return_type)
.finish(),
}
}
}
impl Value {
pub fn bytes<T: Into<Vec<u8>>>(v: T) -> Self {
Self::Bytes(v.into())
}
#[must_use]
pub fn object<const N: usize>(v: [(&'static str, Value); N]) -> Self {
Self::Object(v.into())
}
pub fn function<F>(f: F) -> Self
where
F: Fn(&mut EvalContext, Vec<Value>) -> Option<Value> + Send + Sync + 'static,
{
Self::Function(Arc::new(f))
}
}
impl StaticValue {
pub fn bytes<T: Into<Vec<u8>>>(v: T) -> Self {
Self::Bytes(v.into())
}
#[must_use]
pub fn object<const N: usize>(v: [(&'static str, StaticValue); N]) -> Self {
Self::Object(v.into())
}
pub fn function(
fun: fn(&mut EvalContext, Vec<Value>) -> Option<Value>,
arguments_types: Vec<Vec<Type>>,
return_type: Type,
) -> Self {
Self::Function {
fun,
arguments_types,
return_type,
}
}
}
#[derive(Clone, Debug)]
pub enum Type {
Integer,
Float,
Bytes,
Regex,
Boolean,
Object(HashMap<&'static str, Type>),
Array {
value_type: Box<Type>,
},
Dictionary {
value_type: Box<Type>,
},
Function {
arguments_types: Vec<Vec<Type>>,
return_type: Box<Type>,
},
}
impl Type {
#[must_use]
pub fn object<const N: usize>(v: [(&'static str, Type); N]) -> Self {
Self::Object(v.into())
}
#[must_use]
pub fn array(value_type: Type) -> Self {
Self::Array {
value_type: Box::new(value_type),
}
}
#[must_use]
pub fn dict(value_type: Type) -> Self {
Self::Dictionary {
value_type: Box::new(value_type),
}
}
#[must_use]
pub fn function(arguments_types: Vec<Vec<Type>>, return_type: Type) -> Self {
Self::Function {
arguments_types,
return_type: Box::new(return_type),
}
}
}
pub struct ValueTryFromError;
macro_rules! try_from_value {
($ty:ty, $name:ident) => {
impl TryFrom<Value> for $ty {
type Error = ValueTryFromError;
fn try_from(value: Value) -> Result<$ty, Self::Error> {
match value {
Value::$name(v) => Ok(v),
_ => Err(ValueTryFromError),
}
}
}
};
}
try_from_value!(i64, Integer);
try_from_value!(f64, Float);
try_from_value!(Vec<u8>, Bytes);
try_from_value!(Regex, Regex);
try_from_value!(bool, Boolean);
macro_rules! from_prim {
($ty:ty, $name:ident) => {
impl From<$ty> for Value {
fn from(v: $ty) -> Value {
Value::$name(v.into())
}
}
};
}
from_prim!(i64, Integer);
from_prim!(u32, Integer);
from_prim!(i32, Integer);
from_prim!(u16, Integer);
from_prim!(i16, Integer);
from_prim!(u8, Integer);
from_prim!(i8, Integer);
from_prim!(f64, Float);
from_prim!(Vec<u8>, Bytes);
from_prim!(Regex, Regex);
from_prim!(bool, Boolean);
macro_rules! from_big_integer {
($ty:ty) => {
impl From<$ty> for Value {
fn from(v: $ty) -> Value {
v.try_into().map_or(Value::Undefined, Value::Integer)
}
}
};
}
from_big_integer!(u64);
from_big_integer!(usize);
impl<T> From<Option<T>> for Value
where
Value: From<T>,
{
fn from(v: Option<T>) -> Value {
v.map_or(Value::Undefined, Value::from)
}
}
fn hex_encode<T: AsRef<[u8]>>(v: T) -> Vec<u8> {
hex_encode_inner(v.as_ref())
}
fn hex_encode_inner(v: &[u8]) -> Vec<u8> {
const DICT: &[u8] = b"0123456789abcdef";
v.iter()
.flat_map(|b| {
[
DICT[usize::from((*b & 0xF0) >> 4)],
DICT[usize::from(*b & 0x0F)],
]
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::{test_type_traits, test_type_traits_non_clonable};
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_fun(_ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
drop(args);
None
}
#[test]
fn test_types_traits() {
test_type_traits(ModuleUserData::default());
test_type_traits_non_clonable(ScanContext {
region: &Region { start: 0, mem: b"" },
module_data: &mut ModuleDataMap::new(&ModuleUserData::default()),
process_memory: false,
});
test_type_traits_non_clonable(EvalContext {
mem: &mut Memory::Direct(b""),
module_data: &ModuleDataMap::new(&ModuleUserData::default()),
process_memory: false,
});
test_type_traits(Value::Integer(0));
test_type_traits(StaticValue::Integer(0));
test_type_traits(Type::Integer);
test_type_traits_non_clonable(Time);
test_type_traits_non_clonable(Math);
test_type_traits_non_clonable(String_);
#[cfg(feature = "hash")]
test_type_traits_non_clonable(Hash);
#[cfg(feature = "object")]
{
test_type_traits_non_clonable(Elf);
test_type_traits_non_clonable(MachO);
test_type_traits_non_clonable(Pe);
}
assert_eq!(format!("{:?}", Value::Integer(0)), "Integer(0)");
assert_eq!(format!("{:?}", Value::Float(0.0)), "Float(0.0)");
assert_eq!(format!("{:?}", Value::Bytes(Vec::new())), "Bytes(\"\")");
assert_eq!(format!("{:?}", Value::Bytes(vec![255])), "Bytes([255])");
assert!(format!(
"{:?}",
Value::Regex(Regex::from_string(String::new(), false, false).unwrap())
)
.starts_with("Regex("),);
assert_eq!(format!("{:?}", Value::Boolean(true)), "Boolean(true)");
assert_eq!(format!("{:?}", Value::Object(HashMap::new())), "Object({})");
assert_eq!(format!("{:?}", Value::Array(Vec::new())), "Array([])");
assert_eq!(
format!("{:?}", Value::Dictionary(HashMap::new())),
"Dictionary({})"
);
assert!(format!("{:?}", Value::function(test_fun)).starts_with("Function"));
assert_eq!(format!("{:?}", StaticValue::Integer(0)), "Integer(0)");
assert_eq!(format!("{:?}", StaticValue::Float(0.0)), "Float(0.0)");
assert_eq!(format!("{:?}", StaticValue::Bytes(Vec::new())), "Bytes([])");
assert_eq!(format!("{:?}", StaticValue::Bytes(vec![2])), "Bytes([2])");
assert_eq!(format!("{:?}", StaticValue::Boolean(true)), "Boolean(true)");
assert_eq!(
format!("{:?}", StaticValue::Object(HashMap::new())),
"Object({})"
);
assert!(format!(
"{:?}",
StaticValue::Function {
fun: test_fun,
arguments_types: Vec::new(),
return_type: Type::Boolean
}
)
.starts_with("Function"));
}
}