yara_x/modules/
mod.rs

1use protobuf::reflect::MessageDescriptor;
2use protobuf::MessageDyn;
3use rustc_hash::FxHashMap;
4use std::sync::LazyLock;
5
6use thiserror::Error;
7
8pub mod protos {
9    include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
10}
11
12#[cfg(test)]
13mod tests;
14
15#[allow(unused_imports)]
16pub(crate) mod prelude {
17    pub(crate) use crate::scanner::ScanContext;
18    pub(crate) use crate::wasm::string::FixedLenString;
19    pub(crate) use crate::wasm::string::RuntimeString;
20    pub(crate) use crate::wasm::string::String as _;
21    pub(crate) use crate::wasm::string::{Lowercase, Uppercase};
22    pub(crate) use crate::wasm::*;
23    pub(crate) use bstr::ByteSlice;
24    #[cfg(not(feature = "inventory"))]
25    pub(crate) use linkme::distributed_slice;
26    pub(crate) use wasmtime::Caller;
27    pub(crate) use yara_x_macros::{module_export, module_main, wasm_export};
28}
29
30include!("modules.rs");
31
32/// Enum describing errors occurred in modules.
33#[derive(Error, Debug)]
34#[non_exhaustive]
35pub enum ModuleError {
36    /// Invalid format of module metadata.
37    #[error("invalid metadata: {err}")]
38    MetadataError {
39        /// The error that actually occurred.
40        err: String,
41    },
42    /// Error occurred when processing the input data.
43    #[error("internal error: {err}")]
44    InternalError {
45        /// The error that actually occurred.
46        err: String,
47    },
48}
49
50/// Type of module's main function.
51type MainFn =
52    fn(&[u8], Option<&[u8]>) -> Result<Box<dyn MessageDyn>, ModuleError>;
53
54/// Describes a YARA module.
55pub(crate) struct Module {
56    /// Pointer to the module's main function.
57    pub main_fn: Option<MainFn>,
58    /// Name of the Rust module, if any, that contains code for this YARA
59    /// module (e.g: "test_proto2").
60    pub rust_module_name: Option<&'static str>,
61    /// A [`MessageDescriptor`] that describes the module's structure. This
62    /// corresponds to the protobuf message declared in the "root_message"
63    /// for the YARA module. It allows iterating the fields declared by the
64    /// module and obtaining their names and types.
65    pub root_struct_descriptor: MessageDescriptor,
66}
67
68/// Macro that adds a module to the `BUILTIN_MODULES` map.
69///
70/// This macro is used by `add_modules.rs`, a file that is automatically
71/// generated by `build.rs` based on the Protocol Buffers defined in the
72/// `src/modules/protos` directory.
73///
74/// # Example
75///
76/// add_module!(modules, "test", test, "Test", test_mod, Some(test::main as
77/// MainFn));
78macro_rules! add_module {
79    ($modules:expr, $name:literal, $proto:ident, $root_message:literal, $rust_module_name:expr, $main_fn:expr) => {{
80        use std::stringify;
81        let root_struct_descriptor = protos::$proto::file_descriptor()
82            // message_by_full_name expects a dot (.) at the beginning
83            // of the name.
84            .message_by_full_name(format!(".{}", $root_message).as_str())
85            .expect(format!(
86                "`root_message` option in protobuf `{}` is wrong, message `{}` is not defined",
87                stringify!($proto),
88                $root_message
89            ).as_str());
90
91        $modules.insert(
92            $name,
93            Module {
94                main_fn: $main_fn,
95                rust_module_name: $rust_module_name,
96                root_struct_descriptor,
97            },
98        );
99    }};
100}
101
102/// `BUILTIN_MODULES` is a static, global map where keys are module names
103/// and values are [`Module`] structures that describe a YARA module.
104///
105/// This table is populated with the modules defined by a `.proto` file in
106/// `src/modules/protos`. Each `.proto` file that contains a statement like
107/// the following one defines a YARA module:
108///
109/// ```protobuf
110/// option (yara.module_options) = {
111///   name : "foo"
112///   root_message: "Foo"
113///   rust_module: "foo"
114/// };
115/// ```
116///
117/// The `name` field is the module's name (i.e: the name used in `import`
118/// statements), which is also the key in `BUILTIN_MODULES`. `root_message`
119/// is the name of the message that describes the module's structure. This
120/// is required because a `.proto` file can define more than one message.
121///
122/// `rust_module` is the name of the Rust module where functions exported
123/// by the YARA module are defined. This field is optional, if not provided
124/// the module is considered a data-only module.
125pub(crate) static BUILTIN_MODULES: LazyLock<FxHashMap<&'static str, Module>> =
126    LazyLock::new(|| {
127        let mut modules = FxHashMap::default();
128        // The `add_modules.rs` file is automatically generated at compile time
129        // by `build.rs`. This is an example of how `add_modules.rs` looks like:
130        //
131        // {
132        //  #[cfg(feature = "pe_module")]
133        //  add_module!(modules, "pe", pe, "pe.PE", Some("pe"), Some(pe::__main__ as MainFn));
134        //
135        //  #[cfg(feature = "elf_module")]
136        //  add_module!(modules, "elf", elf, "elf.ELF", Some("elf"), Some(elf::__main__ as MainFn));
137        // }
138        //
139        // `add_modules.rs` will contain an `add_module!` statement for each
140        // protobuf in `src/modules/protos` defining a YARA module.
141        include!("add_modules.rs");
142        modules
143    });
144
145pub mod mods {
146    /*! Utility functions and structures for invoking YARA modules directly.
147
148    The utility functions [`invoke`], [`invoke_dyn`] and [`invoke_all`]
149    allow leveraging YARA modules for parsing some file formats independently
150    of any YARA rule. With these functions you can pass arbitrary data to a
151    YARA module and obtain the same data structure that is accessible to YARA
152    rules and which you use in your rule conditions.
153
154    This allows external projects to benefit from YARA's file-parsing
155    capabilities for their own purposes.
156
157    # Example
158
159    ```rust
160    # use yara_x;
161    let pe_info = yara_x::mods::invoke::<yara_x::mods::PE>(&[]);
162    ```
163    */
164
165    /// Data structures defined by the `crx` module.
166    ///
167    /// The main structure produced by the module is [`crx::Crx`]. The rest
168    /// of them are used by one or more fields in the main structure.
169    ///
170    pub use super::protos::crx;
171    /// Data structure returned by the `pe` module.
172    pub use super::protos::crx::Crx;
173
174    /// Data structure defined by the `dex` module.
175    ///
176    /// The main structure produced by the module is [`dex::Dex`]. The rest
177    /// of them are used by one or more fields in the main structure.
178    ///
179    pub use super::protos::dex;
180    /// Data structure returned by the `dex` module.
181    pub use super::protos::dex::Dex;
182
183    /// Data structures defined by the `dotnet` module.
184    ///
185    /// The main structure produced by the module is [`dotnet::Dotnet`]. The
186    /// rest of them are used by one or more fields in the main structure.
187    ///
188    pub use super::protos::dotnet;
189    /// Data structure returned by the `dotnet` module.
190    pub use super::protos::dotnet::Dotnet;
191
192    /// Data structures defined by the `elf` module.
193    ///
194    /// The main structure produced by the module is [`elf::ELF`]. The rest of
195    /// them are used by one or more fields in the main structure.
196    ///
197    pub use super::protos::elf;
198    /// Data structure returned by the `elf` module.
199    pub use super::protos::elf::ELF;
200
201    /// Data structures defined by the `lnk` module.
202    ///
203    /// The main structure produced by the module is [`lnk::Lnk`]. The rest of
204    /// them are used by one or more fields in the main structure.
205    ///
206    pub use super::protos::lnk;
207    /// Data structure returned by the `lnk` module.
208    pub use super::protos::lnk::Lnk;
209
210    /// Data structures defined by the `macho` module.
211    ///
212    /// The main structure produced by the module is [`macho::Macho`]. The rest
213    /// of them are used by one or more fields in the main structure.
214    ///
215    pub use super::protos::macho;
216    /// Data structure returned by the `macho` module.
217    pub use super::protos::macho::Macho;
218
219    /// Data structures defined by the `pe` module.
220    ///
221    /// The main structure produced by the module is [`pe::PE`]. The rest
222    /// of them are used by one or more fields in the main structure.
223    ///
224    pub use super::protos::pe;
225    /// Data structure returned by the `pe` module.
226    pub use super::protos::pe::PE;
227
228    /// A data structure contains the data returned by all modules.
229    pub use super::protos::mods::Modules;
230
231    /// Invoke a YARA module with arbitrary data.
232    ///
233    /// <br>
234    ///
235    /// YARA modules typically parse specific file formats, returning structures
236    /// that contain information about the file. These structures are used in YARA
237    /// rules for expressing powerful and rich conditions. However, being able to
238    /// access this information outside YARA rules can also be beneficial.
239    ///
240    /// <br>
241    ///
242    /// This function allows the direct invocation of a YARA module for parsing
243    /// arbitrary data. It returns the structure produced by the module, which
244    /// depends upon the invoked module. The result will be [`None`] if the
245    /// module does not exist, or if it doesn't produce any information for
246    /// the input data.
247    ///
248    /// `T` must be one of the structure types returned by a YARA module, which
249    /// are defined [`crate::mods`], like [`crate::mods::PE`], [`crate::mods::ELF`], etc.
250    ///
251    /// # Example
252    /// ```rust
253    /// # use yara_x;
254    /// let elf_info = yara_x::mods::invoke::<yara_x::mods::ELF>(&[]);
255    /// ```
256    pub fn invoke<T: protobuf::MessageFull>(data: &[u8]) -> Option<Box<T>> {
257        let module_output = invoke_dyn::<T>(data)?;
258        Some(<dyn protobuf::MessageDyn>::downcast_box(module_output).unwrap())
259    }
260
261    /// Like [`invoke`], but allows passing metadata to the module.
262    pub fn invoke_with_meta<T: protobuf::MessageFull>(
263        data: &[u8],
264        meta: Option<&[u8]>,
265    ) -> Option<Box<T>> {
266        let module_output = invoke_with_meta_dyn::<T>(data, meta)?;
267        Some(<dyn protobuf::MessageDyn>::downcast_box(module_output).unwrap())
268    }
269
270    /// Invoke a YARA module with arbitrary data, but returns a dynamic
271    /// structure.
272    ///
273    /// This function is similar to [`invoke`] but its result is a dynamic-
274    /// dispatch version of the structure returned by the YARA module.
275    pub fn invoke_dyn<T: protobuf::MessageFull>(
276        data: &[u8],
277    ) -> Option<Box<dyn protobuf::MessageDyn>> {
278        invoke_with_meta_dyn::<T>(data, None)
279    }
280
281    /// Like [`invoke_dyn`], but allows passing metadata to the module.
282    pub fn invoke_with_meta_dyn<T: protobuf::MessageFull>(
283        data: &[u8],
284        meta: Option<&[u8]>,
285    ) -> Option<Box<dyn protobuf::MessageDyn>> {
286        let descriptor = T::descriptor();
287        let proto_name = descriptor.full_name();
288        let (_, module) =
289            super::BUILTIN_MODULES.iter().find(|(_, module)| {
290                module.root_struct_descriptor.full_name() == proto_name
291            })?;
292
293        module.main_fn?(data, meta).ok()
294    }
295
296    /// Invoke all YARA modules and return the data produced by them.
297    ///
298    /// This function is similar to [`invoke`], but it returns the
299    /// information produced by all modules at once.
300    ///
301    /// # Example
302    /// ```rust
303    /// # use yara_x;
304    /// let modules_output = yara_x::mods::invoke_all(&[]);
305    /// ```
306    pub fn invoke_all(data: &[u8]) -> Box<Modules> {
307        let mut info = Box::new(Modules::new());
308        info.pe = protobuf::MessageField(invoke::<PE>(data));
309        info.elf = protobuf::MessageField(invoke::<ELF>(data));
310        info.dotnet = protobuf::MessageField(invoke::<Dotnet>(data));
311        info.macho = protobuf::MessageField(invoke::<Macho>(data));
312        info.lnk = protobuf::MessageField(invoke::<Lnk>(data));
313        info.crx = protobuf::MessageField(invoke::<Crx>(data));
314        info.dex = protobuf::MessageField(invoke::<Dex>(data));
315        info
316    }
317
318    /// Iterator over built-in module names.
319    ///
320    /// See the "debug modules" command.
321    pub fn module_names() -> impl Iterator<Item = &'static str> {
322        super::BUILTIN_MODULES.keys().copied()
323    }
324}
325
326#[cfg(feature = "crypto")]
327pub(crate) mod utils;