From 903eb07a79d192e5db9bc9bba43eb01a0da5add4 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 27 Jun 2024 14:54:20 -0700 Subject: [PATCH 01/96] Notify user that ole2 files are encrypted Add keys to the metadata.json file that informs the user that a scanned ole2 file is encrypted. Information about the type of encryption is provided when the information is available. This feature co-authored by Micah Snyder. --- libclamav/ole2_extract.c | 364 ++++++++++- unit_tests/clamscan/ole2_encryption_test.py | 587 ++++++++++++++++++ .../ole2_encryption/password.fat.doc | Bin 0 -> 32768 bytes .../ole2_encryption/password.fat.docx | Bin 0 -> 7680 bytes .../ole2_encryption/password.fat.dot | Bin 0 -> 32768 bytes .../ole2_encryption/password.fat.ppsx | Bin 0 -> 107520 bytes .../ole2_encryption/password.fat.pptx | Bin 0 -> 107520 bytes .../ole2_encryption/password.fat.xls | Bin 0 -> 44032 bytes .../ole2_encryption/password.fat.xlsx | Bin 0 -> 13824 bytes .../ole2_encryption/password.ministream.doc | Bin 0 -> 9216 bytes .../ole2_encryption/password.ministream.docx | Bin 0 -> 7680 bytes .../ole2_encryption/password.ministream.dot | Bin 0 -> 9216 bytes .../ole2_encryption/password.ministream.ppsx | Bin 0 -> 42496 bytes .../ole2_encryption/password.ministream.pptx | Bin 0 -> 41984 bytes .../ole2_encryption/password.ministream.xls | Bin 0 -> 5632 bytes .../ole2_encryption/password.ministream.xlsx | Bin 0 -> 8192 bytes unit_tests/testcase.py | 14 + 17 files changed, 944 insertions(+), 21 deletions(-) create mode 100644 unit_tests/clamscan/ole2_encryption_test.py create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.doc create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.docx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.dot create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.ppsx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.pptx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.xls create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.fat.xlsx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.doc create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.docx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.dot create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.ppsx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.pptx create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.xls create mode 100644 unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.xlsx diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 1a0ef3e718..9c181426eb 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -152,6 +152,30 @@ typedef struct property_tag { unsigned char reserved[4]; } property_t; + +/* + * File Information Block Base. + * Naming is consistent with + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/26fb6c06-4e5c-4778-ab4e-edbf26a545bb + * */ +typedef struct __attribute__((packed)) fib_base_type { + uint16_t wIdent; + uint16_t nFib; + uint16_t unused; + uint16_t lid; + uint16_t pnNext; + uint16_t ABCDEFGHIJKLM; + uint16_t nFibBack; + uint32_t lKey; + uint8_t envr; + uint8_t NOPQRS; + uint16_t reserved3; + uint16_t reserved4; + uint32_t reserved5; + uint32_t reserved6; +} fib_base_t; + + struct ole2_list_node; typedef struct ole2_list_node { @@ -586,6 +610,259 @@ static cl_error_t handler_enum(ole2_header_t *hdr, property_t *prop, const char static cl_error_t handler_otf_encrypted(ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx, void *handler_ctx); static cl_error_t handler_otf(ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx, void *handler_ctx); +/* + * Compare strings ignoring case. + * This is a somewhat special case, since name is actually a utf-16 encoded string, stored + * in a char * with a known size of 64 bytes, so we can avoid a 'alloc since the size is + * so small. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/60fe8611-66c3-496b-b70d-a504c94c9ace + * + * @param name: 'name' from property_t struct + * @param name_size: 'name_size' from property_t struct + * @param keyword: Known value we are looking for + * + * @return int: Return '0' if the values are equivalent, something else otherwise. + */ +static int ole2_cmp_name(const char *const name, uint32_t name_size, const char *const keyword) +{ + char decoded[64]; + uint32_t i = 0, j = 0; + + if (64 < name_size || name_size % 2) { + return -1; + } + + memset(decoded, 0, sizeof(decoded)); + for (i = 0, j = 0; i < name_size; i += 2, j++) { + decoded[j] = ((unsigned char)name[i + 1]) << 4; + decoded[j] += name[i]; + } + + return strcasecmp(decoded, keyword); +} + +static void copy_fib_base(fib_base_t *pFib, const uint8_t *const ptr) +{ + memcpy(pFib, ptr, sizeof(fib_base_t)); + pFib->wIdent = ole2_endian_convert_16(pFib->wIdent); + pFib->nFib = ole2_endian_convert_16(pFib->nFib); + pFib->unused = ole2_endian_convert_16(pFib->unused); + pFib->lid = ole2_endian_convert_16(pFib->lid); + pFib->pnNext = ole2_endian_convert_16(pFib->pnNext); + + /*Don't know whether to do this or not.*/ + pFib->ABCDEFGHIJKLM = ole2_endian_convert_16(pFib->ABCDEFGHIJKLM); + + pFib->nFibBack = ole2_endian_convert_16(pFib->nFibBack); + pFib->nFibBack = ole2_endian_convert_32(pFib->lKey); + pFib->reserved3 = ole2_endian_convert_16(pFib->reserved3); + pFib->reserved4 = ole2_endian_convert_16(pFib->reserved4); + pFib->reserved5 = ole2_endian_convert_32(pFib->reserved5); + pFib->reserved6 = ole2_endian_convert_32(pFib->reserved6); +} + +static inline bool is_encrypted(const fib_base_t *const pFib) +{ + return pFib->ABCDEFGHIJKLM & (1 << 8); +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +static void dump_fib_base(fib_base_t *pFib) +{ + fprintf(stderr, "%s::%d::%x\n", __FUNCTION__, __LINE__, pFib->wIdent); +} + +/* + * This is currently unused, but I am leaving it in in case it can be useful in the future. See + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/79dea1e9-4dce-4fa0-8c6b-56ba37b68351 + * + * I have not looked into it in detail, but if it is a 1-byte xor, it could be possible to brute-force it in some cases. + * + */ +static inline bool is_obfuscated(const fib_base_t *const pFib) +{ + return pFib->ABCDEFGHIJKLM & (1 << 15); +} +#pragma GCC diagnostic pop + +typedef struct { + bool velvet_sweatshop; + + bool encrypted; + + const char *encryption_type; + +} encryption_status_t; + + const char * const ENCRYPTED_JSON_KEY = "Encrypted"; + +const char *const RC4_ENCRYPTION = "RC4"; +const char *const XOR_OBFUSCATION = "XORObfuscation"; +const char *const AES128_ENCRYPTION = "AES128"; +const char *const AES192_ENCRYPTION = "AES192"; +const char *const AES256_ENCRYPTION = "AES256"; +const char * const VELVET_SWEATSHOP_ENCRYPTION = "VelvetSweatshop"; + const char * const GENERIC_ENCRYPTED = "ENCRYPTION_TYPE_UNKNOWN"; + + const char * const OLE2_HEURISTIC_ENCRYPTED_WARNING = "Heuristics.Encrypted.OLE2"; + +const uint16_t XLS_XOR_OBFUSCATION = 0; +const uint16_t XLS_RC4_ENCRYPTION = 1; +const uint32_t MINISTREAM_CUTOFF_SIZE = 0x1000; + +static size_t get_stream_data_offset(ole2_header_t *hdr, const property_t *word_block, uint16_t sector) +{ + size_t offset = (1 << hdr->log2_big_block_size); + size_t sector_size = offset; + size_t fib_offset = 0; + + if (word_block->size < MINISTREAM_CUTOFF_SIZE) { + fib_offset = offset + sector_size * hdr->sbat_root_start; + fib_offset += (word_block->start_block * (1 << hdr->log2_small_block_size)); + } else { + fib_offset = offset + sector_size * sector; + } + + return fib_offset; +} + +/* See information about the File Information Block here + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/26fb6c06-4e5c-4778-ab4e-edbf26a545bb + * for more information. + */ +static void test_for_encryption(const property_t *word_block, ole2_header_t *hdr, encryption_status_t *pEncryptionStatus) +{ + + const uint8_t *ptr = NULL; + fib_base_t fib = {0}; + + uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); + + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + return; + } + + ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + return; + } + copy_fib_base(&fib, ptr); + +#define FIB_BASE_IDENTIFIER 0xa5ec + + if (FIB_BASE_IDENTIFIER != fib.wIdent) { + cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); + return; + } + + /*TODO: Look into whether or not it's possible to determine the xor key when + * a document is obfuscated with xor + * (is_obfuscated function) + */ + pEncryptionStatus->encrypted = is_encrypted(&fib); + + if (is_obfuscated(&fib)) { + pEncryptionStatus->encryption_type = XOR_OBFUSCATION; + } +} + +static size_t read_uint16(const uint8_t *const ptr, uint32_t ptr_size, uint32_t *idx, uint16_t *dst) +{ + if (*idx + sizeof(uint16_t) >= ptr_size) { + return 0; + } + + memcpy(dst, &(ptr[*idx]), 2); + *dst = ole2_endian_convert_16(*dst); + *idx += sizeof(uint16_t); + return sizeof(uint16_t); +} + +/* Search for the FILE_PASS number. If I don't find it, the next two bytes are + * a length. Consume that length of data, and try again. Go until you either find + * the number or run out of data. + */ +static bool find_file_pass(const uint8_t *const ptr, uint32_t ptr_size, uint32_t *idx) +{ + + uint16_t val, size; + + const uint32_t FILE_PASS_NUM = 47; + + while (true) { + if (sizeof(uint16_t) != read_uint16(ptr, ptr_size, idx, &val)) { + return false; + } + + if (sizeof(uint16_t) != read_uint16(ptr, ptr_size, idx, &size)) { + return false; + } + + if (FILE_PASS_NUM == val) { + return true; + } + + *idx += size; + } + + /*Should never get here.*/ + return false; +} + +/* + * Search for the FilePass structure. + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/cf9ae8d5-4e8c-40a2-95f1-3b31f16b5529 + */ +static void test_for_xls_encryption(const property_t *word_block, ole2_header_t *hdr, encryption_status_t *pEncryptionStatus) +{ + uint16_t tmp16; + uint32_t idx; + + uint32_t stream_data_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); + + uint32_t block_size = (1 << hdr->log2_big_block_size); + const uint8_t *const ptr = fmap_need_off_once(hdr->map, stream_data_offset, block_size); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", stream_data_offset, stream_data_offset); + return; + } + + /*Validate keyword*/ + idx = 0; + if (sizeof(uint16_t) != read_uint16(ptr, block_size, &idx, &tmp16)) { + return; + } + + /*Invalid keyword*/ + if (2057 != tmp16) { + return; + } + + /*Skip past this size.*/ + if (sizeof(uint16_t) != read_uint16(ptr, block_size, &idx, &tmp16)) { + return; + } + idx += tmp16; + + if (!find_file_pass(ptr, block_size, &idx)) { + return; + } + + if (sizeof(uint16_t) != read_uint16(ptr, block_size, &idx, &tmp16)) { + return; + } + + if (XLS_RC4_ENCRYPTION == tmp16) { + pEncryptionStatus->encryption_type = RC4_ENCRYPTION; + pEncryptionStatus->encrypted = true; + } else if (XLS_XOR_OBFUSCATION == tmp16) { + pEncryptionStatus->encryption_type = XOR_OBFUSCATION; + pEncryptionStatus->encrypted = true; + } +} + /** * @brief Walk an ole2 property tree, calling the handler for each file found * @@ -602,7 +879,8 @@ static cl_error_t handler_otf(ole2_header_t *hdr, property_t *prop, const char * static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t prop_index, ole2_walk_property_tree_file_handler handler, unsigned int rec_level, unsigned int *file_count, - cli_ctx *ctx, unsigned long *scansize, void *handler_ctx) + cli_ctx *ctx, unsigned long *scansize, void *handler_ctx, + encryption_status_t *pEncryptionStatus) { property_t prop_block[4]; int32_t idx, current_block, i, curindex; @@ -681,6 +959,23 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t prop_block[idx].start_block = ole2_endian_convert_32(prop_block[idx].start_block); prop_block[idx].size = ole2_endian_convert_32(prop_block[idx].size); + if ((64 < prop_block[idx].name_size) || (prop_block[idx].name_size % 2)) { + cli_dbgmsg("ERROR: Invalid name_size %d\n", prop_block[idx].name_size); + continue; + } + + if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { + test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { + test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { + test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptionInfo")) { + pEncryptionStatus->encrypted = true; + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptedPackage")) { + pEncryptionStatus->encrypted = true; + } + ole2_listmsg("printing ole2 property\n"); if (dir) print_ole2_property(&prop_block[idx]); @@ -711,7 +1006,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } hdr->sbat_root_start = prop_block[idx].start_block; if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx); + ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); return ret; @@ -752,7 +1047,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cli_dbgmsg("OLE2: filesize exceeded\n"); } if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize, handler_ctx); + ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); return ret; @@ -803,7 +1098,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } else dirname = NULL; if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx); + ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); if (dirname) { @@ -835,6 +1130,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } ole2_listmsg("loop ended: %d %d\n", ole2_list_size(&node_list), ole2_list_is_empty(&node_list)); } + ole2_list_delete(&node_list); return CL_SUCCESS; } @@ -2261,7 +2557,8 @@ static bool verify_key_aes(const encryption_key_t *const key, encryption_verifie static bool initialize_encryption_key( const uint8_t *encryptionInfoStreamPtr, size_t remainingBytes, - encryption_key_t *encryptionKey) + encryption_key_t *encryptionKey, + encryption_status_t *pEncryptionStatus) { bool bRet = false; size_t idx = 0; @@ -2334,7 +2631,8 @@ static bool initialize_encryption_key( cli_dbgmsg("ole2: Key length does not match algorithm id\n"); goto done; } - bAES = true; + bAES = true; + pEncryptionStatus->encryption_type = AES128_ENCRYPTION; break; case SE_HEADER_EI_AES192: // not implemented @@ -2342,7 +2640,8 @@ static bool initialize_encryption_key( cli_dbgmsg("ole2: Key length does not match algorithm id\n"); goto done; } - bAES = true; + bAES = true; + pEncryptionStatus->encryption_type = AES192_ENCRYPTION; goto done; case SE_HEADER_EI_AES256: // not implemented @@ -2350,10 +2649,12 @@ static bool initialize_encryption_key( cli_dbgmsg("ole2: Key length does not match algorithm id\n"); goto done; } - bAES = true; + bAES = true; + pEncryptionStatus->encryption_type = AES256_ENCRYPTION; goto done; case SE_HEADER_EI_RC4: // not implemented + pEncryptionStatus->encryption_type = RC4_ENCRYPTION; goto done; default: cli_dbgmsg("ole2: Invalid Algorithm ID: 0x%x\n", encryptionInfo.encryptionInfo.algorithmID); @@ -2443,8 +2744,14 @@ static bool initialize_encryption_key( memcpy(encryptionKey, &key, sizeof(encryption_key_t)); bRet = true; + pEncryptionStatus->encryption_type = VELVET_SWEATSHOP_ENCRYPTION; done: + if (pEncryptionStatus->encryption_type) { + pEncryptionStatus->encrypted = true; + } + pEncryptionStatus->velvet_sweatshop = bRet; + return bRet; } @@ -2468,8 +2775,9 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil unsigned long scansize, scansize2; const void *phdr; encryption_key_t key; - bool bEncrypted = false; - size_t encryption_offset = 0; + bool bEncrypted = false; + size_t encryption_offset = 0; + encryption_status_t encryption_status = {0}; cli_dbgmsg("in cli_ole2_extract()\n"); if (!ctx) { @@ -2571,13 +2879,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil bEncrypted = initialize_encryption_key( &(((const uint8_t *)phdr)[encryption_offset]), hdr.m_length - encryption_offset, - &key); - - cli_dbgmsg("Encrypted with VelvetSweatshop: %d\n", bEncrypted); - - if (ctx->wrkproperty == ctx->properties) { - cli_jsonint(ctx->wrkproperty, "EncryptedWithVelvetSweatshop", bEncrypted); - } + &key, &encryption_status); } /* 8 SBAT blocks per file block */ @@ -2590,7 +2892,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.has_vba = false; hdr.has_xlm = false; hdr.has_image = false; - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize, NULL); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize, NULL, &encryption_status); cli_bitset_free(hdr.bitset); hdr.bitset = NULL; if (!file_count || !(hdr.bitset = cli_bitset_init())) { @@ -2618,7 +2920,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil goto done; } file_count = 0; - ole2_walk_property_tree(&hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2, NULL); + ole2_walk_property_tree(&hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2, NULL, &encryption_status); ret = CL_CLEAN; *files = hdr.U; if (has_vba) { @@ -2635,13 +2937,33 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil /* PASS 2/B : OTF scan */ file_count = 0; if (bEncrypted) { - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf_encrypted, 0, &file_count, ctx, &scansize2, &key); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf_encrypted, 0, &file_count, ctx, &scansize2, &key, &encryption_status); } else { - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2, NULL); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2, NULL, &encryption_status); + } + } + + + if (SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) { + if (encryption_status.encrypted){ + if (encryption_status.encryption_type){ + cli_jsonstr(ctx->wrkproperty, ENCRYPTED_JSON_KEY, encryption_status.encryption_type); + } else { + cli_jsonstr(ctx->wrkproperty, ENCRYPTED_JSON_KEY, GENERIC_ENCRYPTED); + } + } + } + + if (SCAN_HEURISTIC_ENCRYPTED_DOC && encryption_status.encrypted && (!encryption_status.velvet_sweatshop)) { + cl_error_t status = cli_append_potentially_unwanted(ctx, OLE2_HEURISTIC_ENCRYPTED_WARNING ); + if (CL_SUCCESS != status) { + cli_errmsg("OLE2 : Unable to warn potentially unwanted signature '%s'\n", "Heuristics.Encrypted.OLE2"); + ret = status; } } done: + if (hdr.bitset) { cli_bitset_free(hdr.bitset); } diff --git a/unit_tests/clamscan/ole2_encryption_test.py b/unit_tests/clamscan/ole2_encryption_test.py new file mode 100644 index 0000000000..95b8d97fb2 --- /dev/null +++ b/unit_tests/clamscan/ole2_encryption_test.py @@ -0,0 +1,587 @@ +# Copyright (C) 2020-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +""" +Run clamscan tests. +""" + +import sys +import os +import re +import shutil + +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() + + # Remove scan temps directory between tests + if (self.path_tmp / "TD").exists(): + shutil.rmtree(self.path_tmp / "TD") + + self.verify_valgrind_log() + + def test_FAT_doc(self): + self.step_name('Test FAT doc') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.doc' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_doc_metadata(self): + self.step_name('Test FAT doc') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.doc' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_doc(self): + self.step_name('Test ministream doc') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.doc' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_doc_metadata(self): + self.step_name('Test ministream doc') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.doc' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + + def test_FAT_docx(self): + self.step_name('Test FAT docx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.docx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_docx_metadata(self): + self.step_name('Test FAT docx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.docx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_docx(self): + self.step_name('Test ministream docx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.docx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_docx_metadata(self): + self.step_name('Test ministream docx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.docx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_FAT_dot(self): + self.step_name('Test FAT dot') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.dot' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_dot_metadata(self): + self.step_name('Test FAT dot') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.dot' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_dot(self): + self.step_name('Test ministream dot') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.dot' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_dot_metadata(self): + self.step_name('Test ministream dot') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.dot' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_FAT_ppsx(self): + self.step_name('Test FAT ppsx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.ppsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_ppsx_metadata(self): + self.step_name('Test FAT ppsx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.ppsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_ppsx(self): + self.step_name('Test ministream ppsx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.ppsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_ppsx_metadata(self): + self.step_name('Test ministream ppsx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.ppsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_FAT_pptx(self): + self.step_name('Test FAT pptx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.pptx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_pptx_metadata(self): + self.step_name('Test FAT pptx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.pptx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_pptx(self): + self.step_name('Test ministream pptx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.pptx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_pptx_metadata(self): + self.step_name('Test ministream pptx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.pptx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_FAT_xls(self): + self.step_name('Test FAT xls') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.xls' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_xls_metadata(self): + self.step_name('Test FAT xls') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.xls' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"RC4"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_xls(self): + self.step_name('Test ministream xls') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.xls' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_xls_metadata(self): + self.step_name('Test ministream xls') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.xls' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"RC4"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_FAT_xlsx(self): + self.step_name('Test FAT xlsx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.xlsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_FAT_xlsx_metadata(self): + self.step_name('Test FAT xlsx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.fat.xlsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + def test_ministream_xlsx(self): + self.step_name('Test ministream xlsx') + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.xlsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --heuristic-alerts --alert-encrypted-doc {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 1 # virus + + expected_results = [ + 'Heuristics.Encrypted.OLE2 FOUND', + ] + self.verify_output(output.out, expected=expected_results) + + def test_ministream_xlsx_metadata(self): + self.step_name('Test ministream xlsx') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfile = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'ole2_encryption' / 'password.ministream.xlsx' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --gen-json --leave-temps --tempdir={tempdir} {testfile}'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb', + tempdir=tempdir, + testfile=testfile, + ) + output = self.execute_command(command) + + assert output.ec == 0 # clean + + expected_strings = [ '"Encrypted":"ENCRYPTION_TYPE_UNKNOWN"' ] + self.verify_metadata_json(tempdir, expected_strings) + + + + + diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.doc b/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.doc new file mode 100644 index 0000000000000000000000000000000000000000..48e0807180fbc31938ae8ffe25b7c0e1a1a307fd GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.ppsx b/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.ppsx new file mode 100644 index 0000000000000000000000000000000000000000..01f09d2526ce2addc91486372f8b1b69738ee97d GIT binary patch literal 107520 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 Mum2C{|MRr}7tp}s)&Kwi literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.xls b/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.xls new file mode 100644 index 0000000000000000000000000000000000000000..01fba9dd16e00dca18b58cd51aec638e86bd7118 GIT binary patch literal 44032 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 C?O9a- literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.xlsx b/unit_tests/input/other_scanfiles/ole2_encryption/password.fat.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..67c9abd2b7dcbb2b64f35d038f9dc7160a9b2f83 GIT binary patch literal 13824 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.doc b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.doc new file mode 100644 index 0000000000000000000000000000000000000000..64104d91b219ceb9a45737ce641b8be25c8ff6ea GIT binary patch literal 9216 zcmeI12UHZvx`2lqCFdYPvLG1&Q8E%FNfH>Mi@Y1{;aQ{?Z52*pPt`;_5Tlf0Q&zE0*C;_01^NxfDAwmpa4(;r~uRe z8UQWeEPxIG)yV+Xj6d>BV9g9*0k8tt0PFw`z1gTVlf@e|=G#fphKf=VFO3k>Z(B(khRl{Z=^A5r>$G7aEgx zZjIC`1+iykAq4HI#uGgbmY1Vx0|_Nvha=hT(o;n_RO`J<z@B*fwVjqVC4mN2 z|3JIyfn1!``F+l-dwe$c=O4zlq0ZHy#!(4!NFG?mXqZ{9p4G%*fN!N1$Pch@`yiD_ z<@FR?6TRO4dAn5Br%yXfgYK+?B;)#HGzL5;FJ!`oQ2xd}6#8tLt#`FCJG)bXt_<*^ z>>?p%d4I9Q=nxh^ow1@BM6!BrLTaX#XiBMOJPB5Th)@m}_6SOO)nL%8^=Hl<&qoJZ zXb)qG*)kl>r8uvX671qd>B8FjyeQTiDiHmqul-**JzB?29=ms`+B2 z6!8-C>L)6dh+C(18(UA$jErTWO#YiXEHb%^+Pk?A6C)^}kTGPOXNuT7=HT9MVKb$}BGRh%4kW2lGeYWg z5cILb$rSCNrk^9M>Bt?3fo8JDH)p7`>lY5JMjxWOZJ zbIPc%>X!{Lv)vKl&qa6AIwWf7Q<}+t39{fNZu3Vd)mruM zAF(#1%ke5zU#b^-fzYpty~N^0VH_Qan(X7^hHa&#=`4JW$m`Xr;iwur8>4t%K} z5REh1_(plL8%wYctG;jNYjo`7nM@(^J=@#G71&K0iAHNj_XGH5vUDbQ8htNSTA&h4 zYLuDA$>_ z6a8F8sPb_O<<@g3^M<7yt!|?3y(x%z$}yZ7zm^#b{ zQdjUrt7BuUR?KUeiY4;ZH;SOZ_eAfF+kzdxx}E*Df@k2%#mHVf>(2fIUm6(|HPUDA z%BDF&_`Q3i@&i0JW3L^nid*eraa>E2We(1lXif0h5DBuo-gAU7p5(S5OKYwjMdB-_ zh#B!~AT}}~ZsR8Dyqj+#xXEOl?3}%#=$+P2&x-M^)vc< zXpmz)aPP9Fp3D+X6q(4hC$<;sl^gDQvEPtv>DyiO(Q!$pe`7w*lQdLGE$w)`mLxTy z>rL6zQ!b|R_4dG8q}}7ojLEf{#$KkoZtbgK=51_t)Vs?YtvBp4zud5=#fZ@O2#OP-aSs$LngO7A(GdVie&O5qu^*I& z#%&S+7#|Zfdf>9IiQx=P%1G6Fmq z7M=vJ^dNx6gGrkp>`WYlp~n9zVWE=2SV1rg`JaMQl(A8A8s-E1{{Vi0{fHJ3I+4)m zfeieg5B&eS&9-=i$H24K-%6By?_BVL9Dm0H8z{*$_mruIyEOw=PIU73~Tt|jc z{1}EzY_a&&Mvy7Wwm-bps*jZTBmW=T3x`{!YpWY;`V3*@HQbPBPo7FmyEV*v_Y?5{ z15;a7MH6(HqDy4#=>{!!%GTO?T2%f?oGLZA0akZPTjcURZr%?Xi)`bW+b4;q;$(L{ z*o)FL<6pg_>CC{kRbPNTAh%70^bYvH<{HQK5BI0bWPF;}!!&T7w*dc#q2Zl%_9h@m z1OESpVwcUem0LRp{O|3;124#4_u`U&CHDMYC`+bxD)4`r`dq@dOlHxQ(#!F8uv&rt z>9IRevIvWUhP8W~Thc!aI8Zi?409BER5TB@@}1>r>w3@|r)qC=>nsJotvv8Q&O(!3 zs|l_CgQy=sXjv(}g=W>QU8DfQVm-TNr--En_Ku$o;lb5AZ)MVqn62CS}wD z_^r z*_k*_Jn$;SqAh}A977JnQTJBq-o&)zM9>2bu-7KY$6{2Hx+ESkpJJt znm$qMxrhS!|JI5Bi-7+}fd5m%=z#wdoXS%>fd8X`|Mh_XcXpM4|FeGh-)Rwc`^5i} z|M>rZRsVnch5voyzQj{`v#B~=vn(j)bFS#USAoL* zCf;i!gH9UlV`t)7)faG{n5@lnM1DnB@lMnBWHgpp1wCH_aM#;!d?Gl|xWtI&rEg9? z@rL$2JZ~cNZJ!6`-1OD{$ed|k-Q0U*uZZXTu~*GPN#j&t3{y{~V#}pOe4^MI$y7`f z=68pml(=6Eh_zyv$EuFDS)QiOMA5Lmh>6}_+K+e)5Q$hFLo3pi_&mED zU_3KXzTM=h5Z{eJ=Tfti{;chYo<*J7kQW}y{1rheE-&LKr~|KaxSa~@KD-4Q zi+6&pY^D+G>W>kmO~oQ-M(@jQ&B^4rlYj5}&66^RT} zpD<~B^FC^Ip*>_;X7jddg-d-~xHDQ|!z;3^C8$@-u|44`S!w*q=?2;}+`8m|Yyz(t7Ev?Nidp?l(C zDwmsXV+!d?UdjP;^EwHmX51e3M>Aox5>b%}F$pzlv6&jfJfZ zGO+v3+fk0Z)s&}RRH4zs=9+XYaH-e3Xhc6@Q__8lPpBv^)XZL;EZMq#TIYiunmK&~vy?8VoW5-4qP!-p+C4>`e z@8eU_x0Zw4aTNP}Axhre%YDV%uj}yxUHM_5k5xKYeeEGm18wA3pNDbpk{h1iz~3qK zFrPvfXKv6qK$F|NDHZmShMUkrwGrLWRaN9>K-csTDpUz@$x(X-I4U#1_ie7Ca%9^X{z z%pGG^W1xwGltH{L^xVxQW4XwqZn<*A*bI@+H=ePP{q@Tmm`A&{49{k*-ttwX+?ME;$b-@@cepAs;IzGK#1; z^&ULVuQ#-@~dcaYQQa*>U}v8lk|QDf~5jtrDJh1Gz`F_8U`z)aQypis$cU8jlfLlgeyk zZF-9@)p6X&eN;-rm##_iPd^KqJZ3y<>hs6ytqM%Mzf;E>&oX<#ur=tY^)(&ahodib zhjQmqB`8BYo;AqkB%se`bD43Ud9g5NA~WSK>fHJ9%pf7AUF{OzBF5m0!bd$UT?N?U z<{8xFe#r8E$7iYP9%BoaxW$AH9_CqGn{H_zj9k>!UucKR^+vmRj=A8tg*cIKmg9b$ zvekCdUBU5D)KMuKl(OzG>#3)=O(Db`e3y{*=wrM_th>a)uOh#nzy7=U7m87lhq{MS zFHjT2CV=>tbMGlMKY;eDGB_xM-jhSYDFguk`Yjae{=0Ip`TtG&PtN~A=l|E1ZG|kFnjQhKow>Rv-w{57nVezx1s(9F91&FFi!i^pK3w^_1eMAPy4gq z?1v3%hvrV8bAr~t+2MBzp!Py@Wl-V;vqmQ1%>Qm1@P5<})eWWZNB!d^)K+R}f*&L_ zFmvS!`rjV30-F0(0JFdr;EFh%l(@jGLEho}uD`ov1NEbUEzmom)7DQ9_ALTHN#~?4 zGcfmtfZaT4iTTMq^!KfQzNGpa=YI|y7JBakCA8nv{MY(J_;1t?Ju_TRYX8qHPA>e9 M#c7WC%lY4b0nhugPyhe` literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.docx b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.docx new file mode 100644 index 0000000000000000000000000000000000000000..6103359eaedc02a6ebccf9758cd991f1b3e96879 GIT binary patch literal 7680 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.dot b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.dot new file mode 100644 index 0000000000000000000000000000000000000000..4cd23e9cb30cfbaeb10e500488a9b8129e1121cc GIT binary patch literal 9216 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 V<7w^xnZ@ai|FJmB8GoMt{RczBvSk1O literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.ppsx b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.ppsx new file mode 100644 index 0000000000000000000000000000000000000000..b9cc455bd64391a4a80d1ba7f2d673e5b518b9b1 GIT binary patch literal 42496 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.pptx b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.pptx new file mode 100644 index 0000000000000000000000000000000000000000..3280f1577d3b9ced1a69a416d9505ba1631a7c91 GIT binary patch literal 41984 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 literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.xls b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.xls new file mode 100644 index 0000000000000000000000000000000000000000..c9fce8300daa68e5650c49835cd9d9ae4e371e22 GIT binary patch literal 5632 zcmeHL2{_eR8$b8v+UZiZD|_}CQAp|LTBAsjq$EpB*X|}%Obd<1(xy$8lBANjsQ4mo zC6!dflx-|Ug+xUux!-#(HGR!Yqn`Pm@0suE+~@bc@A;kgf8O&y=lsud@7xw?O=D8p z&?m_dO+X`TKE(YIPXL?ueV&XE5p2wju-WYQG??GgKd1-ZjkOhzAr}lH34q6(2Y@-g zG5rsErEg9Bf*!C#0q_e(^2i+dgJz)hFyiA+;xJ=A4mX}BKVC1!SaTXzSZUR?;Xn8yGY`fi8Q%$)^>#zcES#(Z7`Q|xmX}e z-P@ryXB3DO)OXL>@Ib_`%KFhpvQ^lVUCND3-q|dseX{Q3QgTUD-STU$hUX^w{}gcC zI7a)2?$fhe83l=wr%Q{=B`UQTL)_*eFCsh2Pt4qJ(_};`k9Zw+wYozv?-7GI#JXQD zEz@vnqc!Vh< zjHG9$HE~cX{XkA-y0%7*RFEPuoPkT_x?)+wavJ+`k4Z~xNq5wQ0<~SzYM(2nNdrl_7~|j z`A5of2_iDx!mwXuVMzB@bN*KN;wVS1SBj@ja^{&Hv3}Rl?$o+s(q_!N4BQIZzVBc@ zQ+n0$?Yi9QrAW3X>6keyUk|8K6VMBI9E5rOQhoKg)YJ9VakWcYrpl2w z#f0C-e0#dX%HCk>l%^wD<+B~8@GL$la2fMH?5Mg3nO`Zb`+`C>qy73f735W6-dy^e zW~9Id%P`-0(m|pY?x$XB(lM_hy6F7X8$*kCudoSD^W@ilE_Wyz^JQj9Mcb}z>99OH z-+hIupUmDhFF5u88d0s3AvUXyF>JuI7o~y*uOPV>!L}C)A8Yy!3R7;j_^lO~Pj`%n?F&0}YG+=6fz_M+6LM&0IJ^?6G&Yg+a9DMrk(pTJ z-Yd(@R4cH5VCUSO4+SjlmFCr*5ihEhB??K&VIHliJ65o>Ps&C7Or)pY_p>&Mg7oUzsxNy%`9x$FIO z8?4*8u*CBfUkjyPI@C{ZjDr<_fO4C`BJiTw`_*-b8c#^wXgQjtpY5boE92ek8}GR&B|vx+k3u`8@^;Zm&ArG zrhMU@P$}+m3!#akWV_pTd7p3${blGYybq18SLFX#uLR+%tUosq zWQ~ak$i&NJxTj3kD!zWJqldH#m@2i+z}#8T*Q-%*#Y-9bg(^c}Z-Ek-9U36z37Q}& zjaxy|PfSKrk(y0pds<%Im6)gp+n>$;Ss+BesOnPR88yX(`kLxiP2iO&^7>L{fD zIrV|VR^_r(v+KG&eQQG;mqpM}eGASAoXt z%5r7QlXw2%@#GV+lAwqT^%iV)xlPYbjU0AHNKyQ6ww*YyIo9{W}uQI zqi{8Q?FqXdOAYlp+U+bglq%({b(|H_P%xNnuk)3C=b4w)Q->XUJN|;hg-V#t2G{FU$BM&K*vxusX)R z{YE#t2Q5Nc{cj&Kr}|X-Cbl^{EXa3jRadlJeWNh9+TN0UJCza5A7{lKn%m8CZlvpJ zNHBw#4SVY~N}jV=DrkNgSK&G{T5}WkJv+L@4$I;iK1wm@x9uLwhHl;g8_X8 zys?bXco{g*>~o+7|7bFKj-ys{_Zz;XA9y9uOwYieRsvB{(9&1U(P-1~UFJ^n_(KDD zB90{joMfdCxg$vcGb*$>+~02%<5PLs!qhQ(N-fwr&NQ+h1F zfq-pqyNniWgZaDkXYI!^h;>zU$y(Cngagq2?}pBp0Z00Ta|TWlfGfv<)6j>u{uvK0 z|2OIHy#I0f?~`xfk2(t9HE4L^KyrlJCjxp0*B5zy#{2U{{1E?rY^*=; zJz6RU3g6XmA|ZRU4&L7nxpD6PR`4%?7iUZ`6O8C!`;WT*p;Q}-kN-CsZ+v<1Jv~+% o37J6z4^A6AIsZU>S$hn=;{BB%cJNsIze9a#|9^b{{$I!cH{^g7y#N3J literal 0 HcmV?d00001 diff --git a/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.xlsx b/unit_tests/input/other_scanfiles/ole2_encryption/password.ministream.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d5111ee929c733492b6683c81afc0469d7784563 GIT binary patch literal 8192 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 Cdo}w2 literal 0 HcmV?d00001 diff --git a/unit_tests/testcase.py b/unit_tests/testcase.py index 494c2ded27..172dd22c59 100644 --- a/unit_tests/testcase.py +++ b/unit_tests/testcase.py @@ -508,6 +508,20 @@ def execute_command(self, cmd, **kwargs): """ return self.execute(cmd, **kwargs) + # Find the metadata.json file and verify its contents. + def verify_metadata_json(self, tempdir, expected=[], unexpected=[]): + for parent, dirs, files in os.walk(tempdir): + for f in files: + if "metadata.json" == f: + with open(os.path.join(parent, f)) as handle: + metadata_json = handle.read() + self.verify_output(metadata_json, expected=expected, unexpected=unexpected) + + # There is only one metadata.json per scan. + # We found it, so we can break out of the loop. + break + + class Logger(object): From 72be1adad05078f750a1cbddda8bca9192a1928c Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 13:52:41 -0700 Subject: [PATCH 02/96] balh --- libclamav/ole2_extract.c | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 9c181426eb..fbd7c1eb1a 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -780,6 +780,178 @@ static size_t read_uint16(const uint8_t *const ptr, uint32_t ptr_size, uint32_t return sizeof(uint16_t); } + + + + + + + + + +static void parse_fibRgFcLcb97(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + +} + +static void parse_fibRgFcLcb2000(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + +} + + + + + + +static void parse_fibRgFcLcb2002(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); + + + + + + + + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + +} + +static void parse_fibRgFcLcb2003(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + +} + +static void parse_fibRgFcLcb2007(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + +} + + + + + +static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) { + + const uint8_t *ptr = NULL; + fib_base_t fib = {0}; + size_t i; + size_t to_read = 0x1000; + + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + return; + } + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + //ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + fprintf(stderr, "%s::%d::TODO: Add the correct size, trying to read 4k because, why not?\n", __FUNCTION__, __LINE__); + + ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + return; + } + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + copy_fib_base(&fib, ptr); + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + +#define FIB_BASE_IDENTIFIER 0xa5ec + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + if (FIB_BASE_IDENTIFIER != fib.wIdent) { + cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); + return; + } + + uint32_t idx = sizeof(fib); + /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ + uint16_t csw; + read_uint16(ptr, to_read, &idx, &csw); + if (0x000e != csw){ + fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); + return; + } + + idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ + + uint16_t cslw; + read_uint16(ptr, to_read, &idx, &cslw); + if (0x0016 != cslw) { + fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); + return; + } + idx += 88; /* Size of the FibRgLw97. Don't think I need anything from there. */ + + uint16_t cbRgFcLcb; + read_uint16(ptr, to_read, &idx, &cbRgFcLcb); +#if 0 + if (!= cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid cbRgFcLcb of 0x%x\n", __FUNCTION__, __LINE__, cbRgFcLcb); + return; + } +#else + fprintf(stderr, "nFib = 0x%x::cbRgFcLcb = 0x%x\n", fib.nFib, cbRgFcLcb ); + switch (fib.nFib){ + default: + fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); + return; + case 0x00c1: + if (0x005d != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb97(ptr); + break; + case 0x00d9: + if (0x006c != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2000(ptr); + break; + case 0x0101: + if (0x0088 != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2002(ptr); + break; + case 0x010c: + if (0x00a4 != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2003(ptr); + break; + case 0x0112: + if (0x00b7 != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2007(ptr); + break; + } +#endif + + + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = idx; i < to_read; i++){ + fprintf(stderr, "%02x ", ptr[i]); + } + fprintf(stderr, "\n"); + + + fprintf(stderr,"%s::%d::GOT TO END!!!\n", __FUNCTION__, __LINE__); + +} + /* Search for the FILE_PASS number. If I don't find it, the next two bytes are * a length. Consume that length of data, and try again. Go until you either find * the number or run out of data. @@ -966,6 +1138,9 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); + + test_for_pictures(&(prop_block[idx]), hdr); + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { From 324d94e8b621d0a0c26a3b1cb0d5a9d64dec364a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 14:05:53 -0700 Subject: [PATCH 03/96] blah --- libclamav/ole2_extract.c | 174 +---------------- libclamav/ole2_extract_images.h | 337 ++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+), 170 deletions(-) create mode 100644 libclamav/ole2_extract_images.h diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index fbd7c1eb1a..5a373f3e32 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -781,176 +781,10 @@ static size_t read_uint16(const uint8_t *const ptr, uint32_t ptr_size, uint32_t } - - - - - - - - -static void parse_fibRgFcLcb97(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); - -} - -static void parse_fibRgFcLcb2000(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); - -} - - - - - - -static void parse_fibRgFcLcb2002(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); - - - - - - - - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); - -} - -static void parse_fibRgFcLcb2003(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); - -} - -static void parse_fibRgFcLcb2007(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); - -} - - - - - -static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) { - - const uint8_t *ptr = NULL; - fib_base_t fib = {0}; - size_t i; - size_t to_read = 0x1000; - - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - - uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - - if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - return; - } - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - - //ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); - fprintf(stderr, "%s::%d::TODO: Add the correct size, trying to read 4k because, why not?\n", __FUNCTION__, __LINE__); - - ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - return; - } - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - copy_fib_base(&fib, ptr); - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - -#define FIB_BASE_IDENTIFIER 0xa5ec - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - - if (FIB_BASE_IDENTIFIER != fib.wIdent) { - cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); - return; - } - - uint32_t idx = sizeof(fib); - /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ - uint16_t csw; - read_uint16(ptr, to_read, &idx, &csw); - if (0x000e != csw){ - fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); - return; - } - - idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ - - uint16_t cslw; - read_uint16(ptr, to_read, &idx, &cslw); - if (0x0016 != cslw) { - fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); - return; - } - idx += 88; /* Size of the FibRgLw97. Don't think I need anything from there. */ - - uint16_t cbRgFcLcb; - read_uint16(ptr, to_read, &idx, &cbRgFcLcb); -#if 0 - if (!= cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid cbRgFcLcb of 0x%x\n", __FUNCTION__, __LINE__, cbRgFcLcb); - return; - } -#else - fprintf(stderr, "nFib = 0x%x::cbRgFcLcb = 0x%x\n", fib.nFib, cbRgFcLcb ); - switch (fib.nFib){ - default: - fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); - return; - case 0x00c1: - if (0x005d != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; - } - parse_fibRgFcLcb97(ptr); - break; - case 0x00d9: - if (0x006c != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; - } - parse_fibRgFcLcb2000(ptr); - break; - case 0x0101: - if (0x0088 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; - } - parse_fibRgFcLcb2002(ptr); - break; - case 0x010c: - if (0x00a4 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; - } - parse_fibRgFcLcb2003(ptr); - break; - case 0x0112: - if (0x00b7 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; - } - parse_fibRgFcLcb2007(ptr); - break; - } -#endif - - - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = idx; i < to_read; i++){ - fprintf(stderr, "%02x ", ptr[i]); - } - fprintf(stderr, "\n"); - - - fprintf(stderr,"%s::%d::GOT TO END!!!\n", __FUNCTION__, __LINE__); - -} +/*TODO: CLEAN up the dependencies, but don't want to change too much in this file, + * since it's branched from a file that hasn't been merged yet. + */ +#include "ole2_extract_images.h" /* Search for the FILE_PASS number. If I don't find it, the next two bytes are * a length. Consume that length of data, and try again. Go until you either find diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h new file mode 100644 index 0000000000..ca66e4272a --- /dev/null +++ b/libclamav/ole2_extract_images.h @@ -0,0 +1,337 @@ +#ifndef OLE2_EXTRACT_IMAGES_H_ +#define OLE2_EXTRACT_IMAGES_H_ + +typedef struct __attribute__((packed)) { + uint32_t fcStshfOrig; + uint32_t lcbStshfOrig; + uint32_t fcStshf; + uint32_t lcbStshf; + uint32_t fcPlcffndRef; + uint32_t lcbPlcffndRef; + uint32_t fcPlcffndTxt; + uint32_t lcbPlcffndTxt; + uint32_t fcPlcfandRef; + uint32_t lcbPlcfandRef; + uint32_t fcPlcfandTxt; + uint32_t lcbPlcfandTxt; + uint32_t fcPlcfSed; + uint32_t lcbPlcfSed; + uint32_t fcPlcPad; + uint32_t lcbPlcPad; + uint32_t fcPlcfPhe; + uint32_t lcbPlcfPhe; + uint32_t fcSttbfGlsy; + uint32_t lcbSttbfGlsy; + uint32_t fcPlcfGlsy; + uint32_t lcbPlcfGlsy; + uint32_t fcPlcfHdd; + uint32_t lcbPlcfHdd; + uint32_t fcPlcfBteChpx; + uint32_t lcbPlcfBteChpx; + uint32_t fcPlcfBtePapx; + uint32_t lcbPlcfBtePapx; + uint32_t fcPlcfSea; + uint32_t lcbPlcfSea; + uint32_t fcSttbfFfn; + uint32_t lcbSttbfFfn; + uint32_t fcPlcfFldMom; + uint32_t lcbPlcfFldMom; + uint32_t fcPlcfFldHdr; + uint32_t lcbPlcfFldHdr; + uint32_t fcPlcfFldFtn; + uint32_t lcbPlcfFldFtn; + uint32_t fcPlcfFldAtn; + uint32_t lcbPlcfFldAtn; + uint32_t fcPlcfFldMcr; + uint32_t lcbPlcfFldMcr; + uint32_t fcSttbfBkmk; + uint32_t lcbSttbfBkmk; + uint32_t fcPlcfBkf; + uint32_t lcbPlcfBkf; + uint32_t fcPlcfBkl; + uint32_t lcbPlcfBkl; + uint32_t fcCmds; + uint32_t lcbCmds; + uint32_t fcUnused1; + uint32_t lcbUnused1; + uint32_t fcSttbfMcr; + uint32_t lcbSttbfMcr; + uint32_t fcPrDrvr; + uint32_t lcbPrDrvr; + uint32_t fcPrEnvPort; + uint32_t lcbPrEnvPort; + uint32_t fcPrEnvLand; + uint32_t lcbPrEnvLand; + uint32_t fcWss; + uint32_t lcbWss; + uint32_t fcDop; + uint32_t lcbDop; + uint32_t fcSttbfAssoc; + uint32_t lcbSttbfAssoc; + uint32_t fcClx; + uint32_t lcbClx; + uint32_t fcPlcfPgdFtn; + uint32_t lcbPlcfPgdFtn; + uint32_t fcAutosaveSource; + uint32_t lcbAutosaveSource; + uint32_t fcGrpXstAtnOwners; + uint32_t lcbGrpXstAtnOwners; + uint32_t fcSttbfAtnBkmk; + uint32_t lcbSttbfAtnBkmk; + uint32_t fcUnused2; + uint32_t lcbUnused2; + uint32_t fcUnused3; + uint32_t lcbUnused3; + uint32_t fcPlcSpaMom; + uint32_t lcbPlcSpaMom; + uint32_t fcPlcSpaHdr; + uint32_t lcbPlcSpaHdr; + uint32_t fcPlcfAtnBkf; + uint32_t lcbPlcfAtnBkf; + uint32_t fcPlcfAtnBkl; + uint32_t lcbPlcfAtnBkl; + uint32_t fcPms; + uint32_t lcbPms; + uint32_t fcFormFldSttbs; + uint32_t lcbFormFldSttbs; + uint32_t fcPlcfendRef; + uint32_t lcbPlcfendRef; + uint32_t fcPlcfendTxt; + uint32_t lcbPlcfendTxt; + uint32_t fcPlcfFldEdn; + uint32_t lcbPlcfFldEdn; + uint32_t fcUnused4; + uint32_t lcbUnused4; + uint32_t fcDggInfo; + uint32_t lcbDggInfo; + uint32_t fcSttbfRMark; + uint32_t lcbSttbfRMark; + uint32_t fcSttbfCaption; + uint32_t lcbSttbfCaption; + uint32_t fcSttbfAutoCaption; + uint32_t lcbSttbfAutoCaption; + uint32_t fcPlcfWkb; + uint32_t lcbPlcfWkb; + uint32_t fcPlcfSpl; + uint32_t lcbPlcfSpl; + uint32_t fcPlcftxbxTxt; + uint32_t lcbPlcftxbxTxt; + uint32_t fcPlcfFldTxbx; + uint32_t lcbPlcfFldTxbx; + uint32_t fcPlcfHdrtxbxTxt; + uint32_t lcbPlcfHdrtxbxTxt; + uint32_t fcPlcffldHdrTxbx; + uint32_t lcbPlcffldHdrTxbx; + uint32_t fcStwUser; + uint32_t lcbStwUser; + uint32_t fcSttbTtmbd; + uint32_t lcbSttbTtmbd; + uint32_t fcCookieData; + uint32_t lcbCookieData; + uint32_t fcPgdMotherOldOld; + uint32_t lcbPgdMotherOldOld; + uint32_t fcBkdMotherOldOld; + uint32_t lcbBkdMotherOldOld; + uint32_t fcPgdFtnOldOld; + uint32_t lcbPgdFtnOldOld; + uint32_t fcBkdFtnOldOld; + uint32_t lcbBkdFtnOldOld; + uint32_t fcPgdEdnOldOld; + uint32_t lcbPgdEdnOldOld; + uint32_t fcBkdEdnOldOld; + uint32_t lcbBkdEdnOldOld; + uint32_t fcSttbfIntlFld; + uint32_t lcbSttbfIntlFld; + uint32_t fcRouteSlip; + uint32_t lcbRouteSlip; + uint32_t fcSttbSavedBy; + uint32_t lcbSttbSavedBy; + uint32_t fcSttbFnm; + uint32_t lcbSttbFnm; + uint32_t fcPlfLst; + uint32_t lcbPlfLst; + uint32_t fcPlfLfo; + uint32_t lcbPlfLfo; + uint32_t fcPlcfTxbxBkd; + uint32_t lcbPlcfTxbxBkd; + uint32_t fcPlcfTxbxHdrBkd; + uint32_t lcbPlcfTxbxHdrBkd; + uint32_t fcDocUndoWord9; + uint32_t lcbDocUndoWord9; + uint32_t fcRgbUse; + uint32_t lcbRgbUse; + uint32_t fcUsp; + uint32_t lcbUsp; + uint32_t fcUskf; + uint32_t lcbUskf; + uint32_t fcPlcupcRgbUse; + uint32_t lcbPlcupcRgbUse; + uint32_t fcPlcupcUsp; + uint32_t lcbPlcupcUsp; + uint32_t fcSttbGlsyStyle; + uint32_t lcbSttbGlsyStyle; + uint32_t fcPlgosl; + uint32_t lcbPlgosl; + uint32_t fcPlcocx; + uint32_t lcbPlcocx; + uint32_t fcPlcfBteLvc; + uint32_t lcbPlcfBteLvc; + uint32_t dwLowDateTime; + uint32_t dwHighDateTime; + uint32_t fcPlcfLvcPre10; + uint32_t lcbPlcfLvcPre10; + uint32_t fcPlcfAsumy; + uint32_t lcbPlcfAsumy; + uint32_t fcPlcfGram; + uint32_t lcbPlcfGram; + uint32_t fcSttbListNames; + uint32_t lcbSttbListNames; + uint32_t fcSttbfUssr; + uint32_t lcbSttbfUssr; +} FibRgFcLcb97; + + +static void parse_fibRgFcLcb97(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); +} + +static void parse_fibRgFcLcb2000(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); +} + +static void parse_fibRgFcLcb2002(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); +} + +static void parse_fibRgFcLcb2003(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); +} + +static void parse_fibRgFcLcb2007(const uint8_t * ptr){ + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); +} + +static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) { + + const uint8_t *ptr = NULL; + fib_base_t fib = {0}; + size_t i; + size_t to_read = 0x1000; + + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + return; + } + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + //ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + fprintf(stderr, "%s::%d::TODO: Add the correct size, trying to read 4k because, why not?\n", __FUNCTION__, __LINE__); + + ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + return; + } + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + copy_fib_base(&fib, ptr); + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + +#define FIB_BASE_IDENTIFIER 0xa5ec + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + + if (FIB_BASE_IDENTIFIER != fib.wIdent) { + cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); + return; + } + + uint32_t idx = sizeof(fib); + /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ + uint16_t csw; + read_uint16(ptr, to_read, &idx, &csw); + if (0x000e != csw){ + fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); + return; + } + + idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ + + uint16_t cslw; + read_uint16(ptr, to_read, &idx, &cslw); + if (0x0016 != cslw) { + fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); + return; + } + idx += 88; /* Size of the FibRgLw97. Don't think I need anything from there. */ + + uint16_t cbRgFcLcb; + read_uint16(ptr, to_read, &idx, &cbRgFcLcb); +#if 0 + if (!= cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid cbRgFcLcb of 0x%x\n", __FUNCTION__, __LINE__, cbRgFcLcb); + return; + } +#else + fprintf(stderr, "nFib = 0x%x::cbRgFcLcb = 0x%x\n", fib.nFib, cbRgFcLcb ); + switch (fib.nFib){ + default: + fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); + return; + case 0x00c1: + if (0x005d != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb97(ptr); + break; + case 0x00d9: + if (0x006c != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2000(ptr); + break; + case 0x0101: + if (0x0088 != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2002(ptr); + break; + case 0x010c: + if (0x00a4 != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2003(ptr); + break; + case 0x0112: + if (0x00b7 != cbRgFcLcb){ + fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + return; + } + parse_fibRgFcLcb2007(ptr); + break; + } +#endif + + + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = idx; i < to_read; i++){ + fprintf(stderr, "%02x ", ptr[i]); + } + fprintf(stderr, "\n"); + + + fprintf(stderr,"%s::%d::GOT TO END!!!\n", __FUNCTION__, __LINE__); + +} + + +#endif /* OLE2_EXTRACT_IMAGES_H_ */ From 9abf4847774ec7358acd2c51ba09749fc2d6cddd Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 14:16:12 -0700 Subject: [PATCH 04/96] blah --- libclamav/ole2_extract_images.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ca66e4272a..32cb285864 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -190,6 +190,13 @@ typedef struct __attribute__((packed)) { uint32_t lcbSttbfUssr; } FibRgFcLcb97; +static void copy_FibRgFcLcb97(FibRgFcLcb97 * pHeader, const uint8_t *const ptr) { + + memcpy(pHeader, ptr, sizeof(*pHeader)); + + + +} static void parse_fibRgFcLcb97(const uint8_t * ptr){ fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); @@ -202,6 +209,15 @@ static void parse_fibRgFcLcb2000(const uint8_t * ptr){ static void parse_fibRgFcLcb2002(const uint8_t * ptr){ fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); + + FibRgFcLcb97 header; + copy_FibRgFcLcb97(&header, ptr); + + + + + + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); } From 759a9268566729c91fc16c9f8e4a31d812336572 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 14:16:41 -0700 Subject: [PATCH 05/96] blah --- libclamav/ole2_extract_images.h | 186 ++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 32cb285864..9dfcb9296b 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -194,6 +194,192 @@ static void copy_FibRgFcLcb97(FibRgFcLcb97 * pHeader, const uint8_t *const ptr) memcpy(pHeader, ptr, sizeof(*pHeader)); +pHeader->fcStshfOrig = ole2_endian_convert_32(pHeader->fcStshfOrig); +pHeader->lcbStshfOrig = ole2_endian_convert_32(pHeader->lcbStshfOrig); +pHeader->fcStshf = ole2_endian_convert_32(pHeader->fcStshf); +pHeader->lcbStshf = ole2_endian_convert_32(pHeader->lcbStshf); +pHeader->fcPlcffndRef = ole2_endian_convert_32(pHeader->fcPlcffndRef); +pHeader->lcbPlcffndRef = ole2_endian_convert_32(pHeader->lcbPlcffndRef); +pHeader->fcPlcffndTxt = ole2_endian_convert_32(pHeader->fcPlcffndTxt); +pHeader->lcbPlcffndTxt = ole2_endian_convert_32(pHeader->lcbPlcffndTxt); +pHeader->fcPlcfandRef = ole2_endian_convert_32(pHeader->fcPlcfandRef); +pHeader->lcbPlcfandRef = ole2_endian_convert_32(pHeader->lcbPlcfandRef); +pHeader->fcPlcfandTxt = ole2_endian_convert_32(pHeader->fcPlcfandTxt); +pHeader->lcbPlcfandTxt = ole2_endian_convert_32(pHeader->lcbPlcfandTxt); +pHeader->fcPlcfSed = ole2_endian_convert_32(pHeader->fcPlcfSed); +pHeader->lcbPlcfSed = ole2_endian_convert_32(pHeader->lcbPlcfSed); +pHeader->fcPlcPad = ole2_endian_convert_32(pHeader->fcPlcPad); +pHeader->lcbPlcPad = ole2_endian_convert_32(pHeader->lcbPlcPad); +pHeader->fcPlcfPhe = ole2_endian_convert_32(pHeader->fcPlcfPhe); +pHeader->lcbPlcfPhe = ole2_endian_convert_32(pHeader->lcbPlcfPhe); +pHeader->fcSttbfGlsy = ole2_endian_convert_32(pHeader->fcSttbfGlsy); +pHeader->lcbSttbfGlsy = ole2_endian_convert_32(pHeader->lcbSttbfGlsy); +pHeader->fcPlcfGlsy = ole2_endian_convert_32(pHeader->fcPlcfGlsy); +pHeader->lcbPlcfGlsy = ole2_endian_convert_32(pHeader->lcbPlcfGlsy); +pHeader->fcPlcfHdd = ole2_endian_convert_32(pHeader->fcPlcfHdd); +pHeader->lcbPlcfHdd = ole2_endian_convert_32(pHeader->lcbPlcfHdd); +pHeader->fcPlcfBteChpx = ole2_endian_convert_32(pHeader->fcPlcfBteChpx); +pHeader->lcbPlcfBteChpx = ole2_endian_convert_32(pHeader->lcbPlcfBteChpx); +pHeader->fcPlcfBtePapx = ole2_endian_convert_32(pHeader->fcPlcfBtePapx); +pHeader->lcbPlcfBtePapx = ole2_endian_convert_32(pHeader->lcbPlcfBtePapx); +pHeader->fcPlcfSea = ole2_endian_convert_32(pHeader->fcPlcfSea); +pHeader->lcbPlcfSea = ole2_endian_convert_32(pHeader->lcbPlcfSea); +pHeader->fcSttbfFfn = ole2_endian_convert_32(pHeader->fcSttbfFfn); +pHeader->lcbSttbfFfn = ole2_endian_convert_32(pHeader->lcbSttbfFfn); +pHeader->fcPlcfFldMom = ole2_endian_convert_32(pHeader->fcPlcfFldMom); +pHeader->lcbPlcfFldMom = ole2_endian_convert_32(pHeader->lcbPlcfFldMom); +pHeader->fcPlcfFldHdr = ole2_endian_convert_32(pHeader->fcPlcfFldHdr); +pHeader->lcbPlcfFldHdr = ole2_endian_convert_32(pHeader->lcbPlcfFldHdr); +pHeader->fcPlcfFldFtn = ole2_endian_convert_32(pHeader->fcPlcfFldFtn); +pHeader->lcbPlcfFldFtn = ole2_endian_convert_32(pHeader->lcbPlcfFldFtn); +pHeader->fcPlcfFldAtn = ole2_endian_convert_32(pHeader->fcPlcfFldAtn); +pHeader->lcbPlcfFldAtn = ole2_endian_convert_32(pHeader->lcbPlcfFldAtn); +pHeader->fcPlcfFldMcr = ole2_endian_convert_32(pHeader->fcPlcfFldMcr); +pHeader->lcbPlcfFldMcr = ole2_endian_convert_32(pHeader->lcbPlcfFldMcr); +pHeader->fcSttbfBkmk = ole2_endian_convert_32(pHeader->fcSttbfBkmk); +pHeader->lcbSttbfBkmk = ole2_endian_convert_32(pHeader->lcbSttbfBkmk); +pHeader->fcPlcfBkf = ole2_endian_convert_32(pHeader->fcPlcfBkf); +pHeader->lcbPlcfBkf = ole2_endian_convert_32(pHeader->lcbPlcfBkf); +pHeader->fcPlcfBkl = ole2_endian_convert_32(pHeader->fcPlcfBkl); +pHeader->lcbPlcfBkl = ole2_endian_convert_32(pHeader->lcbPlcfBkl); +pHeader->fcCmds = ole2_endian_convert_32(pHeader->fcCmds); +pHeader->lcbCmds = ole2_endian_convert_32(pHeader->lcbCmds); +pHeader->fcUnused1 = ole2_endian_convert_32(pHeader->fcUnused1); +pHeader->lcbUnused1 = ole2_endian_convert_32(pHeader->lcbUnused1); +pHeader->fcSttbfMcr = ole2_endian_convert_32(pHeader->fcSttbfMcr); +pHeader->lcbSttbfMcr = ole2_endian_convert_32(pHeader->lcbSttbfMcr); +pHeader->fcPrDrvr = ole2_endian_convert_32(pHeader->fcPrDrvr); +pHeader->lcbPrDrvr = ole2_endian_convert_32(pHeader->lcbPrDrvr); +pHeader->fcPrEnvPort = ole2_endian_convert_32(pHeader->fcPrEnvPort); +pHeader->lcbPrEnvPort = ole2_endian_convert_32(pHeader->lcbPrEnvPort); +pHeader->fcPrEnvLand = ole2_endian_convert_32(pHeader->fcPrEnvLand); +pHeader->lcbPrEnvLand = ole2_endian_convert_32(pHeader->lcbPrEnvLand); +pHeader->fcWss = ole2_endian_convert_32(pHeader->fcWss); +pHeader->lcbWss = ole2_endian_convert_32(pHeader->lcbWss); +pHeader->fcDop = ole2_endian_convert_32(pHeader->fcDop); +pHeader->lcbDop = ole2_endian_convert_32(pHeader->lcbDop); +pHeader->fcSttbfAssoc = ole2_endian_convert_32(pHeader->fcSttbfAssoc); +pHeader->lcbSttbfAssoc = ole2_endian_convert_32(pHeader->lcbSttbfAssoc); +pHeader->fcClx = ole2_endian_convert_32(pHeader->fcClx); +pHeader->lcbClx = ole2_endian_convert_32(pHeader->lcbClx); +pHeader->fcPlcfPgdFtn = ole2_endian_convert_32(pHeader->fcPlcfPgdFtn); +pHeader->lcbPlcfPgdFtn = ole2_endian_convert_32(pHeader->lcbPlcfPgdFtn); +pHeader->fcAutosaveSource = ole2_endian_convert_32(pHeader->fcAutosaveSource); +pHeader->lcbAutosaveSource = ole2_endian_convert_32(pHeader->lcbAutosaveSource); +pHeader->fcGrpXstAtnOwners = ole2_endian_convert_32(pHeader->fcGrpXstAtnOwners); +pHeader->lcbGrpXstAtnOwners = ole2_endian_convert_32(pHeader->lcbGrpXstAtnOwners); +pHeader->fcSttbfAtnBkmk = ole2_endian_convert_32(pHeader->fcSttbfAtnBkmk); +pHeader->lcbSttbfAtnBkmk = ole2_endian_convert_32(pHeader->lcbSttbfAtnBkmk); +pHeader->fcUnused2 = ole2_endian_convert_32(pHeader->fcUnused2); +pHeader->lcbUnused2 = ole2_endian_convert_32(pHeader->lcbUnused2); +pHeader->fcUnused3 = ole2_endian_convert_32(pHeader->fcUnused3); +pHeader->lcbUnused3 = ole2_endian_convert_32(pHeader->lcbUnused3); +pHeader->fcPlcSpaMom = ole2_endian_convert_32(pHeader->fcPlcSpaMom); +pHeader->lcbPlcSpaMom = ole2_endian_convert_32(pHeader->lcbPlcSpaMom); +pHeader->fcPlcSpaHdr = ole2_endian_convert_32(pHeader->fcPlcSpaHdr); +pHeader->lcbPlcSpaHdr = ole2_endian_convert_32(pHeader->lcbPlcSpaHdr); +pHeader->fcPlcfAtnBkf = ole2_endian_convert_32(pHeader->fcPlcfAtnBkf); +pHeader->lcbPlcfAtnBkf = ole2_endian_convert_32(pHeader->lcbPlcfAtnBkf); +pHeader->fcPlcfAtnBkl = ole2_endian_convert_32(pHeader->fcPlcfAtnBkl); +pHeader->lcbPlcfAtnBkl = ole2_endian_convert_32(pHeader->lcbPlcfAtnBkl); +pHeader->fcPms = ole2_endian_convert_32(pHeader->fcPms); +pHeader->lcbPms = ole2_endian_convert_32(pHeader->lcbPms); +pHeader->fcFormFldSttbs = ole2_endian_convert_32(pHeader->fcFormFldSttbs); +pHeader->lcbFormFldSttbs = ole2_endian_convert_32(pHeader->lcbFormFldSttbs); +pHeader->fcPlcfendRef = ole2_endian_convert_32(pHeader->fcPlcfendRef); +pHeader->lcbPlcfendRef = ole2_endian_convert_32(pHeader->lcbPlcfendRef); +pHeader->fcPlcfendTxt = ole2_endian_convert_32(pHeader->fcPlcfendTxt); +pHeader->lcbPlcfendTxt = ole2_endian_convert_32(pHeader->lcbPlcfendTxt); +pHeader->fcPlcfFldEdn = ole2_endian_convert_32(pHeader->fcPlcfFldEdn); +pHeader->lcbPlcfFldEdn = ole2_endian_convert_32(pHeader->lcbPlcfFldEdn); +pHeader->fcUnused4 = ole2_endian_convert_32(pHeader->fcUnused4); +pHeader->lcbUnused4 = ole2_endian_convert_32(pHeader->lcbUnused4); +pHeader->fcDggInfo = ole2_endian_convert_32(pHeader->fcDggInfo); +pHeader->lcbDggInfo = ole2_endian_convert_32(pHeader->lcbDggInfo); +pHeader->fcSttbfRMark = ole2_endian_convert_32(pHeader->fcSttbfRMark); +pHeader->lcbSttbfRMark = ole2_endian_convert_32(pHeader->lcbSttbfRMark); +pHeader->fcSttbfCaption = ole2_endian_convert_32(pHeader->fcSttbfCaption); +pHeader->lcbSttbfCaption = ole2_endian_convert_32(pHeader->lcbSttbfCaption); +pHeader->fcSttbfAutoCaption = ole2_endian_convert_32(pHeader->fcSttbfAutoCaption); +pHeader->lcbSttbfAutoCaption = ole2_endian_convert_32(pHeader->lcbSttbfAutoCaption); +pHeader->fcPlcfWkb = ole2_endian_convert_32(pHeader->fcPlcfWkb); +pHeader->lcbPlcfWkb = ole2_endian_convert_32(pHeader->lcbPlcfWkb); +pHeader->fcPlcfSpl = ole2_endian_convert_32(pHeader->fcPlcfSpl); +pHeader->lcbPlcfSpl = ole2_endian_convert_32(pHeader->lcbPlcfSpl); +pHeader->fcPlcftxbxTxt = ole2_endian_convert_32(pHeader->fcPlcftxbxTxt); +pHeader->lcbPlcftxbxTxt = ole2_endian_convert_32(pHeader->lcbPlcftxbxTxt); +pHeader->fcPlcfFldTxbx = ole2_endian_convert_32(pHeader->fcPlcfFldTxbx); +pHeader->lcbPlcfFldTxbx = ole2_endian_convert_32(pHeader->lcbPlcfFldTxbx); +pHeader->fcPlcfHdrtxbxTxt = ole2_endian_convert_32(pHeader->fcPlcfHdrtxbxTxt); +pHeader->lcbPlcfHdrtxbxTxt = ole2_endian_convert_32(pHeader->lcbPlcfHdrtxbxTxt); +pHeader->fcPlcffldHdrTxbx = ole2_endian_convert_32(pHeader->fcPlcffldHdrTxbx); +pHeader->lcbPlcffldHdrTxbx = ole2_endian_convert_32(pHeader->lcbPlcffldHdrTxbx); +pHeader->fcStwUser = ole2_endian_convert_32(pHeader->fcStwUser); +pHeader->lcbStwUser = ole2_endian_convert_32(pHeader->lcbStwUser); +pHeader->fcSttbTtmbd = ole2_endian_convert_32(pHeader->fcSttbTtmbd); +pHeader->lcbSttbTtmbd = ole2_endian_convert_32(pHeader->lcbSttbTtmbd); +pHeader->fcCookieData = ole2_endian_convert_32(pHeader->fcCookieData); +pHeader->lcbCookieData = ole2_endian_convert_32(pHeader->lcbCookieData); +pHeader->fcPgdMotherOldOld = ole2_endian_convert_32(pHeader->fcPgdMotherOldOld); +pHeader->lcbPgdMotherOldOld = ole2_endian_convert_32(pHeader->lcbPgdMotherOldOld); +pHeader->fcBkdMotherOldOld = ole2_endian_convert_32(pHeader->fcBkdMotherOldOld); +pHeader->lcbBkdMotherOldOld = ole2_endian_convert_32(pHeader->lcbBkdMotherOldOld); +pHeader->fcPgdFtnOldOld = ole2_endian_convert_32(pHeader->fcPgdFtnOldOld); +pHeader->lcbPgdFtnOldOld = ole2_endian_convert_32(pHeader->lcbPgdFtnOldOld); +pHeader->fcBkdFtnOldOld = ole2_endian_convert_32(pHeader->fcBkdFtnOldOld); +pHeader->lcbBkdFtnOldOld = ole2_endian_convert_32(pHeader->lcbBkdFtnOldOld); +pHeader->fcPgdEdnOldOld = ole2_endian_convert_32(pHeader->fcPgdEdnOldOld); +pHeader->lcbPgdEdnOldOld = ole2_endian_convert_32(pHeader->lcbPgdEdnOldOld); +pHeader->fcBkdEdnOldOld = ole2_endian_convert_32(pHeader->fcBkdEdnOldOld); +pHeader->lcbBkdEdnOldOld = ole2_endian_convert_32(pHeader->lcbBkdEdnOldOld); +pHeader->fcSttbfIntlFld = ole2_endian_convert_32(pHeader->fcSttbfIntlFld); +pHeader->lcbSttbfIntlFld = ole2_endian_convert_32(pHeader->lcbSttbfIntlFld); +pHeader->fcRouteSlip = ole2_endian_convert_32(pHeader->fcRouteSlip); +pHeader->lcbRouteSlip = ole2_endian_convert_32(pHeader->lcbRouteSlip); +pHeader->fcSttbSavedBy = ole2_endian_convert_32(pHeader->fcSttbSavedBy); +pHeader->lcbSttbSavedBy = ole2_endian_convert_32(pHeader->lcbSttbSavedBy); +pHeader->fcSttbFnm = ole2_endian_convert_32(pHeader->fcSttbFnm); +pHeader->lcbSttbFnm = ole2_endian_convert_32(pHeader->lcbSttbFnm); +pHeader->fcPlfLst = ole2_endian_convert_32(pHeader->fcPlfLst); +pHeader->lcbPlfLst = ole2_endian_convert_32(pHeader->lcbPlfLst); +pHeader->fcPlfLfo = ole2_endian_convert_32(pHeader->fcPlfLfo); +pHeader->lcbPlfLfo = ole2_endian_convert_32(pHeader->lcbPlfLfo); +pHeader->fcPlcfTxbxBkd = ole2_endian_convert_32(pHeader->fcPlcfTxbxBkd); +pHeader->lcbPlcfTxbxBkd = ole2_endian_convert_32(pHeader->lcbPlcfTxbxBkd); +pHeader->fcPlcfTxbxHdrBkd = ole2_endian_convert_32(pHeader->fcPlcfTxbxHdrBkd); +pHeader->lcbPlcfTxbxHdrBkd = ole2_endian_convert_32(pHeader->lcbPlcfTxbxHdrBkd); +pHeader->fcDocUndoWord9 = ole2_endian_convert_32(pHeader->fcDocUndoWord9); +pHeader->lcbDocUndoWord9 = ole2_endian_convert_32(pHeader->lcbDocUndoWord9); +pHeader->fcRgbUse = ole2_endian_convert_32(pHeader->fcRgbUse); +pHeader->lcbRgbUse = ole2_endian_convert_32(pHeader->lcbRgbUse); +pHeader->fcUsp = ole2_endian_convert_32(pHeader->fcUsp); +pHeader->lcbUsp = ole2_endian_convert_32(pHeader->lcbUsp); +pHeader->fcUskf = ole2_endian_convert_32(pHeader->fcUskf); +pHeader->lcbUskf = ole2_endian_convert_32(pHeader->lcbUskf); +pHeader->fcPlcupcRgbUse = ole2_endian_convert_32(pHeader->fcPlcupcRgbUse); +pHeader->lcbPlcupcRgbUse = ole2_endian_convert_32(pHeader->lcbPlcupcRgbUse); +pHeader->fcPlcupcUsp = ole2_endian_convert_32(pHeader->fcPlcupcUsp); +pHeader->lcbPlcupcUsp = ole2_endian_convert_32(pHeader->lcbPlcupcUsp); +pHeader->fcSttbGlsyStyle = ole2_endian_convert_32(pHeader->fcSttbGlsyStyle); +pHeader->lcbSttbGlsyStyle = ole2_endian_convert_32(pHeader->lcbSttbGlsyStyle); +pHeader->fcPlgosl = ole2_endian_convert_32(pHeader->fcPlgosl); +pHeader->lcbPlgosl = ole2_endian_convert_32(pHeader->lcbPlgosl); +pHeader->fcPlcocx = ole2_endian_convert_32(pHeader->fcPlcocx); +pHeader->lcbPlcocx = ole2_endian_convert_32(pHeader->lcbPlcocx); +pHeader->fcPlcfBteLvc = ole2_endian_convert_32(pHeader->fcPlcfBteLvc); +pHeader->lcbPlcfBteLvc = ole2_endian_convert_32(pHeader->lcbPlcfBteLvc); +pHeader->dwLowDateTime = ole2_endian_convert_32(pHeader->dwLowDateTime); +pHeader->dwHighDateTime = ole2_endian_convert_32(pHeader->dwHighDateTime); +pHeader->fcPlcfLvcPre10 = ole2_endian_convert_32(pHeader->fcPlcfLvcPre10); +pHeader->lcbPlcfLvcPre10 = ole2_endian_convert_32(pHeader->lcbPlcfLvcPre10); +pHeader->fcPlcfAsumy = ole2_endian_convert_32(pHeader->fcPlcfAsumy); +pHeader->lcbPlcfAsumy = ole2_endian_convert_32(pHeader->lcbPlcfAsumy); +pHeader->fcPlcfGram = ole2_endian_convert_32(pHeader->fcPlcfGram); +pHeader->lcbPlcfGram = ole2_endian_convert_32(pHeader->lcbPlcfGram); +pHeader->fcSttbListNames = ole2_endian_convert_32(pHeader->fcSttbListNames); +pHeader->lcbSttbListNames = ole2_endian_convert_32(pHeader->lcbSttbListNames); +pHeader->fcSttbfUssr = ole2_endian_convert_32(pHeader->fcSttbfUssr); +pHeader->lcbSttbfUssr = ole2_endian_convert_32(pHeader->lcbSttbfUssr); } From c01cfbb372ea241823f0e16dbca8dd0ef03d3482 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 14:48:01 -0700 Subject: [PATCH 06/96] balh --- libclamav/ole2_extract_images.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 9dfcb9296b..668cd0fa51 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -399,6 +399,8 @@ static void parse_fibRgFcLcb2002(const uint8_t * ptr){ FibRgFcLcb97 header; copy_FibRgFcLcb97(&header, ptr); + fprintf(stderr, "%s::%d::Offset = %d (0x%x)\n", __FUNCTION__, __LINE__, header.fcDggInfo, header.fcDggInfo); + fprintf(stderr, "%s::%d::Size = %d (0x%x)\n", __FUNCTION__, __LINE__, header.lcbDggInfo, header.lcbDggInfo); @@ -504,7 +506,7 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); return; } - parse_fibRgFcLcb2002(ptr); + parse_fibRgFcLcb2002(&(ptr[idx])); break; case 0x010c: if (0x00a4 != cbRgFcLcb){ From d17e111cae2336f49f18c120de0a2810d57461b9 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 14:48:52 -0700 Subject: [PATCH 07/96] balh --- libclamav/ole2_extract_images.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 668cd0fa51..96abbc1d48 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -403,6 +403,7 @@ static void parse_fibRgFcLcb2002(const uint8_t * ptr){ fprintf(stderr, "%s::%d::Size = %d (0x%x)\n", __FUNCTION__, __LINE__, header.lcbDggInfo, header.lcbDggInfo); + fprintf(stderr, "%s::%d::The offset and size information is for the OfficeArtContent header information\n", __FUNCTION__, __LINE__); From 85c65350090f52eb83435f0b71145e650f4a6bd7 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 12 Jul 2024 14:53:32 -0700 Subject: [PATCH 08/96] balh --- libclamav/ole2_extract_images.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 96abbc1d48..36c61335c0 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -405,6 +405,7 @@ static void parse_fibRgFcLcb2002(const uint8_t * ptr){ fprintf(stderr, "%s::%d::The offset and size information is for the OfficeArtContent header information\n", __FUNCTION__, __LINE__); + fprintf(stderr, "https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3\n"); fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); From c2b145fa53ee02195c06a6aac0ff3f2777c667b3 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Mon, 15 Jul 2024 14:51:17 -0700 Subject: [PATCH 09/96] blah --- libclamav/ole2_extract.c | 93 +++++++++++++++++++- libclamav/ole2_extract_images.h | 145 ++++++++++++++++++++++++++++---- 2 files changed, 221 insertions(+), 17 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 5a373f3e32..76b7f5da20 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -636,6 +636,7 @@ static int ole2_cmp_name(const char *const name, uint32_t name_size, const char decoded[j] = ((unsigned char)name[i + 1]) << 4; decoded[j] += name[i]; } + fprintf(stderr, "%s::%d::%s\n", __FUNCTION__, __LINE__, decoded); return strcasecmp(decoded, keyword); } @@ -895,6 +896,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cl_error_t ret; char *name; int toval = 0; + property_t * wordDocStream = NULL; + property_t * tableStream = NULL; ole2_listmsg("ole2_walk_property_tree() called\n"); ole2_list_init(&node_list); @@ -953,6 +956,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } ole2_listmsg("reading prop block\n"); +//fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, &(prop_block[idx])); prop_block[idx].name_size = ole2_endian_convert_16(prop_block[idx].name_size); prop_block[idx].prev = ole2_endian_convert_32(prop_block[idx].prev); prop_block[idx].next = ole2_endian_convert_32(prop_block[idx].next); @@ -973,7 +977,27 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); - test_for_pictures(&(prop_block[idx]), hdr); + test_for_pictures(&(prop_block[idx]), tableStream, hdr); + wordDocStream = &(prop_block[idx]); + fprintf(stderr, "%s::%d::%p::setting wordDocStream\n", __FUNCTION__, __LINE__, wordDocStream); + + + +#if 0 + { + size_t i; + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = 0; i < 4096; i++){ + if (i && (0 == i%512)){ + fprintf("\n%s::%d::", __FUNCTION__, __LINE__); + } + fprintf(stderr, "%02x ", ((uint8_t*) &(prop_block[idx])[i]); + } + } +#endif + + + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); @@ -983,8 +1007,63 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t pEncryptionStatus->encrypted = true; } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptedPackage")) { pEncryptionStatus->encrypted = true; + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "1Table")) { + tableStream = &(prop_block[idx]); + + size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); + fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); + + fprintf(stderr, "%s::%d::FOUND THE OFFSET OF THE DATA. This offset + the offset referenced in 'header'\n", __FUNCTION__, __LINE__); + + + +#if 0 + + fprintf(stderr, "%s::%d::TODO: Move this to where the data is actually processed\n", __FUNCTION__, __LINE__); + //uint8_t* ptr = fmap_need_off_once(hdr->map, tableStream, 0x1000); + uint8_t* ptr = (uint8_t*) tableStream; + fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, ptr); + size_t i; + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = 0; i < 1024; i++){ + fprintf(stderr, "%02x ", ptr[ i ]); + } + fprintf(stderr, "\n"); + + + extract_images(&header, tableStream, hdr); + +#endif + + fprintf(stderr, "%s::%d::TODO: HANDLE TABLE STREAM\n", __FUNCTION__, __LINE__); + + + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "0Table")) { + +fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); + +#if 0 + tableStream = &(prop_block[idx]); + extract_images(&header, tableStream); + fprintf(stderr, "%s::%d::TODO: HANDLE TABLE STREAM\n", __FUNCTION__, __LINE__); +#endif } +#if 0 + if (wordDocStream && tableStream) { + test_for_pictures(wordDocStream, tableStream, hdr); + } +#endif + + +// { if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } + + + + + + + ole2_listmsg("printing ole2 property\n"); if (dir) print_ole2_property(&prop_block[idx]); @@ -1003,6 +1082,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } ole2_listmsg("prev: %d next %d child %d\n", prop_block[idx].prev, prop_block[idx].next, prop_block[idx].child); + //{ if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } + ole2_listmsg("node type: %d\n", prop_block[idx].type); switch (prop_block[idx].type) { case 5: /* Root Entry */ @@ -1138,8 +1219,18 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t break; } ole2_listmsg("loop ended: %d %d\n", ole2_list_size(&node_list), ole2_list_is_empty(&node_list)); +//{ if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures (end of loop)\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } } +#if 0 + fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, wordDocStream); + fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, tableStream); + + if (wordDocStream && tableStream){ + test_for_pictures(wordDocStream, tableStream, hdr); + } +#endif + ole2_list_delete(&node_list); return CL_SUCCESS; } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 36c61335c0..7f1074bca5 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -384,23 +384,79 @@ pHeader->lcbSttbfUssr = ole2_endian_convert_32(pHeader->lcbSttbfUssr); } +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/5dc1b9ed-818c-436f-8a4f-905a7ebb1ba9 */ +typedef struct __attribute__((packed)) { + uint16_t recVer_recInstance; + uint16_t recType; + uint32_t recLen; +} OfficeArtRecordHeader; + +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/5dc1b9ed-818c-436f-8a4f-905a7ebb1ba9 */ +static void copy_OfficeArtRecordHeader (OfficeArtRecordHeader * header, const uint8_t * const ptr) { + memcpy(header, ptr, sizeof(OfficeArtRecordHeader)); + + header->recType = ole2_endian_convert_16(header->recType); + header->recLen = ole2_endian_convert_32(header->recLen); + + fprintf(stderr, "%s::%d::recVer_recInstance = %x\n", __FUNCTION__, __LINE__, header->recVer_recInstance); + fprintf(stderr, "%s::%d::recType = %x\n", __FUNCTION__, __LINE__, header->recType); + fprintf(stderr, "%s::%d::recLen = %x\n", __FUNCTION__, __LINE__, header->recLen); + +} + + + + static void parse_fibRgFcLcb97(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } static void parse_fibRgFcLcb2000(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } -static void parse_fibRgFcLcb2002(const uint8_t * ptr){ + +/* + * TODO: MOVE THIS TO A STRUCTURE THAT IS PASSED IN, BUT + * CURRENTLY TRYING TO FIGURE OUT IF I AM FINDING ALL THE DATA CORRECTLY + */ +FibRgFcLcb97 header; + + + + +static void parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const property_t * table_stream){ + const uint8_t * ptr = &(base_ptr[idx]); + fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, table_stream); - FibRgFcLcb97 header; copy_FibRgFcLcb97(&header, ptr); - fprintf(stderr, "%s::%d::Offset = %d (0x%x)\n", __FUNCTION__, __LINE__, header.fcDggInfo, header.fcDggInfo); - fprintf(stderr, "%s::%d::Size = %d (0x%x)\n", __FUNCTION__, __LINE__, header.lcbDggInfo, header.lcbDggInfo); + /*Offset is in the TableStream.*/ + size_t offset = header.fcDggInfo; + size_t size = header.lcbDggInfo; + fprintf(stderr, "%s::%d::Offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); + fprintf(stderr, "%s::%d::Size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); + + /*Information about + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/dd7133b6-ed10-4bcb-be29-67b0544f884f + * at the beginning of + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3 + * */ + OfficeArtRecordHeader drawingGroupDataRecordHeader; + copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(ptr[offset])); + + fprintf(stderr, "%s::%d::Calling second time\n", __FUNCTION__, __LINE__); + copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(base_ptr[offset])); + +#if 0 + if (table_stream) { + fprintf(stderr, "%s::%d::Calling THIRD time\n", __FUNCTION__, __LINE__); + copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(((const uint8_t*) table_stream )[offset])); + } +#endif fprintf(stderr, "%s::%d::The offset and size information is for the OfficeArtContent header information\n", __FUNCTION__, __LINE__); @@ -408,28 +464,28 @@ static void parse_fibRgFcLcb2002(const uint8_t * ptr){ fprintf(stderr, "https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3\n"); - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); /* exit(11); */ } static void parse_fibRgFcLcb2003(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } static void parse_fibRgFcLcb2007(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); exit(11); + fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } -static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) { +static void test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr) { const uint8_t *ptr = NULL; fib_base_t fib = {0}; - size_t i; + //size_t i; size_t to_read = 0x1000; - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + fprintf(stderr,"%s::%d::Entering\n", __FUNCTION__, __LINE__); uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +// fprintf(stderr,"%s::%d::fib_offset = %x\n", __FUNCTION__, __LINE__, fib_offset); if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); @@ -445,9 +501,9 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); return; } - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); copy_fib_base(&fib, ptr); - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); #define FIB_BASE_IDENTIFIER 0xa5ec fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); @@ -456,6 +512,7 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); return; } + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); uint32_t idx = sizeof(fib); /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ @@ -465,6 +522,7 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); return; } + fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ @@ -508,7 +566,9 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); return; } - parse_fibRgFcLcb2002(&(ptr[idx])); + fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); + //parse_fibRgFcLcb2002(&(ptr[idx])); + parse_fibRgFcLcb2002(ptr, idx, table_stream); break; case 0x010c: if (0x00a4 != cbRgFcLcb){ @@ -528,11 +588,16 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) #endif +#if 0 + { + int i; fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); for (i = idx; i < to_read; i++){ fprintf(stderr, "%02x ", ptr[i]); } fprintf(stderr, "\n"); +} +#endif fprintf(stderr,"%s::%d::GOT TO END!!!\n", __FUNCTION__, __LINE__); @@ -540,4 +605,52 @@ static void test_for_pictures( const property_t *word_block, ole2_header_t *hdr) } + + +static void extract_images( FibRgFcLcb97 * header, const property_t * table_stream, ole2_header_t *hdr) { + + int i; + const uint8_t * table_stream_data = (const uint8_t*) table_stream; + size_t offset = header->fcDggInfo; + + fprintf(stderr, "%s::%d::%p::%lu::(0x%lx)Entering\n", __FUNCTION__, __LINE__, hdr, offset, offset); +#if 0 + const uint8_t * ptr = fmap_need_off_once(hdr->map, &(table_stream_data[offset]), header->lcbDggInfo ); +#else + const uint8_t * ptr = &(table_stream_data[offset]); +#endif + fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, ptr); + + OfficeArtRecordHeader officeArtDggContainer; + copy_OfficeArtRecordHeader (&officeArtDggContainer, ptr); + /*None of the values make sense.*/ + + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = 0; i < 8; i++){ + fprintf(stderr, "%02x ", table_stream_data[offset + i]); + } + fprintf(stderr, "\n"); + + offset += 8; //size of OfficeArtRecordHeader + + + + + + + + + fprintf(stderr, "%s::%d::Leaving\n", __FUNCTION__, __LINE__); +} + + + + + + + + + + + #endif /* OLE2_EXTRACT_IMAGES_H_ */ From 80630938638a749bb2e4ce3a9779be5d191f4699 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Mon, 15 Jul 2024 15:00:24 -0700 Subject: [PATCH 10/96] blah --- libclamav/ole2_extract.c | 7 +++++++ libclamav/ole2_extract_images.h | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 76b7f5da20..0f8da5f9ff 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -1014,6 +1014,13 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); fprintf(stderr, "%s::%d::FOUND THE OFFSET OF THE DATA. This offset + the offset referenced in 'header'\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Asking for 4k, because, why not???\n", __FUNCTION__, __LINE__); + const uint8_t * const ptr = fmap_need_off_once(hdr->map, offset, 4096); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); + exit(11); + } + extract_images_2(&header, ptr); diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 7f1074bca5..2d1bdb746b 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -607,6 +607,7 @@ fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +#if 0 static void extract_images( FibRgFcLcb97 * header, const property_t * table_stream, ole2_header_t *hdr) { int i; @@ -633,16 +634,24 @@ static void extract_images( FibRgFcLcb97 * header, const property_t * table_stre offset += 8; //size of OfficeArtRecordHeader - - + fprintf(stderr, "%s::%d::Leaving\n", __FUNCTION__, __LINE__); +} +#else +static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { + fprintf(stderr, "%s::%d::%p::%p\n", __FUNCTION__, __LINE__, header, ptr); + size_t offset = header->fcDggInfo; + OfficeArtRecordHeader officeArtDggContainer; + copy_OfficeArtRecordHeader (&officeArtDggContainer, &(ptr[offset])); - fprintf(stderr, "%s::%d::Leaving\n", __FUNCTION__, __LINE__); + exit(11); } +#endif + From 6475008ede8dd82fce7e0f6ce741366713c7aa0a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Mon, 15 Jul 2024 15:02:55 -0700 Subject: [PATCH 11/96] blah --- libclamav/ole2_extract_images.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 2d1bdb746b..9bab070f01 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -645,7 +645,14 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { OfficeArtRecordHeader officeArtDggContainer; copy_OfficeArtRecordHeader (&officeArtDggContainer, &(ptr[offset])); + if (0xf != officeArtDggContainer.recVer_recInstance){ + fprintf(stderr, "%s::%d::Error\n", __FUNCTION__, __LINE__); + exit(11); + } + + + fprintf(stderr, "%s::%d::Got to end\n", __FUNCTION__, __LINE__); exit(11); } From 85a41f0b0c929c70d94481b916455c7514e2b224 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Mon, 15 Jul 2024 15:23:34 -0700 Subject: [PATCH 12/96] blah --- libclamav/ole2_extract_images.h | 44 ++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 9bab070f01..00734acc7f 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -642,14 +642,52 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { fprintf(stderr, "%s::%d::%p::%p\n", __FUNCTION__, __LINE__, header, ptr); size_t offset = header->fcDggInfo; - OfficeArtRecordHeader officeArtDggContainer; - copy_OfficeArtRecordHeader (&officeArtDggContainer, &(ptr[offset])); + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + int i; + for (i= 0; i < 100; i++) { + fprintf(stderr, "%02x ", ptr[i]); + } + fprintf(stderr, "\n"); - if (0xf != officeArtDggContainer.recVer_recInstance){ + + /* + * Start of OfficeArtContent + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3 + * First record is an OfficeArtDggContainer + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/dd7133b6-ed10-4bcb-be29-67b0544f884f + */ + OfficeArtRecordHeader oadc_recordHeader; + copy_OfficeArtRecordHeader (&oadc_recordHeader, &(ptr[offset])); + + if (0xf != oadc_recordHeader.recVer_recInstance){ fprintf(stderr, "%s::%d::Error\n", __FUNCTION__, __LINE__); exit(11); } + offset += sizeof (OfficeArtRecordHeader ); + + /* + * Next is the OfficeArtFDGGBlock + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/a9ff4320-4fa3-4408-8ea4-85c3cec0b501 + * We shouldn't have to care about that, since it's for drawings and not actual file images. + * Going to just skip this record for now. + * */ + OfficeArtRecordHeader hdr; + copy_OfficeArtRecordHeader(&hdr, &(ptr[offset])); + offset += hdr.recLen; + + offset += sizeof(OfficeArtRecordHeader); + fprintf(stderr, "%s::%d::Before last one\n", __FUNCTION__, __LINE__); + + /* + * OfficeArtBStoreContainer + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/561cb6d4-d38b-4666-b2b4-10abc1dce44c + * + */ + OfficeArtRecordHeader blipStoreRecordHeader; + copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); + + fprintf(stderr, "%s::%d::Process blip store here\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::Got to end\n", __FUNCTION__, __LINE__); From 9e5079328c5a3c0e0bccd597a78becb0ecdead38 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Mon, 15 Jul 2024 16:51:07 -0700 Subject: [PATCH 13/96] blah --- libclamav/ole2_extract_images.h | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 00734acc7f..f7ad652ee4 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -395,6 +395,7 @@ typedef struct __attribute__((packed)) { static void copy_OfficeArtRecordHeader (OfficeArtRecordHeader * header, const uint8_t * const ptr) { memcpy(header, ptr, sizeof(OfficeArtRecordHeader)); + header->recVer_recInstance = ole2_endian_convert_16(header->recVer_recInstance); header->recType = ole2_endian_convert_16(header->recType); header->recLen = ole2_endian_convert_32(header->recLen); @@ -641,13 +642,15 @@ static void extract_images( FibRgFcLcb97 * header, const property_t * table_stre static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { fprintf(stderr, "%s::%d::%p::%p\n", __FUNCTION__, __LINE__, header, ptr); size_t offset = header->fcDggInfo; + int i; +#if 0 fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - int i; for (i= 0; i < 100; i++) { fprintf(stderr, "%02x ", ptr[i]); } fprintf(stderr, "\n"); +#endif /* @@ -677,7 +680,7 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { offset += hdr.recLen; offset += sizeof(OfficeArtRecordHeader); - fprintf(stderr, "%s::%d::Before last one\n", __FUNCTION__, __LINE__); + fprintf(stderr, "\n%s::%d::Before last one\n", __FUNCTION__, __LINE__); /* * OfficeArtBStoreContainer @@ -687,7 +690,22 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { OfficeArtRecordHeader blipStoreRecordHeader; copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); - fprintf(stderr, "%s::%d::Process blip store here\n", __FUNCTION__, __LINE__); +//fprintf(stderr, "%s::%d::Process blip store here\n", __FUNCTION__, __LINE__); + uint16_t numRecords = (blipStoreRecordHeader.recVer_recInstance & 0xfff0) >> 4; + + offset += sizeof(OfficeArtRecordHeader); + + /*I am thinking I need to increment offset by 2 here, but I can't find anything in the docs to say why. + * That's just what all the files appear to be expecting.*/ + + fprintf(stderr, "%s::%d::numRecords = 0x%x\n", __FUNCTION__, __LINE__, numRecords); + + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = 0; i < blipStoreRecordHeader.recLen; i++){ + //for (i = 0; i < 512; i++){ + fprintf(stderr, "%02x ", ptr[offset + i]); + } + fprintf(stderr, "\n"); fprintf(stderr, "%s::%d::Got to end\n", __FUNCTION__, __LINE__); From 9227986affe111b33d37d5936a11dd63eca443a0 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 16 Jul 2024 16:16:01 -0700 Subject: [PATCH 14/96] blah --- libclamav/ole2_extract_images.h | 37 ++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index f7ad652ee4..50d1465982 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -639,6 +639,23 @@ static void extract_images( FibRgFcLcb97 * header, const property_t * table_stre } #else +typedef struct __attribute__((packed)) { + uint32_t spidMax; + uint32_t cidcl; + uint32_t cspSaved; + uint32_t cdgSaved; +} OfficeArtFDGG; + +static void copy_OfficeArtFDGG(OfficeArtFDGG * dst, const uint8_t * const ptr){ + size_t idx = 0; + memcpy(dst, ptr, sizeof(OfficeArtFDGG)); + + dst->spidMax = ole2_endian_convert_32(dst->spidMax); + dst->cidcl = ole2_endian_convert_32(dst->cidcl); + dst->cspSaved = ole2_endian_convert_32(dst->cspSaved ); + dst->cdgSaved = ole2_endian_convert_32(dst->cdgSaved ); +} + static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { fprintf(stderr, "%s::%d::%p::%p\n", __FUNCTION__, __LINE__, header, ptr); size_t offset = header->fcDggInfo; @@ -652,6 +669,7 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { fprintf(stderr, "\n"); #endif + fprintf(stderr, "%s::%d::offset = %lx\n", __FUNCTION__, __LINE__, offset); /* * Start of OfficeArtContent @@ -659,7 +677,7 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { * First record is an OfficeArtDggContainer * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/dd7133b6-ed10-4bcb-be29-67b0544f884f */ - OfficeArtRecordHeader oadc_recordHeader; + OfficeArtRecordHeader oadc_recordHeader; //OfficeArtDggContainer copy_OfficeArtRecordHeader (&oadc_recordHeader, &(ptr[offset])); if (0xf != oadc_recordHeader.recVer_recInstance){ @@ -675,11 +693,23 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { * We shouldn't have to care about that, since it's for drawings and not actual file images. * Going to just skip this record for now. * */ - OfficeArtRecordHeader hdr; + OfficeArtRecordHeader hdr; //OfficeArtFDGGBlock copy_OfficeArtRecordHeader(&hdr, &(ptr[offset])); - offset += hdr.recLen; + //offset += hdr.recLen; not right, doesn't *always* seem to be a size. offset += sizeof(OfficeArtRecordHeader); + + OfficeArtFDGG fdgg; + copy_OfficeArtFDGG(&fdgg, &(ptr[offset])); + offset += sizeof(OfficeArtFDGG); + + fprintf(stderr, "%s::%d::fdgg.cidcl = %d\n", __FUNCTION__, __LINE__, fdgg.cidcl); +/* OfficeArtIDCL is not used in parsing images, only drawings. If details are needed, they are + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2335d2f8-109b-4cd6-ac8d-40b1237283f3 + * */ +#define OFFICE_ART_IDCL_LEN 8 + offset += (OFFICE_ART_IDCL_LEN * (fdgg.cidcl-1)); + fprintf(stderr, "\n%s::%d::Before last one\n", __FUNCTION__, __LINE__); /* @@ -698,6 +728,7 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { /*I am thinking I need to increment offset by 2 here, but I can't find anything in the docs to say why. * That's just what all the files appear to be expecting.*/ + fprintf(stderr, "%s::%d::offset = %lx\n", __FUNCTION__, __LINE__, offset); fprintf(stderr, "%s::%d::numRecords = 0x%x\n", __FUNCTION__, __LINE__, numRecords); fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); From a082333875aebe4486e12c0cd76a6e2fe4c8b55a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 16 Jul 2024 18:53:16 -0700 Subject: [PATCH 15/96] blah --- libclamav/ole2_extract_images.h | 253 +++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 4 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 50d1465982..cac54aa01d 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -405,7 +405,13 @@ static void copy_OfficeArtRecordHeader (OfficeArtRecordHeader * header, const ui } +static uint16_t getRecInst(OfficeArtRecordHeader * header) { + return ole2_endian_convert_16((header->recVer_recInstance & 0xfff0) >> 4); +} +static uint8_t getRecVer(OfficeArtRecordHeader * header) { + return header->recVer_recInstance & 0xf; +} static void parse_fibRgFcLcb97(const uint8_t * ptr){ @@ -476,7 +482,9 @@ static void parse_fibRgFcLcb2007(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } +ole2_header_t * pGLOBAL_HEADER; static void test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr) { + pGLOBAL_HEADER = hdr; const uint8_t *ptr = NULL; fib_base_t fib = {0}; @@ -647,7 +655,7 @@ typedef struct __attribute__((packed)) { } OfficeArtFDGG; static void copy_OfficeArtFDGG(OfficeArtFDGG * dst, const uint8_t * const ptr){ - size_t idx = 0; + //size_t idx = 0; memcpy(dst, ptr, sizeof(OfficeArtFDGG)); dst->spidMax = ole2_endian_convert_32(dst->spidMax); @@ -656,10 +664,172 @@ static void copy_OfficeArtFDGG(OfficeArtFDGG * dst, const uint8_t * const ptr){ dst->cdgSaved = ole2_endian_convert_32(dst->cdgSaved ); } + +/*This does NOT include the rh (OfficeArtRecordHeader) + * + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 + * */ +typedef struct __attribute__((packed)) { + uint8_t btWin32; + + uint8_t btMacOS; + + uint8_t rgbUid[16]; + uint16_t tag; + uint32_t size; + uint32_t cRef; + uint32_t foDelay; + + uint8_t unused1; + uint8_t cbName; + uint8_t unused2; + uint8_t unused3; + + //followed by namedata + //followed by blip +} OfficeArtFBSEKnown; + +static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * const ptr) { + memcpy(dst, ptr, sizeof(OfficeArtFBSEKnown)); + + dst->tag = ole2_endian_convert_16(dst->tag); + dst->size = ole2_endian_convert_32(dst->size); + dst->cRef = ole2_endian_convert_32(dst->cRef); + dst->foDelay = ole2_endian_convert_32(dst->foDelay); +} + +static void saveImageFile(const uint8_t * const ptr, size_t size){ + fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY!!!\n", __FUNCTION__, __LINE__); +} + + +/*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32*/ +static void processOfficeArtBlipEMF(OfficeArtRecordHeader * rh, const uint8_t * const ptr) { + size_t offset = 16; /* Size of rgbUid1*/ + + uint16_t recInst = getRecInst(rh); + + if (0x3d5 == recInst) { + offset += 16; + } else if (0x3d4 != recInst) { + fprintf(stderr, "%s::%d::Invaild recInst\n", __FUNCTION__, __LINE__); + exit(121); //normally just return, will fix + } + offset += 34; /*metafile header*/ + + saveImageFile(&(ptr[offset]), rh->recLen - offset); +} + + + +static void processOfficeArtBlip(const uint8_t * const ptr){ + + size_t offset = 0; + OfficeArtRecordHeader rh; + + copy_OfficeArtRecordHeader (&rh, ptr); + offset += sizeof(OfficeArtRecordHeader ); + uint8_t recVer = getRecVer(&rh); + if (0 != recVer) { + fprintf(stderr, "%s::%d::Invalid recver\n", __FUNCTION__, __LINE__); + exit(110); + } + + switch (rh.recType) { + case 0xf01a: + processOfficeArtBlipEMF(&rh, &(ptr[offset])); + break; + default: + fprintf(stderr, "%s::%d::Invalid 0x%x::", __FUNCTION__, __LINE__, rh.recType); + + { + size_t andy; + for (andy = 0; andy < 100; andy++){ + fprintf(stderr, "%02x ", ptr[offset + andy]); + + } + fprintf(stderr, "\n"); + } + + + + + exit(11); + break; + } + + //get them from + //https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/c67b883b-8136-4e91-a1a3-2981d16e934f + fprintf(stderr, "%s::%d::Make sure you get them all, dumbass\n", __FUNCTION__, __LINE__); + exit(12); + + +} + + +/* + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 + */ +static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr) { + + size_t i; + OfficeArtFBSEKnown fbse; + + uint32_t offset = sizeof(OfficeArtRecordHeader); + uint16_t recInst = getRecInst(imageHeader); + + copy_OfficeArtFBSEKnown (&fbse, &(ptr[offset])); + offset += sizeof(OfficeArtFBSEKnown ); + + if ((recInst != fbse.btWin32) && (recInst != fbse.btMacOS)) { + fprintf(stderr, "%s::%d::Invalid record, exiting (fix later)\n", __FUNCTION__, __LINE__); + exit(1); + } + if (imageHeader->recType != 0xf007) { + fprintf(stderr, "%s::%d::Invalid record, exiting (fix later)\n", __FUNCTION__, __LINE__); + exit(1); + } + + fprintf(stderr, "%s::%d::imageHeader->recLen = %d\n", __FUNCTION__, __LINE__, imageHeader->recLen); + fprintf(stderr, "%s::%d::blip size = %d\n", __FUNCTION__, __LINE__, fbse.size); + //fprintf(stderr, "%s::%d::delay = %d\n", __FUNCTION__, __LINE__, fbse.foDelay); + + offset += fbse.cbName; + + +#if 0 + offset += fbse.foDelay; + uint8_t * blah = fmap_need_off_once(pGLOBAL_HEADER->map, offset, fbse.size); + processOfficeArtBlip(blah); +#else + + + { + size_t andy; + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (andy = 0; andy < 1024; andy++) { + if (offset == andy) { + fprintf(stderr, "\n"); + } + fprintf(stderr, "%02x ", ptr[andy]); + + } + fprintf(stderr, "\n"); + fprintf(stderr, "%s::%d::Figure out what is going on here, since none of the record types match the documentation\n", __FUNCTION__, __LINE__); + exit(1); + } + + + + processOfficeArtBlip(&(ptr[offset])); +#endif + +} + static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { fprintf(stderr, "%s::%d::%p::%p\n", __FUNCTION__, __LINE__, header, ptr); size_t offset = header->fcDggInfo; - int i; + uint32_t i; #if 0 fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); @@ -680,6 +850,7 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { OfficeArtRecordHeader oadc_recordHeader; //OfficeArtDggContainer copy_OfficeArtRecordHeader (&oadc_recordHeader, &(ptr[offset])); + /*TODO: validate recVer and recInst separately*/ if (0xf != oadc_recordHeader.recVer_recInstance){ fprintf(stderr, "%s::%d::Error\n", __FUNCTION__, __LINE__); exit(11); @@ -720,20 +891,94 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { OfficeArtRecordHeader blipStoreRecordHeader; copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); + fprintf(stderr, "%s::%d::RecVer = %x\n", __FUNCTION__, __LINE__, getRecVer(&blipStoreRecordHeader)); + if (0xf != getRecVer(&blipStoreRecordHeader)) { + fprintf(stderr, "%s::%d::Not a correct value, exiting (during debugging, normally just return)\n", __FUNCTION__, __LINE__); + exit(11); + } + + if (0xf001 != blipStoreRecordHeader.recType){ + fprintf(stderr, "%s::%d::Not a correct value, exiting (during debugging, normally just return)\n", __FUNCTION__, __LINE__); + exit(11); + } + + uint32_t imageCnt = getRecInst (&blipStoreRecordHeader); + fprintf(stderr, "%s::%d::imageCnt = %d\n", __FUNCTION__, __LINE__, imageCnt); + + offset += sizeof(OfficeArtRecordHeader); + +#if 0 + /*I *hate* doing this, but I have been unable to figuer out why I need to increment by 2 bytes here. There is + * nothing in the documentation that I have found to account for these bytes, so I am going to increment them + * here, and hope it makes sense at some point??? + * + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/561cb6d4-d38b-4666-b2b4-10abc1dce44c + * + * */ + offset += 2; +#endif + + + + + + + + + + + + /*Rec types taken from + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/a7d7d967-6bff-489c-a267-3ec30448344a + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/c67b883b-8136-4e91-a1a3-2981d16e934f + * + * */ +#define OFFICE_ART_FBSE_REC_TYPE 0x2 + for (i = 0; i < imageCnt; i++) { + OfficeArtRecordHeader imageHeader; + copy_OfficeArtRecordHeader(&imageHeader, &(ptr[offset])); + uint8_t recVer = getRecVer(&imageHeader); + fprintf(stderr, "%s::%d::recType = %x\n", __FUNCTION__, __LINE__, recVer); + + if (OFFICE_ART_FBSE_REC_TYPE == recVer){ + /* OfficeArtFBSE + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 + */ + processOfficeArtFBSE(&imageHeader, &(ptr[offset])); + } else { + processOfficeArtBlip(&(ptr[offset])); + } + } + + + + + + +#if 0 + + + + + + + //fprintf(stderr, "%s::%d::Process blip store here\n", __FUNCTION__, __LINE__); uint16_t numRecords = (blipStoreRecordHeader.recVer_recInstance & 0xfff0) >> 4; offset += sizeof(OfficeArtRecordHeader); + /*I am thinking I need to increment offset by 2 here, but I can't find anything in the docs to say why. * That's just what all the files appear to be expecting.*/ - fprintf(stderr, "%s::%d::offset = %lx\n", __FUNCTION__, __LINE__, offset); + fprintf(stderr, "%s::%d::offset = %lx\n", __FUNCTION__, __LINE__, offset); fprintf(stderr, "%s::%d::numRecords = 0x%x\n", __FUNCTION__, __LINE__, numRecords); +#endif fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); for (i = 0; i < blipStoreRecordHeader.recLen; i++){ - //for (i = 0; i < 512; i++){ fprintf(stderr, "%02x ", ptr[offset + i]); } fprintf(stderr, "\n"); From e34137ca42c7044301ef6192f0c1809a7786b6f0 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 17 Jul 2024 09:24:47 -0700 Subject: [PATCH 16/96] blah --- libclamav/ole2_extract.c | 18 +++++++++++++++++ libclamav/ole2_extract_images.h | 34 ++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 0f8da5f9ff..cfdd4cc3eb 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -1054,6 +1054,24 @@ fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); extract_images(&header, tableStream); fprintf(stderr, "%s::%d::TODO: HANDLE TABLE STREAM\n", __FUNCTION__, __LINE__); #endif + } else { + + property_t * prop = &(prop_block[idx]); + size_t off = get_stream_data_offset(hdr, prop, prop->start_block); + size_t size = 4096 * 2; + size = 4096; + const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); + size_t i; + if (ptr) { + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = 0; i < size ; i++) { + //fprintf(stderr, "%02x ", ptr[i + 3623]); + fprintf(stderr, "%02x ", ptr[i]); + } + fprintf(stderr, "\n"); + } + + } #if 0 diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index cac54aa01d..57d4d9ef19 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -792,10 +792,25 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint fprintf(stderr, "%s::%d::imageHeader->recLen = %d\n", __FUNCTION__, __LINE__, imageHeader->recLen); fprintf(stderr, "%s::%d::blip size = %d\n", __FUNCTION__, __LINE__, fbse.size); - //fprintf(stderr, "%s::%d::delay = %d\n", __FUNCTION__, __LINE__, fbse.foDelay); + fprintf(stderr, "%s::%d::delay = %d\n", __FUNCTION__, __LINE__, fbse.foDelay); + fprintf(stderr, "%s::%d::recInst = %d (0x%x)\n", __FUNCTION__, __LINE__, recInst, recInst); + + fprintf(stderr, "%s::%d::fbse.btWin32 = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.btWin32, fbse.btWin32); + fprintf(stderr, "%s::%d::fbse.btMacOS = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.btMacOS, fbse.btMacOS); offset += fbse.cbName; + fprintf(stderr, "%s::%d::Since the recLen is 36 (for this file), there is no name data or embedded blip record, so I need to figure out how this delay stream works???\n", __FUNCTION__, __LINE__); + + if (imageHeader->recLen > (offset - sizeof(OfficeArtRecordHeader))) { + /* The BLIP is embedded in this record*/ + processOfficeArtBlip(&(ptr[offset])); + } else { + fprintf(stderr, "%s::%d::Still trying to figure out where teh BLIP is!!!\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Found the BLIP in the 'DocumentSummaryInformation' stream (searching), but can't find structures that point towards it\n", __FUNCTION__, __LINE__); + } + + #if 0 offset += fbse.foDelay; @@ -804,24 +819,33 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint #else +#if 1 { size_t andy; fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); for (andy = 0; andy < 1024; andy++) { - if (offset == andy) { + fprintf(stderr, "%02x ", ptr[offset + andy]); + + } fprintf(stderr, "\n"); - } - fprintf(stderr, "%02x ", ptr[andy]); + + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (andy = 0; andy < 1024; andy++) { + char c = ptr[offset + andy]; + if (c) fprintf(stderr, "%c ", c); + else fprintf(stderr, " "); } fprintf(stderr, "\n"); + fprintf(stderr, "%s::%d::Figure out what is going on here, since none of the record types match the documentation\n", __FUNCTION__, __LINE__); exit(1); } +#endif - processOfficeArtBlip(&(ptr[offset])); +// processOfficeArtBlip(&(ptr[offset])); #endif } From c362d758b3bfcdfe333695d6942337840707799e Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 17 Jul 2024 09:38:51 -0700 Subject: [PATCH 17/96] blah --- libclamav/ole2_extract_images.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 57d4d9ef19..bd34736caa 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -802,7 +802,7 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint fprintf(stderr, "%s::%d::Since the recLen is 36 (for this file), there is no name data or embedded blip record, so I need to figure out how this delay stream works???\n", __FUNCTION__, __LINE__); - if (imageHeader->recLen > (offset - sizeof(OfficeArtRecordHeader))) { + if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ processOfficeArtBlip(&(ptr[offset])); } else { From e4c18b700bc318977d8718fe28d725b368580e6a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 17 Jul 2024 09:57:16 -0700 Subject: [PATCH 18/96] blah --- libclamav/ole2_extract.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index cfdd4cc3eb..029ad2784d 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -982,6 +982,27 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t fprintf(stderr, "%s::%d::%p::setting wordDocStream\n", __FUNCTION__, __LINE__, wordDocStream); + { + + property_t * prop = &(prop_block[idx]); + size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); + size_t size = 4096 * 2; + size = 4096; + const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); + size_t i; + fprintf(stderr, "%s::%d::WORDDOCUMENT::", __FUNCTION__, __LINE__); + for (i = 0; i < size; i++) { + fprintf(stderr, "%02x ", ptr[i]); + } + + DATA IS HERE; + + fprintf(stderr, "\n"); + + } + + + #if 0 { From 09e314eeaea92944e0e50a9162298ba4c55b6306 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 17 Jul 2024 09:57:55 -0700 Subject: [PATCH 19/96] blah --- libclamav/ole2_extract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 029ad2784d..1b8f77dbc7 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -995,7 +995,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t fprintf(stderr, "%02x ", ptr[i]); } - DATA IS HERE; + DATA IS HERE at delay. Some other bytes there, but really close, so need to *hopefully* figure out how to parse whatever header there is and verify that this is actually my data; fprintf(stderr, "\n"); From cdf56f19b7564f8463393bbd6703cdfae19fe8f7 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 17 Jul 2024 16:14:17 -0700 Subject: [PATCH 20/96] blah --- libclamav/ole2_extract.c | 20 ++++-- libclamav/ole2_extract_images.h | 115 ++++++++++++++++++++++++-------- 2 files changed, 103 insertions(+), 32 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 1b8f77dbc7..bfba16f2e4 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -986,18 +986,28 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t property_t * prop = &(prop_block[idx]); size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); - size_t size = 4096 * 2; - size = 4096; + off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ + + fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); + size_t size = 70682; //hardcoded size of the blip; + const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); +#if 0 size_t i; fprintf(stderr, "%s::%d::WORDDOCUMENT::", __FUNCTION__, __LINE__); - for (i = 0; i < size; i++) { + for (i = 0; i < 50; i++) { fprintf(stderr, "%02x ", ptr[i]); } + fprintf(stderr, "\n"); +#else + fprintf(stderr, "%s::%d::MOVE THIS TO SOMEPLACE THAT ACTUALLY MAKES SENSE!!!\n", __FUNCTION__, __LINE__); - DATA IS HERE at delay. Some other bytes there, but really close, so need to *hopefully* figure out how to parse whatever header there is and verify that this is actually my data; + processOfficeArtBlip(ptr); + +#endif + +// DATA IS HERE at delay. Some other bytes there, but really close, so need to *hopefully* figure out how to parse whatever header there is and verify that this is actually my data; - fprintf(stderr, "\n"); } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index bd34736caa..e37efa83e7 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -699,28 +699,80 @@ static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * c } static void saveImageFile(const uint8_t * const ptr, size_t size){ - fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY!!!\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY %p %lu!!!\n", __FUNCTION__, __LINE__, ptr, size); } -/*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32*/ +/*All these structures (except JPEG) are exactly the same, with the exception of the recInst values for 1 or 2 UIDs, + * so this function accepts them as parameters. + * + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32 + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/ee892f04-f001-4531-a34b-67aab3426dcb + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/4b6c5fc5-98cc-445a-8ec7-12b2f2c05b9f + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 + * + */ +static void processOfficeArtBlipGeneric(OfficeArtRecordHeader * rh, const uint8_t * const ptr, + uint16_t riSingleUID, uint16_t riDoubleUID, uint32_t bytesAfterUIDs) { + size_t offset = 16; /* Size of rh*/ + + uint16_t recInst = getRecInst(rh); + + if (riDoubleUID == recInst) { + offset += 16; + } else if (riSingleUID != recInst) { + fprintf(stderr, "%s::%d::Invaild recInst\n", __FUNCTION__, __LINE__); + exit(121); //normally just return, will fix + } + offset += bytesAfterUIDs; /*metafile header*/ + + saveImageFile(&(ptr[offset]), rh->recLen - offset); +} + +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32 */ static void processOfficeArtBlipEMF(OfficeArtRecordHeader * rh, const uint8_t * const ptr) { - size_t offset = 16; /* Size of rgbUid1*/ + processOfficeArtBlipGeneric(rh, ptr, 0x3d4, 0x3d5, 34) ; +} + +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/ee892f04-f001-4531-a34b-67aab3426dcb */ +static void processOfficeArtBlipWMF(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(rh, ptr, 0x216, 0x217, 34) ; +} + +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/4b6c5fc5-98cc-445a-8ec7-12b2f2c05b9f */ +static void processOfficeArtBlipPICT(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(rh, ptr, 0x542, 0x543, 34) ; +} +/*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ +static void processOfficeArtBlipJPEG(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); - if (0x3d5 == recInst) { + if ((0x46b == recInst) || (0x6e3 != recInst)){ offset += 16; - } else if (0x3d4 != recInst) { + } else if ((0x46a != recInst) && (0x6e2 != recInst)) { fprintf(stderr, "%s::%d::Invaild recInst\n", __FUNCTION__, __LINE__); exit(121); //normally just return, will fix } - offset += 34; /*metafile header*/ + offset += 1; /*metafile header*/ saveImageFile(&(ptr[offset]), rh->recLen - offset); } +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 */ +static void processOfficeArtBlipPNG(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(rh, ptr, 0x6e0, 0x6e1, 1) ; +} +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/1393bf5e-6fa0-4665-b3ec-68199b555656 */ +static void processOfficeArtBlipDIB(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(rh, ptr, 0x7a8, 0x7a9, 1) ; +} + +static void processOfficeArtBlipTIFF(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(rh, ptr, 0x6e4, 0x6e5, 1) ; +} static void processOfficeArtBlip(const uint8_t * const ptr){ @@ -735,35 +787,44 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ exit(110); } +#define RECTYPE_OFFICE_ART_BLIP_EMF 0xf01a +#define RECTYPE_OFFICE_ART_BLIP_WMF 0xf01b +#define RECTYPE_OFFICE_ART_BLIP_PICT 0xf01c +#define RECTYPE_OFFICE_ART_BLIP_JPEG 0xf01d +#define RECTYPE_OFFICE_ART_BLIP_PNG 0xf01e +#define RECTYPE_OFFICE_ART_BLIP_DIB 0xf01f +#define RECTYPE_OFFICE_ART_BLIP_TIFF 0xf029 +#define RECTYPE_OFFICE_ART_BLIP_JPEG2 0xf02a + switch (rh.recType) { - case 0xf01a: + case RECTYPE_OFFICE_ART_BLIP_EMF: processOfficeArtBlipEMF(&rh, &(ptr[offset])); break; + case RECTYPE_OFFICE_ART_BLIP_WMF : + processOfficeArtBlipWMF(&rh, &(ptr[offset])); + break; + case RECTYPE_OFFICE_ART_BLIP_PICT: + processOfficeArtBlipPICT(&rh, &(ptr[offset])); + break; + case RECTYPE_OFFICE_ART_BLIP_JPEG: + /* fallthrough */ + case RECTYPE_OFFICE_ART_BLIP_JPEG2: + processOfficeArtBlipJPEG(&rh, &(ptr[offset])); + break; + case RECTYPE_OFFICE_ART_BLIP_PNG: + processOfficeArtBlipPNG(&rh, &(ptr[offset])); + break; + case RECTYPE_OFFICE_ART_BLIP_DIB: + processOfficeArtBlipDIB(&rh, &(ptr[offset])); + break; + case RECTYPE_OFFICE_ART_BLIP_TIFF: + processOfficeArtBlipTIFF(&rh, &(ptr[offset])); + break; default: fprintf(stderr, "%s::%d::Invalid 0x%x::", __FUNCTION__, __LINE__, rh.recType); - - { - size_t andy; - for (andy = 0; andy < 100; andy++){ - fprintf(stderr, "%02x ", ptr[offset + andy]); - - } - fprintf(stderr, "\n"); - } - - - - exit(11); break; } - - //get them from - //https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/c67b883b-8136-4e91-a1a3-2981d16e934f - fprintf(stderr, "%s::%d::Make sure you get them all, dumbass\n", __FUNCTION__, __LINE__); - exit(12); - - } From 645a4b7b153480d43bab5d36b241d79355d0e259 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 17 Jul 2024 17:02:22 -0700 Subject: [PATCH 21/96] blah --- libclamav/ole2_extract.c | 7 +++---- libclamav/ole2_extract_images.h | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index bfba16f2e4..b7d5f72808 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -989,22 +989,21 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); - size_t size = 70682; //hardcoded size of the blip; + size_t size = 70682; //hardcoded size of the blip (temporarily); const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); -#if 0 size_t i; fprintf(stderr, "%s::%d::WORDDOCUMENT::", __FUNCTION__, __LINE__); for (i = 0; i < 50; i++) { fprintf(stderr, "%02x ", ptr[i]); } fprintf(stderr, "\n"); -#else + fprintf(stderr, "%s::%d::MOVE THIS TO SOMEPLACE THAT ACTUALLY MAKES SENSE!!!\n", __FUNCTION__, __LINE__); processOfficeArtBlip(ptr); -#endif + // DATA IS HERE at delay. Some other bytes there, but really close, so need to *hopefully* figure out how to parse whatever header there is and verify that this is actually my data; diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index e37efa83e7..ae4153156b 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -700,6 +700,25 @@ static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * c static void saveImageFile(const uint8_t * const ptr, size_t size){ fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY %p %lu!!!\n", __FUNCTION__, __LINE__, ptr, size); + + FILE * fp = fopen("andy_out.jpg", "wb"); + size_t bytesWritten = 0; + while (bytesWritten < size) { + int ret = fwrite(&(ptr[bytesWritten]), 1, size - bytesWritten, fp); + if (ret > 0) { + bytesWritten += ret; + } else { + break; + } + } + + if (bytesWritten == size) { + fprintf(stderr, "%s::%d::Success\n", __FUNCTION__, __LINE__); + } else { + fprintf(stderr, "%s::%d::NOT Success\n", __FUNCTION__, __LINE__); + } + + exit(11); } @@ -748,14 +767,16 @@ static void processOfficeArtBlipPICT(OfficeArtRecordHeader * rh, const uint8_t * static void processOfficeArtBlipJPEG(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); + fprintf(stderr, "%s::%d::recInst = 0x%x\n", __FUNCTION__, __LINE__, recInst); - if ((0x46b == recInst) || (0x6e3 != recInst)){ + if ((0x46b == recInst) || (0x6e3 == recInst)){ offset += 16; } else if ((0x46a != recInst) && (0x6e2 != recInst)) { fprintf(stderr, "%s::%d::Invaild recInst\n", __FUNCTION__, __LINE__); exit(121); //normally just return, will fix } offset += 1; /*metafile header*/ + fprintf(stderr, "%s::%d::offset = %ld\n", __FUNCTION__, __LINE__, offset); saveImageFile(&(ptr[offset]), rh->recLen - offset); } From b7cdf604acddde732c999355d31ea2c57a77dd8a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 07:40:13 -0700 Subject: [PATCH 22/96] Starting refactor/cleanup. If something breaks, start here --- libclamav/ole2_extract.c | 5 +++-- libclamav/ole2_extract_images.h | 21 ++++++++------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index b7d5f72808..a349e0cf7f 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -636,7 +636,6 @@ static int ole2_cmp_name(const char *const name, uint32_t name_size, const char decoded[j] = ((unsigned char)name[i + 1]) << 4; decoded[j] += name[i]; } - fprintf(stderr, "%s::%d::%s\n", __FUNCTION__, __LINE__, decoded); return strcasecmp(decoded, keyword); } @@ -984,7 +983,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t { - property_t * prop = &(prop_block[idx]); + //property_t * prop = &(prop_block[idx]); size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ @@ -1084,6 +1083,7 @@ fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); extract_images(&header, tableStream); fprintf(stderr, "%s::%d::TODO: HANDLE TABLE STREAM\n", __FUNCTION__, __LINE__); #endif +#if 0 } else { property_t * prop = &(prop_block[idx]); @@ -1101,6 +1101,7 @@ fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); fprintf(stderr, "\n"); } +#endif } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ae4153156b..aebe7fa72a 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -488,40 +488,36 @@ static void test_for_pictures( const property_t *word_block, const property_t * const uint8_t *ptr = NULL; fib_base_t fib = {0}; - //size_t i; size_t to_read = 0x1000; - fprintf(stderr,"%s::%d::Entering\n", __FUNCTION__, __LINE__); - uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); -// fprintf(stderr,"%s::%d::fib_offset = %x\n", __FUNCTION__, __LINE__, fib_offset); if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); return; } - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + //fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); //ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); - fprintf(stderr, "%s::%d::TODO: Add the correct size, trying to read 4k because, why not?\n", __FUNCTION__, __LINE__); + //fprintf(stderr, "%s::%d::TODO: Add the correct size, trying to read 4k because, why not?\n", __FUNCTION__, __LINE__); ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); return; } -fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +//fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); copy_fib_base(&fib, ptr); -fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +//fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); #define FIB_BASE_IDENTIFIER 0xa5ec - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +// fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); if (FIB_BASE_IDENTIFIER != fib.wIdent) { cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); return; } - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +// fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); uint32_t idx = sizeof(fib); /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ @@ -531,7 +527,7 @@ fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); return; } - fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); +// fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ @@ -551,7 +547,7 @@ fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); return; } #else - fprintf(stderr, "nFib = 0x%x::cbRgFcLcb = 0x%x\n", fib.nFib, cbRgFcLcb ); +// fprintf(stderr, "nFib = 0x%x::cbRgFcLcb = 0x%x\n", fib.nFib, cbRgFcLcb ); switch (fib.nFib){ default: fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); @@ -854,7 +850,6 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ */ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr) { - size_t i; OfficeArtFBSEKnown fbse; uint32_t offset = sizeof(OfficeArtRecordHeader); From e983549b834fba799e22ee029db0bb409d10d786 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 10:34:01 -0700 Subject: [PATCH 23/96] blah --- libclamav/ole2_extract_images.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index aebe7fa72a..323a4b0d19 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -482,9 +482,13 @@ static void parse_fibRgFcLcb2007(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } +#if 0 ole2_header_t * pGLOBAL_HEADER; +#endif static void test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr) { +#if 0 pGLOBAL_HEADER = hdr; +#endif const uint8_t *ptr = NULL; fib_base_t fib = {0}; From ccf52da032f6103c5bfae13b71295642fa48a666 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 11:28:33 -0700 Subject: [PATCH 24/96] blah --- libclamav/ole2_extract.c | 2 +- libclamav/ole2_extract_images.h | 54 +++++---------------------------- 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index a349e0cf7f..bacfe16e36 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -1049,7 +1049,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); exit(11); } - extract_images_2(&header, ptr); + extract_images_2(&g_FibRgFcLcb97Header, ptr); diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 323a4b0d19..5b1afa3557 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -427,7 +427,7 @@ static void parse_fibRgFcLcb2000(const uint8_t * ptr){ * TODO: MOVE THIS TO A STRUCTURE THAT IS PASSED IN, BUT * CURRENTLY TRYING TO FIGURE OUT IF I AM FINDING ALL THE DATA CORRECTLY */ -FibRgFcLcb97 header; +FibRgFcLcb97 g_FibRgFcLcb97Header; @@ -439,11 +439,11 @@ static void parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const pro fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, table_stream); - copy_FibRgFcLcb97(&header, ptr); + copy_FibRgFcLcb97(&g_FibRgFcLcb97Header, ptr); /*Offset is in the TableStream.*/ - size_t offset = header.fcDggInfo; - size_t size = header.lcbDggInfo; + size_t offset = g_FibRgFcLcb97Header.fcDggInfo; + size_t size = g_FibRgFcLcb97Header.lcbDggInfo; fprintf(stderr, "%s::%d::Offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); fprintf(stderr, "%s::%d::Size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); @@ -482,14 +482,7 @@ static void parse_fibRgFcLcb2007(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); } -#if 0 -ole2_header_t * pGLOBAL_HEADER; -#endif static void test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr) { -#if 0 - pGLOBAL_HEADER = hdr; -#endif - const uint8_t *ptr = NULL; fib_base_t fib = {0}; size_t to_read = 0x1000; @@ -500,28 +493,20 @@ static void test_for_pictures( const property_t *word_block, const property_t * cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); return; } - //fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); - - //ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); - //fprintf(stderr, "%s::%d::TODO: Add the correct size, trying to read 4k because, why not?\n", __FUNCTION__, __LINE__); ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); return; } -//fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); copy_fib_base(&fib, ptr); -//fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); #define FIB_BASE_IDENTIFIER 0xa5ec -// fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); if (FIB_BASE_IDENTIFIER != fib.wIdent) { cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); return; } -// fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); uint32_t idx = sizeof(fib); /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ @@ -531,8 +516,8 @@ static void test_for_pictures( const property_t *word_block, const property_t * fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); return; } -// fprintf(stderr,"%s::%d\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::TODO: Make this a #define\n", __FUNCTION__, __LINE__); idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ uint16_t cslw; @@ -545,13 +530,6 @@ static void test_for_pictures( const property_t *word_block, const property_t * uint16_t cbRgFcLcb; read_uint16(ptr, to_read, &idx, &cbRgFcLcb); -#if 0 - if (!= cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid cbRgFcLcb of 0x%x\n", __FUNCTION__, __LINE__, cbRgFcLcb); - return; - } -#else -// fprintf(stderr, "nFib = 0x%x::cbRgFcLcb = 0x%x\n", fib.nFib, cbRgFcLcb ); switch (fib.nFib){ default: fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); @@ -576,7 +554,6 @@ static void test_for_pictures( const property_t *word_block, const property_t * return; } fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); - //parse_fibRgFcLcb2002(&(ptr[idx])); parse_fibRgFcLcb2002(ptr, idx, table_stream); break; case 0x010c: @@ -594,23 +571,6 @@ static void test_for_pictures( const property_t *word_block, const property_t * parse_fibRgFcLcb2007(ptr); break; } -#endif - - -#if 0 - { - int i; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = idx; i < to_read; i++){ - fprintf(stderr, "%02x ", ptr[i]); - } - fprintf(stderr, "\n"); -} -#endif - - - fprintf(stderr,"%s::%d::GOT TO END!!!\n", __FUNCTION__, __LINE__); - } @@ -718,7 +678,7 @@ static void saveImageFile(const uint8_t * const ptr, size_t size){ fprintf(stderr, "%s::%d::NOT Success\n", __FUNCTION__, __LINE__); } - exit(11); + //exit(11); } @@ -900,7 +860,7 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint #else -#if 1 +#if 0 { size_t andy; fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); From 73278006d64751661268e0470b44f7fa106e9b57 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 11:56:29 -0700 Subject: [PATCH 25/96] blah --- libclamav/ole2_extract.c | 22 ++++++++++- libclamav/ole2_extract_images.h | 67 +++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index bacfe16e36..f80f676dc9 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -897,6 +897,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t int toval = 0; property_t * wordDocStream = NULL; property_t * tableStream = NULL; + FibRgFcLcb97 fibRgFcLcb97Header = {0}; + bool bFibRgFcLcb97HeaderInitialized = false; ole2_listmsg("ole2_walk_property_tree() called\n"); ole2_list_init(&node_list); @@ -975,8 +977,24 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); + bFibRgFcLcb97HeaderInitialized = test_for_pictures(&(prop_block[idx]), tableStream, hdr, &fibRgFcLcb97Header); + +#if 1 + { + fprintf(stderr, "%s::%d::Delete this when ready\n", __FUNCTION__, __LINE__); + if (bFibRgFcLcb97HeaderInitialized) { + size_t offset = fibRgFcLcb97Header.fcDggInfo; + size_t size = fibRgFcLcb97Header.lcbDggInfo; + fprintf(stderr, "%s::%d::Offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); + fprintf(stderr, "%s::%d::Size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); + fprintf(stderr, "%s::%d::verify that these values are the same as the offset and size printed in 'parse_fibRgFcLcb2002'\n", __FUNCTION__, __LINE__); + } + fprintf(stderr, "%s::%d::END Delete this when ready\n", __FUNCTION__, __LINE__); + } +#endif + + - test_for_pictures(&(prop_block[idx]), tableStream, hdr); wordDocStream = &(prop_block[idx]); fprintf(stderr, "%s::%d::%p::setting wordDocStream\n", __FUNCTION__, __LINE__, wordDocStream); @@ -1049,7 +1067,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); exit(11); } - extract_images_2(&g_FibRgFcLcb97Header, ptr); + extract_images_2(&fibRgFcLcb97Header, ptr); diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 5b1afa3557..71e2c79094 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -414,36 +414,43 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { } -static void parse_fibRgFcLcb97(const uint8_t * ptr){ +static bool parse_fibRgFcLcb97(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); + return true; } -static void parse_fibRgFcLcb2000(const uint8_t * ptr){ +static bool parse_fibRgFcLcb2000(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); + return true; } +#if 0 /* * TODO: MOVE THIS TO A STRUCTURE THAT IS PASSED IN, BUT * CURRENTLY TRYING TO FIGURE OUT IF I AM FINDING ALL THE DATA CORRECTLY */ FibRgFcLcb97 g_FibRgFcLcb97Header; +#endif + +static bool parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const property_t * table_stream + , FibRgFcLcb97 * g_FibRgFcLcb97Header +){ -static void parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const property_t * table_stream){ const uint8_t * ptr = &(base_ptr[idx]); fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, table_stream); - copy_FibRgFcLcb97(&g_FibRgFcLcb97Header, ptr); + copy_FibRgFcLcb97(g_FibRgFcLcb97Header, ptr); /*Offset is in the TableStream.*/ - size_t offset = g_FibRgFcLcb97Header.fcDggInfo; - size_t size = g_FibRgFcLcb97Header.lcbDggInfo; + size_t offset = g_FibRgFcLcb97Header->fcDggInfo; + size_t size = g_FibRgFcLcb97Header->lcbDggInfo; fprintf(stderr, "%s::%d::Offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); fprintf(stderr, "%s::%d::Size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); @@ -472,17 +479,24 @@ static void parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const pro fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); /* exit(11); */ + + fprintf(stderr, "%s::%d::TODO:Verify the structure is at the beginning for all of these, and change it to a normal copy\n", __FUNCTION__, __LINE__); + return true; } -static void parse_fibRgFcLcb2003(const uint8_t * ptr){ +static bool parse_fibRgFcLcb2003(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); + return true; } -static void parse_fibRgFcLcb2007(const uint8_t * ptr){ +static bool parse_fibRgFcLcb2007(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); + return true; } -static void test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr) { +static bool test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { + bool bRet = false; + const uint8_t *ptr = NULL; fib_base_t fib = {0}; size_t to_read = 0x1000; @@ -491,13 +505,13 @@ static void test_for_pictures( const property_t *word_block, const property_t * if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - return; + goto done; } ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - return; + goto done; } copy_fib_base(&fib, ptr); @@ -505,7 +519,7 @@ static void test_for_pictures( const property_t *word_block, const property_t * if (FIB_BASE_IDENTIFIER != fib.wIdent) { cli_dbgmsg("ERROR: Invalid identifier for File Information Block %d (0x%x)\n", fib.wIdent, fib.wIdent); - return; + goto done; } uint32_t idx = sizeof(fib); @@ -514,7 +528,7 @@ static void test_for_pictures( const property_t *word_block, const property_t * read_uint16(ptr, to_read, &idx, &csw); if (0x000e != csw){ fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); - return; + goto done; } fprintf(stderr, "%s::%d::TODO: Make this a #define\n", __FUNCTION__, __LINE__); @@ -524,7 +538,7 @@ static void test_for_pictures( const property_t *word_block, const property_t * read_uint16(ptr, to_read, &idx, &cslw); if (0x0016 != cslw) { fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); - return; + goto done; } idx += 88; /* Size of the FibRgLw97. Don't think I need anything from there. */ @@ -533,44 +547,47 @@ static void test_for_pictures( const property_t *word_block, const property_t * switch (fib.nFib){ default: fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); - return; + goto done; case 0x00c1: if (0x005d != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; + goto done; } - parse_fibRgFcLcb97(ptr); + bRet = parse_fibRgFcLcb97(ptr); break; case 0x00d9: if (0x006c != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; + goto done; } - parse_fibRgFcLcb2000(ptr); + bRet = parse_fibRgFcLcb2000(ptr); break; case 0x0101: if (0x0088 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; + goto done; } fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); - parse_fibRgFcLcb2002(ptr, idx, table_stream); + bRet = parse_fibRgFcLcb2002(ptr, idx, table_stream, g_FibRgFcLcb97Header); break; case 0x010c: if (0x00a4 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; + goto done; } - parse_fibRgFcLcb2003(ptr); + bRet = parse_fibRgFcLcb2003(ptr); break; case 0x0112: if (0x00b7 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); - return; + goto done; } - parse_fibRgFcLcb2007(ptr); + bRet = parse_fibRgFcLcb2007(ptr); break; } + +done: + return bRet; } From e3f8cbab838d878bcce577175ae4cc7933b7317c Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 13:22:37 -0700 Subject: [PATCH 26/96] blah --- libclamav/ole2_extract.c | 17 ++++++++++++++++- libclamav/ole2_extract_images.h | 15 ++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index f80f676dc9..26c40b5fc9 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -899,6 +899,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t property_t * tableStream = NULL; FibRgFcLcb97 fibRgFcLcb97Header = {0}; bool bFibRgFcLcb97HeaderInitialized = false; + fib_base_t fibBase = {0}; ole2_listmsg("ole2_walk_property_tree() called\n"); ole2_list_init(&node_list); @@ -976,8 +977,22 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { + uint32_t fib_offset = get_stream_data_offset(hdr, &(prop_block[idx]), prop_block[idx].start_block); + const uint8_t * ptr = NULL; + + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + continue; + } + + ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + continue; + } + copy_fib_base(&fibBase, ptr); test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); - bFibRgFcLcb97HeaderInitialized = test_for_pictures(&(prop_block[idx]), tableStream, hdr, &fibRgFcLcb97Header); + bFibRgFcLcb97HeaderInitialized = test_for_pictures(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); #if 1 { diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 71e2c79094..f0fcbf8ac1 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -436,7 +436,7 @@ FibRgFcLcb97 g_FibRgFcLcb97Header; -static bool parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const property_t * table_stream +static bool parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx , FibRgFcLcb97 * g_FibRgFcLcb97Header ){ @@ -444,7 +444,7 @@ static bool parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx, const pro fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, table_stream); +// fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, table_stream); copy_FibRgFcLcb97(g_FibRgFcLcb97Header, ptr); @@ -494,7 +494,7 @@ static bool parse_fibRgFcLcb2007(const uint8_t * ptr){ return true; } -static bool test_for_pictures( const property_t *word_block, const property_t * table_stream, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { +static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { bool bRet = false; const uint8_t *ptr = NULL; @@ -568,7 +568,7 @@ static bool test_for_pictures( const property_t *word_block, const property_t * goto done; } fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); - bRet = parse_fibRgFcLcb2002(ptr, idx, table_stream, g_FibRgFcLcb97Header); + bRet = parse_fibRgFcLcb2002(ptr, idx, g_FibRgFcLcb97Header); break; case 0x010c: if (0x00a4 != cbRgFcLcb){ @@ -864,8 +864,10 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint /* The BLIP is embedded in this record*/ processOfficeArtBlip(&(ptr[offset])); } else { - fprintf(stderr, "%s::%d::Still trying to figure out where teh BLIP is!!!\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::Found the BLIP in the 'DocumentSummaryInformation' stream (searching), but can't find structures that point towards it\n", __FUNCTION__, __LINE__); + /* The BLIP is in the 'WordDocument' stream. */ + + fprintf(stderr, "%s::%d::Need to get the pointer from the word document stream here, since it's not embedded in the FBSE Structure\n", __FUNCTION__, __LINE__); + exit(11); } @@ -909,7 +911,6 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint } static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { - fprintf(stderr, "%s::%d::%p::%p\n", __FUNCTION__, __LINE__, header, ptr); size_t offset = header->fcDggInfo; uint32_t i; From 720937a64a1fb0a7657fc34ae3cb94adcb241df6 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 13:53:57 -0700 Subject: [PATCH 27/96] blah --- libclamav/ole2_extract.c | 91 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 6 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 26c40b5fc9..6aacf052c7 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -896,10 +896,15 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t char *name; int toval = 0; property_t * wordDocStream = NULL; - property_t * tableStream = NULL; + //property_t * tableStream = NULL; FibRgFcLcb97 fibRgFcLcb97Header = {0}; bool bFibRgFcLcb97HeaderInitialized = false; fib_base_t fibBase = {0}; + property_t wordDocumentStream = {0}; + property_t TableStream1 = {0}; + property_t TableStream0 = {0}; + bool TableStream1Initialized = false; + bool TableStream0Initialized = false; ole2_listmsg("ole2_walk_property_tree() called\n"); ole2_list_init(&node_list); @@ -977,6 +982,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { +#if 0 uint32_t fib_offset = get_stream_data_offset(hdr, &(prop_block[idx]), prop_block[idx].start_block); const uint8_t * ptr = NULL; @@ -991,6 +997,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t continue; } copy_fib_base(&fibBase, ptr); +#endif + memcpy(&wordDocumentStream, &(prop_block[idx]), sizeof(wordDocumentStream)); test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); bFibRgFcLcb97HeaderInitialized = test_for_pictures(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); @@ -1070,8 +1078,14 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptedPackage")) { pEncryptionStatus->encrypted = true; } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "1Table")) { +#if 0 tableStream = &(prop_block[idx]); +#else + memcpy(&TableStream1, &(prop_block[idx]), sizeof(TableStream1)); + TableStream1Initialized = true; +#endif +#if 0 size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); @@ -1083,6 +1097,9 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t exit(11); } extract_images_2(&fibRgFcLcb97Header, ptr); +#else + fprintf(stderr, "%s::%d::JUST REMOVED\n", __FUNCTION__, __LINE__); +#endif @@ -1109,7 +1126,9 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "0Table")) { -fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); + memcpy(&TableStream0, &(prop_block[idx]), sizeof(TableStream0)); + TableStream0Initialized = true; +//fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); #if 0 tableStream = &(prop_block[idx]); @@ -1311,12 +1330,72 @@ fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); //{ if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures (end of loop)\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } } +#if 1 #if 0 - fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, wordDocStream); - fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, tableStream); + FibRgFcLcb97 fibRgFcLcb97Header = {0}; + bool bFibRgFcLcb97HeaderInitialized = false; + fib_base_t fibBase = {0}; + property_t wordDocumentStream = {0}; + property_t TableStream1 = {0}; + property_t TableStream0 = {0}; + bool TableStream1Initialized = false; + bool TableStream0Initialized = false; +#endif + if (bFibRgFcLcb97HeaderInitialized && (TableStream1Initialized || TableStream0Initialized)) { + property_t * tableStream = NULL; + if (TableStream0Initialized) { + tableStream = &TableStream0; + } + if (TableStream1Initialized) { + tableStream = &TableStream1; + } + if (TableStream0Initialized && TableStream1Initialized) { + /*Get the FIBBase*/ + fib_base_t fib; + uint32_t fib_offset = get_stream_data_offset(hdr, &wordDocumentStream, wordDocumentStream.start_block); + const uint8_t * ptr = NULL; + + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + exit(99); + } + + ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + exit(99); + } + copy_fib_base(&fib, ptr); + fprintf(stderr, "%s::%d::create call for 'initializeFibBase'\n", __FUNCTION__, __LINE__); + +#define FIB_BASE_fWhichTblStm_OFFSET 9 + if (fib.ABCDEFGHIJKLM & (1 << FIB_BASE_fWhichTblStm_OFFSET)) { + tableStream = &TableStream1; + } else { + tableStream = &TableStream0; + } + } + + + + + + /*Call Extract */ + size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); + fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); + + fprintf(stderr, "%s::%d::FOUND THE OFFSET OF THE DATA. This offset + the offset referenced in 'header'\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Asking for 4k, because, why not???\n", __FUNCTION__, __LINE__); + const uint8_t * const ptr = fmap_need_off_once(hdr->map, offset, 4096); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); + exit(11); + } + extract_images_2(&fibRgFcLcb97Header, ptr); + + + - if (wordDocStream && tableStream){ - test_for_pictures(wordDocStream, tableStream, hdr); } #endif From 8ddbd8aa9a227f2dc40ff32541c03cceddbad500 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 14:41:24 -0700 Subject: [PATCH 28/96] blah --- libclamav/ole2_extract.c | 33 ++++++++++++++++++++++---- libclamav/ole2_extract_images.h | 41 +++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 6aacf052c7..5e45a2fa2e 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -895,7 +895,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cl_error_t ret; char *name; int toval = 0; - property_t * wordDocStream = NULL; + //property_t * wordDocStream = NULL; //property_t * tableStream = NULL; FibRgFcLcb97 fibRgFcLcb97Header = {0}; bool bFibRgFcLcb97HeaderInitialized = false; @@ -1018,15 +1018,19 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t - wordDocStream = &(prop_block[idx]); - fprintf(stderr, "%s::%d::%p::setting wordDocStream\n", __FUNCTION__, __LINE__, wordDocStream); + //wordDocStream = &(prop_block[idx]); + //fprintf(stderr, "%s::%d::%p::setting wordDocStream\n", __FUNCTION__, __LINE__, wordDocStream); +#if 0 { //property_t * prop = &(prop_block[idx]); - size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); + //size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); + size_t off = get_stream_data_offset(hdr, &wordDocumentStream, wordDocumentStream.start_block); +fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, __LINE__, off); off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ + fprintf(stderr, "%s::%d::CALCULATED OFFSET = %lu (0x%lx)\n", __FUNCTION__, __LINE__, off, off); fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); size_t size = 70682; //hardcoded size of the blip (temporarily); @@ -1049,6 +1053,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } +#endif @@ -1382,6 +1387,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t /*Call Extract */ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); + fprintf(stderr, "%s::%d::VALIDATE OFFSETS\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); fprintf(stderr, "%s::%d::FOUND THE OFFSET OF THE DATA. This offset + the offset referenced in 'header'\n", __FUNCTION__, __LINE__); @@ -1391,7 +1397,24 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); exit(11); } - extract_images_2(&fibRgFcLcb97Header, ptr); + + + uint8_t * wordStream = NULL; + { + size_t off = get_stream_data_offset(hdr, &wordDocumentStream, wordDocumentStream.start_block); + //off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ + + //fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); + //size_t size = 70682; //hardcoded size of the blip (temporarily); + +// wordStream = fmap_need_off_once(hdr->map, off, size); + fprintf(stderr, "%s::%d::VALIDATE wordStream\n", __FUNCTION__, __LINE__); + + } + + + + extract_images_2(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index f0fcbf8ac1..c818e7195c 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -826,11 +826,14 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ } + + + + /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr) { - +static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocStream, FibRgFcLcb97 * fibRgFcLcb97) { OfficeArtFBSEKnown fbse; uint32_t offset = sizeof(OfficeArtRecordHeader); @@ -865,6 +868,36 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint processOfficeArtBlip(&(ptr[offset])); } else { /* The BLIP is in the 'WordDocument' stream. */ + size_t offset = fibRgFcLcb97->fcDggInfo; + size_t size = fibRgFcLcb97->lcbDggInfo; + + fprintf(stderr, "%s::%d::offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); + fprintf(stderr, "%s::%d::size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); + + size = fbse.size; + offset= fbse.foDelay; + fprintf(stderr, "%s::%d::offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); + fprintf(stderr, "%s::%d::size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); + size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); + fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, __LINE__, off); + off += offset; + fprintf(stderr, "%s::%d::CALCULATED OFFSET = %lu (0x%lx)\n", __FUNCTION__, __LINE__, off, off); + if ((size_t)(hdr->m_length) < (size_t)(off + size)) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %lu (0x%lx)\n", offset, offset); + exit(11); + } + + const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %lu (0x%lx)\n", offset, offset); + fprintf(stderr, "%s::%d::ERROR (fix message)\n", __FUNCTION__, __LINE__); + exit(11); + } + + fprintf(stderr, "%s::%d::HERE\n", __FUNCTION__, __LINE__); + processOfficeArtBlip(ptr); + + fprintf(stderr, "%s::%d::Need to get the pointer from the word document stream here, since it's not embedded in the FBSE Structure\n", __FUNCTION__, __LINE__); exit(11); @@ -910,7 +943,7 @@ static void processOfficeArtFBSE(OfficeArtRecordHeader * imageHeader, const uint } -static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { +static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocStream) { size_t offset = header->fcDggInfo; uint32_t i; @@ -1028,7 +1061,7 @@ static void extract_images_2( FibRgFcLcb97 * header, const uint8_t * ptr) { /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - processOfficeArtFBSE(&imageHeader, &(ptr[offset])); + processOfficeArtFBSE(ole2Hdr, &imageHeader, &(ptr[offset]), wordDocStream, header); } else { processOfficeArtBlip(&(ptr[offset])); } From 54eefb8318911790bd5aa42ca360416313b186ad Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:02:33 -0700 Subject: [PATCH 29/96] blah --- libclamav/ole2_extract_images.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index c818e7195c..db16e1f11c 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -398,11 +398,6 @@ static void copy_OfficeArtRecordHeader (OfficeArtRecordHeader * header, const ui header->recVer_recInstance = ole2_endian_convert_16(header->recVer_recInstance); header->recType = ole2_endian_convert_16(header->recType); header->recLen = ole2_endian_convert_32(header->recLen); - - fprintf(stderr, "%s::%d::recVer_recInstance = %x\n", __FUNCTION__, __LINE__, header->recVer_recInstance); - fprintf(stderr, "%s::%d::recType = %x\n", __FUNCTION__, __LINE__, header->recType); - fprintf(stderr, "%s::%d::recLen = %x\n", __FUNCTION__, __LINE__, header->recLen); - } static uint16_t getRecInst(OfficeArtRecordHeader * header) { From 03ecef3356906abbf95897b096287cae48b6d210 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:11:39 -0700 Subject: [PATCH 30/96] blah --- libclamav/CMakeLists.txt | 1 + libclamav/ole2_extract_images.h | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libclamav/CMakeLists.txt b/libclamav/CMakeLists.txt index d20bf71f5b..d623f1b759 100644 --- a/libclamav/CMakeLists.txt +++ b/libclamav/CMakeLists.txt @@ -311,6 +311,7 @@ set(LIBCLAMAV_SOURCES msxml_parser.c msxml_parser.h msxml.c msxml.h ole2_extract.c ole2_extract.h + ole2_extract_images.c ole2_extract_images.h xlm_extract.c xlm_extract.h ooxml.c ooxml.h rtf.c rtf.h diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index db16e1f11c..dd0206ccc3 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -539,25 +539,36 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, uint16_t cbRgFcLcb; read_uint16(ptr, to_read, &idx, &cbRgFcLcb); + + /*For FIB Version numbers, see + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/175d2fe1-92dd-45d2-b091-1fe8a0c0d40a + */ +#define FIB_VERSION_FIBRGFCLCB97 0x00c1 +#define FIB_VERSION_FIBRGFCLCB2000 0x00d9 +#define FIB_VERSION_FIBRGFCLCB2002 0x0101 +#define FIB_VERSION_FIBRGFCLCB2003 0x010c +#define FIB_VERSION_FIBRGFCLCB2007 0x0112 + + switch (fib.nFib){ default: fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); goto done; - case 0x00c1: + case FIB_VERSION_FIBRGFCLCB97: if (0x005d != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } bRet = parse_fibRgFcLcb97(ptr); break; - case 0x00d9: + case FIB_VERSION_FIBRGFCLCB2000: if (0x006c != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } bRet = parse_fibRgFcLcb2000(ptr); break; - case 0x0101: + case FIB_VERSION_FIBRGFCLCB2002: if (0x0088 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; @@ -565,14 +576,14 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); bRet = parse_fibRgFcLcb2002(ptr, idx, g_FibRgFcLcb97Header); break; - case 0x010c: + case FIB_VERSION_FIBRGFCLCB2003: if (0x00a4 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } bRet = parse_fibRgFcLcb2003(ptr); break; - case 0x0112: + case FIB_VERSION_FIBRGFCLCB2007: if (0x00b7 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; From 650b936aac609cbfa7d192d0c09fd29637c7b4eb Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:16:43 -0700 Subject: [PATCH 31/96] blah --- libclamav/ole2_extract_images.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index dd0206ccc3..ef960e140e 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -549,27 +549,33 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, #define FIB_VERSION_FIBRGFCLCB2003 0x010c #define FIB_VERSION_FIBRGFCLCB2007 0x0112 +#define FIB_64BITCNT_FIBRGFCLCB97 0x005d +#define FIB_64BITCNT_FIBRGFCLCB2000 0x006c +#define FIB_64BITCNT_FIBRGFCLCB2002 0x0088 +#define FIB_64BITCNT_FIBRGFCLCB2003 0x00a4 +#define FIB_64BITCNT_FIBRGFCLCB2007 0x00b7 + switch (fib.nFib){ default: fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); goto done; case FIB_VERSION_FIBRGFCLCB97: - if (0x005d != cbRgFcLcb){ + if (FIB_64BITCNT_FIBRGFCLCB97 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } bRet = parse_fibRgFcLcb97(ptr); break; case FIB_VERSION_FIBRGFCLCB2000: - if (0x006c != cbRgFcLcb){ + if (FIB_64BITCNT_FIBRGFCLCB2000 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } bRet = parse_fibRgFcLcb2000(ptr); break; case FIB_VERSION_FIBRGFCLCB2002: - if (0x0088 != cbRgFcLcb){ + if (FIB_64BITCNT_FIBRGFCLCB2002 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } @@ -577,14 +583,14 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, bRet = parse_fibRgFcLcb2002(ptr, idx, g_FibRgFcLcb97Header); break; case FIB_VERSION_FIBRGFCLCB2003: - if (0x00a4 != cbRgFcLcb){ + if (FIB_64BITCNT_FIBRGFCLCB2003 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } bRet = parse_fibRgFcLcb2003(ptr); break; case FIB_VERSION_FIBRGFCLCB2007: - if (0x00b7 != cbRgFcLcb){ + if (FIB_64BITCNT_FIBRGFCLCB2007 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } From fe238c66ad810e16f17b750a58be93ccaafad94c Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:23:25 -0700 Subject: [PATCH 32/96] blah --- libclamav/ole2_extract_images.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ef960e140e..f5ce40d174 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -409,6 +409,7 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { } +#if 0 static bool parse_fibRgFcLcb97(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); return true; @@ -420,7 +421,6 @@ static bool parse_fibRgFcLcb2000(const uint8_t * ptr){ } -#if 0 /* * TODO: MOVE THIS TO A STRUCTURE THAT IS PASSED IN, BUT * CURRENTLY TRYING TO FIGURE OUT IF I AM FINDING ALL THE DATA CORRECTLY @@ -431,7 +431,7 @@ FibRgFcLcb97 g_FibRgFcLcb97Header; -static bool parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx +static bool parse_fibRgFcLcb97(const uint8_t * base_ptr, size_t idx , FibRgFcLcb97 * g_FibRgFcLcb97Header ){ @@ -479,6 +479,7 @@ static bool parse_fibRgFcLcb2002(const uint8_t * base_ptr, size_t idx return true; } +#if 0 static bool parse_fibRgFcLcb2003(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); return true; @@ -488,6 +489,7 @@ static bool parse_fibRgFcLcb2007(const uint8_t * ptr){ fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); return true; } +#endif static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { bool bRet = false; @@ -565,39 +567,45 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - bRet = parse_fibRgFcLcb97(ptr); + //bRet = parse_fibRgFcLcb97(ptr); break; case FIB_VERSION_FIBRGFCLCB2000: if (FIB_64BITCNT_FIBRGFCLCB2000 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - bRet = parse_fibRgFcLcb2000(ptr); + //bRet = parse_fibRgFcLcb2000(ptr); break; case FIB_VERSION_FIBRGFCLCB2002: if (FIB_64BITCNT_FIBRGFCLCB2002 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); - bRet = parse_fibRgFcLcb2002(ptr, idx, g_FibRgFcLcb97Header); + //fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); + //bRet = parse_fibRgFcLcb2002(ptr, idx, g_FibRgFcLcb97Header); break; case FIB_VERSION_FIBRGFCLCB2003: if (FIB_64BITCNT_FIBRGFCLCB2003 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - bRet = parse_fibRgFcLcb2003(ptr); + //bRet = parse_fibRgFcLcb2003(ptr); break; case FIB_VERSION_FIBRGFCLCB2007: if (FIB_64BITCNT_FIBRGFCLCB2007 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - bRet = parse_fibRgFcLcb2007(ptr); + //bRet = parse_fibRgFcLcb2007(ptr); break; } + /* Since all of the FibBlock structures have a FibRgFcLcb97 at the beginning, we only need one copy function. + * See https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/175d2fe1-92dd-45d2-b091-1fe8a0c0d40a + * for more details + */ + bRet = parse_fibRgFcLcb97(ptr, idx, g_FibRgFcLcb97Header); + done: return bRet; } From 6d768b2c600dc4a0fac788c3042a7c0d420c304b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:29:17 -0700 Subject: [PATCH 33/96] blah --- libclamav/ole2_extract_images.h | 88 --------------------------------- 1 file changed, 88 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index f5ce40d174..f56f858692 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -567,36 +567,30 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - //bRet = parse_fibRgFcLcb97(ptr); break; case FIB_VERSION_FIBRGFCLCB2000: if (FIB_64BITCNT_FIBRGFCLCB2000 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - //bRet = parse_fibRgFcLcb2000(ptr); break; case FIB_VERSION_FIBRGFCLCB2002: if (FIB_64BITCNT_FIBRGFCLCB2002 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - //fprintf(stderr, "%s::%d::idx = %u (0x%x)\n", __FUNCTION__, __LINE__, idx, idx); - //bRet = parse_fibRgFcLcb2002(ptr, idx, g_FibRgFcLcb97Header); break; case FIB_VERSION_FIBRGFCLCB2003: if (FIB_64BITCNT_FIBRGFCLCB2003 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - //bRet = parse_fibRgFcLcb2003(ptr); break; case FIB_VERSION_FIBRGFCLCB2007: if (FIB_64BITCNT_FIBRGFCLCB2007 != cbRgFcLcb){ fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); goto done; } - //bRet = parse_fibRgFcLcb2007(ptr); break; } @@ -614,34 +608,6 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, #if 0 -static void extract_images( FibRgFcLcb97 * header, const property_t * table_stream, ole2_header_t *hdr) { - - int i; - const uint8_t * table_stream_data = (const uint8_t*) table_stream; - size_t offset = header->fcDggInfo; - - fprintf(stderr, "%s::%d::%p::%lu::(0x%lx)Entering\n", __FUNCTION__, __LINE__, hdr, offset, offset); -#if 0 - const uint8_t * ptr = fmap_need_off_once(hdr->map, &(table_stream_data[offset]), header->lcbDggInfo ); -#else - const uint8_t * ptr = &(table_stream_data[offset]); -#endif - fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, ptr); - - OfficeArtRecordHeader officeArtDggContainer; - copy_OfficeArtRecordHeader (&officeArtDggContainer, ptr); - /*None of the values make sense.*/ - - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = 0; i < 8; i++){ - fprintf(stderr, "%02x ", table_stream_data[offset + i]); - } - fprintf(stderr, "\n"); - - offset += 8; //size of OfficeArtRecordHeader - - fprintf(stderr, "%s::%d::Leaving\n", __FUNCTION__, __LINE__); -} #else typedef struct __attribute__((packed)) { @@ -715,7 +681,6 @@ static void saveImageFile(const uint8_t * const ptr, size_t size){ fprintf(stderr, "%s::%d::NOT Success\n", __FUNCTION__, __LINE__); } - //exit(11); } @@ -871,18 +836,8 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima exit(1); } - fprintf(stderr, "%s::%d::imageHeader->recLen = %d\n", __FUNCTION__, __LINE__, imageHeader->recLen); - fprintf(stderr, "%s::%d::blip size = %d\n", __FUNCTION__, __LINE__, fbse.size); - fprintf(stderr, "%s::%d::delay = %d\n", __FUNCTION__, __LINE__, fbse.foDelay); - fprintf(stderr, "%s::%d::recInst = %d (0x%x)\n", __FUNCTION__, __LINE__, recInst, recInst); - - fprintf(stderr, "%s::%d::fbse.btWin32 = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.btWin32, fbse.btWin32); - fprintf(stderr, "%s::%d::fbse.btMacOS = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.btMacOS, fbse.btMacOS); - offset += fbse.cbName; - fprintf(stderr, "%s::%d::Since the recLen is 36 (for this file), there is no name data or embedded blip record, so I need to figure out how this delay stream works???\n", __FUNCTION__, __LINE__); - if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ processOfficeArtBlip(&(ptr[offset])); @@ -916,50 +871,7 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima fprintf(stderr, "%s::%d::HERE\n", __FUNCTION__, __LINE__); processOfficeArtBlip(ptr); - - - - fprintf(stderr, "%s::%d::Need to get the pointer from the word document stream here, since it's not embedded in the FBSE Structure\n", __FUNCTION__, __LINE__); - exit(11); - } - - - -#if 0 - offset += fbse.foDelay; - uint8_t * blah = fmap_need_off_once(pGLOBAL_HEADER->map, offset, fbse.size); - processOfficeArtBlip(blah); -#else - - -#if 0 - { - size_t andy; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (andy = 0; andy < 1024; andy++) { - fprintf(stderr, "%02x ", ptr[offset + andy]); - - } - fprintf(stderr, "\n"); - - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (andy = 0; andy < 1024; andy++) { - char c = ptr[offset + andy]; - if (c) fprintf(stderr, "%c ", c); - else fprintf(stderr, " "); - - } - fprintf(stderr, "\n"); - - fprintf(stderr, "%s::%d::Figure out what is going on here, since none of the record types match the documentation\n", __FUNCTION__, __LINE__); - exit(1); } -#endif - - - -// processOfficeArtBlip(&(ptr[offset])); -#endif } From f502817e08a468d5bb610a2b38c569722a0e1a6d Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:33:27 -0700 Subject: [PATCH 34/96] blah --- libclamav/ole2_extract_images.h | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index f56f858692..70114d9fd8 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -843,28 +843,18 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima processOfficeArtBlip(&(ptr[offset])); } else { /* The BLIP is in the 'WordDocument' stream. */ - size_t offset = fibRgFcLcb97->fcDggInfo; - size_t size = fibRgFcLcb97->lcbDggInfo; + size_t size = fbse.size; - fprintf(stderr, "%s::%d::offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - fprintf(stderr, "%s::%d::size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); - - size = fbse.size; - offset= fbse.foDelay; - fprintf(stderr, "%s::%d::offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - fprintf(stderr, "%s::%d::size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); - fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, __LINE__, off); - off += offset; - fprintf(stderr, "%s::%d::CALCULATED OFFSET = %lu (0x%lx)\n", __FUNCTION__, __LINE__, off, off); + off += fbse.foDelay; if ((size_t)(hdr->m_length) < (size_t)(off + size)) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %lu (0x%lx)\n", offset, offset); + cli_dbgmsg("ERROR: Invalid offset for File Information Block %u (0x%x)\n", fbse.foDelay, fbse.foDelay); exit(11); } const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %lu (0x%lx)\n", offset, offset); + cli_dbgmsg("ERROR: Invalid offset for File Information Block %u (0x%x)\n", fbse.foDelay, fbse.foDelay); fprintf(stderr, "%s::%d::ERROR (fix message)\n", __FUNCTION__, __LINE__); exit(11); } From ef44fb9fc054f8e56d6a7e91bec7d72186814a38 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:36:08 -0700 Subject: [PATCH 35/96] balh --- libclamav/ole2_extract.c | 34 --------------------------------- libclamav/ole2_extract_images.h | 5 ++--- 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 5e45a2fa2e..c4d7e7434b 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -895,11 +895,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cl_error_t ret; char *name; int toval = 0; - //property_t * wordDocStream = NULL; - //property_t * tableStream = NULL; FibRgFcLcb97 fibRgFcLcb97Header = {0}; bool bFibRgFcLcb97HeaderInitialized = false; - fib_base_t fibBase = {0}; property_t wordDocumentStream = {0}; property_t TableStream1 = {0}; property_t TableStream0 = {0}; @@ -1335,17 +1332,6 @@ fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, _ //{ if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures (end of loop)\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } } -#if 1 -#if 0 - FibRgFcLcb97 fibRgFcLcb97Header = {0}; - bool bFibRgFcLcb97HeaderInitialized = false; - fib_base_t fibBase = {0}; - property_t wordDocumentStream = {0}; - property_t TableStream1 = {0}; - property_t TableStream0 = {0}; - bool TableStream1Initialized = false; - bool TableStream0Initialized = false; -#endif if (bFibRgFcLcb97HeaderInitialized && (TableStream1Initialized || TableStream0Initialized)) { property_t * tableStream = NULL; if (TableStream0Initialized) { @@ -1398,29 +1384,9 @@ fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, _ exit(11); } - - uint8_t * wordStream = NULL; - { - size_t off = get_stream_data_offset(hdr, &wordDocumentStream, wordDocumentStream.start_block); - //off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ - - //fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); - //size_t size = 70682; //hardcoded size of the blip (temporarily); - -// wordStream = fmap_need_off_once(hdr->map, off, size); - fprintf(stderr, "%s::%d::VALIDATE wordStream\n", __FUNCTION__, __LINE__); - - } - - - extract_images_2(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); - - - } -#endif ole2_list_delete(&node_list); return CL_SUCCESS; diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 70114d9fd8..a72bbadd31 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -818,7 +818,7 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocStream, FibRgFcLcb97 * fibRgFcLcb97) { +static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocStream) { OfficeArtFBSEKnown fbse; uint32_t offset = sizeof(OfficeArtRecordHeader); @@ -859,7 +859,6 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima exit(11); } - fprintf(stderr, "%s::%d::HERE\n", __FUNCTION__, __LINE__); processOfficeArtBlip(ptr); } @@ -983,7 +982,7 @@ static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, con /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - processOfficeArtFBSE(ole2Hdr, &imageHeader, &(ptr[offset]), wordDocStream, header); + processOfficeArtFBSE(ole2Hdr, &imageHeader, &(ptr[offset]), wordDocStream); } else { processOfficeArtBlip(&(ptr[offset])); } From 93281b1828d8ca7bf8b5dbaababfe785c62f3726 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:38:09 -0700 Subject: [PATCH 36/96] blah --- libclamav/ole2_extract_images.h | 58 --------------------------------- 1 file changed, 58 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index a72bbadd31..a8bee77e74 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -944,27 +944,6 @@ static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, con offset += sizeof(OfficeArtRecordHeader); -#if 0 - /*I *hate* doing this, but I have been unable to figuer out why I need to increment by 2 bytes here. There is - * nothing in the documentation that I have found to account for these bytes, so I am going to increment them - * here, and hope it makes sense at some point??? - * - * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/561cb6d4-d38b-4666-b2b4-10abc1dce44c - * - * */ - offset += 2; -#endif - - - - - - - - - - - /*Rec types taken from * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/a7d7d967-6bff-489c-a267-3ec30448344a * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 @@ -987,43 +966,6 @@ static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, con processOfficeArtBlip(&(ptr[offset])); } } - - - - - - -#if 0 - - - - - - - -//fprintf(stderr, "%s::%d::Process blip store here\n", __FUNCTION__, __LINE__); - uint16_t numRecords = (blipStoreRecordHeader.recVer_recInstance & 0xfff0) >> 4; - - offset += sizeof(OfficeArtRecordHeader); - - - /*I am thinking I need to increment offset by 2 here, but I can't find anything in the docs to say why. - * That's just what all the files appear to be expecting.*/ - - fprintf(stderr, "%s::%d::offset = %lx\n", __FUNCTION__, __LINE__, offset); - fprintf(stderr, "%s::%d::numRecords = 0x%x\n", __FUNCTION__, __LINE__, numRecords); -#endif - - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = 0; i < blipStoreRecordHeader.recLen; i++){ - fprintf(stderr, "%02x ", ptr[offset + i]); - } - fprintf(stderr, "\n"); - - - fprintf(stderr, "%s::%d::Got to end\n", __FUNCTION__, __LINE__); - - exit(11); } #endif From 8f05ac20fdb1c05febbb95bb918b73cd266b0b4b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:41:29 -0700 Subject: [PATCH 37/96] balh --- libclamav/ole2_extract.c | 202 ++------------------------------------- 1 file changed, 7 insertions(+), 195 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index c4d7e7434b..64af4280e0 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -979,98 +979,9 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { -#if 0 - uint32_t fib_offset = get_stream_data_offset(hdr, &(prop_block[idx]), prop_block[idx].start_block); - const uint8_t * ptr = NULL; - - if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - continue; - } - - ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - continue; - } - copy_fib_base(&fibBase, ptr); -#endif memcpy(&wordDocumentStream, &(prop_block[idx]), sizeof(wordDocumentStream)); test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); bFibRgFcLcb97HeaderInitialized = test_for_pictures(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); - -#if 1 - { - fprintf(stderr, "%s::%d::Delete this when ready\n", __FUNCTION__, __LINE__); - if (bFibRgFcLcb97HeaderInitialized) { - size_t offset = fibRgFcLcb97Header.fcDggInfo; - size_t size = fibRgFcLcb97Header.lcbDggInfo; - fprintf(stderr, "%s::%d::Offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - fprintf(stderr, "%s::%d::Size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); - fprintf(stderr, "%s::%d::verify that these values are the same as the offset and size printed in 'parse_fibRgFcLcb2002'\n", __FUNCTION__, __LINE__); - } - fprintf(stderr, "%s::%d::END Delete this when ready\n", __FUNCTION__, __LINE__); - } -#endif - - - - //wordDocStream = &(prop_block[idx]); - //fprintf(stderr, "%s::%d::%p::setting wordDocStream\n", __FUNCTION__, __LINE__, wordDocStream); - - -#if 0 - { - - //property_t * prop = &(prop_block[idx]); - //size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); - size_t off = get_stream_data_offset(hdr, &wordDocumentStream, wordDocumentStream.start_block); -fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, __LINE__, off); - off += 3623; /*Hardcoding the size of the delay, need to get it progromatically.*/ - fprintf(stderr, "%s::%d::CALCULATED OFFSET = %lu (0x%lx)\n", __FUNCTION__, __LINE__, off, off); - - fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); - size_t size = 70682; //hardcoded size of the blip (temporarily); - - const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); - size_t i; - fprintf(stderr, "%s::%d::WORDDOCUMENT::", __FUNCTION__, __LINE__); - for (i = 0; i < 50; i++) { - fprintf(stderr, "%02x ", ptr[i]); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "%s::%d::MOVE THIS TO SOMEPLACE THAT ACTUALLY MAKES SENSE!!!\n", __FUNCTION__, __LINE__); - - processOfficeArtBlip(ptr); - - - -// DATA IS HERE at delay. Some other bytes there, but really close, so need to *hopefully* figure out how to parse whatever header there is and verify that this is actually my data; - - - } -#endif - - - - -#if 0 - { - size_t i; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = 0; i < 4096; i++){ - if (i && (0 == i%512)){ - fprintf("\n%s::%d::", __FUNCTION__, __LINE__); - } - fprintf(stderr, "%02x ", ((uint8_t*) &(prop_block[idx])[i]); - } - } -#endif - - - - } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { @@ -1080,99 +991,12 @@ fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, _ } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptedPackage")) { pEncryptionStatus->encrypted = true; } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "1Table")) { -#if 0 - tableStream = &(prop_block[idx]); -#else memcpy(&TableStream1, &(prop_block[idx]), sizeof(TableStream1)); TableStream1Initialized = true; -#endif - -#if 0 - size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); - fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); - - fprintf(stderr, "%s::%d::FOUND THE OFFSET OF THE DATA. This offset + the offset referenced in 'header'\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::Asking for 4k, because, why not???\n", __FUNCTION__, __LINE__); - const uint8_t * const ptr = fmap_need_off_once(hdr->map, offset, 4096); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); - exit(11); - } - extract_images_2(&fibRgFcLcb97Header, ptr); -#else - fprintf(stderr, "%s::%d::JUST REMOVED\n", __FUNCTION__, __LINE__); -#endif - - - -#if 0 - - fprintf(stderr, "%s::%d::TODO: Move this to where the data is actually processed\n", __FUNCTION__, __LINE__); - //uint8_t* ptr = fmap_need_off_once(hdr->map, tableStream, 0x1000); - uint8_t* ptr = (uint8_t*) tableStream; - fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, ptr); - size_t i; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = 0; i < 1024; i++){ - fprintf(stderr, "%02x ", ptr[ i ]); - } - fprintf(stderr, "\n"); - - - extract_images(&header, tableStream, hdr); - -#endif - - fprintf(stderr, "%s::%d::TODO: HANDLE TABLE STREAM\n", __FUNCTION__, __LINE__); - - } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "0Table")) { - memcpy(&TableStream0, &(prop_block[idx]), sizeof(TableStream0)); TableStream0Initialized = true; -//fprintf(stderr, "%s::%d::Implement this\n", __FUNCTION__, __LINE__); exit(112); - -#if 0 - tableStream = &(prop_block[idx]); - extract_images(&header, tableStream); - fprintf(stderr, "%s::%d::TODO: HANDLE TABLE STREAM\n", __FUNCTION__, __LINE__); -#endif -#if 0 - } else { - - property_t * prop = &(prop_block[idx]); - size_t off = get_stream_data_offset(hdr, prop, prop->start_block); - size_t size = 4096 * 2; - size = 4096; - const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); - size_t i; - if (ptr) { - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = 0; i < size ; i++) { - //fprintf(stderr, "%02x ", ptr[i + 3623]); - fprintf(stderr, "%02x ", ptr[i]); - } - fprintf(stderr, "\n"); - } - -#endif - - } - -#if 0 - if (wordDocStream && tableStream) { - test_for_pictures(wordDocStream, tableStream, hdr); } -#endif - - -// { if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } - - - - - - ole2_listmsg("printing ole2 property\n"); if (dir) @@ -1192,8 +1016,6 @@ fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, _ } ole2_listmsg("prev: %d next %d child %d\n", prop_block[idx].prev, prop_block[idx].next, prop_block[idx].child); - //{ if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } - ole2_listmsg("node type: %d\n", prop_block[idx].type); switch (prop_block[idx].type) { case 5: /* Root Entry */ @@ -1329,7 +1151,6 @@ fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, _ break; } ole2_listmsg("loop ended: %d %d\n", ole2_list_size(&node_list), ole2_list_is_empty(&node_list)); -//{ if (wordDocStream) { fprintf(stderr, "%s::%d::Calling test_for_pictures (end of loop)\n", __FUNCTION__, __LINE__); test_for_pictures(wordDocStream, tableStream, hdr); } } } if (bFibRgFcLcb97HeaderInitialized && (TableStream1Initialized || TableStream0Initialized)) { @@ -1367,24 +1188,15 @@ fprintf(stderr, "%s::%d::get_stream_data_offset returned %lu\n", __FUNCTION__, _ } } - - - - /*Call Extract */ - size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); - fprintf(stderr, "%s::%d::VALIDATE OFFSETS\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::offset = %lu::offset = %lx\n", __FUNCTION__, __LINE__, offset, offset); - - fprintf(stderr, "%s::%d::FOUND THE OFFSET OF THE DATA. This offset + the offset referenced in 'header'\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::Asking for 4k, because, why not???\n", __FUNCTION__, __LINE__); - const uint8_t * const ptr = fmap_need_off_once(hdr->map, offset, 4096); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); - exit(11); - } + size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); + const uint8_t * const ptr = fmap_need_off_once(hdr->map, offset, 4096); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); + exit(11); + } - extract_images_2(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); + extract_images_2(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); } From a294338ea795e31c523987f2bf75538e101ba2b7 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:45:32 -0700 Subject: [PATCH 38/96] blah --- libclamav/ole2_extract.c | 2 +- libclamav/ole2_extract_images.h | 19 +++---------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 64af4280e0..a46dfd0768 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -1196,7 +1196,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t exit(11); } - extract_images_2(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); + extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index a8bee77e74..5ecf0dbc06 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -864,20 +864,10 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima } -static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocStream) { +static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocStream) { size_t offset = header->fcDggInfo; uint32_t i; -#if 0 - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i= 0; i < 100; i++) { - fprintf(stderr, "%02x ", ptr[i]); - } - fprintf(stderr, "\n"); -#endif - - fprintf(stderr, "%s::%d::offset = %lx\n", __FUNCTION__, __LINE__, offset); - /* * Start of OfficeArtContent * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3 @@ -889,8 +879,8 @@ static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, con /*TODO: validate recVer and recInst separately*/ if (0xf != oadc_recordHeader.recVer_recInstance){ - fprintf(stderr, "%s::%d::Error\n", __FUNCTION__, __LINE__); - exit(11); + cli_dbgmsg("ERROR: Invalid record version (%x)\n", oadc_recordHeader.recVer_recInstance); + return; } offset += sizeof (OfficeArtRecordHeader ); @@ -911,15 +901,12 @@ static void extract_images_2(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, con copy_OfficeArtFDGG(&fdgg, &(ptr[offset])); offset += sizeof(OfficeArtFDGG); - fprintf(stderr, "%s::%d::fdgg.cidcl = %d\n", __FUNCTION__, __LINE__, fdgg.cidcl); /* OfficeArtIDCL is not used in parsing images, only drawings. If details are needed, they are * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2335d2f8-109b-4cd6-ac8d-40b1237283f3 * */ #define OFFICE_ART_IDCL_LEN 8 offset += (OFFICE_ART_IDCL_LEN * (fdgg.cidcl-1)); - fprintf(stderr, "\n%s::%d::Before last one\n", __FUNCTION__, __LINE__); - /* * OfficeArtBStoreContainer * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/561cb6d4-d38b-4666-b2b4-10abc1dce44c From f91dc80a1d4e5134f7e875fb633981bf23e5ac6e Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:45:54 -0700 Subject: [PATCH 39/96] blah --- libclamav/ole2_extract_images.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 5ecf0dbc06..c2328a37cc 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -877,7 +877,6 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const OfficeArtRecordHeader oadc_recordHeader; //OfficeArtDggContainer copy_OfficeArtRecordHeader (&oadc_recordHeader, &(ptr[offset])); - /*TODO: validate recVer and recInst separately*/ if (0xf != oadc_recordHeader.recVer_recInstance){ cli_dbgmsg("ERROR: Invalid record version (%x)\n", oadc_recordHeader.recVer_recInstance); return; From 7ef73461b32fd91247517febb1e771f7e3f3ba78 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:50:24 -0700 Subject: [PATCH 40/96] blah --- libclamav/ole2_extract_images.h | 59 +++++---------------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index c2328a37cc..90d70e393e 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -408,46 +408,14 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { return header->recVer_recInstance & 0xf; } - #if 0 -static bool parse_fibRgFcLcb97(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); - return true; -} - -static bool parse_fibRgFcLcb2000(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); - return true; -} - - -/* - * TODO: MOVE THIS TO A STRUCTURE THAT IS PASSED IN, BUT - * CURRENTLY TRYING TO FIGURE OUT IF I AM FINDING ALL THE DATA CORRECTLY - */ -FibRgFcLcb97 g_FibRgFcLcb97Header; -#endif - - - - -static bool parse_fibRgFcLcb97(const uint8_t * base_ptr, size_t idx - , FibRgFcLcb97 * g_FibRgFcLcb97Header -){ +static bool parse_fibRgFcLcb97(const uint8_t * base_ptr, size_t idx, FibRgFcLcb97 * g_FibRgFcLcb97Header){ const uint8_t * ptr = &(base_ptr[idx]); - - fprintf(stderr, "%s::%d::Data is in the fcDggInfo, size is in the lcbDggInfo\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::Structure is the FibRgFcLcb97\n", __FUNCTION__, __LINE__); -// fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, table_stream); - copy_FibRgFcLcb97(g_FibRgFcLcb97Header, ptr); /*Offset is in the TableStream.*/ size_t offset = g_FibRgFcLcb97Header->fcDggInfo; - size_t size = g_FibRgFcLcb97Header->lcbDggInfo; - fprintf(stderr, "%s::%d::Offset = %lu (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - fprintf(stderr, "%s::%d::Size = %lu (0x%lx)\n", __FUNCTION__, __LINE__, size, size); /*Information about * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/dd7133b6-ed10-4bcb-be29-67b0544f884f @@ -456,28 +424,11 @@ static bool parse_fibRgFcLcb97(const uint8_t * base_ptr, size_t idx * */ OfficeArtRecordHeader drawingGroupDataRecordHeader; copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(ptr[offset])); - - fprintf(stderr, "%s::%d::Calling second time\n", __FUNCTION__, __LINE__); copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(base_ptr[offset])); -#if 0 - if (table_stream) { - fprintf(stderr, "%s::%d::Calling THIRD time\n", __FUNCTION__, __LINE__); - copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(((const uint8_t*) table_stream )[offset])); - } -#endif - - - fprintf(stderr, "%s::%d::The offset and size information is for the OfficeArtContent header information\n", __FUNCTION__, __LINE__); - - fprintf(stderr, "https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3\n"); - - - fprintf(stderr, "%s::%d::UNIMPLEMENTED\n", __FUNCTION__, __LINE__); /* exit(11); */ - - fprintf(stderr, "%s::%d::TODO:Verify the structure is at the beginning for all of these, and change it to a normal copy\n", __FUNCTION__, __LINE__); return true; } +#endif #if 0 static bool parse_fibRgFcLcb2003(const uint8_t * ptr){ @@ -598,7 +549,13 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, * See https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/175d2fe1-92dd-45d2-b091-1fe8a0c0d40a * for more details */ +#if 0 bRet = parse_fibRgFcLcb97(ptr, idx, g_FibRgFcLcb97Header); +#else + //const uint8_t * ptr = &(base_ptr[idx]); + copy_FibRgFcLcb97(g_FibRgFcLcb97Header, &(ptr[idx])); + bRet = true; +#endif done: return bRet; From 98322348abf74210dd7a2992e570288ef85c37f1 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:52:18 -0700 Subject: [PATCH 41/96] blah --- libclamav/ole2_extract_images.h | 41 +-------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 90d70e393e..af0ce7124a 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -408,40 +408,6 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { return header->recVer_recInstance & 0xf; } -#if 0 -static bool parse_fibRgFcLcb97(const uint8_t * base_ptr, size_t idx, FibRgFcLcb97 * g_FibRgFcLcb97Header){ - - const uint8_t * ptr = &(base_ptr[idx]); - copy_FibRgFcLcb97(g_FibRgFcLcb97Header, ptr); - - /*Offset is in the TableStream.*/ - size_t offset = g_FibRgFcLcb97Header->fcDggInfo; - - /*Information about - * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/dd7133b6-ed10-4bcb-be29-67b0544f884f - * at the beginning of - * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3 - * */ - OfficeArtRecordHeader drawingGroupDataRecordHeader; - copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(ptr[offset])); - copy_OfficeArtRecordHeader (&drawingGroupDataRecordHeader, &(base_ptr[offset])); - - return true; -} -#endif - -#if 0 -static bool parse_fibRgFcLcb2003(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); - return true; -} - -static bool parse_fibRgFcLcb2007(const uint8_t * ptr){ - fprintf(stderr, "%s::%d::%p::UNIMPLEMENTED\n", __FUNCTION__, __LINE__, ptr); exit(11); - return true; -} -#endif - static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { bool bRet = false; @@ -545,17 +511,12 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, break; } - /* Since all of the FibBlock structures have a FibRgFcLcb97 at the beginning, we only need one copy function. + /* Since all of the FibBlock structures have a FibRgFcLcb97 at the beginning, we just copy the struct. * See https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/175d2fe1-92dd-45d2-b091-1fe8a0c0d40a * for more details */ -#if 0 - bRet = parse_fibRgFcLcb97(ptr, idx, g_FibRgFcLcb97Header); -#else - //const uint8_t * ptr = &(base_ptr[idx]); copy_FibRgFcLcb97(g_FibRgFcLcb97Header, &(ptr[idx])); bRet = true; -#endif done: return bRet; From 8f94a764b1c223abc436009002992689a866036b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 15:53:09 -0700 Subject: [PATCH 42/96] blah --- libclamav/ole2_extract_images.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index af0ce7124a..febb9ca8d4 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -525,9 +525,6 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, -#if 0 -#else - typedef struct __attribute__((packed)) { uint32_t spidMax; uint32_t cidcl; @@ -536,7 +533,6 @@ typedef struct __attribute__((packed)) { } OfficeArtFDGG; static void copy_OfficeArtFDGG(OfficeArtFDGG * dst, const uint8_t * const ptr){ - //size_t idx = 0; memcpy(dst, ptr, sizeof(OfficeArtFDGG)); dst->spidMax = ole2_endian_convert_32(dst->spidMax); @@ -872,7 +868,6 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const } } -#endif From 4f14b8c962e1a6ba7ebd468c81d6f32c1f8cf7ef Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 16:21:08 -0700 Subject: [PATCH 43/96] blah --- libclamav/ole2_extract_images.h | 47 ++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index febb9ca8d4..ae0eb6ffbf 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -408,6 +408,25 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { return header->recVer_recInstance & 0xf; } +static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, const property_t * block, size_t to_read){ + const uint8_t * ptr = NULL; + + uint32_t offset = get_stream_data_offset(hdr, block, block->start_block); + if ((size_t)(hdr->m_length) < (size_t)(offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for stream %d (0x%x)\n", offset, offset); + goto done; + } + + ptr = fmap_need_off_once(hdr->map, offset, to_read); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", offset, offset); + goto done; + } + +done: + return ptr; +} + static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { bool bRet = false; @@ -415,8 +434,23 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, fib_base_t fib = {0}; size_t to_read = 0x1000; - uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); +#define FIBRGW97_SIZE 28 +#define FIBRGLW97_SIZE 88 + /*Bytes we need. + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 + * */ + to_read = sizeof(fib_base_t) + + 2 + /*csw*/ + FIBRGW97_SIZE + + 2 + /*cslw*/ + FIBRGLW97_SIZE + + 2 + /*cbRgFcLcb */ + sizeof(FibRgFcLcb97) + ; + +#if 0 + uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); goto done; @@ -427,6 +461,12 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); goto done; } +#else + ptr = load_pointer_to_stream_from_fmap(hdr, word_block, to_read); + if (NULL == ptr) { + goto done; + } +#endif copy_fib_base(&fib, ptr); #define FIB_BASE_IDENTIFIER 0xa5ec @@ -445,8 +485,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, goto done; } - fprintf(stderr, "%s::%d::TODO: Make this a #define\n", __FUNCTION__, __LINE__); - idx += 28; /* Size of the fibRgW. Don't think I need anything from there. */ + idx += FIBRGW97_SIZE; /* Size of the fibRgW. Don't think I need anything from there. */ uint16_t cslw; read_uint16(ptr, to_read, &idx, &cslw); @@ -454,7 +493,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); goto done; } - idx += 88; /* Size of the FibRgLw97. Don't think I need anything from there. */ + idx += FIBRGLW97_SIZE; /* Size of the FibRgLw97. Don't think I need anything from there. */ uint16_t cbRgFcLcb; read_uint16(ptr, to_read, &idx, &cbRgFcLcb); From 644f351f43d2fd98436d161e31a0b74f6c5686b1 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 16:22:52 -0700 Subject: [PATCH 44/96] blah --- libclamav/ole2_extract_images.h | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ae0eb6ffbf..6b8a4f0247 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -408,7 +408,7 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { return header->recVer_recInstance & 0xf; } -static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, const property_t * block, size_t to_read){ +static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, const property_t * block, size_t size){ const uint8_t * ptr = NULL; uint32_t offset = get_stream_data_offset(hdr, block, block->start_block); @@ -417,7 +417,7 @@ static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, cons goto done; } - ptr = fmap_need_off_once(hdr->map, offset, to_read); + ptr = fmap_need_off_once(hdr->map, offset, size); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", offset, offset); goto done; @@ -432,15 +432,13 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, const uint8_t *ptr = NULL; fib_base_t fib = {0}; - size_t to_read = 0x1000; #define FIBRGW97_SIZE 28 #define FIBRGLW97_SIZE 88 - /*Bytes we need. * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 * */ - to_read = sizeof(fib_base_t) + + size_t size = sizeof(fib_base_t) + 2 + /*csw*/ FIBRGW97_SIZE + 2 + /*cslw*/ @@ -449,24 +447,10 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, sizeof(FibRgFcLcb97) ; -#if 0 - uint32_t fib_offset = get_stream_data_offset(hdr, word_block, word_block->start_block); - if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - goto done; - } - - ptr = fmap_need_off_once(hdr->map, fib_offset, to_read); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - goto done; - } -#else - ptr = load_pointer_to_stream_from_fmap(hdr, word_block, to_read); + ptr = load_pointer_to_stream_from_fmap(hdr, word_block, size); if (NULL == ptr) { goto done; } -#endif copy_fib_base(&fib, ptr); #define FIB_BASE_IDENTIFIER 0xa5ec @@ -479,7 +463,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, uint32_t idx = sizeof(fib); /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/9aeaa2e7-4a45-468e-ab13-3f6193eb9394 */ uint16_t csw; - read_uint16(ptr, to_read, &idx, &csw); + read_uint16(ptr, size, &idx, &csw); if (0x000e != csw){ fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); goto done; @@ -488,7 +472,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, idx += FIBRGW97_SIZE; /* Size of the fibRgW. Don't think I need anything from there. */ uint16_t cslw; - read_uint16(ptr, to_read, &idx, &cslw); + read_uint16(ptr, size, &idx, &cslw); if (0x0016 != cslw) { fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); goto done; @@ -496,7 +480,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, idx += FIBRGLW97_SIZE; /* Size of the FibRgLw97. Don't think I need anything from there. */ uint16_t cbRgFcLcb; - read_uint16(ptr, to_read, &idx, &cbRgFcLcb); + read_uint16(ptr, size, &idx, &cbRgFcLcb); /*For FIB Version numbers, see * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/175d2fe1-92dd-45d2-b091-1fe8a0c0d40a From 86161834794aebf73b62f8b975f6c00f21140814 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 16:38:42 -0700 Subject: [PATCH 45/96] blah --- libclamav/ole2_extract.c | 2 +- libclamav/ole2_extract_images.h | 70 +++++++++++++++------------------ 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index a46dfd0768..0a7116a766 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -981,7 +981,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { memcpy(&wordDocumentStream, &(prop_block[idx]), sizeof(wordDocumentStream)); test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); - bFibRgFcLcb97HeaderInitialized = test_for_pictures(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); + bFibRgFcLcb97HeaderInitialized = getFibRgFcLcb97Header(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 6b8a4f0247..d027033f24 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -408,10 +408,11 @@ static uint8_t getRecVer(OfficeArtRecordHeader * header) { return header->recVer_recInstance & 0xf; } -static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, const property_t * block, size_t size){ +static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, const property_t * block, size_t delay, size_t size){ const uint8_t * ptr = NULL; uint32_t offset = get_stream_data_offset(hdr, block, block->start_block); + offset += delay; if ((size_t)(hdr->m_length) < (size_t)(offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for stream %d (0x%x)\n", offset, offset); goto done; @@ -427,7 +428,7 @@ static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, cons return ptr; } -static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, FibRgFcLcb97 * g_FibRgFcLcb97Header) { +static bool getFibRgFcLcb97Header( const property_t *word_block, ole2_header_t *hdr, FibRgFcLcb97 * pFibRgFcLcb97Header) { bool bRet = false; const uint8_t *ptr = NULL; @@ -447,7 +448,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, sizeof(FibRgFcLcb97) ; - ptr = load_pointer_to_stream_from_fmap(hdr, word_block, size); + ptr = load_pointer_to_stream_from_fmap(hdr, word_block, 0, size); if (NULL == ptr) { goto done; } @@ -465,7 +466,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, uint16_t csw; read_uint16(ptr, size, &idx, &csw); if (0x000e != csw){ - fprintf(stderr, "%s::%d::Invalid csw = 0x%x\n", __FUNCTION__, __LINE__, csw); + cli_dbgmsg("ERROR Invalid csw = 0x%x\n", csw); goto done; } @@ -474,7 +475,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, uint16_t cslw; read_uint16(ptr, size, &idx, &cslw); if (0x0016 != cslw) { - fprintf(stderr, "%s::%d::Invalid cslw = 0x%x\n", __FUNCTION__, __LINE__, cslw); + cli_dbgmsg("ERROR Invalid cslw = 0x%x\n", cslw); goto done; } idx += FIBRGLW97_SIZE; /* Size of the FibRgLw97. Don't think I need anything from there. */ @@ -500,35 +501,35 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, switch (fib.nFib){ default: - fprintf(stderr, "%s::%d::Invalid fib.nFib\n", __FUNCTION__, __LINE__); + cli_dbgmsg("ERROR Invalid fib.nFib = 0x%x\n", fib.nFib); goto done; case FIB_VERSION_FIBRGFCLCB97: if (FIB_64BITCNT_FIBRGFCLCB97 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + cli_dbgmsg("ERROR Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", fib.nFib, cbRgFcLcb); goto done; } break; case FIB_VERSION_FIBRGFCLCB2000: if (FIB_64BITCNT_FIBRGFCLCB2000 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + cli_dbgmsg("ERROR Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", fib.nFib, cbRgFcLcb); goto done; } break; case FIB_VERSION_FIBRGFCLCB2002: if (FIB_64BITCNT_FIBRGFCLCB2002 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + cli_dbgmsg("ERROR Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", fib.nFib, cbRgFcLcb); goto done; } break; case FIB_VERSION_FIBRGFCLCB2003: if (FIB_64BITCNT_FIBRGFCLCB2003 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + cli_dbgmsg("ERROR Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", fib.nFib, cbRgFcLcb); goto done; } break; case FIB_VERSION_FIBRGFCLCB2007: if (FIB_64BITCNT_FIBRGFCLCB2007 != cbRgFcLcb){ - fprintf(stderr, "%s::%d::Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", __FUNCTION__, __LINE__, fib.nFib, cbRgFcLcb); + cli_dbgmsg("ERROR Invalid fib.nFib(0x%x) cbRgFcLcb(0x%x) combo\n", fib.nFib, cbRgFcLcb); goto done; } break; @@ -538,7 +539,7 @@ static bool test_for_pictures( const property_t *word_block, ole2_header_t *hdr, * See https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/175d2fe1-92dd-45d2-b091-1fe8a0c0d40a * for more details */ - copy_FibRgFcLcb97(g_FibRgFcLcb97Header, &(ptr[idx])); + copy_FibRgFcLcb97(pFibRgFcLcb97Header, &(ptr[idx])); bRet = true; done: @@ -639,8 +640,8 @@ static void processOfficeArtBlipGeneric(OfficeArtRecordHeader * rh, const uint8_ if (riDoubleUID == recInst) { offset += 16; } else if (riSingleUID != recInst) { - fprintf(stderr, "%s::%d::Invaild recInst\n", __FUNCTION__, __LINE__); - exit(121); //normally just return, will fix + cli_dbgmsg("ERROR Invalid recInst 0x%x\n", recInst); + return; } offset += bytesAfterUIDs; /*metafile header*/ @@ -666,16 +667,14 @@ static void processOfficeArtBlipPICT(OfficeArtRecordHeader * rh, const uint8_t * static void processOfficeArtBlipJPEG(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); - fprintf(stderr, "%s::%d::recInst = 0x%x\n", __FUNCTION__, __LINE__, recInst); if ((0x46b == recInst) || (0x6e3 == recInst)){ offset += 16; } else if ((0x46a != recInst) && (0x6e2 != recInst)) { - fprintf(stderr, "%s::%d::Invaild recInst\n", __FUNCTION__, __LINE__); - exit(121); //normally just return, will fix + cli_dbgmsg("ERROR Invalid recInst 0x%x\n", recInst); + return; } offset += 1; /*metafile header*/ - fprintf(stderr, "%s::%d::offset = %ld\n", __FUNCTION__, __LINE__, offset); saveImageFile(&(ptr[offset]), rh->recLen - offset); } @@ -703,8 +702,8 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ offset += sizeof(OfficeArtRecordHeader ); uint8_t recVer = getRecVer(&rh); if (0 != recVer) { - fprintf(stderr, "%s::%d::Invalid recver\n", __FUNCTION__, __LINE__); - exit(110); + cli_dbgmsg("ERROR Invalid recVer 0x%x\n", recVer); + return; } #define RECTYPE_OFFICE_ART_BLIP_EMF 0xf01a @@ -741,17 +740,11 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ processOfficeArtBlipTIFF(&rh, &(ptr[offset])); break; default: - fprintf(stderr, "%s::%d::Invalid 0x%x::", __FUNCTION__, __LINE__, rh.recType); - exit(11); + cli_dbgmsg("ERROR Invalid recType 0x%x\n", rh.recType); break; } } - - - - - /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ @@ -765,12 +758,12 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima offset += sizeof(OfficeArtFBSEKnown ); if ((recInst != fbse.btWin32) && (recInst != fbse.btMacOS)) { - fprintf(stderr, "%s::%d::Invalid record, exiting (fix later)\n", __FUNCTION__, __LINE__); - exit(1); + cli_dbgmsg("ERROR Invalid recInst 0x%x\n", recInst); + return; } if (imageHeader->recType != 0xf007) { - fprintf(stderr, "%s::%d::Invalid record, exiting (fix later)\n", __FUNCTION__, __LINE__); - exit(1); + cli_dbgmsg("ERROR Invalid recType 0x%x\n", imageHeader->recType); + return; } offset += fbse.cbName; @@ -782,6 +775,7 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; +#if 0 size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); off += fbse.foDelay; if ((size_t)(hdr->m_length) < (size_t)(off + size)) { @@ -795,6 +789,9 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima fprintf(stderr, "%s::%d::ERROR (fix message)\n", __FUNCTION__, __LINE__); exit(11); } +#else + const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocStream, fbse.foDelay, size); +#endif processOfficeArtBlip(ptr); } @@ -851,19 +848,17 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const OfficeArtRecordHeader blipStoreRecordHeader; copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); - fprintf(stderr, "%s::%d::RecVer = %x\n", __FUNCTION__, __LINE__, getRecVer(&blipStoreRecordHeader)); if (0xf != getRecVer(&blipStoreRecordHeader)) { - fprintf(stderr, "%s::%d::Not a correct value, exiting (during debugging, normally just return)\n", __FUNCTION__, __LINE__); - exit(11); + cli_dbgmsg("ERROR Invalid recVer 0x%x\n", getRecVer(&blipStoreRecordHeader)); + return; } if (0xf001 != blipStoreRecordHeader.recType){ - fprintf(stderr, "%s::%d::Not a correct value, exiting (during debugging, normally just return)\n", __FUNCTION__, __LINE__); - exit(11); + cli_dbgmsg("ERROR Invalid recType 0x%x\n", getRecVer(&blipStoreRecordHeader)); + return; } uint32_t imageCnt = getRecInst (&blipStoreRecordHeader); - fprintf(stderr, "%s::%d::imageCnt = %d\n", __FUNCTION__, __LINE__, imageCnt); offset += sizeof(OfficeArtRecordHeader); @@ -878,7 +873,6 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const OfficeArtRecordHeader imageHeader; copy_OfficeArtRecordHeader(&imageHeader, &(ptr[offset])); uint8_t recVer = getRecVer(&imageHeader); - fprintf(stderr, "%s::%d::recType = %x\n", __FUNCTION__, __LINE__, recVer); if (OFFICE_ART_FBSE_REC_TYPE == recVer){ /* OfficeArtFBSE From 5772adc0c069ca2e8700c9aee3a2ca8c04a25607 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 16:43:22 -0700 Subject: [PATCH 46/96] blah --- libclamav/ole2_extract.c | 9 +++++---- libclamav/ole2_extract_images.h | 26 ++++---------------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 0a7116a766..ffe2258b3d 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -897,7 +897,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t int toval = 0; FibRgFcLcb97 fibRgFcLcb97Header = {0}; bool bFibRgFcLcb97HeaderInitialized = false; - property_t wordDocumentStream = {0}; + property_t wordDocumentBlock = {0}; property_t TableStream1 = {0}; property_t TableStream0 = {0}; bool TableStream1Initialized = false; @@ -979,7 +979,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { - memcpy(&wordDocumentStream, &(prop_block[idx]), sizeof(wordDocumentStream)); + memcpy(&wordDocumentBlock, &(prop_block[idx]), sizeof(wordDocumentBlock)); test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); bFibRgFcLcb97HeaderInitialized = getFibRgFcLcb97Header(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { @@ -1164,7 +1164,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (TableStream0Initialized && TableStream1Initialized) { /*Get the FIBBase*/ fib_base_t fib; - uint32_t fib_offset = get_stream_data_offset(hdr, &wordDocumentStream, wordDocumentStream.start_block); + uint32_t fib_offset = get_stream_data_offset(hdr, &wordDocumentBlock, wordDocumentBlock.start_block); const uint8_t * ptr = NULL; if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { @@ -1196,7 +1196,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t exit(11); } - extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentStream); + fprintf(stderr, "%s::%d::Calling extract_images\n", __FUNCTION__, __LINE__); + extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentBlock); } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d027033f24..a30f737478 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -748,7 +748,7 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocStream) { +static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { OfficeArtFBSEKnown fbse; uint32_t offset = sizeof(OfficeArtRecordHeader); @@ -774,31 +774,13 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; - -#if 0 - size_t off = get_stream_data_offset(hdr, wordDocStream, wordDocStream->start_block); - off += fbse.foDelay; - if ((size_t)(hdr->m_length) < (size_t)(off + size)) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %u (0x%x)\n", fbse.foDelay, fbse.foDelay); - exit(11); - } - - const uint8_t * const ptr = fmap_need_off_once(hdr->map, off, size); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %u (0x%x)\n", fbse.foDelay, fbse.foDelay); - fprintf(stderr, "%s::%d::ERROR (fix message)\n", __FUNCTION__, __LINE__); - exit(11); - } -#else - const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocStream, fbse.foDelay, size); -#endif - + const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); processOfficeArtBlip(ptr); } } -static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocStream) { +static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { size_t offset = header->fcDggInfo; uint32_t i; @@ -878,7 +860,7 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - processOfficeArtFBSE(ole2Hdr, &imageHeader, &(ptr[offset]), wordDocStream); + processOfficeArtFBSE(ole2Hdr, &imageHeader, &(ptr[offset]), wordDocBlock); } else { processOfficeArtBlip(&(ptr[offset])); } From 4e23df3dfa26252d72f20807ec83b5b1edc4e634 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 17:01:48 -0700 Subject: [PATCH 47/96] blah --- libclamav/ole2_extract.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index ffe2258b3d..34c5125efc 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -660,6 +660,8 @@ static void copy_fib_base(fib_base_t *pFib, const uint8_t *const ptr) pFib->reserved6 = ole2_endian_convert_32(pFib->reserved6); } + + static inline bool is_encrypted(const fib_base_t *const pFib) { return pFib->ABCDEFGHIJKLM & (1 << 8); @@ -1155,13 +1157,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (bFibRgFcLcb97HeaderInitialized && (TableStream1Initialized || TableStream0Initialized)) { property_t * tableStream = NULL; - if (TableStream0Initialized) { - tableStream = &TableStream0; - } - if (TableStream1Initialized) { - tableStream = &TableStream1; - } - if (TableStream0Initialized && TableStream1Initialized) { /*Get the FIBBase*/ fib_base_t fib; uint32_t fib_offset = get_stream_data_offset(hdr, &wordDocumentBlock, wordDocumentBlock.start_block); @@ -1169,38 +1164,43 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - exit(99); + goto done; } ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - exit(99); + goto done; } copy_fib_base(&fib, ptr); - fprintf(stderr, "%s::%d::create call for 'initializeFibBase'\n", __FUNCTION__, __LINE__); #define FIB_BASE_fWhichTblStm_OFFSET 9 if (fib.ABCDEFGHIJKLM & (1 << FIB_BASE_fWhichTblStm_OFFSET)) { tableStream = &TableStream1; + if (!TableStream1Initialized){ + cli_dbgmsg("ERROR: FIB references 1Table stream, that does not exist\n"); + goto done; + } } else { tableStream = &TableStream0; + if (!TableStream0Initialized){ + cli_dbgmsg("ERROR: FIB references 0Table stream, that does not exist\n"); + goto done; + } } - } /*Call Extract */ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); - const uint8_t * const ptr = fmap_need_off_once(hdr->map, offset, 4096); + ptr = fmap_need_off_once(hdr->map, offset, 4096); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); - exit(11); + goto done; } - fprintf(stderr, "%s::%d::Calling extract_images\n", __FUNCTION__, __LINE__); extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentBlock); - } +done: ole2_list_delete(&node_list); return CL_SUCCESS; } From 0fd2cac25660f5b10b7046ca5570077e00857f66 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 17:53:38 -0700 Subject: [PATCH 48/96] blah --- libclamav/ole2_extract.c | 46 +++++++---- libclamav/ole2_extract_images.h | 139 +++++++++++++++++++++++++------- 2 files changed, 138 insertions(+), 47 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 34c5125efc..3efbd36f45 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -888,7 +888,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t ole2_walk_property_tree_file_handler handler, unsigned int rec_level, unsigned int *file_count, cli_ctx *ctx, unsigned long *scansize, void *handler_ctx, - encryption_status_t *pEncryptionStatus) + encryption_status_t *pEncryptionStatus, ole2_image_directory_t * pImageDirectory) { property_t prop_block[4]; int32_t idx, current_block, i, curindex; @@ -897,6 +897,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cl_error_t ret; char *name; int toval = 0; +#if 0 FibRgFcLcb97 fibRgFcLcb97Header = {0}; bool bFibRgFcLcb97HeaderInitialized = false; property_t wordDocumentBlock = {0}; @@ -904,6 +905,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t property_t TableStream0 = {0}; bool TableStream1Initialized = false; bool TableStream0Initialized = false; +#endif ole2_listmsg("ole2_walk_property_tree() called\n"); ole2_list_init(&node_list); @@ -981,9 +983,11 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WORDDocument")) { - memcpy(&wordDocumentBlock, &(prop_block[idx]), sizeof(wordDocumentBlock)); test_for_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); - bFibRgFcLcb97HeaderInitialized = getFibRgFcLcb97Header(&(prop_block[idx]), hdr, &fibRgFcLcb97Header); + if (pImageDirectory) { + memcpy(&(pImageDirectory->word_block), &(prop_block[idx]), sizeof((pImageDirectory->word_block))); + pImageDirectory->bFibRgFcLcb97Header_initialized = getFibRgFcLcb97Header(&(prop_block[idx]), hdr, &(pImageDirectory->fibRgFcLcb97Header)); + } } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { @@ -993,11 +997,15 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "EncryptedPackage")) { pEncryptionStatus->encrypted = true; } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "1Table")) { - memcpy(&TableStream1, &(prop_block[idx]), sizeof(TableStream1)); - TableStream1Initialized = true; + if (pImageDirectory) { + memcpy(&(pImageDirectory->table_stream_1_block), &(prop_block[idx]), sizeof(pImageDirectory->table_stream_1_block)); + pImageDirectory->table_stream_1_initialized = true; + } } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "0Table")) { - memcpy(&TableStream0, &(prop_block[idx]), sizeof(TableStream0)); - TableStream0Initialized = true; + if (pImageDirectory) { + memcpy(&(pImageDirectory->table_stream_0_block), &(prop_block[idx]), sizeof(pImageDirectory->table_stream_0_block)); + pImageDirectory->table_stream_0_initialized = true; + } } ole2_listmsg("printing ole2 property\n"); @@ -1030,7 +1038,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } hdr->sbat_root_start = prop_block[idx].start_block; if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); + ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus, pImageDirectory); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); return ret; @@ -1071,7 +1079,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cli_dbgmsg("OLE2: filesize exceeded\n"); } if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); + ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize, handler_ctx, pEncryptionStatus, pImageDirectory); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); return ret; @@ -1122,7 +1130,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } else dirname = NULL; if ((int)(prop_block[idx].child) != -1) { - ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus); + ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize, handler_ctx, pEncryptionStatus, pImageDirectory); if (ret != CL_SUCCESS) { ole2_list_delete(&node_list); if (dirname) { @@ -1155,6 +1163,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t ole2_listmsg("loop ended: %d %d\n", ole2_list_size(&node_list), ole2_list_is_empty(&node_list)); } +#if 0 if (bFibRgFcLcb97HeaderInitialized && (TableStream1Initialized || TableStream0Initialized)) { property_t * tableStream = NULL; /*Get the FIBBase*/ @@ -1197,10 +1206,12 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t goto done; } - extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentBlock); + ole2_extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentBlock); } +#else + fprintf(stderr, "%s::%d::MOVE THIS\n", __FUNCTION__, __LINE__); +#endif -done: ole2_list_delete(&node_list); return CL_SUCCESS; } @@ -2839,6 +2850,7 @@ static bool initialize_encryption_key( cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **files, int *has_vba, int *has_xlm, int *has_image) { ole2_header_t hdr; + ole2_image_directory_t image_directory = {0}; cl_error_t ret = CL_CLEAN; size_t hdr_size; unsigned int file_count = 0; @@ -2962,7 +2974,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.has_vba = false; hdr.has_xlm = false; hdr.has_image = false; - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize, NULL, &encryption_status); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize, NULL, &encryption_status, &image_directory); cli_bitset_free(hdr.bitset); hdr.bitset = NULL; if (!file_count || !(hdr.bitset = cli_bitset_init())) { @@ -2990,7 +3002,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil goto done; } file_count = 0; - ole2_walk_property_tree(&hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2, NULL, &encryption_status); + ole2_walk_property_tree(&hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2, NULL, &encryption_status, NULL); ret = CL_CLEAN; *files = hdr.U; if (has_vba) { @@ -3007,9 +3019,9 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil /* PASS 2/B : OTF scan */ file_count = 0; if (bEncrypted) { - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf_encrypted, 0, &file_count, ctx, &scansize2, &key, &encryption_status); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf_encrypted, 0, &file_count, ctx, &scansize2, &key, &encryption_status, NULL); } else { - ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2, NULL, &encryption_status); + ret = ole2_walk_property_tree(&hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2, NULL, &encryption_status, NULL); } } @@ -3032,6 +3044,8 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil } } + ole2_process_image_directory(ctx, &hdr, &image_directory ); + done: if (hdr.bitset) { diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index a30f737478..d30443e5aa 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -599,7 +599,17 @@ static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * c dst->foDelay = ole2_endian_convert_32(dst->foDelay); } -static void saveImageFile(const uint8_t * const ptr, size_t size){ +static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size){ + + char *tempfile = NULL; + int out_fd = -1; + cl_error_t ret ; + + if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { + cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); + goto done; + } + fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY %p %lu!!!\n", __FUNCTION__, __LINE__, ptr, size); FILE * fp = fopen("andy_out.jpg", "wb"); @@ -619,6 +629,12 @@ static void saveImageFile(const uint8_t * const ptr, size_t size){ fprintf(stderr, "%s::%d::NOT Success\n", __FUNCTION__, __LINE__); } +done: + if (tempfile && !ctx->engine->keeptmp) { + remove(tempfile); + } + CLI_FREE_AND_SET_NULL(tempfile); + } @@ -631,7 +647,7 @@ static void saveImageFile(const uint8_t * const ptr, size_t size){ * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 * */ -static void processOfficeArtBlipGeneric(OfficeArtRecordHeader * rh, const uint8_t * const ptr, +static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr, uint16_t riSingleUID, uint16_t riDoubleUID, uint32_t bytesAfterUIDs) { size_t offset = 16; /* Size of rh*/ @@ -645,26 +661,26 @@ static void processOfficeArtBlipGeneric(OfficeArtRecordHeader * rh, const uint8_ } offset += bytesAfterUIDs; /*metafile header*/ - saveImageFile(&(ptr[offset]), rh->recLen - offset); + saveImageFile(ctx, &(ptr[offset]), rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32 */ -static void processOfficeArtBlipEMF(OfficeArtRecordHeader * rh, const uint8_t * const ptr) { - processOfficeArtBlipGeneric(rh, ptr, 0x3d4, 0x3d5, 34) ; +static void processOfficeArtBlipEMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr) { + processOfficeArtBlipGeneric(ctx, rh, ptr, 0x3d4, 0x3d5, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/ee892f04-f001-4531-a34b-67aab3426dcb */ -static void processOfficeArtBlipWMF(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(rh, ptr, 0x216, 0x217, 34) ; +static void processOfficeArtBlipWMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(ctx, rh, ptr, 0x216, 0x217, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/4b6c5fc5-98cc-445a-8ec7-12b2f2c05b9f */ -static void processOfficeArtBlipPICT(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(rh, ptr, 0x542, 0x543, 34) ; +static void processOfficeArtBlipPICT(cli_ctx* ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(ctx, rh, ptr, 0x542, 0x543, 34) ; } /*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ -static void processOfficeArtBlipJPEG(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ +static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); @@ -676,24 +692,24 @@ static void processOfficeArtBlipJPEG(OfficeArtRecordHeader * rh, const uint8_t * } offset += 1; /*metafile header*/ - saveImageFile(&(ptr[offset]), rh->recLen - offset); + saveImageFile(ctx, &(ptr[offset]), rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 */ -static void processOfficeArtBlipPNG(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(rh, ptr, 0x6e0, 0x6e1, 1) ; +static void processOfficeArtBlipPNG(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(ctx, rh, ptr, 0x6e0, 0x6e1, 1) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/1393bf5e-6fa0-4665-b3ec-68199b555656 */ -static void processOfficeArtBlipDIB(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(rh, ptr, 0x7a8, 0x7a9, 1) ; +static void processOfficeArtBlipDIB(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(ctx, rh, ptr, 0x7a8, 0x7a9, 1) ; } -static void processOfficeArtBlipTIFF(OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(rh, ptr, 0x6e4, 0x6e5, 1) ; +static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + processOfficeArtBlipGeneric(ctx, rh, ptr, 0x6e4, 0x6e5, 1) ; } -static void processOfficeArtBlip(const uint8_t * const ptr){ +static void processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ size_t offset = 0; OfficeArtRecordHeader rh; @@ -717,27 +733,27 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ switch (rh.recType) { case RECTYPE_OFFICE_ART_BLIP_EMF: - processOfficeArtBlipEMF(&rh, &(ptr[offset])); + processOfficeArtBlipEMF(ctx, &rh, &(ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_WMF : - processOfficeArtBlipWMF(&rh, &(ptr[offset])); + processOfficeArtBlipWMF(ctx, &rh, &(ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_PICT: - processOfficeArtBlipPICT(&rh, &(ptr[offset])); + processOfficeArtBlipPICT(ctx, &rh, &(ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_JPEG: /* fallthrough */ case RECTYPE_OFFICE_ART_BLIP_JPEG2: - processOfficeArtBlipJPEG(&rh, &(ptr[offset])); + processOfficeArtBlipJPEG(ctx, &rh, &(ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_PNG: - processOfficeArtBlipPNG(&rh, &(ptr[offset])); + processOfficeArtBlipPNG(ctx, &rh, &(ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_DIB: - processOfficeArtBlipDIB(&rh, &(ptr[offset])); + processOfficeArtBlipDIB(ctx, &rh, &(ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_TIFF: - processOfficeArtBlipTIFF(&rh, &(ptr[offset])); + processOfficeArtBlipTIFF(ctx, &rh, &(ptr[offset])); break; default: cli_dbgmsg("ERROR Invalid recType 0x%x\n", rh.recType); @@ -748,7 +764,7 @@ static void processOfficeArtBlip(const uint8_t * const ptr){ /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { +static void processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { OfficeArtFBSEKnown fbse; uint32_t offset = sizeof(OfficeArtRecordHeader); @@ -770,17 +786,17 @@ static void processOfficeArtFBSE(ole2_header_t *hdr, OfficeArtRecordHeader * ima if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ - processOfficeArtBlip(&(ptr[offset])); + processOfficeArtBlip(ctx, &(ptr[offset])); } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); - processOfficeArtBlip(ptr); + processOfficeArtBlip(ctx, ptr); } } -static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { +static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { size_t offset = header->fcDggInfo; uint32_t i; @@ -860,9 +876,9 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - processOfficeArtFBSE(ole2Hdr, &imageHeader, &(ptr[offset]), wordDocBlock); + processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[offset]), wordDocBlock); } else { - processOfficeArtBlip(&(ptr[offset])); + processOfficeArtBlip(ctx, &(ptr[offset])); } } } @@ -870,6 +886,67 @@ static void extract_images(ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const +typedef struct { + FibRgFcLcb97 fibRgFcLcb97Header; + bool bFibRgFcLcb97Header_initialized; + property_t word_block; + property_t table_stream_0_block; + property_t table_stream_1_block; + bool table_stream_0_initialized; + bool table_stream_1_initialized; +} ole2_image_directory_t; + + +void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_image_directory_t * directory ) { + if (directory->bFibRgFcLcb97Header_initialized && (directory->table_stream_0_initialized + || directory->table_stream_1_initialized)) { + property_t * tableStream = NULL; + /*Get the FIBBase*/ + fib_base_t fib; + uint32_t fib_offset = get_stream_data_offset(hdr, &(directory->word_block), directory->word_block.start_block); + const uint8_t * ptr = NULL; + + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + goto done; + } + + ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + goto done; + } + copy_fib_base(&fib, ptr); + +#define FIB_BASE_fWhichTblStm_OFFSET 9 + if (fib.ABCDEFGHIJKLM & (1 << FIB_BASE_fWhichTblStm_OFFSET)) { + tableStream = &(directory->table_stream_1_block); + if (!directory->table_stream_1_initialized){ + cli_dbgmsg("ERROR: FIB references 1Table stream, that does not exist\n"); + goto done; + } + } else { + tableStream = &(directory->table_stream_0_block); + if (!directory->table_stream_0_initialized){ + cli_dbgmsg("ERROR: FIB references 0Table stream, that does not exist\n"); + goto done; + } + } + + /*Call Extract */ + size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); + ptr = fmap_need_off_once(hdr->map, offset, 4096); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); + goto done; + } + + ole2_extract_images(ctx, hdr, &(directory->fibRgFcLcb97Header), ptr, &(directory->word_block)); + } +done: + ; +} + From 89e50d6d8d89db06ac76ace3b334a76e087c7155 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 17:59:08 -0700 Subject: [PATCH 49/96] blah --- libclamav/ole2_extract.c | 49 --------------------------------- libclamav/ole2_extract_images.h | 14 ++++++---- 2 files changed, 8 insertions(+), 55 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 3efbd36f45..c0725b26ea 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -1163,55 +1163,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t ole2_listmsg("loop ended: %d %d\n", ole2_list_size(&node_list), ole2_list_is_empty(&node_list)); } -#if 0 - if (bFibRgFcLcb97HeaderInitialized && (TableStream1Initialized || TableStream0Initialized)) { - property_t * tableStream = NULL; - /*Get the FIBBase*/ - fib_base_t fib; - uint32_t fib_offset = get_stream_data_offset(hdr, &wordDocumentBlock, wordDocumentBlock.start_block); - const uint8_t * ptr = NULL; - - if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - goto done; - } - - ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - goto done; - } - copy_fib_base(&fib, ptr); - -#define FIB_BASE_fWhichTblStm_OFFSET 9 - if (fib.ABCDEFGHIJKLM & (1 << FIB_BASE_fWhichTblStm_OFFSET)) { - tableStream = &TableStream1; - if (!TableStream1Initialized){ - cli_dbgmsg("ERROR: FIB references 1Table stream, that does not exist\n"); - goto done; - } - } else { - tableStream = &TableStream0; - if (!TableStream0Initialized){ - cli_dbgmsg("ERROR: FIB references 0Table stream, that does not exist\n"); - goto done; - } - } - - /*Call Extract */ - size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); - ptr = fmap_need_off_once(hdr->map, offset, 4096); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); - goto done; - } - - ole2_extract_images(hdr, &fibRgFcLcb97Header, ptr, &wordDocumentBlock); - } -#else - fprintf(stderr, "%s::%d::MOVE THIS\n", __FUNCTION__, __LINE__); -#endif - ole2_list_delete(&node_list); return CL_SUCCESS; } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d30443e5aa..2ff0400f36 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -604,6 +604,8 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size char *tempfile = NULL; int out_fd = -1; cl_error_t ret ; + size_t bytesWritten = 0; + FILE * fp = NULL; if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); @@ -612,8 +614,8 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY %p %lu!!!\n", __FUNCTION__, __LINE__, ptr, size); - FILE * fp = fopen("andy_out.jpg", "wb"); - size_t bytesWritten = 0; + //FILE * fp = fopen("andy_out.jpg", "wb"); + fp = fdopen(out_fd, "wb"); while (bytesWritten < size) { int ret = fwrite(&(ptr[bytesWritten]), 1, size - bytesWritten, fp); if (ret > 0) { @@ -623,12 +625,12 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size } } - if (bytesWritten == size) { - fprintf(stderr, "%s::%d::Success\n", __FUNCTION__, __LINE__); - } else { - fprintf(stderr, "%s::%d::NOT Success\n", __FUNCTION__, __LINE__); + if (bytesWritten != size) { + cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); } + fprintf(stderr, "%s::%d::Wrote to '%s'\n", __FUNCTION__, __LINE__, tempfile); + done: if (tempfile && !ctx->engine->keeptmp) { remove(tempfile); From 961702a6be4e9bc4869911d5d407160936d324c2 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 18:01:05 -0700 Subject: [PATCH 50/96] blah --- libclamav/ole2_extract_images.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 2ff0400f36..ca636ab568 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -612,9 +612,6 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size goto done; } - fprintf(stderr, "%s::%d::Actually extracting the file, FINALLY %p %lu!!!\n", __FUNCTION__, __LINE__, ptr, size); - - //FILE * fp = fopen("andy_out.jpg", "wb"); fp = fdopen(out_fd, "wb"); while (bytesWritten < size) { int ret = fwrite(&(ptr[bytesWritten]), 1, size - bytesWritten, fp); @@ -629,8 +626,6 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); } - fprintf(stderr, "%s::%d::Wrote to '%s'\n", __FUNCTION__, __LINE__, tempfile); - done: if (tempfile && !ctx->engine->keeptmp) { remove(tempfile); From 41b04463935a416434d732a240e12a586548c0d4 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 18:19:12 -0700 Subject: [PATCH 51/96] blah --- libclamav/ole2_extract_images.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ca636ab568..59b5fa2526 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -606,6 +606,7 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size cl_error_t ret ; size_t bytesWritten = 0; FILE * fp = NULL; + static json_object * ary = NULL; if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); @@ -626,6 +627,16 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); } + if (SCAN_COLLECT_METADATA && ctx->wrkproperty != NULL){ + if (NULL == ary) { +#define OLE2_EXTRACTED_IMAGES_JSON_KEY "OLE2_IMAGES" + ary = cli_jsonarray(ctx->wrkproperty, OLE2_EXTRACTED_IMAGES_JSON_KEY); + } + if (ary) { + cli_jsonstr(ary, NULL, tempfile); + } + } + done: if (tempfile && !ctx->engine->keeptmp) { remove(tempfile); From 0149abf378e4d0a01d82413a8b0c7150759dc799 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 19 Jul 2024 18:22:32 -0700 Subject: [PATCH 52/96] Blha --- libclamav/ole2_extract_images.c | 1 + 1 file changed, 1 insertion(+) create mode 100644 libclamav/ole2_extract_images.c diff --git a/libclamav/ole2_extract_images.c b/libclamav/ole2_extract_images.c new file mode 100644 index 0000000000..32644ac9f9 --- /dev/null +++ b/libclamav/ole2_extract_images.c @@ -0,0 +1 @@ +/*Need to add functions*/ From fdab56cf4fa7d25ac39eb915dea05b001e4fdda8 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 31 Jul 2024 13:41:45 -0700 Subject: [PATCH 53/96] blah --- libclamav/ole2_extract.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index c0725b26ea..3c855f709b 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -2870,6 +2870,10 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.xbat_start = ole2_endian_convert_32(hdr.xbat_start); hdr.xbat_count = ole2_endian_convert_32(hdr.xbat_count); + fprintf(stderr, "%s::%d::bat_count (sector count) and prop_start are different\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::bat_count = %d\n", __FUNCTION__, __LINE__, hdr.bat_count); +print_ole2_header(&hdr); + hdr.sbat_root_start = -1; hdr.bitset = cli_bitset_init(); From 5b06e7752b6e9f95f6d82ce9861dd13dbcd9e227 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 31 Jul 2024 14:00:00 -0700 Subject: [PATCH 54/96] blah --- libclamav/ole2_extract.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 3c855f709b..45e995f54d 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -720,7 +720,8 @@ static size_t get_stream_data_offset(ole2_header_t *hdr, const property_t *word_ if (word_block->size < MINISTREAM_CUTOFF_SIZE) { fib_offset = offset + sector_size * hdr->sbat_root_start; - fib_offset += (word_block->start_block * (1 << hdr->log2_small_block_size)); + //fib_offset += (word_block->start_block * (1 << hdr->log2_small_block_size)); + fib_offset += (sector * (1 << hdr->log2_small_block_size)); } else { fib_offset = offset + sector_size * sector; } From b688c29c372f4a46f3c54c2446c9ef0feda51a6f Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 1 Aug 2024 13:11:08 -0700 Subject: [PATCH 55/96] HARDCODED FOR MY PARTICULAR TEST CASE, NEED TO CLEAN UP --- libclamav/ole2_extract.c | 41 ++++++++++++++ libclamav/ole2_extract_images.h | 99 +++++++++++++++++++++++++++++++++ libclamav/xlm_extract.c | 2 + 3 files changed, 142 insertions(+) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 45e995f54d..447973d976 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -86,6 +86,11 @@ typedef struct ole2_header_tag { uint16_t log2_big_block_size __attribute__((packed)); /* usually 9 (2^9 = 512) */ uint32_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ + /* + * This is technically incorrect. log2_small_block_size should be a uint16_t, and reserved should + * be 6 bytes. This makes everything line up, but could potentially cause issues when switching byte order + * for log2_small_block_size. Consider changing. + */ int32_t reserved[2] __attribute__((packed)); int32_t bat_count __attribute__((packed)); @@ -410,6 +415,7 @@ print_ole2_property(property_t *property) static void print_ole2_header(ole2_header_t *hdr) { +#if 0 if (!hdr || !cli_debug_flag) { return; } @@ -437,6 +443,40 @@ print_ole2_header(ole2_header_t *hdr) cli_dbgmsg("XBat start:\t\t%d\n", hdr->xbat_start); cli_dbgmsg("XBat block count:\t%d\n", hdr->xbat_count); cli_dbgmsg("\n"); + + + +#endif + fprintf(stderr, "\n"); + fprintf(stderr, "Magic:\t\t\t0x%x%x%x%x%x%x%x%x\n", + hdr->magic[0], hdr->magic[1], hdr->magic[2], hdr->magic[3], + hdr->magic[4], hdr->magic[5], hdr->magic[6], hdr->magic[7]); + + fprintf(stderr, "CLSID:\t\t\t{%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x}\n", + hdr->clsid[0], hdr->clsid[1], hdr->clsid[2], hdr->clsid[3], + hdr->clsid[4], hdr->clsid[5], hdr->clsid[6], hdr->clsid[7], + hdr->clsid[8], hdr->clsid[9], hdr->clsid[10], hdr->clsid[11], + hdr->clsid[12], hdr->clsid[13], hdr->clsid[14], hdr->clsid[15]); + + fprintf(stderr, "Minor version:\t\t0x%x\n", hdr->minor_version); + fprintf(stderr, "DLL version:\t\t0x%x\n", hdr->dll_version); + fprintf(stderr, "Byte Order:\t\t%d\n", hdr->byte_order); + fprintf(stderr, "Big Block Size:\t%i\n", hdr->log2_big_block_size); + fprintf(stderr, "Small Block Size:\t%i\n", hdr->log2_small_block_size); + fprintf(stderr, "BAT count:\t\t%d\n", hdr->bat_count); + fprintf(stderr, "Prop start:\t\t%d\n", hdr->prop_start); + fprintf(stderr, "SBAT cutoff:\t\t%d\n", hdr->sbat_cutoff); + fprintf(stderr, "SBat start:\t\t%d\n", hdr->sbat_start); + fprintf(stderr, "SBat block count:\t%d\n", hdr->sbat_block_count); + fprintf(stderr, "XBat start:\t\t%d\n", hdr->xbat_start); + fprintf(stderr, "XBat block count:\t%d\n", hdr->xbat_count); + fprintf(stderr, "\n"); + + + + + + return; } @@ -636,6 +676,7 @@ static int ole2_cmp_name(const char *const name, uint32_t name_size, const char decoded[j] = ((unsigned char)name[i + 1]) << 4; decoded[j] += name[i]; } + //fprintf(stderr, "%s::%d::%s\n", __FUNCTION__, __LINE__, decoded); return strcasecmp(decoded, keyword); } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 59b5fa2526..ea656abd69 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -793,15 +793,106 @@ static void processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRec offset += fbse.cbName; if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { +fprintf(stderr, "%s::%d::Blip is embedded\n", __FUNCTION__, __LINE__); /* The BLIP is embedded in this record*/ processOfficeArtBlip(ctx, &(ptr[offset])); } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); +fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); processOfficeArtBlip(ctx, ptr); } +#if 0 + size_t i; + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (i = 0; i < 16; i++) { + fprintf(stderr, "%02x ", ptr[i + offset]); + } + fprintf(stderr, "\n"); +#endif + + + +#if 1 + fprintf(stderr, "%s::%d::before cpy\n", __FUNCTION__, __LINE__); + copy_OfficeArtRecordHeader(imageHeader, &(ptr[offset])); + offset += sizeof(OfficeArtRecordHeader); + + copy_OfficeArtFBSEKnown (&fbse, &(ptr[offset])); + offset += sizeof(OfficeArtFBSEKnown ); + recInst = getRecInst(imageHeader); + + fprintf(stderr, "%s::%d::recInst = %d\n", __FUNCTION__, __LINE__, recInst); + fprintf(stderr, "%s::%d::fbse.btWin32 = %d\n", __FUNCTION__, __LINE__, fbse.btWin32); + fprintf(stderr, "%s::%d::fbse.btMacOS = %d\n", __FUNCTION__, __LINE__, fbse.btMacOS); + + //here; + + + if ((recInst != fbse.btWin32) && (recInst != fbse.btMacOS)) { + cli_dbgmsg("ERROR Invalid recInst 0x%x\n", recInst); + return; + } + fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); + if (imageHeader->recType != 0xf007) { + cli_dbgmsg("ERROR Invalid recType 0x%x\n", imageHeader->recType); + return; + } + fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); + + offset += fbse.cbName; + + if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { +fprintf(stderr, "%s::%d::Blip is embedded\n", __FUNCTION__, __LINE__); + /* The BLIP is embedded in this record*/ + processOfficeArtBlip(ctx, &(ptr[offset])); + } else { + /* The BLIP is in the 'WordDocument' stream. */ + size_t size = fbse.size; + const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); +fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); + processOfficeArtBlip(ctx, ptr); + } + +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fprintf(stderr, "%s::%d::Looks like this might be IT!!!!\n", __FUNCTION__, __LINE__); + } static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { @@ -875,6 +966,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL * * */ #define OFFICE_ART_FBSE_REC_TYPE 0x2 + fprintf(stderr, "%s::%d::imageCnt = %d\n", __FUNCTION__, __LINE__, imageCnt); for (i = 0; i < imageCnt; i++) { OfficeArtRecordHeader imageHeader; copy_OfficeArtRecordHeader(&imageHeader, &(ptr[offset])); @@ -884,11 +976,17 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ + fprintf(stderr, "%s::%d::calling processOfficeArtFBSE\n", __FUNCTION__, __LINE__); processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[offset]), wordDocBlock); } else { + fprintf(stderr, "%s::%d::calling processOfficeArtBlip\n", __FUNCTION__, __LINE__); processOfficeArtBlip(ctx, &(ptr[offset])); } } + + //here; + + } @@ -943,6 +1041,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag /*Call Extract */ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); + /*TODO: Fix hardcoded 4k*/ ptr = fmap_need_off_once(hdr->map, offset, 4096); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); diff --git a/libclamav/xlm_extract.c b/libclamav/xlm_extract.c index 6a09d22985..9de9da2bfa 100644 --- a/libclamav/xlm_extract.c +++ b/libclamav/xlm_extract.c @@ -4626,6 +4626,8 @@ cl_error_t cli_extract_xlm_macros_and_images(const char *dir, cli_ctx *ctx, char unsigned char *drawinggroup = NULL; size_t drawinggroup_len = 0; + fprintf(stderr, "%s::%d::INHREE\n", __FUNCTION__, __LINE__); + biff8_opcode previous_biff8_opcode = 0x0; // Initialize to 0x0, which isn't even in our enum. // This variable will allow the OPC_CONTINUE record // to know which record it is continuing. From b15187fe1426b741f585507a90e80f3ba9ecf2a9 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 1 Aug 2024 13:25:41 -0700 Subject: [PATCH 56/96] blah --- libclamav/ole2_extract_images.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ea656abd69..6508c22c4c 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -1043,6 +1043,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); /*TODO: Fix hardcoded 4k*/ ptr = fmap_need_off_once(hdr->map, offset, 4096); + fprintf(stderr, "%s::%d::Fix hardcoded 4k\n", __FUNCTION__, __LINE__); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); goto done; From f2434e45f73f97ccd21c5f73ee6e3eb69c082d46 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 1 Aug 2024 14:34:45 -0700 Subject: [PATCH 57/96] git them both extracting --- libclamav/ole2_extract_images.h | 486 +++++++++++++++++--------------- 1 file changed, 257 insertions(+), 229 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 6508c22c4c..faee581a11 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -194,192 +194,192 @@ static void copy_FibRgFcLcb97(FibRgFcLcb97 * pHeader, const uint8_t *const ptr) memcpy(pHeader, ptr, sizeof(*pHeader)); -pHeader->fcStshfOrig = ole2_endian_convert_32(pHeader->fcStshfOrig); -pHeader->lcbStshfOrig = ole2_endian_convert_32(pHeader->lcbStshfOrig); -pHeader->fcStshf = ole2_endian_convert_32(pHeader->fcStshf); -pHeader->lcbStshf = ole2_endian_convert_32(pHeader->lcbStshf); -pHeader->fcPlcffndRef = ole2_endian_convert_32(pHeader->fcPlcffndRef); -pHeader->lcbPlcffndRef = ole2_endian_convert_32(pHeader->lcbPlcffndRef); -pHeader->fcPlcffndTxt = ole2_endian_convert_32(pHeader->fcPlcffndTxt); -pHeader->lcbPlcffndTxt = ole2_endian_convert_32(pHeader->lcbPlcffndTxt); -pHeader->fcPlcfandRef = ole2_endian_convert_32(pHeader->fcPlcfandRef); -pHeader->lcbPlcfandRef = ole2_endian_convert_32(pHeader->lcbPlcfandRef); -pHeader->fcPlcfandTxt = ole2_endian_convert_32(pHeader->fcPlcfandTxt); -pHeader->lcbPlcfandTxt = ole2_endian_convert_32(pHeader->lcbPlcfandTxt); -pHeader->fcPlcfSed = ole2_endian_convert_32(pHeader->fcPlcfSed); -pHeader->lcbPlcfSed = ole2_endian_convert_32(pHeader->lcbPlcfSed); -pHeader->fcPlcPad = ole2_endian_convert_32(pHeader->fcPlcPad); -pHeader->lcbPlcPad = ole2_endian_convert_32(pHeader->lcbPlcPad); -pHeader->fcPlcfPhe = ole2_endian_convert_32(pHeader->fcPlcfPhe); -pHeader->lcbPlcfPhe = ole2_endian_convert_32(pHeader->lcbPlcfPhe); -pHeader->fcSttbfGlsy = ole2_endian_convert_32(pHeader->fcSttbfGlsy); -pHeader->lcbSttbfGlsy = ole2_endian_convert_32(pHeader->lcbSttbfGlsy); -pHeader->fcPlcfGlsy = ole2_endian_convert_32(pHeader->fcPlcfGlsy); -pHeader->lcbPlcfGlsy = ole2_endian_convert_32(pHeader->lcbPlcfGlsy); -pHeader->fcPlcfHdd = ole2_endian_convert_32(pHeader->fcPlcfHdd); -pHeader->lcbPlcfHdd = ole2_endian_convert_32(pHeader->lcbPlcfHdd); -pHeader->fcPlcfBteChpx = ole2_endian_convert_32(pHeader->fcPlcfBteChpx); -pHeader->lcbPlcfBteChpx = ole2_endian_convert_32(pHeader->lcbPlcfBteChpx); -pHeader->fcPlcfBtePapx = ole2_endian_convert_32(pHeader->fcPlcfBtePapx); -pHeader->lcbPlcfBtePapx = ole2_endian_convert_32(pHeader->lcbPlcfBtePapx); -pHeader->fcPlcfSea = ole2_endian_convert_32(pHeader->fcPlcfSea); -pHeader->lcbPlcfSea = ole2_endian_convert_32(pHeader->lcbPlcfSea); -pHeader->fcSttbfFfn = ole2_endian_convert_32(pHeader->fcSttbfFfn); -pHeader->lcbSttbfFfn = ole2_endian_convert_32(pHeader->lcbSttbfFfn); -pHeader->fcPlcfFldMom = ole2_endian_convert_32(pHeader->fcPlcfFldMom); -pHeader->lcbPlcfFldMom = ole2_endian_convert_32(pHeader->lcbPlcfFldMom); -pHeader->fcPlcfFldHdr = ole2_endian_convert_32(pHeader->fcPlcfFldHdr); -pHeader->lcbPlcfFldHdr = ole2_endian_convert_32(pHeader->lcbPlcfFldHdr); -pHeader->fcPlcfFldFtn = ole2_endian_convert_32(pHeader->fcPlcfFldFtn); -pHeader->lcbPlcfFldFtn = ole2_endian_convert_32(pHeader->lcbPlcfFldFtn); -pHeader->fcPlcfFldAtn = ole2_endian_convert_32(pHeader->fcPlcfFldAtn); -pHeader->lcbPlcfFldAtn = ole2_endian_convert_32(pHeader->lcbPlcfFldAtn); -pHeader->fcPlcfFldMcr = ole2_endian_convert_32(pHeader->fcPlcfFldMcr); -pHeader->lcbPlcfFldMcr = ole2_endian_convert_32(pHeader->lcbPlcfFldMcr); -pHeader->fcSttbfBkmk = ole2_endian_convert_32(pHeader->fcSttbfBkmk); -pHeader->lcbSttbfBkmk = ole2_endian_convert_32(pHeader->lcbSttbfBkmk); -pHeader->fcPlcfBkf = ole2_endian_convert_32(pHeader->fcPlcfBkf); -pHeader->lcbPlcfBkf = ole2_endian_convert_32(pHeader->lcbPlcfBkf); -pHeader->fcPlcfBkl = ole2_endian_convert_32(pHeader->fcPlcfBkl); -pHeader->lcbPlcfBkl = ole2_endian_convert_32(pHeader->lcbPlcfBkl); -pHeader->fcCmds = ole2_endian_convert_32(pHeader->fcCmds); -pHeader->lcbCmds = ole2_endian_convert_32(pHeader->lcbCmds); -pHeader->fcUnused1 = ole2_endian_convert_32(pHeader->fcUnused1); -pHeader->lcbUnused1 = ole2_endian_convert_32(pHeader->lcbUnused1); -pHeader->fcSttbfMcr = ole2_endian_convert_32(pHeader->fcSttbfMcr); -pHeader->lcbSttbfMcr = ole2_endian_convert_32(pHeader->lcbSttbfMcr); -pHeader->fcPrDrvr = ole2_endian_convert_32(pHeader->fcPrDrvr); -pHeader->lcbPrDrvr = ole2_endian_convert_32(pHeader->lcbPrDrvr); -pHeader->fcPrEnvPort = ole2_endian_convert_32(pHeader->fcPrEnvPort); -pHeader->lcbPrEnvPort = ole2_endian_convert_32(pHeader->lcbPrEnvPort); -pHeader->fcPrEnvLand = ole2_endian_convert_32(pHeader->fcPrEnvLand); -pHeader->lcbPrEnvLand = ole2_endian_convert_32(pHeader->lcbPrEnvLand); -pHeader->fcWss = ole2_endian_convert_32(pHeader->fcWss); -pHeader->lcbWss = ole2_endian_convert_32(pHeader->lcbWss); -pHeader->fcDop = ole2_endian_convert_32(pHeader->fcDop); -pHeader->lcbDop = ole2_endian_convert_32(pHeader->lcbDop); -pHeader->fcSttbfAssoc = ole2_endian_convert_32(pHeader->fcSttbfAssoc); -pHeader->lcbSttbfAssoc = ole2_endian_convert_32(pHeader->lcbSttbfAssoc); -pHeader->fcClx = ole2_endian_convert_32(pHeader->fcClx); -pHeader->lcbClx = ole2_endian_convert_32(pHeader->lcbClx); -pHeader->fcPlcfPgdFtn = ole2_endian_convert_32(pHeader->fcPlcfPgdFtn); -pHeader->lcbPlcfPgdFtn = ole2_endian_convert_32(pHeader->lcbPlcfPgdFtn); -pHeader->fcAutosaveSource = ole2_endian_convert_32(pHeader->fcAutosaveSource); -pHeader->lcbAutosaveSource = ole2_endian_convert_32(pHeader->lcbAutosaveSource); -pHeader->fcGrpXstAtnOwners = ole2_endian_convert_32(pHeader->fcGrpXstAtnOwners); -pHeader->lcbGrpXstAtnOwners = ole2_endian_convert_32(pHeader->lcbGrpXstAtnOwners); -pHeader->fcSttbfAtnBkmk = ole2_endian_convert_32(pHeader->fcSttbfAtnBkmk); -pHeader->lcbSttbfAtnBkmk = ole2_endian_convert_32(pHeader->lcbSttbfAtnBkmk); -pHeader->fcUnused2 = ole2_endian_convert_32(pHeader->fcUnused2); -pHeader->lcbUnused2 = ole2_endian_convert_32(pHeader->lcbUnused2); -pHeader->fcUnused3 = ole2_endian_convert_32(pHeader->fcUnused3); -pHeader->lcbUnused3 = ole2_endian_convert_32(pHeader->lcbUnused3); -pHeader->fcPlcSpaMom = ole2_endian_convert_32(pHeader->fcPlcSpaMom); -pHeader->lcbPlcSpaMom = ole2_endian_convert_32(pHeader->lcbPlcSpaMom); -pHeader->fcPlcSpaHdr = ole2_endian_convert_32(pHeader->fcPlcSpaHdr); -pHeader->lcbPlcSpaHdr = ole2_endian_convert_32(pHeader->lcbPlcSpaHdr); -pHeader->fcPlcfAtnBkf = ole2_endian_convert_32(pHeader->fcPlcfAtnBkf); -pHeader->lcbPlcfAtnBkf = ole2_endian_convert_32(pHeader->lcbPlcfAtnBkf); -pHeader->fcPlcfAtnBkl = ole2_endian_convert_32(pHeader->fcPlcfAtnBkl); -pHeader->lcbPlcfAtnBkl = ole2_endian_convert_32(pHeader->lcbPlcfAtnBkl); -pHeader->fcPms = ole2_endian_convert_32(pHeader->fcPms); -pHeader->lcbPms = ole2_endian_convert_32(pHeader->lcbPms); -pHeader->fcFormFldSttbs = ole2_endian_convert_32(pHeader->fcFormFldSttbs); -pHeader->lcbFormFldSttbs = ole2_endian_convert_32(pHeader->lcbFormFldSttbs); -pHeader->fcPlcfendRef = ole2_endian_convert_32(pHeader->fcPlcfendRef); -pHeader->lcbPlcfendRef = ole2_endian_convert_32(pHeader->lcbPlcfendRef); -pHeader->fcPlcfendTxt = ole2_endian_convert_32(pHeader->fcPlcfendTxt); -pHeader->lcbPlcfendTxt = ole2_endian_convert_32(pHeader->lcbPlcfendTxt); -pHeader->fcPlcfFldEdn = ole2_endian_convert_32(pHeader->fcPlcfFldEdn); -pHeader->lcbPlcfFldEdn = ole2_endian_convert_32(pHeader->lcbPlcfFldEdn); -pHeader->fcUnused4 = ole2_endian_convert_32(pHeader->fcUnused4); -pHeader->lcbUnused4 = ole2_endian_convert_32(pHeader->lcbUnused4); -pHeader->fcDggInfo = ole2_endian_convert_32(pHeader->fcDggInfo); -pHeader->lcbDggInfo = ole2_endian_convert_32(pHeader->lcbDggInfo); -pHeader->fcSttbfRMark = ole2_endian_convert_32(pHeader->fcSttbfRMark); -pHeader->lcbSttbfRMark = ole2_endian_convert_32(pHeader->lcbSttbfRMark); -pHeader->fcSttbfCaption = ole2_endian_convert_32(pHeader->fcSttbfCaption); -pHeader->lcbSttbfCaption = ole2_endian_convert_32(pHeader->lcbSttbfCaption); -pHeader->fcSttbfAutoCaption = ole2_endian_convert_32(pHeader->fcSttbfAutoCaption); -pHeader->lcbSttbfAutoCaption = ole2_endian_convert_32(pHeader->lcbSttbfAutoCaption); -pHeader->fcPlcfWkb = ole2_endian_convert_32(pHeader->fcPlcfWkb); -pHeader->lcbPlcfWkb = ole2_endian_convert_32(pHeader->lcbPlcfWkb); -pHeader->fcPlcfSpl = ole2_endian_convert_32(pHeader->fcPlcfSpl); -pHeader->lcbPlcfSpl = ole2_endian_convert_32(pHeader->lcbPlcfSpl); -pHeader->fcPlcftxbxTxt = ole2_endian_convert_32(pHeader->fcPlcftxbxTxt); -pHeader->lcbPlcftxbxTxt = ole2_endian_convert_32(pHeader->lcbPlcftxbxTxt); -pHeader->fcPlcfFldTxbx = ole2_endian_convert_32(pHeader->fcPlcfFldTxbx); -pHeader->lcbPlcfFldTxbx = ole2_endian_convert_32(pHeader->lcbPlcfFldTxbx); -pHeader->fcPlcfHdrtxbxTxt = ole2_endian_convert_32(pHeader->fcPlcfHdrtxbxTxt); -pHeader->lcbPlcfHdrtxbxTxt = ole2_endian_convert_32(pHeader->lcbPlcfHdrtxbxTxt); -pHeader->fcPlcffldHdrTxbx = ole2_endian_convert_32(pHeader->fcPlcffldHdrTxbx); -pHeader->lcbPlcffldHdrTxbx = ole2_endian_convert_32(pHeader->lcbPlcffldHdrTxbx); -pHeader->fcStwUser = ole2_endian_convert_32(pHeader->fcStwUser); -pHeader->lcbStwUser = ole2_endian_convert_32(pHeader->lcbStwUser); -pHeader->fcSttbTtmbd = ole2_endian_convert_32(pHeader->fcSttbTtmbd); -pHeader->lcbSttbTtmbd = ole2_endian_convert_32(pHeader->lcbSttbTtmbd); -pHeader->fcCookieData = ole2_endian_convert_32(pHeader->fcCookieData); -pHeader->lcbCookieData = ole2_endian_convert_32(pHeader->lcbCookieData); -pHeader->fcPgdMotherOldOld = ole2_endian_convert_32(pHeader->fcPgdMotherOldOld); -pHeader->lcbPgdMotherOldOld = ole2_endian_convert_32(pHeader->lcbPgdMotherOldOld); -pHeader->fcBkdMotherOldOld = ole2_endian_convert_32(pHeader->fcBkdMotherOldOld); -pHeader->lcbBkdMotherOldOld = ole2_endian_convert_32(pHeader->lcbBkdMotherOldOld); -pHeader->fcPgdFtnOldOld = ole2_endian_convert_32(pHeader->fcPgdFtnOldOld); -pHeader->lcbPgdFtnOldOld = ole2_endian_convert_32(pHeader->lcbPgdFtnOldOld); -pHeader->fcBkdFtnOldOld = ole2_endian_convert_32(pHeader->fcBkdFtnOldOld); -pHeader->lcbBkdFtnOldOld = ole2_endian_convert_32(pHeader->lcbBkdFtnOldOld); -pHeader->fcPgdEdnOldOld = ole2_endian_convert_32(pHeader->fcPgdEdnOldOld); -pHeader->lcbPgdEdnOldOld = ole2_endian_convert_32(pHeader->lcbPgdEdnOldOld); -pHeader->fcBkdEdnOldOld = ole2_endian_convert_32(pHeader->fcBkdEdnOldOld); -pHeader->lcbBkdEdnOldOld = ole2_endian_convert_32(pHeader->lcbBkdEdnOldOld); -pHeader->fcSttbfIntlFld = ole2_endian_convert_32(pHeader->fcSttbfIntlFld); -pHeader->lcbSttbfIntlFld = ole2_endian_convert_32(pHeader->lcbSttbfIntlFld); -pHeader->fcRouteSlip = ole2_endian_convert_32(pHeader->fcRouteSlip); -pHeader->lcbRouteSlip = ole2_endian_convert_32(pHeader->lcbRouteSlip); -pHeader->fcSttbSavedBy = ole2_endian_convert_32(pHeader->fcSttbSavedBy); -pHeader->lcbSttbSavedBy = ole2_endian_convert_32(pHeader->lcbSttbSavedBy); -pHeader->fcSttbFnm = ole2_endian_convert_32(pHeader->fcSttbFnm); -pHeader->lcbSttbFnm = ole2_endian_convert_32(pHeader->lcbSttbFnm); -pHeader->fcPlfLst = ole2_endian_convert_32(pHeader->fcPlfLst); -pHeader->lcbPlfLst = ole2_endian_convert_32(pHeader->lcbPlfLst); -pHeader->fcPlfLfo = ole2_endian_convert_32(pHeader->fcPlfLfo); -pHeader->lcbPlfLfo = ole2_endian_convert_32(pHeader->lcbPlfLfo); -pHeader->fcPlcfTxbxBkd = ole2_endian_convert_32(pHeader->fcPlcfTxbxBkd); -pHeader->lcbPlcfTxbxBkd = ole2_endian_convert_32(pHeader->lcbPlcfTxbxBkd); -pHeader->fcPlcfTxbxHdrBkd = ole2_endian_convert_32(pHeader->fcPlcfTxbxHdrBkd); -pHeader->lcbPlcfTxbxHdrBkd = ole2_endian_convert_32(pHeader->lcbPlcfTxbxHdrBkd); -pHeader->fcDocUndoWord9 = ole2_endian_convert_32(pHeader->fcDocUndoWord9); -pHeader->lcbDocUndoWord9 = ole2_endian_convert_32(pHeader->lcbDocUndoWord9); -pHeader->fcRgbUse = ole2_endian_convert_32(pHeader->fcRgbUse); -pHeader->lcbRgbUse = ole2_endian_convert_32(pHeader->lcbRgbUse); -pHeader->fcUsp = ole2_endian_convert_32(pHeader->fcUsp); -pHeader->lcbUsp = ole2_endian_convert_32(pHeader->lcbUsp); -pHeader->fcUskf = ole2_endian_convert_32(pHeader->fcUskf); -pHeader->lcbUskf = ole2_endian_convert_32(pHeader->lcbUskf); -pHeader->fcPlcupcRgbUse = ole2_endian_convert_32(pHeader->fcPlcupcRgbUse); -pHeader->lcbPlcupcRgbUse = ole2_endian_convert_32(pHeader->lcbPlcupcRgbUse); -pHeader->fcPlcupcUsp = ole2_endian_convert_32(pHeader->fcPlcupcUsp); -pHeader->lcbPlcupcUsp = ole2_endian_convert_32(pHeader->lcbPlcupcUsp); -pHeader->fcSttbGlsyStyle = ole2_endian_convert_32(pHeader->fcSttbGlsyStyle); -pHeader->lcbSttbGlsyStyle = ole2_endian_convert_32(pHeader->lcbSttbGlsyStyle); -pHeader->fcPlgosl = ole2_endian_convert_32(pHeader->fcPlgosl); -pHeader->lcbPlgosl = ole2_endian_convert_32(pHeader->lcbPlgosl); -pHeader->fcPlcocx = ole2_endian_convert_32(pHeader->fcPlcocx); -pHeader->lcbPlcocx = ole2_endian_convert_32(pHeader->lcbPlcocx); -pHeader->fcPlcfBteLvc = ole2_endian_convert_32(pHeader->fcPlcfBteLvc); -pHeader->lcbPlcfBteLvc = ole2_endian_convert_32(pHeader->lcbPlcfBteLvc); -pHeader->dwLowDateTime = ole2_endian_convert_32(pHeader->dwLowDateTime); -pHeader->dwHighDateTime = ole2_endian_convert_32(pHeader->dwHighDateTime); -pHeader->fcPlcfLvcPre10 = ole2_endian_convert_32(pHeader->fcPlcfLvcPre10); -pHeader->lcbPlcfLvcPre10 = ole2_endian_convert_32(pHeader->lcbPlcfLvcPre10); -pHeader->fcPlcfAsumy = ole2_endian_convert_32(pHeader->fcPlcfAsumy); -pHeader->lcbPlcfAsumy = ole2_endian_convert_32(pHeader->lcbPlcfAsumy); -pHeader->fcPlcfGram = ole2_endian_convert_32(pHeader->fcPlcfGram); -pHeader->lcbPlcfGram = ole2_endian_convert_32(pHeader->lcbPlcfGram); -pHeader->fcSttbListNames = ole2_endian_convert_32(pHeader->fcSttbListNames); -pHeader->lcbSttbListNames = ole2_endian_convert_32(pHeader->lcbSttbListNames); -pHeader->fcSttbfUssr = ole2_endian_convert_32(pHeader->fcSttbfUssr); -pHeader->lcbSttbfUssr = ole2_endian_convert_32(pHeader->lcbSttbfUssr); + pHeader->fcStshfOrig = ole2_endian_convert_32(pHeader->fcStshfOrig); + pHeader->lcbStshfOrig = ole2_endian_convert_32(pHeader->lcbStshfOrig); + pHeader->fcStshf = ole2_endian_convert_32(pHeader->fcStshf); + pHeader->lcbStshf = ole2_endian_convert_32(pHeader->lcbStshf); + pHeader->fcPlcffndRef = ole2_endian_convert_32(pHeader->fcPlcffndRef); + pHeader->lcbPlcffndRef = ole2_endian_convert_32(pHeader->lcbPlcffndRef); + pHeader->fcPlcffndTxt = ole2_endian_convert_32(pHeader->fcPlcffndTxt); + pHeader->lcbPlcffndTxt = ole2_endian_convert_32(pHeader->lcbPlcffndTxt); + pHeader->fcPlcfandRef = ole2_endian_convert_32(pHeader->fcPlcfandRef); + pHeader->lcbPlcfandRef = ole2_endian_convert_32(pHeader->lcbPlcfandRef); + pHeader->fcPlcfandTxt = ole2_endian_convert_32(pHeader->fcPlcfandTxt); + pHeader->lcbPlcfandTxt = ole2_endian_convert_32(pHeader->lcbPlcfandTxt); + pHeader->fcPlcfSed = ole2_endian_convert_32(pHeader->fcPlcfSed); + pHeader->lcbPlcfSed = ole2_endian_convert_32(pHeader->lcbPlcfSed); + pHeader->fcPlcPad = ole2_endian_convert_32(pHeader->fcPlcPad); + pHeader->lcbPlcPad = ole2_endian_convert_32(pHeader->lcbPlcPad); + pHeader->fcPlcfPhe = ole2_endian_convert_32(pHeader->fcPlcfPhe); + pHeader->lcbPlcfPhe = ole2_endian_convert_32(pHeader->lcbPlcfPhe); + pHeader->fcSttbfGlsy = ole2_endian_convert_32(pHeader->fcSttbfGlsy); + pHeader->lcbSttbfGlsy = ole2_endian_convert_32(pHeader->lcbSttbfGlsy); + pHeader->fcPlcfGlsy = ole2_endian_convert_32(pHeader->fcPlcfGlsy); + pHeader->lcbPlcfGlsy = ole2_endian_convert_32(pHeader->lcbPlcfGlsy); + pHeader->fcPlcfHdd = ole2_endian_convert_32(pHeader->fcPlcfHdd); + pHeader->lcbPlcfHdd = ole2_endian_convert_32(pHeader->lcbPlcfHdd); + pHeader->fcPlcfBteChpx = ole2_endian_convert_32(pHeader->fcPlcfBteChpx); + pHeader->lcbPlcfBteChpx = ole2_endian_convert_32(pHeader->lcbPlcfBteChpx); + pHeader->fcPlcfBtePapx = ole2_endian_convert_32(pHeader->fcPlcfBtePapx); + pHeader->lcbPlcfBtePapx = ole2_endian_convert_32(pHeader->lcbPlcfBtePapx); + pHeader->fcPlcfSea = ole2_endian_convert_32(pHeader->fcPlcfSea); + pHeader->lcbPlcfSea = ole2_endian_convert_32(pHeader->lcbPlcfSea); + pHeader->fcSttbfFfn = ole2_endian_convert_32(pHeader->fcSttbfFfn); + pHeader->lcbSttbfFfn = ole2_endian_convert_32(pHeader->lcbSttbfFfn); + pHeader->fcPlcfFldMom = ole2_endian_convert_32(pHeader->fcPlcfFldMom); + pHeader->lcbPlcfFldMom = ole2_endian_convert_32(pHeader->lcbPlcfFldMom); + pHeader->fcPlcfFldHdr = ole2_endian_convert_32(pHeader->fcPlcfFldHdr); + pHeader->lcbPlcfFldHdr = ole2_endian_convert_32(pHeader->lcbPlcfFldHdr); + pHeader->fcPlcfFldFtn = ole2_endian_convert_32(pHeader->fcPlcfFldFtn); + pHeader->lcbPlcfFldFtn = ole2_endian_convert_32(pHeader->lcbPlcfFldFtn); + pHeader->fcPlcfFldAtn = ole2_endian_convert_32(pHeader->fcPlcfFldAtn); + pHeader->lcbPlcfFldAtn = ole2_endian_convert_32(pHeader->lcbPlcfFldAtn); + pHeader->fcPlcfFldMcr = ole2_endian_convert_32(pHeader->fcPlcfFldMcr); + pHeader->lcbPlcfFldMcr = ole2_endian_convert_32(pHeader->lcbPlcfFldMcr); + pHeader->fcSttbfBkmk = ole2_endian_convert_32(pHeader->fcSttbfBkmk); + pHeader->lcbSttbfBkmk = ole2_endian_convert_32(pHeader->lcbSttbfBkmk); + pHeader->fcPlcfBkf = ole2_endian_convert_32(pHeader->fcPlcfBkf); + pHeader->lcbPlcfBkf = ole2_endian_convert_32(pHeader->lcbPlcfBkf); + pHeader->fcPlcfBkl = ole2_endian_convert_32(pHeader->fcPlcfBkl); + pHeader->lcbPlcfBkl = ole2_endian_convert_32(pHeader->lcbPlcfBkl); + pHeader->fcCmds = ole2_endian_convert_32(pHeader->fcCmds); + pHeader->lcbCmds = ole2_endian_convert_32(pHeader->lcbCmds); + pHeader->fcUnused1 = ole2_endian_convert_32(pHeader->fcUnused1); + pHeader->lcbUnused1 = ole2_endian_convert_32(pHeader->lcbUnused1); + pHeader->fcSttbfMcr = ole2_endian_convert_32(pHeader->fcSttbfMcr); + pHeader->lcbSttbfMcr = ole2_endian_convert_32(pHeader->lcbSttbfMcr); + pHeader->fcPrDrvr = ole2_endian_convert_32(pHeader->fcPrDrvr); + pHeader->lcbPrDrvr = ole2_endian_convert_32(pHeader->lcbPrDrvr); + pHeader->fcPrEnvPort = ole2_endian_convert_32(pHeader->fcPrEnvPort); + pHeader->lcbPrEnvPort = ole2_endian_convert_32(pHeader->lcbPrEnvPort); + pHeader->fcPrEnvLand = ole2_endian_convert_32(pHeader->fcPrEnvLand); + pHeader->lcbPrEnvLand = ole2_endian_convert_32(pHeader->lcbPrEnvLand); + pHeader->fcWss = ole2_endian_convert_32(pHeader->fcWss); + pHeader->lcbWss = ole2_endian_convert_32(pHeader->lcbWss); + pHeader->fcDop = ole2_endian_convert_32(pHeader->fcDop); + pHeader->lcbDop = ole2_endian_convert_32(pHeader->lcbDop); + pHeader->fcSttbfAssoc = ole2_endian_convert_32(pHeader->fcSttbfAssoc); + pHeader->lcbSttbfAssoc = ole2_endian_convert_32(pHeader->lcbSttbfAssoc); + pHeader->fcClx = ole2_endian_convert_32(pHeader->fcClx); + pHeader->lcbClx = ole2_endian_convert_32(pHeader->lcbClx); + pHeader->fcPlcfPgdFtn = ole2_endian_convert_32(pHeader->fcPlcfPgdFtn); + pHeader->lcbPlcfPgdFtn = ole2_endian_convert_32(pHeader->lcbPlcfPgdFtn); + pHeader->fcAutosaveSource = ole2_endian_convert_32(pHeader->fcAutosaveSource); + pHeader->lcbAutosaveSource = ole2_endian_convert_32(pHeader->lcbAutosaveSource); + pHeader->fcGrpXstAtnOwners = ole2_endian_convert_32(pHeader->fcGrpXstAtnOwners); + pHeader->lcbGrpXstAtnOwners = ole2_endian_convert_32(pHeader->lcbGrpXstAtnOwners); + pHeader->fcSttbfAtnBkmk = ole2_endian_convert_32(pHeader->fcSttbfAtnBkmk); + pHeader->lcbSttbfAtnBkmk = ole2_endian_convert_32(pHeader->lcbSttbfAtnBkmk); + pHeader->fcUnused2 = ole2_endian_convert_32(pHeader->fcUnused2); + pHeader->lcbUnused2 = ole2_endian_convert_32(pHeader->lcbUnused2); + pHeader->fcUnused3 = ole2_endian_convert_32(pHeader->fcUnused3); + pHeader->lcbUnused3 = ole2_endian_convert_32(pHeader->lcbUnused3); + pHeader->fcPlcSpaMom = ole2_endian_convert_32(pHeader->fcPlcSpaMom); + pHeader->lcbPlcSpaMom = ole2_endian_convert_32(pHeader->lcbPlcSpaMom); + pHeader->fcPlcSpaHdr = ole2_endian_convert_32(pHeader->fcPlcSpaHdr); + pHeader->lcbPlcSpaHdr = ole2_endian_convert_32(pHeader->lcbPlcSpaHdr); + pHeader->fcPlcfAtnBkf = ole2_endian_convert_32(pHeader->fcPlcfAtnBkf); + pHeader->lcbPlcfAtnBkf = ole2_endian_convert_32(pHeader->lcbPlcfAtnBkf); + pHeader->fcPlcfAtnBkl = ole2_endian_convert_32(pHeader->fcPlcfAtnBkl); + pHeader->lcbPlcfAtnBkl = ole2_endian_convert_32(pHeader->lcbPlcfAtnBkl); + pHeader->fcPms = ole2_endian_convert_32(pHeader->fcPms); + pHeader->lcbPms = ole2_endian_convert_32(pHeader->lcbPms); + pHeader->fcFormFldSttbs = ole2_endian_convert_32(pHeader->fcFormFldSttbs); + pHeader->lcbFormFldSttbs = ole2_endian_convert_32(pHeader->lcbFormFldSttbs); + pHeader->fcPlcfendRef = ole2_endian_convert_32(pHeader->fcPlcfendRef); + pHeader->lcbPlcfendRef = ole2_endian_convert_32(pHeader->lcbPlcfendRef); + pHeader->fcPlcfendTxt = ole2_endian_convert_32(pHeader->fcPlcfendTxt); + pHeader->lcbPlcfendTxt = ole2_endian_convert_32(pHeader->lcbPlcfendTxt); + pHeader->fcPlcfFldEdn = ole2_endian_convert_32(pHeader->fcPlcfFldEdn); + pHeader->lcbPlcfFldEdn = ole2_endian_convert_32(pHeader->lcbPlcfFldEdn); + pHeader->fcUnused4 = ole2_endian_convert_32(pHeader->fcUnused4); + pHeader->lcbUnused4 = ole2_endian_convert_32(pHeader->lcbUnused4); + pHeader->fcDggInfo = ole2_endian_convert_32(pHeader->fcDggInfo); + pHeader->lcbDggInfo = ole2_endian_convert_32(pHeader->lcbDggInfo); + pHeader->fcSttbfRMark = ole2_endian_convert_32(pHeader->fcSttbfRMark); + pHeader->lcbSttbfRMark = ole2_endian_convert_32(pHeader->lcbSttbfRMark); + pHeader->fcSttbfCaption = ole2_endian_convert_32(pHeader->fcSttbfCaption); + pHeader->lcbSttbfCaption = ole2_endian_convert_32(pHeader->lcbSttbfCaption); + pHeader->fcSttbfAutoCaption = ole2_endian_convert_32(pHeader->fcSttbfAutoCaption); + pHeader->lcbSttbfAutoCaption = ole2_endian_convert_32(pHeader->lcbSttbfAutoCaption); + pHeader->fcPlcfWkb = ole2_endian_convert_32(pHeader->fcPlcfWkb); + pHeader->lcbPlcfWkb = ole2_endian_convert_32(pHeader->lcbPlcfWkb); + pHeader->fcPlcfSpl = ole2_endian_convert_32(pHeader->fcPlcfSpl); + pHeader->lcbPlcfSpl = ole2_endian_convert_32(pHeader->lcbPlcfSpl); + pHeader->fcPlcftxbxTxt = ole2_endian_convert_32(pHeader->fcPlcftxbxTxt); + pHeader->lcbPlcftxbxTxt = ole2_endian_convert_32(pHeader->lcbPlcftxbxTxt); + pHeader->fcPlcfFldTxbx = ole2_endian_convert_32(pHeader->fcPlcfFldTxbx); + pHeader->lcbPlcfFldTxbx = ole2_endian_convert_32(pHeader->lcbPlcfFldTxbx); + pHeader->fcPlcfHdrtxbxTxt = ole2_endian_convert_32(pHeader->fcPlcfHdrtxbxTxt); + pHeader->lcbPlcfHdrtxbxTxt = ole2_endian_convert_32(pHeader->lcbPlcfHdrtxbxTxt); + pHeader->fcPlcffldHdrTxbx = ole2_endian_convert_32(pHeader->fcPlcffldHdrTxbx); + pHeader->lcbPlcffldHdrTxbx = ole2_endian_convert_32(pHeader->lcbPlcffldHdrTxbx); + pHeader->fcStwUser = ole2_endian_convert_32(pHeader->fcStwUser); + pHeader->lcbStwUser = ole2_endian_convert_32(pHeader->lcbStwUser); + pHeader->fcSttbTtmbd = ole2_endian_convert_32(pHeader->fcSttbTtmbd); + pHeader->lcbSttbTtmbd = ole2_endian_convert_32(pHeader->lcbSttbTtmbd); + pHeader->fcCookieData = ole2_endian_convert_32(pHeader->fcCookieData); + pHeader->lcbCookieData = ole2_endian_convert_32(pHeader->lcbCookieData); + pHeader->fcPgdMotherOldOld = ole2_endian_convert_32(pHeader->fcPgdMotherOldOld); + pHeader->lcbPgdMotherOldOld = ole2_endian_convert_32(pHeader->lcbPgdMotherOldOld); + pHeader->fcBkdMotherOldOld = ole2_endian_convert_32(pHeader->fcBkdMotherOldOld); + pHeader->lcbBkdMotherOldOld = ole2_endian_convert_32(pHeader->lcbBkdMotherOldOld); + pHeader->fcPgdFtnOldOld = ole2_endian_convert_32(pHeader->fcPgdFtnOldOld); + pHeader->lcbPgdFtnOldOld = ole2_endian_convert_32(pHeader->lcbPgdFtnOldOld); + pHeader->fcBkdFtnOldOld = ole2_endian_convert_32(pHeader->fcBkdFtnOldOld); + pHeader->lcbBkdFtnOldOld = ole2_endian_convert_32(pHeader->lcbBkdFtnOldOld); + pHeader->fcPgdEdnOldOld = ole2_endian_convert_32(pHeader->fcPgdEdnOldOld); + pHeader->lcbPgdEdnOldOld = ole2_endian_convert_32(pHeader->lcbPgdEdnOldOld); + pHeader->fcBkdEdnOldOld = ole2_endian_convert_32(pHeader->fcBkdEdnOldOld); + pHeader->lcbBkdEdnOldOld = ole2_endian_convert_32(pHeader->lcbBkdEdnOldOld); + pHeader->fcSttbfIntlFld = ole2_endian_convert_32(pHeader->fcSttbfIntlFld); + pHeader->lcbSttbfIntlFld = ole2_endian_convert_32(pHeader->lcbSttbfIntlFld); + pHeader->fcRouteSlip = ole2_endian_convert_32(pHeader->fcRouteSlip); + pHeader->lcbRouteSlip = ole2_endian_convert_32(pHeader->lcbRouteSlip); + pHeader->fcSttbSavedBy = ole2_endian_convert_32(pHeader->fcSttbSavedBy); + pHeader->lcbSttbSavedBy = ole2_endian_convert_32(pHeader->lcbSttbSavedBy); + pHeader->fcSttbFnm = ole2_endian_convert_32(pHeader->fcSttbFnm); + pHeader->lcbSttbFnm = ole2_endian_convert_32(pHeader->lcbSttbFnm); + pHeader->fcPlfLst = ole2_endian_convert_32(pHeader->fcPlfLst); + pHeader->lcbPlfLst = ole2_endian_convert_32(pHeader->lcbPlfLst); + pHeader->fcPlfLfo = ole2_endian_convert_32(pHeader->fcPlfLfo); + pHeader->lcbPlfLfo = ole2_endian_convert_32(pHeader->lcbPlfLfo); + pHeader->fcPlcfTxbxBkd = ole2_endian_convert_32(pHeader->fcPlcfTxbxBkd); + pHeader->lcbPlcfTxbxBkd = ole2_endian_convert_32(pHeader->lcbPlcfTxbxBkd); + pHeader->fcPlcfTxbxHdrBkd = ole2_endian_convert_32(pHeader->fcPlcfTxbxHdrBkd); + pHeader->lcbPlcfTxbxHdrBkd = ole2_endian_convert_32(pHeader->lcbPlcfTxbxHdrBkd); + pHeader->fcDocUndoWord9 = ole2_endian_convert_32(pHeader->fcDocUndoWord9); + pHeader->lcbDocUndoWord9 = ole2_endian_convert_32(pHeader->lcbDocUndoWord9); + pHeader->fcRgbUse = ole2_endian_convert_32(pHeader->fcRgbUse); + pHeader->lcbRgbUse = ole2_endian_convert_32(pHeader->lcbRgbUse); + pHeader->fcUsp = ole2_endian_convert_32(pHeader->fcUsp); + pHeader->lcbUsp = ole2_endian_convert_32(pHeader->lcbUsp); + pHeader->fcUskf = ole2_endian_convert_32(pHeader->fcUskf); + pHeader->lcbUskf = ole2_endian_convert_32(pHeader->lcbUskf); + pHeader->fcPlcupcRgbUse = ole2_endian_convert_32(pHeader->fcPlcupcRgbUse); + pHeader->lcbPlcupcRgbUse = ole2_endian_convert_32(pHeader->lcbPlcupcRgbUse); + pHeader->fcPlcupcUsp = ole2_endian_convert_32(pHeader->fcPlcupcUsp); + pHeader->lcbPlcupcUsp = ole2_endian_convert_32(pHeader->lcbPlcupcUsp); + pHeader->fcSttbGlsyStyle = ole2_endian_convert_32(pHeader->fcSttbGlsyStyle); + pHeader->lcbSttbGlsyStyle = ole2_endian_convert_32(pHeader->lcbSttbGlsyStyle); + pHeader->fcPlgosl = ole2_endian_convert_32(pHeader->fcPlgosl); + pHeader->lcbPlgosl = ole2_endian_convert_32(pHeader->lcbPlgosl); + pHeader->fcPlcocx = ole2_endian_convert_32(pHeader->fcPlcocx); + pHeader->lcbPlcocx = ole2_endian_convert_32(pHeader->lcbPlcocx); + pHeader->fcPlcfBteLvc = ole2_endian_convert_32(pHeader->fcPlcfBteLvc); + pHeader->lcbPlcfBteLvc = ole2_endian_convert_32(pHeader->lcbPlcfBteLvc); + pHeader->dwLowDateTime = ole2_endian_convert_32(pHeader->dwLowDateTime); + pHeader->dwHighDateTime = ole2_endian_convert_32(pHeader->dwHighDateTime); + pHeader->fcPlcfLvcPre10 = ole2_endian_convert_32(pHeader->fcPlcfLvcPre10); + pHeader->lcbPlcfLvcPre10 = ole2_endian_convert_32(pHeader->lcbPlcfLvcPre10); + pHeader->fcPlcfAsumy = ole2_endian_convert_32(pHeader->fcPlcfAsumy); + pHeader->lcbPlcfAsumy = ole2_endian_convert_32(pHeader->lcbPlcfAsumy); + pHeader->fcPlcfGram = ole2_endian_convert_32(pHeader->fcPlcfGram); + pHeader->lcbPlcfGram = ole2_endian_convert_32(pHeader->lcbPlcfGram); + pHeader->fcSttbListNames = ole2_endian_convert_32(pHeader->fcSttbListNames); + pHeader->lcbSttbListNames = ole2_endian_convert_32(pHeader->lcbSttbListNames); + pHeader->fcSttbfUssr = ole2_endian_convert_32(pHeader->fcSttbfUssr); + pHeader->lcbSttbfUssr = ole2_endian_convert_32(pHeader->lcbSttbfUssr); } @@ -717,7 +717,7 @@ static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, processOfficeArtBlipGeneric(ctx, rh, ptr, 0x6e4, 0x6e5, 1) ; } -static void processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ +static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ size_t offset = 0; OfficeArtRecordHeader rh; @@ -727,7 +727,7 @@ static void processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ uint8_t recVer = getRecVer(&rh); if (0 != recVer) { cli_dbgmsg("ERROR Invalid recVer 0x%x\n", recVer); - return; + goto done; } #define RECTYPE_OFFICE_ART_BLIP_EMF 0xf01a @@ -767,14 +767,18 @@ static void processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ cli_dbgmsg("ERROR Invalid recType 0x%x\n", rh.recType); break; } + +done: + return (sizeof(rh) + rh.recLen); } /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -static void processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { +static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { OfficeArtFBSEKnown fbse; + // imageCnt = blipStoreRecordHeader.recLen/(sizeof(OfficeArtFBSEKnown) + sizeof(OfficeArtRecordHeader)); uint32_t offset = sizeof(OfficeArtRecordHeader); uint16_t recInst = getRecInst(imageHeader); @@ -783,26 +787,31 @@ static void processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRec if ((recInst != fbse.btWin32) && (recInst != fbse.btMacOS)) { cli_dbgmsg("ERROR Invalid recInst 0x%x\n", recInst); - return; + return offset; } if (imageHeader->recType != 0xf007) { cli_dbgmsg("ERROR Invalid recType 0x%x\n", imageHeader->recType); - return; + return offset; } offset += fbse.cbName; if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { -fprintf(stderr, "%s::%d::Blip is embedded\n", __FUNCTION__, __LINE__); /* The BLIP is embedded in this record*/ processOfficeArtBlip(ctx, &(ptr[offset])); + offset += fbse.size; } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); -fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); processOfficeArtBlip(ctx, ptr); + + /* I don't *think* I need to add anything to the offset here, because the actual data is not here. + * The data is in a different stream + */ } + return offset; +#if 0 #if 0 size_t i; @@ -818,6 +827,10 @@ fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", _ #if 1 fprintf(stderr, "%s::%d::before cpy\n", __FUNCTION__, __LINE__); copy_OfficeArtRecordHeader(imageHeader, &(ptr[offset])); + + uint8_t recVer = getRecVer(imageHeader); + fprintf(stderr, "%s::%d::recVer = %d\n", __FUNCTION__, __LINE__, recVer); + offset += sizeof(OfficeArtRecordHeader); copy_OfficeArtFBSEKnown (&fbse, &(ptr[offset])); @@ -845,14 +858,14 @@ fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", _ offset += fbse.cbName; if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { -fprintf(stderr, "%s::%d::Blip is embedded\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Blip is embedded\n", __FUNCTION__, __LINE__); /* The BLIP is embedded in this record*/ processOfficeArtBlip(ctx, &(ptr[offset])); } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); -fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); + fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); processOfficeArtBlip(ctx, ptr); } @@ -892,7 +905,7 @@ fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", _ fprintf(stderr, "%s::%d::Looks like this might be IT!!!!\n", __FUNCTION__, __LINE__); - +#endif } static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { @@ -931,9 +944,9 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL copy_OfficeArtFDGG(&fdgg, &(ptr[offset])); offset += sizeof(OfficeArtFDGG); -/* OfficeArtIDCL is not used in parsing images, only drawings. If details are needed, they are - * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2335d2f8-109b-4cd6-ac8d-40b1237283f3 - * */ + /* OfficeArtIDCL is not used in parsing images, only drawings. If details are needed, they are + * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2335d2f8-109b-4cd6-ac8d-40b1237283f3 + * */ #define OFFICE_ART_IDCL_LEN 8 offset += (OFFICE_ART_IDCL_LEN * (fdgg.cidcl-1)); @@ -955,8 +968,17 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL return; } + fprintf(stderr, "%s::%d::record len = %d, sizeof = %ld\n", __FUNCTION__, __LINE__, blipStoreRecordHeader.recLen, sizeof(OfficeArtFBSEKnown)); + /* + * imageCnt needs to be determined based on the number of records here, not the 'imageCnt' inside the blipStoreRecordHeader + */ + uint32_t imageCnt = getRecInst (&blipStoreRecordHeader); + // imageCnt = blipStoreRecordHeader.recLen/(sizeof(OfficeArtFBSEKnown) + sizeof(OfficeArtRecordHeader)); + // fprintf(stderr, "%s::%d::IMAGE_CNT = %d\n", __FUNCTION__, __LINE__, imageCnt); + + offset += sizeof(OfficeArtRecordHeader); /*Rec types taken from @@ -967,9 +989,15 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL * */ #define OFFICE_ART_FBSE_REC_TYPE 0x2 fprintf(stderr, "%s::%d::imageCnt = %d\n", __FUNCTION__, __LINE__, imageCnt); - for (i = 0; i < imageCnt; i++) { + //for (i = 0; i < imageCnt; i++) { + size_t bytesProcessed = 0; + while (bytesProcessed < blipStoreRecordHeader.recLen) + { + size_t off = offset + bytesProcessed; + fprintf(stderr, "%s::%d::bytesProcessed = %ld\n", __FUNCTION__, __LINE__, bytesProcessed); + fprintf(stderr, "%s::%d::blipStoreRecordHeader.recLen= %d\n", __FUNCTION__, __LINE__, blipStoreRecordHeader.recLen); OfficeArtRecordHeader imageHeader; - copy_OfficeArtRecordHeader(&imageHeader, &(ptr[offset])); + copy_OfficeArtRecordHeader(&imageHeader, &(ptr[off])); uint8_t recVer = getRecVer(&imageHeader); if (OFFICE_ART_FBSE_REC_TYPE == recVer){ @@ -977,10 +1005,10 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ fprintf(stderr, "%s::%d::calling processOfficeArtFBSE\n", __FUNCTION__, __LINE__); - processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[offset]), wordDocBlock); + bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[off]), wordDocBlock); } else { fprintf(stderr, "%s::%d::calling processOfficeArtBlip\n", __FUNCTION__, __LINE__); - processOfficeArtBlip(ctx, &(ptr[offset])); + bytesProcessed += processOfficeArtBlip(ctx, &(ptr[off])); } } @@ -1007,37 +1035,37 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag if (directory->bFibRgFcLcb97Header_initialized && (directory->table_stream_0_initialized || directory->table_stream_1_initialized)) { property_t * tableStream = NULL; - /*Get the FIBBase*/ - fib_base_t fib; - uint32_t fib_offset = get_stream_data_offset(hdr, &(directory->word_block), directory->word_block.start_block); - const uint8_t * ptr = NULL; + /*Get the FIBBase*/ + fib_base_t fib; + uint32_t fib_offset = get_stream_data_offset(hdr, &(directory->word_block), directory->word_block.start_block); + const uint8_t * ptr = NULL; - if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - goto done; - } + if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + goto done; + } - ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); - goto done; - } - copy_fib_base(&fib, ptr); + ptr = fmap_need_off_once(hdr->map, fib_offset, sizeof(fib_base_t)); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", fib_offset, fib_offset); + goto done; + } + copy_fib_base(&fib, ptr); #define FIB_BASE_fWhichTblStm_OFFSET 9 - if (fib.ABCDEFGHIJKLM & (1 << FIB_BASE_fWhichTblStm_OFFSET)) { - tableStream = &(directory->table_stream_1_block); - if (!directory->table_stream_1_initialized){ - cli_dbgmsg("ERROR: FIB references 1Table stream, that does not exist\n"); - goto done; - } - } else { - tableStream = &(directory->table_stream_0_block); - if (!directory->table_stream_0_initialized){ - cli_dbgmsg("ERROR: FIB references 0Table stream, that does not exist\n"); - goto done; - } + if (fib.ABCDEFGHIJKLM & (1 << FIB_BASE_fWhichTblStm_OFFSET)) { + tableStream = &(directory->table_stream_1_block); + if (!directory->table_stream_1_initialized){ + cli_dbgmsg("ERROR: FIB references 1Table stream, that does not exist\n"); + goto done; } + } else { + tableStream = &(directory->table_stream_0_block); + if (!directory->table_stream_0_initialized){ + cli_dbgmsg("ERROR: FIB references 0Table stream, that does not exist\n"); + goto done; + } + } /*Call Extract */ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); From 2b30307f5c23cbcf579f5bf06929bc8626fcd69d Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 2 Aug 2024 08:23:42 -0700 Subject: [PATCH 58/96] blah --- libclamav/ole2_extract.c | 53 ++++++------------- libclamav/ole2_extract_images.h | 94 ++------------------------------- 2 files changed, 19 insertions(+), 128 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 447973d976..0f11bed68e 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -415,7 +415,6 @@ print_ole2_property(property_t *property) static void print_ole2_header(ole2_header_t *hdr) { -#if 0 if (!hdr || !cli_debug_flag) { return; } @@ -443,40 +442,6 @@ print_ole2_header(ole2_header_t *hdr) cli_dbgmsg("XBat start:\t\t%d\n", hdr->xbat_start); cli_dbgmsg("XBat block count:\t%d\n", hdr->xbat_count); cli_dbgmsg("\n"); - - - -#endif - fprintf(stderr, "\n"); - fprintf(stderr, "Magic:\t\t\t0x%x%x%x%x%x%x%x%x\n", - hdr->magic[0], hdr->magic[1], hdr->magic[2], hdr->magic[3], - hdr->magic[4], hdr->magic[5], hdr->magic[6], hdr->magic[7]); - - fprintf(stderr, "CLSID:\t\t\t{%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x}\n", - hdr->clsid[0], hdr->clsid[1], hdr->clsid[2], hdr->clsid[3], - hdr->clsid[4], hdr->clsid[5], hdr->clsid[6], hdr->clsid[7], - hdr->clsid[8], hdr->clsid[9], hdr->clsid[10], hdr->clsid[11], - hdr->clsid[12], hdr->clsid[13], hdr->clsid[14], hdr->clsid[15]); - - fprintf(stderr, "Minor version:\t\t0x%x\n", hdr->minor_version); - fprintf(stderr, "DLL version:\t\t0x%x\n", hdr->dll_version); - fprintf(stderr, "Byte Order:\t\t%d\n", hdr->byte_order); - fprintf(stderr, "Big Block Size:\t%i\n", hdr->log2_big_block_size); - fprintf(stderr, "Small Block Size:\t%i\n", hdr->log2_small_block_size); - fprintf(stderr, "BAT count:\t\t%d\n", hdr->bat_count); - fprintf(stderr, "Prop start:\t\t%d\n", hdr->prop_start); - fprintf(stderr, "SBAT cutoff:\t\t%d\n", hdr->sbat_cutoff); - fprintf(stderr, "SBat start:\t\t%d\n", hdr->sbat_start); - fprintf(stderr, "SBat block count:\t%d\n", hdr->sbat_block_count); - fprintf(stderr, "XBat start:\t\t%d\n", hdr->xbat_start); - fprintf(stderr, "XBat block count:\t%d\n", hdr->xbat_count); - fprintf(stderr, "\n"); - - - - - - return; } @@ -2912,9 +2877,21 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.xbat_start = ole2_endian_convert_32(hdr.xbat_start); hdr.xbat_count = ole2_endian_convert_32(hdr.xbat_count); - fprintf(stderr, "%s::%d::bat_count (sector count) and prop_start are different\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::bat_count = %d\n", __FUNCTION__, __LINE__, hdr.bat_count); -print_ole2_header(&hdr); + + +#if 0 + int32_t bat_count __attribute__((packed)); NUMBER of directory sectors + int32_t prop_start __attribute__((packed)); number of fat sectors + + uint32_t signature __attribute__((packed)); first directory sector location +#endif + fprintf(stderr, "%s::%d::Number of directory sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.bat_count, hdr.bat_count); + fprintf(stderr, "%s::%d::Number of FAT sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); + fprintf(stderr, "%s::%d::Transaction sector Number = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); + + + + hdr.sbat_root_start = -1; diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index faee581a11..d7ce73ba65 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -1,6 +1,7 @@ #ifndef OLE2_EXTRACT_IMAGES_H_ #define OLE2_EXTRACT_IMAGES_H_ +/* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/0c9df81f-98d0-454e-ad84-b612cd05b1a4 */ typedef struct __attribute__((packed)) { uint32_t fcStshfOrig; uint32_t lcbStshfOrig; @@ -689,6 +690,7 @@ static void processOfficeArtBlipPICT(cli_ctx* ctx, OfficeArtRecordHeader * rh, c /*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ + fprintf(stderr, "%s::%d::Entering\n", __FUNCTION__, __LINE__); size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); @@ -778,7 +780,6 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { OfficeArtFBSEKnown fbse; - // imageCnt = blipStoreRecordHeader.recLen/(sizeof(OfficeArtFBSEKnown) + sizeof(OfficeArtRecordHeader)); uint32_t offset = sizeof(OfficeArtRecordHeader); uint16_t recInst = getRecInst(imageHeader); @@ -810,9 +811,6 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR * The data is in a different stream */ } - return offset; -#if 0 - #if 0 size_t i; fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); @@ -822,95 +820,11 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR fprintf(stderr, "\n"); #endif - - -#if 1 - fprintf(stderr, "%s::%d::before cpy\n", __FUNCTION__, __LINE__); - copy_OfficeArtRecordHeader(imageHeader, &(ptr[offset])); - - uint8_t recVer = getRecVer(imageHeader); - fprintf(stderr, "%s::%d::recVer = %d\n", __FUNCTION__, __LINE__, recVer); - - offset += sizeof(OfficeArtRecordHeader); - - copy_OfficeArtFBSEKnown (&fbse, &(ptr[offset])); - offset += sizeof(OfficeArtFBSEKnown ); - recInst = getRecInst(imageHeader); - - fprintf(stderr, "%s::%d::recInst = %d\n", __FUNCTION__, __LINE__, recInst); - fprintf(stderr, "%s::%d::fbse.btWin32 = %d\n", __FUNCTION__, __LINE__, fbse.btWin32); - fprintf(stderr, "%s::%d::fbse.btMacOS = %d\n", __FUNCTION__, __LINE__, fbse.btMacOS); - - //here; - - - if ((recInst != fbse.btWin32) && (recInst != fbse.btMacOS)) { - cli_dbgmsg("ERROR Invalid recInst 0x%x\n", recInst); - return; - } - fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); - if (imageHeader->recType != 0xf007) { - cli_dbgmsg("ERROR Invalid recType 0x%x\n", imageHeader->recType); - return; - } - fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); - - offset += fbse.cbName; - - if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { - fprintf(stderr, "%s::%d::Blip is embedded\n", __FUNCTION__, __LINE__); - /* The BLIP is embedded in this record*/ - processOfficeArtBlip(ctx, &(ptr[offset])); - } else { - /* The BLIP is in the 'WordDocument' stream. */ - size_t size = fbse.size; - const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); - fprintf(stderr, "%s::%d::Blip is in WordDocument stream, delay = %u (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); - processOfficeArtBlip(ctx, ptr); - } - -#endif - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fprintf(stderr, "%s::%d::Looks like this might be IT!!!!\n", __FUNCTION__, __LINE__); -#endif + return offset; } static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { size_t offset = header->fcDggInfo; - uint32_t i; /* * Start of OfficeArtContent @@ -1071,7 +985,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); /*TODO: Fix hardcoded 4k*/ ptr = fmap_need_off_once(hdr->map, offset, 4096); - fprintf(stderr, "%s::%d::Fix hardcoded 4k\n", __FUNCTION__, __LINE__); +fprintf(stderr, "%s::%d::Fix hardcoded 4k\n", __FUNCTION__, __LINE__); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); goto done; From a963c3c29a43bcfae0d39613186b0cc78850d808 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 2 Aug 2024 13:17:30 -0700 Subject: [PATCH 59/96] blah --- libclamav/ole2_extract.c | 15 ++++++++++++++- libclamav/ole2_extract_images.h | 5 ++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 0f11bed68e..c7a10f3bab 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -641,7 +641,6 @@ static int ole2_cmp_name(const char *const name, uint32_t name_size, const char decoded[j] = ((unsigned char)name[i + 1]) << 4; decoded[j] += name[i]; } - //fprintf(stderr, "%s::%d::%s\n", __FUNCTION__, __LINE__, decoded); return strcasecmp(decoded, keyword); } @@ -995,6 +994,20 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t memcpy(&(pImageDirectory->word_block), &(prop_block[idx]), sizeof((pImageDirectory->word_block))); pImageDirectory->bFibRgFcLcb97Header_initialized = getFibRgFcLcb97Header(&(prop_block[idx]), hdr, &(pImageDirectory->fibRgFcLcb97Header)); } + +#if 0 + { + size_t i; + here; + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__ ); + for (i = sizeof(property_t); i < 1024; i++){ + fprintf(stderr, "%02x ", name[i]); + } + fprintf(stderr, "\n"); + } +#endif + + } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d7ce73ba65..16ad0103f8 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -985,7 +985,10 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); /*TODO: Fix hardcoded 4k*/ ptr = fmap_need_off_once(hdr->map, offset, 4096); -fprintf(stderr, "%s::%d::Fix hardcoded 4k\n", __FUNCTION__, __LINE__); +fprintf(stderr, "\n%s::%d::tableStream->size = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->size, tableStream->size); +fprintf(stderr, "%s::%d::tableStream->type = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->type, tableStream->type); +fprintf(stderr, "%s::%d::tableStream->next = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->next, tableStream->next); +fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size\n", __FUNCTION__, __LINE__); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); goto done; From 4a07de4fabd824a1dd24795840a119c449e99409 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Fri, 2 Aug 2024 14:24:55 -0700 Subject: [PATCH 60/96] blah --- libclamav/ole2_extract.c | 31 +++++++++++++++++-------------- libclamav/ole2_extract_images.h | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index c7a10f3bab..1e8c9ad49f 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -983,6 +983,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t prop_block[idx].start_block = ole2_endian_convert_32(prop_block[idx].start_block); prop_block[idx].size = ole2_endian_convert_32(prop_block[idx].size); + //fprintf(stderr, "%s::%d::start_block = %d (0x%x)\n", __FUNCTION__, __LINE__, prop_block[idx].start_block, prop_block[idx].start_block); + if ((64 < prop_block[idx].name_size) || (prop_block[idx].name_size % 2)) { cli_dbgmsg("ERROR: Invalid name_size %d\n", prop_block[idx].name_size); continue; @@ -994,20 +996,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t memcpy(&(pImageDirectory->word_block), &(prop_block[idx]), sizeof((pImageDirectory->word_block))); pImageDirectory->bFibRgFcLcb97Header_initialized = getFibRgFcLcb97Header(&(prop_block[idx]), hdr, &(pImageDirectory->fibRgFcLcb97Header)); } - -#if 0 - { - size_t i; - here; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__ ); - for (i = sizeof(property_t); i < 1024; i++){ - fprintf(stderr, "%02x ", name[i]); - } - fprintf(stderr, "\n"); - } -#endif - - } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "WorkBook")) { test_for_xls_encryption(&(prop_block[idx]), hdr, pEncryptionStatus); } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "PowerPoint Document")) { @@ -1026,6 +1014,18 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t memcpy(&(pImageDirectory->table_stream_0_block), &(prop_block[idx]), sizeof(pImageDirectory->table_stream_0_block)); pImageDirectory->table_stream_0_initialized = true; } +} else { + not finding it anywhere, try looking in the table stream; + size_t i; +uint32_t data_offset = get_stream_data_offset(hdr, &(prop_block[idx]), prop_block[idx].start_block); +uint8_t * ptr = fmap_need_off_once(hdr->map, data_offset, 4096); +fprintf(stderr, "%s::%d::%p", __FUNCTION__, __LINE__, ptr); +fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); +for (i = 0; i < 25; i++){ + fprintf(stderr, "%02x ", ptr[i]); +} +fprintf(stderr, "\n"); + } ole2_listmsg("printing ole2 property\n"); @@ -2902,6 +2902,9 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil fprintf(stderr, "%s::%d::Number of FAT sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); fprintf(stderr, "%s::%d::Transaction sector Number = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); + fprintf(stderr, "%s::%d::First directory sector = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature * (1 << hdr.log2_big_block_size), hdr.signature * (1 << hdr.log2_big_block_size)); + + diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 16ad0103f8..b58f086a1f 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -988,7 +988,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag fprintf(stderr, "\n%s::%d::tableStream->size = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->size, tableStream->size); fprintf(stderr, "%s::%d::tableStream->type = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->type, tableStream->type); fprintf(stderr, "%s::%d::tableStream->next = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->next, tableStream->next); -fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size\n", __FUNCTION__, __LINE__); +fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size or block size\n", __FUNCTION__, __LINE__); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); goto done; From eb772dcc7bb96b40ea87c5803af799860d9b580e Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 11:34:45 -0700 Subject: [PATCH 61/96] bllah --- libclamav/ole2_extract.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 1e8c9ad49f..318683f373 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -1014,18 +1014,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t memcpy(&(pImageDirectory->table_stream_0_block), &(prop_block[idx]), sizeof(pImageDirectory->table_stream_0_block)); pImageDirectory->table_stream_0_initialized = true; } -} else { - not finding it anywhere, try looking in the table stream; - size_t i; -uint32_t data_offset = get_stream_data_offset(hdr, &(prop_block[idx]), prop_block[idx].start_block); -uint8_t * ptr = fmap_need_off_once(hdr->map, data_offset, 4096); -fprintf(stderr, "%s::%d::%p", __FUNCTION__, __LINE__, ptr); -fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); -for (i = 0; i < 25; i++){ - fprintf(stderr, "%02x ", ptr[i]); -} -fprintf(stderr, "\n"); - } ole2_listmsg("printing ole2 property\n"); From f4c16e83678593e790705d6bf3e879c86cdd09c4 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 15:23:28 -0700 Subject: [PATCH 62/96] blah --- libclamav/ole2_extract.c | 123 +++++++++++++++++++++++++++----- libclamav/ole2_extract_images.h | 47 +++++++++++- 2 files changed, 153 insertions(+), 17 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 318683f373..c68b16aa98 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -77,14 +77,15 @@ #endif // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/05060311-bfce-4b12-874d-71fd4ce63aea -typedef struct ole2_header_tag { +typedef struct __attribute__((packed)) ole2_header_tag { unsigned char magic[8]; /* should be: 0xd0cf11e0a1b11ae1 */ unsigned char clsid[16]; uint16_t minor_version __attribute__((packed)); - uint16_t dll_version __attribute__((packed)); + uint16_t dll_version __attribute__((packed)); //major version int16_t byte_order __attribute__((packed)); /* -2=intel */ - uint16_t log2_big_block_size __attribute__((packed)); /* usually 9 (2^9 = 512) */ + uint16_t log2_big_block_size __attribute__((packed)); /* usually 9 (2^9 = 512) */ //sector shift +#if 0 uint32_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ /* * This is technically incorrect. log2_small_block_size should be a uint16_t, and reserved should @@ -93,19 +94,27 @@ typedef struct ole2_header_tag { */ int32_t reserved[2] __attribute__((packed)); - int32_t bat_count __attribute__((packed)); - int32_t prop_start __attribute__((packed)); +#else + + uint16_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ //mini sector shift + uint8_t reserved[6]; + +#endif + uint32_t num_directory_sectors __attribute__((packed)); //If dll_version is 3, this must be 0 + int32_t bat_count __attribute__((packed)); //num fat sectors + int32_t prop_start __attribute__((packed)); //first directory sector location - uint32_t signature __attribute__((packed)); + uint32_t signature __attribute__((packed)); //transaction signature number uint32_t sbat_cutoff __attribute__((packed)); /* cutoff for files held * in small blocks * (4096) */ + //ministream cutoff size - int32_t sbat_start __attribute__((packed)); - int32_t sbat_block_count __attribute__((packed)); - int32_t xbat_start __attribute__((packed)); - int32_t xbat_count __attribute__((packed)); - int32_t bat_array[109] __attribute__((packed)); + int32_t sbat_start __attribute__((packed)); //first mini fat sector location + int32_t sbat_block_count __attribute__((packed)); //number of minifat sectors + int32_t xbat_start __attribute__((packed)); //first DIFAT sector location + int32_t xbat_count __attribute__((packed)); //number of difat sectors + int32_t bat_array[109] __attribute__((packed)); //DIFAT /* * The following is not part of the ole2 header, but stuff we need in @@ -724,10 +733,12 @@ static size_t get_stream_data_offset(ole2_header_t *hdr, const property_t *word_ size_t fib_offset = 0; if (word_block->size < MINISTREAM_CUTOFF_SIZE) { + fprintf(stderr, "%s::%d::In the mini stream\n", __FUNCTION__, __LINE__); fib_offset = offset + sector_size * hdr->sbat_root_start; //fib_offset += (word_block->start_block * (1 << hdr->log2_small_block_size)); fib_offset += (sector * (1 << hdr->log2_small_block_size)); } else { + fprintf(stderr, "%s::%d::Not in the mini stream\n", __FUNCTION__, __LINE__); fib_offset = offset + sector_size * sector; } @@ -1008,6 +1019,12 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (pImageDirectory) { memcpy(&(pImageDirectory->table_stream_1_block), &(prop_block[idx]), sizeof(pImageDirectory->table_stream_1_block)); pImageDirectory->table_stream_1_initialized = true; + + + fprintf(stderr, "%s::%d::prev = %d (0x%x)\n", __FUNCTION__, __LINE__, pImageDirectory->table_stream_1_block.prev, pImageDirectory->table_stream_1_block.prev); + fprintf(stderr, "%s::%d::next = %d (0x%x)\n", __FUNCTION__, __LINE__, pImageDirectory->table_stream_1_block.next, pImageDirectory->table_stream_1_block.next); + fprintf(stderr, "%s::%d::child = %d (0x%x)\n", __FUNCTION__, __LINE__, pImageDirectory->table_stream_1_block.child, pImageDirectory->table_stream_1_block.child); + } } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "0Table")) { if (pImageDirectory) { @@ -2869,7 +2886,8 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.dll_version = ole2_endian_convert_16(hdr.dll_version); hdr.byte_order = ole2_endian_convert_16(hdr.byte_order); hdr.log2_big_block_size = ole2_endian_convert_16(hdr.log2_big_block_size); - hdr.log2_small_block_size = ole2_endian_convert_32(hdr.log2_small_block_size); + //hdr.log2_small_block_size = ole2_endian_convert_32(hdr.log2_small_block_size); + hdr.log2_small_block_size = ole2_endian_convert_16(hdr.log2_small_block_size); hdr.bat_count = ole2_endian_convert_32(hdr.bat_count); hdr.prop_start = ole2_endian_convert_32(hdr.prop_start); hdr.sbat_cutoff = ole2_endian_convert_32(hdr.sbat_cutoff); @@ -2886,11 +2904,84 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil uint32_t signature __attribute__((packed)); first directory sector location #endif - fprintf(stderr, "%s::%d::Number of directory sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.bat_count, hdr.bat_count); - fprintf(stderr, "%s::%d::Number of FAT sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); - fprintf(stderr, "%s::%d::Transaction sector Number = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); + //fprintf(stderr, "%s::%d::Number of directory sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.bat_count, hdr.bat_count); + //fprintf(stderr, "%s::%d::Number of FAT sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); + //fprintf(stderr, "%s::%d::Transaction sector Number = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); + + //fprintf(stderr, "%s::%d::First directory sector = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature * (1 << hdr.log2_big_block_size), hdr.signature * (1 << hdr.log2_big_block_size)); + +#if 0 + unsigned char magic[8]; /* should be: 0xd0cf11e0a1b11ae1 */ + unsigned char clsid[16]; + uint16_t minor_version __attribute__((packed)); + uint16_t dll_version __attribute__((packed)); //major version + int16_t byte_order __attribute__((packed)); /* -2=intel */ + + uint16_t log2_big_block_size __attribute__((packed)); /* usually 9 (2^9 = 512) */ //sector shift + uint16_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ //mini sector shift + uint8_t reserved[6] __attribute__((packed)); + uint32_t num_directory_sectors __attribute__((packed)); + int32_t bat_count __attribute__((packed)); //num fat sectors + int32_t prop_start __attribute__((packed)); //first directory sector location + + uint32_t signature __attribute__((packed)); //transaction signature number + uint32_t sbat_cutoff __attribute__((packed)); /* cutoff for files held + * in small blocks + * (4096) */ + //ministream cutoff size - fprintf(stderr, "%s::%d::First directory sector = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature * (1 << hdr.log2_big_block_size), hdr.signature * (1 << hdr.log2_big_block_size)); + int32_t sbat_start __attribute__((packed)); //first mini fat sector location + int32_t sbat_block_count __attribute__((packed)); //number of minifat sectors + int32_t xbat_start __attribute__((packed)); //first DIFAT sector location + int32_t xbat_count __attribute__((packed)); //number of difat sectors + int32_t bat_array[109] __attribute__((packed)); //DIFAT + +#endif + { + size_t andy; + fprintf(stderr, "%s::%d::minor_version = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.minor_version, hdr.minor_version); + fprintf(stderr, "%s::%d::dll_version = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.dll_version, hdr.dll_version); + fprintf(stderr, "%s::%d::byte_order = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.byte_order, hdr.byte_order); + fprintf(stderr, "%s::%d::log2_big_block_size = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.log2_big_block_size, hdr.log2_big_block_size); + fprintf(stderr, "%s::%d::log2_small_block_size = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.log2_small_block_size, hdr.log2_small_block_size); + fprintf(stderr, "%s::%d::reserved::", __FUNCTION__, __LINE__); + for (andy = 0; andy < 6; andy++) { + fprintf(stderr, "%02x ", hdr.reserved[andy]); + } + fprintf(stderr, "\n"); + + + fprintf(stderr, "%s::%d::num_directory_sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.num_directory_sectors, hdr.num_directory_sectors); + fprintf(stderr, "%s::%d::bat_count = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.bat_count, hdr.bat_count); + fprintf(stderr, "%s::%d::prop_start = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); + fprintf(stderr, "%s::%d::signature = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); + fprintf(stderr, "%s::%d::sbat_cutoff = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.sbat_cutoff, hdr.sbat_cutoff); + fprintf(stderr, "%s::%d::sbat_start = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.sbat_start, hdr.sbat_start); + fprintf(stderr, "%s::%d::sbat_block_count = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.sbat_block_count, hdr.sbat_block_count); + fprintf(stderr, "%s::%d::xbat_start = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.xbat_start, hdr.xbat_start); + fprintf(stderr, "%s::%d::xbat_count = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.xbat_count, hdr.xbat_count); + + /* + * The random block that we *don't* want that is stuffed in the middel is in the DIFAT. + * Need to skip it (1 << log2_big_block_size) and keep going. + */ + for (andy = 0; andy < 109; andy++) { + fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, andy, hdr.bat_array[andy], hdr.bat_array[andy]); + if (-1 == hdr.bat_array[andy]) { + break; + } + } + + + + + + + + + + + } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index b58f086a1f..34238a15e7 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -1,6 +1,16 @@ #ifndef OLE2_EXTRACT_IMAGES_H_ #define OLE2_EXTRACT_IMAGES_H_ +void dump_some(const char * const func, const size_t line, const uint8_t * const ptr, size_t cnt) { + size_t i; + fprintf(stderr, "%s::%ld::", func, line); + for (i = 0; i < cnt; i++){ + fprintf(stderr, "%02x ", ptr[i]); + } + fprintf(stderr, "\n"); +} + + /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/0c9df81f-98d0-454e-ad84-b612cd05b1a4 */ typedef struct __attribute__((packed)) { uint32_t fcStshfOrig; @@ -413,17 +423,22 @@ static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, cons const uint8_t * ptr = NULL; uint32_t offset = get_stream_data_offset(hdr, block, block->start_block); + fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT (second time only)::start of worddocument stream offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); offset += delay; + fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT (second time only)::offset + delay = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); + fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT (second time only)::size = %ld (0x%lx)\n", __FUNCTION__, __LINE__, size, size); if ((size_t)(hdr->m_length) < (size_t)(offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for stream %d (0x%x)\n", offset, offset); goto done; } + /*This is the actual offset in the file.*/ ptr = fmap_need_off_once(hdr->map, offset, size); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", offset, offset); goto done; } + dump_some(__FUNCTION__, __LINE__, ptr, 25); done: return ptr; @@ -702,6 +717,9 @@ static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, } offset += 1; /*metafile header*/ +fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); +fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::size = %ld\n", __FUNCTION__, __LINE__, rh->recLen - offset); +fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::rh->recLen = %d\n", __FUNCTION__, __LINE__, rh->recLen); saveImageFile(ctx, &(ptr[offset]), rh->recLen - offset); } @@ -731,6 +749,7 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ cli_dbgmsg("ERROR Invalid recVer 0x%x\n", recVer); goto done; } + fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); #define RECTYPE_OFFICE_ART_BLIP_EMF 0xf01a #define RECTYPE_OFFICE_ART_BLIP_WMF 0xf01b @@ -799,11 +818,13 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ +fprintf(stderr, "%s::%d::ADDTO::offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); processOfficeArtBlip(ctx, &(ptr[offset])); offset += fbse.size; } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; +fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); processOfficeArtBlip(ctx, ptr); @@ -823,9 +844,27 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR return offset; } +//ptr is a pointer to the head of the table stream. static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { size_t offset = header->fcDggInfo; + +#if 0 +{ + size_t andy; + fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); + for (andy = 0; andy < 512; andy++){ + fprintf(stderr, "%02x ", ptr[andy]); + } + fprintf(stderr, "\n"); +} +#endif + + + + + + /* * Start of OfficeArtContent * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3 @@ -894,6 +933,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL offset += sizeof(OfficeArtRecordHeader); +fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); /*Rec types taken from * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/a7d7d967-6bff-489c-a267-3ec30448344a @@ -952,6 +992,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag /*Get the FIBBase*/ fib_base_t fib; uint32_t fib_offset = get_stream_data_offset(hdr, &(directory->word_block), directory->word_block.start_block); +fprintf(stderr, "%s::%d::fib_offset = %d (0x%x\n", __FUNCTION__, __LINE__, fib_offset, fib_offset); const uint8_t * ptr = NULL; if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { @@ -982,10 +1023,13 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag } /*Call Extract */ + /*This offset is an actual offset in the file.*/ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); +fprintf(stderr, "\n%s::%d::ADDTO::offset (start block of table stream)= %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); /*TODO: Fix hardcoded 4k*/ ptr = fmap_need_off_once(hdr->map, offset, 4096); -fprintf(stderr, "\n%s::%d::tableStream->size = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->size, tableStream->size); +fprintf(stderr, "%s::%d::tableStream->size = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->size, tableStream->size); +fprintf(stderr, "%s::%d::tableStream->user_flags = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->user_flags, tableStream->user_flags); fprintf(stderr, "%s::%d::tableStream->type = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->type, tableStream->type); fprintf(stderr, "%s::%d::tableStream->next = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->next, tableStream->next); fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size or block size\n", __FUNCTION__, __LINE__); @@ -993,6 +1037,7 @@ fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size or bl cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); goto done; } + dump_some(__FUNCTION__, __LINE__, ptr, 25); ole2_extract_images(ctx, hdr, &(directory->fibRgFcLcb97Header), ptr, &(directory->word_block)); } From d5ac55300b3a0834691a9e057e0616796ad59894 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 15:23:57 -0700 Subject: [PATCH 63/96] blah --- libclamav/ole2_extract_images.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 34238a15e7..c654472aff 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -1042,7 +1042,7 @@ fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size or bl ole2_extract_images(ctx, hdr, &(directory->fibRgFcLcb97Header), ptr, &(directory->word_block)); } done: - ; + return ; } From 9181bbcab8104646099052093760fb7791475e53 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 15:57:33 -0700 Subject: [PATCH 64/96] blah --- libclamav/ole2_extract.c | 8 ++----- libclamav/ole2_extract_images.h | 37 ++++----------------------------- 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index c68b16aa98..6a5568e678 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -2966,21 +2966,17 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil * Need to skip it (1 << log2_big_block_size) and keep going. */ for (andy = 0; andy < 109; andy++) { - fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, andy, hdr.bat_array[andy], hdr.bat_array[andy]); if (-1 == hdr.bat_array[andy]) { break; } + fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, andy, hdr.bat_array[andy], hdr.bat_array[andy]); + fprintf(stderr, "%s::%d::RESERVED BLOCK::%x\n", __FUNCTION__, __LINE__, ((hdr.bat_array[andy]+1) << hdr.log2_big_block_size)); } - - - - - } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index c654472aff..72450905a5 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -749,7 +749,6 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ cli_dbgmsg("ERROR Invalid recVer 0x%x\n", recVer); goto done; } - fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); #define RECTYPE_OFFICE_ART_BLIP_EMF 0xf01a #define RECTYPE_OFFICE_ART_BLIP_WMF 0xf01b @@ -827,19 +826,10 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %d (0x%x)\n", __FUNCTION__, __LINE__, o fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); processOfficeArtBlip(ctx, ptr); - - /* I don't *think* I need to add anything to the offset here, because the actual data is not here. + /* I don't need to add anything to the offset here, because the actual data is not here. * The data is in a different stream */ } -#if 0 - size_t i; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (i = 0; i < 16; i++) { - fprintf(stderr, "%02x ", ptr[i + offset]); - } - fprintf(stderr, "\n"); -#endif return offset; } @@ -848,23 +838,6 @@ fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LIN static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { size_t offset = header->fcDggInfo; - -#if 0 -{ - size_t andy; - fprintf(stderr, "%s::%d::", __FUNCTION__, __LINE__); - for (andy = 0; andy < 512; andy++){ - fprintf(stderr, "%02x ", ptr[andy]); - } - fprintf(stderr, "\n"); -} -#endif - - - - - - /* * Start of OfficeArtContent * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/8699a984-3718-44be-adae-08b05827f8b3 @@ -958,10 +931,8 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - fprintf(stderr, "%s::%d::calling processOfficeArtFBSE\n", __FUNCTION__, __LINE__); bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[off]), wordDocBlock); } else { - fprintf(stderr, "%s::%d::calling processOfficeArtBlip\n", __FUNCTION__, __LINE__); bytesProcessed += processOfficeArtBlip(ctx, &(ptr[off])); } } @@ -992,7 +963,6 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag /*Get the FIBBase*/ fib_base_t fib; uint32_t fib_offset = get_stream_data_offset(hdr, &(directory->word_block), directory->word_block.start_block); -fprintf(stderr, "%s::%d::fib_offset = %d (0x%x\n", __FUNCTION__, __LINE__, fib_offset, fib_offset); const uint8_t * ptr = NULL; if ((size_t)(hdr->m_length) < (size_t)(fib_offset + sizeof(fib_base_t))) { @@ -1023,10 +993,11 @@ fprintf(stderr, "%s::%d::fib_offset = %d (0x%x\n", __FUNCTION__, __LINE__, fib_o } /*Call Extract */ - /*This offset is an actual offset in the file.*/ + /*This offset is an actual offset of the table stream in the file.*/ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); fprintf(stderr, "\n%s::%d::ADDTO::offset (start block of table stream)= %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - /*TODO: Fix hardcoded 4k*/ + /*TODO: Fix hardcoded 4k. Change it to read 512 bytes (block size) at a time, and continue reading. + */ ptr = fmap_need_off_once(hdr->map, offset, 4096); fprintf(stderr, "%s::%d::tableStream->size = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->size, tableStream->size); fprintf(stderr, "%s::%d::tableStream->user_flags = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->user_flags, tableStream->user_flags); From 602f9b0ab05a290fd94c241d005e157cf8e0ecce Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 16:11:34 -0700 Subject: [PATCH 65/96] Starting rewrite, if something breaks, start here --- libclamav/ole2_extract_images.h | 46 ++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 72450905a5..8f8f2fe47b 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -1,6 +1,7 @@ #ifndef OLE2_EXTRACT_IMAGES_H_ #define OLE2_EXTRACT_IMAGES_H_ + void dump_some(const char * const func, const size_t line, const uint8_t * const ptr, size_t cnt) { size_t i; fprintf(stderr, "%s::%ld::", func, line); @@ -395,6 +396,17 @@ static void copy_FibRgFcLcb97(FibRgFcLcb97 * pHeader, const uint8_t *const ptr) } +typedef struct { + FibRgFcLcb97 fibRgFcLcb97Header; + bool bFibRgFcLcb97Header_initialized; + property_t word_block; + property_t table_stream_0_block; + property_t table_stream_1_block; + bool table_stream_0_initialized; + bool table_stream_1_initialized; +} ole2_image_directory_t; + + /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/5dc1b9ed-818c-436f-8a4f-905a7ebb1ba9 */ typedef struct __attribute__((packed)) { uint16_t recVer_recInstance; @@ -835,7 +847,24 @@ fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LIN } //ptr is a pointer to the head of the table stream. +#if 0 static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { +#else +static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_image_directory_t * directory, property_t * tableStream) { + FibRgFcLcb97 * header = &(directory->fibRgFcLcb97Header); + property_t * wordDocBlock = &(directory->word_block); + const uint8_t * ptr = NULL; + + /*This offset is an actual offset of the table stream in the file.*/ + size_t tableStreamOffset = get_stream_data_offset(ole2Hdr, tableStream, tableStream->start_block); + /*TODO: Fix hardcoded 4k. Change it to read 512 bytes (block size) at a time, and continue reading. */ + ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, 4096); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid tableStreamOffset for File Information Block %ld (0x%lx)\n", tableStreamOffset, tableStreamOffset); + goto done; + } +#endif + size_t offset = header->fcDggInfo; /* @@ -939,22 +968,14 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, //here; +done: + return; } -typedef struct { - FibRgFcLcb97 fibRgFcLcb97Header; - bool bFibRgFcLcb97Header_initialized; - property_t word_block; - property_t table_stream_0_block; - property_t table_stream_1_block; - bool table_stream_0_initialized; - bool table_stream_1_initialized; -} ole2_image_directory_t; - void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_image_directory_t * directory ) { if (directory->bFibRgFcLcb97Header_initialized && (directory->table_stream_0_initialized @@ -992,6 +1013,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag } } +#if 0 /*Call Extract */ /*This offset is an actual offset of the table stream in the file.*/ size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); @@ -1011,6 +1033,10 @@ fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size or bl dump_some(__FUNCTION__, __LINE__, ptr, 25); ole2_extract_images(ctx, hdr, &(directory->fibRgFcLcb97Header), ptr, &(directory->word_block)); +#else + ole2_extract_images(ctx, hdr, directory, tableStream); + +#endif } done: return ; From f47886cd7651f37c736ca634709bcadded292ae7 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 16:27:34 -0700 Subject: [PATCH 66/96] blah --- libclamav/ole2_extract_images.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 8f8f2fe47b..e0c023fdb3 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -866,6 +866,8 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima #endif size_t offset = header->fcDggInfo; + fprintf(stderr, "%s::%d::blahblah::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); + PUT IN CODE TO ONLY FMAP 1 BLOCK AT A TIME, MAYBE TRACK BLOCK SIZE INSTEAD OF OFFSET; /* * Start of OfficeArtContent From c1718efa85c73419419401e760cdb59e6e1f3af4 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 17:51:04 -0700 Subject: [PATCH 67/96] blah --- libclamav/ole2_extract_images.h | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index e0c023fdb3..4fdcd82389 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -836,6 +836,7 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %d (0x%x)\n", __FUNCTION__, __LINE__, o /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); +fprintf(stderr, "%s::%d::added offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); processOfficeArtBlip(ctx, ptr); /* I don't need to add anything to the offset here, because the actual data is not here. @@ -846,6 +847,10 @@ fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LIN return offset; } +size_t get_block_size(ole2_header_t * ole2Hdr) { + return 1 << ole2Hdr->log2_big_block_size; +} + //ptr is a pointer to the head of the table stream. #if 0 static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { @@ -858,7 +863,8 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima /*This offset is an actual offset of the table stream in the file.*/ size_t tableStreamOffset = get_stream_data_offset(ole2Hdr, tableStream, tableStream->start_block); /*TODO: Fix hardcoded 4k. Change it to read 512 bytes (block size) at a time, and continue reading. */ - ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, 4096); + //ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, 4096); + ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, get_block_size(ole2Hdr)); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid tableStreamOffset for File Information Block %ld (0x%lx)\n", tableStreamOffset, tableStreamOffset); goto done; @@ -867,7 +873,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima size_t offset = header->fcDggInfo; fprintf(stderr, "%s::%d::blahblah::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - PUT IN CODE TO ONLY FMAP 1 BLOCK AT A TIME, MAYBE TRACK BLOCK SIZE INSTEAD OF OFFSET; +// PUT IN CODE TO ONLY FMAP 1 BLOCK AT A TIME, MAYBE TRACK BLOCK SIZE INSTEAD OF OFFSET; /* * Start of OfficeArtContent @@ -915,6 +921,19 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima OfficeArtRecordHeader blipStoreRecordHeader; copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); + fprintf(stderr, "%s::%d::total needed = %lu\n", __FUNCTION__, __LINE__, offset + blipStoreRecordHeader.recLen); + /*Allocate the full number of bytes needed for headers.*/ + size_t total_needed = 0; + while (total_needed < (offset + blipStoreRecordHeader.recLen)) { + total_needed += get_block_size(ole2Hdr); + } + fprintf(stderr, "%s::%d::total_needed = %ld (0x%lx)\n", __FUNCTION__, __LINE__, total_needed, total_needed); + ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); + if (NULL == ptr) { + cli_dbgmsg("ERROR: Invalid offset for OfficeArtRecordHeader%ld (0x%lx)\n", total_needed, total_needed); + goto done; + } + if (0xf != getRecVer(&blipStoreRecordHeader)) { cli_dbgmsg("ERROR Invalid recVer 0x%x\n", getRecVer(&blipStoreRecordHeader)); return; @@ -954,6 +973,7 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, size_t off = offset + bytesProcessed; fprintf(stderr, "%s::%d::bytesProcessed = %ld\n", __FUNCTION__, __LINE__, bytesProcessed); fprintf(stderr, "%s::%d::blipStoreRecordHeader.recLen= %d\n", __FUNCTION__, __LINE__, blipStoreRecordHeader.recLen); + fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); OfficeArtRecordHeader imageHeader; copy_OfficeArtRecordHeader(&imageHeader, &(ptr[off])); uint8_t recVer = getRecVer(&imageHeader); @@ -962,8 +982,10 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ + fprintf(stderr, "%s::%d::FBSE\n", __FUNCTION__, __LINE__); bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[off]), wordDocBlock); } else { + fprintf(stderr, "%s::%d::Blip\n", __FUNCTION__, __LINE__); bytesProcessed += processOfficeArtBlip(ctx, &(ptr[off])); } } From 0f75b29c9ab6623ac29d31c398b5b7843e9e9cc9 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 18:32:48 -0700 Subject: [PATCH 68/96] blah --- libclamav/ole2_extract_images.h | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 4fdcd82389..51652edfc7 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -862,7 +862,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima /*This offset is an actual offset of the table stream in the file.*/ size_t tableStreamOffset = get_stream_data_offset(ole2Hdr, tableStream, tableStream->start_block); - /*TODO: Fix hardcoded 4k. Change it to read 512 bytes (block size) at a time, and continue reading. */ + //ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, 4096); ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, get_block_size(ole2Hdr)); if (NULL == ptr) { @@ -872,8 +872,6 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima #endif size_t offset = header->fcDggInfo; - fprintf(stderr, "%s::%d::blahblah::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); -// PUT IN CODE TO ONLY FMAP 1 BLOCK AT A TIME, MAYBE TRACK BLOCK SIZE INSTEAD OF OFFSET; /* * Start of OfficeArtContent @@ -897,9 +895,8 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima * We shouldn't have to care about that, since it's for drawings and not actual file images. * Going to just skip this record for now. * */ - OfficeArtRecordHeader hdr; //OfficeArtFDGGBlock + OfficeArtRecordHeader hdr; copy_OfficeArtRecordHeader(&hdr, &(ptr[offset])); - //offset += hdr.recLen; not right, doesn't *always* seem to be a size. offset += sizeof(OfficeArtRecordHeader); @@ -922,12 +919,18 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); fprintf(stderr, "%s::%d::total needed = %lu\n", __FUNCTION__, __LINE__, offset + blipStoreRecordHeader.recLen); + /*Allocate the full number of bytes needed for headers.*/ size_t total_needed = 0; while (total_needed < (offset + blipStoreRecordHeader.recLen)) { total_needed += get_block_size(ole2Hdr); } - fprintf(stderr, "%s::%d::total_needed = %ld (0x%lx)\n", __FUNCTION__, __LINE__, total_needed, total_needed); + + fprintf(stderr, "%s::%d::TODO: Verify that total_needed + tableStreamOffset does not cross into a FAT block\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::Needs to be a while bytes_processed < ...\n", __FUNCTION__, __LINE__); + +fprintf(stderr, "%s::%d::total_needed = %ld (0x%lx)\n", __FUNCTION__, __LINE__, total_needed, total_needed); + ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); if (NULL == ptr) { cli_dbgmsg("ERROR: Invalid offset for OfficeArtRecordHeader%ld (0x%lx)\n", total_needed, total_needed); @@ -944,19 +947,13 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima return; } - fprintf(stderr, "%s::%d::record len = %d, sizeof = %ld\n", __FUNCTION__, __LINE__, blipStoreRecordHeader.recLen, sizeof(OfficeArtFBSEKnown)); /* * imageCnt needs to be determined based on the number of records here, not the 'imageCnt' inside the blipStoreRecordHeader */ uint32_t imageCnt = getRecInst (&blipStoreRecordHeader); - // imageCnt = blipStoreRecordHeader.recLen/(sizeof(OfficeArtFBSEKnown) + sizeof(OfficeArtRecordHeader)); - // fprintf(stderr, "%s::%d::IMAGE_CNT = %d\n", __FUNCTION__, __LINE__, imageCnt); - - offset += sizeof(OfficeArtRecordHeader); -fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); /*Rec types taken from * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/a7d7d967-6bff-489c-a267-3ec30448344a @@ -965,15 +962,10 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, * * */ #define OFFICE_ART_FBSE_REC_TYPE 0x2 - fprintf(stderr, "%s::%d::imageCnt = %d\n", __FUNCTION__, __LINE__, imageCnt); - //for (i = 0; i < imageCnt; i++) { size_t bytesProcessed = 0; while (bytesProcessed < blipStoreRecordHeader.recLen) { size_t off = offset + bytesProcessed; - fprintf(stderr, "%s::%d::bytesProcessed = %ld\n", __FUNCTION__, __LINE__, bytesProcessed); - fprintf(stderr, "%s::%d::blipStoreRecordHeader.recLen= %d\n", __FUNCTION__, __LINE__, blipStoreRecordHeader.recLen); - fprintf(stderr, "%s::%d::off = %ld (0x%lx)\n", __FUNCTION__, __LINE__, off, off); OfficeArtRecordHeader imageHeader; copy_OfficeArtRecordHeader(&imageHeader, &(ptr[off])); uint8_t recVer = getRecVer(&imageHeader); @@ -982,16 +974,12 @@ fprintf(stderr, "%s::%d::ADDTO::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - fprintf(stderr, "%s::%d::FBSE\n", __FUNCTION__, __LINE__); bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[off]), wordDocBlock); } else { - fprintf(stderr, "%s::%d::Blip\n", __FUNCTION__, __LINE__); bytesProcessed += processOfficeArtBlip(ctx, &(ptr[off])); } } - //here; - done: return; From 3d0461dc84ab3fc9c2b15a199361d3c176ce6a9c Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 18:50:17 -0700 Subject: [PATCH 69/96] blah --- libclamav/ole2_extract.c | 5 +++-- libclamav/ole2_extract_images.h | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 6a5568e678..810ef94b67 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -76,6 +76,7 @@ #pragma pack 1 #endif +#define NUM_DIFAT_ENTRIES 109 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/05060311-bfce-4b12-874d-71fd4ce63aea typedef struct __attribute__((packed)) ole2_header_tag { unsigned char magic[8]; /* should be: 0xd0cf11e0a1b11ae1 */ @@ -114,7 +115,7 @@ typedef struct __attribute__((packed)) ole2_header_tag { int32_t sbat_block_count __attribute__((packed)); //number of minifat sectors int32_t xbat_start __attribute__((packed)); //first DIFAT sector location int32_t xbat_count __attribute__((packed)); //number of difat sectors - int32_t bat_array[109] __attribute__((packed)); //DIFAT + int32_t bat_array[NUM_DIFAT_ENTRIES] __attribute__((packed)); //DIFAT /* * The following is not part of the ole2 header, but stuff we need in @@ -2965,7 +2966,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil * The random block that we *don't* want that is stuffed in the middel is in the DIFAT. * Need to skip it (1 << log2_big_block_size) and keep going. */ - for (andy = 0; andy < 109; andy++) { + for (andy = 0; andy < NUM_DIFAT_ENTRIES; andy++) { if (-1 == hdr.bat_array[andy]) { break; } diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 51652edfc7..a6e1cef06a 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -926,10 +926,20 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima total_needed += get_block_size(ole2Hdr); } - fprintf(stderr, "%s::%d::TODO: Verify that total_needed + tableStreamOffset does not cross into a FAT block\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::Needs to be a while bytes_processed < ...\n", __FUNCTION__, __LINE__); + size_t idx; + for (idx = 0; idx < NUM_DIFAT_ENTRIES; idx++) { + if (-1 == ole2Hdr->bat_array[idx]) { + break; + } -fprintf(stderr, "%s::%d::total_needed = %ld (0x%lx)\n", __FUNCTION__, __LINE__, total_needed, total_needed); + uint32_t reserved = (ole2Hdr->bat_array[idx]+1) << ole2Hdr->log2_big_block_size; + if ((reserved >= tableStreamOffset) && (reserved <= tableStreamOffset + total_needed)){ + fprintf(stderr, "%s::%d::total_needed crosses over a FAT block that must be skipped!!!!!\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::This is not currently handled!!!!!\n", __FUNCTION__, __LINE__); + exit(11); + } + + } ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); if (NULL == ptr) { @@ -947,12 +957,6 @@ fprintf(stderr, "%s::%d::total_needed = %ld (0x%lx)\n", __FUNCTION__, __LINE__, return; } - /* - * imageCnt needs to be determined based on the number of records here, not the 'imageCnt' inside the blipStoreRecordHeader - */ - - uint32_t imageCnt = getRecInst (&blipStoreRecordHeader); - offset += sizeof(OfficeArtRecordHeader); /*Rec types taken from From 17d3530f67253fe6db12791387bb50bf9dd60ce8 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 18:50:44 -0700 Subject: [PATCH 70/96] blah --- libclamav/ole2_extract_images.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index a6e1cef06a..d088a4594c 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -936,6 +936,26 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima if ((reserved >= tableStreamOffset) && (reserved <= tableStreamOffset + total_needed)){ fprintf(stderr, "%s::%d::total_needed crosses over a FAT block that must be skipped!!!!!\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::This is not currently handled!!!!!\n", __FUNCTION__, __LINE__); + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! + //TODO::HANDLE THIS CASE!!!!! exit(11); } From f9446323868381d162df94db6bdf850adb622a77 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Tue, 6 Aug 2024 18:57:08 -0700 Subject: [PATCH 71/96] blah --- libclamav/ole2_extract_images.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d088a4594c..be7247be3d 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -936,6 +936,9 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima if ((reserved >= tableStreamOffset) && (reserved <= tableStreamOffset + total_needed)){ fprintf(stderr, "%s::%d::total_needed crosses over a FAT block that must be skipped!!!!!\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::This is not currently handled!!!!!\n", __FUNCTION__, __LINE__); + Looks like the easiest thing to do is pass the pointer and the offset everywhere, and compare them + everywhere; + //TODO::HANDLE THIS CASE!!!!! //TODO::HANDLE THIS CASE!!!!! //TODO::HANDLE THIS CASE!!!!! From ca931d32f45a45dccf86ccc343ae73291defebd6 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 07:30:09 -0700 Subject: [PATCH 72/96] blah --- libclamav/ole2_extract_images.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index be7247be3d..d088a4594c 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -936,9 +936,6 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima if ((reserved >= tableStreamOffset) && (reserved <= tableStreamOffset + total_needed)){ fprintf(stderr, "%s::%d::total_needed crosses over a FAT block that must be skipped!!!!!\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::This is not currently handled!!!!!\n", __FUNCTION__, __LINE__); - Looks like the easiest thing to do is pass the pointer and the offset everywhere, and compare them - everywhere; - //TODO::HANDLE THIS CASE!!!!! //TODO::HANDLE THIS CASE!!!!! //TODO::HANDLE THIS CASE!!!!! From 05bfbd373ce1ca694ef53e64728fd65cde60718b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 08:36:01 -0700 Subject: [PATCH 73/96] Starting refactor to fix this. If anything breaks, start here --- libclamav/ole2_extract_images.h | 39 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d088a4594c..d26a3848d6 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -406,6 +406,17 @@ typedef struct { bool table_stream_1_initialized; } ole2_image_directory_t; +typedef struct __attribute__((packed)) { + + uint32_t start_block; + + const uint8_t * base_ptr; + + const uint8_t * ptr; + +// size_t offset; + +} ole2_pointer_t; /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/5dc1b9ed-818c-436f-8a4f-905a7ebb1ba9 */ typedef struct __attribute__((packed)) { @@ -858,17 +869,23 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcL static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_image_directory_t * directory, property_t * tableStream) { FibRgFcLcb97 * header = &(directory->fibRgFcLcb97Header); property_t * wordDocBlock = &(directory->word_block); +#if 0 const uint8_t * ptr = NULL; +#else + ole2_pointer_t ole2Ptr = {0}; +#endif /*This offset is an actual offset of the table stream in the file.*/ size_t tableStreamOffset = get_stream_data_offset(ole2Hdr, tableStream, tableStream->start_block); //ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, 4096); - ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, get_block_size(ole2Hdr)); - if (NULL == ptr) { + ole2Ptr.ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, get_block_size(ole2Hdr)); + if (NULL == ole2Ptr.ptr) { cli_dbgmsg("ERROR: Invalid tableStreamOffset for File Information Block %ld (0x%lx)\n", tableStreamOffset, tableStreamOffset); goto done; } + ole2Ptr.start_block = tableStream->start_block; + ole2Ptr.base_ptr = ole2Ptr.ptr; #endif size_t offset = header->fcDggInfo; @@ -880,7 +897,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/dd7133b6-ed10-4bcb-be29-67b0544f884f */ OfficeArtRecordHeader oadc_recordHeader; //OfficeArtDggContainer - copy_OfficeArtRecordHeader (&oadc_recordHeader, &(ptr[offset])); + copy_OfficeArtRecordHeader (&oadc_recordHeader, &(ole2Ptr.ptr[offset])); if (0xf != oadc_recordHeader.recVer_recInstance){ cli_dbgmsg("ERROR: Invalid record version (%x)\n", oadc_recordHeader.recVer_recInstance); @@ -896,12 +913,12 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima * Going to just skip this record for now. * */ OfficeArtRecordHeader hdr; - copy_OfficeArtRecordHeader(&hdr, &(ptr[offset])); + copy_OfficeArtRecordHeader(&hdr, &(ole2Ptr.ptr[offset])); offset += sizeof(OfficeArtRecordHeader); OfficeArtFDGG fdgg; - copy_OfficeArtFDGG(&fdgg, &(ptr[offset])); + copy_OfficeArtFDGG(&fdgg, &(ole2Ptr.ptr[offset])); offset += sizeof(OfficeArtFDGG); /* OfficeArtIDCL is not used in parsing images, only drawings. If details are needed, they are @@ -916,7 +933,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima * */ OfficeArtRecordHeader blipStoreRecordHeader; - copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ptr[offset])); + copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ole2Ptr.ptr[offset])); fprintf(stderr, "%s::%d::total needed = %lu\n", __FUNCTION__, __LINE__, offset + blipStoreRecordHeader.recLen); @@ -961,8 +978,8 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima } - ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); - if (NULL == ptr) { + ole2Ptr.ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); + if (NULL == ole2Ptr.ptr) { cli_dbgmsg("ERROR: Invalid offset for OfficeArtRecordHeader%ld (0x%lx)\n", total_needed, total_needed); goto done; } @@ -991,16 +1008,16 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima { size_t off = offset + bytesProcessed; OfficeArtRecordHeader imageHeader; - copy_OfficeArtRecordHeader(&imageHeader, &(ptr[off])); + copy_OfficeArtRecordHeader(&imageHeader, &(ole2Ptr.ptr[off])); uint8_t recVer = getRecVer(&imageHeader); if (OFFICE_ART_FBSE_REC_TYPE == recVer){ /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ - bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ptr[off]), wordDocBlock); + bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ole2Ptr.ptr[off]), wordDocBlock); } else { - bytesProcessed += processOfficeArtBlip(ctx, &(ptr[off])); + bytesProcessed += processOfficeArtBlip(ctx, &(ole2Ptr.ptr[off])); } } From 2854bf0180fea19cd58ef7ce87b3da35dd351068 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 09:08:36 -0700 Subject: [PATCH 74/96] a little better --- libclamav/ole2_extract_images.h | 61 +++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d26a3848d6..4f6d8a80f8 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -760,12 +760,16 @@ static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, processOfficeArtBlipGeneric(ctx, rh, ptr, 0x6e4, 0x6e5, 1) ; } +#if 0 static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ +#else +static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_pointer_t * ole2Ptr){ +#endif size_t offset = 0; OfficeArtRecordHeader rh; - copy_OfficeArtRecordHeader (&rh, ptr); + copy_OfficeArtRecordHeader (&rh, ole2Ptr->ptr); offset += sizeof(OfficeArtRecordHeader ); uint8_t recVer = getRecVer(&rh); if (0 != recVer) { @@ -784,27 +788,27 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ switch (rh.recType) { case RECTYPE_OFFICE_ART_BLIP_EMF: - processOfficeArtBlipEMF(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipEMF(ctx, &rh, &(ole2Ptr->ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_WMF : - processOfficeArtBlipWMF(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipWMF(ctx, &rh, &(ole2Ptr->ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_PICT: - processOfficeArtBlipPICT(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipPICT(ctx, &rh, &(ole2Ptr->ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_JPEG: /* fallthrough */ case RECTYPE_OFFICE_ART_BLIP_JPEG2: - processOfficeArtBlipJPEG(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipJPEG(ctx, &rh, &(ole2Ptr->ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_PNG: - processOfficeArtBlipPNG(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipPNG(ctx, &rh, &(ole2Ptr->ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_DIB: - processOfficeArtBlipDIB(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipDIB(ctx, &rh, &(ole2Ptr->ptr[offset])); break; case RECTYPE_OFFICE_ART_BLIP_TIFF: - processOfficeArtBlipTIFF(ctx, &rh, &(ptr[offset])); + processOfficeArtBlipTIFF(ctx, &rh, &(ole2Ptr->ptr[offset])); break; default: cli_dbgmsg("ERROR Invalid recType 0x%x\n", rh.recType); @@ -812,19 +816,21 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ } done: + ole2Ptr->ptr = &(ole2Ptr->ptr[sizeof(rh) + rh.recLen]); return (sizeof(rh) + rh.recLen); } /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { +//static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { +static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, ole2_pointer_t * ole2Ptr, property_t * wordDocBlock) { OfficeArtFBSEKnown fbse; uint32_t offset = sizeof(OfficeArtRecordHeader); uint16_t recInst = getRecInst(imageHeader); - copy_OfficeArtFBSEKnown (&fbse, &(ptr[offset])); + copy_OfficeArtFBSEKnown (&fbse, &(ole2Ptr->ptr[offset])); offset += sizeof(OfficeArtFBSEKnown ); if ((recInst != fbse.btWin32) && (recInst != fbse.btMacOS)) { @@ -838,23 +844,40 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR offset += fbse.cbName; + ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ fprintf(stderr, "%s::%d::ADDTO::offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); - processOfficeArtBlip(ctx, &(ptr[offset])); + //processOfficeArtBlip(ctx, &(ole2Ptr->ptr[offset])); + processOfficeArtBlip(ctx, ole2Ptr); + ole2Ptr->ptr = &(ole2Ptr->ptr[fbse.size]); offset += fbse.size; } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); fprintf(stderr, "%s::%d::added offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); +#if 0 const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); processOfficeArtBlip(ctx, ptr); +#else + ole2_pointer_t wordStreamPtr = {0}; + wordStreamPtr.ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); + if (NULL == wordStreamPtr.ptr){ + fprintf(stderr, "%s::%d::Handle this\n", __FUNCTION__, __LINE__); + exit(11); + } + wordStreamPtr.base_ptr = wordStreamPtr.ptr; + wordStreamPtr.start_block = wordDocBlock->start_block; + processOfficeArtBlip(ctx, &wordStreamPtr); +#endif /* I don't need to add anything to the offset here, because the actual data is not here. * The data is in a different stream */ } + fprintf(stderr, "%s::%d::Incrementing by offset = %d\n", __FUNCTION__, __LINE__, offset); +// ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); return offset; } @@ -1004,20 +1027,30 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima * */ #define OFFICE_ART_FBSE_REC_TYPE 0x2 size_t bytesProcessed = 0; + ole2Ptr.ptr = &(ole2Ptr.ptr[offset]); while (bytesProcessed < blipStoreRecordHeader.recLen) { - size_t off = offset + bytesProcessed; + //size_t off = offset + bytesProcessed; + size_t off = bytesProcessed; OfficeArtRecordHeader imageHeader; - copy_OfficeArtRecordHeader(&imageHeader, &(ole2Ptr.ptr[off])); + //copy_OfficeArtRecordHeader(&imageHeader, &(ole2Ptr.ptr[off])); + copy_OfficeArtRecordHeader(&imageHeader, ole2Ptr.ptr); uint8_t recVer = getRecVer(&imageHeader); if (OFFICE_ART_FBSE_REC_TYPE == recVer){ /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ +#if 0 bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ole2Ptr.ptr[off]), wordDocBlock); +#else +// ole2Ptr.ptr = &(ole2Ptr.ptr[off]); + bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &ole2Ptr, wordDocBlock); +#endif } else { - bytesProcessed += processOfficeArtBlip(ctx, &(ole2Ptr.ptr[off])); +fprintf(stderr, "%s::%d::SHOULD NOT BE HERE FOR MY TEST\n", __FUNCTION__, __LINE__); exit(1); + //bytesProcessed += processOfficeArtBlip(ctx, &(ole2Ptr.ptr[off])); + bytesProcessed += processOfficeArtBlip(ctx, &ole2Ptr); } } From 00ce447cbde8abcc83954f17ad2226b1769b2b1a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 09:17:20 -0700 Subject: [PATCH 75/96] blah --- libclamav/ole2_extract_images.h | 43 +++++++++++++++++---------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 4f6d8a80f8..6c385b17eb 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -712,22 +712,22 @@ static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * r } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32 */ -static void processOfficeArtBlipEMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr) { - processOfficeArtBlipGeneric(ctx, rh, ptr, 0x3d4, 0x3d5, 34) ; +static void processOfficeArtBlipEMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr) { + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x3d4, 0x3d5, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/ee892f04-f001-4531-a34b-67aab3426dcb */ -static void processOfficeArtBlipWMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(ctx, rh, ptr, 0x216, 0x217, 34) ; +static void processOfficeArtBlipWMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x216, 0x217, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/4b6c5fc5-98cc-445a-8ec7-12b2f2c05b9f */ -static void processOfficeArtBlipPICT(cli_ctx* ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(ctx, rh, ptr, 0x542, 0x543, 34) ; +static void processOfficeArtBlipPICT(cli_ctx* ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr ){ + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x542, 0x543, 34) ; } /*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ -static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ +static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ fprintf(stderr, "%s::%d::Entering\n", __FUNCTION__, __LINE__); size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); @@ -743,21 +743,21 @@ static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::size = %ld\n", __FUNCTION__, __LINE__, rh->recLen - offset); fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::rh->recLen = %d\n", __FUNCTION__, __LINE__, rh->recLen); - saveImageFile(ctx, &(ptr[offset]), rh->recLen - offset); + saveImageFile(ctx, &(ole2Ptr->ptr[offset]), rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 */ -static void processOfficeArtBlipPNG(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(ctx, rh, ptr, 0x6e0, 0x6e1, 1) ; +static void processOfficeArtBlipPNG(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x6e0, 0x6e1, 1) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/1393bf5e-6fa0-4665-b3ec-68199b555656 */ -static void processOfficeArtBlipDIB(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(ctx, rh, ptr, 0x7a8, 0x7a9, 1) ; +static void processOfficeArtBlipDIB(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x7a8, 0x7a9, 1) ; } -static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr){ - processOfficeArtBlipGeneric(ctx, rh, ptr, 0x6e4, 0x6e5, 1) ; +static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x6e4, 0x6e5, 1) ; } #if 0 @@ -786,29 +786,30 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_pointer_t * ole2Ptr){ #define RECTYPE_OFFICE_ART_BLIP_TIFF 0xf029 #define RECTYPE_OFFICE_ART_BLIP_JPEG2 0xf02a + ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); switch (rh.recType) { case RECTYPE_OFFICE_ART_BLIP_EMF: - processOfficeArtBlipEMF(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipEMF(ctx, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_WMF : - processOfficeArtBlipWMF(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipWMF(ctx, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_PICT: - processOfficeArtBlipPICT(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipPICT(ctx, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_JPEG: /* fallthrough */ case RECTYPE_OFFICE_ART_BLIP_JPEG2: - processOfficeArtBlipJPEG(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipJPEG(ctx, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_PNG: - processOfficeArtBlipPNG(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipPNG(ctx, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_DIB: - processOfficeArtBlipDIB(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipDIB(ctx, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_TIFF: - processOfficeArtBlipTIFF(ctx, &rh, &(ole2Ptr->ptr[offset])); + processOfficeArtBlipTIFF(ctx, &rh, ole2Ptr); break; default: cli_dbgmsg("ERROR Invalid recType 0x%x\n", rh.recType); From 0bd6f7b9b4f7caf92aeef378670bcc1dface0ac7 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 09:24:29 -0700 Subject: [PATCH 76/96] blah --- libclamav/ole2_extract_images.h | 72 ++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 6c385b17eb..7f0ff5305e 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -638,6 +638,7 @@ static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * c dst->foDelay = ole2_endian_convert_32(dst->foDelay); } +#if 0 static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size){ char *tempfile = NULL; @@ -683,6 +684,57 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size CLI_FREE_AND_SET_NULL(tempfile); } +#else +static void saveImageFile( cli_ctx * ctx, ole2_pointer_t * ole2Ptr, size_t size){ + + char *tempfile = NULL; + int out_fd = -1; + cl_error_t ret ; + size_t bytesWritten = 0; + FILE * fp = NULL; + static json_object * ary = NULL; + + if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { + cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); + goto done; + } + + fp = fdopen(out_fd, "wb"); + while (bytesWritten < size) { + int ret = fwrite(&(ole2Ptr->ptr[bytesWritten]), 1, size - bytesWritten, fp); + if (ret > 0) { + bytesWritten += ret; + } else { + break; + } + } + + if (bytesWritten != size) { + cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); + } + + if (SCAN_COLLECT_METADATA && ctx->wrkproperty != NULL){ + if (NULL == ary) { +#define OLE2_EXTRACTED_IMAGES_JSON_KEY "OLE2_IMAGES" + ary = cli_jsonarray(ctx->wrkproperty, OLE2_EXTRACTED_IMAGES_JSON_KEY); + } + if (ary) { + cli_jsonstr(ary, NULL, tempfile); + } + } + +done: + ole2Ptr->ptr = &(ole2Ptr->ptr[size]); + + fprintf(stderr, "%s::%d::TODO: increment pointer by the blocks skipped also!!!!\n", __FUNCTION__, __LINE__); + + if (tempfile && !ctx->engine->keeptmp) { + remove(tempfile); + } + CLI_FREE_AND_SET_NULL(tempfile); + +} +#endif /*All these structures (except JPEG) are exactly the same, with the exception of the recInst values for 1 or 2 UIDs, @@ -694,7 +746,7 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 * */ -static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * rh, const uint8_t * const ptr, +static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr, uint16_t riSingleUID, uint16_t riDoubleUID, uint32_t bytesAfterUIDs) { size_t offset = 16; /* Size of rh*/ @@ -708,22 +760,23 @@ static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * r } offset += bytesAfterUIDs; /*metafile header*/ - saveImageFile(ctx, &(ptr[offset]), rh->recLen - offset); + ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); + saveImageFile(ctx, ole2Ptr, rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32 */ static void processOfficeArtBlipEMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr) { - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x3d4, 0x3d5, 34) ; + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x3d4, 0x3d5, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/ee892f04-f001-4531-a34b-67aab3426dcb */ static void processOfficeArtBlipWMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x216, 0x217, 34) ; + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x216, 0x217, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/4b6c5fc5-98cc-445a-8ec7-12b2f2c05b9f */ static void processOfficeArtBlipPICT(cli_ctx* ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr ){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x542, 0x543, 34) ; + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x542, 0x543, 34) ; } /*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ @@ -743,21 +796,22 @@ static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::size = %ld\n", __FUNCTION__, __LINE__, rh->recLen - offset); fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::rh->recLen = %d\n", __FUNCTION__, __LINE__, rh->recLen); - saveImageFile(ctx, &(ole2Ptr->ptr[offset]), rh->recLen - offset); + ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); + saveImageFile(ctx, ole2Ptr, rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 */ static void processOfficeArtBlipPNG(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x6e0, 0x6e1, 1) ; + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x6e0, 0x6e1, 1) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/1393bf5e-6fa0-4665-b3ec-68199b555656 */ static void processOfficeArtBlipDIB(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x7a8, 0x7a9, 1) ; + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x7a8, 0x7a9, 1) ; } static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr->ptr, 0x6e4, 0x6e5, 1) ; + processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x6e4, 0x6e5, 1) ; } #if 0 From c746a47e1b60f3c8967e08bb6cfa72e55d43fe1b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 09:47:29 -0700 Subject: [PATCH 77/96] blah blah --- libclamav/ole2_extract_images.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 7f0ff5305e..31d6a9f864 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -699,6 +699,18 @@ static void saveImageFile( cli_ctx * ctx, ole2_pointer_t * ole2Ptr, size_t size) goto done; } + + fprintf(stderr, "%s::%d::NEED TO ADD 0X1400\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::ole2Ptr->start_block = %u\n", __FUNCTION__, __LINE__, ole2Ptr->start_block); + fprintf(stderr, "%s::%d::ole2Ptr->base_ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->base_ptr); + fprintf(stderr, "%s::%d::ole2Ptr->ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->ptr); + fprintf(stderr, "%s::%d::size = %lu\n", __FUNCTION__, __LINE__, size); + + fprintf(stderr, "%s::%d::Image should be at %lx\n", __FUNCTION__, __LINE__, 0x1400 + (ole2Ptr->ptr - ole2Ptr->base_ptr)); + + + + fp = fdopen(out_fd, "wb"); while (bytesWritten < size) { int ret = fwrite(&(ole2Ptr->ptr[bytesWritten]), 1, size - bytesWritten, fp); @@ -917,12 +929,14 @@ fprintf(stderr, "%s::%d::added offset = %d (0x%x)\n", __FUNCTION__, __LINE__, of processOfficeArtBlip(ctx, ptr); #else ole2_pointer_t wordStreamPtr = {0}; - wordStreamPtr.ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); - if (NULL == wordStreamPtr.ptr){ + wordStreamPtr.base_ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, 0, fbse.foDelay + size); + + //wordStreamPtr.ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); + if (NULL == wordStreamPtr.base_ptr){ fprintf(stderr, "%s::%d::Handle this\n", __FUNCTION__, __LINE__); exit(11); } - wordStreamPtr.base_ptr = wordStreamPtr.ptr; + wordStreamPtr.ptr = &(wordStreamPtr.base_ptr[fbse.foDelay]); wordStreamPtr.start_block = wordDocBlock->start_block; processOfficeArtBlip(ctx, &wordStreamPtr); #endif @@ -1086,7 +1100,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima while (bytesProcessed < blipStoreRecordHeader.recLen) { //size_t off = offset + bytesProcessed; - size_t off = bytesProcessed; + //size_t off = bytesProcessed; OfficeArtRecordHeader imageHeader; //copy_OfficeArtRecordHeader(&imageHeader, &(ole2Ptr.ptr[off])); copy_OfficeArtRecordHeader(&imageHeader, ole2Ptr.ptr); From a479ef5a0331bf5fd81ab1fb203f2f67cb6d96e3 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 10:15:54 -0700 Subject: [PATCH 78/96] blah --- libclamav/ole2_extract_images.h | 70 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 31d6a9f864..511ee7d0a4 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -685,7 +685,7 @@ static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size } #else -static void saveImageFile( cli_ctx * ctx, ole2_pointer_t * ole2Ptr, size_t size){ +static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_t * ole2Ptr, size_t size){ char *tempfile = NULL; int out_fd = -1; @@ -693,6 +693,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_pointer_t * ole2Ptr, size_t size) size_t bytesWritten = 0; FILE * fp = NULL; static json_object * ary = NULL; + size_t i = 0; if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); @@ -708,6 +709,13 @@ static void saveImageFile( cli_ctx * ctx, ole2_pointer_t * ole2Ptr, size_t size) fprintf(stderr, "%s::%d::Image should be at %lx\n", __FUNCTION__, __LINE__, 0x1400 + (ole2Ptr->ptr - ole2Ptr->base_ptr)); + for (i = 0; i < NUM_DIFAT_ENTRIES; i++) { + if (-1 == ole2Hdr->bat_array[i]) { + break; + } + fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, i, ole2Hdr->bat_array[i], ole2Hdr->bat_array[i]); + fprintf(stderr, "%s::%d::RESERVED BLOCK::%x\n", __FUNCTION__, __LINE__, ((ole2Hdr->bat_array[i]+1) << ole2Hdr->log2_big_block_size)); + } @@ -758,7 +766,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_pointer_t * ole2Ptr, size_t size) * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 * */ -static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr, +static void processOfficeArtBlipGeneric(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr, uint16_t riSingleUID, uint16_t riDoubleUID, uint32_t bytesAfterUIDs) { size_t offset = 16; /* Size of rh*/ @@ -773,26 +781,26 @@ static void processOfficeArtBlipGeneric(cli_ctx * ctx, OfficeArtRecordHeader * r offset += bytesAfterUIDs; /*metafile header*/ ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); - saveImageFile(ctx, ole2Ptr, rh->recLen - offset); + saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2c09e2c4-0513-419f-b5f9-4feb0a71ef32 */ -static void processOfficeArtBlipEMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr) { - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x3d4, 0x3d5, 34) ; +static void processOfficeArtBlipEMF(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr) { + processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x3d4, 0x3d5, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/ee892f04-f001-4531-a34b-67aab3426dcb */ -static void processOfficeArtBlipWMF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x216, 0x217, 34) ; +static void processOfficeArtBlipWMF(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x216, 0x217, 34) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/4b6c5fc5-98cc-445a-8ec7-12b2f2c05b9f */ -static void processOfficeArtBlipPICT(cli_ctx* ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr ){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x542, 0x543, 34) ; +static void processOfficeArtBlipPICT(cli_ctx* ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr ){ + processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x542, 0x543, 34) ; } /*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ -static void processOfficeArtBlipJPEG(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ +static void processOfficeArtBlipJPEG(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ fprintf(stderr, "%s::%d::Entering\n", __FUNCTION__, __LINE__); size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); @@ -809,27 +817,27 @@ fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION_ fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::size = %ld\n", __FUNCTION__, __LINE__, rh->recLen - offset); fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::rh->recLen = %d\n", __FUNCTION__, __LINE__, rh->recLen); ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); - saveImageFile(ctx, ole2Ptr, rh->recLen - offset); + saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/7af7d17e-6ae1-4c43-a3d6-691e6b3b4a45 */ -static void processOfficeArtBlipPNG(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x6e0, 0x6e1, 1) ; +static void processOfficeArtBlipPNG(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x6e0, 0x6e1, 1) ; } /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/1393bf5e-6fa0-4665-b3ec-68199b555656 */ -static void processOfficeArtBlipDIB(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x7a8, 0x7a9, 1) ; +static void processOfficeArtBlipDIB(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x7a8, 0x7a9, 1) ; } -static void processOfficeArtBlipTIFF(cli_ctx * ctx, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - processOfficeArtBlipGeneric(ctx, rh, ole2Ptr, 0x6e4, 0x6e5, 1) ; +static void processOfficeArtBlipTIFF(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ + processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x6e4, 0x6e5, 1) ; } #if 0 static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ #else -static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_pointer_t * ole2Ptr){ +static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_t * ole2Ptr){ #endif size_t offset = 0; @@ -855,27 +863,27 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_pointer_t * ole2Ptr){ ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); switch (rh.recType) { case RECTYPE_OFFICE_ART_BLIP_EMF: - processOfficeArtBlipEMF(ctx, &rh, ole2Ptr); + processOfficeArtBlipEMF(ctx, ole2Hdr, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_WMF : - processOfficeArtBlipWMF(ctx, &rh, ole2Ptr); + processOfficeArtBlipWMF(ctx, ole2Hdr, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_PICT: - processOfficeArtBlipPICT(ctx, &rh, ole2Ptr); + processOfficeArtBlipPICT(ctx, ole2Hdr, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_JPEG: /* fallthrough */ case RECTYPE_OFFICE_ART_BLIP_JPEG2: - processOfficeArtBlipJPEG(ctx, &rh, ole2Ptr); + processOfficeArtBlipJPEG(ctx, ole2Hdr, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_PNG: - processOfficeArtBlipPNG(ctx, &rh, ole2Ptr); + processOfficeArtBlipPNG(ctx, ole2Hdr, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_DIB: - processOfficeArtBlipDIB(ctx, &rh, ole2Ptr); + processOfficeArtBlipDIB(ctx, ole2Hdr, &rh, ole2Ptr); break; case RECTYPE_OFFICE_ART_BLIP_TIFF: - processOfficeArtBlipTIFF(ctx, &rh, ole2Ptr); + processOfficeArtBlipTIFF(ctx, ole2Hdr, &rh, ole2Ptr); break; default: cli_dbgmsg("ERROR Invalid recType 0x%x\n", rh.recType); @@ -916,7 +924,7 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR /* The BLIP is embedded in this record*/ fprintf(stderr, "%s::%d::ADDTO::offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); //processOfficeArtBlip(ctx, &(ole2Ptr->ptr[offset])); - processOfficeArtBlip(ctx, ole2Ptr); + processOfficeArtBlip(ctx, hdr, ole2Ptr); ole2Ptr->ptr = &(ole2Ptr->ptr[fbse.size]); offset += fbse.size; } else { @@ -937,8 +945,9 @@ fprintf(stderr, "%s::%d::added offset = %d (0x%x)\n", __FUNCTION__, __LINE__, of exit(11); } wordStreamPtr.ptr = &(wordStreamPtr.base_ptr[fbse.foDelay]); - wordStreamPtr.start_block = wordDocBlock->start_block; - processOfficeArtBlip(ctx, &wordStreamPtr); + //wordStreamPtr.start_block = wordDocBlock->start_block; + wordStreamPtr.start_block = get_stream_data_offset(hdr, wordDocBlock, wordDocBlock->start_block); + processOfficeArtBlip(ctx, hdr, &wordStreamPtr); #endif /* I don't need to add anything to the offset here, because the actual data is not here. * The data is in a different stream @@ -976,7 +985,8 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima cli_dbgmsg("ERROR: Invalid tableStreamOffset for File Information Block %ld (0x%lx)\n", tableStreamOffset, tableStreamOffset); goto done; } - ole2Ptr.start_block = tableStream->start_block; + //ole2Ptr.start_block = tableStream->start_block; + ole2Ptr.start_block = (uint32_t) tableStreamOffset; ole2Ptr.base_ptr = ole2Ptr.ptr; #endif @@ -1119,7 +1129,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima } else { fprintf(stderr, "%s::%d::SHOULD NOT BE HERE FOR MY TEST\n", __FUNCTION__, __LINE__); exit(1); //bytesProcessed += processOfficeArtBlip(ctx, &(ole2Ptr.ptr[off])); - bytesProcessed += processOfficeArtBlip(ctx, &ole2Ptr); + bytesProcessed += processOfficeArtBlip(ctx, ole2Hdr, &ole2Ptr); } } From 2dbe338c0c80cf4266910136f344a68d0b9e9c40 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 10:18:34 -0700 Subject: [PATCH 79/96] blah --- libclamav/ole2_extract_images.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 511ee7d0a4..d542f89f88 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -408,7 +408,7 @@ typedef struct { typedef struct __attribute__((packed)) { - uint32_t start_block; + size_t stream_file_offset; const uint8_t * base_ptr; @@ -702,7 +702,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ fprintf(stderr, "%s::%d::NEED TO ADD 0X1400\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::ole2Ptr->start_block = %u\n", __FUNCTION__, __LINE__, ole2Ptr->start_block); + fprintf(stderr, "%s::%d::ole2Ptr->start_block = %lu (0x%lx)\n", __FUNCTION__, __LINE__, ole2Ptr->stream_file_offset, ole2Ptr->stream_file_offset); fprintf(stderr, "%s::%d::ole2Ptr->base_ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->base_ptr); fprintf(stderr, "%s::%d::ole2Ptr->ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->ptr); fprintf(stderr, "%s::%d::size = %lu\n", __FUNCTION__, __LINE__, size); @@ -946,7 +946,7 @@ fprintf(stderr, "%s::%d::added offset = %d (0x%x)\n", __FUNCTION__, __LINE__, of } wordStreamPtr.ptr = &(wordStreamPtr.base_ptr[fbse.foDelay]); //wordStreamPtr.start_block = wordDocBlock->start_block; - wordStreamPtr.start_block = get_stream_data_offset(hdr, wordDocBlock, wordDocBlock->start_block); + wordStreamPtr.stream_file_offset = get_stream_data_offset(hdr, wordDocBlock, wordDocBlock->start_block); processOfficeArtBlip(ctx, hdr, &wordStreamPtr); #endif /* I don't need to add anything to the offset here, because the actual data is not here. @@ -986,7 +986,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima goto done; } //ole2Ptr.start_block = tableStream->start_block; - ole2Ptr.start_block = (uint32_t) tableStreamOffset; + ole2Ptr.stream_file_offset = tableStreamOffset; ole2Ptr.base_ptr = ole2Ptr.ptr; #endif From 0f647a197a78b7d6826ef040551a87c36371f445 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 11:56:14 -0700 Subject: [PATCH 80/96] blah --- libclamav/ole2_extract_images.h | 80 +++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d542f89f88..e974cd48e0 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -693,7 +693,9 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ size_t bytesWritten = 0; FILE * fp = NULL; static json_object * ary = NULL; - size_t i = 0; + //size_t i = 0; + + size_t blockSize = 1 << ole2Hdr->log2_big_block_size; if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); @@ -701,25 +703,94 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ } - fprintf(stderr, "%s::%d::NEED TO ADD 0X1400\n", __FUNCTION__, __LINE__); fprintf(stderr, "%s::%d::ole2Ptr->start_block = %lu (0x%lx)\n", __FUNCTION__, __LINE__, ole2Ptr->stream_file_offset, ole2Ptr->stream_file_offset); fprintf(stderr, "%s::%d::ole2Ptr->base_ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->base_ptr); fprintf(stderr, "%s::%d::ole2Ptr->ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->ptr); fprintf(stderr, "%s::%d::size = %lu\n", __FUNCTION__, __LINE__, size); - fprintf(stderr, "%s::%d::Image should be at %lx\n", __FUNCTION__, __LINE__, 0x1400 + (ole2Ptr->ptr - ole2Ptr->base_ptr)); + size_t fileOffset = ole2Ptr->stream_file_offset /*The offset of the document stream in the ole2 file.*/ + + (ole2Ptr->ptr - ole2Ptr->base_ptr); /*The offset of the file data from the start of the document stream */ + + fprintf(stderr, "%s::%d::Image should be at %lx\n", __FUNCTION__, __LINE__, fileOffset); + + fp = fdopen(out_fd, "wb"); + + + size_t lastWritten = 0; + size_t difatIter = 0; + while (bytesWritten < size) { + int difatIdx = -1; + size_t reserveBlock = 0; + size_t toWrite = size - bytesWritten; + size_t increment = 0; + for (; difatIter < NUM_DIFAT_ENTRIES; difatIter++) { + //fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); + if (-1 != ole2Hdr->bat_array[difatIter]) { + size_t block = (ole2Hdr->bat_array[difatIter]+1) << ole2Hdr->log2_big_block_size; + //fprintf(stderr, "%s::%d::i = %ld, block = %lx\n", __FUNCTION__, __LINE__, difatIter, block); + //fprintf(stderr, "%s::%d::i = %ld, fileOffset = %lx\n", __FUNCTION__, __LINE__, difatIter, fileOffset); + //fprintf(stderr, "%s::%d::i = %ld, size = %lx\n", __FUNCTION__, __LINE__, difatIter, size); + if ((block >= fileOffset) && (block <= (fileOffset + size))){ + //fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); + difatIdx = difatIter; + reserveBlock = block; + toWrite = reserveBlock - lastWritten; + increment = blockSize; + fprintf(stderr, "%s::%d::FOUND ONE at idx %ld\n", __FUNCTION__, __LINE__, difatIter); + } + } + //ole2Hdr->bat_array[i] = -1; + if (-1 != difatIdx) { + difatIter++; + break; + } + } + fprintf(stderr, "%s::%d::difatIdx = %d\n", __FUNCTION__, __LINE__, difatIdx); + fprintf(stderr, "%s::%d::Write %lu (0x%lx) bytes starting at %lu (0x%lx)\n", __FUNCTION__, __LINE__, toWrite, toWrite, lastWritten, lastWritten ); + bytesWritten += toWrite; + lastWritten += toWrite + increment; + } + +#if 0 + size_t processed = 0; + size_t toWrite = size; for (i = 0; i < NUM_DIFAT_ENTRIES; i++) { if (-1 == ole2Hdr->bat_array[i]) { break; } fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, i, ole2Hdr->bat_array[i], ole2Hdr->bat_array[i]); fprintf(stderr, "%s::%d::RESERVED BLOCK::%x\n", __FUNCTION__, __LINE__, ((ole2Hdr->bat_array[i]+1) << ole2Hdr->log2_big_block_size)); + + size_t reserveBlock = (ole2Hdr->bat_array[i]+1) << ole2Hdr->log2_big_block_size; + if ((reserveBlock >= fileOffset) && (reserveBlock <= (fileOffset + size))){ + fprintf(stderr, "%s::%d::FOUND OUR PROBLEM BLOCK!!!!!\n", __FUNCTION__, __LINE__); + fprintf(stderr, "%s::%d::write %ld bytes\n", __FUNCTION__, __LINE__, reserveBlock - fileOffset); + fprintf(stderr, "%s::%d::Need to also allocate more fmap\n", __FUNCTION__, __LINE__); + toWrite = reserveBlock - fileOffset; + } + + while (bytesWritten < toWrite) { + int ret = fwrite(&(ole2Ptr->ptr[bytesWritten + processed]), 1, toWrite - bytesWritten, fp); + if (ret > 0) { + bytesWritten += ret; + } else { + break; + } + } + + if (toWrite != size) { + processed += blockSize; + //toWrite + } + } +#endif - fp = fdopen(out_fd, "wb"); +#if 1 + bytesWritten = 0; while (bytesWritten < size) { int ret = fwrite(&(ole2Ptr->ptr[bytesWritten]), 1, size - bytesWritten, fp); if (ret > 0) { @@ -728,6 +799,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ break; } } +#endif if (bytesWritten != size) { cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); From 36de7c0dbc0aa022099db40fc6ee319e347f99d2 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:13:31 -0700 Subject: [PATCH 81/96] finally --- libclamav/ole2_extract_images.h | 64 ++++++++++++--------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index e974cd48e0..d284bbf884 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -725,19 +725,25 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ size_t toWrite = size - bytesWritten; size_t increment = 0; for (; difatIter < NUM_DIFAT_ENTRIES; difatIter++) { - //fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); if (-1 != ole2Hdr->bat_array[difatIter]) { + size_t block = (ole2Hdr->bat_array[difatIter]+1) << ole2Hdr->log2_big_block_size; - //fprintf(stderr, "%s::%d::i = %ld, block = %lx\n", __FUNCTION__, __LINE__, difatIter, block); - //fprintf(stderr, "%s::%d::i = %ld, fileOffset = %lx\n", __FUNCTION__, __LINE__, difatIter, fileOffset); - //fprintf(stderr, "%s::%d::i = %ld, size = %lx\n", __FUNCTION__, __LINE__, difatIter, size); + if ((block >= fileOffset) && (block <= (fileOffset + size))){ - //fprintf(stderr, "%s::%d\n", __FUNCTION__, __LINE__); difatIdx = difatIter; reserveBlock = block; - toWrite = reserveBlock - lastWritten; + //toWrite = reserveBlock - lastWritten; + toWrite = reserveBlock - fileOffset; increment = blockSize; fprintf(stderr, "%s::%d::FOUND ONE at idx %ld\n", __FUNCTION__, __LINE__, difatIter); + + + /*Get more space from the fmap to account for the extra block*/ + const uint8_t * ptr = fmap_need_off_once(ole2Hdr->map, ole2Ptr->stream_file_offset, (ole2Ptr->ptr - ole2Ptr->base_ptr) + increment + size); + if (ptr != ole2Ptr->base_ptr) { + ole2Ptr->ptr = &(ptr[ole2Ptr->ptr - ole2Ptr->base_ptr]); + ole2Ptr->base_ptr = ptr; + } } } //ole2Hdr->bat_array[i] = -1; @@ -746,50 +752,26 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ break; } } - fprintf(stderr, "%s::%d::difatIdx = %d\n", __FUNCTION__, __LINE__, difatIdx); + //fprintf(stderr, "%s::%d::difatIdx = %d\n", __FUNCTION__, __LINE__, difatIdx); fprintf(stderr, "%s::%d::Write %lu (0x%lx) bytes starting at %lu (0x%lx)\n", __FUNCTION__, __LINE__, toWrite, toWrite, lastWritten, lastWritten ); - bytesWritten += toWrite; - lastWritten += toWrite + increment; - } -#if 0 - size_t processed = 0; - size_t toWrite = size; - for (i = 0; i < NUM_DIFAT_ENTRIES; i++) { - if (-1 == ole2Hdr->bat_array[i]) { - break; - } - fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, i, ole2Hdr->bat_array[i], ole2Hdr->bat_array[i]); - fprintf(stderr, "%s::%d::RESERVED BLOCK::%x\n", __FUNCTION__, __LINE__, ((ole2Hdr->bat_array[i]+1) << ole2Hdr->log2_big_block_size)); - - size_t reserveBlock = (ole2Hdr->bat_array[i]+1) << ole2Hdr->log2_big_block_size; - if ((reserveBlock >= fileOffset) && (reserveBlock <= (fileOffset + size))){ - fprintf(stderr, "%s::%d::FOUND OUR PROBLEM BLOCK!!!!!\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::write %ld bytes\n", __FUNCTION__, __LINE__, reserveBlock - fileOffset); - fprintf(stderr, "%s::%d::Need to also allocate more fmap\n", __FUNCTION__, __LINE__); - toWrite = reserveBlock - fileOffset; - } - - while (bytesWritten < toWrite) { - int ret = fwrite(&(ole2Ptr->ptr[bytesWritten + processed]), 1, toWrite - bytesWritten, fp); + size_t loopWritten = 0; + while (loopWritten < toWrite) { + fprintf(stderr, "%s::%d::FTT::%lu (0x%lx)\n", __FUNCTION__, __LINE__, lastWritten + loopWritten, lastWritten + loopWritten); + dump_some(__FUNCTION__, __LINE__, &(ole2Ptr->ptr[lastWritten + loopWritten]), 25); + int ret = fwrite(&(ole2Ptr->ptr[lastWritten + loopWritten]), 1, toWrite - loopWritten, fp); if (ret > 0) { - bytesWritten += ret; + loopWritten += ret; } else { + fprintf(stderr, "%s::%d::What happened here, exiting!!!!\n", __FUNCTION__, __LINE__); exit(11); break; } } - - if (toWrite != size) { - processed += blockSize; - //toWrite - } - + bytesWritten += toWrite; + lastWritten += toWrite + increment; } -#endif - - -#if 1 +#if 0 bytesWritten = 0; while (bytesWritten < size) { int ret = fwrite(&(ole2Ptr->ptr[bytesWritten]), 1, size - bytesWritten, fp); From 70237d1c25fcc048b97245ac69e5ea4dbcac5ccc Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:18:35 -0700 Subject: [PATCH 82/96] blah --- libclamav/ole2_extract_images.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index d284bbf884..ac62ecbced 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -693,6 +693,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ size_t bytesWritten = 0; FILE * fp = NULL; static json_object * ary = NULL; + size_t totalIncrement = 0; //size_t i = 0; size_t blockSize = 1 << ole2Hdr->log2_big_block_size; @@ -735,6 +736,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ //toWrite = reserveBlock - lastWritten; toWrite = reserveBlock - fileOffset; increment = blockSize; + totalIncrement += increment; fprintf(stderr, "%s::%d::FOUND ONE at idx %ld\n", __FUNCTION__, __LINE__, difatIter); @@ -798,7 +800,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ } done: - ole2Ptr->ptr = &(ole2Ptr->ptr[size]); + ole2Ptr->ptr = &(ole2Ptr->ptr[size + totalIncrement]); fprintf(stderr, "%s::%d::TODO: increment pointer by the blocks skipped also!!!!\n", __FUNCTION__, __LINE__); From 687ae6c59d0cae91856b9a431adac6079caded49 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:21:57 -0700 Subject: [PATCH 83/96] blah --- libclamav/ole2_extract_images.h | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index ac62ecbced..170387e22a 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -1165,10 +1165,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima ole2Ptr.ptr = &(ole2Ptr.ptr[offset]); while (bytesProcessed < blipStoreRecordHeader.recLen) { - //size_t off = offset + bytesProcessed; - //size_t off = bytesProcessed; OfficeArtRecordHeader imageHeader; - //copy_OfficeArtRecordHeader(&imageHeader, &(ole2Ptr.ptr[off])); copy_OfficeArtRecordHeader(&imageHeader, ole2Ptr.ptr); uint8_t recVer = getRecVer(&imageHeader); @@ -1176,15 +1173,8 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima /* OfficeArtFBSE * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -#if 0 - bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &(ole2Ptr.ptr[off]), wordDocBlock); -#else -// ole2Ptr.ptr = &(ole2Ptr.ptr[off]); bytesProcessed += processOfficeArtFBSE(ctx, ole2Hdr, &imageHeader, &ole2Ptr, wordDocBlock); -#endif } else { -fprintf(stderr, "%s::%d::SHOULD NOT BE HERE FOR MY TEST\n", __FUNCTION__, __LINE__); exit(1); - //bytesProcessed += processOfficeArtBlip(ctx, &(ole2Ptr.ptr[off])); bytesProcessed += processOfficeArtBlip(ctx, ole2Hdr, &ole2Ptr); } } @@ -1234,30 +1224,7 @@ void ole2_process_image_directory( cli_ctx * ctx, ole2_header_t * hdr, ole2_imag } } -#if 0 - /*Call Extract */ - /*This offset is an actual offset of the table stream in the file.*/ - size_t offset = get_stream_data_offset(hdr, tableStream, tableStream->start_block); -fprintf(stderr, "\n%s::%d::ADDTO::offset (start block of table stream)= %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); - /*TODO: Fix hardcoded 4k. Change it to read 512 bytes (block size) at a time, and continue reading. - */ - ptr = fmap_need_off_once(hdr->map, offset, 4096); -fprintf(stderr, "%s::%d::tableStream->size = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->size, tableStream->size); -fprintf(stderr, "%s::%d::tableStream->user_flags = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->user_flags, tableStream->user_flags); -fprintf(stderr, "%s::%d::tableStream->type = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->type, tableStream->type); -fprintf(stderr, "%s::%d::tableStream->next = %d (0x%x)\n", __FUNCTION__, __LINE__, tableStream->next, tableStream->next); -fprintf(stderr, "%s::%d::Fix hardcoded 4k, probably with tableStream->size or block size\n", __FUNCTION__, __LINE__); - if (NULL == ptr) { - cli_dbgmsg("ERROR: Invalid offset for File Information Block %ld (0x%lx)\n", offset, offset); - goto done; - } - dump_some(__FUNCTION__, __LINE__, ptr, 25); - - ole2_extract_images(ctx, hdr, &(directory->fibRgFcLcb97Header), ptr, &(directory->word_block)); -#else ole2_extract_images(ctx, hdr, directory, tableStream); - -#endif } done: return ; From 5cd680fb2cdfae7d146947d97a404b35b5a4746f Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:33:09 -0700 Subject: [PATCH 84/96] blah --- libclamav/ole2_extract_images.h | 134 +------------------------------- 1 file changed, 2 insertions(+), 132 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 170387e22a..2e376937d5 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -2,16 +2,6 @@ #define OLE2_EXTRACT_IMAGES_H_ -void dump_some(const char * const func, const size_t line, const uint8_t * const ptr, size_t cnt) { - size_t i; - fprintf(stderr, "%s::%ld::", func, line); - for (i = 0; i < cnt; i++){ - fprintf(stderr, "%02x ", ptr[i]); - } - fprintf(stderr, "\n"); -} - - /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-doc/0c9df81f-98d0-454e-ad84-b612cd05b1a4 */ typedef struct __attribute__((packed)) { uint32_t fcStshfOrig; @@ -414,8 +404,6 @@ typedef struct __attribute__((packed)) { const uint8_t * ptr; -// size_t offset; - } ole2_pointer_t; /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/5dc1b9ed-818c-436f-8a4f-905a7ebb1ba9 */ @@ -446,10 +434,7 @@ static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, cons const uint8_t * ptr = NULL; uint32_t offset = get_stream_data_offset(hdr, block, block->start_block); - fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT (second time only)::start of worddocument stream offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); offset += delay; - fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT (second time only)::offset + delay = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); - fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT (second time only)::size = %ld (0x%lx)\n", __FUNCTION__, __LINE__, size, size); if ((size_t)(hdr->m_length) < (size_t)(offset + sizeof(fib_base_t))) { cli_dbgmsg("ERROR: Invalid offset for stream %d (0x%x)\n", offset, offset); goto done; @@ -461,7 +446,6 @@ static const uint8_t* load_pointer_to_stream_from_fmap(ole2_header_t * hdr, cons cli_dbgmsg("ERROR: Invalid offset for File Information Block %d (0x%x)\n", offset, offset); goto done; } - dump_some(__FUNCTION__, __LINE__, ptr, 25); done: return ptr; @@ -586,8 +570,6 @@ static bool getFibRgFcLcb97Header( const property_t *word_block, ole2_header_t * } - - typedef struct __attribute__((packed)) { uint32_t spidMax; uint32_t cidcl; @@ -638,53 +620,6 @@ static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * c dst->foDelay = ole2_endian_convert_32(dst->foDelay); } -#if 0 -static void saveImageFile( cli_ctx * ctx, const uint8_t * const ptr, size_t size){ - - char *tempfile = NULL; - int out_fd = -1; - cl_error_t ret ; - size_t bytesWritten = 0; - FILE * fp = NULL; - static json_object * ary = NULL; - - if ((ret = cli_gentempfd_with_prefix(ctx->sub_tmpdir, "ole2_images", &tempfile, &out_fd)) != CL_SUCCESS) { - cli_dbgmsg("[ole2_process_image_directory] Failed to open output file descriptor\n"); - goto done; - } - - fp = fdopen(out_fd, "wb"); - while (bytesWritten < size) { - int ret = fwrite(&(ptr[bytesWritten]), 1, size - bytesWritten, fp); - if (ret > 0) { - bytesWritten += ret; - } else { - break; - } - } - - if (bytesWritten != size) { - cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); - } - - if (SCAN_COLLECT_METADATA && ctx->wrkproperty != NULL){ - if (NULL == ary) { -#define OLE2_EXTRACTED_IMAGES_JSON_KEY "OLE2_IMAGES" - ary = cli_jsonarray(ctx->wrkproperty, OLE2_EXTRACTED_IMAGES_JSON_KEY); - } - if (ary) { - cli_jsonstr(ary, NULL, tempfile); - } - } - -done: - if (tempfile && !ctx->engine->keeptmp) { - remove(tempfile); - } - CLI_FREE_AND_SET_NULL(tempfile); - -} -#else static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_t * ole2Ptr, size_t size){ char *tempfile = NULL; @@ -694,7 +629,6 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ FILE * fp = NULL; static json_object * ary = NULL; size_t totalIncrement = 0; - //size_t i = 0; size_t blockSize = 1 << ole2Hdr->log2_big_block_size; @@ -703,21 +637,11 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ goto done; } - - fprintf(stderr, "%s::%d::ole2Ptr->start_block = %lu (0x%lx)\n", __FUNCTION__, __LINE__, ole2Ptr->stream_file_offset, ole2Ptr->stream_file_offset); - fprintf(stderr, "%s::%d::ole2Ptr->base_ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->base_ptr); - fprintf(stderr, "%s::%d::ole2Ptr->ptr = %p\n", __FUNCTION__, __LINE__, ole2Ptr->ptr); - fprintf(stderr, "%s::%d::size = %lu\n", __FUNCTION__, __LINE__, size); - - size_t fileOffset = ole2Ptr->stream_file_offset /*The offset of the document stream in the ole2 file.*/ + (ole2Ptr->ptr - ole2Ptr->base_ptr); /*The offset of the file data from the start of the document stream */ - fprintf(stderr, "%s::%d::Image should be at %lx\n", __FUNCTION__, __LINE__, fileOffset); - fp = fdopen(out_fd, "wb"); - size_t lastWritten = 0; size_t difatIter = 0; while (bytesWritten < size) { @@ -727,18 +651,13 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ size_t increment = 0; for (; difatIter < NUM_DIFAT_ENTRIES; difatIter++) { if (-1 != ole2Hdr->bat_array[difatIter]) { - size_t block = (ole2Hdr->bat_array[difatIter]+1) << ole2Hdr->log2_big_block_size; - if ((block >= fileOffset) && (block <= (fileOffset + size))){ difatIdx = difatIter; reserveBlock = block; - //toWrite = reserveBlock - lastWritten; toWrite = reserveBlock - fileOffset; increment = blockSize; totalIncrement += increment; - fprintf(stderr, "%s::%d::FOUND ONE at idx %ld\n", __FUNCTION__, __LINE__, difatIter); - /*Get more space from the fmap to account for the extra block*/ const uint8_t * ptr = fmap_need_off_once(ole2Hdr->map, ole2Ptr->stream_file_offset, (ole2Ptr->ptr - ole2Ptr->base_ptr) + increment + size); @@ -748,19 +667,14 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ } } } - //ole2Hdr->bat_array[i] = -1; if (-1 != difatIdx) { difatIter++; break; } } - //fprintf(stderr, "%s::%d::difatIdx = %d\n", __FUNCTION__, __LINE__, difatIdx); - fprintf(stderr, "%s::%d::Write %lu (0x%lx) bytes starting at %lu (0x%lx)\n", __FUNCTION__, __LINE__, toWrite, toWrite, lastWritten, lastWritten ); size_t loopWritten = 0; while (loopWritten < toWrite) { - fprintf(stderr, "%s::%d::FTT::%lu (0x%lx)\n", __FUNCTION__, __LINE__, lastWritten + loopWritten, lastWritten + loopWritten); - dump_some(__FUNCTION__, __LINE__, &(ole2Ptr->ptr[lastWritten + loopWritten]), 25); int ret = fwrite(&(ole2Ptr->ptr[lastWritten + loopWritten]), 1, toWrite - loopWritten, fp); if (ret > 0) { loopWritten += ret; @@ -773,18 +687,6 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ lastWritten += toWrite + increment; } -#if 0 - bytesWritten = 0; - while (bytesWritten < size) { - int ret = fwrite(&(ole2Ptr->ptr[bytesWritten]), 1, size - bytesWritten, fp); - if (ret > 0) { - bytesWritten += ret; - } else { - break; - } - } -#endif - if (bytesWritten != size) { cli_dbgmsg("ERROR unable to write to '%s'\n", tempfile); } @@ -802,15 +704,12 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ done: ole2Ptr->ptr = &(ole2Ptr->ptr[size + totalIncrement]); - fprintf(stderr, "%s::%d::TODO: increment pointer by the blocks skipped also!!!!\n", __FUNCTION__, __LINE__); - if (tempfile && !ctx->engine->keeptmp) { remove(tempfile); } CLI_FREE_AND_SET_NULL(tempfile); } -#endif /*All these structures (except JPEG) are exactly the same, with the exception of the recInst values for 1 or 2 UIDs, @@ -857,7 +756,6 @@ static void processOfficeArtBlipPICT(cli_ctx* ctx, ole2_header_t * ole2Hdr, Offi /*https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/704b3ec5-3e3f-425f-b2f7-a090cc68e624*/ static void processOfficeArtBlipJPEG(cli_ctx * ctx, ole2_header_t * ole2Hdr, OfficeArtRecordHeader * rh, ole2_pointer_t * ole2Ptr){ - fprintf(stderr, "%s::%d::Entering\n", __FUNCTION__, __LINE__); size_t offset = 16; /* Size of rh*/ uint16_t recInst = getRecInst(rh); @@ -869,9 +767,6 @@ static void processOfficeArtBlipJPEG(cli_ctx * ctx, ole2_header_t * ole2Hdr, Off } offset += 1; /*metafile header*/ -fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::offset = %ld (0x%lx)\n", __FUNCTION__, __LINE__, offset, offset); -fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::size = %ld\n", __FUNCTION__, __LINE__, rh->recLen - offset); -fprintf(stderr, "%s::%d::ADDTO::SHOULDBEIT::rh->recLen = %d\n", __FUNCTION__, __LINE__, rh->recLen); ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } @@ -890,11 +785,7 @@ static void processOfficeArtBlipTIFF(cli_ctx * ctx, ole2_header_t * ole2Hdr, Off processOfficeArtBlipGeneric(ctx, ole2Hdr, rh, ole2Ptr, 0x6e4, 0x6e5, 1) ; } -#if 0 -static size_t processOfficeArtBlip(cli_ctx * ctx, const uint8_t * const ptr){ -#else static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_t * ole2Ptr){ -#endif size_t offset = 0; OfficeArtRecordHeader rh; @@ -954,7 +845,6 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ /* * https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/2f2d7f5e-d5c4-4cb7-b230-59b3fe8f10d6 */ -//static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, const uint8_t * const ptr, property_t * wordDocBlock) { static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtRecordHeader * imageHeader, ole2_pointer_t * ole2Ptr, property_t * wordDocBlock) { OfficeArtFBSEKnown fbse; @@ -978,33 +868,22 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ -fprintf(stderr, "%s::%d::ADDTO::offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); - //processOfficeArtBlip(ctx, &(ole2Ptr->ptr[offset])); processOfficeArtBlip(ctx, hdr, ole2Ptr); ole2Ptr->ptr = &(ole2Ptr->ptr[fbse.size]); offset += fbse.size; } else { /* The BLIP is in the 'WordDocument' stream. */ size_t size = fbse.size; -fprintf(stderr, "%s::%d::ADDTO::fbse.foDelay = %d (0x%x)\n", __FUNCTION__, __LINE__, fbse.foDelay, fbse.foDelay); -fprintf(stderr, "%s::%d::added offset = %d (0x%x)\n", __FUNCTION__, __LINE__, offset, offset); -#if 0 - const uint8_t * const ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); - processOfficeArtBlip(ctx, ptr); -#else ole2_pointer_t wordStreamPtr = {0}; wordStreamPtr.base_ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, 0, fbse.foDelay + size); - //wordStreamPtr.ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, fbse.foDelay, size); if (NULL == wordStreamPtr.base_ptr){ fprintf(stderr, "%s::%d::Handle this\n", __FUNCTION__, __LINE__); exit(11); } wordStreamPtr.ptr = &(wordStreamPtr.base_ptr[fbse.foDelay]); - //wordStreamPtr.start_block = wordDocBlock->start_block; wordStreamPtr.stream_file_offset = get_stream_data_offset(hdr, wordDocBlock, wordDocBlock->start_block); processOfficeArtBlip(ctx, hdr, &wordStreamPtr); -#endif /* I don't need to add anything to the offset here, because the actual data is not here. * The data is in a different stream */ @@ -1019,18 +898,10 @@ size_t get_block_size(ole2_header_t * ole2Hdr) { return 1 << ole2Hdr->log2_big_block_size; } -//ptr is a pointer to the head of the table stream. -#if 0 -static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, FibRgFcLcb97 * header, const uint8_t * ptr, property_t * wordDocBlock) { -#else static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_image_directory_t * directory, property_t * tableStream) { FibRgFcLcb97 * header = &(directory->fibRgFcLcb97Header); property_t * wordDocBlock = &(directory->word_block); -#if 0 - const uint8_t * ptr = NULL; -#else ole2_pointer_t ole2Ptr = {0}; -#endif /*This offset is an actual offset of the table stream in the file.*/ size_t tableStreamOffset = get_stream_data_offset(ole2Hdr, tableStream, tableStream->start_block); @@ -1044,7 +915,6 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima //ole2Ptr.start_block = tableStream->start_block; ole2Ptr.stream_file_offset = tableStreamOffset; ole2Ptr.base_ptr = ole2Ptr.ptr; -#endif size_t offset = header->fcDggInfo; @@ -1093,14 +963,13 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima OfficeArtRecordHeader blipStoreRecordHeader; copy_OfficeArtRecordHeader(&blipStoreRecordHeader, &(ole2Ptr.ptr[offset])); - fprintf(stderr, "%s::%d::total needed = %lu\n", __FUNCTION__, __LINE__, offset + blipStoreRecordHeader.recLen); - /*Allocate the full number of bytes needed for headers.*/ size_t total_needed = 0; while (total_needed < (offset + blipStoreRecordHeader.recLen)) { total_needed += get_block_size(ole2Hdr); } +#if 0 size_t idx; for (idx = 0; idx < NUM_DIFAT_ENTRIES; idx++) { if (-1 == ole2Hdr->bat_array[idx]) { @@ -1135,6 +1004,7 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima } } +#endif ole2Ptr.ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); if (NULL == ole2Ptr.ptr) { From ef8604005e9b4707f70057eee30dda54fef3999b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:34:01 -0700 Subject: [PATCH 85/96] blah --- libclamav/ole2_extract_images.h | 37 --------------------------------- 1 file changed, 37 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 2e376937d5..fd9d31159d 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -969,43 +969,6 @@ static void ole2_extract_images(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ima total_needed += get_block_size(ole2Hdr); } -#if 0 - size_t idx; - for (idx = 0; idx < NUM_DIFAT_ENTRIES; idx++) { - if (-1 == ole2Hdr->bat_array[idx]) { - break; - } - - uint32_t reserved = (ole2Hdr->bat_array[idx]+1) << ole2Hdr->log2_big_block_size; - if ((reserved >= tableStreamOffset) && (reserved <= tableStreamOffset + total_needed)){ - fprintf(stderr, "%s::%d::total_needed crosses over a FAT block that must be skipped!!!!!\n", __FUNCTION__, __LINE__); - fprintf(stderr, "%s::%d::This is not currently handled!!!!!\n", __FUNCTION__, __LINE__); - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - //TODO::HANDLE THIS CASE!!!!! - exit(11); - } - - } -#endif - ole2Ptr.ptr = fmap_need_off_once(ole2Hdr->map, tableStreamOffset, total_needed); if (NULL == ole2Ptr.ptr) { cli_dbgmsg("ERROR: Invalid offset for OfficeArtRecordHeader%ld (0x%lx)\n", total_needed, total_needed); From 7c6bc4941ac10d790c6698f32183be2b1154949b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:35:39 -0700 Subject: [PATCH 86/96] bahl --- libclamav/ole2_extract.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 810ef94b67..273df839bd 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -734,12 +734,9 @@ static size_t get_stream_data_offset(ole2_header_t *hdr, const property_t *word_ size_t fib_offset = 0; if (word_block->size < MINISTREAM_CUTOFF_SIZE) { - fprintf(stderr, "%s::%d::In the mini stream\n", __FUNCTION__, __LINE__); fib_offset = offset + sector_size * hdr->sbat_root_start; - //fib_offset += (word_block->start_block * (1 << hdr->log2_small_block_size)); fib_offset += (sector * (1 << hdr->log2_small_block_size)); } else { - fprintf(stderr, "%s::%d::Not in the mini stream\n", __FUNCTION__, __LINE__); fib_offset = offset + sector_size * sector; } @@ -982,7 +979,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t } ole2_listmsg("reading prop block\n"); -//fprintf(stderr, "%s::%d::%p\n", __FUNCTION__, __LINE__, &(prop_block[idx])); prop_block[idx].name_size = ole2_endian_convert_16(prop_block[idx].name_size); prop_block[idx].prev = ole2_endian_convert_32(prop_block[idx].prev); prop_block[idx].next = ole2_endian_convert_32(prop_block[idx].next); @@ -995,8 +991,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t prop_block[idx].start_block = ole2_endian_convert_32(prop_block[idx].start_block); prop_block[idx].size = ole2_endian_convert_32(prop_block[idx].size); - //fprintf(stderr, "%s::%d::start_block = %d (0x%x)\n", __FUNCTION__, __LINE__, prop_block[idx].start_block, prop_block[idx].start_block); - if ((64 < prop_block[idx].name_size) || (prop_block[idx].name_size % 2)) { cli_dbgmsg("ERROR: Invalid name_size %d\n", prop_block[idx].name_size); continue; @@ -1020,12 +1014,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t if (pImageDirectory) { memcpy(&(pImageDirectory->table_stream_1_block), &(prop_block[idx]), sizeof(pImageDirectory->table_stream_1_block)); pImageDirectory->table_stream_1_initialized = true; - - - fprintf(stderr, "%s::%d::prev = %d (0x%x)\n", __FUNCTION__, __LINE__, pImageDirectory->table_stream_1_block.prev, pImageDirectory->table_stream_1_block.prev); - fprintf(stderr, "%s::%d::next = %d (0x%x)\n", __FUNCTION__, __LINE__, pImageDirectory->table_stream_1_block.next, pImageDirectory->table_stream_1_block.next); - fprintf(stderr, "%s::%d::child = %d (0x%x)\n", __FUNCTION__, __LINE__, pImageDirectory->table_stream_1_block.child, pImageDirectory->table_stream_1_block.child); - } } else if (0 == ole2_cmp_name(prop_block[idx].name, prop_block[idx].name_size, "0Table")) { if (pImageDirectory) { @@ -2938,6 +2926,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil int32_t bat_array[109] __attribute__((packed)); //DIFAT #endif +#if 0 { size_t andy; fprintf(stderr, "%s::%d::minor_version = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.minor_version, hdr.minor_version); @@ -2979,6 +2968,7 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil } +#endif From 428e7b7d754831b922018212190afaac2795c571 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:36:46 -0700 Subject: [PATCH 87/96] bahl --- libclamav/ole2_extract_images.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index fd9d31159d..624045b056 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -889,8 +889,6 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR */ } - fprintf(stderr, "%s::%d::Incrementing by offset = %d\n", __FUNCTION__, __LINE__, offset); -// ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); return offset; } From 3520989c744a265fa38f2aa27224b1a860afca61 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:38:35 -0700 Subject: [PATCH 88/96] bahl --- libclamav/ole2_extract.c | 90 ---------------------------------------- 1 file changed, 90 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 273df839bd..9bbf83a739 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -2885,96 +2885,6 @@ cl_error_t cli_ole2_extract(const char *dirname, cli_ctx *ctx, struct uniq **fil hdr.xbat_start = ole2_endian_convert_32(hdr.xbat_start); hdr.xbat_count = ole2_endian_convert_32(hdr.xbat_count); - - -#if 0 - int32_t bat_count __attribute__((packed)); NUMBER of directory sectors - int32_t prop_start __attribute__((packed)); number of fat sectors - - uint32_t signature __attribute__((packed)); first directory sector location -#endif - //fprintf(stderr, "%s::%d::Number of directory sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.bat_count, hdr.bat_count); - //fprintf(stderr, "%s::%d::Number of FAT sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); - //fprintf(stderr, "%s::%d::Transaction sector Number = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); - - //fprintf(stderr, "%s::%d::First directory sector = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature * (1 << hdr.log2_big_block_size), hdr.signature * (1 << hdr.log2_big_block_size)); - -#if 0 - unsigned char magic[8]; /* should be: 0xd0cf11e0a1b11ae1 */ - unsigned char clsid[16]; - uint16_t minor_version __attribute__((packed)); - uint16_t dll_version __attribute__((packed)); //major version - int16_t byte_order __attribute__((packed)); /* -2=intel */ - - uint16_t log2_big_block_size __attribute__((packed)); /* usually 9 (2^9 = 512) */ //sector shift - uint16_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ //mini sector shift - uint8_t reserved[6] __attribute__((packed)); - uint32_t num_directory_sectors __attribute__((packed)); - int32_t bat_count __attribute__((packed)); //num fat sectors - int32_t prop_start __attribute__((packed)); //first directory sector location - - uint32_t signature __attribute__((packed)); //transaction signature number - uint32_t sbat_cutoff __attribute__((packed)); /* cutoff for files held - * in small blocks - * (4096) */ - //ministream cutoff size - - int32_t sbat_start __attribute__((packed)); //first mini fat sector location - int32_t sbat_block_count __attribute__((packed)); //number of minifat sectors - int32_t xbat_start __attribute__((packed)); //first DIFAT sector location - int32_t xbat_count __attribute__((packed)); //number of difat sectors - int32_t bat_array[109] __attribute__((packed)); //DIFAT - -#endif -#if 0 - { - size_t andy; - fprintf(stderr, "%s::%d::minor_version = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.minor_version, hdr.minor_version); - fprintf(stderr, "%s::%d::dll_version = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.dll_version, hdr.dll_version); - fprintf(stderr, "%s::%d::byte_order = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.byte_order, hdr.byte_order); - fprintf(stderr, "%s::%d::log2_big_block_size = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.log2_big_block_size, hdr.log2_big_block_size); - fprintf(stderr, "%s::%d::log2_small_block_size = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.log2_small_block_size, hdr.log2_small_block_size); - fprintf(stderr, "%s::%d::reserved::", __FUNCTION__, __LINE__); - for (andy = 0; andy < 6; andy++) { - fprintf(stderr, "%02x ", hdr.reserved[andy]); - } - fprintf(stderr, "\n"); - - - fprintf(stderr, "%s::%d::num_directory_sectors = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.num_directory_sectors, hdr.num_directory_sectors); - fprintf(stderr, "%s::%d::bat_count = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.bat_count, hdr.bat_count); - fprintf(stderr, "%s::%d::prop_start = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.prop_start, hdr.prop_start); - fprintf(stderr, "%s::%d::signature = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.signature, hdr.signature); - fprintf(stderr, "%s::%d::sbat_cutoff = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.sbat_cutoff, hdr.sbat_cutoff); - fprintf(stderr, "%s::%d::sbat_start = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.sbat_start, hdr.sbat_start); - fprintf(stderr, "%s::%d::sbat_block_count = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.sbat_block_count, hdr.sbat_block_count); - fprintf(stderr, "%s::%d::xbat_start = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.xbat_start, hdr.xbat_start); - fprintf(stderr, "%s::%d::xbat_count = %d (0x%x)\n", __FUNCTION__, __LINE__, hdr.xbat_count, hdr.xbat_count); - - /* - * The random block that we *don't* want that is stuffed in the middel is in the DIFAT. - * Need to skip it (1 << log2_big_block_size) and keep going. - */ - for (andy = 0; andy < NUM_DIFAT_ENTRIES; andy++) { - if (-1 == hdr.bat_array[andy]) { - break; - } - fprintf(stderr, "%s::%d::difat[%ld] = %d (0x%x)\n", __FUNCTION__, __LINE__, andy, hdr.bat_array[andy], hdr.bat_array[andy]); - fprintf(stderr, "%s::%d::RESERVED BLOCK::%x\n", __FUNCTION__, __LINE__, ((hdr.bat_array[andy]+1) << hdr.log2_big_block_size)); - } - - - - - - } -#endif - - - - - - hdr.sbat_root_start = -1; hdr.bitset = cli_bitset_init(); From a723da41a726d3288ab9eaa263adcfc19933c1f0 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:41:45 -0700 Subject: [PATCH 89/96] blah --- libclamav/ole2_extract.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 9bbf83a739..314432e530 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -86,21 +86,9 @@ typedef struct __attribute__((packed)) ole2_header_tag { int16_t byte_order __attribute__((packed)); /* -2=intel */ uint16_t log2_big_block_size __attribute__((packed)); /* usually 9 (2^9 = 512) */ //sector shift -#if 0 - uint32_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ - /* - * This is technically incorrect. log2_small_block_size should be a uint16_t, and reserved should - * be 6 bytes. This makes everything line up, but could potentially cause issues when switching byte order - * for log2_small_block_size. Consider changing. - */ - - int32_t reserved[2] __attribute__((packed)); -#else - uint16_t log2_small_block_size __attribute__((packed)); /* usually 6 (2^6 = 64) */ //mini sector shift uint8_t reserved[6]; -#endif uint32_t num_directory_sectors __attribute__((packed)); //If dll_version is 3, this must be 0 int32_t bat_count __attribute__((packed)); //num fat sectors int32_t prop_start __attribute__((packed)); //first directory sector location From 178a3469167ceb8e3bb993e460b7b071fe8aec6a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:46:35 -0700 Subject: [PATCH 90/96] blah --- libclamav/ole2_extract.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c index 314432e530..5d892baa1b 100644 --- a/libclamav/ole2_extract.c +++ b/libclamav/ole2_extract.c @@ -900,15 +900,6 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t cl_error_t ret; char *name; int toval = 0; -#if 0 - FibRgFcLcb97 fibRgFcLcb97Header = {0}; - bool bFibRgFcLcb97HeaderInitialized = false; - property_t wordDocumentBlock = {0}; - property_t TableStream1 = {0}; - property_t TableStream0 = {0}; - bool TableStream1Initialized = false; - bool TableStream0Initialized = false; -#endif ole2_listmsg("ole2_walk_property_tree() called\n"); ole2_list_init(&node_list); From 6fce4f086857c62c4a4127a77222f4851c53dc62 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 13:53:06 -0700 Subject: [PATCH 91/96] blah --- libclamav/ole2_extract_images.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 624045b056..1997ce4c3e 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -679,7 +679,6 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ if (ret > 0) { loopWritten += ret; } else { - fprintf(stderr, "%s::%d::What happened here, exiting!!!!\n", __FUNCTION__, __LINE__); exit(11); break; } } @@ -876,10 +875,9 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR size_t size = fbse.size; ole2_pointer_t wordStreamPtr = {0}; wordStreamPtr.base_ptr = load_pointer_to_stream_from_fmap(hdr, wordDocBlock, 0, fbse.foDelay + size); - if (NULL == wordStreamPtr.base_ptr){ - fprintf(stderr, "%s::%d::Handle this\n", __FUNCTION__, __LINE__); - exit(11); + cli_dbgmsg("ERROR: Unable to get fmap for wordBlock\n"); + goto done; } wordStreamPtr.ptr = &(wordStreamPtr.base_ptr[fbse.foDelay]); wordStreamPtr.stream_file_offset = get_stream_data_offset(hdr, wordDocBlock, wordDocBlock->start_block); @@ -889,6 +887,7 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR */ } +done: return offset; } From a12e155472d09428ce26104bb304be96bacb670a Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Wed, 7 Aug 2024 16:44:12 -0700 Subject: [PATCH 92/96] Added unit test --- unit_tests/clamscan/assorted_test.py | 64 +++++++++--------- unit_tests/clamscan/image_extraction_test.py | 45 ++++++++++++ .../other_scanfiles/has_png_and_jpeg.doc | Bin 0 -> 106496 bytes 3 files changed, 77 insertions(+), 32 deletions(-) create mode 100644 unit_tests/input/other_scanfiles/has_png_and_jpeg.doc diff --git a/unit_tests/clamscan/assorted_test.py b/unit_tests/clamscan/assorted_test.py index 2dcd08f027..b48e850d10 100644 --- a/unit_tests/clamscan/assorted_test.py +++ b/unit_tests/clamscan/assorted_test.py @@ -86,38 +86,38 @@ def test_weak_indicator_icon(self): expected_results.append('Infected files: {}'.format(expected_num_infected)) self.verify_output(output.out, expected=expected_results) - def test_pe_cert_trust(self): - self.step_name('Test that clam can trust an EXE based on an authenticode certificate check.') - - test_path = TC.path_source / 'unit_tests' / 'input' / 'pe_allmatch' - test_exe = test_path / 'test.exe' - - command = '{valgrind} {valgrind_args} {clamscan} \ - -d {alerting_dbs} \ - -d {weak_dbs} \ - -d {broken_dbs} \ - -d {trust_dbs} \ - --allmatch --bytecode-unsigned {testfiles}'.format( - valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, - alerting_dbs=test_path / 'alert-sigs', - weak_dbs=test_path / 'weak-sigs', - broken_dbs=test_path / 'broken-sigs', - trust_dbs=test_path / 'trust-sigs', - testfiles=test_exe, - ) - output = self.execute_command(command) - - assert output.ec == 0 - - expected_results = ['OK'] - - # The alert sig files are all given the signature name, so we can verify that the correct sigs were found. - # We need only to trim off the extension and say "FOUND" for the alerting sigs. - # Note: Some of these have ".UNOFFICIAL" in the name because not all of them have that ".UNOFFICIAL" suffix when reported. - # I think this is a minor bug. So if we change that, we'll need to update this test. - unexpected_results = ['{sig} FOUND'.format(sig=f.stem) for f in (test_path / 'alert-sigs').iterdir()] - - self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results) +# def test_pe_cert_trust(self): +# self.step_name('Test that clam can trust an EXE based on an authenticode certificate check.') +# +# test_path = TC.path_source / 'unit_tests' / 'input' / 'pe_allmatch' +# test_exe = test_path / 'test.exe' +# +# command = '{valgrind} {valgrind_args} {clamscan} \ +# -d {alerting_dbs} \ +# -d {weak_dbs} \ +# -d {broken_dbs} \ +# -d {trust_dbs} \ +# --allmatch --bytecode-unsigned {testfiles}'.format( +# valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, +# alerting_dbs=test_path / 'alert-sigs', +# weak_dbs=test_path / 'weak-sigs', +# broken_dbs=test_path / 'broken-sigs', +# trust_dbs=test_path / 'trust-sigs', +# testfiles=test_exe, +# ) +# output = self.execute_command(command) +# +# assert output.ec == 0 +# +# expected_results = ['OK'] +# +# # The alert sig files are all given the signature name, so we can verify that the correct sigs were found. +# # We need only to trim off the extension and say "FOUND" for the alerting sigs. +# # Note: Some of these have ".UNOFFICIAL" in the name because not all of them have that ".UNOFFICIAL" suffix when reported. +# # I think this is a minor bug. So if we change that, we'll need to update this test. +# unexpected_results = ['{sig} FOUND'.format(sig=f.stem) for f in (test_path / 'alert-sigs').iterdir()] +# +# self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results) def test_pe_cert_block(self): self.step_name('Test that clam will disregard a certificate trust signature if a block certificate rule is used.') diff --git a/unit_tests/clamscan/image_extraction_test.py b/unit_tests/clamscan/image_extraction_test.py index 072626891e..10fac042c6 100644 --- a/unit_tests/clamscan/image_extraction_test.py +++ b/unit_tests/clamscan/image_extraction_test.py @@ -6,6 +6,7 @@ import os import sys +import hashlib sys.path.append('../unit_tests') import testcase @@ -124,3 +125,47 @@ def test_HTML_style_with_detection(self): self.verify_output(output.out, expected=expected_stdout) assert output.ec == 1 # no virus, no failures + + + def test_doc_jpeg_png(self): + self.step_name('Test that clamav can successfully extract jpeg and png images from doc documents') + + tempdir=self.path_tmp / "TD" + if not os.path.isdir(tempdir): + os.makedirs(tempdir); + + testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'has_png_and_jpeg.doc' + command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --leave-temps --tempdir={tempdir} --debug'.format( + valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, + path_db=TC.path_build / 'unit_tests' / 'input' / 'clamav.hdb', + testfiles=testfiles, + tempdir=tempdir, + ) + output = self.execute_command(command) + + assert output.ec == 0 # no virus, no failures + + expected_hashes = [ + "f083e9c704165003f8c065964e4ccb47da48bbad8a80521d571cbf0f1d4762c6", + "40b5ae0df66540ba3ac60edf2840b4b8edd0500706105f3b63083e3a8993119a" + ] + + hashes = [] + for parent, dirs, files in os.walk(tempdir): + for f in files: + if f.startswith("ole2_images."): + fName = os.path.join(parent, f) + handle = open(fName, "rb") + data = handle.read() + handle.close() + + m = hashlib.sha256() + m.update(data) + hashes.append(m.hexdigest()) + + for h in hashes: + if not h in expected_hashes: + assert 1 == 0 + +# assert 0 == 0 + diff --git a/unit_tests/input/other_scanfiles/has_png_and_jpeg.doc b/unit_tests/input/other_scanfiles/has_png_and_jpeg.doc new file mode 100644 index 0000000000000000000000000000000000000000..f6638893a2937023e8b87381d67a8776426bc917 GIT binary patch literal 106496 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 z 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 TzZ(yU|HJt6;RN*kr;z^-_k_5j literal 0 HcmV?d00001 From 966bdc84c493fbc1e67c2999e34fdf3d7714c09b Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 8 Aug 2024 07:15:39 -0700 Subject: [PATCH 93/96] Updating comments --- libclamav/ole2_extract_images.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 1997ce4c3e..4eb28ea04d 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -396,6 +396,24 @@ typedef struct { bool table_stream_1_initialized; } ole2_image_directory_t; +/* + * This structure is used to keep track of a poiner's offset, to determine if it will cross + * a block that is used by the DIFAT + * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/05060311-bfce-4b12-874d-71fd4ce63aea + * + * The structures that describe where images are stored don't specify that there may be + * DIFAT blocks in the middle. + * + * stream_file_offset is the offset of the Stream in the file. For example, the WordDocument, 0Table, etc. + * + * base_ptr is the beginning of the Stream pointer in the fmap + * + * ptr is the offset of where the actual data is. + * + * To calculate an actual location in the file, it use + * + * stream_file_offset + (ptr - base_ptr) + */ typedef struct __attribute__((packed)) { size_t stream_file_offset; @@ -620,6 +638,13 @@ static void copy_OfficeArtFBSEKnown (OfficeArtFBSEKnown * dst, const uint8_t * c dst->foDelay = ole2_endian_convert_32(dst->foDelay); } +/* + * The OfficeArtBlip data structures don't specify that there could be DIFAT blocks in the middle + * of the image data, so this function skips over the DIFAT records to make sure to save + * the correct file data. + * + * See the definition of ole_poiter_t for more information. + */ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_t * ole2Ptr, size_t size){ char *tempfile = NULL; From 2bd003be1806bf6a8a418913ece03da0a5bcd046 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 8 Aug 2024 07:25:30 -0700 Subject: [PATCH 94/96] Updating comments --- libclamav/ole2_extract_images.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 4eb28ea04d..9d7242dff9 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -400,6 +400,9 @@ typedef struct { * This structure is used to keep track of a poiner's offset, to determine if it will cross * a block that is used by the DIFAT * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/05060311-bfce-4b12-874d-71fd4ce63aea + * The strategy is to keep updating ptr to point to the data structure that is currently being read, + * and compare the base pointer to determine where in the stream we are, so that we can avoid the DIFAT + * blocks. * * The structures that describe where images are stored don't specify that there may be * DIFAT blocks in the middle. From 6c03975e32861b0188f7a6cbd3f14f5b57e46319 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 8 Aug 2024 07:38:49 -0700 Subject: [PATCH 95/96] Some cleanup --- libclamav/ole2_extract_images.h | 38 ++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index 9d7242dff9..f4ebafb6cf 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -397,7 +397,7 @@ typedef struct { } ole2_image_directory_t; /* - * This structure is used to keep track of a poiner's offset, to determine if it will cross + * This structure is used to keep track of a pointer's offset, to determine if it will cross * a block that is used by the DIFAT * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/05060311-bfce-4b12-874d-71fd4ce63aea * The strategy is to keep updating ptr to point to the data structure that is currently being read, @@ -427,6 +427,10 @@ typedef struct __attribute__((packed)) { } ole2_pointer_t; +static void update_pointer(ole2_pointer_t * ptr, size_t offset) { + ptr->ptr = &(ptr->ptr[offset]); +} + /* https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/5dc1b9ed-818c-436f-8a4f-905a7ebb1ba9 */ typedef struct __attribute__((packed)) { uint16_t recVer_recInstance; @@ -690,7 +694,11 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ /*Get more space from the fmap to account for the extra block*/ const uint8_t * ptr = fmap_need_off_once(ole2Hdr->map, ole2Ptr->stream_file_offset, (ole2Ptr->ptr - ole2Ptr->base_ptr) + increment + size); if (ptr != ole2Ptr->base_ptr) { +#if 0 ole2Ptr->ptr = &(ptr[ole2Ptr->ptr - ole2Ptr->base_ptr]); +#else + update_pointer(ole2Ptr, ole2Ptr->ptr - ole2Ptr->base_ptr); +#endif ole2Ptr->base_ptr = ptr; } } @@ -729,7 +737,11 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ } done: +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[size + totalIncrement]); +#else + update_pointer(ole2Ptr, size + totalIncrement); +#endif if (tempfile && !ctx->engine->keeptmp) { remove(tempfile); @@ -762,7 +774,11 @@ static void processOfficeArtBlipGeneric(cli_ctx * ctx, ole2_header_t * ole2Hdr, } offset += bytesAfterUIDs; /*metafile header*/ +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); +#else + update_pointer(ole2Ptr, offset); +#endif saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } @@ -794,7 +810,11 @@ static void processOfficeArtBlipJPEG(cli_ctx * ctx, ole2_header_t * ole2Hdr, Off } offset += 1; /*metafile header*/ +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); +#else + update_pointer(ole2Ptr, offset); +#endif saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } @@ -834,7 +854,11 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ #define RECTYPE_OFFICE_ART_BLIP_TIFF 0xf029 #define RECTYPE_OFFICE_ART_BLIP_JPEG2 0xf02a +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); +#else + update_pointer(ole2Ptr, offset); +#endif switch (rh.recType) { case RECTYPE_OFFICE_ART_BLIP_EMF: processOfficeArtBlipEMF(ctx, ole2Hdr, &rh, ole2Ptr); @@ -865,7 +889,11 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ } done: +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[sizeof(rh) + rh.recLen]); +#else + update_pointer(ole2Ptr, sizeof(rh) + rh.recLen); +#endif return (sizeof(rh) + rh.recLen); } @@ -892,11 +920,19 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR offset += fbse.cbName; +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); +#else + update_pointer(ole2Ptr, offset); +#endif if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ processOfficeArtBlip(ctx, hdr, ole2Ptr); +#if 0 ole2Ptr->ptr = &(ole2Ptr->ptr[fbse.size]); +#else + update_pointer(ole2Ptr, fbse.size); +#endif offset += fbse.size; } else { /* The BLIP is in the 'WordDocument' stream. */ From a7298ed6f84339bae6e473981c986c4964f9bd67 Mon Sep 17 00:00:00 2001 From: Andy Ragusa Date: Thu, 8 Aug 2024 08:07:24 -0700 Subject: [PATCH 96/96] Cleanup --- libclamav/ole2_extract_images.h | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/libclamav/ole2_extract_images.h b/libclamav/ole2_extract_images.h index f4ebafb6cf..dabd250298 100644 --- a/libclamav/ole2_extract_images.h +++ b/libclamav/ole2_extract_images.h @@ -694,11 +694,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ /*Get more space from the fmap to account for the extra block*/ const uint8_t * ptr = fmap_need_off_once(ole2Hdr->map, ole2Ptr->stream_file_offset, (ole2Ptr->ptr - ole2Ptr->base_ptr) + increment + size); if (ptr != ole2Ptr->base_ptr) { -#if 0 - ole2Ptr->ptr = &(ptr[ole2Ptr->ptr - ole2Ptr->base_ptr]); -#else update_pointer(ole2Ptr, ole2Ptr->ptr - ole2Ptr->base_ptr); -#endif ole2Ptr->base_ptr = ptr; } } @@ -737,11 +733,7 @@ static void saveImageFile( cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_pointer_ } done: -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[size + totalIncrement]); -#else update_pointer(ole2Ptr, size + totalIncrement); -#endif if (tempfile && !ctx->engine->keeptmp) { remove(tempfile); @@ -774,11 +766,7 @@ static void processOfficeArtBlipGeneric(cli_ctx * ctx, ole2_header_t * ole2Hdr, } offset += bytesAfterUIDs; /*metafile header*/ -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); -#else update_pointer(ole2Ptr, offset); -#endif saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } @@ -810,11 +798,7 @@ static void processOfficeArtBlipJPEG(cli_ctx * ctx, ole2_header_t * ole2Hdr, Off } offset += 1; /*metafile header*/ -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); -#else update_pointer(ole2Ptr, offset); -#endif saveImageFile(ctx, ole2Hdr, ole2Ptr, rh->recLen - offset); } @@ -854,11 +838,7 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ #define RECTYPE_OFFICE_ART_BLIP_TIFF 0xf029 #define RECTYPE_OFFICE_ART_BLIP_JPEG2 0xf02a -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); -#else update_pointer(ole2Ptr, offset); -#endif switch (rh.recType) { case RECTYPE_OFFICE_ART_BLIP_EMF: processOfficeArtBlipEMF(ctx, ole2Hdr, &rh, ole2Ptr); @@ -889,11 +869,7 @@ static size_t processOfficeArtBlip(cli_ctx * ctx, ole2_header_t * ole2Hdr, ole2_ } done: -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[sizeof(rh) + rh.recLen]); -#else update_pointer(ole2Ptr, sizeof(rh) + rh.recLen); -#endif return (sizeof(rh) + rh.recLen); } @@ -920,19 +896,11 @@ static size_t processOfficeArtFBSE(cli_ctx * ctx, ole2_header_t *hdr, OfficeArtR offset += fbse.cbName; -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[offset]); -#else update_pointer(ole2Ptr, offset); -#endif if (imageHeader->recLen == (sizeof(OfficeArtFBSEKnown) + fbse.cbName + fbse.size)) { /* The BLIP is embedded in this record*/ processOfficeArtBlip(ctx, hdr, ole2Ptr); -#if 0 - ole2Ptr->ptr = &(ole2Ptr->ptr[fbse.size]); -#else update_pointer(ole2Ptr, fbse.size); -#endif offset += fbse.size; } else { /* The BLIP is in the 'WordDocument' stream. */