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;