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;