Coverage Report

Created: 2024-05-20 06:31

/src/clamav/libclamav/ishield.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (C) 2013-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3
 *  Copyright (C) 2009-2013 Sourcefire, Inc.
4
 *
5
 *  Authors: aCaB <acab@clamav.net>
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License version 2 as
9
 *  published by the Free Software Foundation.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 *  MA 02110-1301, USA.
20
 */
21
22
/* common routines to deal with installshield archives and installers */
23
24
#if HAVE_CONFIG_H
25
#include "clamav-config.h"
26
#endif
27
28
#include <sys/types.h>
29
#include <sys/stat.h>
30
#include <fcntl.h>
31
#ifdef HAVE_UNISTD_H
32
#include <unistd.h>
33
#endif
34
#if HAVE_STRING_H
35
#include <string.h>
36
#endif
37
#include <limits.h>
38
#if HAVE_STRINGS_H
39
#include <strings.h>
40
#endif
41
#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
42
#include <sys/mman.h>
43
#endif
44
#include <zlib.h>
45
46
#include "clamav.h"
47
#include "scanners.h"
48
#include "others.h"
49
#include "fmap.h"
50
#include "ishield.h"
51
52
#ifndef LONG_MAX
53
#define LONG_MAX ((-1UL) >> 1)
54
#endif
55
56
#ifndef HAVE_ATTRIB_PACKED
57
#define __attribute__(x)
58
#endif
59
#ifdef HAVE_PRAGMA_PACK
60
#pragma pack(1)
61
#endif
62
#ifdef HAVE_PRAGMA_PACK_HPPA
63
#pragma pack 1
64
#endif
65
66
/* PACKED things go here */
67
68
struct IS_HDR {
69
    uint32_t magic;
70
    uint32_t unk1; /* version ??? */
71
    uint32_t unk2; /* ??? */
72
    uint32_t data_off;
73
    uint32_t data_sz; /* ??? */
74
} __attribute__((packed));
75
76
struct IS_FB {
77
    char fname[0x104]; /* MAX_PATH */
78
    uint32_t unk1;     /* 6 */
79
    uint32_t unk2;
80
    uint64_t csize;
81
    uint32_t unk3;
82
    uint32_t unk4; /* 1 */
83
    uint32_t unk5;
84
    uint32_t unk6;
85
    uint32_t unk7;
86
    uint32_t unk8;
87
    uint32_t unk9;
88
    uint32_t unk10;
89
    uint32_t unk11;
90
} __attribute__((packed));
91
92
struct IS_COMPONENT {
93
    uint32_t str_name_off;
94
    uint32_t unk_str1_off;
95
    uint32_t unk_str2_off;
96
    uint16_t unk_flags;
97
    uint32_t unk_str3_off;
98
    uint32_t unk_str4_off;
99
    uint16_t ordinal_id;
100
    uint32_t str_shortname_off;
101
    uint32_t unk_str6_off;
102
    uint32_t unk_str7_off;
103
    uint32_t unk_str8_off;
104
    char guid1[16];
105
    char guid2[16];
106
    uint32_t unk_str9_off;
107
    char unk1[3];
108
    uint16_t unk_flags2;
109
    uint32_t unk3[5];
110
    uint32_t unk_str10_off;
111
    uint32_t unk4[4];
112
    uint16_t unk5;
113
    uint16_t sub_comp_cnt;
114
    uint32_t sub_comp_offs_array;
115
    uint32_t next_comp_off;
116
    uint32_t unk_str11_off;
117
    uint32_t unk_str12_off;
118
    uint32_t unk_str13_off;
119
    uint32_t unk_str14_off;
120
    uint32_t str_next1_off;
121
    uint32_t str_next2_off;
122
} __attribute__((packed));
123
124
struct IS_INSTTYPEHDR {
125
    uint32_t unk1;
126
    uint32_t cnt;
127
    uint32_t off;
128
} __attribute__((packed));
129
130
struct IS_INSTTYPEITEM {
131
    uint32_t str_name1_off;
132
    uint32_t str_name2_off;
133
    uint32_t str_name3_off;
134
    uint32_t cnt;
135
    uint32_t off;
136
} __attribute__((packed));
137
138
struct IS_OBJECTS {
139
    /* 200 */ uint32_t strings_off;
140
    /* 204 */ uint32_t zero1;
141
    /* 208 */ uint32_t comps_off;
142
    /* 20c */ uint32_t dirs_off;
143
    /* 210 */ uint32_t zero2;
144
    /* 214 */ uint32_t unk1, unk2; /* 0x4a636 304694 uguali - NOT AN OFFSET! */
145
    /* 21c */ uint32_t dirs_cnt;
146
    /* 220 */ uint32_t zero3;
147
    /* 224 */ uint32_t dirs_sz; /* dirs_cnt * 4 */
148
    /* 228 */ uint32_t files_cnt;
149
    /* 22c */ uint32_t dir_sz2; /* same as dirs_sz ?? */
150
    /* 230 */ uint16_t unk5;    /* 1 - comp count ?? */
151
    /* 232 */ uint32_t insttype_off;
152
    /* 234 */ uint16_t zero4;
153
    /* 238 */ uint32_t zero5;
154
    /* 23c */ uint32_t unk7; /* 0xd0 - 208 */
155
    /* 240 */ uint16_t unk8;
156
    /* 242 */ uint32_t unk9;
157
    /* 246 */ uint32_t unk10;
158
} __attribute__((packed));
159
160
struct IS_FILEITEM {
161
    uint16_t flags; /* 0 = EXTERNAL | 4 = INTERNAL | 8 = NAME_fuckup_rare | c = name_fuckup_common */
162
    uint64_t size;
163
    uint64_t csize;
164
    uint64_t stream_off;
165
    uint8_t md5[16];
166
    uint64_t versioninfo_id;
167
    uint32_t zero1;
168
    uint32_t zero2;
169
    uint32_t str_name_off;
170
    uint16_t dir_id;
171
    uint32_t unk13;       /* 0, 20, 21 ??? */
172
    uint32_t unk14;       /* timestamp ??? */
173
    uint32_t unk15;       /* begins with 1 then 2 but not the cab# ??? */
174
    uint32_t prev_dup_id; /* msvcrt #38(0, 97, 2) #97(38, 1181, 3) ... , 0, 1) */
175
    uint32_t next_dup_id;
176
    uint8_t flag_has_dup; /* HAS_NEXT = 2 | HAS_BOTH = 3 | HAS_PREV = 1 */
177
    uint16_t datafile_id;
178
} __attribute__((packed));
179
180
#ifdef HAVE_PRAGMA_PACK
181
#pragma pack()
182
#endif
183
#ifdef HAVE_PRAGMA_PACK_HPPA
184
#pragma pack
185
#endif
186
187
static cl_error_t is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize);
188
static const uint8_t skey[] = {0xec, 0xca, 0x79, 0xf8}; /* ~0x13, ~0x35, ~0x86, ~0x07 */
189
190
/* Extracts the content of MSI based IS */
191
cl_error_t cli_scanishield_msi(cli_ctx *ctx, off_t off)
192
0
{
193
0
    cl_error_t ret;
194
0
    const uint8_t *buf;
195
0
    unsigned int fcount, scanned = 0;
196
0
    fmap_t *map = ctx->fmap;
197
198
0
    cli_dbgmsg("in ishield-msi\n");
199
0
    if (!(buf = fmap_need_off_once(map, off, 0x20))) {
200
0
        cli_dbgmsg("ishield-msi: short read for header\n");
201
0
        return CL_SUCCESS;
202
0
    }
203
204
0
    off += 0x20;
205
0
    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)) {
206
0
        return CL_SUCCESS;
207
0
    }
