Browse code

bcomp - revamping option parsing; adding binary byte extraction; adding exact byte length matching option

Mickey Sola authored on 2018/09/27 05:40:28
Showing 2 changed files
... ...
@@ -25,6 +25,8 @@
25 25
 #include "clamav-config.h"
26 26
 #endif
27 27
 
28
+#include <errno.h>
29
+
28 30
 #include "clamav.h"
29 31
 #include "cltypes.h"
30 32
 #include "others.h"
... ...
@@ -67,8 +69,9 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons
67 67
     size_t toks = 0;
68 68
     int16_t ref_subsigid = -1;
69 69
     int64_t offset_param = 0;
70
+    int64_t ret = CL_SUCCESS;
70 71
     size_t byte_length = 0;
71
-    uint32_t comp_val = 0;
72
+    uint64_t comp_val = 0;
72 73
     char *hexcpy = NULL;
73 74
 
74 75
     if (!hexsig || !(*hexsig) || !root || !virname) {
... ...
@@ -210,31 +213,64 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons
210 210
     /* the byte length indicator options are stored in a bitmask--by design each option gets its own nibble */
211 211
     buf_start = tokens[1];
212 212
 
213
-    switch (*buf_start) {
214
-        case 'h': bcomp->options |= CLI_BCOMP_HEX;    break;
215
-        case 'd': bcomp->options |= CLI_BCOMP_DEC;    break;
216
-
217
-        default:
218
-            cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), hex/decimal byte length indicator was found invalid\n", tokens[0], tokens[1], tokens[2]);
219
-            free(buf);
220
-            cli_bcomp_freemeta(root, bcomp);
221
-            return CL_EMALFDB;
222
-    }
213
+    while (!isdigit(*buf_start)) {
214
+
215
+        switch (*buf_start) {
216
+            case 'h':
217
+                /* hex, decimal, and binary options are mutually exclusive parameters */
218
+                if (bcomp->options & CLI_BCOMP_DEC || bcomp->options & CLI_BCOMP_BIN) {
219
+                    ret = CL_EMALFDB;
220
+                } else {
221
+                    bcomp->options |= CLI_BCOMP_HEX;
222
+                } break;
223
+            case 'd':
224
+                /* hex, decimal, and binary options are mutually exclusive parameters */
225
+                if (bcomp->options & CLI_BCOMP_HEX || bcomp->options & CLI_BCOMP_BIN) {
226
+                    ret = CL_EMALFDB;
227
+                } else {
228
+                    bcomp->options |= CLI_BCOMP_DEC;
229
+                } break;
230
+            case 'i':
231
+                /* hex, decimal, and binary options are mutually exclusive parameters */
232
+                if (bcomp->options & CLI_BCOMP_HEX || bcomp->options & CLI_BCOMP_DEC) {
233
+                    ret = CL_EMALFDB;
234
+                } else {
235
+                    bcomp->options |= CLI_BCOMP_BIN;
236
+                } break;
237
+            case 'l':
238
+                /* little and big endian options are mutually exclusive parameters */
239
+                if (bcomp->options & CLI_BCOMP_BE) {
240
+                    ret = CL_EMALFDB;
241
+                } else {
242
+                    bcomp->options |= CLI_BCOMP_LE;
243
+                } break;
244
+            case 'b':
245
+                /* little and big endian options are mutually exclusive parameters */
246
+                if (bcomp->options & CLI_BCOMP_LE) {
247
+                    ret = CL_EMALFDB;
248
+                } else {
249
+                    bcomp->options |= CLI_BCOMP_BE;
250
+                } break;
251
+            case 'e':
252
+                /* for exact byte length matches */
253
+                bcomp->options |= CLI_BCOMP_EXACT;
254
+                break;
223 255
 
224
-    buf_start++;
225
-    switch (*buf_start) {
226
-        case 'l': bcomp->options |= CLI_BCOMP_LE;    break;
227
-        case 'b': bcomp->options |= CLI_BCOMP_BE;    break;
256
+            default:
257
+                ret = CL_EMALFDB;
258
+                break;
259
+        }
228 260
 
229
-        default:
230
-            cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), little/big endian byte length indicator was invalid\n", tokens[0], tokens[1], tokens[2]);
261
+        if (CL_EMALFDB == ret) {
262
+            cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), option parameter was found invalid\n", tokens[0], tokens[1], tokens[2]);
231 263
             free(buf);
232 264
             cli_bcomp_freemeta(root, bcomp);
233
-            return CL_EMALFDB;
265
+            return ret;
266
+        }
267
+        buf_start++;
234 268
     }
