Add parser for ALZ archives

pull/1248/head
Andy Ragusa 1 year ago committed by Micah Snyder
parent a0f4dc79a2
commit 79f2a5f2f6
  1. 323
      Cargo.lock
  2. 1
      libclamav/dconf.c
  3. 1
      libclamav/dconf.h
  4. 1
      libclamav/filetypes.c
  5. 1
      libclamav/filetypes.h
  6. 1
      libclamav/filetypes_int.h
  7. 5
      libclamav/scanners.c
  8. 3
      libclamav_rust/Cargo.toml
  9. 1
      libclamav_rust/cbindgen.toml
  10. 552
      libclamav_rust/src/alz.rs
  11. 15
      libclamav_rust/src/evidence.rs
  12. 9
      libclamav_rust/src/fuzzy_hash.rs
  13. 1
      libclamav_rust/src/lib.rs
  14. 49
      libclamav_rust/src/scanners.rs
  15. 121
      unit_tests/clamscan/alz_test.py
  16. BIN
      unit_tests/input/other_scanfiles/alz/bzip2.alz
  17. BIN
      unit_tests/input/other_scanfiles/alz/bzip2.bin.alz
  18. BIN
      unit_tests/input/other_scanfiles/alz/deflate.alz
  19. BIN
      unit_tests/input/other_scanfiles/alz/uncompressed.alz
  20. BIN
      unit_tests/input/other_scanfiles/alz/uncompressed.bin.alz
  21. 13
      unit_tests/input/other_sigs/alz.hdb

323
Cargo.lock generated

@ -8,11 +8,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
@ -34,9 +40,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "base64"
@ -63,7 +69,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn 2.0.52",
"syn 2.0.58",
"which",
]
@ -81,9 +87,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.2"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "block-buffer"
@ -96,15 +102,15 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.15.3"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.14.3"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f"
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
[[package]]
name = "byteorder"
@ -114,9 +120,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.5.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "bzip2-rs"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beeb59e7e4c811ab37cc73680c798c7a5da77fc9989c62b09138e31ee740f735"
dependencies = [
"crc32fast",
"tinyvec",
]
[[package]]
name = "cbindgen"
@ -138,9 +154,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.88"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
[[package]]
name = "cexpr"
@ -159,16 +175,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.34"
version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.4",
"windows-targets",
]
[[package]]
@ -177,12 +193,15 @@ version = "0.0.1"
dependencies = [
"base64",
"bindgen",
"byteorder",
"bzip2-rs",
"cbindgen",
"delharc",
"flate2",
"hex",
"hex-literal",
"image",
"inflate",
"libc",
"log",
"num-traits",
@ -284,7 +303,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f420b1ede12094758715adaee221d93cb2af86dc499cef368ef81a23fea96029"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"chrono",
"memchr",
]
@ -301,15 +320,15 @@ dependencies = [
[[package]]
name = "either"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "encoding_rs"
version = "0.8.33"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"cfg-if",
]
@ -332,7 +351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -353,9 +372,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.0.1"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
[[package]]
name = "fdeflate"
@ -413,9 +432,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "half"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"cfg-if",
"crunchy",
@ -451,7 +470,7 @@ version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -505,6 +524,15 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "inflate"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
dependencies = [
"adler32",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -516,9 +544,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.10"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jpeg-decoder"
@ -531,9 +559,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.68"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
@ -564,12 +592,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
version = "0.8.1"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
"windows-targets",
]
[[package]]
@ -596,9 +624,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.1"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "minimal-lexical"
@ -702,12 +730,12 @@ dependencies = [
[[package]]
name = "prettyplease"
version = "0.2.16"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7"
dependencies = [
"proc-macro2",
"syn 2.0.52",
"syn 2.0.58",
]
[[package]]
@ -721,9 +749,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.78"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
@ -739,18 +767,18 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.9.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
@ -768,9 +796,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.3"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
@ -780,9 +808,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
@ -791,9 +819,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rustc-hash"
@ -827,15 +855,15 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.31"
version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
"bitflags 2.4.2",
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -867,14 +895,14 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.58",
]
[[package]]
name = "serde_json"
version = "1.0.114"
version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
dependencies = [
"itoa",
"ryu",
@ -917,9 +945,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "smallvec"
version = "1.13.1"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "spin"
@ -949,9 +977,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.52"
version = "2.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
dependencies = [
"proc-macro2",
"quote",
@ -967,27 +995,27 @@ dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "1.0.57"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.58",
]
[[package]]
@ -1001,6 +1029,12 @@ dependencies = [
"weezl",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
[[package]]
name = "toml"
version = "0.5.11"
@ -1040,9 +1074,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "uuid"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
[[package]]
name = "version_check"
@ -1052,9 +1086,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-bindgen"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -1062,24 +1096,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.58",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1087,22 +1121,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.58",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "weezl"
@ -1124,9 +1158,9 @@ dependencies = [
[[package]]
name = "widestring"
version = "1.0.2"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]]
name = "windows-core"
@ -1134,16 +1168,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
"windows-targets",
]
[[package]]
@ -1152,122 +1177,72 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "zune-inflate"

