~ubuntu-branches/ubuntu/feisty/clamav/feisty

« back to all changes in this revision

Viewing changes to libclamav/ishield.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2007-02-20 10:33:44 UTC
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: james.westby@ubuntu.com-20070220103344-zgcu2psnx9d98fpa
Tags: upstream-0.90
ImportĀ upstreamĀ versionĀ 0.90

Show diffs side-by-side

added added

removed removed

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