235 269
 
236 270
     /* parse out the byte length parameter */
237
-    buf_start++;
238 271
     buf_end = NULL;
239 272
     byte_length = strtol(buf_start, (char **) &buf_end, 0);
240 273
     if (buf_end && buf_end+1 != tokens[2]) {
... ...
@@ -244,6 +280,13 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons
244 244
         return CL_EMALFDB;
245 245
     }
246 246
 
247
+    if (bcomp->options | CLI_BCOMP_BIN && byte_length > CLI_BCOMP_MAX_BIN_BLEN) {
248
+        cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), byte length for binary extraction was too long (max size (%d))\n", tokens[0], tokens[1], tokens[3], CLI_BCOMP_MAX_BIN_BLEN);
249
+        free(buf);
250
+        cli_bcomp_freemeta(root, bcomp);
251
+        return CL_EMALFDB;
252
+    }
253
+
247 254
     bcomp->byte_len = byte_length;
248 255
 
249 256
     /* currently only >, <, and = are supported comparison symbols--this makes parsing very simple */
... ...
@@ -262,10 +305,10 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons
262 262
     }
263 263
 
264 264
 
265
-    /* no more tokens after this, so we take advantage of strtol and check if the buf_end is null terminated or not */
265
+    /* no more tokens after this, so we take advantage of strtoll and check if the buf_end is null terminated or not */
266 266
     buf_start++;
267 267
     buf_end = NULL;
268
-    comp_val = strtol(buf_start, (char **) &buf_end, 0);
268
+    comp_val = strtoll(buf_start, (char **) &buf_end, 0);
269 269
     if (*buf_end) {
270 270
         cli_errmsg("cli_bcomp_addpatt: while parsing (%s#%s#%s), comparison value contained invalid input\n", tokens[0], tokens[1], tokens[2]);
271 271
         free(buf);
... ...
@@ -276,12 +319,13 @@ cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, cons
276 276
     bcomp->comp_value = comp_val;
277 277
 
278 278
     /* manually verify successful pattern parsing */
279
-    bcm_dbgmsg("Matcher Byte Compare: (%s%ld#%c%c%zu#%c%d)\n",
279
+    bcm_dbgmsg("Matcher Byte Compare: (%s%ld#%c%c%s%zu#%c%lu)\n",
280 280
                     bcomp->offset ==  0 ? "" : 
281 281
                     (bcomp->offset < 0 ? "<<" : ">>"),
282 282
                     bcomp->offset,
283
-                    bcomp->options & CLI_BCOMP_HEX ? 'h' : 'd',
283
+                    bcomp->options & CLI_BCOMP_HEX ? 'h' : (bcomp->options & CLI_BCOMP_DEC ? 'd' : 'i'),
284 284
                     bcomp->options & CLI_BCOMP_LE ? 'l' : 'b',
285
+                    bcomp->options & CLI_BCOMP_EXACT ? "e" : "",
285 286
                     bcomp->byte_len,
286 287
                     bcomp->comp_symbol,
287 288
                     bcomp->comp_value);
... ...
@@ -409,10 +453,11 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met
409 409
 
410 410
     uint32_t byte_len = 0;
411 411
     uint32_t length = 0;
412
+    uint16_t opt = 0;
412 413
     const unsigned char *buffer = NULL;
413 414
     unsigned char *conversion_buf = NULL;
414
-    char opt = (char) bm->options;
415
-    uint32_t value = 0;
415
+    uint64_t value = 0;
416
+    uint64_t *bin_value = NULL;
416 417
     const unsigned char* end_buf = NULL;
417 418
 
418 419
     if (!map || !bm) {
... ...
@@ -422,6 +467,7 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met
422 422
 
423 423
     byte_len = bm->byte_len;
424 424
     length = map->len;
425
+    opt = bm->options;
425 426
 
426 427
     /* ensure we won't run off the end of the file buffer */
427 428
     if (bm->offset > 0) {
... ...
@@ -443,57 +489,110 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met
443 443
         bcm_dbgmsg("bcmp_compare_check: could not extract bytes from buffer offset\n");
444 444
         return CL_EMEM;
445 445
     }
446
-    bcm_dbgmsg("bcmp_compare_check: literal extracted bytes before comparison (%s)\n", buffer);
446
+    bcm_dbgmsg("bcmp_compare_check: literal extracted bytes before comparison %s\n", buffer);
447 447
 
448
-    /* handle byte length options to convert the string appropriately */
449
-    switch(opt) {
448
+    /* grab the first byte to handle byte length options to convert the string appropriately */
449
+    switch((opt & 0x00FF)) {
450 450
         /*hl*/
451 451
         case CLI_BCOMP_HEX | CLI_BCOMP_LE:
452
+            errno = 0;
452 453
             value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 16);
453
-            if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) {
454
+            if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) {
455
+
454 456
                 bcm_dbgmsg("bcmp_compare_check: little endian hex conversion unsuccessful\n");
455 457
                 return CL_CLEAN;
456 458
             }
459
+            /*hle*/
460
+            if (opt & CLI_BCOMP_EXACT) {
461
+                if (buffer+byte_len != end_buf) {
462
+
463
+                    bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n");
464
+                    return CL_CLEAN;
465
+                }
466
+            }
457 467
 
458
-            value = le32_to_host(value);
468
+            value = le64_to_host(value);
459 469
             break;
460 470
 
461 471
         /*hb*/  
462 472
         case CLI_BCOMP_HEX | CLI_BCOMP_BE:
463 473
             value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 16);
464
-            if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) {
474
+            if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) {
465 475
 
466 476
                 bcm_dbgmsg("bcmp_compare_check: big endian hex conversion unsuccessful\n");
467 477
                 return CL_CLEAN;
468 478
             }
479
+            /*hbe*/
480
+            if (opt & CLI_BCOMP_EXACT) {
481
+                if (buffer+byte_len != end_buf) {
469 482
 
470
-            value = be32_to_host(value);
483
+                    bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n");
484
+                    return CL_CLEAN;
485
+                }
486
+            }
487
+
488
+            value = be64_to_host(value);
471 489
             break;
472 490
 
473 491
         /*dl*/
474 492
         case CLI_BCOMP_DEC | CLI_BCOMP_LE:
475 493
             value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 10);
