From e4be5651c53272c2d5b1a8921abfd9ffa7a87f82 Mon Sep 17 00:00:00 2001 From: Ryan O'Horo <10855297+ryanohoro@users.noreply.github.com> Date: Tue, 26 Sep 2023 21:54:39 -0500 Subject: [PATCH] Scanner, tests, fixtures, taste, config, and docs for ScanDonut --- configs/python/backend/backend.yaml | 5 + configs/python/backend/taste/taste.yara | 44 ++++++++- docs/README.md | 5 +- src/python/requirements.txt | 4 +- src/python/strelka/scanners/scan_donut.py | 91 ++++++++++++++++++ .../strelka/tests/fixtures/test_donut.bin | Bin 0 -> 35285 bytes .../tests/fixtures/test_donut_compressed.bin | Bin 0 -> 33002 bytes src/python/strelka/tests/test_scan_donut.py | 75 +++++++++++++++ 8 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 src/python/strelka/scanners/scan_donut.py create mode 100644 src/python/strelka/tests/fixtures/test_donut.bin create mode 100644 src/python/strelka/tests/fixtures/test_donut_compressed.bin create mode 100644 src/python/strelka/tests/test_scan_donut.py diff --git a/configs/python/backend/backend.yaml b/configs/python/backend/backend.yaml index 8e436e6c..9fe4c701 100644 --- a/configs/python/backend/backend.yaml +++ b/configs/python/backend/backend.yaml @@ -86,6 +86,11 @@ scanners: # flavors: # - 'credit_cards' # priority: 5 + 'ScanDonut': + - positive: + flavors: + - 'hacktool_win_shellcode_donut' + priority: 5 'ScanDmg': - positive: flavors: diff --git a/configs/python/backend/taste/taste.yara b/configs/python/backend/taste/taste.yara index 34e6236b..966d30c1 100644 --- a/configs/python/backend/taste/taste.yara +++ b/configs/python/backend/taste/taste.yara @@ -685,7 +685,49 @@ rule rpm_file { uint32(0) == 0x6D707264 or uint32(0) == 0xDBEEABED } -// Packer Files +// Packer / Loader Files + +rule hacktool_win_shellcode_donut +{ + meta: + author = "threatintel@volexity.com" + description = "Detection for donut loader shellcodes" + date = "2023-05-08" + reference = "https://github.com/TheWover/donut/" + + strings: + $loader_ver_1_0_0_V1_x64_raw = {48 89 5c 24 08 48 89 6c 24 10 48 89 74 24 18 57 41 56 41 57 48 81 ec 00 05 00 00 33 ff 48 8b d9 39 b9 38 02 00 00 0f 84 ce 00 00 00 4c 8b 41 28} + $loader_ver_1_0_0_V1_x64_b64 = "\x48\x89\x5c\x24\x08\x48\x89\x6c\x24\x10\x48\x89\x74\x24\x18\x57\x41\x56\x41\x57\x48\x81\xec\x00\x05\x00\x00\x33\xff\x48\x8b\xd9\x39\xb9\x38\x02\x00\x00\x0f\x84\xce\x00\x00\x00\x4c\x8b\x41\x28" base64 + $loader_ver_1_0_0_V1_x86_raw = {81 ec d4 02 00 00 53 55 56 8b b4 24 e4 02 00 00 33 db 57 8b fb 39 9e 38 02 00 00 0f 84 ea 00 00 00 ff 76 2c ff 76 28 ff b6 8c 00 00 00 ff b6 88} + $loader_ver_1_0_0_V1_x86_b64 = "\x81\xec\xd4\x02\x00\x00\x53\x55\x56\x8b\xb4\x24\xe4\x02\x00\x00\x33\xdb\x57\x8b\xfb\x39\x9e\x38\x02\x00\x00\x0f\x84\xea\x00\x00\x00\xff\x76\x2c\xff\x76\x28\xff\xb6\x8c\x00\x00\x00\xff\xb6\x88" base64 + $loader_ver_0_9_3_V1_x64_raw = {48 89 5c 24 08 48 89 6c 24 10 48 89 74 24 18 57 48 81 ec 00 05 00 00 33 ff 48 8b d9 48 39 b9 38 02 00 00 0f 84 c0 00 00 00 4c 8b 41 28 48 8b 91} + $loader_ver_0_9_3_V1_x64_b64 = "\x48\x89\x5c\x24\x08\x48\x89\x6c\x24\x10\x48\x89\x74\x24\x18\x57\x48\x81\xec\x00\x05\x00\x00\x33\xff\x48\x8b\xd9\x48\x39\xb9\x38\x02\x00\x00\x0f\x84\xc0\x00\x00\x00\x4c\x8b\x41\x28\x48\x8b\x91" base64 + $loader_ver_0_9_3_V2_x64_raw = {55 48 81 EC 30 05 00 00 48 8D AC 24 80 00 00 00 48 89 8D C0 04 00 00 48 C7 85 A8 04 00 00 00 00 00 00 48 8B 85 C0 04 00 00 48 8B 80 38 02 00 00} + $loader_ver_0_9_3_V2_x64_b64 = "\x55\x48\x81\xEC\x30\x05\x00\x00\x48\x8D\xAC\x24\x80\x00\x00\x00\x48\x89\x8D\xC0\x04\x00\x00\x48\xC7\x85\xA8\x04\x00\x00\x00\x00\x00\x00\x48\x8B\x85\xC0\x04\x00\x00\x48\x8B\x80\x38\x02\x00\x00" base64 + $loader_ver_0_9_3_V1_x32_raw = {81 ec cc 02 00 00 53 55 56 8b b4 24 dc 02 00 00 33 db 57 8b fb 8b 86 38 02 00 00 0b 86 3c 02 00 00 0f 84 d4 00 00 00 ff 76 2c ff 76 28 ff b6 8c} + $loader_ver_0_9_3_V1_x32_b64 = "\x81\xec\xcc\x02\x00\x00\x53\x55\x56\x8b\xb4\x24\xdc\x02\x00\x00\x33\xdb\x57\x8b\xfb\x8b\x86\x38\x02\x00\x00\x0b\x86\x3c\x02\x00\x00\x0f\x84\xd4\x00\x00\x00\xff\x76\x2c\xff\x76\x28\xff\xb6\x8c" base64 + $loader_ver_0_9_3_V2_x32_raw = {55 89 E5 56 53 81 EC 10 03 00 00 C7 45 F4 00 00 00 00 8B 4D 08 8B 99 3C 02 00 00 8B 89 38 02 00 00 89 CE 83 F6 00 89 F0 80 F7 00 89 DA 09 D0 85} + $loader_ver_0_9_3_V2_x32_b64 = "\x55\x89\xE5\x56\x53\x81\xEC\x10\x03\x00\x00\xC7\x45\xF4\x00\x00\x00\x00\x8B\x4D\x08\x8B\x99\x3C\x02\x00\x00\x8B\x89\x38\x02\x00\x00\x89\xCE\x83\xF6\x00\x89\xF0\x80\xF7\x00\x89\xDA\x09\xD0\x85" base64 + $loader_ver_0_9_3_V3_x32_raw = {55 89 E5 56 53 81 EC 10 03 00 00 C7 45 F4 00 00 00 00 8B 45 08 8B 90 3C 02 00 00 8B 80 38 02 00 00 89 C6 83 F6 00 89 F1 89 D0 80 F4 00 89 C3 89} + $loader_ver_0_9_3_V3_x32_b64 = "\x55\x89\xE5\x56\x53\x81\xEC\x10\x03\x00\x00\xC7\x45\xF4\x00\x00\x00\x00\x8B\x45\x08\x8B\x90\x3C\x02\x00\x00\x8B\x80\x38\x02\x00\x00\x89\xC6\x83\xF6\x00\x89\xF1\x89\xD0\x80\xF4\x00\x89\xC3\x89" base64 + $loader_ver_0_9_3_V4_x32_raw = {55 8B EC 81 EC 10 03 00 00 83 65 BC 00 6A 5C 68 0C B0 42 00 E8 67 C5 00 00 59 59 85 C0 74 14 6A 5C 68 1C B0 42 00 E8 55 C5 00 00 59 59 40 89 45} + $loader_ver_0_9_3_V4_x32_b64 = "\x55\x8B\xEC\x81\xEC\x10\x03\x00\x00\x83\x65\xBC\x00\x6A\x5C\x68\x0C\xB0\x42\x00\xE8\x67\xC5\x00\x00\x59\x59\x85\xC0\x74\x14\x6A\x5C\x68\x1C\xB0\x42\x00\xE8\x55\xC5\x00\x00\x59\x59\x40\x89\x45" base64 + $loader_ver_0_9_2_V1_x64_raw = {55 48 89 e5 48 81 ec b0 00 00 00 48 89 4d 10 48 8b 45 10 48 89 45 e8 48 8b 45 e8 48 8b 40 48 48 89 45 e0 48 8b 45 e8 48 8b 48 28 48 8b 55 e0 48} + $loader_ver_0_9_2_V1_x64_b64 = "\x55\x48\x89\xe5\x48\x81\xec\xb0\x00\x00\x00\x48\x89\x4d\x10\x48\x8b\x45\x10\x48\x89\x45\xe8\x48\x8b\x45\xe8\x48\x8b\x40\x48\x48\x89\x45\xe0\x48\x8b\x45\xe8\x48\x8b\x48\x28\x48\x8b\x55\xe0\x48" base64 + $loader_ver_0_9_2_V1_x86_raw = {83 ec 20 53 55 56 57 8b 7c 24 34 ff 77 2c ff 77 28 ff 77 4c ff 77 48 57 e8 d1 1a 00 00 ff 77 2c 8b f0 ff 77 28 ff 77 54 ff 77 50 57 e8 bd 1a 00} + $loader_ver_0_9_2_V1_x86_b64 = "\x83\xec\x20\x53\x55\x56\x57\x8b\x7c\x24\x34\xff\x77\x2c\xff\x77\x28\xff\x77\x4c\xff\x77\x48\x57\xe8\xd1\x1a\x00\x00\xff\x77\x2c\x8b\xf0\xff\x77\x28\xff\x77\x54\xff\x77\x50\x57\xe8\xbd\x1a\x00" base64 + $loader_ver_0_9_1_V1_x64_raw = {48 89 5c 24 08 48 89 74 24 10 57 48 83 ec 60 33 d2 48 8b f9 48 8d 4c 24 20 44 8d 42 40 e8 a2 10 00 00 44 8b 0f 4c 8d 47 24 41 83 e9 24 48 8d 57} + $loader_ver_0_9_1_V1_x64_b64 = "\x48\x89\x5c\x24\x08\x48\x89\x74\x24\x10\x57\x48\x83\xec\x60\x33\xd2\x48\x8b\xf9\x48\x8d\x4c\x24\x20\x44\x8d\x42\x40\xe8\xa2\x10\x00\x00\x44\x8b\x0f\x4c\x8d\x47\x24\x41\x83\xe9\x24\x48\x8d\x57" base64 + $loader_ver_0_9_1_V1_x86_raw = {83 ec 20 8d 04 24 53 55 56 57 6a 20 6a 00 50 e8 69 0e 00 00 8b 74 24 40 8b 06 83 e8 24 50 8d 46 24 50 8d 46 14 50 8d 46 04 50 e8 2a 0c 00 00 ff} + $loader_ver_0_9_1_V1_x86_b64 = "\x83\xec\x20\x8d\x04\x24\x53\x55\x56\x57\x6a\x20\x6a\x00\x50\xe8\x69\x0e\x00\x00\x8b\x74\x24\x40\x8b\x06\x83\xe8\x24\x50\x8d\x46\x24\x50\x8d\x46\x14\x50\x8d\x46\x04\x50\xe8\x2a\x0c\x00\x00\xff" base64 + $loader_ver_0_9_0_V1_x64_raw = {55 48 89 e5 48 83 c4 80 48 89 4d 10 48 8b 45 10 48 89 45 f0 c7 45 ec 24 00 00 00 8b 55 ec 48 8b 45 f0 48 01 d0 48 89 45 e0 48 8b 45 f0 8b 00 2b} + $loader_ver_0_9_0_V1_x64_b64 = "\x55\x48\x89\xe5\x48\x83\xc4\x80\x48\x89\x4d\x10\x48\x8b\x45\x10\x48\x89\x45\xf0\xc7\x45\xec\x24\x00\x00\x00\x8b\x55\xec\x48\x8b\x45\xf0\x48\x01\xd0\x48\x89\x45\xe0\x48\x8b\x45\xf0\x8b\x00\x2b" base64 + $loader_ver_0_9_0_V1_x86_raw = {55 89 e5 56 53 83 ec 60 8b 45 08 89 45 f0 c7 45 ec 24 00 00 00 8b 55 f0 8b 45 ec 01 d0 89 45 e8 8b 45 f0 8b 00 2b 45 ec 8b 55 f0 8d 4a 14 8b 55} + $loader_ver_0_9_0_V1_x86_b64 = "\x55\x89\xe5\x56\x53\x83\xec\x60\x8b\x45\x08\x89\x45\xf0\xc7\x45\xec\x24\x00\x00\x00\x8b\x55\xf0\x8b\x45\xec\x01\xd0\x89\x45\xe8\x8b\x45\xf0\x8b\x00\x2b\x45\xec\x8b\x55\xf0\x8d\x4a\x14\x8b\x55" base64 + + condition: + any of them +} rule upx_file { meta: diff --git a/docs/README.md b/docs/README.md index 7f8e56ff..cfae9a3c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -747,6 +747,7 @@ The table below describes each scanner and its options. Each scanner has the hid | ScanCapa | Analyzes executable files with FireEye [capa](https://github.com/fireeye/capa) | `tempfile_directory` -- location where `tempfile` will write temporary files (defaults to `/tmp/`)
`location_rules` -- location of the capa rules file or directory (defaults to `/etc/capa/rules/`)
`location_signatures` -- location of the capa signature file or directory (defaults to `/etc/capa/signatures/`) | | ScanCcn | Flags files containing credit card formatted numbers | N/A | [Ryan O'Horo](https://github.com/ryanohoro) | | ScanCuckoo | Sends files to a Cuckoo sandbox | `url` -- URL of the Cuckoo sandbox (defaults to None)
`priority` -- Cuckoo priority assigned to the task (defaults to `3`)
`timeout` -- amount of time (in seconds) to wait for the task to upload (defaults to `10`)
`unique` -- boolean that tells Cuckoo to only analyze samples that have not been analyzed before (defaults to `True`)
`username` -- username used for authenticating to Cuckoo (defaults to None, optionally read from environment variable "CUCKOO_USERNAME")
`password` -- password used for authenticating to Cuckoo (defaults to None, optionally read from environment variable "CUCKOO_PASSWORD") | +| ScanDonut | Decrypts, extracts config and embedded payloads from Donut loader payloads (https://github.com/TheWover/donut) using donut-decrypt (https://github.com/volexity/donut-decryptor/) | | [Ryan O'Horo](https://github.com/ryanohoro) | | ScanDmg | Collects metadata from Mac DMG and other disk images, and extracts archived files | `limit` -- maximum number of files to extract (defaults to `1000`) | | ScanDocx | Collects metadata and extracts text from docx files | `extract_text` -- boolean that determines if document text should be extracted as a child file (defaults to `False`) | | ScanElf | Collects metadata from ELF files | N/A | @@ -758,7 +759,7 @@ The table below describes each scanner and its options. Each scanner has the hid | ScanFloss | Analyzes executable files with FireEye [floss](https://github.com/fireeye/flare-floss) | `tempfile_directory` -- location where `tempfile` will write temporary files (defaults to `/tmp/`)
`limit` -- Maximum amount of strings to collect. (defaults to `100`) | | ScanFooter | Collects file footer | `length` -- number of footer characters to log as metadata (defaults to `50`)
`encodings` -- list of output encodings, any of `classic`, `raw`, `hex`, `backslash` | | ScanGif | Extracts data embedded in GIF files | N/A | -| ScanGzip | Decompresses gzip files | N/A +| ScanGzip | Decompresses gzip files | N/A | ScanHash | Calculates file hash values | N/A | | ScanHeader | Collects file header | `length` -- number of header characters to log as metadata (defaults to `50`)
`encodings` -- list of output encodings, any of `classic`, `raw`, `hex`, `backslash` | | ScanHtml | Collects metadata and extracts embedded files from HTML files | `parser` -- sets the HTML parser used during scanning (defaults to `html.parser`)
`max_links` -- Maximum amount of links to output in hyperlinks field (defaults to `50`) | @@ -807,7 +808,7 @@ The table below describes each scanner and its options. Each scanner has the hid | ScanXml | Log metadata and extract files from XML files | `extract_tags` -- list of XML tags that will have their text extracted as child files (defaults to empty list)
`metadata_tags` -- list of XML tags that will have their text logged as metadata (defaults to empty list) | | ScanYara | Scans files with YARA rules | `location` -- location of the YARA rules file or directory (defaults to `/etc/strelka/yara/`)
`store_offset` -- Stores file offset for YARA match
`offset_meta_key` -- YARA meta key that must exist in the YARA rule for the offset to be stored.
`offset_padding` -- Amount of data to be stored before and after offset for additional context. | | ScanZip | Extracts files from zip archives | `limit` -- maximum number of files to extract (defaults to `1000`)
`password_file` -- location of passwords file for zip archives (defaults to `/etc/strelka/passwords.dat`) | -| ScanZlib | Decompresses gzip files | N/A +| ScanZlib | Decompresses gzip files | N/A ## Tests As Strelka consists of many scanners and dependencies for those scanners. Pytests are particularly valuable for testing the ongoing functionality of Strelka and it's scanners. Tests allow users to write test cases that verify the correct behavior of Strelka scanners to ensure that the scanners remain reliable and accurate. Additionally, using pytests can help streamline the development process, allowing developers to focus on writing new features and improvements for the scanners. Strelka contains a set of standard test fixture files that represent the types of files Strelka ingests. diff --git a/src/python/requirements.txt b/src/python/requirements.txt index d398f530..86f267c4 100644 --- a/src/python/requirements.txt +++ b/src/python/requirements.txt @@ -2,12 +2,14 @@ M2Crypto==0.38.0 PyMuPDF==1.21.1 beautifulsoup4==4.12.0 boltons==23.0.0 +chaskey @ git+https://github.com/volexity/chaskey-lts@2fd80f732dd9422a9e92556758180ceee3b5b4ec # For donut-decryptor construct==2.10.68 +donut-decryptor @ git+https://github.com/volexity/donut-decryptor@57ab87cc6274f4216e81409d66f4c011739bdf1f eml-parser>=1.17.5 +entropy @ git+https://github.com/jshlbrd/python-entropy.git@a49f1addccb88d54115832a6d1982b957a56f329 # v0.11 as of this commit esprima==4.0.1 flare-capa==5.0.0 formulas==1.2.6 -git+https://github.com/jshlbrd/python-entropy.git # v0.11 as of this freeze (package installed as 'entropy') grpcio==1.53.0 html5lib==1.1 inflection==0.5.1 diff --git a/src/python/strelka/scanners/scan_donut.py b/src/python/strelka/scanners/scan_donut.py new file mode 100644 index 00000000..0e13afe4 --- /dev/null +++ b/src/python/strelka/scanners/scan_donut.py @@ -0,0 +1,91 @@ +import json +import os +import tempfile + +from donut_decryptor.donut_decryptor import DonutDecryptor + +from strelka import strelka + + +class ScanDonut(strelka.Scanner): + """Extracts configs and modules from donut payloads""" + + def scan(self, data, file, options, expire_at): + tmp_directory = options.get("tmp_directory", "/tmp/") + + with tempfile.NamedTemporaryFile(dir=tmp_directory, mode="wb") as tmp_data: + tmp_data.write(data) + tmp_data.flush() + tmp_data.seek(0) + + try: + donuts = DonutDecryptor.find_donuts(tmp_data.name) + except Exception: + # Set output flag on error + self.flags.append("donut_decrypt_find_exception") + + self.event["total"] = {"donuts": len(donuts), "files": 0} + + self.event["donuts"] = [] + + for donut in donuts: + donut_data = {} + donut_data["instance_version"] = donut.instance_version + donut_data["loader_version"] = donut.loader_version + donut_data["offset_loader_start"] = donut.offset_loader_start + donut_data["offsets"] = {} + donut_data["offsets"]["size_instance"] = donut.offsets.get( + "size_instance" + ) + donut_data["offsets"]["encryption_start"] = donut.offsets.get( + "encryption_start" + ) + + self.event["donuts"].append(donut_data) + + try: + with tempfile.TemporaryDirectory() as tmpdirname: + donut.parse(tmpdirname) + + # Retrieve module file + with open( + os.path.join( + tmpdirname, f"mod_{os.path.basename(tmp_data.name)}" + ), + "rb", + ) as mod_file: + # Send extracted file back to Strelka + self.emit_file(mod_file.read()) + self.event["total"]["files"] += 1 + + # Retrieve instance metadata file + with open( + os.path.join( + tmpdirname, f"inst_{os.path.basename(tmp_data.name)}" + ), + "rb", + ) as inst_file: + inst_json = json.load(inst_file) + + # Remove unneeded File key + inst_json.pop("File", None) + + def change_dict_key( + d, old_key, new_key, default_value=None + ): + d[new_key] = d.pop(old_key, default_value) + + # Reformat the dictionary keys to be consistent + for key in inst_json: + change_dict_key( + inst_json, key, key.lower().replace(" ", "_") + ) + + # Update the current donut output + self.event["donuts"][len(self.event["donuts"]) - 1].update( + inst_json + ) + + except Exception: + # Set output flag on error + self.flags.append("donut_decrypt_parse_exception") diff --git a/src/python/strelka/tests/fixtures/test_donut.bin b/src/python/strelka/tests/fixtures/test_donut.bin new file mode 100644 index 0000000000000000000000000000000000000000..34aec934123a03cedf4176b90c7fc58556ddd3d7 GIT binary patch literal 35285 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%0A31`5v=S23d|Rq8br|}hbf-m$Bn>1b39@d$TE49$ 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 G|NTF4Tr_Y1 literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/fixtures/test_donut_compressed.bin b/src/python/strelka/tests/fixtures/test_donut_compressed.bin new file mode 100644 index 0000000000000000000000000000000000000000..7d125e3ed6056bd2387b084f10ddb703290c3463 GIT binary patch literal 33002 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

wB0Dj$h!4A7Ow1R+UY0X!$&p~+IIE=O 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 literal 0 HcmV?d00001 diff --git a/src/python/strelka/tests/test_scan_donut.py b/src/python/strelka/tests/test_scan_donut.py new file mode 100644 index 00000000..b02520fa --- /dev/null +++ b/src/python/strelka/tests/test_scan_donut.py @@ -0,0 +1,75 @@ +from pathlib import Path +from unittest import TestCase, mock + +from strelka.scanners.scan_donut import ScanDonut as ScanUnderTest +from strelka.tests import run_test_scan + + +def test_scan_donut(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": [], + "total": {"donuts": 1, "files": 1}, + "donuts": [ + { + "compression_type": "DONUT_COMPRESS_NONE", + "decoy_module": "", + "entropy_type": "DONUT_ENTROPY_DEFAULT", + "instance_type": "DONUT_INSTANCE_EMBED", + "module_type": "DONUT_MODULE_NET_DLL", + "instance_version": "1.0", + "loader_version": "1.0_64", + "offset_loader_start": 10196, + "offsets": {"size_instance": 4744, "encryption_start": 572}, + } + ], + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_donut.bin", + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event) + + +def test_scan_donut_compressed(mocker): + """ + Pass: Sample event matches output of scanner. + Failure: Unable to load file or sample event fails to match. + """ + + test_scan_event = { + "elapsed": mock.ANY, + "flags": [], + "total": {"donuts": 1, "files": 1}, + "donuts": [ + { + "compression_type": "DONUT_COMPRESS_APLIB", + "decoy_module": "", + "entropy_type": "DONUT_ENTROPY_DEFAULT", + "instance_type": "DONUT_INSTANCE_EMBED", + "module_type": "DONUT_MODULE_NET_DLL", + "instance_version": "1.0", + "loader_version": "1.0_64", + "offset_loader_start": 7913, + "offsets": {"size_instance": 4744, "encryption_start": 572}, + } + ], + } + + scanner_event = run_test_scan( + mocker=mocker, + scan_class=ScanUnderTest, + fixture_path=Path(__file__).parent / "fixtures/test_donut_compressed.bin", + ) + + TestCase.maxDiff = None + TestCase().assertDictEqual(test_scan_event, scanner_event)