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}