208
209
0
    if (!(fcount = cli_readint32(buf))) {
210
0
        cli_dbgmsg("ishield-msi: no files?\n");
211
0
        return CL_SUCCESS;
212
0
    }
213
214
0
    while (fcount--) {
215
0
        struct IS_FB fb;
216
0
        uint8_t obuf[BUFSIZ], *key = (uint8_t *)&fb.fname;
217
0
        char *filename = NULL;
218
0
        char *tempfile;
219
0
        unsigned int i, lameidx = 0, keylen;
220
0
        int ofd;
221
0
        uint64_t csize;
222
0
        z_stream z;
223
224
0
        if (fmap_readn(map, &fb, off, sizeof(fb)) != sizeof(fb)) {
225
0
            cli_dbgmsg("ishield-msi: short read for fileblock\n");
226
0
            return CL_SUCCESS;
227
0
        }
228
229
0
        off += sizeof(fb);
230
0
        fb.fname[sizeof(fb.fname) - 1] = '\0';
231
232
0
        csize = le64_to_host(fb.csize);
233
0
        if (!CLI_ISCONTAINED_0_TO(map->len, off, csize)) {
234
0
            cli_dbgmsg("ishield-msi: next stream is out of file, giving up\n");
235
0
            return CL_SUCCESS;
236
0
        }
237
238
0
        if (ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) {
239
0
            cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", (unsigned long int)csize, (unsigned long int)ctx->engine->maxfilesize);
240
0
            off += csize;
241
0
            continue;
242
0
        }
243
244
0
        keylen = strlen((const char *)key);
245
0
        if (!keylen) {
246
0
            return CL_SUCCESS;
247
0
        }
248
249
0
        filename = cli_safer_strdup((const char *)key);
250
251
        /* FIXMEISHIELD: cleanup the spam below */
252
0
        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);
