1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2007-2013 Sourcefire, Inc.
4  *
5  *  Authors: Tomasz Kojm
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 #if HAVE_CONFIG_H
23 #include "clamav-config.h"
24 #endif
25 
26 #include <string.h>
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 #include <stdbool.h>
34 
35 #include "clamav.h"
36 #include "others.h"
37 #include "matcher-ac.h"
38 #include "matcher-bm.h"
39 #include "matcher-pcre.h"
40 #include "filetypes.h"
41 #include "matcher.h"
42 #include "pe.h"
43 #include "elf.h"
44 #include "execs.h"
45 #include "special.h"
46 #include "scanners.h"
47 #include "str.h"
48 #include "default.h"
49 #include "macho.h"
50 #include "fmap.h"
51 #include "pe_icons.h"
52 #include "regex/regex.h"
53 #include "filtering.h"
54 #include "perflogging.h"
55 #include "bytecode_priv.h"
56 #include "bytecode_api_impl.h"
57 #ifdef HAVE_YARA
58 #include "yara_clam.h"
59 #include "yara_exec.h"
60 #endif
61 
62 #ifdef CLI_PERF_LOGGING
63 
perf_log_filter(int32_t pos,int32_t length,int8_t trie)64 static inline void perf_log_filter(int32_t pos, int32_t length, int8_t trie)
65 {
66     cli_perf_log_add(RAW_BYTES_SCANNED, length);
67     cli_perf_log_add(FILTER_BYTES_SCANNED, length - pos);
68     cli_perf_log_count2(TRIE_SCANNED, trie, length - pos);
69 }
70 
perf_log_tries(int8_t acmode,int8_t bm_called,int32_t length)71 static inline int perf_log_tries(int8_t acmode, int8_t bm_called, int32_t length)
72 {
73     if (bm_called)
74         cli_perf_log_add(BM_SCANNED, length);
75     if (acmode)
76         cli_perf_log_add(AC_SCANNED, length);
77     return 0;
78 }
79 
80 #else
perf_log_filter(int32_t pos,uint32_t length,int8_t trie)81 static inline void perf_log_filter(int32_t pos, uint32_t length, int8_t trie)
82 {
83     UNUSEDPARAM(pos);
84     UNUSEDPARAM(length);
85     UNUSEDPARAM(trie);
86 }
87 
perf_log_tries(int8_t acmode,int8_t bm_called,int32_t length)88 static inline int perf_log_tries(int8_t acmode, int8_t bm_called, int32_t length)
89 {
90     UNUSEDPARAM(acmode);
91     UNUSEDPARAM(bm_called);
92     UNUSEDPARAM(length);
93 
94     return 0;
95 }
96 #endif
97 
matcher_run(const struct cli_matcher * root,const unsigned char * buffer,uint32_t length,const char ** virname,struct cli_ac_data * mdata,uint32_t offset,const struct cli_target_info * tinfo,cli_file_t ftype,struct cli_matched_type ** ftoffset,unsigned int acmode,unsigned int pcremode,struct cli_ac_result ** acres,fmap_t * map,struct cli_bm_off * offdata,struct cli_pcre_off * poffdata,cli_ctx * ctx)98 static inline cl_error_t matcher_run(const struct cli_matcher *root,
99                                      const unsigned char *buffer, uint32_t length,
100                                      const char **virname, struct cli_ac_data *mdata,
101                                      uint32_t offset,
102                                      const struct cli_target_info *tinfo,
103                                      cli_file_t ftype,
104                                      struct cli_matched_type **ftoffset,
105                                      unsigned int acmode,
106                                      unsigned int pcremode,
107                                      struct cli_ac_result **acres,
108                                      fmap_t *map,
109                                      struct cli_bm_off *offdata,
110                                      struct cli_pcre_off *poffdata,
111                                      cli_ctx *ctx)
112 {
113     cl_error_t ret, saved_ret = CL_CLEAN;
114     int32_t pos = 0;
115     struct filter_match_info info;
116     uint32_t orig_length, orig_offset;
117     const unsigned char *orig_buffer;
118     unsigned int viruses_found = 0;
119 
120     if (root->filter) {
121         if (filter_search_ext(root->filter, buffer, length, &info) == -1) {
122             /*  for safety always scan last maxpatlen bytes */
123             pos = length - root->maxpatlen - 1;
124             if (pos < 0) pos = 0;
125             perf_log_filter(pos, length, root->type);
126         } else {
127             /* must not cut buffer for 64[4-4]6161, because we must be able to check
128              * 64! */
129             pos = info.first_match - root->maxpatlen - 1;
130             if (pos < 0) pos = 0;
131             perf_log_filter(pos, length, root->type);
132         }
133     } else {
134         perf_log_filter(0, length, root->type);
135     }
136 
137     orig_length = length;
138     orig_buffer = buffer;
139     orig_offset = offset;
140     length -= pos;
141     buffer += pos;
142     offset += pos;
143     if (!root->ac_only) {
144         perf_log_tries(0, 1, length);
145         if (root->bm_offmode) {
146             /* Don't use prefiltering for BM offset mode, since BM keeps tracks
147              * of offsets itself, and doesn't work if we skip chunks of input
148              * data */
149             ret = cli_bm_scanbuff(orig_buffer, orig_length, virname, NULL, root, orig_offset, tinfo, offdata, ctx);
150         } else {
151             ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata, ctx);
152         }
153         if (ret != CL_CLEAN) {
154             if (ret != CL_VIRUS)
155                 return ret;
156 
157             /* else (ret == CL_VIRUS) */
158             if (SCAN_ALLMATCHES)
159                 viruses_found = 1;
160             else {
161                 ret = cli_append_virus(ctx, *virname);
162                 if (ret != CL_CLEAN)
163                     return ret;
164             }
165         }
166     }
167     perf_log_tries(acmode, 0, length);
168     ret = cli_ac_scanbuff(buffer, length, virname, NULL, acres, root, mdata, offset, ftype, ftoffset, acmode, ctx);
169     if (ret != CL_CLEAN) {
170         if (ret == CL_VIRUS) {
171             if (SCAN_ALLMATCHES)
172                 viruses_found = 1;
173             else {
174                 ret = cli_append_virus(ctx, *virname);
175                 if (ret != CL_CLEAN)
176                     return ret;
177             }
178         } else if (ret > CL_TYPENO && acmode & AC_SCAN_VIR)
179             saved_ret = ret;
180         else
181             return ret;
182     }
183 
184     if (root->bcomp_metas) {
185         ret = cli_bcomp_scanbuf(orig_buffer, orig_length, virname, acres, root, mdata, ctx);
186         if (ret != CL_CLEAN) {
187             if (ret == CL_VIRUS) {
188                 if (SCAN_ALLMATCHES)
189                     viruses_found = 1;
190                 else {
191                     ret = cli_append_virus(ctx, *virname);
192                     if (ret != CL_CLEAN)
193                         return ret;
194                 }
195             } else if (ret > CL_TYPENO && acmode & AC_SCAN_VIR)
196                 saved_ret = ret;
197             else
198                 return ret;
199         }
200     }
201 
202     /* due to logical triggered, pcres cannot be evaluated until after full subsig matching */
203     /* cannot save pcre execution state without possible evasion; must scan entire buffer */
204     /* however, scanning the whole buffer may require the whole buffer being loaded into memory */
205 #if HAVE_PCRE
206     if (root->pcre_metas) {
207         int rc;
208         uint64_t maxfilesize;
209 
210         if (map && (pcremode == PCRE_SCAN_FMAP)) {
211             if (offset + length >= map->len) {
212                 /* check that scanned map does not exceed pcre maxfilesize limit */
213                 maxfilesize = (uint64_t)cl_engine_get_num(ctx->engine, CL_ENGINE_PCRE_MAX_FILESIZE, &rc);
214                 if (rc != CL_SUCCESS)
215                     return rc;
216                 if (maxfilesize && (map->len > maxfilesize)) {
217                     cli_dbgmsg("matcher_run: pcre max filesize (map) exceeded (limit: %llu, needed: %llu)\n",
218                                (long long unsigned)maxfilesize, (long long unsigned)map->len);
219                     return CL_EMAXSIZE;
220                 }
221 
222                 cli_dbgmsg("matcher_run: performing regex matching on full map: %u+%u(%u) >= %zu\n", offset, length, offset + length, map->len);
223 
224                 buffer = fmap_need_off_once(map, 0, map->len);
225                 if (!buffer)
226                     return CL_EMEM;
227 
228                 /* scan the full buffer */
229                 ret = cli_pcre_scanbuf(buffer, map->len, virname, acres, root, mdata, poffdata, ctx);
230             }
231         } else if (pcremode == PCRE_SCAN_BUFF) {
232             /* check that scanned buffer does not exceed pcre maxfilesize limit */
233             maxfilesize = (uint64_t)cl_engine_get_num(ctx->engine, CL_ENGINE_PCRE_MAX_FILESIZE, &rc);
234             if (rc != CL_SUCCESS)
235                 return rc;
236             if (maxfilesize && (length > maxfilesize)) {
237                 cli_dbgmsg("matcher_run: pcre max filesize (buf) exceeded (limit: %llu, needed: %u)\n", (long long unsigned)maxfilesize, length);
238                 return CL_EMAXSIZE;
239             }
240 
241             cli_dbgmsg("matcher_run: performing regex matching on buffer with no map: %u+%u(%u)\n", offset, length, offset + length);
242             /* scan the specified buffer */
243             ret = cli_pcre_scanbuf(buffer, length, virname, acres, root, mdata, poffdata, ctx);
244         }
245     }
246 #endif /* HAVE_PCRE */
247     /* end experimental fragment */
248 
249     if (ctx && !SCAN_ALLMATCHES && ret == CL_VIRUS) {
250         return cli_append_virus(ctx, *virname);
251     }
252     if (ctx && SCAN_ALLMATCHES && viruses_found) {
253         return CL_VIRUS;
254     }
255     if (saved_ret && ret == CL_CLEAN) {
256         return saved_ret;
257     }
258 
259     return ret;
260 }
261 
cli_scan_buff(const unsigned char * buffer,uint32_t length,uint32_t offset,cli_ctx * ctx,cli_file_t ftype,struct cli_ac_data ** acdata)262 cl_error_t cli_scan_buff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata)
263 {
264     cl_error_t ret = CL_CLEAN;
265     unsigned int i = 0, j = 0, viruses_found = 0;
266     struct cli_ac_data mdata;
267     struct cli_matcher *groot, *troot = NULL;
268     const char *virname            = NULL;
269     const struct cl_engine *engine = ctx->engine;
270 
271     if (!engine) {
272         cli_errmsg("cli_scan_buff: engine == NULL\n");
273         return CL_ENULLARG;
274     }
275 
276     groot = engine->root[0]; /* generic signatures */
277 
278     if (ftype) {
279         for (i = 1; i < CLI_MTARGETS; i++) {
280             for (j = 0; j < cli_mtargets[i].target_count; ++j) {
281                 if (cli_mtargets[i].target[j] == ftype) {
282                     troot = ctx->engine->root[i];
283                     break;
284                 }
285             }
286             if (troot) break;
287         }
288     }
289 
290     if (troot) {
291 
292         if (!acdata && (ret = cli_ac_initdata(&mdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
293             return ret;
294 
295         ret = matcher_run(troot, buffer, length, &virname, acdata ? (acdata[0]) : (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, PCRE_SCAN_BUFF, NULL, ctx->fmap, NULL, NULL, ctx);
296 
297         if (!acdata)
298             cli_ac_freedata(&mdata);
299 
300         if (ret == CL_EMEM)
301             return ret;
302         if (ret == CL_VIRUS) {
303             viruses_found = 1;
304             if (ctx && !SCAN_ALLMATCHES) {
305                 return ret;
306             }
307         }
308     }
309 
310     virname = NULL;
311 
312     if (!acdata && (ret = cli_ac_initdata(&mdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
313         return ret;
314 
315     ret = matcher_run(groot, buffer, length, &virname, acdata ? (acdata[1]) : (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, PCRE_SCAN_BUFF, NULL, ctx->fmap, NULL, NULL, ctx);
316 
317     if (!acdata)
318         cli_ac_freedata(&mdata);
319 
320     if (viruses_found)
321         return CL_VIRUS;
322     return ret;
323 }
324 
325 /*
326  * offdata[0]: type
327  * offdata[1]: offset value
328  * offdata[2]: max shift
329  * offdata[3]: section number
330  */
cli_caloff(const char * offstr,const struct cli_target_info * info,unsigned int target,uint32_t * offdata,uint32_t * offset_min,uint32_t * offset_max)331 cl_error_t cli_caloff(const char *offstr, const struct cli_target_info *info, unsigned int target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max)
332 {
333     char offcpy[65];
334     unsigned int n, val;
335     char *pt;
336 
337     if (!info) { /* decode offset string */
338         if (!offstr) {
339             cli_errmsg("cli_caloff: offstr == NULL\n");
340             return CL_ENULLARG;
341         }
342 
343         if (!strcmp(offstr, "*")) {
344             offdata[0] = *offset_max = *offset_min = CLI_OFF_ANY;
345             return CL_SUCCESS;
346         }
347 
348         if (strlen(offstr) > 64) {
349             cli_errmsg("cli_caloff: Offset string too long\n");
350             return CL_EMALFDB;
351         }
352         strcpy(offcpy, offstr);
353 
354         if ((pt = strchr(offcpy, ','))) {
355             if (!cli_isnumber(pt + 1)) {
356                 cli_errmsg("cli_caloff: Invalid offset shift value\n");
357                 return CL_EMALFDB;
358             }
359             offdata[2] = atoi(pt + 1);
360             *pt        = 0;
361         } else {
362             offdata[2] = 0;
363         }
364 
365         *offset_max = *offset_min = CLI_OFF_NONE;
366 
367         if (!strncmp(offcpy, "EP+", 3) || !strncmp(offcpy, "EP-", 3)) {
368             if (offcpy[2] == '+')
369                 offdata[0] = CLI_OFF_EP_PLUS;
370             else
371                 offdata[0] = CLI_OFF_EP_MINUS;
372 
373             if (!cli_isnumber(&offcpy[3])) {
374                 cli_errmsg("cli_caloff: Invalid offset value\n");
375                 return CL_EMALFDB;
376             }
377             offdata[1] = atoi(&offcpy[3]);
378 
379         } else if (offcpy[0] == 'S') {
380             if (offcpy[1] == 'E') {
381                 if (!cli_isnumber(&offcpy[2])) {
382                     cli_errmsg("cli_caloff: Invalid section number\n");
383                     return CL_EMALFDB;
384                 }
385                 offdata[0] = CLI_OFF_SE;
386                 offdata[3] = atoi(&offcpy[2]);
387 
388             } else if (!strncmp(offstr, "SL+", 3)) {
389                 offdata[0] = CLI_OFF_SL_PLUS;
390                 if (!cli_isnumber(&offcpy[3])) {
391                     cli_errmsg("cli_caloff: Invalid offset value\n");
392                     return CL_EMALFDB;
393                 }
394                 offdata[1] = atoi(&offcpy[3]);
395 
396             } else if (sscanf(offcpy, "S%u+%u", &n, &val) == 2) {
397                 offdata[0] = CLI_OFF_SX_PLUS;
398                 offdata[1] = val;
399                 offdata[3] = n;
400             } else {
401                 cli_errmsg("cli_caloff: Invalid offset string\n");
402                 return CL_EMALFDB;
403             }
404 
405         } else if (!strncmp(offcpy, "EOF-", 4)) {
406             offdata[0] = CLI_OFF_EOF_MINUS;
407             if (!cli_isnumber(&offcpy[4])) {
408                 cli_errmsg("cli_caloff: Invalid offset value\n");
409                 return CL_EMALFDB;
410             }
411             offdata[1] = atoi(&offcpy[4]);
412         } else if (!strncmp(offcpy, "VI", 2)) {
413             /* versioninfo */
414             offdata[0] = CLI_OFF_VERSION;
415         } else if (strchr(offcpy, '$')) {
416             if (sscanf(offcpy, "$%u$", &n) != 1) {
417                 cli_errmsg("cli_caloff: Invalid macro($) in offset: %s\n", offcpy);
418                 return CL_EMALFDB;
419             }
420             if (n >= 32) {
421                 cli_errmsg("cli_caloff: at most 32 macro groups supported\n");
422                 return CL_EMALFDB;
423             }
424             offdata[0] = CLI_OFF_MACRO;
425             offdata[1] = n;
426         } else {
427             offdata[0] = CLI_OFF_ABSOLUTE;
428             if (!cli_isnumber(offcpy)) {
429                 cli_errmsg("cli_caloff: Invalid offset value\n");
430                 return CL_EMALFDB;
431             }
432             *offset_min = offdata[1] = atoi(offcpy);
433             *offset_max              = *offset_min + offdata[2];
434         }
435 
436         if (offdata[0] != CLI_OFF_ANY && offdata[0] != CLI_OFF_ABSOLUTE &&
437             offdata[0] != CLI_OFF_EOF_MINUS && offdata[0] != CLI_OFF_MACRO) {
438             if (target != 1 && target != 6 && target != 9) {
439                 cli_errmsg("cli_caloff: Invalid offset type for target %u\n", target);
440                 return CL_EMALFDB;
441             }
442         }
443 
444     } else {
445         /* calculate relative offsets */
446         *offset_min = CLI_OFF_NONE;
447         if (offset_max)
448             *offset_max = CLI_OFF_NONE;
449         if (info->status == -1) {
450             // If the executable headers weren't parsed successfully then we
451             // can't process any ndb/ldb EOF-n/EP+n/EP-n/Sx+n/SEx/SL+n subsigs
452             return CL_SUCCESS;
453         }
454 
455         switch (offdata[0]) {
456             case CLI_OFF_EOF_MINUS:
457                 *offset_min = info->fsize - offdata[1];
458                 break;
459 
460             case CLI_OFF_EP_PLUS:
461                 *offset_min = info->exeinfo.ep + offdata[1];
462                 break;
463 
464             case CLI_OFF_EP_MINUS:
465                 *offset_min = info->exeinfo.ep - offdata[1];
466                 break;
467 
468             case CLI_OFF_SL_PLUS:
469                 *offset_min = info->exeinfo.sections[info->exeinfo.nsections - 1].raw + offdata[1];
470                 break;
471 
472             case CLI_OFF_SX_PLUS:
473                 if (offdata[3] >= info->exeinfo.nsections)
474                     *offset_min = CLI_OFF_NONE;
475                 else
476                     *offset_min = info->exeinfo.sections[offdata[3]].raw + offdata[1];
477                 break;
478 
479             case CLI_OFF_SE:
480                 if (offdata[3] >= info->exeinfo.nsections) {
481                     *offset_min = CLI_OFF_NONE;
482                 } else {
483                     *offset_min = info->exeinfo.sections[offdata[3]].raw;
484                     if (offset_max)
485                         *offset_max = *offset_min + info->exeinfo.sections[offdata[3]].rsz + offdata[2];
486                     // TODO offdata[2] == MaxShift. Won't this make offset_max
487                     // extend beyond the end of the section?  This doesn't seem like
488                     // what we want...
489                 }
490                 break;
491 
492             case CLI_OFF_VERSION:
493                 if (offset_max)
494                     *offset_min = *offset_max = CLI_OFF_ANY;
495                 break;
496             default:
497                 cli_errmsg("cli_caloff: Not a relative offset (type: %u)\n", offdata[0]);
498                 return CL_EARG;
499         }
500 
501         if (offset_max && *offset_max == CLI_OFF_NONE && *offset_min != CLI_OFF_NONE)
502             *offset_max = *offset_min + offdata[2];
503     }
504 
505     return CL_SUCCESS;
506 }
507 
cli_targetinfo_init(struct cli_target_info * info)508 void cli_targetinfo_init(struct cli_target_info *info)
509 {
510 
511     if (NULL == info) {
512         return;
513     }
514     info->status = 0;
515     cli_exe_info_init(&(info->exeinfo), 0);
516 }
517 
cli_targetinfo(struct cli_target_info * info,unsigned int target,cli_ctx * ctx)518 void cli_targetinfo(struct cli_target_info *info, unsigned int target, cli_ctx *ctx)
519 {
520     int (*einfo)(cli_ctx *, struct cli_exe_info *) = NULL;
521 
522     info->fsize = ctx->fmap->len;
523 
524     if (target == 1)
525         einfo = cli_pe_targetinfo;
526     else if (target == 6)
527         einfo = cli_elfheader;
528     else if (target == 9)
529         einfo = cli_machoheader;
530     else
531         return;
532 
533     if (einfo(ctx, &info->exeinfo))
534         info->status = -1;
535     else
536         info->status = 1;
537 }
538 
cli_targetinfo_destroy(struct cli_target_info * info)539 void cli_targetinfo_destroy(struct cli_target_info *info)
540 {
541 
542     if (NULL == info) {
543         return;
544     }
545 
546     cli_exe_info_destroy(&(info->exeinfo));
547     info->status = 0;
548 }
549 
cli_check_fp(cli_ctx * ctx,const char * vname)550 cl_error_t cli_check_fp(cli_ctx *ctx, const char *vname)
551 {
552     cl_error_t status = CL_VIRUS;
553     char md5[33];
554     unsigned int i;
555     const char *virname = NULL;
556     fmap_t *map;
557     int32_t stack_index;
558     const char *ptr;
559     uint8_t shash1[SHA1_HASH_SIZE * 2 + 1];
560     uint8_t shash256[SHA256_HASH_SIZE * 2 + 1];
561     int have_sha1, have_sha256;
562     unsigned char *digest;
563     size_t size;
564 
565     stack_index = (int32_t)ctx->recursion_level;
566 
567     while (stack_index >= 0) {
568         map = ctx->recursion_stack[stack_index].fmap;
569 
570         if (CL_SUCCESS != fmap_get_MD5(map, &digest)) {
571             cli_dbgmsg("cli_check_fp: Failed to get a hash for the map at stack index # %u\n", stack_index);
572             stack_index--;
573             continue;
574         }
575         size = map->len;
576 
577         /*
578          * First, check the MD5 digest.
579          * MD5 is default, so it always exists.
580          */
581         if (cli_hm_scan(digest, size, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) {
582             cli_dbgmsg("cli_check_fp(md5): Found false positive detection (fp sig: %s), size: %d\n", virname, (int)size);
583             return CL_CLEAN;
584         } else if (cli_hm_scan_wild(digest, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) {
585             cli_dbgmsg("cli_check_fp(md5): Found false positive detection (fp sig: %s), size: *\n", virname);
586             return CL_CLEAN;
587         }
588 
589         if (cli_debug_flag || ctx->engine->cb_hash) {
590             const char *name = ctx->recursion_stack[stack_index].fmap->name;
591             const char *type = cli_ftname(ctx->recursion_stack[stack_index].type);
592 
593             for (i = 0; i < 16; i++)
594                 sprintf(md5 + i * 2, "%02x", digest[i]);
595             md5[32] = 0;
596 
597             cli_dbgmsg("FP SIGNATURE: %s:%u:%s  # Name: %s, Type: %s\n",
598                        md5, (unsigned int)size, vname ? vname : "Name", name ? name : "n/a", type);
599         }
600 
601         have_sha1   = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA1) || cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 1);
602         have_sha256 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA256, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA256);
603         if (have_sha1 || have_sha256) {
604             if ((ptr = fmap_need_off_once(map, 0, size))) {
605                 if (have_sha1) {
606                     cl_sha1(ptr, size, &shash1[SHA1_HASH_SIZE], NULL);
607 
608                     if (cli_hm_scan(&shash1[SHA1_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) {
609                         cli_dbgmsg("cli_check_fp(sha1): Found false positive detection (fp sig: %s)\n", virname);
610                         return CL_CLEAN;
611                     }
612                     if (cli_hm_scan_wild(&shash1[SHA1_HASH_SIZE], &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) {
613                         cli_dbgmsg("cli_check_fp(sha1): Found false positive detection (fp sig: %s)\n", virname);
614                         return CL_CLEAN;
615                     }
616                     /* See whether the hash matches those loaded in from .cat files
617                      * (associated with the .CAB file type) */
618                     if (cli_hm_scan(&shash1[SHA1_HASH_SIZE], 1, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) {
619                         cli_dbgmsg("cli_check_fp(sha1): Found .CAB false positive detection via catalog file\n");
620                         return CL_CLEAN;
621                     }
622                 }
623 
624                 if (have_sha256) {
625                     cl_sha256(ptr, size, &shash256[SHA256_HASH_SIZE], NULL);
626 
627                     if (cli_hm_scan(&shash256[SHA256_HASH_SIZE], size, &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) {
628                         cli_dbgmsg("cli_check_fp(sha256): Found false positive detection (fp sig: %s)\n", virname);
629                         return CL_CLEAN;
630                     }
631                     if (cli_hm_scan_wild(&shash256[SHA256_HASH_SIZE], &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) {
632                         cli_dbgmsg("cli_check_fp(sha256): Found false positive detection (fp sig: %s)\n", virname);
633                         return CL_CLEAN;
634                     }
635                     /* See whether the hash matches those loaded in from .cat files
636                      * (associated with the .CAB file type) */
637                     if (cli_hm_scan(&shash256[SHA256_HASH_SIZE], 1, &virname, ctx->engine->hm_fp, CLI_HASH_SHA256) == CL_VIRUS) {
638                         cli_dbgmsg("cli_check_fp(sha256): Found .CAB false positive detection via catalog file\n");
639                         return CL_CLEAN;
640                     }
641                 }
642             }
643         }
644 
645 #ifdef HAVE__INTERNAL__SHA_COLLECT
646         if (SCAN_DEV_COLLECT_SHA && (ctx->sha_collect > 0)) {
647             if ((ptr = fmap_need_off_once(map, 0, size))) {
648                 if (!have_sha256)
649                     cl_sha256(ptr, size, shash256 + SHA256_HASH_SIZE, NULL);
650 
651                 for (i = 0; i < SHA256_HASH_SIZE; i++)
652                     sprintf((char *)shash256 + i * 2, "%02x", shash256[SHA256_HASH_SIZE + i]);
653 
654                 if (!have_sha1)
655                     cl_sha1(ptr, size, shash1 + SHA1_HASH_SIZE);
656 
657                 for (i = 0; i < SHA1_HASH_SIZE; i++)
658                     sprintf((char *)shash1 + i * 2, "%02x", shash1[SHA1_HASH_SIZE + i]);
659 
660                 if (NULL == ctx->target_filepath) {
661                     cli_errmsg("COLLECT:%s:%s:%u:%s:%s\n", shash256, shash1, size, vname ? vname : "noname", "NO_IDEA");
662                 } else {
663                     cli_errmsg("COLLECT:%s:%s:%u:%s:%s\n", shash256, shash1, size, vname ? vname : "noname", ctx->target_filepath);
664                 }
665             } else
666                 cli_errmsg("can't compute sha\n!");
667 
668             ctx->sha_collect = -1;
669         }
670 #endif
671 
672         if (ctx->engine->cb_hash)
673             ctx->engine->cb_hash(fmap_fd(ctx->fmap), size, (const unsigned char *)md5, vname ? vname : "noname", ctx->cb_ctx);
674 
675         if (ctx->engine->cb_stats_add_sample) {
676             stats_section_t sections;
677             memset(&sections, 0x00, sizeof(stats_section_t));
678 
679             if (!(ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_STATS) &&
680                 !(ctx->engine->dconf->stats & (DCONF_STATS_DISABLED | DCONF_STATS_PE_SECTION_DISABLED)))
681                 cli_genhash_pe(ctx, CL_GENHASH_PE_CLASS_SECTION, 1, &sections);
682 
683             // TODO We probably only want to call cb_stats_add_sample when
684             // sections.section != NULL... leaving as is for now
685             ctx->engine->cb_stats_add_sample(vname ? vname : "noname", digest, size, &sections, ctx->engine->stats_data);
686 
687             if (sections.sections) {
688                 free(sections.sections);
689             }
690         }
691 
692         stack_index -= 1;
693     }
694 
695     return status;
696 }
697 
matchicon(cli_ctx * ctx,struct cli_exe_info * exeinfo,const char * grp1,const char * grp2)698 static cl_error_t matchicon(cli_ctx *ctx, struct cli_exe_info *exeinfo, const char *grp1, const char *grp2)
699 {
700     icon_groupset iconset;
701 
702     if (!ctx ||
703         !ctx->engine ||
704         !ctx->engine->iconcheck ||
705         !ctx->engine->iconcheck->group_counts[0] ||
706         !ctx->engine->iconcheck->group_counts[1] ||
707         !exeinfo->res_addr) return CL_CLEAN;
708 
709     if (!(ctx->dconf->pe & PE_CONF_MATCHICON))
710         return CL_CLEAN;
711 
712     cli_icongroupset_init(&iconset);
713     cli_icongroupset_add(grp1 ? grp1 : "*", &iconset, 0, ctx);
714     cli_icongroupset_add(grp2 ? grp2 : "*", &iconset, 1, ctx);
715     return cli_scanicon(&iconset, ctx, exeinfo);
716 }
717 
cli_bcapi_matchicon(struct cli_bc_ctx * ctx,const uint8_t * grp1,int32_t grp1len,const uint8_t * grp2,int32_t grp2len)718 int32_t cli_bcapi_matchicon(struct cli_bc_ctx *ctx, const uint8_t *grp1, int32_t grp1len,
719                             const uint8_t *grp2, int32_t grp2len)
720 {
721     cl_error_t ret;
722     char group1[128], group2[128];
723     const char **oldvirname;
724     struct cli_exe_info info;
725 
726     // TODO This isn't a good check, since EP will be zero for DLLs and
727     // (assuming pedata->ep is populated from exeinfo->pe) non-zero for
728     // some MachO and ELF executables
729     if (!ctx->hooks.pedata->ep) {
730         cli_dbgmsg("bytecode: matchicon only works with PE files\n");
731         return -1;
732     }
733     if ((size_t)grp1len > sizeof(group1) - 1 ||
734         (size_t)grp2len > sizeof(group2) - 1)
735         return -1;
736     oldvirname                     = ((cli_ctx *)ctx->ctx)->virname;
737     ((cli_ctx *)ctx->ctx)->virname = NULL;
738     memcpy(group1, grp1, grp1len);
739     memcpy(group2, grp2, grp2len);
740     group1[grp1len] = 0;
741     group2[grp2len] = 0;
742     memset(&info, 0, sizeof(info));
743     if (ctx->bc->kind == BC_PE_UNPACKER || ctx->bc->kind == BC_PE_ALL) {
744         if (le16_to_host(ctx->hooks.pedata->file_hdr.Characteristics) & 0x2000 ||
745             !ctx->hooks.pedata->dirs[2].Size)
746             info.res_addr = 0;
747         else
748             info.res_addr = le32_to_host(ctx->hooks.pedata->dirs[2].VirtualAddress);
749     } else
750         info.res_addr = ctx->resaddr; /* from target_info */
751     info.sections  = (struct cli_exe_section *)ctx->sections;
752     info.nsections = ctx->hooks.pedata->nsections;
753     info.hdr_size  = ctx->hooks.pedata->hdr_size;
754     cli_dbgmsg("bytecode matchicon %s %s\n", group1, group2);
755     ret                            = matchicon(ctx->ctx, &info, group1[0] ? group1 : NULL,
756                     group2[0] ? group2 : NULL);
757     ((cli_ctx *)ctx->ctx)->virname = oldvirname;
758     return (int32_t)ret;
759 }
760 
cli_scan_desc(int desc,cli_ctx * ctx,cli_file_t ftype,uint8_t ftonly,struct cli_matched_type ** ftoffset,unsigned int acmode,struct cli_ac_result ** acres,const char * name)761 cl_error_t cli_scan_desc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, const char *name)
762 {
763     cl_error_t status = CL_CLEAN;
764     int empty;
765     fmap_t *new_map = NULL;
766     fmap_t *map     = ctx->fmap; /* Store off the parent fmap for easy reference */
767 
768     new_map = fmap_check_empty(desc, 0, 0, &empty, name);
769     if (NULL == new_map) {
770         if (!empty) {
771             cli_dbgmsg("cli_scan_desc: Failed to allocate new map for file descriptor scan.\n");
772             status = CL_EMEM;
773         }
774         goto done;
775     }
776 
777     status = cli_recursion_stack_push(ctx, new_map, ftype, true); /* Perform scan with child fmap */
778     if (CL_SUCCESS != status) {
779         cli_dbgmsg("cli_scan_desc: Failed to scan fmap.\n");
780         goto done;
781     }
782 
783     status = cli_scan_fmap(ctx, ftype, ftonly, ftoffset, acmode, acres, NULL);
784 
785     map->dont_cache_flag = ctx->fmap->dont_cache_flag; /* Set the parent layer's "don't cache" flag to match the child.
786                                                           TODO: This may not be needed since `emax_reached()` should've
787                                                           already done that for us. */
788 
789     (void)cli_recursion_stack_pop(ctx); /* Restore the parent fmap */
790 
791 done:
792     if (NULL != new_map) {
793         funmap(new_map);
794     }
795 
796     return status;
797 }
798 
intermediates_eval(cli_ctx * ctx,struct cli_ac_lsig * ac_lsig)799 static int intermediates_eval(cli_ctx *ctx, struct cli_ac_lsig *ac_lsig)
800 {
801     uint32_t i, icnt = ac_lsig->tdb.intermediates[0];
802     int32_t j = -1;
803 
804     if (ctx->recursion_level < icnt)
805         return 0;
806 
807     for (i = icnt; i > 0; i--) {
808         if (ac_lsig->tdb.intermediates[i] == CL_TYPE_ANY)
809             continue;
810         if (ac_lsig->tdb.intermediates[i] != cli_recursion_stack_get_type(ctx, j--))
811             return 0;
812     }
813     return 1;
814 }
815 
lsig_eval(cli_ctx * ctx,struct cli_matcher * root,struct cli_ac_data * acdata,struct cli_target_info * target_info,const char * hash,uint32_t lsid)816 static cl_error_t lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash, uint32_t lsid)
817 {
818     cl_error_t status           = CL_CLEAN;
819     unsigned evalcnt            = 0;
820     uint64_t evalids            = 0;
821     fmap_t *new_map             = NULL;
822     struct cli_ac_lsig *ac_lsig = root->ac_lsigtable[lsid];
823     char *exp                   = ac_lsig->u.logic;
824     char *exp_end               = exp + strlen(exp);
825 
826     status = cli_ac_chkmacro(root, acdata, lsid);
827     if (status != CL_SUCCESS)
828         return status;
829 
830     if (cli_ac_chklsig(exp, exp_end, acdata->lsigcnt[lsid], &evalcnt, &evalids, 0) == 1) {
831         if (ac_lsig->tdb.container && ac_lsig->tdb.container[0] != cli_recursion_stack_get_type(ctx, -2))
832             goto done;
833         if (ac_lsig->tdb.intermediates && !intermediates_eval(ctx, ac_lsig))
834             goto done;
835         if (ac_lsig->tdb.filesize && (ac_lsig->tdb.filesize[0] > ctx->fmap->len || ac_lsig->tdb.filesize[1] < ctx->fmap->len))
836             goto done;
837 
838         if (ac_lsig->tdb.ep || ac_lsig->tdb.nos) {
839             if (!target_info || target_info->status != 1)
840                 goto done;
841             if (ac_lsig->tdb.ep && (ac_lsig->tdb.ep[0] > target_info->exeinfo.ep || ac_lsig->tdb.ep[1] < target_info->exeinfo.ep))
842                 goto done;
843             if (ac_lsig->tdb.nos && (ac_lsig->tdb.nos[0] > target_info->exeinfo.nsections || ac_lsig->tdb.nos[1] < target_info->exeinfo.nsections))
844                 goto done;
845         }
846 
847         if (hash && ac_lsig->tdb.handlertype) {
848             if (0 != memcmp(ctx->handlertype_hash, hash, 16)) {
849                 /*
850                  * Create an fmap window into our current fmap using the original offset & length, and rescan as the new type
851                  *
852                  * TODO: Unsure if creating an fmap is the right move, or if we should rescan with the current fmap as-is,
853                  * since it's not really a container so much as it is type reassignment. This new fmap layer protect agains
854                  * a possible infinite loop by applying the scan recursion limit, but maybe there's a better way?
855                  * Testing with both HandlerType type reassignment sigs + Container/Intermediates sigs should indicate if
856                  * a change is needed.
857                  */
858                 new_map = fmap_duplicate(ctx->fmap, 0, ctx->fmap->len, ctx->fmap->name);
859                 if (NULL == new_map) {
860                     status = CL_EMEM;
861                     cli_dbgmsg("Failed to duplicate the current fmap for a re-scan as a different type.\n");
862                     goto done;
863                 }
864 
865                 memcpy(ctx->handlertype_hash, hash, 16);
866 
867                 status = cli_recursion_stack_push(ctx, new_map, ac_lsig->tdb.handlertype[0], true); /* Perform scan with child fmap */
868                 if (CL_SUCCESS != status) {
869                     cli_dbgmsg("Failed to re-scan fmap as a new type.\n");
870                     goto done;
871                 }
872 
873                 status = cli_magic_scan(ctx, ac_lsig->tdb.handlertype[0]);
874 
875                 (void)cli_recursion_stack_pop(ctx); /* Restore the parent fmap */
876 
877                 if (CL_VIRUS == status) {
878                     status = CL_VIRUS;
879                     goto done;
880                 }
881 
882                 goto done;
883             }
884         }
885 
886         if (ac_lsig->tdb.icongrp1 || ac_lsig->tdb.icongrp2) {
887             if (!target_info || target_info->status != 1) {
888                 goto done;
889             }
890 
891             if (CL_VIRUS == matchicon(ctx, &target_info->exeinfo, ac_lsig->tdb.icongrp1, ac_lsig->tdb.icongrp2)) {
892                 if (!ac_lsig->bc_idx) {
893                     status = cli_append_virus(ctx, ac_lsig->virname);
894                     if (status != CL_CLEAN) {
895                         goto done;
896                     }
897                 } else if (CL_VIRUS == cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, ac_lsig->bc_idx, acdata->lsigcnt[lsid], acdata->lsigsuboff_first[lsid], ctx->fmap)) {
898                     status = CL_VIRUS;
899                     goto done;
900                 }
901             }
902             goto done;
903         }
904         if (!ac_lsig->bc_idx) {
905             status = cli_append_virus(ctx, ac_lsig->virname);
906             if (status != CL_CLEAN) {
907                 goto done;
908             }
909         }
910         if (CL_VIRUS == cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, ac_lsig->bc_idx, acdata->lsigcnt[lsid], acdata->lsigsuboff_first[lsid], ctx->fmap)) {
911             status = CL_VIRUS;
912             goto done;
913         }
914     }
915 
916 done:
917     if (NULL != new_map) {
918         free_duplicate_fmap(new_map);
919     }
920 
921     return status;
922 }
923 
924 #ifdef HAVE_YARA
yara_eval(cli_ctx * ctx,struct cli_matcher * root,struct cli_ac_data * acdata,struct cli_target_info * target_info,const char * hash,uint32_t lsid)925 static cl_error_t yara_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash, uint32_t lsid)
926 {
927     struct cli_ac_lsig *ac_lsig = root->ac_lsigtable[lsid];
928     cl_error_t rc;
929     YR_SCAN_CONTEXT context;
930 
931     (void)hash;
932 
933     memset(&context, 0, sizeof(YR_SCAN_CONTEXT));
934     context.fmap      = ctx->fmap;
935     context.file_size = ctx->fmap->len;
936     if (target_info != NULL) {
937         if (target_info->status == 1)
938             context.entry_point = target_info->exeinfo.ep;
939     }
940 
941     rc = yr_execute_code(ac_lsig, acdata, &context, 0, 0);
942 
943     if (rc == CL_VIRUS) {
944         if (ac_lsig->flag & CLI_LSIG_FLAG_PRIVATE) {
945             rc = CL_CLEAN;
946         } else {
947             rc = cli_append_virus(ctx, ac_lsig->virname);
948         }
949     }
950     return rc;
951 }
952 #endif
953 
cli_exp_eval(cli_ctx * ctx,struct cli_matcher * root,struct cli_ac_data * acdata,struct cli_target_info * target_info,const char * hash)954 cl_error_t cli_exp_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash)
955 {
956     uint8_t viruses_found = 0;
957     uint32_t i;
958     cl_error_t rc = CL_SUCCESS;
959 
960     for (i = 0; i < root->ac_lsigs; i++) {
961         if (root->ac_lsigtable[i]->type == CLI_LSIG_NORMAL)
962             rc = lsig_eval(ctx, root, acdata, target_info, hash, i);
963 #ifdef HAVE_YARA
964         else if (root->ac_lsigtable[i]->type == CLI_YARA_NORMAL || root->ac_lsigtable[i]->type == CLI_YARA_OFFSET)
965             rc = yara_eval(ctx, root, acdata, target_info, hash, i);
966 #endif
967         if (rc == CL_VIRUS) {
968             viruses_found = 1;
969             if (SCAN_ALLMATCHES)
970                 continue;
971             break;
972         }
973     }
974     if (viruses_found)
975         return CL_VIRUS;
976     return CL_CLEAN;
977 }
978 
cli_scan_fmap(cli_ctx * ctx,cli_file_t ftype,uint8_t ftonly,struct cli_matched_type ** ftoffset,unsigned int acmode,struct cli_ac_result ** acres,unsigned char * refhash)979 cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash)
980 {
981     const unsigned char *buff;
982     cl_error_t ret = CL_CLEAN, type = CL_CLEAN;
983     int compute_hash[CLI_HASH_AVAIL_TYPES];
984     unsigned int i = 0, j = 0, bm_offmode = 0;
985     uint32_t maxpatlen, bytes, offset     = 0;
986     struct cli_ac_data gdata, tdata;
987     struct cli_bm_off toff;
988     struct cli_pcre_off gpoff, tpoff;
989     unsigned char digest[CLI_HASH_AVAIL_TYPES][32];
990     struct cli_matcher *groot = NULL, *troot = NULL;
991     struct cli_target_info info;
992     struct cli_matcher *hdb, *fp;
993     const char *virname;
994     uint32_t viruses_found = 0;
995     void *md5ctx, *sha1ctx, *sha256ctx;
996 
997     if (!ctx->engine) {
998         cli_errmsg("cli_scan_fmap: engine == NULL\n");
999         return CL_ENULLARG;
1000     }
1001 
1002     md5ctx = cl_hash_init("md5");
1003     if (!(md5ctx))
1004         return CL_EMEM;
1005 
1006     sha1ctx = cl_hash_init("sha1");
1007     if (!(sha1ctx)) {
1008         cl_hash_destroy(md5ctx);
1009         return CL_EMEM;
1010     }
1011 
1012     sha256ctx = cl_hash_init("sha256");
1013     if (!(sha256ctx)) {
1014         cl_hash_destroy(md5ctx);
1015         cl_hash_destroy(sha1ctx);
1016         return CL_EMEM;
1017     }
1018 
1019     if (!ftonly)
1020         groot = ctx->engine->root[0]; /* generic signatures */
1021 
1022     if (ftype) {
1023         for (i = 1; i < CLI_MTARGETS; i++) {
1024             for (j = 0; j < cli_mtargets[i].target_count; ++j) {
1025                 if (cli_mtargets[i].target[j] == ftype) {
1026                     troot = ctx->engine->root[i];
1027                     break;
1028                 }
1029             }
1030             if (troot) break;
1031         }
1032     }
1033 
1034     if (ftonly) {
1035         if (!troot) {
1036             cl_hash_destroy(md5ctx);
1037             cl_hash_destroy(sha1ctx);
1038             cl_hash_destroy(sha256ctx);
1039             return CL_CLEAN;
1040         }
1041 
1042         maxpatlen = troot->maxpatlen;
1043     } else {
1044         if (troot)
1045             maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen);
1046         else
1047             maxpatlen = groot->maxpatlen;
1048     }
1049 
1050     cli_targetinfo_init(&info);
1051     cli_targetinfo(&info, i, ctx);
1052 
1053     if (-1 == info.status) {
1054         cli_dbgmsg("cli_scan_fmap: Failed to successfully parse the executable header. "
1055                    "Scan features will be disabled, such as "
1056                    "NDB/LDB subsigs using EOF-n/EP+n/EP-n/Sx+n/SEx/SL+n, "
1057                    "fuzzy icon matching, "
1058                    "MDB/IMP sigs, "
1059                    "and bytecode sigs that require exe metadata\n");
1060     }
1061 
1062     /* If it's a PE, check the Authenticode header.  This would be more
1063      * appropriate in cli_scanpe, but scanraw->cli_scan_fmap gets
1064      * called first for PEs, and we want to determine the whitelist/blacklist
1065      * status early on so we can skip things like embedded PE extraction
1066      * (which is broken for signed binaries within signed binaries).
1067      *
1068      * If we want to add support for more signature parsing in the future
1069      * (Ex: MachO sigs), do that here too.
1070      *
1071      * One benefit of not continuing on to scan files with trusted signatures
1072      * is that the bytes associated with the exe won't get counted against the
1073      * scansize limits, which means we have an increased chance of catching
1074      * malware in container types (NSIS, iShield, etc.) where the file size is
1075      * large.  A common case where this occurs is installers that embed one
1076      * or more of the various Microsoft Redistributable Setup packages.  These
1077      * can easily be 5 MB or more in size, and might appear before malware
1078      * does in a given sample.
1079      */
1080 
1081     if (1 == info.status && i == 1) {
1082         ret = cli_check_auth_header(ctx, &(info.exeinfo));
1083 
1084         if ((ret == CL_VIRUS || ret == CL_VERIFIED) && !SCAN_ALLMATCHES) {
1085             cli_targetinfo_destroy(&info);
1086             cl_hash_destroy(md5ctx);
1087             cl_hash_destroy(sha1ctx);
1088             cl_hash_destroy(sha256ctx);
1089             return ret;
1090         }
1091 
1092         ret = CL_CLEAN;
1093     }
1094 
1095     if (!ftonly) {
1096         if ((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) ||
1097             (ret = cli_ac_caloff(groot, &gdata, &info))) {
1098             cli_targetinfo_destroy(&info);
1099             cl_hash_destroy(md5ctx);
1100             cl_hash_destroy(sha1ctx);
1101             cl_hash_destroy(sha256ctx);
1102             return ret;
1103         }
1104         if ((ret = cli_pcre_recaloff(groot, &gpoff, &info, ctx))) {
1105             cli_ac_freedata(&gdata);
1106             cli_targetinfo_destroy(&info);
1107             cl_hash_destroy(md5ctx);
1108             cl_hash_destroy(sha1ctx);
1109             cl_hash_destroy(sha256ctx);
1110             return ret;
1111         }
1112     }
1113 
1114     if (troot) {
1115         if ((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) ||
1116             (ret = cli_ac_caloff(troot, &tdata, &info))) {
1117             if (!ftonly) {
1118                 cli_ac_freedata(&gdata);
1119                 cli_pcre_freeoff(&gpoff);
1120             }
1121             cli_targetinfo_destroy(&info);
1122             cl_hash_destroy(md5ctx);
1123             cl_hash_destroy(sha1ctx);
1124             cl_hash_destroy(sha256ctx);
1125             return ret;
1126         }
1127         if (troot->bm_offmode) {
1128             if (ctx->fmap->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) {
1129                 if ((ret = cli_bm_initoff(troot, &toff, &info))) {
1130                     if (!ftonly) {
1131                         cli_ac_freedata(&gdata);
1132                         cli_pcre_freeoff(&gpoff);
1133                     }
1134 
1135                     cli_ac_freedata(&tdata);
1136                     cli_targetinfo_destroy(&info);
1137                     cl_hash_destroy(md5ctx);
1138                     cl_hash_destroy(sha1ctx);
1139                     cl_hash_destroy(sha256ctx);
1140                     return ret;
1141                 }
1142 
1143                 bm_offmode = 1;
1144             }
1145         }
1146         if ((ret = cli_pcre_recaloff(troot, &tpoff, &info, ctx))) {
1147             if (!ftonly) {
1148                 cli_ac_freedata(&gdata);
1149                 cli_pcre_freeoff(&gpoff);
1150             }
1151 
1152             cli_ac_freedata(&tdata);
1153             if (bm_offmode)
1154                 cli_bm_freeoff(&toff);
1155             cli_targetinfo_destroy(&info);
1156             cl_hash_destroy(md5ctx);
1157             cl_hash_destroy(sha1ctx);
1158             cl_hash_destroy(sha256ctx);
1159             return ret;
1160         }
1161     }
1162 
1163     hdb = ctx->engine->hm_hdb;
1164     fp  = ctx->engine->hm_fp;
1165 
1166     if (!ftonly && hdb) {
1167         if (!refhash) {
1168             if (cli_hm_have_size(hdb, CLI_HASH_MD5, ctx->fmap->len) ||
1169                 cli_hm_have_size(fp, CLI_HASH_MD5, ctx->fmap->len) ||
1170                 cli_hm_have_wild(hdb, CLI_HASH_MD5) ||
1171                 cli_hm_have_wild(fp, CLI_HASH_MD5)) {
1172                 compute_hash[CLI_HASH_MD5] = 1;
1173             } else {
1174                 compute_hash[CLI_HASH_MD5] = 0;
1175             }
1176         } else {
1177             compute_hash[CLI_HASH_MD5] = 0;
1178             memcpy(digest[CLI_HASH_MD5], refhash, 16);
1179         }
1180 
1181         if (cli_hm_have_size(hdb, CLI_HASH_SHA1, ctx->fmap->len) ||
1182             cli_hm_have_wild(hdb, CLI_HASH_SHA1) ||
1183             cli_hm_have_size(fp, CLI_HASH_SHA1, ctx->fmap->len) ||
1184             cli_hm_have_wild(fp, CLI_HASH_SHA1)) {
1185             compute_hash[CLI_HASH_SHA1] = 1;
1186         } else {
1187             compute_hash[CLI_HASH_SHA1] = 0;
1188         }
1189 
1190         if (cli_hm_have_size(hdb, CLI_HASH_SHA256, ctx->fmap->len) ||
1191             cli_hm_have_wild(hdb, CLI_HASH_SHA256) ||
1192             cli_hm_have_size(fp, CLI_HASH_SHA256, ctx->fmap->len) ||
1193             cli_hm_have_wild(fp, CLI_HASH_SHA256)) {
1194             compute_hash[CLI_HASH_SHA256] = 1;
1195         } else {
1196             compute_hash[CLI_HASH_SHA256] = 0;
1197         }
1198     }
1199 
1200     while (offset < ctx->fmap->len) {
1201         bytes = MIN(ctx->fmap->len - offset, SCANBUFF);
1202         if (!(buff = fmap_need_off_once(ctx->fmap, offset, bytes)))
1203             break;
1204         if (ctx->scanned)
1205             *ctx->scanned += bytes / CL_COUNT_PRECISION;
1206 
1207         if (troot) {
1208             virname = NULL;
1209             ret     = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, ctx->fmap, bm_offmode ? &toff : NULL, &tpoff, ctx);
1210 
1211             if (virname) {
1212                 /* virname already appended by matcher_run */
1213                 viruses_found = 1;
1214             }
1215             if ((ret == CL_VIRUS && !SCAN_ALLMATCHES) || ret == CL_EMEM) {
1216                 if (!ftonly) {
1217                     cli_ac_freedata(&gdata);
1218                     cli_pcre_freeoff(&gpoff);
1219                 }
1220 
1221                 cli_ac_freedata(&tdata);
1222                 if (bm_offmode)
1223                     cli_bm_freeoff(&toff);
1224                 cli_pcre_freeoff(&tpoff);
1225 
1226                 cli_targetinfo_destroy(&info);
1227                 cl_hash_destroy(md5ctx);
1228                 cl_hash_destroy(sha1ctx);
1229                 cl_hash_destroy(sha256ctx);
1230                 return ret;
1231             }
1232         }
1233 
1234         if (!ftonly) {
1235             virname = NULL;
1236             ret     = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, ctx->fmap, NULL, &gpoff, ctx);
1237 
1238             if (virname) {
1239                 /* virname already appended by matcher_run */
1240                 viruses_found = 1;
1241             }
1242             if ((ret == CL_VIRUS && !SCAN_ALLMATCHES) || ret == CL_EMEM) {
1243                 cli_ac_freedata(&gdata);
1244                 cli_pcre_freeoff(&gpoff);
1245                 if (troot) {
1246                     cli_ac_freedata(&tdata);
1247                     if (bm_offmode)
1248                         cli_bm_freeoff(&toff);
1249                     cli_pcre_freeoff(&tpoff);
1250                 }
1251 
1252                 cli_targetinfo_destroy(&info);
1253                 cl_hash_destroy(md5ctx);
1254                 cl_hash_destroy(sha1ctx);
1255                 cl_hash_destroy(sha256ctx);
1256                 return ret;
1257             } else if ((acmode & AC_SCAN_FT) && ((cli_file_t)ret >= CL_TYPENO)) {
1258                 if (ret > type)
1259                     type = ret;
1260             }
1261 
1262             /* if (bytes <= (maxpatlen * (offset!=0))), it means the last window finished the file hashing *
1263              *   since the last window is responsible for adding intersection between windows (maxpatlen)  */
1264             if (hdb && (bytes > (maxpatlen * (offset != 0)))) {
1265                 const void *data  = buff + maxpatlen * (offset != 0);
1266                 uint32_t data_len = bytes - maxpatlen * (offset != 0);
1267 
1268                 if (compute_hash[CLI_HASH_MD5])
1269                     cl_update_hash(md5ctx, (void *)data, data_len);
1270                 if (compute_hash[CLI_HASH_SHA1])
1271                     cl_update_hash(sha1ctx, (void *)data, data_len);
1272                 if (compute_hash[CLI_HASH_SHA256])
1273                     cl_update_hash(sha256ctx, (void *)data, data_len);
1274             }
1275         }
1276 
1277         if (bytes < SCANBUFF)
1278             break;
1279 
1280         offset += bytes - maxpatlen;
1281     }
1282 
1283     if (!ftonly && hdb) {
1284         enum CLI_HASH_TYPE hashtype, hashtype2;
1285 
1286         if (compute_hash[CLI_HASH_MD5]) {
1287             cl_finish_hash(md5ctx, digest[CLI_HASH_MD5]);
1288             md5ctx = NULL;
1289         }
1290         if (refhash)
1291             compute_hash[CLI_HASH_MD5] = 1;
1292         if (compute_hash[CLI_HASH_SHA1]) {
1293             cl_finish_hash(sha1ctx, digest[CLI_HASH_SHA1]);
1294             sha1ctx = NULL;
1295         }
1296         if (compute_hash[CLI_HASH_SHA256]) {
1297             cl_finish_hash(sha256ctx, digest[CLI_HASH_SHA256]);
1298             sha256ctx = NULL;
1299         }
1300 
1301         virname = NULL;
1302         for (hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) {
1303             const char *virname_w = NULL;
1304             int found             = 0;
1305 
1306             /* If no hash, skip to next type */
1307             if (!compute_hash[hashtype])
1308                 continue;
1309 
1310             /* Do hash scan */
1311             if ((ret = cli_hm_scan(digest[hashtype], ctx->fmap->len, &virname, hdb, hashtype)) == CL_VIRUS) {
1312                 found += 1;
1313             }
1314             if (!found || SCAN_ALLMATCHES) {
1315                 if ((ret = cli_hm_scan_wild(digest[hashtype], &virname_w, hdb, hashtype)) == CL_VIRUS)
1316                     found += 2;
1317             }
1318 
1319             /* If found, do immediate hash-only FP check */
1320             if (found && fp) {
1321                 for (hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) {
1322                     if (!compute_hash[hashtype2])
1323                         continue;
1324                     if (cli_hm_scan(digest[hashtype2], ctx->fmap->len, NULL, fp, hashtype2) == CL_VIRUS) {
1325                         found = 0;
1326                         ret   = CL_CLEAN;
1327                         break;
1328                     } else if (cli_hm_scan_wild(digest[hashtype2], NULL, fp, hashtype2) == CL_VIRUS) {
1329                         found = 0;
1330                         ret   = CL_CLEAN;
1331                         break;
1332                     }
1333                 }
1334             }
1335 
1336             /* If matched size-based hash ... */
1337             if (found % 2) {
1338                 viruses_found = 1;
1339                 ret           = cli_append_virus(ctx, virname);
1340                 if (!SCAN_ALLMATCHES || ret != CL_CLEAN)
1341                     break;
1342                 virname = NULL;
1343             }
1344             /* If matched size-agnostic hash ... */
1345             if (found > 1) {
1346                 viruses_found = 1;
1347                 ret           = cli_append_virus(ctx, virname_w);
1348                 if (!SCAN_ALLMATCHES || ret != CL_CLEAN)
1349                     break;
1350             }
1351         }
1352     }
1353 
1354     cl_hash_destroy(md5ctx);
1355     cl_hash_destroy(sha1ctx);
1356     cl_hash_destroy(sha256ctx);
1357 
1358     if (troot) {
1359         if (ret != CL_VIRUS || SCAN_ALLMATCHES)
1360             ret = cli_exp_eval(ctx, troot, &tdata, &info, (const char *)refhash);
1361         if (ret == CL_VIRUS)
1362             viruses_found++;
1363 
1364         cli_ac_freedata(&tdata);
1365         if (bm_offmode)
1366             cli_bm_freeoff(&toff);
1367         cli_pcre_freeoff(&tpoff);
1368     }
1369 
1370     if (groot) {
1371         if (ret != CL_VIRUS || SCAN_ALLMATCHES)
1372             ret = cli_exp_eval(ctx, groot, &gdata, &info, (const char *)refhash);
1373         cli_ac_freedata(&gdata);
1374         cli_pcre_freeoff(&gpoff);
1375     }
1376 
1377     cli_targetinfo_destroy(&info);
1378 
1379     if (SCAN_ALLMATCHES && viruses_found) {
1380         return CL_VIRUS;
1381     }
1382     if (ret == CL_VIRUS) {
1383         return CL_VIRUS;
1384     }
1385 
1386     return (acmode & AC_SCAN_FT) ? type : CL_CLEAN;
1387 }
1388 
1389 #define CDBRANGE(field, val)                                              \
1390     if (field[0] != CLI_OFF_ANY) {                                        \
1391         if (field[0] == field[1] && field[0] != val)                      \
1392             continue;                                                     \
1393         else if (field[0] != field[1] && ((field[0] && field[0] > val) || \
1394                                           (field[1] && field[1] < val)))  \
1395             continue;                                                     \
1396     }
1397 
cli_matchmeta(cli_ctx * ctx,const char * fname,size_t fsizec,size_t fsizer,int encrypted,unsigned int filepos,int res1,void * res2)1398 cl_error_t cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t fsizer, int encrypted, unsigned int filepos, int res1, void *res2)
1399 {
1400     const struct cli_cdb *cdb;
1401     unsigned int viruses_found = 0;
1402     cl_error_t ret             = CL_CLEAN;
1403 
1404     cli_dbgmsg("CDBNAME:%s:%llu:%s:%llu:%llu:%d:%u:%u:%p\n",
1405                cli_ftname(cli_recursion_stack_get_type(ctx, -1)), (long long unsigned)fsizec, fname, (long long unsigned)fsizec, (long long unsigned)fsizer,
1406                encrypted, filepos, res1, res2);
1407 
1408     if (ctx->engine && ctx->engine->cb_meta)
1409         if (ctx->engine->cb_meta(cli_ftname(cli_recursion_stack_get_type(ctx, -1)), fsizec, fname, fsizer, encrypted, filepos, ctx->cb_ctx) == CL_VIRUS) {
1410             cli_dbgmsg("inner file blocked by callback: %s\n", fname);
1411 
1412             ret = cli_append_virus(ctx, "Detected.By.Callback");
1413             viruses_found++;
1414             if (!SCAN_ALLMATCHES || ret != CL_CLEAN)
1415                 return ret;
1416         }
1417 
1418     if (!ctx->engine || !(cdb = ctx->engine->cdb))
1419         return CL_CLEAN;
1420 
1421     do {
1422         if (cdb->ctype != CL_TYPE_ANY && cdb->ctype != cli_recursion_stack_get_type(ctx, -1))
1423             continue;
1424 
1425         if (cdb->encrypted != 2 && cdb->encrypted != encrypted)
1426             continue;
1427 
1428         if (cdb->res1 && (cdb->ctype == CL_TYPE_ZIP || cdb->ctype == CL_TYPE_RAR) && cdb->res1 != res1)
1429             continue;
1430 
1431         CDBRANGE(cdb->csize, cli_recursion_stack_get_size(ctx, -1));
1432         CDBRANGE(cdb->fsizec, fsizec);
1433         CDBRANGE(cdb->fsizer, fsizer);
1434         CDBRANGE(cdb->filepos, filepos);
1435 
1436         if (cdb->name.re_magic && (!fname || cli_regexec(&cdb->name, fname, 0, NULL, 0) == REG_NOMATCH))
1437             continue;
1438 
1439         ret = cli_append_virus(ctx, cdb->virname);
1440         viruses_found++;
1441         if (!SCAN_ALLMATCHES || ret != CL_CLEAN)
1442             return ret;
1443 
1444     } while ((cdb = cdb->next));
1445 
1446     if (SCAN_ALLMATCHES && viruses_found)
1447         return CL_VIRUS;
1448     return CL_CLEAN;
1449 }
1450