476
-            if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) {
494
+            if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) {
477 495
 
478 496
                 bcm_dbgmsg("bcmp_compare_check: little endian decimal conversion unsuccessful\n");
479 497
                 return CL_CLEAN;
480 498
             }
499
+            /*dle*/
500
+            if (opt & CLI_BCOMP_EXACT) {
501
+                if (buffer+byte_len != end_buf) {
502
+
503
+                    bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n");
504
+                    return CL_CLEAN;
505
+                }
506
+            }
481 507
 
482
-            value = le32_to_host(value);
508
+            value = le64_to_host(value);
483 509
             break;
484 510
 
485 511
         /*db*/
486 512
         case CLI_BCOMP_DEC | CLI_BCOMP_BE:
487 513
             value = cli_strntoul((char*) buffer, byte_len, (char**) &end_buf, 10);
488
-            if (value < 0 || NULL == end_buf || buffer+byte_len != end_buf) {
514
+            if ((((value == LONG_MAX) || (value == LONG_MIN)) && errno == ERANGE) || NULL == end_buf) {
489 515
 
490 516
                 bcm_dbgmsg("bcmp_compare_check: big endian decimal conversion unsuccessful\n");
491 517
                 return CL_CLEAN;
492 518
             }
519
+            /*dbe*/
520
+            if (opt & CLI_BCOMP_EXACT) {
521
+                if (buffer+byte_len != end_buf) {
522
+
523
+                    bcm_dbgmsg("bcmp_compare_check: couldn't extract the exact number of requested bytes\n");
524
+                    return CL_CLEAN;
525
+                }
526
+            }
493 527
 
494
-            value = be32_to_host(value);
528
+            value = be64_to_host(value);
495 529
             break;
530
+        case CLI_BCOMP_BIN | CLI_BCOMP_LE:
531
+            /* dropping bin_value on the heap to ensure our local stack is isolated from raw user input */
532
+            bin_value = cli_calloc(1, sizeof(uint64_t));
496 533
 
534
+            /* copy, then shift to align */
535
+            memcpy(bin_value, buffer, byte_len);
536
+            *bin_value = *bin_value >> CLI_BCOMP_MAX_BIN_BLEN - byte_len;
537
+
538
+            value = le64_to_host(*bin_value);
539
+            break;
540
+        case CLI_BCOMP_BIN | CLI_BCOMP_BE:
541
+            /* dropping bin_value on the heap to ensure our local stack is isolated from raw user input */
542
+            bin_value = cli_calloc(1, sizeof(uint64_t));
543
+
544
+            /* copy, then shift to align */
545
+            memcpy(bin_value, buffer, byte_len);
546
+            *bin_value = *bin_value >> CLI_BCOMP_MAX_BIN_BLEN - byte_len;
547
+
548
+            value = be64_to_host(*bin_value);
549
+            break;
497 550
         default:
498 551
             return CL_ENULLARG;
499 552
     }