253
0
        if (!(tempfile = cli_gentemp(ctx->sub_tmpdir))) {
254
0
            if (NULL != filename) {
255
0
                free(filename);
256
0
            }
257
0
            return CL_EMEM;
258
0
        }
259
260
0
        if ((ofd = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
261
0
            cli_dbgmsg("ishield-msi: failed to create file %s\n", tempfile);
262
0
            free(tempfile);
263
0
            if (NULL != filename) {
264
0
                free(filename);
265
0
            }
266
0
            return CL_ECREAT;
267
0
        }
268
269
0
        for (i = 0; i < keylen; i++) {
270
0
            key[i] ^= skey[i & 3];
271
0
        }
272
273
0
        memset(&z, 0, sizeof(z));
274
0
        inflateInit(&z);
275
276
0
        ret = CL_SUCCESS;
277
278
0
        while (csize) {
279
0
            uint8_t buf2[BUFSIZ];
280
0
            z.avail_in = MIN(csize, sizeof(buf2));
281
0
            if (fmap_readn(map, buf2, off, z.avail_in) != z.avail_in) {
282
0
                cli_dbgmsg("ishield-msi: premature EOS or read fail\n");
283
0
                break;
284
0
            }
285
0
            off += z.avail_in;
286
0
            for (i = 0; i < z.avail_in; i++, lameidx++) {
287
0
                uint8_t c = buf2[i];
288
0
                c         = (c >> 4) | (c << 4);
289
0
                c ^= key[(lameidx & 0x3ff) % keylen];
290
0
                buf2[i] = c;
291
0
            }
292
0
            csize -= z.avail_in;
293
0
            z.next_in = buf2;
294
0
            do {
295
0
                int inf;
296
0
                z.avail_out = sizeof(obuf);
297
0
                z.next_out  = obuf;
298
0
                inf         = inflate(&z, 0);
299
0
                if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) {
300
0
                    cli_dbgmsg("ishield-msi: bad stream\n");
301
0
                    csize = 0;
302
0
                    off += csize;
303
0
                    break;
304
0
                }
305
0
                if (cli_writen(ofd, obuf, sizeof(obuf) - z.avail_out) == (size_t)-1) {
306
0
                    ret   = CL_EWRITE;
307
0
                    csize = 0;
308
0
                    break;
309
0
                }
310
0
                if (ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
311
0
                    cli_dbgmsg("ishield-msi: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int)ctx->engine->maxfilesize);
312
0
                    off += csize;
313
0
                    csize = 0;
314
0
                    break;
315
0
                }
316
0
            } while (!z.avail_out);
317
0
        }
318
319
0
        inflateEnd(&z);
320
321
0
        if (ret == CL_SUCCESS) {
322
0
            cli_dbgmsg("ishield-msi: extracted to %s\n", tempfile);
323
324
0
            if (lseek(ofd, 0, SEEK_SET) == -1) {
325
0
                cli_dbgmsg("ishield-msi: call to lseek() failed\n");
326
0
                ret = CL_ESEEK;
327
0
            }
328
0
            ret = cli_magic_scan_desc(ofd, tempfile, ctx, filename, LAYER_ATTRIBUTES_NONE);
329
0
        }
330
0
        close(ofd);
331
332
0
        if (!ctx->engine->keeptmp) {
333
0
            if (cli_unlink(tempfile)) {
334
0
                ret = CL_EUNLINK;
335
0
            }
336
0
        }
337
0
        free(tempfile);
338
339
0
        if (NULL != filename) {
340
0
            free(filename);
341
0
        }
342
343
0
        if (ret != CL_SUCCESS) {
344
0
            return ret;
345
0
        }
346
347
0
        scanned++;
348
0
        if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
349
0
            cli_dbgmsg("ishield-msi: File limit reached (max: %u)\n", ctx->engine->maxfiles);
350
0
            return CL_EMAXFILES;
351
0
        }
352
0
    }
353
0
    return CL_SUCCESS;
