47bbbc56 |
/*
* Extract component parts of OLE2 files (e.g. MS Office Documents)
* |
2023340a |
* Copyright (C) 2007-2008 Sourcefire, Inc.
*
* Authors: Trog |
47bbbc56 |
*
* This program is free software; you can redistribute it and/or modify |
2023340a |
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. |
47bbbc56 |
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software |
48b7b4a7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
47bbbc56 |
*/
|
6d6e8271 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
47bbbc56 |
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h> |
b58fdfc2 |
#ifdef HAVE_UNISTD_H |
47bbbc56 |
#include <unistd.h> |
b58fdfc2 |
#endif |
47bbbc56 |
#include <ctype.h>
#include <stdlib.h> |
11195c0b |
#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
#include <sys/mman.h>
#endif |
b58fdfc2 |
#include "clamav.h" |
47bbbc56 |
|
f893c0f3 |
#include "cltypes.h" |
1dc96b6c |
#include "others.h" |
079229d6 |
#include "ole2_extract.h" |
72ce4b70 |
#include "scanners.h" |
11b50569 |
#include "mbox.h"
|
22af56a3 |
#define ole2_endian_convert_16(v) le16_to_host((uint16_t)(v))
#define ole2_endian_convert_32(v) le32_to_host((uint32_t)(v)) |
c83b8845 |
|
1dc96b6c |
#ifndef HAVE_ATTRIB_PACKED
#define __attribute__(x)
#endif
#ifdef HAVE_PRAGMA_PACK
#pragma pack(1)
#endif
|
ef38c83d |
#ifdef HAVE_PRAGMA_PACK_HPPA
#pragma pack 1
#endif
|
47bbbc56 |
typedef struct ole2_header_tag
{ |
bda5598b |
unsigned char magic[8]; /* should be: 0xd0cf11e0a1b11ae1 */
unsigned char clsid[16]; |
e0f90556 |
uint16_t minor_version __attribute__ ((packed));
uint16_t dll_version __attribute__ ((packed));
int16_t byte_order __attribute__ ((packed)); /* -2=intel */ |
47bbbc56 |
|
e0f90556 |
uint16_t log2_big_block_size __attribute__ ((packed)); /* usually 9 (2^9 = 512) */ |
ec981799 |
uint32_t log2_small_block_size __attribute__ ((packed)); /* usually 6 (2^6 = 64) */ |
47bbbc56 |
|
e0f90556 |
int32_t reserved[2] __attribute__ ((packed));
int32_t bat_count __attribute__ ((packed));
int32_t prop_start __attribute__ ((packed)); |
47bbbc56 |
|
e0f90556 |
uint32_t signature __attribute__ ((packed));
uint32_t sbat_cutoff __attribute__ ((packed)); /* cutoff for files held in small blocks (4096) */ |
47bbbc56 |
|
e0f90556 |
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)); |
349e0502 |
/* not part of the ole2 header, but stuff we need in order to decode */
/* must take account of the size of variables below here when
reading the header */ |
e0f90556 |
int32_t sbat_root_start __attribute__ ((packed)); |
b5231f5f |
uint32_t max_block_no; |
8c030302 |
unsigned char *m_area;
off_t m_length; |
8a9c2d19 |
bitset_t *bitset; |
72ce4b70 |
struct uniq *U;
int has_vba; |
e0f90556 |
} ole2_header_t; |
47bbbc56 |
|
72ce4b70 |
|
47bbbc56 |
typedef struct property_tag
{ |
bda5598b |
char name[64]; /* in unicode */ |
f8c58685 |
uint16_t name_size __attribute__ ((packed)); |
bda5598b |
unsigned char type; /* 1=dir 2=file 5=root */
unsigned char color; /* black or red */ |
f8c58685 |
uint32_t prev __attribute__ ((packed));
uint32_t next __attribute__ ((packed));
uint32_t child __attribute__ ((packed)); |
e0f90556 |
|
bda5598b |
unsigned char clsid[16]; |
e0f90556 |
uint32_t user_flags __attribute__ ((packed));
uint32_t create_lowdate __attribute__ ((packed));
uint32_t create_highdate __attribute__ ((packed));
uint32_t mod_lowdate __attribute__ ((packed));
uint32_t mod_highdate __attribute__ ((packed)); |
f8c58685 |
uint32_t start_block __attribute__ ((packed));
uint32_t size __attribute__ ((packed)); |
bda5598b |
unsigned char reserved[4]; |
e0f90556 |
} property_t; |
47bbbc56 |
|
1dc96b6c |
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif
|
ef38c83d |
#ifdef HAVE_PRAGMA_PACK_HPPA
#pragma pack
#endif
|
bda5598b |
static unsigned char magic_id[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1}; |
47bbbc56 |
|
72ce4b70 |
static char *get_property_name2(char *name, int size) |
47bbbc56 |
{
int i, j;
char *newname;
|
f5646d91 |
if (*name == 0 || size <= 0 || size > 64) { |
47bbbc56 |
return NULL;
}
|
9e7e2c76 |
newname = (char *) cli_malloc(size*7); |
47bbbc56 |
if (!newname) {
return NULL;
}
j=0;
/* size-2 to ignore trailing NULL */
for (i=0 ; i < size-2; i+=2) { |
b58fdfc2 |
if((!(name[i]&0x80)) && isprint(name[i])) { |
72ce4b70 |
newname[j++] = tolower(name[i]); |
47bbbc56 |
} else {
if (name[i] < 10 && name[i] >= 0) {
newname[j++] = '_';
newname[j++] = name[i] + '0';
} |
9e7e2c76 |
else {
const uint16_t x = (((uint16_t)name[i]) << 8) | name[i+1];
newname[j++] = '_';
newname[j++] = 'a'+((x&0xF));
newname[j++] = 'a'+((x>>4)&0xF);
newname[j++] = 'a'+((x>>8)&0xF);
newname[j++] = 'a'+((x>>16)&0xF);
newname[j++] = 'a'+((x>>24)&0xF);
} |
47bbbc56 |
newname[j++] = '_';
}
}
newname[j] = '\0'; |
349e0502 |
if (strlen(newname) == 0) {
free(newname);
return NULL;
} |
47bbbc56 |
return newname;
} |
e0f90556 |
|
72ce4b70 |
static char *get_property_name(char *name, int size) {
const char *carray = "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz._";
int csize = size>>1;
char *newname, *cname;
char *oname = name;
|
77fd59a8 |
if (csize<=0) return NULL; |
72ce4b70 |
newname = cname = (char *)cli_malloc(size);
if (!newname) return NULL;
while(--csize) {
uint16_t lo, hi, u=cli_readint16(oname)-0x3800;
oname+=2;
if (u > 0x1040) {
free(newname);
return get_property_name2(name, size);
}
lo = u % 64;
u >>= 6;
hi = u % 64;
*cname++=carray[lo];
if(csize!=1 || u!= 64) *cname++=carray[hi];
}
*cname='\0';
return newname; |
d95b7372 |
} |
47bbbc56 |
|
72ce4b70 |
|
e0f90556 |
static void print_ole2_property(property_t *property) |
47bbbc56 |
{ |
72ce4b70 |
char spam[128], *buf; |
d95b7372 |
if (property->name_size > 64) {
cli_dbgmsg("[err name len: %d]\n", property->name_size);
return;
} |
72ce4b70 |
buf = get_property_name(property->name, property->name_size);
snprintf(spam, sizeof(spam), "OLE2: %s ", buf ? buf : "<noname>");
spam[sizeof(spam)-1]='\0';
if (buf) free(buf); |
47bbbc56 |
switch (property->type) {
case 2: |
72ce4b70 |
strncat(spam, " [file] ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
break;
case 1: |
72ce4b70 |
strncat(spam, " [dir ] ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
break;
case 5: |
72ce4b70 |
strncat(spam, " [root] ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
break;
default: |
72ce4b70 |
strncat(spam, " [unkn] ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
} |
72ce4b70 |
spam[sizeof(spam)-1]='\0'; |
47bbbc56 |
switch (property->color) {
case 0: |
72ce4b70 |
strncat(spam, " r ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
break;
case 1: |
72ce4b70 |
strncat(spam, " b ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
break;
default: |
72ce4b70 |
strncat(spam, " u ", sizeof(spam) - 1 - strlen(spam)); |
47bbbc56 |
} |
72ce4b70 |
spam[sizeof(spam)-1]='\0';
cli_dbgmsg("%s size:0x%.8x flags:0x%.8x\n", spam, property->size, property->user_flags); |
47bbbc56 |
}
|
e0f90556 |
static void print_ole2_header(ole2_header_t *hdr) |
47bbbc56 |
{
int i;
if (!hdr) {
return;
}
cli_dbgmsg("\nMagic:\t\t\t0x");
for (i=0 ; i<8; i++) {
cli_dbgmsg("%x", hdr->magic[i]);
}
cli_dbgmsg("\n");
cli_dbgmsg("CLSID:\t\t\t{");
for (i=0 ; i<16; i++) {
cli_dbgmsg("%x ", hdr->clsid[i]);
}
cli_dbgmsg("}\n");
cli_dbgmsg("Minor version:\t\t0x%x\n", hdr->minor_version);
cli_dbgmsg("DLL version:\t\t0x%x\n", hdr->dll_version);
cli_dbgmsg("Byte Order:\t\t%d\n", hdr->byte_order);
cli_dbgmsg("Big Block Size:\t\t%i\n", hdr->log2_big_block_size);
cli_dbgmsg("Small Block Size:\t%i\n", hdr->log2_small_block_size);
cli_dbgmsg("BAT count:\t\t%d\n", hdr->bat_count);
cli_dbgmsg("Prop start:\t\t%d\n", hdr->prop_start);
cli_dbgmsg("SBAT cutoff:\t\t%d\n", hdr->sbat_cutoff);
cli_dbgmsg("SBat start:\t\t%d\n", hdr->sbat_start);
cli_dbgmsg("SBat block count:\t%d\n", hdr->sbat_block_count);
cli_dbgmsg("XBat start:\t\t%d\n", hdr->xbat_start);
cli_dbgmsg("XBat block count:\t%d\n\n", hdr->xbat_count);
return;
}
|
ec981799 |
static int ole2_read_block(int fd, ole2_header_t *hdr, void *buff, unsigned int size, int32_t blockno) |
47bbbc56 |
{ |
c30e4a78 |
off_t offset, offend; |
47bbbc56 |
|
767c51ae |
if (blockno < 0) {
return FALSE;
}
|
bf5a1ce7 |
/* other methods: (blockno+1) * 512 or (blockno * block_size) + 512; */ |
ec981799 |
offset = (blockno << hdr->log2_big_block_size) + MAX(512, 1 << hdr->log2_big_block_size); /* 512 is header size */ |
8c030302 |
if (hdr->m_area == NULL) {
if (lseek(fd, offset, SEEK_SET) != offset) {
return FALSE;
} |
ec981799 |
if (cli_readn(fd, buff, size) != size) { |
8c030302 |
return FALSE;
}
} else { |
ec981799 |
offend = offset + size; |
c30e4a78 |
if ((offend <= 0) || (offend > hdr->m_length)) { |
8c030302 |
return FALSE;
} |
ec981799 |
memcpy(buff, hdr->m_area+offset, size); |
47bbbc56 |
}
return TRUE;
}
|
e0f90556 |
static int32_t ole2_get_next_bat_block(int fd, ole2_header_t *hdr, int32_t current_block) |
47bbbc56 |
{ |
1dc96b6c |
int32_t bat_array_index; |
47bbbc56 |
uint32_t bat[128];
|
767c51ae |
if (current_block < 0) {
return -1;
}
|
47bbbc56 |
bat_array_index = current_block / 128;
if (bat_array_index > hdr->bat_count) {
cli_dbgmsg("bat_array index error\n");
return -10;
} |
ec981799 |
if (!ole2_read_block(fd, hdr, &bat, 512, |
767c51ae |
ole2_endian_convert_32(hdr->bat_array[bat_array_index]))) {
return -1;
} |
c83b8845 |
return ole2_endian_convert_32(bat[current_block-(bat_array_index * 128)]); |
47bbbc56 |
}
|
e0f90556 |
static int32_t ole2_get_next_xbat_block(int fd, ole2_header_t *hdr, int32_t current_block) |
47bbbc56 |
{ |
1dc96b6c |
int32_t xbat_index, xbat_block_index, bat_index, bat_blockno; |
47bbbc56 |
uint32_t xbat[128], bat[128];
|
767c51ae |
if (current_block < 0) {
return -1;
}
|
47bbbc56 |
xbat_index = current_block / 128;
/* NB: The last entry in each XBAT points to the next XBAT block.
This reduces the number of entries in each block by 1.
*/
xbat_block_index = (xbat_index - 109) / 127;
bat_blockno = (xbat_index - 109) % 127;
bat_index = current_block % 128;
|
ec981799 |
if (!ole2_read_block(fd, hdr, &xbat, 512, hdr->xbat_start)) { |
767c51ae |
return -1;
} |
47bbbc56 |
/* Follow the chain of XBAT blocks */
while (xbat_block_index > 0) { |
ec981799 |
if (!ole2_read_block(fd, hdr, &xbat, 512, |
767c51ae |
ole2_endian_convert_32(xbat[127]))) {
return -1;
} |
47bbbc56 |
xbat_block_index--;
}
|
ec981799 |
if (!ole2_read_block(fd, hdr, &bat, 512, ole2_endian_convert_32(xbat[bat_blockno]))) { |
767c51ae |
return -1;
} |
47bbbc56 |
|
c83b8845 |
return ole2_endian_convert_32(bat[bat_index]); |
47bbbc56 |
}
|
e0f90556 |
static int32_t ole2_get_next_block_number(int fd, ole2_header_t *hdr, int32_t current_block) |
47bbbc56 |
{ |
767c51ae |
if (current_block < 0) {
return -1;
}
|
47bbbc56 |
if ((current_block / 128) > 108) {
return ole2_get_next_xbat_block(fd, hdr, current_block);
} else {
return ole2_get_next_bat_block(fd, hdr, current_block);
}
}
|
e0f90556 |
static int32_t ole2_get_next_sbat_block(int fd, ole2_header_t *hdr, int32_t current_block) |
1dc96b6c |
{
int32_t iter, current_bat_block;
uint32_t sbat[128];
|
767c51ae |
if (current_block < 0) {
return -1;
}
|
1dc96b6c |
current_bat_block = hdr->sbat_start;
iter = current_block / 128;
while (iter > 0) {
current_bat_block = ole2_get_next_block_number(fd, hdr, current_bat_block);
iter--;
} |
ec981799 |
if (!ole2_read_block(fd, hdr, &sbat, 512, current_bat_block)) { |
767c51ae |
return -1;
} |
1dc96b6c |
return ole2_endian_convert_32(sbat[current_block % 128]);
}
|
47bbbc56 |
/* Retrieve the block containing the data for the given sbat index */ |
e0f90556 |
static int32_t ole2_get_sbat_data_block(int fd, ole2_header_t *hdr, void *buff, int32_t sbat_index) |
47bbbc56 |
{ |
1dc96b6c |
int32_t block_count, current_block; |
47bbbc56 |
|
767c51ae |
if (sbat_index < 0) {
return FALSE;
}
|
349e0502 |
if (hdr->sbat_root_start < 0) { |
f2d79ab3 |
cli_dbgmsg("No root start block\n"); |
47bbbc56 |
return FALSE;
}
|
ec981799 |
block_count = sbat_index / (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)); |
349e0502 |
current_block = hdr->sbat_root_start; |
47bbbc56 |
while (block_count > 0) { |
1dc96b6c |
current_block = ole2_get_next_block_number(fd, hdr, current_block); |
47bbbc56 |
block_count--;
}
/* current_block now contains the block number of the sbat array
containing the entry for the required small block */
|
ec981799 |
return(ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)); |
47bbbc56 |
}
|
72ce4b70 |
static int ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, int32_t prop_index,
int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx),
unsigned int rec_level, unsigned int *file_count, cli_ctx *ctx, unsigned long *scansize) |
ad2f8396 |
{
property_t prop_block[4]; |
24555841 |
int32_t idx, current_block, i; |
bda5598b |
char *dirname; |
72ce4b70 |
int ret; |
f8c58685 |
|
ad2f8396 |
current_block = hdr->prop_start; |
f8c58685 |
|
24555841 |
if ((prop_index < 0) || (prop_index > (int32_t) hdr->max_block_no) || (rec_level > 100) || (*file_count > 100000)) { |
72ce4b70 |
return CL_SUCCESS; |
e5a5b2f2 |
} |
724b2bf7 |
if (ctx && ctx->engine->maxfiles && (*file_count > ctx->engine->maxfiles)) {
cli_dbgmsg("OLE2: File limit reached (max: %d)\n", ctx->engine->maxfiles); |
72ce4b70 |
return CL_SUCCESS; |
ad2f8396 |
}
|
724b2bf7 |
if (ctx && ctx->engine->maxreclevel && (rec_level > ctx->engine->maxreclevel)) {
cli_dbgmsg("OLE2: Recursion limit reached (max: %d)\n", ctx->engine->maxreclevel); |
72ce4b70 |
return CL_SUCCESS; |
42034091 |
} |
7df56d47 |
|
24555841 |
idx = prop_index / 4;
for (i=0 ; i < idx ; i++) { |
ad2f8396 |
current_block = ole2_get_next_block_number(fd, hdr, current_block);
if (current_block < 0) { |
72ce4b70 |
return CL_SUCCESS; |
ad2f8396 |
}
} |
24555841 |
idx = prop_index % 4; |
ec981799 |
if (!ole2_read_block(fd, hdr, prop_block, 512, |
ad2f8396 |
current_block)) { |
72ce4b70 |
return CL_SUCCESS; |
ad2f8396 |
} |
24555841 |
if (prop_block[idx].type <= 0) { |
72ce4b70 |
return CL_SUCCESS; |
ad2f8396 |
} |
24555841 |
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);
prop_block[idx].child = ole2_endian_convert_32(prop_block[idx].child);
prop_block[idx].user_flags = ole2_endian_convert_32(prop_block[idx].user_flags);
prop_block[idx].create_lowdate = ole2_endian_convert_32(prop_block[idx].create_lowdate);
prop_block[idx].create_highdate = ole2_endian_convert_32(prop_block[idx].create_highdate);
prop_block[idx].mod_lowdate = ole2_endian_convert_32(prop_block[idx].mod_lowdate);
prop_block[idx].mod_highdate = ole2_endian_convert_32(prop_block[idx].mod_highdate);
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); |
ad2f8396 |
|
72ce4b70 |
if (dir) print_ole2_property(&prop_block[idx]); |
8a9c2d19 |
/* Check we aren't in a loop */
if (cli_bitset_test(hdr->bitset, (unsigned long) prop_index)) {
/* Loop in property tree detected */
cli_dbgmsg("OLE2: Property tree loop detected at index %d\n", prop_index); |
72ce4b70 |
return CL_BREAK; |
8a9c2d19 |
}
if (!cli_bitset_set(hdr->bitset, (unsigned long) prop_index)) { |
72ce4b70 |
return CL_SUCCESS; |
8a9c2d19 |
}
|
24555841 |
switch (prop_block[idx].type) { |
ad2f8396 |
case 5: /* Root Entry */ |
cd9833ef |
if ((prop_index != 0) || (rec_level !=0) || |
e5a5b2f2 |
(*file_count != 0)) { |
ad2f8396 |
/* Can only have RootEntry as the top */
cli_dbgmsg("ERROR: illegal Root Entry\n"); |
72ce4b70 |
return CL_SUCCESS; |
ad2f8396 |
} |
24555841 |
hdr->sbat_root_start = prop_block[idx].start_block; |
72ce4b70 |
if (
(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
||
(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
||
(ret=ole2_walk_property_tree(fd, hdr, dir,prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
) return ret; |
ad2f8396 |
break;
case 2: /* File */ |
724b2bf7 |
if (ctx && ctx->engine->maxfiles && ctx->scannedfiles + *file_count > ctx->engine->maxfiles) {
cli_dbgmsg("OLE2: files limit reached (max: %u)\n", ctx->engine->maxfiles); |
72ce4b70 |
return CL_BREAK; |
08d0fdc5 |
} |
724b2bf7 |
if (!ctx || !ctx->engine->maxfilesize || prop_block[idx].size <= ctx->engine->maxfilesize || prop_block[idx].size <= *scansize) { |
08d0fdc5 |
(*file_count)++;
*scansize-=prop_block[idx].size; |
72ce4b70 |
if ((ret=handler(fd, hdr, &prop_block[idx], dir, ctx)) != CL_SUCCESS)
return ret; |
08d0fdc5 |
} else { |
72ce4b70 |
cli_dbgmsg("OLE2: filesize exceeded\n"); |
ad2f8396 |
} |
72ce4b70 |
if (
(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS
||
(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].next, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS
||
(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS
) return ret; |
ad2f8396 |
break;
case 1: /* Directory */ |
72ce4b70 |
if (dir) {
dirname = (char *) cli_malloc(strlen(dir)+8);
if (!dirname) return CL_BREAK; |
58481352 |
snprintf(dirname, strlen(dir)+8, "%s"PATHSEP"%.6d", dir, prop_index); |
72ce4b70 |
if (mkdir(dirname, 0700) != 0) {
free(dirname);
return CL_BREAK;
}
cli_dbgmsg("OLE2 dir entry: %s\n",dirname);
} else dirname = NULL;
if (
(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
||
(ret=ole2_walk_property_tree(fd, hdr, dir,prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
||
(ret=ole2_walk_property_tree(fd, hdr, dirname, prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
) {}
if (dirname) free(dirname);
return ret; |
ad2f8396 |
break;
default: |
24555841 |
cli_dbgmsg("ERROR: unknown OLE2 entry type: %d\n", prop_block[idx].type); |
ad2f8396 |
break;
} |
72ce4b70 |
return CL_SUCCESS; |
ad2f8396 |
} |
47bbbc56 |
/* Write file Handler - write the contents of the entry to a file */ |
72ce4b70 |
static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx) |
47bbbc56 |
{ |
3c21733d |
unsigned char *buff; |
1dc96b6c |
int32_t current_block, ofd, len, offset; |
72ce4b70 |
char *name, newname[1024]; |
42438bd5 |
bitset_t *blk_bitset; |
937ade08 |
char *hash;
uint32_t cnt; |
47bbbc56 |
if (prop->type != 2) { |
bf5a1ce7 |
/* Not a file */ |
72ce4b70 |
return CL_SUCCESS; |
d95b7372 |
}
if (prop->name_size > 64) { |
72ce4b70 |
cli_dbgmsg("OLE2 [handler_writefile]: property name too long: %d\n", prop->name_size);
return CL_SUCCESS; |
bf34c7e7 |
} |
b58fdfc2 |
|
72ce4b70 |
name = get_property_name2(prop->name, prop->name_size);
if (name) cnt = uniq_add(hdr->U, name, strlen(name), &hash);
else cnt = uniq_add(hdr->U, NULL, 0, &hash); |
58481352 |
snprintf(newname, sizeof(newname), "%s"PATHSEP"%s_%u", dir, hash, cnt); |
72ce4b70 |
newname[sizeof(newname)-1]='\0';
cli_dbgmsg("OLE2 [handler_writefile]: Dumping '%s' to '%s'\n", name ? name : "<empty>", newname);
if (name) free(name); |
47bbbc56 |
|
b58fdfc2 |
ofd = open(newname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU); |
47bbbc56 |
if (ofd < 0) { |
72ce4b70 |
cli_errmsg("OLE2 [handler_writefile]: failed to create file: %s\n", newname);
return CL_SUCCESS; |
47bbbc56 |
}
current_block = prop->start_block;
len = prop->size;
|
3c21733d |
buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size);
if (!buff) {
close(ofd); |
72ce4b70 |
return CL_BREAK; |
3c21733d |
} |
42438bd5 |
blk_bitset = cli_bitset_init();
if (!blk_bitset) { |
72ce4b70 |
cli_errmsg("OLE2 [handler_writefile]: init bitset failed\n"); |
42438bd5 |
close(ofd); |
ec981799 |
free(buff); |
72ce4b70 |
return CL_BREAK; |
42438bd5 |
} |
47bbbc56 |
while((current_block >= 0) && (len > 0)) { |
24555841 |
if (current_block > (int32_t) hdr->max_block_no) { |
72ce4b70 |
cli_dbgmsg("OLE2 [handler_writefile]: Max block number for file size exceeded: %d\n", current_block); |
ac6082ed |
close(ofd);
free(buff);
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_SUCCESS; |
ac6082ed |
} |
42438bd5 |
/* Check we aren't in a loop */
if (cli_bitset_test(blk_bitset, (unsigned long) current_block)) {
/* Loop in block list */ |
72ce4b70 |
cli_dbgmsg("OLE2 [handler_writefile]: Block list loop detected\n"); |
42438bd5 |
close(ofd);
free(buff);
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_BREAK; |
42438bd5 |
}
if (!cli_bitset_set(blk_bitset, (unsigned long) current_block)) {
close(ofd);
free(buff);
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_BREAK; |
42438bd5 |
} |
9d3c38ba |
if (prop->size < (int64_t)hdr->sbat_cutoff) { |
bf5a1ce7 |
/* Small block file */ |
3c21733d |
if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) { |
72ce4b70 |
cli_dbgmsg("OLE2 [handler_writefile]: ole2_get_sbat_data_block failed\n"); |
47bbbc56 |
close(ofd); |
3c21733d |
free(buff); |
42438bd5 |
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_SUCCESS; |
47bbbc56 |
} |
ec981799 |
/* buff now contains the block with N small blocks in it */
offset = (1 << hdr->log2_small_block_size) * (current_block % (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)));
if (cli_writen(ofd, &buff[offset], MIN(len,1 << hdr->log2_small_block_size)) != MIN(len,1 << hdr->log2_small_block_size)) { |
47bbbc56 |
close(ofd); |
3c21733d |
free(buff); |
42438bd5 |
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_BREAK; |
47bbbc56 |
}
|
ec981799 |
len -= MIN(len,1 << hdr->log2_small_block_size); |
47bbbc56 |
current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
} else { |
bf5a1ce7 |
/* Big block file */ |
ec981799 |
if (!ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)) { |
47bbbc56 |
close(ofd); |
3c21733d |
free(buff); |
42438bd5 |
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_SUCCESS; |
47bbbc56 |
} |
3c21733d |
if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) != |
349e0502 |
MIN(len,(1 << hdr->log2_big_block_size))) { |
47bbbc56 |
close(ofd); |
3c21733d |
free(buff); |
42438bd5 |
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_BREAK; |
47bbbc56 |
}
current_block = ole2_get_next_block_number(fd, hdr, current_block); |
349e0502 |
len -= MIN(len,(1 << hdr->log2_big_block_size)); |
47bbbc56 |
}
}
close(ofd); |
3c21733d |
free(buff); |
42438bd5 |
cli_bitset_free(blk_bitset); |
72ce4b70 |
return CL_SUCCESS;
}
/* enum file Handler - checks for VBA presence */
static int handler_enum(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx)
{
char *name;
if (!hdr->has_vba) {
name = get_property_name2(prop->name, prop->name_size);
if (name) {
if (!strcmp(name, "_vba_project") || !strcmp(name, "powerpoint document") || !strcmp(name, "worddocument") || !strcmp(name, "_1_ole10native"))
hdr->has_vba = 1;
free(name);
}
}
return CL_SUCCESS;
}
static int handler_otf(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx)
{
char *tempfile;
unsigned char *buff;
int32_t current_block, len, offset;
int ofd, ret;
bitset_t *blk_bitset;
if (prop->type != 2) {
/* Not a file */
return CL_SUCCESS;
}
print_ole2_property(prop);
|
33068e09 |
if(!(tempfile = cli_gentemp(ctx ? ctx->engine->tmpdir : NULL))) |
72ce4b70 |
return CL_EMEM;
if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
cli_dbgmsg("OLE2: Can't create file %s\n", tempfile);
free(tempfile); |
871177cd |
return CL_ECREAT; |
72ce4b70 |
}
current_block = prop->start_block;
len = prop->size;
buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size);
if (!buff) {
close(ofd);
cli_unlink(tempfile);
free(tempfile);
return CL_EMEM;
}
blk_bitset = cli_bitset_init();
if (!blk_bitset) {
cli_errmsg("OLE2: OTF handler init bitset failed\n");
free(buff);
close(ofd);
if (cli_unlink(tempfile)) {
free(tempfile); |
871177cd |
return CL_EUNLINK; |
72ce4b70 |
}
free(tempfile);
return CL_BREAK;
}
while((current_block >= 0) && (len > 0)) {
if (current_block > (int32_t) hdr->max_block_no) {
cli_dbgmsg("OLE2: Max block number for file size exceeded: %d\n", current_block); |
77fd59a8 |
break; |
72ce4b70 |
}
/* Check we aren't in a loop */
if (cli_bitset_test(blk_bitset, (unsigned long) current_block)) {
/* Loop in block list */
cli_dbgmsg("OLE2: Block list loop detected\n"); |
77fd59a8 |
break; |
72ce4b70 |
}
if (!cli_bitset_set(blk_bitset, (unsigned long) current_block)) { |
77fd59a8 |
break; |
72ce4b70 |
}
if (prop->size < (int64_t)hdr->sbat_cutoff) {
/* Small block file */
if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) {
cli_dbgmsg("ole2_get_sbat_data_block failed\n"); |
77fd59a8 |
break; |
72ce4b70 |
} |
ec981799 |
/* buff now contains the block with N small blocks in it */
offset = (1 << hdr->log2_small_block_size) * (current_block % (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)));
if (cli_writen(ofd, &buff[offset], MIN(len,1 << hdr->log2_small_block_size)) != MIN(len,1 << hdr->log2_small_block_size)) { |
72ce4b70 |
close(ofd);
free(buff);
cli_bitset_free(blk_bitset);
if (cli_unlink(tempfile)) {
free(tempfile); |
871177cd |
return CL_EUNLINK; |
72ce4b70 |
}
free(tempfile);
return CL_BREAK;
}
|
ec981799 |
len -= MIN(len,1 << hdr->log2_small_block_size); |
72ce4b70 |
current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
} else {
/* Big block file */ |
ec981799 |
if (!ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)) { |
77fd59a8 |
break; |
72ce4b70 |
}
if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) !=
MIN(len,(1 << hdr->log2_big_block_size))) {
close(ofd);
free(buff);
cli_bitset_free(blk_bitset);
if (cli_unlink(tempfile)) {
free(tempfile); |
871177cd |
return CL_EUNLINK; |
72ce4b70 |
}
free(tempfile); |
871177cd |
return CL_EWRITE; |
72ce4b70 |
}
current_block = ole2_get_next_block_number(fd, hdr, current_block);
len -= MIN(len,(1 << hdr->log2_big_block_size));
}
}
lseek(ofd, 0, SEEK_SET);
ret=cli_magic_scandesc(ofd, ctx);
close(ofd);
free(buff);
cli_bitset_free(blk_bitset); |
33068e09 |
if(ctx && !ctx->engine->keeptmp) { |
72ce4b70 |
if (cli_unlink(tempfile)) {
free(tempfile); |
871177cd |
return CL_EUNLINK; |
72ce4b70 |
}
}
free(tempfile);
return ret==CL_VIRUS ? CL_VIRUS : CL_SUCCESS;
|
47bbbc56 |
}
|
ef38c83d |
#if !defined(HAVE_ATTRIB_PACKED) && !defined(HAVE_PRAGMA_PACK) && !defined(HAVE_PRAGMA_PACK_HPPA) |
e0f90556 |
static int ole2_read_header(int fd, ole2_header_t *hdr) |
1dc96b6c |
{
int i;
|
5b25b5e8 |
if (cli_readn(fd, &hdr->magic, 8) != 8) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->clsid, 16) != 16) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->minor_version, 2) != 2) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->dll_version, 2) != 2) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->byte_order, 2) != 2) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->log2_big_block_size, 2) != 2) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->log2_small_block_size, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->reserved, 8) != 8) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->bat_count, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->prop_start, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->signature, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->sbat_cutoff, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->sbat_start, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->sbat_block_count, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->xbat_start, 4) != 4) { |
1dc96b6c |
return FALSE;
} |
5b25b5e8 |
if (cli_readn(fd, &hdr->xbat_count, 4) != 4) { |
1dc96b6c |
return FALSE;
}
for (i=0 ; i < 109 ; i++) { |
5b25b5e8 |
if (cli_readn(fd, &hdr->bat_array[i], 4) != 4) { |
1dc96b6c |
return FALSE;
}
}
return TRUE;
} |
bda5598b |
#endif |
1dc96b6c |
|
72ce4b70 |
int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx, struct uniq **vba) |
47bbbc56 |
{
ole2_header_t hdr; |
72ce4b70 |
int hdr_size, ret=CL_CLEAN; |
8c030302 |
struct stat statbuf; |
bda5598b |
unsigned int file_count=0; |
72ce4b70 |
unsigned long scansize, scansize2; |
08d0fdc5 |
|
47bbbc56 |
cli_dbgmsg("in cli_ole2_extract()\n"); |
08d0fdc5 |
|
724b2bf7 |
if (ctx && ctx->engine->maxscansize) {
if (ctx->engine->maxscansize > ctx->scansize)
scansize = ctx->engine->maxscansize - ctx->scansize; |
08d0fdc5 |
else
return CL_EMAXSIZE;
} else scansize = -1; |
72ce4b70 |
scansize2 = scansize; |
1dc96b6c |
|
349e0502 |
/* size of header - size of other values in struct */ |
72ce4b70 |
hdr_size = sizeof(struct ole2_header_tag) - sizeof(int32_t) - sizeof(uint32_t) - |
ac6082ed |
sizeof(unsigned char *) - sizeof(off_t) - sizeof(bitset_t *) - |
72ce4b70 |
sizeof(struct uniq *) - sizeof(int); |
1dc96b6c |
|
8c030302 |
hdr.m_area = NULL;
if (fstat(fd, &statbuf) == 0) { |
4c64f434 |
if (statbuf.st_size < hdr_size) { |
72ce4b70 |
return CL_CLEAN; |
4c64f434 |
} |
ac6082ed |
#ifdef HAVE_MMAP |
8c030302 |
hdr.m_length = statbuf.st_size;
hdr.m_area = (unsigned char *) mmap(NULL, hdr.m_length, PROT_READ, MAP_PRIVATE, fd, 0);
if (hdr.m_area == MAP_FAILED) {
hdr.m_area = NULL; |
c96406a0 |
} else {
cli_dbgmsg("mmap'ed file\n");
memcpy(&hdr, hdr.m_area, hdr_size); |
8c030302 |
} |
1dc96b6c |
#endif |
ac6082ed |
} |
349e0502 |
|
8c030302 |
if (hdr.m_area == NULL) { |
72ce4b70 |
hdr.bitset = NULL; |
ef38c83d |
#if defined(HAVE_ATTRIB_PACKED) || defined(HAVE_PRAGMA_PACK) || defined(HAVE_PRAGMA_PACK_HPPA) |
8c030302 |
if (cli_readn(fd, &hdr, hdr_size) != hdr_size) { |
72ce4b70 |
goto abort; |
8c030302 |
}
#else
if (!ole2_read_header(fd, &hdr)) { |
72ce4b70 |
goto abort; |
8c030302 |
}
#endif
}
|
c83b8845 |
hdr.minor_version = ole2_endian_convert_16(hdr.minor_version);
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.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);
hdr.sbat_start = ole2_endian_convert_32(hdr.sbat_start);
hdr.sbat_block_count = ole2_endian_convert_32(hdr.sbat_block_count);
hdr.xbat_start = ole2_endian_convert_32(hdr.xbat_start);
hdr.xbat_count = ole2_endian_convert_32(hdr.xbat_count);
|
349e0502 |
hdr.sbat_root_start = -1; |
8a9c2d19 |
hdr.bitset = cli_bitset_init(); |
77fd59a8 |
if (!hdr.bitset) { |
871177cd |
ret=CL_EMEM; |
77fd59a8 |
goto abort; |
8a9c2d19 |
}
|
bda5598b |
if (memcmp(hdr.magic, magic_id, 8) != 0) { |
47bbbc56 |
cli_dbgmsg("OLE2 magic failed!\n"); |
871177cd |
ret=CL_EFORMAT; |
72ce4b70 |
goto abort; |
47bbbc56 |
}
|
ec981799 |
if (hdr.log2_big_block_size < 6 || hdr.log2_big_block_size > 30) {
cli_dbgmsg("CAN'T PARSE: Invalid big block size (2^%u)\n", hdr.log2_big_block_size); |
4b6040a4 |
goto abort; |
47bbbc56 |
} |
ec981799 |
if (!hdr.log2_small_block_size || hdr.log2_small_block_size > hdr.log2_big_block_size) {
cli_dbgmsg("CAN'T PARSE: Invalid small block size (2^%u)\n", hdr.log2_small_block_size); |
4b6040a4 |
goto abort; |
47bbbc56 |
} |
ec981799 |
|
47bbbc56 |
if (hdr.sbat_cutoff != 4096) { |
ec981799 |
cli_dbgmsg("WARNING: Untested sbat cutoff (%u); data may not extract correctly\n", hdr.sbat_cutoff); |
47bbbc56 |
} |
7df56d47 |
/* 8 SBAT blocks per file block */ |
ec981799 |
hdr.max_block_no = (statbuf.st_size - MAX(512, 1 << hdr.log2_big_block_size)) / (1 << hdr.log2_small_block_size);
|
47bbbc56 |
print_ole2_header(&hdr); |
24555841 |
cli_dbgmsg("Max block number: %lu\n", (unsigned long int) hdr.max_block_no); |
47bbbc56 |
|
72ce4b70 |
/* PASS 1 : Count files and check for VBA */
hdr.has_vba = 0;
ret = ole2_walk_property_tree(fd, &hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize);
cli_bitset_free(hdr.bitset);
hdr.bitset = NULL;
if (!file_count || !(hdr.bitset = cli_bitset_init()))
goto abort;
/* If there's no VBA we scan OTF */
if (hdr.has_vba) {
/* PASS 2/A : VBA scan */
cli_dbgmsg("OLE2: VBA project found\n");
if (!(hdr.U = uniq_init(file_count))) {
cli_dbgmsg("OLE2: uniq_init() failed\n");
ret = CL_EMEM;
goto abort;
}
file_count = 0;
ole2_walk_property_tree(fd, &hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2);
ret = CL_CLEAN;
*vba = hdr.U;
} else { |
77fd59a8 |
cli_dbgmsg("OLE2: no VBA projects found\n"); |
72ce4b70 |
/* PASS 2/B : OTF scan */
file_count = 0; |
b0c7e984 |
if(ctx)
ret = ole2_walk_property_tree(fd, &hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2); |
72ce4b70 |
} |
8c030302 |
|
4b6040a4 |
abort: |
8c030302 |
#ifdef HAVE_MMAP
if (hdr.m_area != NULL) {
munmap(hdr.m_area, hdr.m_length);
}
#endif |
72ce4b70 |
if(hdr.bitset)
cli_bitset_free(hdr.bitset);
|
2624c770 |
return ret == CL_BREAK ? CL_CLEAN : ret; |
47bbbc56 |
} |