Skip to main content

yara_x/modules/
mod.rs

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