354
0
}
355
356
struct IS_CABSTUFF {
357
    struct CABARRAY {
358
        unsigned int cabno;
359
        off_t off;
360
        size_t sz;
361
    } *cabs;
362
    off_t hdr;
363
    size_t hdrsz;
364
    unsigned int cabcnt;
365
};
366
367
static void md5str(uint8_t *sum);
368
static cl_error_t is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c);
369
static cl_error_t is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
370
371
/* Extract the content of older (non-MSI) IS */
372
cl_error_t cli_scanishield(cli_ctx *ctx, off_t off, size_t sz)
373
12.6k
{
374
12.6k
    cl_error_t ret = CL_SUCCESS;
375
12.6k
    const char *fname, *path, *version, *strsz, *data;
376
12.6k
    char *eostr;
377
12.6k
    long fsize;
378
12.6k
    off_t coff           = off;
379
12.6k
    struct IS_CABSTUFF c = {NULL, -1, 0, 0};
380
12.6k
    fmap_t *map          = ctx->fmap;
381
12.6k
    unsigned fc          = 0;
382
383
12.8k
    while (ret == CL_SUCCESS) {
384
12.7k
        fname = fmap_need_offstr(map, coff, 2048);
385
12.7k
        if (!fname) break;
386
9.02k
        coff += strlen(fname) + 1;
387
388
9.02k
        path = fmap_need_offstr(map, coff, 2048);
389
9.02k
        if (!path) break;
390
8.93k
        coff += strlen(path) + 1;
391
392
8.93k
        version = fmap_need_offstr(map, coff, 2048);
393
8.93k
        if (!version) break;
394
8.92k
        coff += strlen(version) + 1;
395
396
8.92k
        strsz = fmap_need_offstr(map, coff, 2048);
397
8.92k
        if (!strsz) break;
398
8.86k
        coff += strlen(strsz) + 1;
399
400
8.86k
        data = &strsz[strlen(strsz) + 1];
401
402
8.86k
        fsize = strtol(strsz, &eostr, 10);
403
8.86k
        if (fsize < 0 || fsize == LONG_MAX ||
404
8.86k
            !*strsz || !eostr || eostr == strsz || *eostr ||
405
8.86k
            (unsigned long)fsize >= sz ||
406
8.86k
            (size_t)(data - fname) >= sz - fsize) break;
407
408
189
        cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int)coff, fname, path, version, (unsigned long int)fsize);
409
189
        if (CL_SUCCESS != cli_matchmeta(ctx, fname, fsize, fsize, 0, fc++, 0)) {
410
0
            ret = CL_VIRUS;
411
0
            break;
412
0
        }
413
414
189
        sz -= (data - fname) + fsize;
415
416
189
        if (!strncasecmp(fname, "data", 4)) {
417
0
            long cabno;
418
0
            if (!strcasecmp(fname + 4, "1.hdr")) {
419
0
                if (c.hdr == -1) {
420
0
                    cli_dbgmsg("ishield: added data1.hdr to array\n");
421
0
                    c.hdr   = coff;
422
0
                    c.hdrsz = fsize;
423
0
                    coff += fsize;
424
0
                    continue;
425
0
                }
426
0
                cli_warnmsg("ishield: got multiple header files\n");
427
0
            }
428
0
            cabno = strtol(fname + 4, &eostr, 10);
429
0
            if (cabno > 0 && cabno < 65536 && fname[4] && eostr && eostr != &fname[4] && !strcasecmp(eostr, ".cab")) {
430
0
                unsigned int i;
431
0
                for (i = 0; i < c.cabcnt && i != c.cabs[i].cabno; i++) {
432
0
                }
433
0
                if (i == c.cabcnt) {
434
0
                    c.cabcnt++;
435
0
                    if (!(c.cabs = cli_max_realloc_or_free(c.cabs, sizeof(struct CABARRAY) * c.cabcnt))) {
436
0
                        ret = CL_EMEM;
437
0
                        break;
438
0
                    }
439
0
                    cli_dbgmsg("ishield: added data%lu.cab to array\n", cabno);
440
0
                    c.cabs[i].cabno = cabno;
441
0
                    c.cabs[i].off   = coff;
442
0
                    c.cabs[i].sz    = fsize;
443
0
                    coff += fsize;
444
0
                    continue;
445
0
                }
446
0
                cli_warnmsg("ishield: got multiple data%lu.cab files\n", cabno);
447
0
            }
448
0
        }
449
450
189
        fmap_unneed_ptr(map, fname, data - fname);
451
189
        ret = is_dump_and_scan(ctx, coff, fsize);
452
189
        coff += fsize;
453
189
    }
