clambc/bcrun.c
d6ada03c
 /*
  *  ClamAV bytecode handler tool.
  *
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2009-2013 Sourcefire, Inc.
d6ada03c
  *
  *  Authors: Török Edvin
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
3ff75dbf
 #ifndef _WIN32
74b00233
 #include <sys/time.h>
3ff75dbf
 #endif
74b00233
 #include <stdlib.h>
b2e7c931
 
d6ada03c
 #include "bytecode.h"
65c740d7
 #include "bytecode_priv.h"
d6ada03c
 #include "clamav.h"
0d71db97
 #include "shared/optparser.h"
860c2737
 #include "shared/misc.h"
6eeadbfe
 #include "libclamav/dconf.h"
3de96271
 #include "libclamav/others.h"
d6ada03c
 
a1781898
 #include <fcntl.h>
d6ada03c
 #include <stdlib.h>
a1781898
 #include <errno.h>
646395bb
 #include <string.h>
3ff75dbf
 #ifdef HAVE_UNISTD_H
646395bb
 #include <unistd.h>
3ff75dbf
 #endif
d6ada03c
 
0d71db97
 static void help(void)
 {
     printf("\n");
e098cdc5
     printf("                       Clam AntiVirus: Bytecode Testing Tool %s\n", get_version());
964a1e73
     printf("           By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
e1cbc270
     printf("           (C) 2019 Cisco Systems, Inc.\n");
e098cdc5
     printf("\n");
     printf("    clambc <file> [function] [param1 ...]\n");
     printf("\n");
     printf("    --help                 -h         Show this help\n");
0d71db97
     printf("    --version              -V         Show version\n");
8d170d58
     printf("    --debug                           Show debug\n");
     printf("    --force-interpreter    -f         Force using the interpreter instead of the JIT\n");
     printf("    --trust-bytecode       -t         Trust loaded bytecode (default yes)\n");
d4c9f766
     printf("    --info                 -i         Print information about bytecode\n");
     printf("    --printsrc             -p         Print bytecode source\n");
8d170d58
     printf("    --printbcir            -c         Print IR of bytecode signature\n");
e098cdc5
     printf("    --input                -c         Input file to run the bytecode on\n");
1a7ce1e1
     printf("    --trace <level>        -T         Set bytecode trace level 0..7 (default 7)\n");
8d170d58
     printf("    --no-trace-showsource  -s         Don't show source line during tracing\n");
c94a95b8
     printf("    --statistics=bytecode             Collect and print bytecode execution statistics\n");
e098cdc5
     printf("    file                              File to test\n");
0d71db97
     printf("\n");
     return;
 }
 
0a11015b
 static struct dbg_state {
     const char *directory;
     const char *file;
     const char *scope;
     uint32_t scopeid;
     unsigned line;
     unsigned col;
     unsigned showline;
 } dbg_state;
 
 static void tracehook(struct cli_bc_ctx *ctx, unsigned event)
 {
     dbg_state.directory = ctx->directory;
     if (*ctx->file == '?')
288057e9
         return;
0a11015b
     switch (event) {
288057e9
         case trace_func:
             fprintf(stderr, "[trace] %s:%u:%u -> %s:%u:%u Entered function %s\n",
                     dbg_state.file, dbg_state.line, dbg_state.col,
                     ctx->file, ctx->line, ctx->col, ctx->scope);
             dbg_state.scope = ctx->scope;
             break;
         case trace_param:
             fprintf(stderr, "[trace] function parameter:\n");
             return;
         case trace_scope:
             fprintf(stderr, "[trace] %s:%u:%u -> %s:%u:%u\n",
                     dbg_state.file, dbg_state.line, dbg_state.col,
                     ctx->file, ctx->line, ctx->col);
             dbg_state.scope = ctx->scope;
             break;
         case trace_line:
         case trace_col:
             if (dbg_state.showline)
                 cli_bytecode_debug_printsrc(ctx);
             else
                 fprintf(stderr, "[trace] %s:%u:%u\n",
                         dbg_state.file, dbg_state.line, dbg_state.col);
             break;
         default:
             break;
0a11015b
     }
     dbg_state.file = ctx->file;
     dbg_state.line = ctx->line;
288057e9
     dbg_state.col  = ctx->col;
0a11015b
 }
 
 static void tracehook_op(struct cli_bc_ctx *ctx, const char *op)
 {
6df13d04
     UNUSEDPARAM(ctx);
1e96c19e
     fprintf(stderr, "[trace] %s\n", op);
0a11015b
 }
 
 static void tracehook_val(struct cli_bc_ctx *ctx, const char *name, uint32_t value)
 {
6df13d04
     UNUSEDPARAM(ctx);
1e96c19e
     fprintf(stderr, "[trace] %s = %u\n", name, value);
0a11015b
 }
 
688799d1
 static void tracehook_ptr(struct cli_bc_ctx *ctx, const void *ptr)
 {
6df13d04
     UNUSEDPARAM(ctx);
688799d1
     fprintf(stderr, "[trace] %p\n", ptr);
 }
 
a46e16af
 static uint8_t debug_flag = 0;
9bdaf35d
 static void print_src(const char *file)
 {
288057e9
     char buf[4096];
     int nread, i, found = 0, lcnt = 0;
     FILE *f = fopen(file, "r");
     if (!f) {
         fprintf(stderr, "Unable to reopen %s\n", file);
         return;
9bdaf35d
     }
288057e9
     do {
         nread = fread(buf, 1, sizeof(buf), f);
         for (i = 0; i < nread - 1; i++) {
             if (buf[i] == '\n') {
                 lcnt++;
             }
             /* skip over the logical trigger */
             if (lcnt >= 2 && buf[i] == '\n' && buf[i + 1] == 'S') {
                 found = 1;
                 i += 2;
                 break;
             }
         }
     } while (!found && (nread == sizeof(buf)));
     if (debug_flag)
         printf("[clambc] Source code:");
     do {
         for (; i + 1 < nread; i++) {
             if (buf[i] == 'S' || buf[i] == '\n') {
                 putc('\n', stdout);
                 continue;
             }
             putc(((buf[i] & 0xf) | ((buf[i + 1] & 0xf) << 4)), stdout);
             i++;
         }
         if (i == nread - 1 && nread != 1)
             fseek(f, -1, SEEK_CUR);
         i     = 0;
         nread = fread(buf, 1, sizeof(buf), f);
     } while (nread > 0);
     fclose(f);