@ -108,6 +108,7 @@ static struct dconf_module modules[] = {
{"ARCHIVE", "EGG", ARCH_CONF_EGG, 1},
{"ARCHIVE", "UDF", ARCH_CONF_UDF, 1},
{"ARCHIVE", "LHA", ARCH_CONF_LHA_LZH, 1},
{"ARCHIVE", "ALZ", ARCH_CONF_ALZ, 1},
{"DOCUMENT", "HTML", DOC_CONF_HTML, 1},
{"DOCUMENT", "RTF", DOC_CONF_RTF, 1},

@ -98,6 +98,7 @@ struct cli_dconf {
#define ARCH_CONF_EGG 0x4000000
#define ARCH_CONF_UDF 0x8000000
#define ARCH_CONF_LHA_LZH 0x10000000
#define ARCH_CONF_ALZ 0x20000000
/* Document flags */
#define DOC_CONF_HTML 0x1

@ -138,6 +138,7 @@ static const struct ftmap_s {
{ "CL_TYPE_EGG", CL_TYPE_EGG },
{ "CL_TYPE_EGGSFX", CL_TYPE_EGGSFX },
{ "CL_TYPE_UDF", CL_TYPE_UDF },
{ "CL_TYPE_ALZ", CL_TYPE_ALZ },
{ "CL_TYPE_ONENOTE", CL_TYPE_ONENOTE },
{ "CL_TYPE_PYTHON_COMPILED", CL_TYPE_PYTHON_COMPILED },
{ "CL_TYPE_LHA_LZH", CL_TYPE_LHA_LZH },

@ -126,6 +126,7 @@ typedef enum cli_file {
CL_TYPE_MHTML,
CL_TYPE_LNK,
CL_TYPE_UDF,
CL_TYPE_ALZ,
CL_TYPE_OTHER, /* on-the-fly, used for target 14 (OTHER) */
CL_TYPE_IGNORED /* please don't add anything below */
} cli_file_t;

@ -301,5 +301,6 @@ static const char *ftypes_int[] = {
"1:2:2d6c68(30|31|32|33|34|35|36|37|64|78)2d:LHA or LZH archive:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210",
"1:2:2d6c7a(73|34|35)2d:LHA archive using .LZS extension:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210",
"1:2:2d706d302d:LHA archive using PMarc (.PMA) extension:CL_TYPE_ANY:CL_TYPE_LHA_LZH:210",
"0:0:414c5a01:ALZ:CL_TYPE_ANY:CL_TYPE_ALZ:210",
NULL};
#endif

@ -4550,6 +4550,11 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
if (SCAN_PARSE_ONENOTE && (DCONF_ARCH & DOC_CONF_ONENOTE))
ret = scan_onenote(ctx);
break;
case CL_TYPE_ALZ:
if (SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ALZ)) {
ret = cli_scanalz(ctx);
}
break;
case CL_TYPE_LHA_LZH:
if (SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_LHA_LZH))

@ -22,6 +22,9 @@ unicode-segmentation = "1.10"
bindgen = "0.65"
onenote_parser = { git = "https://github.com/Cisco-Talos/onenote.rs.git", branch = "CLAM-2329-new-from-slice" }
hex-literal = "0.4"
inflate = "0.4"
bzip2-rs = "0.1"
byteorder = "1.5"
delharc = "0.5"
[lib]

@ -37,6 +37,7 @@ include = [
"evidence::evidence_add_indicator",
"evidence::IndicatorType",
"scanners::scan_onenote",
"scanners::cli_scanalz",
]
# prefix = "CAPI_"

@ -0,0 +1,552 @@
/*
* ALZ archive extraction.
*
* Copyright (C) 2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Andy Ragusa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
#![warn(
clippy::all,
clippy::restriction,
clippy::pedantic,
clippy::nursery,
clippy::cargo,
)]
*/
use std::io::{Cursor, Read};
use byteorder::{LittleEndian, ReadBytesExt};
use bzip2_rs::DecoderReader;
use inflate::InflateStream;
use log::debug;
/// File header
const ALZ_FILE_HEADER: u32 = 0x015a_4c41;
/// Local file header
const ALZ_LOCAL_FILE_HEADER: u32 = 0x015a_4c42;
/// Central directory header
const ALZ_CENTRAL_DIRECTORY_HEADER: u32 = 0x015a_4c43;
/// End of Central directory header
const ALZ_END_OF_CENTRAL_DIRECTORY_HEADER: u32 = 0x025a_4c43;
/// Error enumerates all possible errors returned by this library.
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Error parsing ALZ archive: {0}")]
Parse(&'static str),
#[error("Unrecognized sig: '{0}'")]
UnrecognizedSig(String),
#[error("Unsupported ALZ feature: {0}")]
UnsupportedFeature(&'static str),
#[error("Failed to extract file")]
Extract,
#[error("Failed to read field: {0}")]
Read(&'static str),
}
struct AlzLocalFileHeaderHead {
file_name_length: u16,
file_attribute: u8,
file_time_date: u32,
file_descriptor: u8,
unknown: u8,
}
const ALZ_ENCR_HEADER_LEN: u32 = 12;
struct AlzLocalFileHeader {
head: AlzLocalFileHeaderHead,
compression_method: u8,
unknown: u8,
file_crc: u32,
/* Can be smaller sizes, depending on file_descriptor/0x10 .*/
compressed_size: u64,
uncompressed_size: u64,
file_name: String,
enc_chk: [u8; ALZ_ENCR_HEADER_LEN as usize],
start_of_compressed_data: u64,
}
#[allow(dead_code)]
enum AlzFileAttribute {
Readonly = 0x1,
Hidden = 0x2,
Directory = 0x10,
File = 0x20,
}
impl AlzLocalFileHeader {
const fn is_encrypted(&self) -> bool {
0 != (self.head.file_descriptor & 0x1)
}
const fn is_data_descriptor(&self) -> bool {
0 != (self.head.file_descriptor & 0x8)
}
const fn is_directory(&self) -> bool {
0 != ((AlzFileAttribute::Directory as u8) & self.head.file_attribute)
}
const fn _is_file(&self) -> bool {
0 != ((AlzFileAttribute::File as u8) & self.head.file_attribute)
}
const fn _is_readonly(&self) -> bool {
0 != ((AlzFileAttribute::Readonly as u8) & self.head.file_attribute)
}
const fn _is_hidden(&self) -> bool {
0 != ((AlzFileAttribute::Hidden as u8) & self.head.file_attribute)
}
fn _dump(&self) {
println!(
"self.start_of_compressed_data = {}",
self.start_of_compressed_data
);
println!(
"self.head.file_name_length = {:x}",
self.head.file_name_length
);
println!(
"self.head.file_attribute = {:02x}",
self.head.file_attribute
);
println!("self.head.file_time_date = {:x}", self.head.file_time_date);
println!(
"self.head.file_descriptor = {:x}",
self.head.file_descriptor
);
println!("self.head.unknown = {:x}", self.head.unknown);
println!("self.compression_method = {:x}", self.compression_method);
println!("self.unknown = {:x}", self.unknown);
println!("self.file_crc = {:x}", self.file_crc);
println!("self.compressed_size = {:x}", self.compressed_size);
println!("self.uncompressed_size = {:x}", self.uncompressed_size);
println!("self.file_name = {}", self.file_name);
print!("self.enc_chk = ");
for i in 0..ALZ_ENCR_HEADER_LEN {
if 0 != i {
print!(" ");
}
print!("{}", self.enc_chk[i as usize]);
}
println!();
println!("is_encrypted = {}", self.is_encrypted());
println!("is_data_descriptor = {}", self.is_data_descriptor());
println!();
}
pub const fn new() -> Self {
Self {
head: AlzLocalFileHeaderHead {
file_name_length: 0,
file_attribute: 0,
file_time_date: 0,
file_descriptor: 0,
unknown: 0,
},
compression_method: 0,
unknown: 0,
file_crc: 0,
compressed_size: 0,
uncompressed_size: 0,
file_name: String::new(),
enc_chk: [0; ALZ_ENCR_HEADER_LEN as usize],
start_of_compressed_data: 0,
}
}
pub fn parse(&mut self, cursor: &mut std::io::Cursor<&Vec<u8>>) -> Result<(), Error> {
self.head.file_name_length = cursor
.read_u16::<LittleEndian>()
.map_err(|_| Error::Read("file_name_length"))?;
self.head.file_attribute = cursor
.read_u8()
.map_err(|_| Error::Read("file_attribute"))?;
self.head.file_time_date = cursor
.read_u32::<LittleEndian>()
.map_err(|_| Error::Read("file_time_date"))?;
self.head.file_descriptor = cursor
.read_u8()
.map_err(|_| Error::Read("file_descriptor"))?;
self.head.unknown = cursor.read_u8().map_err(|_| Error::Read("unknown u8"))?;
if 0 == self.head.file_name_length {
return Err(Error::Parse("File Name Length is zero"));
}
let byte_len = self.head.file_descriptor / 0x10;
if byte_len > 0 {
self.compression_method = cursor
.read_u8()
.map_err(|_| Error::Read("compression_method"))?;
self.unknown = cursor.read_u8().map_err(|_| Error::Read("unknown u8"))?;
self.file_crc = cursor
.read_u32::<LittleEndian>()
.map_err(|_| Error::Read("file_crc"))?;
match byte_len {
1 => {
self.compressed_size = u64::from(
cursor
.read_u8()
.map_err(|_| Error::Read("compressed_size"))?,
);
self.uncompressed_size = u64::from(
cursor
.read_u8()
.map_err(|_| Error::Read("uncompressed_size"))?,
);
}
2 => {
self.compressed_size = u64::from(
cursor
.read_u16::<LittleEndian>()
.map_err(|_| Error::Read("compressed_size"))?,
);
self.uncompressed_size = u64::from(
cursor
.read_u16::<LittleEndian>()
.map_err(|_| Error::Read("uncompressed_size"))?,
);
}
4 => {
self.compressed_size = u64::from(
cursor
.read_u32::<LittleEndian>()
.map_err(|_| Error::Read("compressed_size"))?,
);
self.uncompressed_size = u64::from(
cursor
.read_u32::<LittleEndian>()
.map_err(|_| Error::Read("uncompressed_size"))?,
);
}
8 => {
self.compressed_size = cursor
.read_u64::<LittleEndian>()
.map_err(|_| Error::Read("compressed_size"))?;
self.uncompressed_size = cursor
.read_u64::<LittleEndian>()
.map_err(|_| Error::Read("uncompressed_size"))?;
}
_ => return Err(Error::Parse("Unsupported File Descriptor")),
}
}
#[allow(clippy::cast_possible_truncation)]
let idx0: usize = cursor.position() as usize;
let idx1: usize = idx0 + self.head.file_name_length as usize;
if idx1 > cursor.get_ref().len() {
return Err(Error::Parse("Invalid file name length"));
}
let filename = &cursor.get_ref().as_slice()[idx0..idx1];
cursor.set_position(idx1 as u64);
self.file_name = String::from_utf8_lossy(filename).into_owned();
if self.is_encrypted() {
cursor
.read_exact(&mut self.enc_chk)
.map_err(|_| Error::Read("encrypted buffer"))?;
}
self.start_of_compressed_data = cursor.position();
cursor.set_position(self.start_of_compressed_data + self.compressed_size);
if self.start_of_compressed_data + self.compressed_size > cursor.get_ref().len() as u64 {
return Err(Error::Parse("Invalid compressed data length"));
}
Ok(())
}
pub fn is_supported(&self) -> Result<(), Error> {
if self.is_encrypted() {
return Err(Error::UnsupportedFeature("Encryption Unsupported"));
}
if self.is_data_descriptor() {
return Err(Error::UnsupportedFeature("Data Descriptors are Unsupported"));
}
Ok(())
}
/*
* This has no header/checksum validation.
*/
fn extract_file_deflate(
&mut self,
cursor: &std::io::Cursor<&Vec<u8>>,
files: &mut Vec<ExtractedFile>,
) -> Result<(), Error> {
#[allow(clippy::cast_possible_truncation)]
let start: usize = self.start_of_compressed_data as usize;
#[allow(clippy::cast_possible_truncation)]
let end: usize = start + self.compressed_size as usize;
if end >= cursor.get_ref().len() {
return Err(Error::Extract);
}
let data: &[u8] = &cursor.get_ref().as_slice()[start..end];
let mut inflater = InflateStream::new();
let mut out: Vec<u8> = Vec::<u8>::new();
let mut n: usize = 0;
while n < data.len() {
let res = inflater.update(&data[n..]);
if let Ok((num_bytes_read, result)) = res {
n += num_bytes_read;
out.extend(result.iter().copied());
} else {
return Err(Error::Extract);
}
}
self.write_file(&out, files);
Ok(())
}
fn write_file(&mut self, buffer: &[u8], files: &mut Vec<ExtractedFile>) {
let extracted_file: ExtractedFile = ExtractedFile {
name: Some(self.file_name.to_string()),
data: buffer.to_vec(),
};
files.push(extracted_file);
}
fn extract_file_nocomp(
&mut self,
cursor: &mut std::io::Cursor<&Vec<u8>>,
files: &mut Vec<ExtractedFile>,
) -> Result<(), Error> {
#[allow(clippy::cast_possible_truncation)]
let idx0: usize = self.start_of_compressed_data as usize;
let mut len = self.compressed_size;
if self.compressed_size != self.uncompressed_size {
debug!("Uncompressed file has different lengths for compressed vs uncompressed, using the shorter");
if self.compressed_size > self.uncompressed_size {
len = self.uncompressed_size;
}
}
#[allow(clippy::cast_possible_truncation)]
let idx1: usize = idx0 + len as usize;
if idx1 > cursor.get_ref().len() {
debug!("Invalid data length");
return Err(Error::Extract);
}
let contents = &cursor.get_ref().as_slice()[idx0..idx1];
cursor.set_position(idx1 as u64);
self.write_file(contents, files);
Ok(())
}
fn extract_file_bzip2(
&mut self,
cursor: &std::io::Cursor<&Vec<u8>>,
files: &mut Vec<ExtractedFile>,
) -> Result<(), Error> {
#[allow(clippy::cast_possible_truncation)]
let idx0: usize = self.start_of_compressed_data as usize;
#[allow(clippy::cast_possible_truncation)]
let idx1: usize = idx0 + self.compressed_size as usize;
let contents = &cursor.get_ref().as_slice()[idx0..idx1];
/*
* Create vector of the needed capacity.
*/
let mut out: Vec<u8> = Vec::new();
for _i in 0..self.uncompressed_size {
out.push(0);
}
let mut decompressor = DecoderReader::new(contents);
let ret = decompressor.read_exact(&mut out);
if ret.is_err() {
debug!("Unable to decompress bz2 data");
return Err(Error::Extract);
}
self.write_file(&out, files);
Ok(())
}
fn extract_file(
&mut self,
cursor: &mut std::io::Cursor<&Vec<u8>>,
files: &mut Vec<ExtractedFile>,
) -> Result<(), Error> {
const ALZ_COMP_NOCOMP: u8 = 0;
const ALZ_COMP_BZIP2: u8 = 1;
const ALZ_COMP_DEFLATE: u8 = 2;
match self.compression_method {
ALZ_COMP_NOCOMP => self.extract_file_nocomp(cursor, files),
ALZ_COMP_BZIP2 => self.extract_file_bzip2(cursor, files),
ALZ_COMP_DEFLATE => self.extract_file_deflate(cursor, files),
_ => Err(Error::Extract),
}
}
}
/*TODO: Merge this with the onenote extracted_file struct, and use the same one everywhere.*/
pub struct ExtractedFile {
pub name: Option<String>,
pub data: Vec<u8>,
}
#[derive(Default)]
pub struct Alz {
pub embedded_files: Vec<ExtractedFile>,
}
impl<'aa> Alz {
/* Check for the ALZ file header. */
#[allow(clippy::unused_self)]
fn is_alz(&self, cursor: &mut std::io::Cursor<&Vec<u8>>) -> bool {
cursor
.read_u32::<LittleEndian>()
.map_or(false, |n| ALZ_FILE_HEADER == n)
}
fn parse_local_fileheader(&mut self, cursor: &mut std::io::Cursor<&Vec<u8>>) -> bool {
let mut local_fileheader = AlzLocalFileHeader::new();
if let Err(err) = local_fileheader.parse(cursor) {
debug!("{err}");
return false;
}
if let Err(err) = local_fileheader.is_supported() {
debug!("{err}");
return false;
}
if !local_fileheader.is_directory() {
/* The is_file flag doesn't appear to always be set, so we'll just assume it's a file if
* it's not marked as a directory.*/
let res2 = local_fileheader.extract_file(cursor, &mut self.embedded_files);
if res2.is_err() {
return false;
}
}
true
}
#[allow(clippy::unused_self)]
fn parse_central_directoryheader(&self, cursor: &mut std::io::Cursor<&Vec<u8>>) -> bool {
/*
* This is ignored in unalz (UnAlz.cpp ReadCentralDirectoryStructure).
*
* It actually reads 12 bytes, and I think it happens to work because EOF is hit on the next
* read, which it does not consider an error.
*/
let ret = cursor.read_u64::<LittleEndian>();
ret.is_ok()
}
#[must_use]
pub const fn new() -> Self {
Self {
embedded_files: Vec::new(),
}
}
/// # Errors
/// Will return `Error::Parse` if file headers are not correct or are inconsistent.
pub fn from_bytes(bytes: &'aa [u8]) -> Result<Self, Error> {
let binding = bytes.to_vec();
let mut cursor = Cursor::new(&binding);
let mut alz: Self = Self::new();
if !alz.is_alz(&mut cursor) {
return Err(Error::Parse("No ALZ file header"));
}
//What these bytes are supposed to be in unspecified, but they need to be there.
let ret = cursor.read_u32::<LittleEndian>();
if ret.is_err() {
return Err(Error::Parse("Error reading uint32 from file"));
}
loop {
let Ok(sig) = cursor.read_u32::<LittleEndian>() else {
break;
};
match sig {
ALZ_LOCAL_FILE_HEADER => {
if alz.parse_local_fileheader(&mut cursor) {
continue;
}
}
ALZ_CENTRAL_DIRECTORY_HEADER => {
if alz.parse_central_directoryheader(&mut cursor) {
continue;
}
}
ALZ_END_OF_CENTRAL_DIRECTORY_HEADER => {
break;
/*This is the end, nothing really to do here.*/
}
_ => {
#[allow(clippy::uninlined_format_args)]
return Err(Error::UnrecognizedSig(format!("{:x}", sig)));
}
}
}
Ok(alz)
}
}

@ -244,17 +244,11 @@ impl Evidence {
match indicator_type {
IndicatorType::Strong => {
self.strong
.entry(name.to_string())
.or_default()
.push(meta);
self.strong.entry(name.to_string()).or_default().push(meta);
}
IndicatorType::PotentiallyUnwanted => {
self.pua
.entry(name.to_string())
.or_default()
.push(meta);
self.pua.entry(name.to_string()).or_default().push(meta);
}
#[cfg(feature = "not_ready")]
@ -262,10 +256,7 @@ impl Evidence {
// match the archive/extraction level at which each was found.
// This will be required for alerting signatures to depend on weak-indicators for embedded content.
IndicatorType::Weak => {
self.weak
.entry(name.to_string())
.or_default()
.push(meta);
self.weak.entry(name.to_string()).or_default().push(meta);
}
}

@ -26,8 +26,7 @@ use std::{
ffi::CStr,
mem::ManuallyDrop,
os::raw::c_char,
panic,
slice,
panic, slice,
};
use image::{imageops::FilterType::Lanczos3, DynamicImage, ImageBuffer, Luma, Pixel, Rgb};
@ -308,10 +307,7 @@ impl FuzzyHashMap {
// If the hash key does not exist in the hashmap, insert an empty vec.
// Then add the current meta struct to the entry.
self.hashmap
.entry(fuzzy_hash)
.or_default()
.push(meta);
self.hashmap.entry(fuzzy_hash).or_default().push(meta);
Ok(())
}
@ -415,7 +411,6 @@ impl FuzzyHashMap {
/// param: hash_out is an output variable
/// param: hash_out_len indicates the size of the hash_out buffer
pub fn fuzzy_hash_calculate_image(buffer: &[u8]) -> Result<Vec<u8>, Error> {
// Load image and attempt to catch panics in case the decoders encounter unexpected issues
let result = panic::catch_unwind(|| -> Result<DynamicImage, Error> {
let image = image::load_from_memory(buffer).map_err(Error::ImageLoad)?;

@ -34,3 +34,4 @@ pub mod logging;
pub mod onenote;
pub mod scanners;
pub mod util;
pub mod alz;

@ -33,6 +33,7 @@ use libc::c_void;
use log::{debug, error, warn};
use crate::{
alz::Alz,
ctx,
onenote::OneNote,
sys::{
@ -295,3 +296,51 @@ pub unsafe extern "C" fn scan_lha_lzh(ctx: *mut cli_ctx) -> cl_error_t {
cl_error_t_CL_SUCCESS
}
/// Scan an Alz file for attachments
///
/// # Safety
///
/// Must be a valid ctx pointer.
#[no_mangle]
pub unsafe extern "C" fn cli_scanalz(ctx: *mut cli_ctx) -> cl_error_t {
let fmap = match ctx::current_fmap(ctx) {
Ok(fmap) => fmap,
Err(e) => {
warn!("Error getting FMap from ctx: {e}");
return cl_error_t_CL_ERROR;
}
};
let file_bytes = match fmap.need_off(0, fmap.len()) {
Ok(bytes) => bytes,
Err(err) => {
error!(
"Failed to get file bytes for fmap of size {}: {err}",
fmap.len()
);
return cl_error_t_CL_ERROR;
}
};
let alz = match Alz::from_bytes(file_bytes) {
Ok(x) => x,
Err(err) => {
error!("Failed to parse Alz file: {}", err.to_string());
return cl_error_t_CL_ERROR;
}
};
for i in 0..alz.embedded_files.len() {
let ret = magic_scan(
ctx,
&alz.embedded_files[i].data,
alz.embedded_files[i].name.clone(),
);
if ret != cl_error_t_CL_SUCCESS {
return ret;
}
}
cl_error_t_CL_SUCCESS
}

@ -0,0 +1,121 @@
# Copyright (C) 2020-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
"""
Run clamscan tests.
"""
import sys
sys.path.append('../unit_tests')
import testcase
class TC(testcase.TestCase):
@classmethod
def setUpClass(cls):
super(TC, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TC, cls).tearDownClass()
def setUp(self):
super(TC, self).setUp()
def tearDown(self):
super(TC, self).tearDown()
self.verify_valgrind_log()
def test_deflate(self):
self.step_name('Test alz files compressed with deflate (gzip)')
testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'deflate.alz'
command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb',
testfile=testfile,
)
output = self.execute_command(command)
assert output.ec == 1 # virus
expected_results = [
'ALZ_TEST_FILE.UNOFFICIAL FOUND',
]
self.verify_output(output.out, expected=expected_results)
def test_bzip2(self):
self.step_name('Test alz files compressed with bzip2')
testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'bzip2.alz'
command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb',
testfile=testfile,
)
output = self.execute_command(command)
assert output.ec == 1 # virus
expected_results = [
'ALZ_TEST_FILE.UNOFFICIAL FOUND',
]
self.verify_output(output.out, expected=expected_results)
def test_bzip2_with_binary(self):
self.step_name('Test alz files compressed with bzip2')
testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'bzip2.bin.alz'
command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb',
testfile=testfile,
)
output = self.execute_command(command)
assert output.ec == 1 # virus
expected_results = [
'ALZ_TEST_FILE_EXECUTABLE.UNOFFICIAL FOUND',
]
self.verify_output(output.out, expected=expected_results)
def test_uncompressed(self):
self.step_name('Test alz files with no compression')
testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'uncompressed.alz'
command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb',
testfile=testfile,
)
output = self.execute_command(command)
assert output.ec == 1 # virus
expected_results = [
'ALZ_TEST_FILE.UNOFFICIAL FOUND',
]
self.verify_output(output.out, expected=expected_results)
def test_uncompressed_with_binary(self):
self.step_name('Test alz files with no compression with binary data')
testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'alz' / 'uncompressed.bin.alz'
command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfile}'.format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'alz.hdb',
testfile=testfile,
)
output = self.execute_command(command)
assert output.ec == 1 # virus
expected_results = [
'ALZ_TEST_FILE_EXECUTABLE.UNOFFICIAL FOUND',
]
self.verify_output(output.out, expected=expected_results)

@ -0,0 +1,13 @@
03d4c70e6aa3832fa51959137b6a3fc55d8b9f55:16:ALZ_TEST_FILE
05cf0585be97d8f544f034c7e46cf98778925c66:13:ALZ_TEST_FILE
12f41f69d25d0ba9b73da429e0d69b27c95522db:16:ALZ_TEST_FILE
24578375a0454c0657bac54084b50fdda1efaa21:11:ALZ_TEST_FILE
26c0e077ad49260d416dbf449569efbc7ce02448:16:ALZ_TEST_FILE
33ab5639bfd8e7b95eb1d8d0b87781d4ffea4d5d:12:ALZ_TEST_FILE
67b33eebc1e4537d839bc6b04affd6b06074c746:13:ALZ_TEST_FILE
6847c9c6e9218691910a0d7e36ac544149e3ce7d:13:ALZ_TEST_FILE
9645df16bc733a92129563ad2e5f1f6a9ed483c9:16:ALZ_TEST_FILE
9ac5483905f6c4b72c314c901ceb5eca2fee95c3:13:ALZ_TEST_FILE
cb9431a94ca1d5c64d9a1e467c543905f592f351:13:ALZ_TEST_FILE
ce5cec9fef4940d0d1fe2bc5004b14d7f8fc290c:77:ALZ_TEST_FILE
edf6cd48d7b44a6cc0a96a6139cfe020865f8c4c:16712:ALZ_TEST_FILE_EXECUTABLE
Loading…
Cancel
Save