454
455
12.6k
    if ((ret == CL_SUCCESS) &&
456
12.6k
        (c.cabcnt || c.hdr != -1)) {
457
458
0
        if (CL_SUCCESS == (ret = is_parse_hdr(ctx, &c))) {
459
0
            unsigned int i;
460
0
            if (c.hdr != -1) {
461
0
                cli_dbgmsg("ishield: scanning data1.hdr\n");
462
0
                ret = is_dump_and_scan(ctx, c.hdr, c.hdrsz);
463
0
            }
464
0
            for (i = 0; i < c.cabcnt && ret == CL_SUCCESS; i++) {
465
0
                cli_dbgmsg("ishield: scanning data%u.cab\n", c.cabs[i].cabno);
466
0
                ret = is_dump_and_scan(ctx, c.cabs[i].off, c.cabs[i].sz);
467
0
            }
468
469
0
        } else if (ret == CL_BREAK) {
470
0
            ret = CL_SUCCESS;
471
0
        }
472
0
    }
473
474
12.6k
    if (c.cabs) {
475
0
        free(c.cabs);
476
0
    }
477
478
12.6k
    return ret;
479
12.6k
}
480
481
/* Utility func to scan a fd @ a given offset and size */
482
static cl_error_t is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize)
483
189
{
484
189
    char *fname;
485
189
    const char *buf;
486
189
    cl_error_t ofd, ret = CL_SUCCESS;
487
189
    fmap_t *map = ctx->fmap;
488
489
189
    if (!fsize) {
490
9
        cli_dbgmsg("ishield: skipping empty file\n");
491
9
        return CL_SUCCESS;
492
9
    }
493
494
180
    if (!(fname = cli_gentemp(ctx->sub_tmpdir))) {
495
0
        return CL_EMEM;
496
0
    }
497
498
180
    if ((ofd = open(fname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
499
0
        cli_errmsg("ishield: failed to create file %s\n", fname);
500
0
        free(fname);
501
0
        return CL_ECREAT;
502
0
    }
503
504
360
    while (fsize) {
505
180
        size_t rd = MIN(fsize, map->pgsz);
506
180
        if (!(buf = fmap_need_off_once(map, off, rd))) {
507
0
            cli_dbgmsg("ishield: read error\n");
508
0
            ret = CL_EREAD;
509
0
            break;
510
0
        }
511
180
        if (cli_writen(ofd, buf, rd) != rd) {
512
0
            ret = CL_EWRITE;
513
0
            break;
514
0
        }
515
180
        fsize -= rd;
516
180
        off += rd;
517
180
    }
518
519
180
    if (!fsize) {
520
180
        cli_dbgmsg("ishield: extracted to %s\n", fname);
521
180
        if (lseek(ofd, 0, SEEK_SET) == -1) {
522
0
            cli_dbgmsg("ishield: call to lseek() failed\n");
523
0
            ret = CL_ESEEK;
524
0
        }
525
180
        ret = cli_magic_scan_desc(ofd, fname, ctx, NULL, LAYER_ATTRIBUTES_NONE);
526
180
    }
527
528
180
    close(ofd);
529
530
180
    if (!ctx->engine->keeptmp) {
531
180
        if (cli_unlink(fname)) {
532
0
            ret = CL_EUNLINK;
533
0
        }
534
180
    }
535
536
180
    free(fname);
537
180
    return ret;
538
180
}
539
540
/* Process data1.hdr and extracts all the available files from dataX.cab */
541
static cl_error_t is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
542
0
{
543
0
    uint32_t h1_data_off, objs_files_cnt, objs_dirs_off;
544
0
    unsigned int off, i, scanned = 0;
545
0
    int ret = CL_BREAK;
546
0
    char hash[33], *hdr;
547
0
    fmap_t *map = ctx->fmap;
548
549
0
    const struct IS_HDR *h1;
550
0
    struct IS_OBJECTS *objs;
551
    /* struct IS_INSTTYPEHDR *typehdr; -- UNUSED */
552
553
0
    if (!c->hdr || !c->hdrsz || !c->cabcnt) {
554
0
        cli_dbgmsg("is_parse_hdr: inconsistent hdr, maybe a false match\n");
555
0
        return CL_SUCCESS;
556
0
    }
557
558
0
    if (!(h1 = fmap_need_off(map, c->hdr, c->hdrsz))) {
559
0
        cli_dbgmsg("is_parse_hdr: not enough room for H1\n");
560
0
        return CL_SUCCESS;
561
0
    }
562
563
0
    hdr         = (char *)h1;
564
0
    h1_data_off = le32_to_host(h1->data_off);
565
0
    objs        = (struct IS_OBJECTS *)fmap_need_ptr(map, hdr + h1_data_off, sizeof(*objs));
566
0
    if (!objs) {
567
0
        cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
568
0
        return CL_SUCCESS;
569
0
    }
570
571
0
    cli_dbgmsg("is_parse_hdr: magic %x, unk1 %x, unk2 %x, data_off %x, data_sz %x\n",
572
0
               h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz);
573
0
    if (le32_to_host(h1->magic) != 0x28635349) {
574
0
        cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
575
0
        return CL_SUCCESS;
576
0
    }
577
578
0
    fmap_unneed_ptr(map, h1, sizeof(*h1));
579
580
    /*     cli_errmsg("COMPONENTS\n"); */
581
    /*     off = le32_to_host(objs->comps_off) + h1_data_off; */
582
    /*     for(i=1;  ; i++) { */
583
    /*  struct IS_COMPONENT *cmp = (struct IS_COMPONENT *)(hdr + off); */
584
    /*  if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)cmp), sizeof(*cmp))) { */
585
    /*      cli_dbgmsg("is_extract: not enough room for COMPONENT\n"); */
586
    /*      free(hdr); */
587
    /*      return CL_SUCCESS; */
588
    /*  } */
589
    /*  cli_errmsg("%06u\t%s\n", i, &hdr[le32_to_host(cmp->str_name_off) + h1_data_off]); */
590
    /*  spam_strarray(hdr, h1_data_off + cmp->sub_comp_offs_array, h1_data_off, cmp->sub_comp_cnt); */
591
    /*  if(!cmp->next_comp_off) break; */
592
    /*  off = le32_to_host(cmp->next_comp_off) + h1_data_off; */
593
    /*     } */
594
595
    /*     cli_errmsg("DIRECTORIES (%u)", le32_to_host(objs->dirs_cnt)); */
596
0
    objs_dirs_off = le32_to_host(objs->dirs_off);
597
    /*     spam_strarray(hdr, h1_data_off + objs_dirs_off, h1_data_off + objs_dirs_off, objs->dirs_cnt); */
598
599
    /*     typehdr = (struct INSTTYPEHDR *)&hdr[h1_data_off + le32_to_host(objs->insttype_off)]; */
600
    /*     printf("INSTTYPES (unk1: %d)\n-----------\n", typehdr->unk1); */
601
    /*     off = typehdr->off + h1_data_off; */
602
    /*     for(i=1; i<=typehdr->cnt; i++) { */
603
    /*  uint32_t x = *(uint32_t *)(&hdr[off]); */
604
    /*  struct INSTTYPEITEM *item = (struct INSTTYPEITEM *)&hdr[x + h1_data_off]; */
605
    /*  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]); */
606
    /*  printf("components:\n"); */
607
    /*  spam_strarray(hdr, h1_data_off + item->off, h1_data_off, item->cnt); */
608
    /*  off+=4; */
609
    /*     } */
610
611
    /* dir = &hdr[*(uint32_t *)(&hdr[h1_data_off + objs_dirs_off + 4 * file->dir_id]) + h1_data_off + objs_dirs_off] */
612
613
0
    objs_files_cnt = le32_to_host(objs->files_cnt);
614
0
    off            = h1_data_off + objs_dirs_off + le32_to_host(objs->dir_sz2);
615
0
    fmap_unneed_ptr(map, objs, sizeof(*objs));
616
0
    for (i = 0; i < objs_files_cnt; i++) {
617
0
        struct IS_FILEITEM *file = (struct IS_FILEITEM *)fmap_need_off(map, c->hdr + off, sizeof(*file));
618
619
0
        if (file) {
620
0
            const char *emptyname = "", *dir_name = emptyname, *file_name = emptyname;
621
0
            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 */
622
0
            uint32_t file_rel = objs_dirs_off + h1_data_off + le32_to_host(file->str_name_off); /* rel off of fname */
623
0
            uint64_t file_stream_off, file_size, file_csize;
624
0
            uint16_t cabno;
625
626
0
            memcpy(hash, file->md5, 16);
627
0
            md5str((uint8_t *)hash);
628
0
            if (fmap_need_ptr_once(map, &hdr[dir_rel], 4)) {
629
0
                dir_rel = cli_readint32(&hdr[dir_rel]) + h1_data_off + objs_dirs_off;
630
0
                if (fmap_need_str(map, &hdr[dir_rel], c->hdrsz - dir_rel))
631
0
                    dir_name = &hdr[dir_rel];
632
0
            }
633
0
            if (fmap_need_str(map, &hdr[file_rel], c->hdrsz - file_rel))
634
0
                file_name = &hdr[file_rel];
635
636
0
            file_stream_off = le64_to_host(file->stream_off);
637
0
            file_size       = le64_to_host(file->size);
638
0
            file_csize      = le64_to_host(file->csize);
639
0
            cabno           = le16_to_host(file->datafile_id);
640
641
0
            switch (le16_to_host(file->flags)) {
642
0
                case 0:
643
                    /* FIXMEISHIELD: for FS scan ? */
644
0
                    cli_dbgmsg("is_parse_hdr: skipped external file:%s\\%s (size: %llu csize: %llu md5:%s)\n",
645
0
                               dir_name,
646
0
                               file_name,
647
0
                               (long long)file_size, (long long)file_csize, hash);
648
0
                    break;
649
0
                case 4:
650
0
                    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",
651
0
                               dir_name,
652
0
                               file_name,
653
0
                               (long long)file_size, (long long)file_csize, hash, (long long)file_stream_off,
654
0
                               cabno, file->unk13, file->unk14, file->unk15);
655
0
                    if (file->flag_has_dup & 1)
656
0
                        cli_dbgmsg("is_parse_hdr: not scanned (dup)\n");
657
0
                    else {
658
0
                        if (file_size) {
659
0
                            unsigned int j;
660
0
                            cl_error_t cabret = CL_SUCCESS;
661
662
0
                            if (ctx->engine->maxfilesize && file_csize > ctx->engine->maxfilesize) {
663
0
                                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);
664
0
                                break;
665
0
                            }
666
667
0
                            for (j = 0; j < c->cabcnt && c->cabs[j].cabno != cabno; j++) {
668
0
                            }
669
0
                            if (j != c->cabcnt) {
670
0
                                if (CLI_ISCONTAINED(c->cabs[j].off, c->cabs[j].sz, file_stream_off + c->cabs[j].off, file_csize)) {
671
0
                                    scanned++;
672
0
                                    if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
673
0
                                        cli_dbgmsg("is_parse_hdr: File limit reached (max: %u)\n", ctx->engine->maxfiles);
674
0
                                        if (file_name != emptyname)
675
0
                                            fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
676
0
                                        if (dir_name != emptyname)
677
0
                                            fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
678
0
                                        return CL_EMAXFILES;
679
0
                                    }
680
0
                                    cabret = is_extract_cab(ctx, file_stream_off + c->cabs[j].off, file_size, file_csize);
681
0
                                } else {
682
0
                                    ret = CL_SUCCESS;
683
0
                                    cli_dbgmsg("is_parse_hdr: stream out of file\n");
684
0
                                }
685
0
                            } else {
686
0
                                ret = CL_SUCCESS;
687
0
                                cli_dbgmsg("is_parse_hdr: data%u.cab not available\n", cabno);
688
0
                            }
689
0
                            if (cabret == CL_BREAK) {
690
0
                                ret    = CL_SUCCESS;
691
0
                                cabret = CL_SUCCESS;
692
0
                            }
693
0
                            if (cabret != CL_SUCCESS) {
694
0
                                if (file_name != emptyname)
695
0
                                    fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
696
0
                                if (dir_name != emptyname)
697
0
                                    fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
698
0
                                return cabret;
699
0
                            }
700
0
                        } else {
701
0
                            cli_dbgmsg("is_parse_hdr: skipped empty file\n");
702
0
                        }
703
0
                    }
704
0
                    break;
705
0
                default:
706
0
                    cli_dbgmsg("is_parse_hdr: skipped unknown file entry %u\n", i);
707
0
            }
708
0
            if (file_name != emptyname)
709
0
                fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
710
0
            if (dir_name != emptyname)
711
0
                fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
712
0
            fmap_unneed_ptr(map, file, sizeof(*file));
713
0
        } else {
714
0
            ret = CL_SUCCESS;
715
0
            cli_dbgmsg("is_parse_hdr: FILEITEM out of bounds\n");
716
0
        }
717
0
        off += sizeof(*file);
718
0
    }
