1 /*
2  *  Copyright (C) 2013-2022 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 int 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 */
cli_scanishield_msi(cli_ctx * ctx,off_t off)191 int cli_scanishield_msi(cli_ctx *ctx, off_t off)
192 {
193     const uint8_t *buf;
194     unsigned int fcount, scanned = 0;
195     int ret;
196     fmap_t *map = ctx->fmap;
197 
198     cli_dbgmsg("in ishield-msi\n");
199     if (!(buf = fmap_need_off_once(map, off, 0x20))) {
200         cli_dbgmsg("ishield-msi: short read for header\n");
201         return CL_CLEAN;
202     }
203     off += 0x20;
204     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))
205         return CL_CLEAN;
206     if (!(fcount = cli_readint32(buf))) {
207         cli_dbgmsg("ishield-msi: no files?\n");
208         return CL_CLEAN;
209     }
210     while (fcount--) {
211         struct IS_FB fb;
212         uint8_t obuf[BUFSIZ], *key = (uint8_t *)&fb.fname;
213         char *filename = NULL;
214         char *tempfile;
215         unsigned int i, lameidx = 0, keylen;
216         int ofd;
217         uint64_t csize;
218         z_stream z;
219 
220         if (fmap_readn(map, &fb, off, sizeof(fb)) != sizeof(fb)) {
221             cli_dbgmsg("ishield-msi: short read for fileblock\n");
222             return CL_CLEAN;
223         }
224         off += sizeof(fb);
225         fb.fname[sizeof(fb.fname) - 1] = '\0';
226         csize                          = le64_to_host(fb.csize);
227         if (!CLI_ISCONTAINED_0_TO(map->len, off, csize)) {
228             cli_dbgmsg("ishield-msi: next stream is out of file, giving up\n");
229             return CL_CLEAN;
230         }
231         if (ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) {
232             cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", (unsigned long int)csize, (unsigned long int)ctx->engine->maxfilesize);
233             off += csize;
234             continue;
235         }
236 
237         keylen = strlen((const char *)key);
238         if (!keylen) return CL_CLEAN;
239 
240         filename = cli_strdup((const char *)key);
241 
242         /* FIXMEISHIELD: cleanup the spam below */
243         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);
244         if (!(tempfile = cli_gentemp(ctx->sub_tmpdir))) {
245             if (NULL != filename) {
246                 free(filename);
247             }
248             return CL_EMEM;
249         }
250         if ((ofd = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
251             cli_dbgmsg("ishield-msi: failed to create file %s\n", tempfile);
252             free(tempfile);
253             if (NULL != filename) {
254                 free(filename);
255             }
256             return CL_ECREAT;
257         }
258 
259         for (i = 0; i < keylen; i++)
260             key[i] ^= skey[i & 3];
261         memset(&z, 0, sizeof(z));
262         inflateInit(&z);
263         ret = CL_SUCCESS;
264         while (csize) {
265             uint8_t buf2[BUFSIZ];
266             z.avail_in = MIN(csize, sizeof(buf2));
267             if (fmap_readn(map, buf2, off, z.avail_in) != z.avail_in) {
268                 cli_dbgmsg("ishield-msi: premature EOS or read fail\n");
269                 break;
270             }
271             off += z.avail_in;
272             for (i = 0; i < z.avail_in; i++, lameidx++) {
273                 uint8_t c = buf2[i];
274                 c         = (c >> 4) | (c << 4);
275                 c ^= key[(lameidx & 0x3ff) % keylen];
276                 buf2[i] = c;
277             }
278             csize -= z.avail_in;
279             z.next_in = buf2;
280             do {
281                 int inf;
282                 z.avail_out = sizeof(obuf);
283                 z.next_out  = obuf;
284                 inf         = inflate(&z, 0);
285                 if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) {
286                     cli_dbgmsg("ishield-msi: bad stream\n");
287                     csize = 0;
288                     off += csize;
289                     break;
290                 }
291                 if (cli_writen(ofd, obuf, sizeof(obuf) - z.avail_out) == (size_t)-1) {
292                     ret   = CL_EWRITE;
293                     csize = 0;
294                     break;
295                 }
296                 if (ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
297                     cli_dbgmsg("ishield-msi: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int)ctx->engine->maxfilesize);
298                     off += csize;
299                     csize = 0;
300                     break;
301                 }
302             } while (!z.avail_out);
303         }
304 
305         inflateEnd(&z);
306 
307         if (ret == CL_SUCCESS) {
308             cli_dbgmsg("ishield-msi: extracted to %s\n", tempfile);
309 
310             if (lseek(ofd, 0, SEEK_SET) == -1) {
311                 cli_dbgmsg("ishield-msi: call to lseek() failed\n");
312                 ret = CL_ESEEK;
313             }
314             ret = cli_magic_scan_desc(ofd, tempfile, ctx, filename);
315         }
316         close(ofd);
317 
318         if (!ctx->engine->keeptmp) {
319             if (cli_unlink(tempfile)) {
320                 ret = CL_EUNLINK;
321             }
322         }
323         free(tempfile);
324 
325         if (NULL != filename) {
326             free(filename);
327         }
328 
329         if (ret != CL_CLEAN)
330             return ret;
331 
332         scanned++;
333         if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
334             cli_dbgmsg("ishield-msi: File limit reached (max: %u)\n", ctx->engine->maxfiles);
335             return CL_EMAXFILES;
336         }
337     }
338     return CL_CLEAN;
339 }
340 
341 struct IS_CABSTUFF {
342     struct CABARRAY {
343         unsigned int cabno;
344         off_t off;
345         size_t sz;
346     } * cabs;
347     off_t hdr;
348     size_t hdrsz;
349     unsigned int cabcnt;
350 };
351 
352 static void md5str(uint8_t *sum);
353 static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c);
354 static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
355 
356 /* Extract the content of older (non-MSI) IS */
cli_scanishield(cli_ctx * ctx,off_t off,size_t sz)357 int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz)
358 {
359     const char *fname, *path, *version, *strsz, *data;
360     char *eostr;
361     int ret = CL_CLEAN;
362     long fsize;
363     off_t coff           = off;
364     struct IS_CABSTUFF c = {NULL, -1, 0, 0};
365     fmap_t *map          = ctx->fmap;
366     unsigned fc          = 0;
367     int virus_found      = 0;
368 
369     while (ret == CL_CLEAN) {
370         fname = fmap_need_offstr(map, coff, 2048);
371         if (!fname) break;
372         coff += strlen(fname) + 1;
373 
374         path = fmap_need_offstr(map, coff, 2048);
375         if (!path) break;
376         coff += strlen(path) + 1;
377 
378         version = fmap_need_offstr(map, coff, 2048);
379         if (!version) break;
380         coff += strlen(version) + 1;
381 
382         strsz = fmap_need_offstr(map, coff, 2048);
383         if (!strsz) break;
384         coff += strlen(strsz) + 1;
385 
386         data = &strsz[strlen(strsz) + 1];
387 
388         fsize = strtol(strsz, &eostr, 10);
389         if (fsize < 0 || fsize == LONG_MAX ||
390             !*strsz || !eostr || eostr == strsz || *eostr ||
391             (unsigned long)fsize >= sz ||
392             (size_t)(data - fname) >= sz - fsize) break;
393 
394         cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int)coff, fname, path, version, (unsigned long int)fsize);
395         if (cli_matchmeta(ctx, fname, fsize, fsize, 0, fc++, 0, NULL) == CL_VIRUS) {
396             if (!SCAN_ALLMATCHES) {
397                 ret = CL_VIRUS;
398                 break;
399             }
400             ret         = CL_CLEAN;
401             virus_found = 1;
402         }
403         sz -= (data - fname) + fsize;
404 
405         if (!strncasecmp(fname, "data", 4)) {
406             long cabno;
407             if (!strcasecmp(fname + 4, "1.hdr")) {
408                 if (c.hdr == -1) {
409                     cli_dbgmsg("ishield: added data1.hdr to array\n");
410                     c.hdr   = coff;
411                     c.hdrsz = fsize;
412                     coff += fsize;
413                     continue;
414                 }
415                 cli_warnmsg("ishield: got multiple header files\n");
416             }
417             cabno = strtol(fname + 4, &eostr, 10);
418             if (cabno > 0 && cabno < 65536 && fname[4] && eostr && eostr != &fname[4] && !strcasecmp(eostr, ".cab")) {
419                 unsigned int i;
420                 for (i = 0; i < c.cabcnt && i != c.cabs[i].cabno; i++) {
421                 }
422                 if (i == c.cabcnt) {
423                     c.cabcnt++;
424                     if (!(c.cabs = cli_realloc2(c.cabs, sizeof(struct CABARRAY) * c.cabcnt))) {
425                         ret = CL_EMEM;
426                         break;
427                     }
428                     cli_dbgmsg("ishield: added data%lu.cab to array\n", cabno);
429                     c.cabs[i].cabno = cabno;
430                     c.cabs[i].off   = coff;
431                     c.cabs[i].sz    = fsize;
432                     coff += fsize;
433                     continue;
434                 }
435                 cli_warnmsg("ishield: got multiple data%lu.cab files\n", cabno);
436             }
437         }
438 
439         fmap_unneed_ptr(map, fname, data - fname);
440         ret = is_dump_and_scan(ctx, coff, fsize);
441         coff += fsize;
442     }
443 
444     if (ret == CL_CLEAN && (c.cabcnt || c.hdr != -1)) {
445         if ((ret = is_parse_hdr(ctx, &c)) == CL_CLEAN) {
446             unsigned int i;
447             if (c.hdr != -1) {
448                 cli_dbgmsg("ishield: scanning data1.hdr\n");
449                 ret = is_dump_and_scan(ctx, c.hdr, c.hdrsz);
450             }
451             for (i = 0; i < c.cabcnt && ret == CL_CLEAN; i++) {
452                 cli_dbgmsg("ishield: scanning data%u.cab\n", c.cabs[i].cabno);
453                 ret = is_dump_and_scan(ctx, c.cabs[i].off, c.cabs[i].sz);
454             }
455         } else if (ret == CL_BREAK)
456             ret = CL_CLEAN;
457     }
458     if (c.cabs) free(c.cabs);
459 
460     if (virus_found != 0)
461         return CL_VIRUS;
462     return ret;
463 }
464 
465 /* Utility func to scan a fd @ a given offset and size */
is_dump_and_scan(cli_ctx * ctx,off_t off,size_t fsize)466 static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize)
467 {
468     char *fname;
469     const char *buf;
470     int ofd, ret = CL_CLEAN;
471     fmap_t *map = ctx->fmap;
472 
473     if (!fsize) {
474         cli_dbgmsg("ishield: skipping empty file\n");
475         return CL_CLEAN;
476     }
477     if (!(fname = cli_gentemp(ctx->sub_tmpdir)))
478         return CL_EMEM;
479 
480     if ((ofd = open(fname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
481         cli_errmsg("ishield: failed to create file %s\n", fname);
482         free(fname);
483         return CL_ECREAT;
484     }
485     while (fsize) {
486         size_t rd = MIN(fsize, map->pgsz);
487         if (!(buf = fmap_need_off_once(map, off, rd))) {
488             cli_dbgmsg("ishield: read error\n");
489             ret = CL_EREAD;
490             break;
491         }
492         if (cli_writen(ofd, buf, rd) != rd) {
493             ret = CL_EWRITE;
494             break;
495         }
496         fsize -= rd;
497         off += rd;
498     }
499     if (!fsize) {
500         cli_dbgmsg("ishield: extracted to %s\n", fname);
501         if (lseek(ofd, 0, SEEK_SET) == -1) {
502             cli_dbgmsg("ishield: call to lseek() failed\n");
503             ret = CL_ESEEK;
504         }
505         ret = cli_magic_scan_desc(ofd, fname, ctx, NULL);
506     }
507     close(ofd);
508     if (!ctx->engine->keeptmp)
509         if (cli_unlink(fname)) ret = CL_EUNLINK;
510     free(fname);
511     return ret;
512 }
513 
514 /* Process data1.hdr and extracts all the available files from dataX.cab */
is_parse_hdr(cli_ctx * ctx,struct IS_CABSTUFF * c)515 static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
516 {
517     uint32_t h1_data_off, objs_files_cnt, objs_dirs_off;
518     unsigned int off, i, scanned = 0;
519     int ret = CL_BREAK;
520     char hash[33], *hdr;
521     fmap_t *map = ctx->fmap;
522 
523     const struct IS_HDR *h1;
524     struct IS_OBJECTS *objs;
525     /* struct IS_INSTTYPEHDR *typehdr; -- UNUSED */
526 
527     if (!c->hdr || !c->hdrsz || !c->cabcnt) {
528         cli_dbgmsg("is_parse_hdr: inconsistent hdr, maybe a false match\n");
529         return CL_CLEAN;
530     }
531 
532     if (!(h1 = fmap_need_off(map, c->hdr, c->hdrsz))) {
533         cli_dbgmsg("is_parse_hdr: not enough room for H1\n");
534         return CL_CLEAN;
535     }
536     hdr         = (char *)h1;
537     h1_data_off = le32_to_host(h1->data_off);
538     objs        = (struct IS_OBJECTS *)fmap_need_ptr(map, hdr + h1_data_off, sizeof(*objs));
539     if (!objs) {
540         cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
541         return CL_CLEAN;
542     }
543 
544     cli_dbgmsg("is_parse_hdr: magic %x, unk1 %x, unk2 %x, data_off %x, data_sz %x\n",
545                h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz);
546     if (le32_to_host(h1->magic) != 0x28635349) {
547         cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
548         return CL_CLEAN;
549     }
550 
551     fmap_unneed_ptr(map, h1, sizeof(*h1));
552 
553     /*     cli_errmsg("COMPONENTS\n"); */
554     /*     off = le32_to_host(objs->comps_off) + h1_data_off; */
555     /*     for(i=1;  ; i++) { */
556     /* 	struct IS_COMPONENT *cmp = (struct IS_COMPONENT *)(hdr + off); */
557     /* 	if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)cmp), sizeof(*cmp))) { */
558     /* 	    cli_dbgmsg("is_extract: not enough room for COMPONENT\n"); */
559     /* 	    free(hdr); */
560     /* 	    return CL_CLEAN; */
561     /* 	} */
562     /* 	cli_errmsg("%06u\t%s\n", i, &hdr[le32_to_host(cmp->str_name_off) + h1_data_off]); */
563     /* 	spam_strarray(hdr, h1_data_off + cmp->sub_comp_offs_array, h1_data_off, cmp->sub_comp_cnt); */
564     /* 	if(!cmp->next_comp_off) break; */
565     /* 	off = le32_to_host(cmp->next_comp_off) + h1_data_off; */
566     /*     } */
567 
568     /*     cli_errmsg("DIRECTORIES (%u)", le32_to_host(objs->dirs_cnt)); */
569     objs_dirs_off = le32_to_host(objs->dirs_off);
570     /*     spam_strarray(hdr, h1_data_off + objs_dirs_off, h1_data_off + objs_dirs_off, objs->dirs_cnt); */
571 
572     /*     typehdr = (struct INSTTYPEHDR *)&hdr[h1_data_off + le32_to_host(objs->insttype_off)]; */
573     /*     printf("INSTTYPES (unk1: %d)\n-----------\n", typehdr->unk1); */
574     /*     off = typehdr->off + h1_data_off; */
575     /*     for(i=1; i<=typehdr->cnt; i++) { */
576     /* 	uint32_t x = *(uint32_t *)(&hdr[off]); */
577     /* 	struct INSTTYPEITEM *item = (struct INSTTYPEITEM *)&hdr[x + h1_data_off]; */
578     /* 	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]); */
579     /* 	printf("components:\n"); */
580     /* 	spam_strarray(hdr, h1_data_off + item->off, h1_data_off, item->cnt); */
581     /* 	off+=4; */
582     /*     } */
583 
584     /* dir = &hdr[*(uint32_t *)(&hdr[h1_data_off + objs_dirs_off + 4 * file->dir_id]) + h1_data_off + objs_dirs_off] */
585 
586     objs_files_cnt = le32_to_host(objs->files_cnt);
587     off            = h1_data_off + objs_dirs_off + le32_to_host(objs->dir_sz2);
588     fmap_unneed_ptr(map, objs, sizeof(*objs));
589     for (i = 0; i < objs_files_cnt; i++) {
590         struct IS_FILEITEM *file = (struct IS_FILEITEM *)fmap_need_off(map, c->hdr + off, sizeof(*file));
591 
592         if (file) {
593             const char *emptyname = "", *dir_name = emptyname, *file_name = emptyname;
594             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 */
595             uint32_t file_rel = objs_dirs_off + h1_data_off + le32_to_host(file->str_name_off); /* rel off of fname */
596             uint64_t file_stream_off, file_size, file_csize;
597             uint16_t cabno;
598 
599             memcpy(hash, file->md5, 16);
600             md5str((uint8_t *)hash);
601             if (fmap_need_ptr_once(map, &hdr[dir_rel], 4)) {
602                 dir_rel = cli_readint32(&hdr[dir_rel]) + h1_data_off + objs_dirs_off;
603                 if (fmap_need_str(map, &hdr[dir_rel], c->hdrsz - dir_rel))
604                     dir_name = &hdr[dir_rel];
605             }
606             if (fmap_need_str(map, &hdr[file_rel], c->hdrsz - file_rel))
607                 file_name = &hdr[file_rel];
608 
609             file_stream_off = le64_to_host(file->stream_off);
610             file_size       = le64_to_host(file->size);
611             file_csize      = le64_to_host(file->csize);
612             cabno           = le16_to_host(file->datafile_id);
613 
614             switch (le16_to_host(file->flags)) {
615                 case 0:
616                     /* FIXMEISHIELD: for FS scan ? */
617                     cli_dbgmsg("is_parse_hdr: skipped external file:%s\\%s (size: %llu csize: %llu md5:%s)\n",
618                                dir_name,
619                                file_name,
620                                (long long)file_size, (long long)file_csize, hash);
621                     break;
622                 case 4:
623                     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",
624                                dir_name,
625                                file_name,
626                                (long long)file_size, (long long)file_csize, hash, (long long)file_stream_off,
627                                cabno, file->unk13, file->unk14, file->unk15);
628                     if (file->flag_has_dup & 1)
629                         cli_dbgmsg("is_parse_hdr: not scanned (dup)\n");
630                     else {
631                         if (file_size) {
632                             unsigned int j;
633                             int cabret = CL_CLEAN;
634 
635                             if (ctx->engine->maxfilesize && file_csize > ctx->engine->maxfilesize) {
636                                 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);
637                                 break;
638                             }
639 
640                             for (j = 0; j < c->cabcnt && c->cabs[j].cabno != cabno; j++) {
641                             }
642                             if (j != c->cabcnt) {
643                                 if (CLI_ISCONTAINED(c->cabs[j].off, c->cabs[j].sz, file_stream_off + c->cabs[j].off, file_csize)) {
644                                     scanned++;
645                                     if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
646                                         cli_dbgmsg("is_parse_hdr: File limit reached (max: %u)\n", ctx->engine->maxfiles);
647                                         if (file_name != emptyname)
648                                             fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
649                                         if (dir_name != emptyname)
650                                             fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
651                                         return CL_EMAXFILES;
652                                     }
653                                     cabret = is_extract_cab(ctx, file_stream_off + c->cabs[j].off, file_size, file_csize);
654                                 } else {
655                                     ret = CL_CLEAN;
656                                     cli_dbgmsg("is_parse_hdr: stream out of file\n");
657                                 }
658                             } else {
659                                 ret = CL_CLEAN;
660                                 cli_dbgmsg("is_parse_hdr: data%u.cab not available\n", cabno);
661                             }
662                             if (cabret == CL_BREAK) {
663                                 ret    = CL_CLEAN;
664                                 cabret = CL_CLEAN;
665                             }
666                             if (cabret != CL_CLEAN) {
667                                 if (file_name != emptyname)
668                                     fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
669                                 if (dir_name != emptyname)
670                                     fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
671                                 return cabret;
672                             }
673                         } else {
674                             cli_dbgmsg("is_parse_hdr: skipped empty file\n");
675                         }
676                     }
677                     break;
678                 default:
679                     cli_dbgmsg("is_parse_hdr: skipped unknown file entry %u\n", i);
680             }
681             if (file_name != emptyname)
682                 fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
683             if (dir_name != emptyname)
684                 fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
685             fmap_unneed_ptr(map, file, sizeof(*file));
686         } else {
687             ret = CL_CLEAN;
688             cli_dbgmsg("is_parse_hdr: FILEITEM out of bounds\n");
689         }
690         off += sizeof(*file);
691     }
692     return ret;
693 }
694 
md5str(uint8_t * sum)695 static void md5str(uint8_t *sum)
696 {
697     int i;
698     for (i = 15; i >= 0; i--) {
699         uint8_t lo = (sum[i] & 0xf), hi = (sum[i] >> 4);
700         lo += '0' + (lo > 9) * '\'';
701         hi += '0' + (hi > 9) * '\'';
702         sum[i * 2 + 1] = lo;
703         sum[i * 2]     = hi;
704     }
705     sum[32] = '\0';
706 }
707 
708 #define IS_CABBUFSZ 65536
709 
is_extract_cab(cli_ctx * ctx,uint64_t off,uint64_t size,uint64_t csize)710 static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize)
711 {
712     const uint8_t *inbuf;
713     uint8_t *outbuf;
714     char *tempfile;
715     int ofd, ret = CL_CLEAN;
716     z_stream z;
717     uint64_t outsz = 0;
718     int success    = 0;
719     fmap_t *map    = ctx->fmap;
720 
721     if (!(outbuf = cli_malloc(IS_CABBUFSZ))) {
722         cli_errmsg("is_extract_cab: Unable to allocate memory for outbuf\n");
723         return CL_EMEM;
724     }
725 
726     if (!(tempfile = cli_gentemp(ctx->sub_tmpdir))) {
727         free(outbuf);
728         return CL_EMEM;
729     }
730     if ((ofd = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
731         cli_errmsg("is_extract_cab: failed to create file %s\n", tempfile);
732         free(tempfile);
733         free(outbuf);
734         return CL_ECREAT;
735     }
736 
737     while (csize) {
738         uint16_t chunksz;
739         success = 0;
740         if (csize < 2) {
741             cli_dbgmsg("is_extract_cab: no room for chunk size\n");
742             break;
743         }
744         csize -= 2;
745         if (!(inbuf = fmap_need_off_once(map, off, 2))) {
746             cli_dbgmsg("is_extract_cab: short read for chunk size\n");
747             break;
748         }
749         off += 2;
750         chunksz = inbuf[0] | (inbuf[1] << 8);
751         if (!chunksz) {
752             cli_dbgmsg("is_extract_cab: zero sized chunk\n");
753             continue;
754         }
755         if (csize < chunksz) {
756             cli_dbgmsg("is_extract_cab: chunk is bigger than csize\n");
757             break;
758         }
759         csize -= chunksz;
760         if (!(inbuf = fmap_need_off_once(map, off, chunksz))) {
761             cli_dbgmsg("is_extract_cab: short read for chunk\n");
762             break;
763         }
764         off += chunksz;
765         memset(&z, 0, sizeof(z));
766         inflateInit2(&z, -MAX_WBITS);
767         z.next_in  = (uint8_t *)inbuf;
768         z.avail_in = chunksz;
769         while (1) {
770             int zret;
771             z.next_out  = outbuf;
772             z.avail_out = IS_CABBUFSZ;
773             zret        = inflate(&z, 0);
774             if (zret == Z_OK || zret == Z_STREAM_END || zret == Z_BUF_ERROR) {
775                 unsigned int umpd = IS_CABBUFSZ - z.avail_out;
776                 if (cli_writen(ofd, outbuf, umpd) != umpd)
777                     break;
778                 outsz += umpd;
779                 if (zret == Z_STREAM_END || z.avail_out == IS_CABBUFSZ /* FIXMEISHIELD: is the latter ok? */) {
780                     success = 1;
781                     break;
782                 }
783                 if (ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
784                     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);
785                     success = 1;
786                     outsz   = size;
787                     break;
788                 }
789                 continue;
790             }
791             cli_dbgmsg("is_extract_cab: file decompression failed with %d\n", zret);
792             break;
793         }
794         inflateEnd(&z);
795         if (!success) break;
796     }
797     free(outbuf);
798     if (success) {
799         if (outsz != size)
800             cli_dbgmsg("is_extract_cab: extracted %llu bytes to %s, expected %llu, scanning anyway.\n", (long long)outsz, tempfile, (long long)size);
801         else
802             cli_dbgmsg("is_extract_cab: extracted to %s\n", tempfile);
803         if (lseek(ofd, 0, SEEK_SET) == -1)
804             cli_dbgmsg("is_extract_cab: call to lseek() failed\n");
805         ret = cli_magic_scan_desc(ofd, tempfile, ctx, NULL);
806     }
807 
808     close(ofd);
809     if (!ctx->engine->keeptmp)
810         if (cli_unlink(tempfile)) ret = CL_EUNLINK;
811     free(tempfile);
812     return success ? ret : CL_BREAK;
813 }
814