Skip to main content

yara_x/modules/
mod.rs

1use protobuf::MessageDyn;
2use protobuf::reflect::MessageDescriptor;
3
4use thiserror::Error;
5
6pub mod protos {
7    #[cfg(feature = "generate-proto-code")]
8    include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
9
10    #[cfg(not(feature = "generate-proto-code"))]
11    include!("protos/generated/mod.rs");
12}
13
14#[cfg(test)]
15mod tests;
16
17pub(crate) mod field_docs;
18
19include!("modules.rs");
20
21/// Enum describing errors occurred in modules.
22#[derive(Error, Debug)]
23#[non_exhaustive]
24pub enum ModuleError {
25    /// Invalid format of module metadata.
26    #[error("invalid metadata: {err}")]
27    MetadataError {
28        /// The error that actually occurred.
29        err: String,
30    },
31    /// Error occurred when processing the input data.
32    #[error("internal error: {err}")]
33    InternalError {
34        /// The error that actually occurred.
35        err: String,
36    },
37}
38
39/// The trait implemented by all registered modules.
40pub trait RegisteredModule: Send + Sync {
41    /// Name used for the module in `import` statements (e.g. `"my_module"`).
42    fn name(&self) -> &'static str;
43
44    /// Returns the descriptor of the protobuf message that defines the
45    /// module's root structure.
46    fn root_descriptor(&self) -> MessageDescriptor;
47
48    /// Main function called every time YARA scans some data, before
49    /// evaluating the rules. Set to `None` for data-only modules.
50    fn main_fn(
51        &self,
52        data: &[u8],
53        meta: Option<&[u8]>,
54    ) -> Option<Result<Box<dyn MessageDyn>, ModuleError>>;
55
56    /// Rust module path of the submodule inside the external crate that
57    /// contains functions registered with `#[module_export(yara_x_crate = ...)]`.
58    ///
59    /// Must match the value that `module_path!()` expands to at those
60    /// functions' definition site (e.g. `"my_crate::my_mod"`). Set to
61    /// `None` for data-only modules that export no callable functions.
62    fn rust_module_name(&self) -> Option<&'static str>;
63}
64
65/// Main function in a YARA module.
66pub type ModuleMainFn<T> = fn(&[u8], Option<&[u8]>) -> Result<T, ModuleError>;
67
68/// Description of a YARA module, generic over the type `T` returned by the
69/// main function.
70pub struct Module<T>
71where
72    T: protobuf::MessageFull + 'static,
73{
74    /// Name used for the module in `import` statements (e.g. `"my_module"`).
75    pub name: &'static str,
76    /// Main function called every time YARA scans some data, before
77    /// evaluating the rules. Set to `None` for data-only modules.
78    pub main_fn: Option<ModuleMainFn<T>>,
79    /// Rust module path of the submodule inside the external crate that
80    /// contains functions registered with `#[module_export(yara_x_crate = ...)]`.
81    pub rust_module_name: Option<&'static str>,
82}
83
84impl<T> RegisteredModule for Module<T>
85where
86    T: protobuf::MessageFull + 'static,
87{
88    fn name(&self) -> &'static str {
89        self.name
90    }
91
92    fn root_descriptor(&self) -> MessageDescriptor {
93        T::descriptor()
94    }
95
96    fn main_fn(
97        &self,
98        data: &[u8],
99        meta: Option<&[u8]>,
100    ) -> Option<Result<Box<dyn MessageDyn>, ModuleError>> {
101        self.main_fn.map(|f| {
102            f(data, meta).map(|ok| Box::new(ok) as Box<dyn MessageDyn>)
103        })
104    }
105
106    fn rust_module_name(&self) -> Option<&'static str> {
107        self.rust_module_name
108    }
109}
110
111/// Macro used to register a YARA module.
112///
113/// # Examples
114///
115/// Registering a module with a main function:
116///
117/// ```ignore
118/// register_module!("my_module", MyModuleProto, main);
119/// ```
120///
121/// Registering a data-only module with no main function:
122///
123/// ```ignore
124/// register_module!("my_module", MyModuleProto);
125/// ```
126#[macro_export]
127macro_rules! register_module {
128    ($name:literal, $root_message:ty, $main_fn:path) => {
129        $crate::mods::prelude::inventory::submit! {
130            &$crate::mods::prelude::Module::<$root_message> {
131                name: $name,
132                main_fn: Some($main_fn),
133                rust_module_name: Some(module_path!()),
134            } as &dyn $crate::mods::prelude::RegisteredModule
135        }
136    };
137    ($name:literal, $root_message:ty) => {
138        $crate::mods::prelude::inventory::submit! {
139            &$crate::mods::prelude::Module::<$root_message> {
140                name: $name,
141                main_fn: None,
142                rust_module_name: None,
143            } as &dyn $crate::mods::prelude::RegisteredModule
144        }
145    };
146}
147
148inventory::collect!(&'static dyn RegisteredModule);
149
150/// Returns an iterator over all registered modules.
151#[inline]
152pub(crate) fn registered_modules()
153-> impl Iterator<Item = &'static dyn RegisteredModule> {
154    inventory::iter::<&'static dyn RegisteredModule>().copied()
155}
156
157pub mod mods {
158    /*! Utility functions and structures that allow invoking YARA modules directly.
159
160    The utility functions [`invoke`], [`invoke_dyn`] and [`invoke_all`]
161    allow leveraging YARA modules for parsing some file formats independently
162    of any YARA rule. With these functions you can pass arbitrary data to a
163    YARA module and obtain the same data structure that is accessible to YARA
164    rules and which you use in your rule conditions.
165
166    This allows external projects to benefit from YARA's file-parsing
167    capabilities for their own purposes.
168
169    # Example
170
171    ```rust
172    # use yara_x;
173    let pe_info = yara_x::mods::invoke::<yara_x::mods::PE>(&[]);
174    ```
175    */
176
177    /// Data structures defined by the `crx` module.
178    ///
179    /// The main structure produced by the module is [`crx::Crx`]. The rest
180    /// of them are used by one or more fields in the main structure.
181    ///
182    pub use super::protos::crx;
183    /// Data structure returned by the `crx` module.
184    pub use super::protos::crx::Crx;
185    /// Data structures defined by the `dex` module.
186    ///
187    /// The main structure produced by the module is [`dex::Dex`]. The rest
188    /// of them are used by one or more fields in the main structure.
189    ///
190    pub use super::protos::dex;
191    /// Data structure returned by the `dex` module.
192    pub use super::protos::dex::Dex;
193    /// Data structures defined by the `dotnet` module.
194    ///
195    /// The main structure produced by the module is [`dotnet::Dotnet`]. The
196    /// rest of them are used by one or more fields in the main structure.
197    ///
198    pub use super::protos::dotnet;
199    /// Data structure returned by the `dotnet` module.
200    pub use super::protos::dotnet::Dotnet;
201    /// Data structures defined by the `elf` module.
202    ///
203    /// The main structure produced by the module is [`elf::ELF`]. The rest of
204    /// them are used by one or more fields in the main structure.
205    ///
206    pub use super::protos::elf;
207    /// Data structure returned by the `elf` module.
208    pub use super::protos::elf::ELF;
209    /// Data structures defined by the `lnk` module.
210    ///
211    /// The main structure produced by the module is [`lnk::Lnk`]. The rest of
212    /// them are used by one or more fields in the main structure.
213    ///
214    pub use super::protos::lnk;
215    /// Data structure returned by the `lnk` module.
216    pub use super::protos::lnk::Lnk;
217
218    /// Data structures defined by the `macho` module.
219    ///
220    /// The main structure produced by the module is [`macho::Macho`]. The rest
221    /// of them are used by one or more fields in the main structure.
222    ///
223    pub use super::protos::macho;
224    /// Data structure returned by the `macho` module.
225    pub use super::protos::macho::Macho;
226
227    /// Data structures defined by the `pe` module.
228    ///
229    /// The main structure produced by the module is [`pe::PE`]. The rest
230    /// of them are used by one or more fields in the main structure.
231    ///
232    pub use super::protos::pe;
233    /// Data structure returned by the `pe` module.
234    pub use super::protos::pe::PE;
235
236    /// A data structure containing the data returned by all modules.
237    pub use super::protos::mods::Modules;
238
239    /// Invokes a YARA module with arbitrary data.
240    ///
241    /// <br>
242    ///
243    /// YARA modules typically parse specific file formats, returning structures
244    /// that contain information about the file. These structures are used in YARA
245    /// rules for expressing powerful and rich conditions. However, being able to
246    /// access this information outside YARA rules can also be beneficial.
247    ///
248    /// <br>
249    ///
250    /// This function allows the direct invocation of a YARA module for parsing
251    /// arbitrary data. It returns the structure produced by the module, which
252    /// depends upon the invoked module. The result will be [`None`] if the
253    /// module does not exist, or if it doesn't produce any information for
254    /// the input data.
255    ///
256    /// `T` must be one of the structure types returned by a YARA module, which
257    /// are defined in [`crate::mods`], like [`crate::mods::PE`], [`crate::mods::ELF`], etc.
258    ///
259    /// # Example
260    /// ```rust
261    /// # use yara_x;
262    /// let elf_info = yara_x::mods::invoke::<yara_x::mods::ELF>(&[]);
263    /// ```
264    pub fn invoke<T: protobuf::MessageFull>(data: &[u8]) -> Option<Box<T>> {
265        let module_output = invoke_dyn::<T>(data)?;
266        Some(<dyn protobuf::MessageDyn>::downcast_box(module_output).unwrap())
267    }
268
269    /// Like [`invoke`], but allows passing metadata to the module.
270    pub fn invoke_with_meta<T: protobuf::MessageFull>(
271        data: &[u8],
272        meta: Option<&[u8]>,
273    ) -> Option<Box<T>> {
274        let module_output = invoke_with_meta_dyn::<T>(data, meta)?;
275        Some(<dyn protobuf::MessageDyn>::downcast_box(module_output).unwrap())
276    }
277
278    /// Invokes a YARA module with arbitrary data, returning a dynamic
279    /// structure.
280    ///
281    /// This function is similar to [`invoke`] but its result is a dynamic-
282    /// dispatch version of the structure returned by the YARA module.
283    pub fn invoke_dyn<T: protobuf::MessageFull>(
284        data: &[u8],
285    ) -> Option<Box<dyn protobuf::MessageDyn>> {
286        invoke_with_meta_dyn::<T>(data, None)
287    }
288
289    /// Like [`invoke_dyn`], but allows passing metadata to the module.
290    pub fn invoke_with_meta_dyn<T: protobuf::MessageFull>(
291        data: &[u8],
292        meta: Option<&[u8]>,
293    ) -> Option<Box<dyn protobuf::MessageDyn>> {
294        let descriptor = T::descriptor();
295        let proto_name = descriptor.full_name();
296
297        let module = super::registered_modules()
298            .find(|m| m.root_descriptor().full_name() == proto_name)?;
299
300        module.main_fn(data, meta)?.ok()
301    }
302
303    /// Invokes all YARA modules and returns the data produced by them.
304    ///
305    /// This function is similar to [`invoke`], but it returns the
306    /// information produced by all modules at once.
307    ///
308    /// # Example
309    /// ```rust
310    /// # use yara_x;
311    /// let modules_output = yara_x::mods::invoke_all(&[]);
312    /// ```
313    pub fn invoke_all(data: &[u8]) -> Box<Modules> {
314        let mut info = Box::new(Modules::new());
315        info.pe = protobuf::MessageField(invoke::<PE>(data));
316        info.elf = protobuf::MessageField(invoke::<ELF>(data));
317        info.dotnet = protobuf::MessageField(invoke::<Dotnet>(data));
318        info.macho = protobuf::MessageField(invoke::<Macho>(data));
319        info.lnk = protobuf::MessageField(invoke::<Lnk>(data));
320        info.crx = protobuf::MessageField(invoke::<Crx>(data));
321        info.dex = protobuf::MessageField(invoke::<Dex>(data));
322        info
323    }
324
325    /// Iterator over all registered module names.
326    ///
327    /// See the "debug modules" command.
328    pub fn module_names() -> impl Iterator<Item = &'static str> {
329        use itertools::Itertools;
330        super::registered_modules().map(|m| m.name()).sorted()
331    }
332
333    /// Returns the definition of the module with the given name.
334    pub fn module_definition(name: &str) -> Option<reflect::Struct> {
335        use std::rc::Rc;
336        super::registered_modules()
337            .find(|m| m.name() == name)
338            .map(|m| reflect::Struct::new(Rc::<crate::types::Struct>::from(m)))
339    }
340
341    /// Everything needed to implement your own YARA-X modules.
342    #[allow(unused_imports)]
343    #[allow(missing_docs)]
344    pub mod prelude {
345        pub use crate::modules::Module;
346        pub use crate::modules::ModuleError;
347        pub use crate::modules::RegisteredModule;
348        pub use crate::register_module;
349        pub use crate::wasm::runtime::Caller;
350        pub use crate::wasm::string::FixedLenString;
351        pub use crate::wasm::string::RuntimeString;
352        pub use crate::wasm::string::String as _;
353        pub use crate::wasm::string::{Lowercase, Uppercase};
354        pub use crate::wasm::*;
355        pub use bstr::ByteSlice;
356        pub use inventory;
357        pub use protobuf::MessageFull;
358        pub use yara_x_macros::wasm_export;
359
360        /// Opaque scan context passed as first argument to functions exported from a
361        /// [`Module`] via `#[module_export]`.
362        ///
363        /// Functions only receive a reference to it; all fields are private.
364        pub type ScanContext<'r, 'd> = crate::scanner::ScanContext<'r, 'd>;
365
366        /// Attribute macro for exporting a callable function from a [`Module`].
367        ///
368        /// ```ignore
369        /// use yara_x::mods::prelude::*;
370        /// #[module_export]
371        /// fn add(_ctx: &ScanContext, a: i64, b: i64) -> i64 { a + b }
372        /// ```
373        pub use yara_x_macros::module_export;
374    }
375
376    /// Types that allow for module introspection.
377    ///
378    /// This API is unstable and not ready for public use.
379    #[doc(hidden)]
380    pub mod reflect {
381        use std::borrow::Cow;
382        use std::rc::Rc;
383
384        use crate::types;
385        use crate::types::{Map, TypeValue};
386
387        /// Describes a structure or module.
388        #[derive(Clone, Debug, PartialEq)]
389        pub struct Struct {
390            inner: Rc<types::Struct>,
391        }
392
393        impl Struct {
394            pub(super) fn new(inner: Rc<types::Struct>) -> Self {
395                Self { inner }
396            }
397
398            /// Returns an iterator over the fields defined in the structure.
399            ///
400            /// The fields are sorted by name.
401            pub fn fields(&self) -> impl Iterator<Item = Field<'_>> + '_ {
402                self.inner
403                    .fields()
404                    .map(|(name, field)| Field::new(name, field))
405            }
406        }
407
408        /// Describes a function.
409        #[derive(Clone, Debug, PartialEq)]
410        pub struct Func {
411            /// All the existing signatures for this function. A function
412            /// can have multiple signatures that differ in their arguments
413            /// or return type.
414            pub signatures: Vec<FuncSignature>,
415        }
416
417        impl From<Rc<types::Func>> for Func {
418            fn from(func: Rc<types::Func>) -> Self {
419                let mut signatures =
420                    Vec::with_capacity(func.signatures().len());
421
422                for signature in func.signatures() {
423                    signatures.push(FuncSignature {
424                        args: signature
425                            .args
426                            .iter()
427                            .map(|(name, ty)| (name.clone(), Type::from(ty)))
428                            .collect(),
429                        ret: Type::from(&signature.result),
430                        doc: signature.doc.clone(),
431                    });
432                }
433
434                Func { signatures }
435            }
436        }
437
438        /// Describes a function signature.
439        #[derive(Clone, Debug, PartialEq)]
440        pub struct FuncSignature {
441            /// The names and types of the function arguments.
442            args: Vec<(String, Type)>,
443            /// The return type for the function.
444            ret: Type,
445            /// Function's documentation.
446            doc: Option<Cow<'static, str>>,
447        }
448
449        impl FuncSignature {
450            /// The names and types of the function arguments.
451            pub fn args(
452                &self,
453            ) -> impl ExactSizeIterator<Item = (&str, &Type)> {
454                self.args.iter().map(|(name, ty)| (name.as_str(), ty))
455            }
456
457            /// The return type for the function.
458            pub fn ret_type(&self) -> &Type {
459                &self.ret
460            }
461
462            /// Function's documentation.
463            pub fn doc(&self) -> Option<&str> {
464                self.doc.as_deref()
465            }
466        }
467
468        /// Describes a field within a structure or module.
469        #[derive(Clone)]
470        pub struct Field<'a> {
471            name: &'a str,
472            struct_field: &'a types::StructField,
473        }
474
475        impl<'a> Field<'a> {
476            fn new(
477                name: &'a str,
478                struct_field: &'a types::StructField,
479            ) -> Self {
480                Self { name, struct_field }
481            }
482
483            /// Returns the name of the field.
484            pub fn name(&self) -> &'a str {
485                self.name
486            }
487
488            /// Returns the type of the field.
489            pub fn ty(&self) -> Type {
490                Type::from(&self.struct_field.type_value)
491            }
492
493            /// Returns the documentation for the current field.
494            pub fn doc(&self) -> Option<&str> {
495                self.struct_field.doc.as_deref()
496            }
497        }
498
499        /// The type of field, function argument or return value.
500        #[derive(Clone, Debug, PartialEq)]
501        pub enum Type {
502            /// An integer.
503            Integer,
504            /// A float.
505            Float,
506            /// A boolean.
507            Bool,
508            /// A string.
509            String,
510            /// A regular expression
511            Regexp,
512            /// A structure.
513            Struct(Struct),
514            /// An array.
515            Array(Box<Type>),
516            /// A map.
517            Map(Box<Type>, Box<Type>),
518            /// A function.
519            Func(Func),
520        }
521
522        impl From<&TypeValue> for Type {
523            fn from(type_value: &TypeValue) -> Self {
524                match type_value {
525                    TypeValue::Bool { .. } => Type::Bool,
526                    TypeValue::Float { .. } => Type::Float,
527                    TypeValue::Integer { .. } => Type::Integer,
528                    TypeValue::String { .. } => Type::String,
529                    TypeValue::Regexp(_) => Type::Regexp,
530                    TypeValue::Struct(s) => {
531                        Type::Struct(Struct::new(s.clone()))
532                    }
533                    TypeValue::Array(a) => {
534                        Type::Array(Box::new(Type::from(&a.deputy())))
535                    }
536                    TypeValue::Map(m) => {
537                        let key_kind = match **m {
538                            Map::IntegerKeys { .. } => Type::Integer,
539                            Map::StringKeys { .. } => Type::String,
540                        };
541                        Type::Map(
542                            Box::new(key_kind),
543                            Box::new(Type::from(&m.deputy())),
544                        )
545                    }
546                    TypeValue::Func(func) => Type::Func(func.clone().into()),
547                    TypeValue::Unknown => unreachable!(),
548                }
549            }
550        }
551    }
552}
553
554#[cfg(feature = "crypto")]
555pub(crate) mod utils;