... ...
@@ -503,21 +602,21 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met
503 503
 
504 504
         case '>':
505 505
             if (value > bm->comp_value) {
506
-                bcm_dbgmsg("bcmp_compare_check: extracted value (%u) greater than comparison value (%u)\n", value, bm->comp_value);
506
+                bcm_dbgmsg("bcmp_compare_check: extracted value (%lu) greater than comparison value (%lu)\n", value, bm->comp_value);
507 507
                 return CL_VIRUS;
508 508
             }
509 509
             break;
510 510
 
511 511
         case '<':
512 512
             if (value < bm->comp_value) {
513
-                bcm_dbgmsg("bcmp_compare_check: extracted value (%u) less than comparison value (%u)\n", value, bm->comp_value);
513
+                bcm_dbgmsg("bcmp_compare_check: extracted value (%lu) less than comparison value (%lu)\n", value, bm->comp_value);
514 514
                 return CL_VIRUS;
515 515
             }
516 516
             break;
517 517
 
518 518
         case '=':
519 519
             if (value == bm->comp_value) {
520
-                bcm_dbgmsg("bcmp_compare_check: extracted value (%u) equal to comparison value (%u)\n", value, bm->comp_value);
520
+                bcm_dbgmsg("bcmp_compare_check: extracted value (%lu) equal to comparison value (%lu)\n", value, bm->comp_value);
521 521
                 return CL_VIRUS;
522 522
             }
523 523
             break;
... ...
@@ -528,7 +627,7 @@ cl_error_t cli_bcomp_compare_check(fmap_t *map, int offset, struct cli_bcomp_met
528 528
     }
529 529
 
530 530
     /* comparison was not successful */
531
-    bcm_dbgmsg("bcmp_compare_check: extracted value was not %c %u\n", bm->comp_symbol, bm->comp_value);
531
+    bcm_dbgmsg("bcmp_compare_check: extracted value was not %c %lu\n", bm->comp_symbol, bm->comp_value);
532 532
     return CL_CLEAN;
533 533
 }
534 534
 
... ...
@@ -34,10 +34,14 @@
34 34
 #include "dconf.h"
35 35
 #include "mpool.h"
36 36
 
37
-#define CLI_BCOMP_HEX 0x0001
38
-#define CLI_BCOMP_DEC 0x0002
39
-#define CLI_BCOMP_LE  0x0010 
40
-#define CLI_BCOMP_BE  0x0020
37
+#define CLI_BCOMP_MAX_BIN_BLEN 8
38
+
39
+#define CLI_BCOMP_HEX   0x0001
40
+#define CLI_BCOMP_DEC   0x0002
41
+#define CLI_BCOMP_BIN   0x0004
42
+#define CLI_BCOMP_LE    0x0010
43
+#define CLI_BCOMP_BE    0x0020
44
+#define CLI_BCOMP_EXACT 0x0100
41 45
 
42 46
 struct cli_bcomp_meta {
43 47
     char *virname;
... ...
@@ -47,7 +51,7 @@ struct cli_bcomp_meta {
47 47
     uint16_t options; /* bitmask */
48 48
     size_t byte_len;
49 49
     char comp_symbol; /* <, >, = are supported */
50
-    uint32_t comp_value;
50
+    uint64_t comp_value;
51 51
 };
52 52
 
53 53
 cl_error_t cli_bcomp_addpatt(struct cli_matcher *root, const char *virname, const char* hexsig, const uint32_t *lsigid, unsigned int options);