719
0
    return ret;
720
0
}
721
722
static void md5str(uint8_t *sum)
723
0
{
724
0
    int i;
725
0
    for (i = 15; i >= 0; i--) {
726
0
        uint8_t lo = (sum[i] & 0xf), hi = (sum[i] >> 4);
727
0
        lo += '0' + (lo > 9) * '\'';
728
0
        hi += '0' + (hi > 9) * '\'';
729
0
        sum[i * 2 + 1] = lo;
730
0
        sum[i * 2]     = hi;
731
0
    }
732
0
    sum[32] = '\0';
733
0
}
734
735
0
#define IS_CABBUFSZ 65536
736
737
static cl_error_t is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize)
738
0
{
739
0
    cl_error_t ret = CL_SUCCESS;
740
0
    const uint8_t *inbuf;
741
0
    uint8_t *outbuf;
742
0
    char *tempfile;
743
0
    int ofd;
744
0
    z_stream z;
745
0
    uint64_t outsz = 0;
746
0
    int success    = 0;
747
0
    fmap_t *map    = ctx->fmap;
748
749
0
    if (!(outbuf = malloc(IS_CABBUFSZ))) {
750
0
        cli_errmsg("is_extract_cab: Unable to allocate memory for outbuf\n");
751
0
        return CL_EMEM;
752
0
    }
753
754
0
    if (!(tempfile = cli_gentemp(ctx->sub_tmpdir))) {
755
0
        free(outbuf);
756
0
        return CL_EMEM;
757
0
    }
758
0
    if ((ofd = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
759
0
        cli_errmsg("is_extract_cab: failed to create file %s\n", tempfile);
760
0
        free(tempfile);
761
0
        free(outbuf);
762
0
        return CL_ECREAT;
763
0
    }
764
765
0
    while (csize) {
766
0
        uint16_t chunksz;
767
0
        success = 0;
768
0
        if (csize < 2) {
769
0
            cli_dbgmsg("is_extract_cab: no room for chunk size\n");
770
0
            break;
771
0
        }
772
0
        csize -= 2;
773
0
        if (!(inbuf = fmap_need_off_once(map, off, 2))) {
774
0
            cli_dbgmsg("is_extract_cab: short read for chunk size\n");
775
0
            break;
776
0
        }
777
0
        off += 2;
778
0
        chunksz = inbuf[0] | (inbuf[1] << 8);
779
0
        if (!chunksz) {
780
0
            cli_dbgmsg("is_extract_cab: zero sized chunk\n");
781
0
            continue;
782
0
        }
783
0
        if (csize < chunksz) {
784
0
            cli_dbgmsg("is_extract_cab: chunk is bigger than csize\n");
785
0
            break;
786
0
        }
787
0
        csize -= chunksz;
788
0
        if (!(inbuf = fmap_need_off_once(map, off, chunksz))) {
789
0
            cli_dbgmsg("is_extract_cab: short read for chunk\n");
790
0
            break;
791
0
        }
792
0
        off += chunksz;
793
0
        memset(&z, 0, sizeof(z));
794
0
        inflateInit2(&z, -MAX_WBITS);
795
0
        z.next_in  = (uint8_t *)inbuf;
796
0
        z.avail_in = chunksz;
797
0
        while (1) {
798
0
            int zret;
799
0
            z.next_out  = outbuf;
800
0
            z.avail_out = IS_CABBUFSZ;
801
0
            zret        = inflate(&z, 0);
802
0
            if (zret == Z_OK || zret == Z_STREAM_END || zret == Z_BUF_ERROR) {
803
0
                unsigned int umpd = IS_CABBUFSZ - z.avail_out;
804
0
                if (cli_writen(ofd, outbuf, umpd) != umpd)
805
0
                    break;
806
0
                outsz += umpd;
807
0
                if (zret == Z_STREAM_END || z.avail_out == IS_CABBUFSZ /* FIXMEISHIELD: is the latter ok? */) {
808
0
                    success = 1;
809
0
                    break;
810
0
                }
811
0
                if (ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
812
0
                    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);
813
0
                    success = 1;
814
0
                    outsz   = size;
815
0
                    break;
816
0
                }
817
0
                continue;
818
0
            }
819
0
            cli_dbgmsg("is_extract_cab: file decompression failed with %d\n", zret);
820
0
            break;
821
0
        }
822
0
        inflateEnd(&z);
823
0
        if (!success) break;
824
0
    }
825
0
    free(outbuf);
826
0
    if (success) {
827
0
        if (outsz != size)
828
0
            cli_dbgmsg("is_extract_cab: extracted %llu bytes to %s, expected %llu, scanning anyway.\n", (long long)outsz, tempfile, (long long)size);
829
0
        else
830
0
            cli_dbgmsg("is_extract_cab: extracted to %s\n", tempfile);
831
0
        if (lseek(ofd, 0, SEEK_SET) == -1)
832
0
            cli_dbgmsg("is_extract_cab: call to lseek() failed\n");
833
0
        ret = cli_magic_scan_desc(ofd, tempfile, ctx, NULL, LAYER_ATTRIBUTES_NONE);
834
0
    }
835
836
0
    close(ofd);
837
0
    if (!ctx->engine->keeptmp)
838
0
        if (cli_unlink(tempfile)) ret = CL_EUNLINK;
839
0
    free(tempfile);
840
0
    return success ? ret : CL_BREAK;
841
0
}