9bdaf35d
 }
74f5816c
 static uint32_t deadbeefcounts[64] = {
     0xdeadbeef,
     0,
     0xbeefdead,
     0,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
     0xdeadbeef,
 };
d6ada03c
 int main(int argc, char *argv[])
 {
     FILE *f;
     struct cli_bc *bc;
     struct cli_bc_ctx *ctx;
288057e9
     int rc, dbgargc, bc_stats = 0;
0d71db97
     struct optstruct *opts;
a1781898
     const struct optstruct *opt;
288057e9
     unsigned funcid = 0, i;
d1487222
     struct cli_all_bc bcs;
e4a0f2c9
     int fd = -1;
acb53e3a
     unsigned tracelevel;
0d71db97
 
288057e9
     if (check_flevel())
         exit(1);
bca0b679
 
0d71db97
     opts = optparse(NULL, argc, argv, 1, OPT_CLAMBC, 0, NULL);
     if (!opts) {
288057e9
         fprintf(stderr, "ERROR: Can't parse command line options\n");
         exit(1);
d6ada03c
     }
288057e9
     if (optget(opts, "version")->enabled) {
         printf("Clam AntiVirus Bytecode Testing Tool %s\n", get_version());
         cl_init(CL_INIT_DEFAULT);
         cli_bytecode_printversion();
         optfree(opts);
         exit(0);
0d71db97
     }
288057e9
     if (optget(opts, "help")->enabled || !opts->filename) {
         optfree(opts);
         help();
         exit(0);
0d71db97
     }
e0c4fd85
     f = fopen(opts->filename[0], "r");
d6ada03c
     if (!f) {
288057e9
         fprintf(stderr, "Unable to load %s\n", argv[1]);
         optfree(opts);
         exit(2);
d6ada03c
     }
 
     bc = malloc(sizeof(*bc));
     if (!bc) {
288057e9
         fprintf(stderr, "Out of memory\n");
         optfree(opts);
         exit(3);
d6ada03c
     }
 
288057e9
     if (optget(opts, "debug")->enabled) {
         cl_debug();
         debug_flag = 1;
d4c9f766
     }
d1487222
     rc = cl_init(CL_INIT_DEFAULT);
     if (rc != CL_SUCCESS) {
288057e9
         fprintf(stderr, "Unable to init libclamav: %s\n", cl_strerror(rc));
         optfree(opts);
         exit(4);
d1487222
     }
 
288057e9
     dbgargc = 1;
3b33bd68
     while (opts->filename[dbgargc]) dbgargc++;
 
     if (dbgargc > 1)
288057e9
         cli_bytecode_debug(dbgargc, opts->filename);
1a486dca
 
     if (optget(opts, "force-interpreter")->enabled) {
288057e9
         bcs.engine = NULL;
1a486dca
     } else {
288057e9
         rc = cli_bytecode_init(&bcs);
         if (rc != CL_SUCCESS) {
             fprintf(stderr, "Unable to init bytecode engine: %s\n", cl_strerror(rc));
             optfree(opts);
             exit(4);
         }
d1487222
     }
 
     bcs.all_bcs = bc;
288057e9
     bcs.count   = 1;
d1487222
 
288057e9
     if ((opt = optget(opts, "statistics"))->enabled) {
         while (opt) {
             if (!strcasecmp(opt->strarg, "bytecode"))
                 bc_stats = 1;
             opt = opt->nextarg;
e77937b4
         }
     }
 
     rc = cli_bytecode_load(bc, f, NULL, optget(opts, "trust-bytecode")->enabled, bc_stats);
d6ada03c
     if (rc != CL_SUCCESS) {
288057e9
         fprintf(stderr, "Unable to load bytecode: %s\n", cl_strerror(rc));
         optfree(opts);
         exit(4);
d6ada03c
     }
     fclose(f);
3735fda1
     if (bc->state == bc_skip) {
288057e9
         fprintf(stderr, "bytecode load skipped\n");
         exit(0);
3735fda1
     }
a46e16af
     if (debug_flag)
288057e9
         printf("[clambc] Bytecode loaded\n");
d4c9f766
     if (optget(opts, "info")->enabled) {
288057e9
         cli_bytecode_describe(bc);
d4c9f766
     } else if (optget(opts, "printsrc")->enabled) {
9bdaf35d
         print_src(opts->filename[0]);
0ff13b31
     } else if (optget(opts, "printbcir")->enabled) {
3d061071
         unsigned i;
0ff13b31
         cli_bytetype_describe(bc);
3d061071
         for (i = 0; i < bc->num_func; ++i) {
985d96aa
             printf("########################################################################\n"
                    "####################### Function id %3u ################################\n"
694e7882
                    "########################################################################\n",
                    i);
985d96aa
             cli_bytevalue_describe(bc, i);
             cli_bytefunc_describe(bc, i);
         }
a35cfe51
     } else {
288057e9
         cli_ctx cctx;
         struct cl_engine *engine = cl_engine_new();
         fmap_t *map              = NULL;
         memset(&cctx, 0, sizeof(cctx));
         if (!engine) {
             fprintf(stderr, "Unable to create engine\n");
             optfree(opts);
             exit(3);
         }
         rc = cl_engine_compile(engine);
         if (rc) {
             fprintf(stderr, "Unable to compile engine: %s\n", cl_strerror(rc));
             optfree(opts);
             exit(4);
         }
         rc = cli_bytecode_prepare2(engine, &bcs, BYTECODE_ENGINE_MASK);
         if (rc != CL_SUCCESS) {
             fprintf(stderr, "Unable to prepare bytecode: %s\n", cl_strerror(rc));
             optfree(opts);
             exit(4);
         }
         if (debug_flag)
             printf("[clambc] Bytecode prepared\n");
d6ada03c
 
288057e9
         ctx = cli_bytecode_context_alloc();
         if (!ctx) {
             fprintf(stderr, "Out of memory\n");
             exit(3);
         }
         ctx->ctx    = &cctx;
         cctx.engine = engine;
         cctx.fmap   = cli_calloc(sizeof(fmap_t *), engine->maxreclevel + 2);
         if (!cctx.fmap) {
             fprintf(stderr, "Out of memory\n");
             exit(3);
         }
         memset(&dbg_state, 0, sizeof(dbg_state));
         dbg_state.file     = "<libclamav>";
         dbg_state.line     = 0;
         dbg_state.col      = 0;
         dbg_state.showline = !optget(opts, "no-trace-showsource")->enabled;
         tracelevel         = optget(opts, "trace")->numarg;
         cli_bytecode_context_set_trace(ctx, tracelevel,
                                        tracehook,
                                        tracehook_op,
                                        tracehook_val,
                                        tracehook_ptr);
e0c4fd85
 
288057e9
         if (opts->filename[1]) {
             funcid = atoi(opts->filename[1]);
         }
         cli_bytecode_context_setfuncid(ctx, bc, funcid);
         if (debug_flag)
             printf("[clambc] Running bytecode function :%u\n", funcid);
e0c4fd85
 
288057e9
         if (opts->filename[1]) {
             i = 2;
             while (opts->filename[i]) {
                 rc = cli_bytecode_context_setparam_int(ctx, i - 2, atoi(opts->filename[i]));
                 if (rc != CL_SUCCESS) {
                     fprintf(stderr, "Unable to set param %u: %s\n", i - 2, cl_strerror(rc));
                 }
                 i++;
             }
         }
a35cfe51
 
288057e9
         if ((opt = optget(opts, "input"))->enabled) {
             fd = open(opt->strarg, O_RDONLY);
             if (fd == -1) {
                 fprintf(stderr, "Unable to open input file %s: %s\n", opt->strarg, strerror(errno));
                 optfree(opts);
                 exit(5);
             }
             map = fmap(fd, 0, 0);
             if (!map) {
                 fprintf(stderr, "Unable to map input file %s\n", opt->strarg);
                 exit(5);
             }
             rc = cli_bytecode_context_setfile(ctx, map);
             if (rc != CL_SUCCESS) {
                 fprintf(stderr, "Unable to set file %s: %s\n", opt->strarg, cl_strerror(rc));
                 optfree(opts);
                 exit(5);
             }
         }
         /* for testing */
         ctx->hooks.match_counts  = deadbeefcounts;
         ctx->hooks.match_offsets = deadbeefcounts;
         rc                       = cli_bytecode_run(&bcs, bc, ctx);
         if (rc != CL_SUCCESS) {
             fprintf(stderr, "Unable to run bytecode: %s\n", cl_strerror(rc));
         } else {
             uint64_t v;
             if (debug_flag)
                 printf("[clambc] Bytecode run finished\n");
             v = cli_bytecode_context_getresult_int(ctx);
             if (debug_flag)
                 printf("[clambc] Bytecode returned: 0x%llx\n", (long long)v);
         }
         cli_bytecode_context_destroy(ctx);
         if (map)
             funmap(map);
         cl_engine_free(engine);
         free(cctx.fmap);
e0c4fd85
     }
d6ada03c
     cli_bytecode_destroy(bc);
d1487222
     cli_bytecode_done(&bcs);
d6ada03c
     free(bc);
2e0a9ae6
     optfree(opts);
a1781898
     if (fd != -1)
288057e9
         close(fd);
a46e16af
     if (debug_flag)
288057e9
         printf("[clambc] Exiting\n");
694e7882
 
d6ada03c
     return 0;
 }