From 07ae17aeccaf2fd41c9b872b2547427385cc7a46 Mon Sep 17 00:00:00 2001 From: Vincent Thiberville Date: Tue, 30 Apr 2024 01:53:51 +0200 Subject: [PATCH] feat: add ability to set module data in scan callback Some modules in YARA need to be fed data to be usable, notably the cuckoo module. This works by setting the module data in the "import module" callback, as can be seen here: This MR adds bindings to be able to do exactly this: the object related to this callback msg is wrapped in a YrModuleImport object, which exposes two functions: - one to retrieve the module name - one to set the module data This makes the code looks like this: ```rust let res = yara_scanner.scan_mem_callback(b"", |msg| { if let yara::CallbackMsg::ImportModule(mut module) = msg { if module.name() == Some(b"cuckoo") { // Safety: report is alive for longer than the scan. unsafe { module.set_module_data( report.as_mut_ptr().cast(), report.len() as u64, ); } } } yara::CallbackReturn::Continue }); ``` I haven't added a test for it, because the only module that uses this is the cuckoo module, and to use it, the module-cuckoo feature must be enabled and the libjansson-dev needs to be installed. If you prefer to have a test, I can try to update the CI to have a test like this working. --- src/internals/mod.rs | 2 + src/internals/module_import.rs | 45 ++++++++++++++++ src/internals/scan.rs | 7 ++- .../yara-4.5.0-x86_64-apple-darwin.rs | 52 +++++++++++++++++++ .../yara-4.5.0-x86_64-pc-windows-gnu.rs | 52 +++++++++++++++++++ .../yara-4.5.0-x86_64-pc-windows-msvc.rs | 52 +++++++++++++++++++ .../yara-4.5.0-x86_64-unknown-linux-gnu.rs | 52 +++++++++++++++++++ .../yara-4.5.0-x86_64-unknown-linux-musl.rs | 52 +++++++++++++++++++ yara-sys/build.rs | 1 + 9 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 src/internals/module_import.rs diff --git a/src/internals/mod.rs b/src/internals/mod.rs index a7fbc9c..c0230cd 100644 --- a/src/internals/mod.rs +++ b/src/internals/mod.rs @@ -6,6 +6,7 @@ use crate::errors::*; pub use self::compiler::*; pub use self::iterator::*; +pub use self::module_import::*; pub use self::object::*; pub use self::rules::*; pub use self::scan::*; @@ -17,6 +18,7 @@ pub mod string; mod compiler; pub mod configuration; mod iterator; +mod module_import; mod object; mod rules; mod scan; diff --git a/src/internals/module_import.rs b/src/internals/module_import.rs new file mode 100644 index 0000000..0a4d364 --- /dev/null +++ b/src/internals/module_import.rs @@ -0,0 +1,45 @@ +use std::ffi::{c_void, CStr}; +use std::fmt::Debug; + +/// Details about a module being imported. +pub struct YrModuleImport<'a>(&'a mut yara_sys::YR_MODULE_IMPORT); + +impl Debug for YrModuleImport<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt("YrModuleImport", f) + } +} + +impl<'a> From<&'a mut yara_sys::YR_MODULE_IMPORT> for YrModuleImport<'a> { + fn from(value: &'a mut yara_sys::YR_MODULE_IMPORT) -> Self { + Self(value) + } +} + +impl YrModuleImport<'_> { + /// Get the name of the module. + pub fn name(&self) -> Option<&[u8]> { + let ptr = self.0.module_name; + if ptr.is_null() { + None + } else { + // Safety: + // - ptr is not null, and is guaranteed by libyara to be nul-terminated + // - returned slice is valid for as long as self, guaranteeing the ptr to stay valid. + let cstr = unsafe { CStr::from_ptr(ptr) }; + Some(cstr.to_bytes()) + } + } + + /// Set the module data to be used by the module. + /// + /// # Safety + /// + /// The caller must guarantee that: + /// - `ptr` is valid for reads of `size` bytes. + /// - `ptr` stays valid for the full duration of the scan. + pub unsafe fn set_module_data(&mut self, ptr: *mut c_void, size: u64) { + self.0.module_data = ptr; + self.0.module_data_size = size; + } +} diff --git a/src/internals/scan.rs b/src/internals/scan.rs index 5479fd9..a22881f 100644 --- a/src/internals/scan.rs +++ b/src/internals/scan.rs @@ -13,7 +13,7 @@ use crate::{Rule, YrString}; pub enum CallbackMsg<'r> { RuleMatching(Rule<'r>), RuleNotMatching(Rule<'r>), - ImportModule, + ImportModule(YrModuleImport<'r>), ModuleImported(YrObject<'r>), TooManyMatches(YrString<'r>), ScanFinished, @@ -40,7 +40,10 @@ impl<'r> CallbackMsg<'r> { let context = unsafe { &*context }; RuleNotMatching(Rule::from((context, rule))) } - yara_sys::CALLBACK_MSG_IMPORT_MODULE => ImportModule, + yara_sys::CALLBACK_MSG_IMPORT_MODULE => { + let object = unsafe { &mut *(message_data as *mut yara_sys::YR_MODULE_IMPORT) }; + ImportModule(YrModuleImport::from(object)) + } yara_sys::CALLBACK_MSG_MODULE_IMPORTED => { let object = unsafe { &*(message_data as *mut yara_sys::YR_OBJECT) }; ModuleImported(YrObject::from(object)) diff --git a/yara-sys/bindings/yara-4.5.0-x86_64-apple-darwin.rs b/yara-sys/bindings/yara-4.5.0-x86_64-apple-darwin.rs index 2f00fe1..7159823 100644 --- a/yara-sys/bindings/yara-4.5.0-x86_64-apple-darwin.rs +++ b/yara-sys/bindings/yara-4.5.0-x86_64-apple-darwin.rs @@ -5527,3 +5527,55 @@ extern "C" { rules: *mut *mut YR_RULES, ) -> ::std::os::raw::c_int; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct YR_MODULE_IMPORT { + pub module_name: *const ::std::os::raw::c_char, + pub module_data: *mut ::std::os::raw::c_void, + pub module_data_size: size_t, +} +#[test] +fn bindgen_test_layout_YR_MODULE_IMPORT() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_name) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_name) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data_size) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data_size) + ) + ); +} diff --git a/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-gnu.rs b/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-gnu.rs index 3ab8299..902fa0d 100644 --- a/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-gnu.rs +++ b/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-gnu.rs @@ -8134,3 +8134,55 @@ extern "C" { rules: *mut *mut YR_RULES, ) -> ::std::os::raw::c_int; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct YR_MODULE_IMPORT { + pub module_name: *const ::std::os::raw::c_char, + pub module_data: *mut ::std::os::raw::c_void, + pub module_data_size: size_t, +} +#[test] +fn bindgen_test_layout_YR_MODULE_IMPORT() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_name) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_name) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data_size) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data_size) + ) + ); +} diff --git a/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-msvc.rs b/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-msvc.rs index 3ab8299..902fa0d 100644 --- a/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-msvc.rs +++ b/yara-sys/bindings/yara-4.5.0-x86_64-pc-windows-msvc.rs @@ -8134,3 +8134,55 @@ extern "C" { rules: *mut *mut YR_RULES, ) -> ::std::os::raw::c_int; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct YR_MODULE_IMPORT { + pub module_name: *const ::std::os::raw::c_char, + pub module_data: *mut ::std::os::raw::c_void, + pub module_data_size: size_t, +} +#[test] +fn bindgen_test_layout_YR_MODULE_IMPORT() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_name) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_name) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data_size) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data_size) + ) + ); +} diff --git a/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-gnu.rs b/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-gnu.rs index 51f1320..393e13b 100644 --- a/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-gnu.rs +++ b/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-gnu.rs @@ -5719,3 +5719,55 @@ extern "C" { rules: *mut *mut YR_RULES, ) -> ::std::os::raw::c_int; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct YR_MODULE_IMPORT { + pub module_name: *const ::std::os::raw::c_char, + pub module_data: *mut ::std::os::raw::c_void, + pub module_data_size: size_t, +} +#[test] +fn bindgen_test_layout_YR_MODULE_IMPORT() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_name) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_name) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data_size) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data_size) + ) + ); +} diff --git a/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-musl.rs b/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-musl.rs index 51f1320..393e13b 100644 --- a/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-musl.rs +++ b/yara-sys/bindings/yara-4.5.0-x86_64-unknown-linux-musl.rs @@ -5719,3 +5719,55 @@ extern "C" { rules: *mut *mut YR_RULES, ) -> ::std::os::raw::c_int; } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct YR_MODULE_IMPORT { + pub module_name: *const ::std::os::raw::c_char, + pub module_data: *mut ::std::os::raw::c_void, + pub module_data_size: size_t, +} +#[test] +fn bindgen_test_layout_YR_MODULE_IMPORT() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 24usize, + concat!("Size of: ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(YR_MODULE_IMPORT)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_name) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_name) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).module_data_size) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(YR_MODULE_IMPORT), + "::", + stringify!(module_data_size) + ) + ); +} diff --git a/yara-sys/build.rs b/yara-sys/build.rs index 709042a..cc025d1 100644 --- a/yara-sys/build.rs +++ b/yara-sys/build.rs @@ -431,6 +431,7 @@ mod bindings { .allowlist_type("YR_AC_AUTOMATON") .allowlist_type("YR_AC_MATCH") .allowlist_type("YR_ATOMS_CONFIG") + .allowlist_type("YR_MODULE_IMPORT") .opaque_type("YR_AC_.*") .opaque_type("YR_ATOMS_CONFIG") .opaque_type("YR_FIXUP")