a4b30692 |
/* |
c442ca9c |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc. |
a4b30692 |
*
* Authors: aCaB <acab@clamav.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* common routines to deal with installshield archives and installers */
|
7ebede9b |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif |
9c3c24ca |
#if HAVE_STRING_H |
7ebede9b |
#include <string.h> |
9c3c24ca |
#endif
#include <limits.h> |
86d59b24 |
#if HAVE_STRINGS_H |
a8271e9d |
#include <strings.h> |
86d59b24 |
#endif |
11195c0b |
#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
#include <sys/mman.h>
#endif |
7ebede9b |
#include <zlib.h>
|
60d8d2c3 |
#include "clamav.h" |
7ebede9b |
#include "scanners.h"
#include "others.h" |
303f7b34 |
#include "fmap.h" |
7ebede9b |
#include "ishield.h"
|
9c3c24ca |
#ifndef LONG_MAX
#define LONG_MAX ((-1UL)>>1)
#endif
|
a4b30692 |
#ifndef HAVE_ATTRIB_PACKED
#define __attribute__(x)
#endif
#ifdef HAVE_PRAGMA_PACK
#pragma pack(1)
#endif
#ifdef HAVE_PRAGMA_PACK_HPPA
#pragma pack 1
#endif
/* PACKED things go here */
|
9a4da6af |
struct IS_HDR {
uint32_t magic;
uint32_t unk1; /* version ??? */
uint32_t unk2; /* ??? */
uint32_t data_off;
uint32_t data_sz; /* ??? */
} __attribute__((packed));
|
a4b30692 |
struct IS_FB {
char fname[0x104]; /* MAX_PATH */
uint32_t unk1; /* 6 */
uint32_t unk2;
uint64_t csize;
uint32_t unk3;
uint32_t unk4; /* 1 */
uint32_t unk5;
uint32_t unk6;
uint32_t unk7;
uint32_t unk8;
uint32_t unk9;
uint32_t unk10;
uint32_t unk11;
} __attribute__((packed));
|
1d7cebd8 |
struct IS_COMPONENT {
uint32_t str_name_off;
uint32_t unk_str1_off;
uint32_t unk_str2_off;
uint16_t unk_flags;
uint32_t unk_str3_off;
uint32_t unk_str4_off;
uint16_t ordinal_id;
uint32_t str_shortname_off;
uint32_t unk_str6_off;
uint32_t unk_str7_off;
uint32_t unk_str8_off;
char guid1[16];
char guid2[16];
uint32_t unk_str9_off;
char unk1[3];
uint16_t unk_flags2;
uint32_t unk3[5];
uint32_t unk_str10_off;
uint32_t unk4[4];
uint16_t unk5;
uint16_t sub_comp_cnt;
uint32_t sub_comp_offs_array;
uint32_t next_comp_off;
uint32_t unk_str11_off;
uint32_t unk_str12_off;
uint32_t unk_str13_off;
uint32_t unk_str14_off; |
1b98609f |
uint32_t str_next1_off;
uint32_t str_next2_off; |
1d7cebd8 |
} __attribute__((packed));
struct IS_INSTTYPEHDR {
uint32_t unk1;
uint32_t cnt;
uint32_t off;
} __attribute__((packed));
struct IS_INSTTYPEITEM {
uint32_t str_name1_off;
uint32_t str_name2_off;
uint32_t str_name3_off;
uint32_t cnt;
uint32_t off;
} __attribute__((packed));
struct IS_OBJECTS {
/* 200 */ uint32_t strings_off;
/* 204 */ uint32_t zero1;
/* 208 */ uint32_t comps_off;
/* 20c */ uint32_t dirs_off;
/* 210 */ uint32_t zero2;
/* 214 */ uint32_t unk1, unk2; /* 0x4a636 304694 uguali - NOT AN OFFSET! */
/* 21c */ uint32_t dirs_cnt;
/* 220 */ uint32_t zero3;
/* 224 */ uint32_t dirs_sz; /* dirs_cnt * 4 */
/* 228 */ uint32_t files_cnt;
/* 22c */ uint32_t dir_sz2; /* same as dirs_sz ?? */
/* 230 */ uint16_t unk5; /* 1 - comp count ?? */
/* 232 */ uint32_t insttype_off;
/* 234 */ uint16_t zero4;
/* 238 */ uint32_t zero5;
/* 23c */ uint32_t unk7; /* 0xd0 - 208 */
/* 240 */ uint16_t unk8;
/* 242 */ uint32_t unk9;
/* 246 */ uint32_t unk10;
} __attribute__((packed));
struct IS_FILEITEM {
uint16_t flags; /* 0 = EXTERNAL | 4 = INTERNAL | 8 = NAME_fuckup_rare | c = name_fuckup_common */
uint64_t size;
uint64_t csize;
uint64_t stream_off;
uint8_t md5[16];
uint64_t versioninfo_id;
uint32_t zero1;
uint32_t zero2;
uint32_t str_name_off;
uint16_t dir_id;
uint32_t unk13; /* 0, 20, 21 ??? */
uint32_t unk14; /* timestamp ??? */
uint32_t unk15; /* begins with 1 then 2 but not the cab# ??? */
uint32_t prev_dup_id; /* msvcrt #38(0, 97, 2) #97(38, 1181, 3) ... , 0, 1) */
uint32_t next_dup_id;
uint8_t flag_has_dup; /* HAS_NEXT = 2 | HAS_BOTH = 3 | HAS_PREV = 1 */
uint16_t datafile_id;
} __attribute__((packed));
|
a4b30692 |
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif
#ifdef HAVE_PRAGMA_PACK_HPPA
#pragma pack
#endif
|
7ebede9b |
|
a4b30692 |
|
9a4da6af |
static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize); |
a8271e9d |
static const uint8_t skey[] = { 0xec, 0xca, 0x79, 0xf8 }; /* ~0x13, ~0x35, ~0x86, ~0x07 */ |
a4b30692 |
|
1d7cebd8 |
/* Extracts the content of MSI based IS */ |
9a4da6af |
int cli_scanishield_msi(cli_ctx *ctx, off_t off) { |
f304dc68 |
const uint8_t *buf; |
12a812db |
unsigned int fcount, scanned = 0; |
7ebede9b |
int ret; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
7ebede9b |
cli_dbgmsg("in ishield-msi\n"); |
9a4da6af |
if(!(buf = fmap_need_off_once(map, off, 0x20))) { |
7ebede9b |
cli_dbgmsg("ishield-msi: short read for header\n");
return CL_CLEAN;
} |
9a4da6af |
off += 0x20; |
12a812db |
if(cli_readint32(buf + 8) | cli_readint32(buf + 0xc) | cli_readint32(buf + 0x10) | cli_readint32(buf + 0x14) | cli_readint32(buf + 0x18) | cli_readint32(buf + 0x1c))
return CL_CLEAN; |
7ebede9b |
if(!(fcount = cli_readint32(buf))) {
cli_dbgmsg("ishield-msi: no files?\n");
return CL_CLEAN;
}
while(fcount--) { |
a4b30692 |
struct IS_FB fb; |
7ebede9b |
uint8_t obuf[BUFSIZ], *key = (uint8_t *)&fb.fname;
char *tempfile;
unsigned int i, lameidx=0, keylen;
int ofd;
uint64_t csize;
z_stream z;
|
9a4da6af |
if(fmap_readn(map, &fb, off, sizeof(fb)) != sizeof(fb)) { |
7ebede9b |
cli_dbgmsg("ishield-msi: short read for fileblock\n");
return CL_CLEAN;
} |
9a4da6af |
off += sizeof(fb); |
7ebede9b |
fb.fname[sizeof(fb.fname)-1] = '\0';
csize = le64_to_host(fb.csize); |
9a4da6af |
if(!CLI_ISCONTAINED(0, map->len, off, csize)) {
cli_dbgmsg("ishield-msi: next stream is out of file, giving up\n");
return CL_CLEAN;
} |
12a812db |
if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", (unsigned long int) csize, (unsigned long int) ctx->engine->maxfilesize); |
9a4da6af |
off += csize; |
12a812db |
continue;
}
|
7ebede9b |
keylen = strlen((const char *)key);
if(!keylen) return CL_CLEAN; |
12a812db |
/* FIXMEISHIELD: cleanup the spam below */ |
4cda88cc |
cli_dbgmsg("ishield-msi: File %s (csize: %llx, unk1:%x unk2:%x unk3:%x unk4:%x unk5:%x unk6:%x unk7:%x unk8:%x unk9:%x unk10:%x unk11:%x)\n", key, (long long)csize, fb.unk1, fb.unk2, fb.unk3, fb.unk4, fb.unk5, fb.unk6, fb.unk7, fb.unk8, fb.unk9, fb.unk10, fb.unk11); |
7ebede9b |
if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM;
if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
cli_dbgmsg("ishield-msi: failed to create file %s\n", tempfile);
free(tempfile);
return CL_ECREAT;
}
for(i=0; i<keylen; i++)
key[i] ^= skey[i & 3];
memset(&z, 0, sizeof(z));
inflateInit(&z); |
4cda88cc |
ret = CL_SUCCESS; |
7ebede9b |
while(csize) { |
9a4da6af |
uint8_t buf2[BUFSIZ];
z.avail_in = MIN(csize, sizeof(buf2)); |
cd94be7a |
if((uInt)fmap_readn(map, buf2, off, z.avail_in) != z.avail_in) { |
7ebede9b |
cli_dbgmsg("ishield-msi: premature EOS or read fail\n"); |
9a4da6af |
break; |
7ebede9b |
} |
9a4da6af |
off += z.avail_in; |
7ebede9b |
for(i=0; i<z.avail_in; i++, lameidx++) { |
9a4da6af |
uint8_t c = buf2[i]; |
7ebede9b |
c = (c>>4) | (c<<4);
c ^= key[(lameidx & 0x3ff) % keylen]; |
9a4da6af |
buf2[i] = c; |
7ebede9b |
}
csize -= z.avail_in; |
9a4da6af |
z.next_in = buf2; |
7ebede9b |
do { |
f0b36be4 |
int inf; |
7ebede9b |
z.avail_out = sizeof(obuf);
z.next_out = obuf; |
f0b36be4 |
inf = inflate(&z, 0);
if(inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) { |
7ebede9b |
cli_dbgmsg("ishield-msi: bad stream\n");
csize = 0; |
9a4da6af |
off += csize; |
7ebede9b |
break;
} |
4cda88cc |
if (cli_writen(ofd, obuf, sizeof(obuf) - z.avail_out) < 0) {
ret = CL_EWRITE;
csize = 0;
break;
} |
12a812db |
if(ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("ishield-msi: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int) ctx->engine->maxfilesize); |
9a4da6af |
off += csize; |
12a812db |
csize = 0;
break;
} |
7ebede9b |
} while (!z.avail_out);
}
inflateEnd(&z);
|
4cda88cc |
if (ret == CL_SUCCESS) {
cli_dbgmsg("ishield-msi: extracted to %s\n", tempfile); |
7ebede9b |
|
96914546 |
if (lseek(ofd, 0, SEEK_SET) == -1) {
cli_dbgmsg("ishield-msi: call to lseek() failed\n");
ret = CL_ESEEK;
} |
d39cb658 |
ret = cli_magic_scandesc(ofd, tempfile, ctx); |
4cda88cc |
} |
7ebede9b |
close(ofd);
if(!ctx->engine->keeptmp)
if(cli_unlink(tempfile)) ret = CL_EUNLINK;
free(tempfile);
if(ret != CL_CLEAN)
return ret; |
12a812db |
scanned++;
if (ctx->engine->maxfiles && scanned>=ctx->engine->maxfiles) {
cli_dbgmsg("ishield-msi: File limit reached (max: %u)\n", ctx->engine->maxfiles);
return CL_EMAXFILES;
} |
7ebede9b |
}
return CL_CLEAN;
} |
56e5821b |
|
1d7cebd8 |
struct IS_CABSTUFF { |
a8271e9d |
struct CABARRAY {
unsigned int cabno;
off_t off;
size_t sz;
} *cabs;
off_t hdr;
size_t hdrsz;
unsigned int cabcnt;
};
|
1d7cebd8 |
static void md5str(uint8_t *sum); |
9a4da6af |
static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c); |
430a6261 |
static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize); |
56e5821b |
|
1d7cebd8 |
/* Extract the content of older (non-MSI) IS */ |
9a4da6af |
int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz) { |
f304dc68 |
const char *fname, *path, *version, *strsz, *data;
char *eostr; |
9a4da6af |
int ret = CL_CLEAN; |
a8271e9d |
long fsize; |
56e5821b |
off_t coff = off; |
1d7cebd8 |
struct IS_CABSTUFF c = { NULL, -1, 0, 0 }; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
248c2f4d |
unsigned fc = 0; |
7a307529 |
int virus_found = 0; |
56e5821b |
|
a8271e9d |
while(ret == CL_CLEAN) { |
9a4da6af |
fname = fmap_need_offstr(map, coff, 2048);
if(!fname) break;
coff += strlen(fname) + 1; |
56e5821b |
|
9a4da6af |
path = fmap_need_offstr(map, coff, 2048);
if(!path) break;
coff += strlen(path) + 1; |
56e5821b |
|
9a4da6af |
version = fmap_need_offstr(map, coff, 2048);
if(!version) break;
coff += strlen(version) + 1; |
56e5821b |
|
9a4da6af |
strsz = fmap_need_offstr(map, coff, 2048);
if(!strsz) break;
coff += strlen(strsz) + 1; |
56e5821b |
|
9a4da6af |
data = &strsz[strlen(strsz) + 1]; |
56e5821b |
fsize = strtol(strsz, &eostr, 10); |
1b98609f |
if(fsize < 0 || fsize == LONG_MAX ||
!*strsz || !eostr || eostr == strsz || *eostr || |
a8271e9d |
(unsigned long)fsize >= sz || |
cd94be7a |
(size_t)(data - fname) >= sz - fsize |
1b98609f |
) break; |
56e5821b |
|
e78b5186 |
cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int) coff, fname, path, version, (unsigned long int) fsize); |
248c2f4d |
if(cli_matchmeta(ctx, fname, fsize, fsize, 0, fc++, 0, NULL) == CL_VIRUS) { |
d7979d4f |
if (!SCAN_ALLMATCHES) { |
7a307529 |
ret = CL_VIRUS;
break;
}
ret = CL_CLEAN;
virus_found = 1; |
248c2f4d |
} |
9a4da6af |
sz -= (data - fname) + fsize;
|
a8271e9d |
if(!strncasecmp(fname, "data", 4)) {
long cabno;
if(!strcasecmp(fname + 4, "1.hdr")) {
if(c.hdr == -1) { |
c19515d5 |
cli_dbgmsg("ishield: added data1.hdr to array\n"); |
a8271e9d |
c.hdr = coff;
c.hdrsz = fsize;
coff += fsize;
continue;
}
cli_warnmsg("ishield: got multiple header files\n");
}
cabno = strtol(fname + 4, &eostr, 10);
if(cabno > 0 && cabno < 65536 && fname[4] && eostr && eostr != &fname[4] && !strcasecmp(eostr, ".cab")) {
unsigned int i;
for(i=0; i<c.cabcnt && i!=c.cabs[i].cabno; i++) { }
if(i==c.cabcnt) {
c.cabcnt++;
if(!(c.cabs = cli_realloc2(c.cabs, sizeof(struct CABARRAY) * c.cabcnt))) {
ret = CL_EMEM;
break;
} |
c19515d5 |
cli_dbgmsg("ishield: added data%lu.cab to array\n", cabno); |
a8271e9d |
c.cabs[i].cabno = cabno;
c.cabs[i].off = coff;
c.cabs[i].sz = fsize;
coff += fsize;
continue;
}
cli_warnmsg("ishield: got multiple data%lu.cab files\n", cabno);
}
}
|
9a4da6af |
fmap_unneed_ptr(map, fname, data-fname);
ret = is_dump_and_scan(ctx, coff, fsize); |
a8271e9d |
coff += fsize;
} |
56e5821b |
|
a8271e9d |
if(ret == CL_CLEAN && (c.cabcnt || c.hdr != -1)) { |
9a4da6af |
if((ret = is_parse_hdr(ctx, &c)) == CL_CLEAN) { |
a8271e9d |
unsigned int i; |
c19515d5 |
if(c.hdr != -1) { |
1b98609f |
cli_dbgmsg("ishield: scanning data1.hdr\n"); |
9a4da6af |
ret = is_dump_and_scan(ctx, c.hdr, c.hdrsz); |
c19515d5 |
} |
a8271e9d |
for(i=0; i<c.cabcnt && ret == CL_CLEAN; i++) { |
1b98609f |
cli_dbgmsg("ishield: scanning data%u.cab\n", c.cabs[i].cabno); |
9a4da6af |
ret = is_dump_and_scan(ctx, c.cabs[i].off, c.cabs[i].sz); |
a8271e9d |
} |
1b98609f |
} else if( ret == CL_BREAK ) ret = CL_CLEAN; |
56e5821b |
} |
a8271e9d |
if(c.cabs) free(c.cabs); |
7a307529 |
if (virus_found != 0)
return CL_VIRUS; |
1b98609f |
return ret; |
56e5821b |
} |
a8271e9d |
|
1d7cebd8 |
/* Utility func to scan a fd @ a given offset and size */ |
9a4da6af |
static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize) { |
f304dc68 |
char *fname;
const char *buf; |
a8271e9d |
int ofd, ret = CL_CLEAN; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
a8271e9d |
if(!fsize) { |
1b98609f |
cli_dbgmsg("ishield: skipping empty file\n"); |
a8271e9d |
return CL_CLEAN;
}
if(!(fname = cli_gentemp(ctx->engine->tmpdir)))
return CL_EMEM;
if((ofd = open(fname, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
cli_errmsg("ishield: failed to create file %s\n", fname);
free(fname);
return CL_ECREAT;
}
while(fsize) { |
9a4da6af |
size_t rd = MIN(fsize, map->pgsz);
if(!(buf = fmap_need_off_once(map, off, rd))) { |
1b98609f |
cli_dbgmsg("ishield: read error\n"); |
a8271e9d |
ret = CL_EREAD;
break;
} |
9a4da6af |
if(cli_writen(ofd, buf, rd) <= 0) { |
a8271e9d |
ret = CL_EWRITE;
break;
} |
9a4da6af |
fsize -= rd;
off += rd; |
a8271e9d |
}
if(!fsize) { |
1b98609f |
cli_dbgmsg("ishield: extracted to %s\n", fname); |
96914546 |
if (lseek(ofd, 0, SEEK_SET) == -1) {
cli_dbgmsg("ishield: call to lseek() failed\n");
ret = CL_ESEEK;
} |
d39cb658 |
ret = cli_magic_scandesc(ofd, fname, ctx); |
a8271e9d |
}
close(ofd);
if(!ctx->engine->keeptmp)
if(cli_unlink(fname)) ret = CL_EUNLINK;
free(fname);
return ret;
} |
1d7cebd8 |
|
1b98609f |
/* Process data1.hdr and extracts all the available files from dataX.cab */ |
9a4da6af |
static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c) { |
1d7cebd8 |
uint32_t h1_data_off, objs_files_cnt, objs_dirs_off; |
1b98609f |
unsigned int off, i, scanned = 0;
int ret = CL_BREAK; |
ba65fdc8 |
char hash[33], *hdr; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
1d7cebd8 |
|
f304dc68 |
const struct IS_HDR *h1; |
1d7cebd8 |
struct IS_OBJECTS *objs;
/* struct IS_INSTTYPEHDR *typehdr; -- UNUSED */
|
1b98609f |
if(!c->hdr || !c->hdrsz || !c->cabcnt) {
cli_dbgmsg("is_parse_hdr: inconsistent hdr, maybe a false match\n");
return CL_CLEAN;
} |
1d7cebd8 |
|
9a4da6af |
if(!(h1 = fmap_need_off(map, c->hdr, c->hdrsz))) { |
1d7cebd8 |
cli_dbgmsg("is_parse_hdr: not enough room for H1\n");
return CL_CLEAN;
} |
9a4da6af |
hdr = (char *)h1; |
1d7cebd8 |
h1_data_off = le32_to_host(h1->data_off); |
303f7b34 |
objs = (struct IS_OBJECTS *)fmap_need_ptr(map, hdr + h1_data_off, sizeof(*objs));
if(!objs) { |
9a4da6af |
cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
return CL_CLEAN; |
1d7cebd8 |
}
|
1b98609f |
cli_dbgmsg("is_parse_hdr: magic %x, unk1 %x, unk2 %x, data_off %x, data_sz %x\n", |
9a4da6af |
h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz); |
1d7cebd8 |
if(le32_to_host(h1->magic) != 0x28635349) { |
9a4da6af |
cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
return CL_CLEAN; |
1d7cebd8 |
}
|
1ae65c29 |
fmap_unneed_ptr(map, h1, sizeof(*h1));
|
1d7cebd8 |
/* cli_errmsg("COMPONENTS\n"); */
/* off = le32_to_host(objs->comps_off) + h1_data_off; */
/* for(i=1; ; i++) { */
/* struct IS_COMPONENT *cmp = (struct IS_COMPONENT *)(hdr + off); */
/* if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)cmp), sizeof(*cmp))) { */
/* cli_dbgmsg("is_extract: not enough room for COMPONENT\n"); */
/* free(hdr); */
/* return CL_CLEAN; */
/* } */
/* cli_errmsg("%06u\t%s\n", i, &hdr[le32_to_host(cmp->str_name_off) + h1_data_off]); */
/* spam_strarray(hdr, h1_data_off + cmp->sub_comp_offs_array, h1_data_off, cmp->sub_comp_cnt); */
/* if(!cmp->next_comp_off) break; */
/* off = le32_to_host(cmp->next_comp_off) + h1_data_off; */
/* } */
/* cli_errmsg("DIRECTORIES (%u)", le32_to_host(objs->dirs_cnt)); */
objs_dirs_off = le32_to_host(objs->dirs_off);
/* spam_strarray(hdr, h1_data_off + objs_dirs_off, h1_data_off + objs_dirs_off, objs->dirs_cnt); */
/* typehdr = (struct INSTTYPEHDR *)&hdr[h1_data_off + le32_to_host(objs->insttype_off)]; */
/* printf("INSTTYPES (unk1: %d)\n-----------\n", typehdr->unk1); */
/* off = typehdr->off + h1_data_off; */
/* for(i=1; i<=typehdr->cnt; i++) { */
/* uint32_t x = *(uint32_t *)(&hdr[off]); */
/* struct INSTTYPEITEM *item = (struct INSTTYPEITEM *)&hdr[x + h1_data_off]; */
/* printf("%06u\t%s\t aka %s\taka %s\n", i, &hdr[item->str_name1_off + h1_data_off], &hdr[item->str_name2_off + h1_data_off], &hdr[item->str_name3_off + h1_data_off]); */
/* printf("components:\n"); */
/* spam_strarray(hdr, h1_data_off + item->off, h1_data_off, item->cnt); */
/* off+=4; */
/* } */
/* dir = &hdr[*(uint32_t *)(&hdr[h1_data_off + objs_dirs_off + 4 * file->dir_id]) + h1_data_off + objs_dirs_off] */
objs_files_cnt = le32_to_host(objs->files_cnt);
off = h1_data_off + objs_dirs_off + le32_to_host(objs->dir_sz2); |
1ae65c29 |
fmap_unneed_ptr(map, objs, sizeof(*objs)); |
1d7cebd8 |
for(i=0; i<objs_files_cnt ;i++) { |
9a4da6af |
struct IS_FILEITEM *file = (struct IS_FILEITEM *)fmap_need_off(map, c->hdr + off, sizeof(*file)); |
1d7cebd8 |
|
303f7b34 |
if(file) { |
1ae65c29 |
const char *emptyname = "", *dir_name = emptyname, *file_name = emptyname; |
1d7cebd8 |
uint32_t dir_rel = h1_data_off + objs_dirs_off + 4 * le32_to_host(file->dir_id); /* rel off of dir entry from array of rel ptrs */
uint32_t file_rel = objs_dirs_off + h1_data_off + le32_to_host(file->str_name_off); /* rel off of fname */ |
4f841728 |
uint64_t file_stream_off, file_size, file_csize;
uint16_t cabno; |
1d7cebd8 |
memcpy(hash, file->md5, 16);
md5str((uint8_t *)hash); |
f52b5724 |
if(fmap_need_ptr_once(map, &hdr[dir_rel], 4)) { |
1d7cebd8 |
dir_rel = cli_readint32(&hdr[dir_rel]) + h1_data_off + objs_dirs_off; |
303f7b34 |
if(fmap_need_str(map, &hdr[dir_rel], c->hdrsz - dir_rel)) |
1d7cebd8 |
dir_name = &hdr[dir_rel];
} |
303f7b34 |
if(fmap_need_str(map, &hdr[file_rel], c->hdrsz - file_rel)) |
1d7cebd8 |
file_name = &hdr[file_rel];
|
4f841728 |
file_stream_off = le64_to_host(file->stream_off);
file_size = le64_to_host(file->size);
file_csize = le64_to_host(file->csize);
cabno = le16_to_host(file->datafile_id);
|
1b98609f |
switch(le16_to_host(file->flags)) { |
1d7cebd8 |
case 0:
/* FIXMEISHIELD: for FS scan ? */ |
1b98609f |
cli_dbgmsg("is_parse_hdr: skipped external file:%s\\%s (size: %llu csize: %llu md5:%s)\n", |
1d7cebd8 |
dir_name,
file_name, |
4cda88cc |
(long long)file_size, (long long)file_csize, hash); |
1d7cebd8 |
break;
case 4: |
1b98609f |
cli_dbgmsg("is_parse_hdr: file %s\\%s (size: %llu csize: %llu md5:%s offset:%llx (data%u.cab) 13:%x 14:%x 15:%x)\n", |
1d7cebd8 |
dir_name,
file_name, |
4cda88cc |
(long long)file_size, (long long)file_csize, hash, (long long)file_stream_off, |
4f841728 |
cabno, file->unk13, file->unk14, file->unk15); |
1d7cebd8 |
if(file->flag_has_dup & 1) |
1b98609f |
cli_dbgmsg("is_parse_hdr: not scanned (dup)\n"); |
1d7cebd8 |
else { |
1b98609f |
if(file_size) { |
c19515d5 |
unsigned int j; |
1b98609f |
int cabret = CL_CLEAN;
if(ctx->engine->maxfilesize && file_csize > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("is_parse_hdr: skipping file due to size limits (%lu vs %lu)\n", (unsigned long int) file_csize, (unsigned long int) ctx->engine->maxfilesize); |
1b98609f |
break;
}
|
c19515d5 |
for(j=0; j<c->cabcnt && c->cabs[j].cabno != cabno; j++) {}
if(j != c->cabcnt) { |
1b98609f |
if(CLI_ISCONTAINED(c->cabs[j].off, c->cabs[j].sz, file_stream_off + c->cabs[j].off, file_csize)) {
scanned++;
if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
cli_dbgmsg("is_parse_hdr: File limit reached (max: %u)\n", ctx->engine->maxfiles); |
9a4da6af |
if(file_name != emptyname)
fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
if(dir_name != emptyname)
fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1); |
1b98609f |
return CL_EMAXFILES;
} |
430a6261 |
cabret = is_extract_cab(ctx, file_stream_off + c->cabs[j].off, file_size, file_csize); |
1b98609f |
} else {
ret = CL_CLEAN; |
c19515d5 |
cli_dbgmsg("is_parse_hdr: stream out of file\n"); |
1b98609f |
} |
c19515d5 |
} else { |
1b98609f |
ret = CL_CLEAN; |
c19515d5 |
cli_dbgmsg("is_parse_hdr: data%u.cab not available\n", cabno); |
4f841728 |
} |
1b98609f |
if(cabret == CL_BREAK) {
ret = CL_CLEAN;
cabret = CL_CLEAN;
}
if(cabret != CL_CLEAN) { |
9a4da6af |
if(file_name != emptyname)
fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
if(dir_name != emptyname)
fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1); |
1b98609f |
return cabret; |
f0b36be4 |
} |
c19515d5 |
} else {
cli_dbgmsg("is_parse_hdr: skipped empty file\n"); |
1d7cebd8 |
}
}
break;
default:
cli_dbgmsg("is_parse_hdr: skipped unknown file entry %u\n", i);
} |
1ae65c29 |
if(file_name != emptyname) |
9a4da6af |
fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1); |
1ae65c29 |
if(dir_name != emptyname) |
9a4da6af |
fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1); |
f52b5724 |
fmap_unneed_ptr(map, file, sizeof(*file)); |
1b98609f |
} else {
ret = CL_CLEAN;
cli_dbgmsg("is_parse_hdr: FILEITEM out of bounds\n");
}
off += sizeof(*file); |
1d7cebd8 |
} |
1b98609f |
return ret; |
1d7cebd8 |
}
static void md5str(uint8_t *sum) {
int i;
for(i=15; i>=0; i--) {
uint8_t lo = (sum[i] & 0xf), hi = (sum[i] >> 4);
lo += '0' + (lo > 9) * '\'';
hi += '0' + (hi > 9) * '\'';
sum[i*2+1] = lo;
sum[i*2] = hi;
}
sum[32] = '\0';
} |
f0b36be4 |
#define IS_CABBUFSZ 65536
|
430a6261 |
static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize) { |
f304dc68 |
const uint8_t *inbuf;
uint8_t *outbuf; |
f0b36be4 |
char *tempfile;
int ofd, ret = CL_CLEAN;
z_stream z;
uint64_t outsz = 0;
int success = 0; |
49cc1e3c |
fmap_t *map = *ctx->fmap; |
f0b36be4 |
|
241e7eb1 |
if(!(outbuf = cli_malloc(IS_CABBUFSZ))) {
cli_errmsg("is_extract_cab: Unable to allocate memory for outbuf\n");
return CL_EMEM;
} |
430a6261 |
|
9c3c24ca |
if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) {
free(outbuf); |
13529b8e |
return CL_EMEM; |
9c3c24ca |
} |
f0b36be4 |
if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
cli_errmsg("is_extract_cab: failed to create file %s\n", tempfile);
free(tempfile); |
9c3c24ca |
free(outbuf); |
f0b36be4 |
return CL_ECREAT;
}
while(csize) {
uint16_t chunksz;
success = 0;
if(csize<2) { |
1b98609f |
cli_dbgmsg("is_extract_cab: no room for chunk size\n"); |
f0b36be4 |
break;
}
csize -= 2; |
430a6261 |
if(!(inbuf = fmap_need_off_once(map, off, 2))) { |
f0b36be4 |
cli_dbgmsg("is_extract_cab: short read for chunk size\n");
break;
} |
430a6261 |
off += 2;
chunksz = inbuf[0] | (inbuf[1] << 8); |
f0b36be4 |
if(!chunksz) {
cli_dbgmsg("is_extract_cab: zero sized chunk\n");
continue;
}
if(csize < chunksz) {
cli_dbgmsg("is_extract_cab: chunk is bigger than csize\n");
break;
}
csize -= chunksz; |
430a6261 |
if(!(inbuf = fmap_need_off_once(map, off, chunksz))) { |
f0b36be4 |
cli_dbgmsg("is_extract_cab: short read for chunk\n");
break;
} |
430a6261 |
off += chunksz; |
f0b36be4 |
memset(&z, 0, sizeof(z));
inflateInit2(&z, -MAX_WBITS);
z.next_in = (uint8_t *)inbuf;
z.avail_in = chunksz;
while(1) {
int zret;
z.next_out = outbuf;
z.avail_out = IS_CABBUFSZ;
zret = inflate(&z, 0);
if(zret == Z_OK || zret == Z_STREAM_END || zret == Z_BUF_ERROR) {
unsigned int umpd = IS_CABBUFSZ - z.avail_out;
if(cli_writen(ofd, outbuf, umpd) < (ssize_t)umpd)
break;
outsz += umpd;
if(zret == Z_STREAM_END || z.avail_out == IS_CABBUFSZ /* FIXMEISHIELD: is the latter ok? */) {
success = 1;
break;
} |
1b98609f |
if(ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) { |
e78b5186 |
cli_dbgmsg("ishield_extract_cab: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int) ctx->engine->maxfilesize); |
1b98609f |
success = 1;
outsz = size;
break;
} |
f0b36be4 |
continue;
}
cli_dbgmsg("is_extract_cab: file decompression failed with %d\n", zret);
break;
}
inflateEnd(&z);
if(!success) break;
} |
9c3c24ca |
free(outbuf); |
f0b36be4 |
if(success) {
if (outsz != size) |
4cda88cc |
cli_dbgmsg("is_extract_cab: extracted %llu bytes to %s, expected %llu, scanning anyway.\n", (long long)outsz, tempfile, (long long)size); |
f0b36be4 |
else
cli_dbgmsg("is_extract_cab: extracted to %s\n", tempfile); |
96914546 |
if (lseek(ofd, 0, SEEK_SET) == -1)
cli_dbgmsg("is_extract_cab: call to lseek() failed\n"); |
d39cb658 |
ret = cli_magic_scandesc(ofd, tempfile, ctx); |
f0b36be4 |
}
close(ofd);
if(!ctx->engine->keeptmp)
if(cli_unlink(tempfile)) ret = CL_EUNLINK;
free(tempfile); |
1b98609f |
return success ? ret : CL_BREAK; |
f0b36be4 |
} |