997a1efe |
/* |
7a9022c9 |
* Load, and verify ClamAV bytecode. |
997a1efe |
* |
c442ca9c |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
6a9086d2 |
* Copyright (C) 2009-2013 Sourcefire, Inc. |
997a1efe |
*
* 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
|
b2e7c931 |
#include <string.h> |
c074ecec |
#include <assert.h> |
b00a7cc8 |
#include <fcntl.h> |
b2e7c931 |
|
6eeadbfe |
#include "dconf.h" |
997a1efe |
#include "clamav.h"
#include "others.h" |
ab636570 |
#include "pe.h" |
997a1efe |
#include "bytecode.h" |
7a9022c9 |
#include "bytecode_priv.h" |
88d54dcb |
#include "bytecode_detect.h" |
997a1efe |
#include "readdb.h" |
d0934caf |
#include "scanners.h" |
3735fda1 |
#include "bytecode_api.h" |
9aa1d725 |
#include "bytecode_api_impl.h" |
0d4c9946 |
#include "builtin_bytecodes.h" |
c6a3b294 |
#if HAVE_JSON
#include "json.h"
#endif |
997a1efe |
|
54402320 |
#define MAX_BC 64
#define BC_EVENTS_PER_SIG 2
#define MAX_BC_SIGEVENT_ID MAX_BC*BC_EVENTS_PER_SIG
cli_events_t * g_sigevents = NULL;
unsigned int g_sigid;
|
74f5816c |
/* dummy values */
static const uint32_t nomatch[64] = {
0xdeadbeef, 0xdeaddead, 0xbeefdead, 0xdeaddead, 0xdeadbeef, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
}; |
1c4683ac |
static const uint32_t nooffsets[64] = {
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE,
CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE, CLI_OFF_NONE
}; |
74f5816c |
static const uint16_t nokind;
static const uint32_t nofilesize;
static const struct cli_pe_hook_data nopedata;
static void context_safe(struct cli_bc_ctx *ctx)
{
/* make sure these are never NULL */
if (!ctx->hooks.kind)
ctx->hooks.kind = &nokind;
if (!ctx->hooks.match_counts)
ctx->hooks.match_counts = nomatch; |
1c4683ac |
if (!ctx->hooks.match_offsets) |
8254e4ad |
ctx->hooks.match_offsets = nooffsets; |
74f5816c |
if (!ctx->hooks.filesize)
ctx->hooks.filesize = &nofilesize;
if (!ctx->hooks.pedata)
ctx->hooks.pedata = &nopedata;
}
|
7a7365ef |
static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx); |
7a9022c9 |
struct cli_bc_ctx *cli_bytecode_context_alloc(void)
{ |
d9fbce26 |
struct cli_bc_ctx *ctx = cli_calloc(1, sizeof(*ctx)); |
e522909e |
if (!ctx) {
cli_errmsg("Out of memory allocating cli_bytecode_context_reset\n");
return NULL;
} |
12876d3c |
ctx->bytecode_timeout = 60000; |
7a7365ef |
cli_bytecode_context_reset(ctx); |
7a9022c9 |
return ctx;
} |
997a1efe |
|
7a9022c9 |
void cli_bytecode_context_destroy(struct cli_bc_ctx *ctx)
{
cli_bytecode_context_clear(ctx);
free(ctx);
} |
997a1efe |
|
3ae0a76d |
int cli_bytecode_context_getresult_file(struct cli_bc_ctx *ctx, char **tempfilename)
{
int fd;
*tempfilename = ctx->tempfile;
fd = ctx->outfd;
ctx->tempfile = NULL; |
d9fbce26 |
ctx->outfd = 0; |
3ae0a76d |
return fd;
}
|
ab636570 |
/* resets bytecode state, so you can run another bytecode with same ctx */ |
d0934caf |
static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx) |
7a9022c9 |
{ |
778df8c2 |
unsigned i;
|
6922903a |
free(ctx->opsizes); |
778df8c2 |
ctx->opsizes = NULL;
|
7a9022c9 |
free(ctx->values); |
778df8c2 |
ctx->values = NULL;
|
7a9022c9 |
free(ctx->operands); |
53bd5bb1 |
ctx->operands = NULL; |
778df8c2 |
|
d9fbce26 |
if (ctx->outfd) { |
e5f2b2c5 |
cli_ctx *cctx = ctx->ctx; |
d9fbce26 |
if (ctx->outfd)
close(ctx->outfd); |
6cd36d95 |
if (ctx->tempfile && (!cctx || !cctx->engine->keeptmp)) {
cli_unlink(ctx->tempfile);
} |
3ae0a76d |
free(ctx->tempfile);
ctx->tempfile = NULL; |
d9fbce26 |
ctx->outfd = 0; |
3ae0a76d |
} |
b00a7cc8 |
if (ctx->jsnormdir) {
char fullname[1025];
cli_ctx *cctx = ctx->ctx;
int fd, ret = CL_CLEAN;
if (!ctx->found) {
snprintf(fullname, 1024, "%s"PATHSEP"javascript", ctx->jsnormdir);
fd = open(fullname, O_RDONLY|O_BINARY);
if(fd >= 0) { |
ffa9b060 |
ret = cli_scandesc(fd, cctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL); |
b00a7cc8 |
if (ret == CL_CLEAN) { |
96914546 |
if (lseek(fd, 0, SEEK_SET) == -1)
cli_dbgmsg("cli_bytecode: call to lseek() has failed\n");
else
ret = cli_scandesc(fd, cctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL); |
b00a7cc8 |
}
close(fd);
}
}
if (!cctx || !cctx->engine->keeptmp) {
cli_rmdirs(ctx->jsnormdir);
}
free(ctx->jsnormdir);
if (ret != CL_CLEAN)
ctx->found = 1;
} |
778df8c2 |
ctx->numParams = 0;
ctx->funcid = 0; |
35d6165f |
/* don't touch fmap, file_size, and hooks, sections, ctx, timeout, pdf* */ |
778df8c2 |
ctx->off = 0; |
d9fbce26 |
ctx->written = 0; |
b00a7cc8 |
ctx->jsnormwritten = 0; |
2d45ef06 |
#if USE_MPOOL
if (ctx->mpool) {
mpool_destroy(ctx->mpool);
ctx->mpool = NULL;
}
#else |
c0afc517 |
/*TODO: implement for no-mmap case too*/ |
2d45ef06 |
#endif |
778df8c2 |
for (i=0;i<ctx->ninflates;i++)
cli_bcapi_inflate_done(ctx, i);
free(ctx->inflates);
ctx->inflates = NULL;
ctx->ninflates = 0;
for (i=0;i<ctx->nbuffers;i++)
cli_bcapi_buffer_pipe_done(ctx, i);
free(ctx->buffers);
ctx->buffers = NULL;
ctx->nbuffers = 0;
for (i=0;i<ctx->nhashsets;i++)
cli_bcapi_hashset_done(ctx, i);
free(ctx->hashsets);
ctx->hashsets = NULL;
ctx->nhashsets = 0; |
b00a7cc8 |
for (i=0;i<ctx->njsnorms;i++)
cli_bcapi_jsnorm_done(ctx, i);
free(ctx->jsnorms);
ctx->jsnorms = NULL;
ctx->njsnorms = 0;
ctx->jsnormdir = NULL; |
7a7365ef |
for (i=0;i<ctx->nmaps;i++)
cli_bcapi_map_done(ctx, i);
free(ctx->maps);
ctx->maps = NULL;
ctx->nmaps = 0;
|
f3575db2 |
#if HAVE_JSON |
3107a6c2 |
free((json_object**)(ctx->jsonobjs)); |
f3575db2 |
ctx->jsonobjs = NULL;
ctx->njsonobjs = 0;
#endif
|
7a7365ef |
ctx->containertype = CL_TYPE_ANY; |
ab636570 |
return CL_SUCCESS;
}
int cli_bytecode_context_clear(struct cli_bc_ctx *ctx)
{
cli_bytecode_context_reset(ctx); |
f6798708 |
memset(ctx, 0, sizeof(*ctx)); |
957bf651 |
return CL_SUCCESS; |
7a9022c9 |
} |
997a1efe |
|
6922903a |
static unsigned typesize(const struct cli_bc *bc, uint16_t type)
{ |
669623d5 |
struct cli_bc_type *ty;
unsigned j;
|
a45e2577 |
type &= 0x7fff; |
6922903a |
if (!type)
return 0;
if (type <= 8)
return 1;
if (type <= 16)
return 2;
if (type <= 32)
return 4;
if (type <= 64)
return 8; |
669623d5 |
ty = &bc->types[type-65];
if (ty->size)
return ty->size;
switch (ty->kind) {
case 2:
case 3:
for (j=0;j<ty->numElements;j++)
ty->size += typesize(bc, ty->containedTypes[j]);
break;
case 4:
ty->size = ty->numElements * typesize(bc, ty->containedTypes[0]);
break;
default:
break;
}
if (!ty->size && ty->kind != DFunctionType) {
cli_warnmsg("type %d size is 0\n", type-65);
}
return ty->size; |
6922903a |
}
static unsigned typealign(const struct cli_bc *bc, uint16_t type)
{ |
a45e2577 |
type &= 0x7fff; |
7a14dc4c |
if (type <= 64) {
unsigned size = typesize(bc, type);
return size ? size : 1;
}
return bc->types[type-65].align; |
6922903a |
}
|
30188fcc |
int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid) |
7a9022c9 |
{ |
6922903a |
unsigned i, s=0; |
30188fcc |
const struct cli_bc_func *func; |
7a9022c9 |
if (funcid >= bc->num_func) {
cli_errmsg("bytecode: function ID doesn't exist: %u\n", funcid);
return CL_EARG;
} |
e0c4fd85 |
func = ctx->func = &bc->funcs[funcid]; |
7a9022c9 |
ctx->bc = bc;
ctx->numParams = func->numArgs;
ctx->funcid = funcid; |
e0c4fd85 |
if (func->numArgs) {
ctx->operands = cli_malloc(sizeof(*ctx->operands)*func->numArgs);
if (!ctx->operands) {
cli_errmsg("bytecode: error allocating memory for parameters\n");
return CL_EMEM;
} |
6922903a |
ctx->opsizes = cli_malloc(sizeof(*ctx->opsizes)*func->numArgs); |
cbb9db19 |
if (!ctx->opsizes) {
cli_errmsg("bytecode: error allocating memory for opsizes\n");
return CL_EMEM;
} |
6922903a |
for (i=0;i<func->numArgs;i++) {
unsigned al = typealign(bc, func->types[i]);
s = (s+al-1)&~(al-1);
ctx->operands[i] = s;
s += ctx->opsizes[i] = typesize(bc, func->types[i]);
} |
7a9022c9 |
} |
6922903a |
s += 8;/* return value */
ctx->bytes = s;
ctx->values = cli_malloc(s);
if (!ctx->values) {
cli_errmsg("bytecode: error allocating memory for parameters\n");
return CL_EMEM; |
7a9022c9 |
}
return CL_SUCCESS;
} |
997a1efe |
|
7a9022c9 |
static inline int type_isint(uint16_t type) |
997a1efe |
{ |
7a9022c9 |
return type > 0 && type <= 64; |
997a1efe |
}
|
7a9022c9 |
int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c) |
997a1efe |
{ |
7a9022c9 |
if (i >= ctx->numParams) {
cli_errmsg("bytecode: param index out of bounds: %u\n", i);
return CL_EARG;
}
if (!type_isint(ctx->func->types[i])) {
cli_errmsg("bytecode: parameter type mismatch\n");
return CL_EARG;
} |
6922903a |
switch (ctx->opsizes[i]) {
case 1:
ctx->values[ctx->operands[i]] = c;
break;
case 2:
*(uint16_t*)&ctx->values[ctx->operands[i]] = c;
break;
case 4:
*(uint32_t*)&ctx->values[ctx->operands[i]] = c;
break;
case 8:
*(uint64_t*)&ctx->values[ctx->operands[i]] = c;
break;
} |
7a9022c9 |
return CL_SUCCESS; |
997a1efe |
}
|
7a9022c9 |
int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen)
{ |
cd94be7a |
UNUSEDPARAM(ctx);
UNUSEDPARAM(i);
UNUSEDPARAM(data);
UNUSEDPARAM(datalen); |
7a9022c9 |
cli_errmsg("Pointer parameters are not implemented yet!\n");
return CL_EARG;
} |
997a1efe |
static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigned len, char *ok)
{
uint64_t n=0;
unsigned i, newoff, lim, p0 = p[*off], shift=0;
lim = p0 - 0x60;
if (lim > 0x10) {
cli_errmsg("Invalid number type: %c\n", p0);
*ok = 0;
return 0;
}
newoff = *off +lim+1;
if (newoff > len) {
cli_errmsg("End of line encountered while reading number\n");
*ok = 0;
return 0;
}
if (p0 == 0x60) {
*off = newoff;
return 0;
}
for (i=*off+1;i < newoff; i++) {
uint64_t v = p[i];
if (UNLIKELY((v&0xf0) != 0x60)) { |
d9432eea |
cli_errmsg("Invalid number part: %c\n", (char)v); |
997a1efe |
*ok = 0;
return 0;
}
v &= 0xf;
v <<= shift;
n |= v;
shift += 4;
}
*off = newoff;
return n;
}
|
3555ee75 |
static inline funcid_t readFuncID(struct cli_bc *bc, unsigned char *p,
unsigned *off, unsigned len, char *ok) |
997a1efe |
{ |
e0c4fd85 |
funcid_t id = readNumber(p, off, len, ok)-1;
if (*ok && id >= bc->num_func) { |
3555ee75 |
cli_errmsg("Called function out of range: %u >= %u\n", id, bc->num_func);
*ok = 0;
return ~0;
}
return id;
}
|
cf0cd429 |
static inline funcid_t readAPIFuncID(struct cli_bc *bc, unsigned char *p,
unsigned *off, unsigned len, char *ok)
{
funcid_t id = readNumber(p, off, len, ok)-1;
if (*ok && !cli_bitset_test(bc->uses_apis, id)) {
cli_errmsg("Called undeclared API function: %u\n", id);
*ok = 0;
return ~0;
}
return id;
}
|
997a1efe |
static inline unsigned readFixedNumber(const unsigned char *p, unsigned *off,
unsigned len, char *ok, unsigned width)
{
unsigned i, n=0, shift=0;
unsigned newoff = *off + width;
if (newoff > len) {
cli_errmsg("Newline encountered while reading number\n");
*ok = 0;
return 0;
}
for (i=*off;i<newoff;i++) {
unsigned v = p[i];
if (UNLIKELY((v&0xf0) != 0x60)) {
cli_errmsg("Invalid number part: %c\n", v);
*ok = 0;
return 0;
}
v &= 0xf;
v <<= shift;
n |= v;
shift += 4;
}
*off = newoff;
return n;
}
|
09bd9839 |
static inline operand_t readOperand(struct cli_bc_func *func, unsigned char *p,
unsigned *off, unsigned len, char *ok)
{
uint64_t v;
if ((p[*off]&0xf0) == 0x40 || p[*off] == 0x50) {
uint64_t *dest;
uint16_t ty;
p[*off] |= 0x20;
/* TODO: unique constants */
func->constants = cli_realloc2(func->constants, (func->numConstants+1)*sizeof(*func->constants));
if (!func->constants) {
*ok = 0;
return MAX_OP;
}
v = readNumber(p, off, len, ok);
dest = &func->constants[func->numConstants];
/* Write the constant to the correct place according to its type.
* This is needed on big-endian machines, because constants are always |
7cd9337a |
* read as u64, but accessed as one of these types: u8, u16, u32, u64 */ |
09bd9839 |
*dest= 0;
ty = 8*readFixedNumber(p, off, len, ok, 1);
if (!ty) { |
ec077929 |
/* This is a global variable */
return 0x80000000 | v; |
09bd9839 |
}
if (ty <= 8)
*(uint8_t*)dest = v;
else if (ty <= 16)
*(uint16_t*)dest = v;
else if (ty <= 32)
*(uint32_t*)dest = v;
else
*dest = v;
return func->numValues + func->numConstants++;
}
v = readNumber(p, off, len, ok);
if (!*ok)
return MAX_OP;
if (v >= func->numValues) {
cli_errmsg("Operand index exceeds bounds: %u >= %u!\n", (unsigned)v, (unsigned)func->numValues);
*ok = 0;
return MAX_OP;
}
return v;
}
|
e4a0f2c9 |
static inline char *readData(const unsigned char *p, unsigned *off, unsigned len, char *ok, unsigned *datalen) |
997a1efe |
{
unsigned char *dat, *q;
unsigned l, newoff, i;
if (p[*off] != '|') {
cli_errmsg("Data start marker missing: %c\n", p[*off]);
*ok = 0;
return NULL;
}
(*off)++;
l = readNumber(p, off, len, ok); |
53bd5bb1 |
if (!l || !ok) { |
8c0933ce |
*datalen = l; |
997a1efe |
return NULL; |
8c0933ce |
} |
cf0cd429 |
newoff = *off + 2*l; |
997a1efe |
if (newoff > len) {
cli_errmsg("Line ended while reading data\n");
*ok = 0;
return 0;
}
dat = cli_malloc(l);
if (!dat) {
cli_errmsg("Cannot allocate memory for data\n");
*ok = 0;
return NULL;
}
q = dat; |
cf0cd429 |
for (i=*off;i<newoff;i += 2) {
const unsigned char v0 = p[i];
const unsigned char v1 = p[i+1];
if (UNLIKELY((v0&0xf0) != 0x60 || (v1&0xf0) != 0x60)) {
cli_errmsg("Invalid data part: %c%c\n", v0, v1); |
997a1efe |
*ok = 0; |
bebd86a6 |
free(dat); |
997a1efe |
return 0;
} |
cf0cd429 |
*q++ = (v0&0xf) | ((v1&0xf) << 4); |
997a1efe |
}
*off = newoff; |
c3c97d4a |
*datalen = l; |
e4a0f2c9 |
return (char*)dat; |
997a1efe |
}
|
c3c97d4a |
static inline char *readString(const unsigned char *p, unsigned *off, unsigned len, char *ok)
{ |
c9a070c9 |
unsigned stringlen = 0; |
e4a0f2c9 |
char *str = readData(p, off, len, ok, &stringlen); |
c3c97d4a |
if (*ok && stringlen && str[stringlen-1] != '\0') { |
cf0cd429 |
str[stringlen-1] = '\0';
cli_errmsg("bytecode: string missing \\0 terminator: %s\n", str); |
c3c97d4a |
free(str);
*ok = 0;
return NULL;
}
return str;
} |
8cc286f5 |
|
482e97db |
static int parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linelength) |
997a1efe |
{
uint64_t magic1;
unsigned magic2;
char ok = 1;
unsigned offset, len, flevel; |
482e97db |
char *pos; |
3735fda1 |
|
30188fcc |
if (strncmp((const char*)buffer, BC_HEADER, sizeof(BC_HEADER)-1)) { |
997a1efe |
cli_errmsg("Missing file magic in bytecode");
return CL_EMALFDB;
}
offset = sizeof(BC_HEADER)-1; |
c3c97d4a |
len = strlen((const char*)buffer); |
3735fda1 |
bc->metadata.formatlevel = readNumber(buffer, &offset, len, &ok); |
997a1efe |
if (!ok) { |
3735fda1 |
cli_errmsg("Unable to parse (format) functionality level in bytecode header\n"); |
997a1efe |
return CL_EMALFDB;
} |
3735fda1 |
/* we support 2 bytecode formats */
if (bc->metadata.formatlevel != BC_FORMAT_096 &&
bc->metadata.formatlevel != BC_FORMAT_LEVEL) {
cli_dbgmsg("Skipping bytecode with (format) functionality level: %u (current %u)\n",
bc->metadata.formatlevel, BC_FORMAT_LEVEL); |
997a1efe |
return CL_BREAK;
} |
c0afc517 |
/* Optimistic parsing, check for error only at the end.*/ |
a35cfe51 |
bc->metadata.timestamp = readNumber(buffer, &offset, len, &ok);
bc->metadata.sigmaker = readString(buffer, &offset, len, &ok);
bc->metadata.targetExclude = readNumber(buffer, &offset, len, &ok); |
9fac78c7 |
bc->kind = readNumber(buffer, &offset, len, &ok); |
3735fda1 |
bc->metadata.minfunc = readNumber(buffer, &offset, len, &ok);
bc->metadata.maxfunc = readNumber(buffer, &offset, len, &ok);
flevel = cl_retflevel();
/* in 0.96 these 2 fields are unused / zero, in post 0.96 these mean
* min/max flevel.
* So 0 for min/max means no min/max
* Note that post 0.96 bytecode/bytecode lsig needs format 7, because
* 0.96 doesn't check lsig functionality level.
*/
if ((bc->metadata.minfunc && bc->metadata.minfunc > flevel) ||
(bc->metadata.maxfunc && bc->metadata.maxfunc < flevel)) {
cli_dbgmsg("Skipping bytecode with (engine) functionality level %u-%u (current %u)\n",
bc->metadata.minfunc, bc->metadata.maxfunc, flevel);
return CL_BREAK;
}
bc->metadata.maxresource = readNumber(buffer, &offset, len, &ok); |
a35cfe51 |
bc->metadata.compiler = readString(buffer, &offset, len, &ok); |
8cc286f5 |
bc->num_types = readNumber(buffer, &offset, len, &ok); |
997a1efe |
bc->num_func = readNumber(buffer, &offset, len, &ok); |
6922903a |
bc->state = bc_loaded; |
cf0cd429 |
bc->uses_apis = NULL; |
53bd5bb1 |
bc->dbgnodes = NULL;
bc->dbgnode_cnt = 0; |
997a1efe |
if (!ok) { |
c3c97d4a |
cli_errmsg("Invalid bytecode header at %u\n", offset); |
997a1efe |
return CL_EMALFDB;
}
magic1 = readNumber(buffer, &offset, len, &ok);
magic2 = readFixedNumber(buffer, &offset, len, &ok, 2);
if (!ok || magic1 != 0x53e5493e9f3d1c30ull || magic2 != 42) { |
d9432eea |
unsigned long m0 = magic1 >> 32;
unsigned long m1 = magic1; |
997a1efe |
cli_errmsg("Magic numbers don't match: %lx%lx, %u\n", m0, m1, magic2);
return CL_EMALFDB;
} |
482e97db |
if (buffer[offset] != ':') {
cli_errmsg("Expected : but found: %c\n", buffer[offset]);
return CL_EMALFDB;
}
offset++; |
e4a0f2c9 |
*linelength = strtol((const char*)buffer+offset, &pos, 10); |
c1aff5ec |
if (*pos != '\0') { |
482e97db |
cli_errmsg("Invalid number: %s\n", buffer+offset); |
997a1efe |
return CL_EMALFDB;
}
bc->funcs = cli_calloc(bc->num_func, sizeof(*bc->funcs));
if (!bc->funcs) {
cli_errmsg("Out of memory allocating %u functions\n", bc->num_func);
return CL_EMEM;
} |
8cc286f5 |
bc->types = cli_calloc(bc->num_types, sizeof(*bc->types));
if (!bc->types) {
cli_errmsg("Out of memory allocating %u types\n", bc->num_types);
return CL_EMEM;
}
return CL_SUCCESS;
}
|
e4a0f2c9 |
static int parseLSig(struct cli_bc *bc, char *buffer) |
dcee45cc |
{ |
ec41017b |
const char *prefix;
char *vnames, *vend = strchr(buffer, ';');
if (vend) {
bc->lsig = cli_strdup(buffer); |
a45e2577 |
*vend++ = '\0';
prefix = buffer;
vnames = strchr(vend, '{'); |
ec41017b |
} else {
/* Not a logical signature, but we still have a virusname */ |
16c4fcdd |
bc->hook_name = cli_strdup(buffer);
bc->lsig = NULL; |
ec41017b |
}
|
dcee45cc |
return CL_SUCCESS;
}
|
8cc286f5 |
static uint16_t readTypeID(struct cli_bc *bc, unsigned char *buffer,
unsigned *offset, unsigned len, char *ok)
{
uint64_t t = readNumber(buffer, offset, len, ok);
if (!ok) |
800a79e0 |
return ~0; |
8cc286f5 |
if (t >= bc->num_types + bc->start_tid) { |
800a79e0 |
cli_errmsg("Invalid type id: %llu\n", (unsigned long long)t); |
8cc286f5 |
*ok = 0; |
800a79e0 |
return ~0; |
8cc286f5 |
}
return t;
}
static void parseType(struct cli_bc *bc, struct cli_bc_type *ty,
unsigned char *buffer, unsigned *off, unsigned len,
char *ok)
{
unsigned j;
|
34da9ae4 |
ty->numElements = readNumber(buffer, off, len, ok); |
d11cced2 |
if (!*ok) { |
8cc286f5 |
cli_errmsg("Error parsing type\n");
*ok = 0;
return;
}
ty->containedTypes = cli_malloc(sizeof(*ty->containedTypes)*ty->numElements);
if (!ty->containedTypes) {
cli_errmsg("Out of memory allocating %u types\n", ty->numElements);
*ok = 0;
return;
}
for (j=0;j<ty->numElements;j++) {
ty->containedTypes[j] = readTypeID(bc, buffer, off, len, ok);
}
}
static uint16_t containedTy[] = {8,16,32,64};
|
36fab4bd |
#define NUM_STATIC_TYPES 4 |
8cc286f5 |
static void add_static_types(struct cli_bc *bc)
{
unsigned i; |
36fab4bd |
for (i=0;i<NUM_STATIC_TYPES;i++) { |
c466339d |
bc->types[i].kind = DPointerType; |
8cc286f5 |
bc->types[i].numElements = 1;
bc->types[i].containedTypes = &containedTy[i]; |
4993d6cf |
bc->types[i].size = bc->types[i].align = 8; |
8cc286f5 |
}
}
static int parseTypes(struct cli_bc *bc, unsigned char *buffer)
{ |
cf0cd429 |
unsigned i, offset = 1, len = strlen((const char*)buffer); |
800a79e0 |
char ok=1;
|
8cc286f5 |
if (buffer[0] != 'T') {
cli_errmsg("Invalid function types header: %c\n", buffer[0]);
return CL_EMALFDB;
}
bc->start_tid = readFixedNumber(buffer, &offset, len, &ok, 2);
if (bc->start_tid != BC_START_TID) {
cli_warnmsg("Type start id mismatch: %u != %u\n", bc->start_tid,
BC_START_TID);
return CL_BREAK;
}
add_static_types(bc); |
e32caecb |
for (i=(BC_START_TID - 65);i<bc->num_types-1;i++) { |
8cc286f5 |
struct cli_bc_type *ty = &bc->types[i];
uint8_t t = readFixedNumber(buffer, &offset, len, &ok, 1);
if (!ok) {
cli_errmsg("Error reading type kind\n");
return CL_EMALFDB;
}
switch (t) {
case 1: |
c466339d |
ty->kind = DFunctionType; |
7a14dc4c |
ty->size = ty->align = sizeof(void*); |
8cc286f5 |
parseType(bc, ty, buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Error parsing type %u\n", i);
return CL_EMALFDB;
} |
e32caecb |
if (!ty->numElements) {
cli_errmsg("Function with no return type? %u\n", i);
return CL_EMALFDB;
} |
8cc286f5 |
break;
case 2:
case 3: |
479fa713 |
ty->kind = (t == 2) ? DPackedStructType : DStructType; |
7a14dc4c |
ty->size = ty->align = 0;/* TODO:calculate size/align of structs */ |
c074ecec |
ty->align = 8; |
8cc286f5 |
parseType(bc, ty, buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Error parsing type %u\n", i);
return CL_EMALFDB;
}
break;
case 4: |
c466339d |
ty->kind = DArrayType; |
8cc286f5 |
/* number of elements of array, not subtypes! */
ty->numElements = readNumber(buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Error parsing type %u\n", i);
return CL_EMALFDB;
}
/* fall-through */
case 5:
if (t == 5) { |
c466339d |
ty->kind = DPointerType; |
8cc286f5 |
ty->numElements = 1;
}
ty->containedTypes = cli_malloc(sizeof(*ty->containedTypes));
if (!ty->containedTypes) {
cli_errmsg("Out of memory allocating containedType\n");
return CL_EMALFDB;
}
ty->containedTypes[0] = readTypeID(bc, buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Error parsing type %u\n", i);
return CL_EMALFDB;
} |
7a14dc4c |
if (t == 5) { |
b3b8b6dd |
/* for interpreter, pointers 64-bit there */
ty->size = ty->align = 8; |
7a14dc4c |
} else {
ty->size = ty->numElements*typesize(bc, ty->containedTypes[0]);
ty->align = typealign(bc, ty->containedTypes[0]);
} |
8cc286f5 |
break;
default:
cli_errmsg("Invalid type kind: %u\n", t);
return CL_EMALFDB;
}
} |
6b5af78c |
for (i=(BC_START_TID - 65);i<bc->num_types-1;i++) {
struct cli_bc_type *ty = &bc->types[i];
if (ty->kind == DArrayType) {
ty->size = ty->numElements*typesize(bc, ty->containedTypes[0]);
ty->align = typealign(bc, ty->containedTypes[0]);
}
} |
8cc286f5 |
return CL_SUCCESS;
}
|
cf0cd429 |
/* checks whether the type described by tid is the same as the one described by |
7cd9337a |
* apitid. */ |
cf0cd429 |
static int types_equal(const struct cli_bc *bc, uint16_t *apity2ty, uint16_t tid, uint16_t apitid)
{
unsigned i; |
479fa713 |
const struct cli_bc_type *ty = &bc->types[tid - 65]; |
cf0cd429 |
const struct cli_bc_type *apity = &cli_apicall_types[apitid];
/* If we've already verified type equality, return.
* Since we need to check equality of recursive types, we assume types are
* equal while checking equality of contained types, unless proven
* otherwise. */
if (apity2ty[apitid] == tid + 1)
return 1;
apity2ty[apitid] = tid+1;
if (ty->kind != apity->kind) {
cli_dbgmsg("bytecode: type kind mismatch: %u != %u\n", ty->kind, apity->kind);
return 0;
}
if (ty->numElements != apity->numElements) {
cli_dbgmsg("bytecode: type numElements mismatch: %u != %u\n", ty->numElements, apity->numElements);
return 0;
} |
b31f2e7a |
for (i=0;i<ty->numElements;i++) { |
cf0cd429 |
if (apity->containedTypes[i] < BC_START_TID) { |
b31f2e7a |
if (ty->containedTypes[i] != apity->containedTypes[i]) {
cli_dbgmsg("bytecode: contained type mismatch: %u != %u\n",
ty->containedTypes[i], apity->containedTypes[i]); |
cf0cd429 |
return 0; |
b31f2e7a |
} |
cf0cd429 |
} else if (!types_equal(bc, apity2ty, ty->containedTypes[i], apity->containedTypes[i] - BC_START_TID))
return 0; |
b31f2e7a |
if (ty->kind == DArrayType)
break;/* validated the contained type already */
} |
cf0cd429 |
return 1;
}
|
8cc286f5 |
static int parseApis(struct cli_bc *bc, unsigned char *buffer)
{ |
cf0cd429 |
unsigned i, offset = 1, len = strlen((const char*)buffer), maxapi, calls;
char ok =1;
uint16_t *apity2ty;/*map of api type to current bytecode type ID */
if (buffer[0] != 'E') {
cli_errmsg("bytecode: Invalid api header: %c\n", buffer[0]);
return CL_EMALFDB;
}
maxapi = readNumber(buffer, &offset, len, &ok);
if (!ok)
return CL_EMALFDB;
if (maxapi > cli_apicall_maxapi) {
cli_dbgmsg("bytecode using API %u, but highest API known to libclamav is %u, skipping\n", maxapi, cli_apicall_maxapi);
return CL_BREAK;
}
calls = readNumber(buffer, &offset, len, &ok);
if (!ok)
return CL_EMALFDB;
if (calls > maxapi) {
cli_errmsg("bytecode: attempting to describe more APIs than max: %u > %u\n", calls, maxapi);
return CL_EMALFDB;
}
bc->uses_apis = cli_bitset_init();
if (!bc->uses_apis) {
cli_errmsg("Out of memory allocating apis bitset\n");
return CL_EMEM;
}
apity2ty = cli_calloc(cli_apicall_maxtypes, sizeof(*cli_apicall_types));
if (!apity2ty) {
cli_errmsg("Out of memory allocating apity2ty\n");
return CL_EMEM;
}
for (i=0;i < calls; i++) {
unsigned id = readNumber(buffer, &offset, len, &ok);
uint16_t tid = readTypeID(bc, buffer, &offset, len, &ok);
char *name = readString(buffer, &offset, len, &ok);
/* validate APIcall prototype */
if (id > maxapi) {
cli_errmsg("bytecode: API id %u out of range, max %u\n", id, maxapi);
ok = 0;
}
/* API ids start from 1 */
id--;
if (ok && name && strcmp(cli_apicalls[id].name, name)) {
cli_errmsg("bytecode: API %u name mismatch: %s expected %s\n", id, name, cli_apicalls[id].name);
ok = 0;
}
if (ok && !types_equal(bc, apity2ty, tid, cli_apicalls[id].type)) {
cli_errmsg("bytecode: API %u prototype doesn't match\n", id);
ok = 0;
}
/* don't need the name anymore */
free(name); |
bebd86a6 |
if (!ok) {
free(apity2ty); /* free temporary map */ |
cf0cd429 |
return CL_EMALFDB; |
bebd86a6 |
} |
cf0cd429 |
/* APIcall is valid */
cli_bitset_set(bc->uses_apis, id);
}
free(apity2ty); /* free temporary map */
cli_dbgmsg("bytecode: Parsed %u APIcalls, maxapi %u\n", calls, maxapi); |
997a1efe |
return CL_SUCCESS;
}
|
ec077929 |
static uint16_t type_components(struct cli_bc *bc, uint16_t id, char *ok)
{
unsigned i, sum=0;
const struct cli_bc_type *ty;
if (id <= 64)
return 1;
ty = &bc->types[id-65];
/* TODO: protect against recursive types */
switch (ty->kind) {
case DFunctionType:
cli_errmsg("bytecode: function type not accepted for constant: %u\n", id);
/* don't accept functions as constant initializers */
*ok = 0;
return 0;
case DPointerType: |
7189addb |
return 2; |
ec077929 |
case DStructType:
case DPackedStructType:
for (i=0;i<ty->numElements;i++) {
sum += type_components(bc, ty->containedTypes[i], ok);
}
return sum;
case DArrayType:
return type_components(bc, ty->containedTypes[0], ok)*ty->numElements;
default:
*ok = 0;
return 0;
}
}
static void readConstant(struct cli_bc *bc, unsigned i, unsigned comp,
unsigned char *buffer, unsigned *offset,
unsigned len, char *ok)
{
unsigned j=0; |
dcaa4038 |
if (*ok && buffer[*offset] == 0x40 &&
buffer [*offset+1] == 0x60) {
/* zero initializer */
memset(bc->globals[i], 0, sizeof(*bc->globals[0])*comp);
(*offset)+=2;
return;
} |
ec077929 |
while (*ok && buffer[*offset] != 0x60) { |
7189addb |
if (j >= comp) { |
ec077929 |
cli_errmsg("bytecode: constant has too many subcomponents, expected %u\n", comp);
*ok = 0;
return;
}
buffer[*offset] |= 0x20;
bc->globals[i][j++] = readNumber(buffer, offset, len, ok);
}
if (*ok && j != comp) { |
dcaa4038 |
cli_errmsg("bytecode: constant has too few subcomponents: %u < %u\n", j, comp);
*ok = 0; |
ec077929 |
} |
7189addb |
(*offset)++; |
ec077929 |
}
/* parse constant globals with constant initializers */
static int parseGlobals(struct cli_bc *bc, unsigned char *buffer)
{
unsigned i, offset = 1, len = strlen((const char*)buffer), numglobals; |
9fac78c7 |
unsigned maxglobal; |
ec077929 |
char ok=1;
if (buffer[0] != 'G') {
cli_errmsg("bytecode: Invalid globals header: %c\n", buffer[0]);
return CL_EMALFDB;
} |
9fac78c7 |
maxglobal = readNumber(buffer, &offset, len, &ok);
if (maxglobal > cli_apicall_maxglobal) {
cli_dbgmsg("bytecode using global %u, but highest global known to libclamav is %u, skipping\n", maxglobal, cli_apicall_maxglobal);
return CL_BREAK;
} |
ec077929 |
numglobals = readNumber(buffer, &offset, len, &ok);
bc->globals = cli_calloc(numglobals, sizeof(*bc->globals));
if (!bc->globals) {
cli_errmsg("bytecode: OOM allocating memory for %u globals\n", numglobals);
return CL_EMEM;
}
bc->globaltys = cli_calloc(numglobals, sizeof(*bc->globaltys));
if (!bc->globaltys) {
cli_errmsg("bytecode: OOM allocating memory for %u global types\n", numglobals);
return CL_EMEM;
}
bc->num_globals = numglobals;
if (!ok)
return CL_EMALFDB;
for (i=0;i<numglobals;i++) {
unsigned comp;
bc->globaltys[i] = readTypeID(bc, buffer, &offset, len, &ok);
comp = type_components(bc, bc->globaltys[i], &ok);
if (!ok)
return CL_EMALFDB; |
3ff75dbf |
bc->globals[i] = cli_malloc(sizeof(*bc->globals[0])*comp); |
ec077929 |
if (!bc->globals[i])
return CL_EMEM;
readConstant(bc, i, comp, buffer, &offset, len, &ok);
}
if (!ok)
return CL_EMALFDB; |
7189addb |
if (offset != len) {
cli_errmsg("Trailing garbage in globals: %d extra bytes\n",
len-offset);
return CL_EMALFDB;
} |
ec077929 |
return CL_SUCCESS;
}
|
53bd5bb1 |
static int parseMD(struct cli_bc *bc, unsigned char *buffer)
{ |
e4a0f2c9 |
unsigned offset = 1, len = strlen((const char*)buffer); |
53bd5bb1 |
unsigned numMD, i, b;
char ok = 1;
if (buffer[0] != 'D')
return CL_EMALFDB;
numMD = readNumber(buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Unable to parse number of MD nodes\n");
return CL_EMALFDB;
}
b = bc->dbgnode_cnt;
bc->dbgnode_cnt += numMD;
bc->dbgnodes = cli_realloc(bc->dbgnodes, bc->dbgnode_cnt * sizeof(*bc->dbgnodes));
if (!bc->dbgnodes)
return CL_EMEM;
for (i=0;i<numMD;i++) {
unsigned j;
struct cli_bc_dbgnode_element* elts;
unsigned el = readNumber(buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Unable to parse number of elements\n");
return CL_EMALFDB;
}
bc->dbgnodes[b+i].numelements = el;
bc->dbgnodes[b+i].elements = elts = cli_calloc(el, sizeof(*elts));
if (!elts)
return CL_EMEM;
for (j=0;j<el;j++) {
if (buffer[offset] == '|') {
elts[j].string = readData(buffer, &offset, len, &ok, &elts[j].len);
if (!ok)
return CL_EMALFDB;
} else {
elts[j].len = readNumber(buffer, &offset, len, &ok);
if (!ok)
return CL_EMALFDB;
if (elts[j].len) {
elts[j].constant = readNumber(buffer, &offset, len, &ok);
}
else
elts[j].nodeid = readNumber(buffer, &offset, len, &ok);
if (!ok)
return CL_EMALFDB;
}
}
}
cli_dbgmsg("bytecode: Parsed %u nodes total\n", bc->dbgnode_cnt);
return CL_SUCCESS;
}
|
c3c97d4a |
static int parseFunctionHeader(struct cli_bc *bc, unsigned fn, unsigned char *buffer) |
997a1efe |
{
char ok=1;
unsigned offset, len, all_locals=0, i;
struct cli_bc_func *func;
if (fn >= bc->num_func) {
cli_errmsg("Found more functions than declared: %u >= %u\n", fn,
bc->num_func);
return CL_EMALFDB;
}
func = &bc->funcs[fn]; |
c3c97d4a |
len = strlen((const char*)buffer); |
997a1efe |
if (buffer[0] != 'A') {
cli_errmsg("Invalid function arguments header: %c\n", buffer[0]);
return CL_EMALFDB;
}
offset = 1;
func->numArgs = readFixedNumber(buffer, &offset, len, &ok, 1); |
3b33bd68 |
func->returnType = readTypeID(bc, buffer, &offset, len, &ok); |
997a1efe |
if (buffer[offset] != 'L') {
cli_errmsg("Invalid function locals header: %c\n", buffer[offset]);
return CL_EMALFDB;
}
offset++;
func->numLocals = readNumber(buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Invalid number of arguments/locals\n");
return CL_EMALFDB;
}
all_locals = func->numArgs + func->numLocals; |
5475ec2a |
if (!all_locals) {
func->types = NULL;
} else {
func->types = cli_calloc(all_locals, sizeof(*func->types));
if (!func->types) {
cli_errmsg("Out of memory allocating function arguments\n");
return CL_EMEM;
} |
997a1efe |
}
for (i=0;i<all_locals;i++) {
func->types[i] = readNumber(buffer, &offset, len, &ok); |
a45e2577 |
if (readFixedNumber(buffer, &offset, len, &ok, 1))
func->types[i] |= 0x8000; |
997a1efe |
}
if (!ok) {
cli_errmsg("Invalid local types\n");
return CL_EMALFDB;
}
if (buffer[offset] != 'F') {
cli_errmsg("Invalid function body header: %c\n", buffer[offset]);
return CL_EMALFDB;
}
offset++; |
3555ee75 |
func->numInsts = readNumber(buffer, &offset, len, &ok);
if (!ok ){
cli_errmsg("Invalid instructions count\n");
return CL_EMALFDB;
} |
ec5cccc7 |
func->numValues = func->numArgs + func->numLocals; |
3555ee75 |
func->insn_idx = 0;
func->numConstants=0;
func->allinsts = cli_calloc(func->numInsts, sizeof(*func->allinsts));
if (!func->allinsts) {
cli_errmsg("Out of memory allocating instructions\n");
return CL_EMEM;
} |
997a1efe |
func->numBB = readNumber(buffer, &offset, len, &ok);
if (!ok) {
cli_errmsg("Invalid basic block count\n");
return CL_EMALFDB;
}
func->BB = cli_calloc(func->numBB, sizeof(*func->BB));
if (!func->BB) {
cli_errmsg("Out of memory allocating basic blocks\n");
return CL_EMEM;
}
return CL_SUCCESS;
}
|
3555ee75 |
static bbid_t readBBID(struct cli_bc_func *func, const unsigned char *buffer, unsigned *off, unsigned len, char *ok) { |
c3c97d4a |
unsigned id = readNumber(buffer, off, len, ok);
if (!id || id >= func->numBB) {
cli_errmsg("Basic block ID out of range: %u\n", id);
*ok = 0;
}
if (!*ok) |
3555ee75 |
return ~0;
return id; |
c3c97d4a |
}
|
800a79e0 |
/* |
6922903a |
static uint16_t get_type(struct cli_bc_func *func, operand_t op)
{
if (op >= func->numValues)
return 64;
return func->types[op]; |
800a79e0 |
}*/ |
48fc8b98 |
static int16_t get_optype(const struct cli_bc_func *bcfunc, operand_t op)
{
if (op >= bcfunc->numArgs + bcfunc->numLocals)
return 0;
return bcfunc->types[op]&0x7fff;
} |
6922903a |
|
c3c97d4a |
static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char *buffer) |
997a1efe |
{
char ok=1; |
6922903a |
unsigned offset, len, i, last = 0; |
997a1efe |
struct cli_bc_bb *BB; |
c3c97d4a |
struct cli_bc_func *bcfunc = &bc->funcs[func]; |
997a1efe |
struct cli_bc_inst inst;
|
c3c97d4a |
if (bb >= bcfunc->numBB) { |
997a1efe |
cli_errmsg("Found too many basic blocks\n");
return CL_EMALFDB;
}
|
c3c97d4a |
BB = &bcfunc->BB[bb];
len = strlen((const char*) buffer); |
997a1efe |
if (buffer[0] != 'B') {
cli_errmsg("Invalid basic block header: %c\n", buffer[0]);
return CL_EMALFDB;
}
offset = 1;
BB->numInsts = 0; |
3555ee75 |
BB->insts = &bcfunc->allinsts[bcfunc->insn_idx]; |
997a1efe |
while (!last) { |
800a79e0 |
unsigned numOp; |
997a1efe |
if (buffer[offset] == 'T') {
last = 1;
offset++;
/* terminators are void */
inst.type = 0; |
f3b2dc9e |
inst.dest = 0; |
997a1efe |
} else {
inst.type = readNumber(buffer, &offset, len, &ok); |
f3b2dc9e |
inst.dest = readNumber(buffer, &offset, len, &ok); |
997a1efe |
}
inst.opcode = readFixedNumber(buffer, &offset, len, &ok, 2);
if (!ok) {
cli_errmsg("Invalid type or operand\n");
return CL_EMALFDB;
} |
52dd3a6b |
if (inst.opcode >= OP_BC_INVALID) { |
997a1efe |
cli_errmsg("Invalid opcode: %u\n", inst.opcode);
return CL_EMALFDB;
} |
09bd9839 |
|
c3c97d4a |
switch (inst.opcode) { |
52dd3a6b |
case OP_BC_JMP: |
c3c97d4a |
inst.u.jump = readBBID(bcfunc, buffer, &offset, len, &ok); |
997a1efe |
break; |
52dd3a6b |
case OP_BC_RET: |
6922903a |
inst.type = readNumber(buffer, &offset, len, &ok); |
09bd9839 |
inst.u.unaryop = readOperand(bcfunc, buffer, &offset, len, &ok); |
6922903a |
break; |
52dd3a6b |
case OP_BC_BRANCH: |
3555ee75 |
inst.u.branch.condition = readOperand(bcfunc, buffer, &offset, len, &ok); |
c3c97d4a |
inst.u.branch.br_true = readBBID(bcfunc, buffer, &offset, len, &ok);
inst.u.branch.br_false = readBBID(bcfunc, buffer, &offset, len, &ok); |
997a1efe |
break; |
52dd3a6b |
case OP_BC_CALL_API:/* fall-through */
case OP_BC_CALL_DIRECT: |
3555ee75 |
numOp = readFixedNumber(buffer, &offset, len, &ok, 1); |
a0d6fec0 |
if (ok) {
inst.u.ops.numOps = numOp; |
6922903a |
inst.u.ops.opsizes=NULL; |
5475ec2a |
if (!numOp) {
inst.u.ops.ops = NULL;
} else {
inst.u.ops.ops = cli_calloc(numOp, sizeof(*inst.u.ops.ops));
if (!inst.u.ops.ops) {
cli_errmsg("Out of memory allocating operands\n");
return CL_EMEM;
} |
a0d6fec0 |
} |
52dd3a6b |
if (inst.opcode == OP_BC_CALL_DIRECT) |
cf0cd429 |
inst.u.ops.funcid = readFuncID(bc, buffer, &offset, len, &ok);
else
inst.u.ops.funcid = readAPIFuncID(bc, buffer, &offset, len, &ok); |
a0d6fec0 |
for (i=0;i<numOp;i++) { |
3555ee75 |
inst.u.ops.ops[i] = readOperand(bcfunc, buffer, &offset, len, &ok); |
a0d6fec0 |
}
}
break; |
52dd3a6b |
case OP_BC_ZEXT:
case OP_BC_SEXT:
case OP_BC_TRUNC: |
e0c4fd85 |
inst.u.cast.source = readOperand(bcfunc, buffer, &offset, len, &ok); |
cfec3d90 |
inst.u.cast.mask = bcfunc->types[inst.u.cast.source]; |
82ca2ab4 |
if (inst.u.cast.mask == 1)
inst.u.cast.size = 0;
else if (inst.u.cast.mask <= 8)
inst.u.cast.size = 1;
else if (inst.u.cast.mask <= 16)
inst.u.cast.size = 2;
else if (inst.u.cast.mask <= 32)
inst.u.cast.size = 3;
else if (inst.u.cast.mask <= 64)
inst.u.cast.size = 4; |
cfec3d90 |
/* calculate mask */ |
52dd3a6b |
if (inst.opcode != OP_BC_SEXT) |
cfec3d90 |
inst.u.cast.mask = inst.u.cast.mask != 64 ?
(1ull<<inst.u.cast.mask)-1 :
~0ull; |
e0c4fd85 |
break; |
9463f9fd |
case OP_BC_GEP1:
case OP_BC_GEPZ:
inst.u.three[0] = readNumber(buffer, &offset, len, &ok);
inst.u.three[1] = readOperand(bcfunc, buffer, &offset, len, &ok);
inst.u.three[2] = readOperand(bcfunc, buffer, &offset, len, &ok);
break; |
688799d1 |
case OP_BC_GEPN:
numOp = readFixedNumber(buffer, &offset, len, &ok, 1);
if (ok) { |
9463f9fd |
inst.u.ops.numOps = numOp+2; |
688799d1 |
inst.u.ops.opsizes = NULL; |
b31f2e7a |
inst.u.ops.ops = cli_calloc(numOp+2, sizeof(*inst.u.ops.ops)); |
688799d1 |
if (!inst.u.ops.ops) {
cli_errmsg("Out of memory allocating operands\n");
return CL_EMEM;
}
inst.u.ops.ops[0] = readNumber(buffer, &offset, len, &ok); |
9463f9fd |
for (i=1;i<numOp+2;i++) |
688799d1 |
inst.u.ops.ops[i] = readOperand(bcfunc, buffer, &offset, len, &ok);
}
break; |
52dd3a6b |
case OP_BC_ICMP_EQ:
case OP_BC_ICMP_NE:
case OP_BC_ICMP_UGT:
case OP_BC_ICMP_UGE:
case OP_BC_ICMP_ULT:
case OP_BC_ICMP_ULE:
case OP_BC_ICMP_SGT:
case OP_BC_ICMP_SGE:
case OP_BC_ICMP_SLE:
case OP_BC_ICMP_SLT: |
09bd9839 |
/* instruction type must be correct before readOperand! */
inst.type = readNumber(buffer, &offset, len, &ok);
/* fall-through */ |
997a1efe |
default: |
c3c97d4a |
numOp = operand_counts[inst.opcode];
switch (numOp) { |
d38d6dad |
case 0:
break; |
c3c97d4a |
case 1: |
3555ee75 |
inst.u.unaryop = readOperand(bcfunc, buffer, &offset, len, &ok); |
c3c97d4a |
break;
case 2: |
3555ee75 |
inst.u.binop[0] = readOperand(bcfunc, buffer, &offset, len, &ok);
inst.u.binop[1] = readOperand(bcfunc, buffer, &offset, len, &ok); |
c3c97d4a |
break;
case 3: |
3555ee75 |
inst.u.three[0] = readOperand(bcfunc, buffer, &offset, len, &ok);
inst.u.three[1] = readOperand(bcfunc, buffer, &offset, len, &ok);
inst.u.three[2] = readOperand(bcfunc, buffer, &offset, len, &ok); |
c3c97d4a |
break;
default: |
d38d6dad |
cli_errmsg("Opcode %u with too many operands: %u?\n", inst.opcode, numOp); |
a0d6fec0 |
ok = 0; |
c3c97d4a |
break; |
997a1efe |
}
} |
fa0a9143 |
if (inst.opcode == OP_BC_STORE) {
int16_t t = get_optype(bcfunc, inst.u.binop[0]);
if (t)
inst.type = t;
} |
5dfb4bf1 |
if (inst.opcode == OP_BC_COPY) |
48fc8b98 |
inst.type = get_optype(bcfunc, inst.u.binop[1]); |
997a1efe |
if (!ok) {
cli_errmsg("Invalid instructions or operands\n");
return CL_EMALFDB;
} |
3555ee75 |
if (bcfunc->insn_idx + BB->numInsts >= bcfunc->numInsts) { |
6922903a |
cli_errmsg("More instructions than declared in total: %u > %u!\n",
bcfunc->insn_idx+BB->numInsts, bcfunc->numInsts); |
3555ee75 |
return CL_EMALFDB; |
997a1efe |
} |
82ca2ab4 |
inst.interp_op = inst.opcode*5; |
09bd9839 |
if (inst.type > 1) {
if (inst.type <= 8)
inst.interp_op += 1;
else if (inst.type <= 16)
inst.interp_op += 2;
else if (inst.type <= 32)
inst.interp_op += 3; |
74f5816c |
else if (inst.type <= 65) |
09bd9839 |
inst.interp_op += 4; |
74f5816c |
else {
cli_dbgmsg("unknown inst type: %d\n", inst.type);
} |
09bd9839 |
} |
3555ee75 |
BB->insts[BB->numInsts++] = inst; |
997a1efe |
} |
2cad7c39 |
if (bb+1 == bc->funcs[func].numBB) { |
997a1efe |
if (buffer[offset] != 'E') {
cli_errmsg("Missing basicblock terminator, got: %c\n", buffer[offset]);
return CL_EMALFDB;
}
offset++;
} |
53bd5bb1 |
if (buffer[offset] == 'D') { |
7e64560c |
uint32_t num; |
53bd5bb1 |
offset += 3;
if (offset >= len)
return CL_EMALFDB; |
7e64560c |
num = (uint32_t)readNumber(buffer, &offset, len, &ok); |
53bd5bb1 |
if (!ok)
return CL_EMALFDB;
if (num != bcfunc->numInsts) {
cli_errmsg("invalid number of dbg nodes, expected: %u, got: %u\n", bcfunc->numInsts, num);
return CL_EMALFDB;
}
bcfunc->dbgnodes = cli_malloc(num*sizeof(*bcfunc->dbgnodes)); |
241e7eb1 |
if (!bcfunc->dbgnodes) { |
7e64560c |
cli_errmsg("Unable to allocate memory for dbg nodes: %u\n", num * (uint32_t)sizeof(*bcfunc->dbgnodes)); |
53bd5bb1 |
return CL_EMEM; |
241e7eb1 |
} |
7e64560c |
for (i=0; (uint32_t)i < num; i++) { |
53bd5bb1 |
bcfunc->dbgnodes[i] = readNumber(buffer, &offset, len, &ok);
if (!ok)
return CL_EMALFDB;
}
} |
997a1efe |
if (offset != len) {
cli_errmsg("Trailing garbage in basicblock: %d extra bytes\n",
len-offset);
return CL_EMALFDB;
} |
6922903a |
bcfunc->numBytes = 0; |
f3b2dc9e |
bcfunc->insn_idx += BB->numInsts; |
997a1efe |
return CL_SUCCESS;
}
enum parse_state { |
482e97db |
PARSE_BC_TYPES=0, |
8cc286f5 |
PARSE_BC_APIS, |
ec077929 |
PARSE_BC_GLOBALS, |
dcee45cc |
PARSE_BC_LSIG, |
53bd5bb1 |
PARSE_MD_OPT_HEADER, |
997a1efe |
PARSE_FUNC_HEADER, |
fcbfb1c6 |
PARSE_BB,
PARSE_SKIP |
997a1efe |
};
|
54402320 |
struct sigperf_elem {
const char * bc_name;
uint64_t usecs;
unsigned long run_count;
unsigned long match_count;
};
static int sigelem_comp(const void * a, const void * b)
{
const struct sigperf_elem *ela = a;
const struct sigperf_elem *elb = b;
return elb->usecs/elb->run_count - ela->usecs/ela->run_count;
}
void cli_sigperf_print()
{
struct sigperf_elem stats[MAX_BC], *elem = stats;
int i, elems = 0, max_name_len = 0, name_len;
|
5c2c7233 |
if (!g_sigid || !g_sigevents) {
cli_warnmsg("cli_sigperf_print: statistics requested but no bytecodes were loaded!\n");
return;
}
|
54402320 |
memset(stats, 0, sizeof(stats));
for (i=0;i<MAX_BC;i++) {
union ev_val val;
uint32_t count;
const char * name = cli_event_get_name(g_sigevents, i*BC_EVENTS_PER_SIG);
cli_event_get(g_sigevents, i*BC_EVENTS_PER_SIG, &val, &count);
if (!count) {
if (name)
cli_dbgmsg("No event triggered for %s\n", name);
continue;
} |
3bd9a14c |
if (name) |
54402320 |
name_len = strlen(name); |
3bd9a14c |
else
name_len = 0; |
54402320 |
if (name_len > max_name_len)
max_name_len = name_len;
elem->bc_name = name?name:"\"noname\"";
elem->usecs = val.v_int;
elem->run_count = count;
cli_event_get(g_sigevents, i*BC_EVENTS_PER_SIG+1, &val, &count);
elem->match_count = count; |
38956aac |
elem++;
elems++; |
54402320 |
} |
90379a9e |
if (max_name_len < strlen("Bytecode name"))
max_name_len = strlen("Bytecode name"); |
54402320 |
|
38956aac |
cli_qsort(stats, elems, sizeof(struct sigperf_elem), sigelem_comp); |
54402320 |
elem = stats;
/* name runs matches microsecs avg */
cli_infomsg (NULL, "%-*s %*s %*s %*s %*s\n", max_name_len, "Bytecode name",
8, "#runs", 8, "#matches", 12, "usecs total", 9, "usecs avg");
cli_infomsg (NULL, "%-*s %*s %*s %*s %*s\n", max_name_len, "=============",
8, "=====", 8, "========", 12, "===========", 9, "=========");
while (elem->run_count) { |
653b471b |
cli_infomsg (NULL, "%-*s %*lu %*lu %*" PRIu64 " %*.2f\n", max_name_len, elem->bc_name, |
54402320 |
8, elem->run_count, 8, elem->match_count,
12, elem->usecs, 9, (double)elem->usecs/elem->run_count);
elem++;
}
}
static void sigperf_events_init(struct cli_bc *bc)
{
int ret; |
16c4fcdd |
char * bc_name; |
54402320 |
if (!g_sigevents)
g_sigevents = cli_events_new(MAX_BC_SIGEVENT_ID);
if (!g_sigevents) {
cli_errmsg("No memory for events table\n");
return;
}
if (g_sigid > MAX_BC_SIGEVENT_ID - BC_EVENTS_PER_SIG - 1) {
cli_errmsg("sigperf_events_init: events table full. Increase MAX_BC\n");
return;
}
|
16c4fcdd |
if (!(bc_name = bc->lsig)) {
if (!(bc_name = bc->hook_name)) {
cli_dbgmsg("cli_event_define error for time event id %d\n", bc->sigtime_id);
return;
} |
54402320 |
}
|
16c4fcdd |
cli_dbgmsg("sigperf_events_init(): adding sig ids starting %u for %s\n", g_sigid, bc_name);
|
54402320 |
/* register time event */
bc->sigtime_id = g_sigid; |
16c4fcdd |
ret = cli_event_define(g_sigevents, g_sigid++, bc_name, ev_time, multiple_sum); |
54402320 |
if (ret) {
cli_errmsg("sigperf_events_init: cli_event_define() error for time event id %d\n", bc->sigtime_id);
bc->sigtime_id = MAX_BC_SIGEVENT_ID+1;
return;
}
/* register match count */
bc->sigmatch_id = g_sigid; |
16c4fcdd |
ret = cli_event_define(g_sigevents, g_sigid++, bc_name, ev_int, multiple_sum); |
54402320 |
if (ret) {
cli_errmsg("sigperf_events_init: cli_event_define() error for matches event id %d\n", bc->sigmatch_id); |
3bd9a14c |
bc->sigmatch_id = MAX_BC_SIGEVENT_ID+1; |
54402320 |
return;
}
}
void cli_sigperf_events_destroy()
{
cli_events_free(g_sigevents);
}
int cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int trust, int sigperf) |
997a1efe |
{
unsigned row = 0, current_func = 0, bb=0; |
482e97db |
char *buffer;
unsigned linelength=0;
char firstbuf[FILEBUFF];
enum parse_state state; |
9bdaf35d |
int rc, end=0; |
997a1efe |
|
2d45ef06 |
memset(bc, 0, sizeof(*bc)); |
606fd6d0 |
cli_dbgmsg("Loading %s bytecode\n", trust ? "trusted" : "untrusted"); |
be43f951 |
bc->trusted = trust; |
997a1efe |
if (!f && !dbio) {
cli_errmsg("Unable to load bytecode (null file)\n");
return CL_ENULLARG;
} |
482e97db |
if (!cli_dbgets(firstbuf, FILEBUFF, f, dbio)) {
cli_errmsg("Unable to load bytecode (empty file)\n");
return CL_EMALFDB;
} |
c1aff5ec |
cli_chomp(firstbuf); |
482e97db |
rc = parseHeader(bc, (unsigned char*)firstbuf, &linelength); |
fcbfb1c6 |
state = PARSE_BC_LSIG; |
482e97db |
if (rc == CL_BREAK) { |
fcbfb1c6 |
const char *len = strchr(firstbuf, ':'); |
482e97db |
bc->state = bc_skip; |
fcbfb1c6 |
if (!linelength) {
linelength = len ? atoi(len+1) : 4096;
} |
93e0033f |
if (linelength < 4096)
linelength = 4096; |
fcbfb1c6 |
cli_dbgmsg("line: %d\n", linelength);
state = PARSE_SKIP;
rc = CL_SUCCESS; |
482e97db |
}
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row);
return rc;
}
buffer = cli_malloc(linelength);
if (!buffer) {
cli_errmsg("Out of memory allocating line of length %u\n", linelength);
return CL_EMEM;
} |
9bdaf35d |
while (cli_dbgets(buffer, linelength, f, dbio) && !end) { |
997a1efe |
cli_chomp(buffer); |
f60a0e00 |
row++; |
997a1efe |
switch (state) { |
dcee45cc |
case PARSE_BC_LSIG: |
e4a0f2c9 |
rc = parseLSig(bc, buffer); |
16c4fcdd |
#if 0
DEAD CODE
if (rc == CL_BREAK) /* skip */ { //FIXME: parseLSig always returns CL_SUCCESS |
dcee45cc |
bc->state = bc_skip; |
fcbfb1c6 |
state = PARSE_SKIP;
continue; |
dcee45cc |
} |
16c4fcdd |
if (rc != CL_SUCCESS) { //FIXME: parseLSig always returns CL_SUCCESS |
dcee45cc |
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
dcee45cc |
return rc;
} |
16c4fcdd |
#endif |
8cc286f5 |
state = PARSE_BC_TYPES;
break;
case PARSE_BC_TYPES:
rc = parseTypes(bc, (unsigned char*)buffer);
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
8cc286f5 |
return rc;
}
state = PARSE_BC_APIS;
break;
case PARSE_BC_APIS:
rc = parseApis(bc, (unsigned char*)buffer); |
4789b8a5 |
if (rc == CL_BREAK) /* skip */ {
bc->state = bc_skip; |
fcbfb1c6 |
state = PARSE_SKIP;
continue; |
4789b8a5 |
} |
8cc286f5 |
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
8cc286f5 |
return rc;
} |
ec077929 |
state = PARSE_BC_GLOBALS;
break;
case PARSE_BC_GLOBALS:
rc = parseGlobals(bc, (unsigned char*)buffer);
if (rc == CL_BREAK) /* skip */ {
bc->state = bc_skip; |
fcbfb1c6 |
state = PARSE_SKIP;
continue; |
ec077929 |
}
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
ec077929 |
return rc;
} |
53bd5bb1 |
state = PARSE_MD_OPT_HEADER; |
997a1efe |
break; |
53bd5bb1 |
case PARSE_MD_OPT_HEADER:
if (buffer[0] == 'D') {
rc = parseMD(bc, (unsigned char*)buffer);
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
53bd5bb1 |
return rc;
}
break;
} |
c0afc517 |
/* fall-through */ |
997a1efe |
case PARSE_FUNC_HEADER: |
9bdaf35d |
if (*buffer == 'S') {
end = 1;
break;
} |
c3c97d4a |
rc = parseFunctionHeader(bc, current_func, (unsigned char*)buffer); |
f60a0e00 |
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
997a1efe |
return rc; |
f60a0e00 |
} |
997a1efe |
bb = 0;
state = PARSE_BB;
break;
case PARSE_BB: |
c3c97d4a |
rc = parseBB(bc, current_func, bb++, (unsigned char*)buffer); |
f60a0e00 |
if (rc != CL_SUCCESS) {
cli_errmsg("Error at bytecode line %u\n", row); |
482e97db |
free(buffer); |
997a1efe |
return rc; |
f60a0e00 |
} |
997a1efe |
if (bb >= bc->funcs[current_func].numBB) { |
6922903a |
if (bc->funcs[current_func].insn_idx != bc->funcs[current_func].numInsts) {
cli_errmsg("Parsed different number of instructions than declared: %u != %u\n",
bc->funcs[current_func].insn_idx, bc->funcs[current_func].numInsts); |
482e97db |
free(buffer); |
6922903a |
return CL_EMALFDB;
} |
cfec3d90 |
cli_dbgmsg("Parsed %u BBs, %u instructions\n",
bb, bc->funcs[current_func].numInsts); |
997a1efe |
state = PARSE_FUNC_HEADER;
current_func++;
}
break; |
fcbfb1c6 |
case PARSE_SKIP:
/* stop at S (source code), readdb.c knows how to skip this one
* */
if (buffer[0] == 'S')
end = 1;
/* noop parse, but we need to use dbgets with dynamic buffer,
* otherwise we get 'Line too long for provided buffer' */
break; |
997a1efe |
}
} |
482e97db |
free(buffer); |
997a1efe |
cli_dbgmsg("Parsed %d functions\n", current_func); |
54402320 |
if (sigperf)
sigperf_events_init(bc); |
0796f1ca |
if (current_func != bc->num_func && bc->state != bc_skip) { |
957bf651 |
cli_errmsg("Loaded less functions than declared: %u vs. %u\n",
current_func, bc->num_func);
return CL_EMALFDB;
} |
997a1efe |
return CL_SUCCESS;
}
|
8a8dbd59 |
static struct {
enum bc_events id;
const char *name;
enum ev_type type;
enum multiple_handling multiple;
} bc_events[] = {
{BCEV_VIRUSNAME, "virusname", ev_string, multiple_last},
{BCEV_EXEC_RETURNVALUE, "returnvalue", ev_int, multiple_last},
{BCEV_WRITE, "bcapi_write", ev_data_fast, multiple_sum},
{BCEV_OFFSET, "read offset", ev_int, multiple_sum},
{BCEV_READ, "read data", ev_data_fast, multiple_sum},
//{BCEV_READ, "read data", ev_data, multiple_concat},
{BCEV_DBG_STR, "debug message", ev_data_fast, multiple_sum},
{BCEV_DBG_INT, "debug int", ev_int, multiple_sum},
{BCEV_MEM_1, "memmem 1", ev_data_fast, multiple_sum},
{BCEV_MEM_2, "memmem 2", ev_data_fast, multiple_sum},
{BCEV_FIND, "find", ev_data_fast, multiple_sum},
{BCEV_EXTRACTED, "extracted files", ev_int, multiple_sum},
{BCEV_READ_ERR, "read errors", ev_int, multiple_sum},
{BCEV_DISASM_FAIL, "disasm fails", ev_int, multiple_sum},
{BCEV_EXEC_TIME, "bytecode execute", ev_time, multiple_sum}
};
static int register_events(cli_events_t *ev)
{ |
d18d7221 |
size_t i; |
8a8dbd59 |
for (i=0;i<sizeof(bc_events)/sizeof(bc_events[0]);i++) {
if (cli_event_define(ev, bc_events[i].id, bc_events[i].name, bc_events[i].type,
bc_events[i].multiple) == -1)
return -1;
}
return 0;
}
|
85a25497 |
int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx) |
997a1efe |
{ |
d7531f2a |
int ret = CL_SUCCESS; |
7a9022c9 |
struct cli_bc_inst inst;
struct cli_bc_func func; |
8a8dbd59 |
cli_events_t *jit_ev = NULL, *interp_ev = NULL;
int test_mode = 0;
cli_ctx *cctx =(cli_ctx*)ctx->ctx;
|
e0c4fd85 |
if (!ctx || !ctx->bc || !ctx->func)
return CL_ENULLARG;
if (ctx->numParams && (!ctx->values || !ctx->operands)) |
7a9022c9 |
return CL_ENULLARG; |
6a049897 |
if (cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
test_mode = 1;
|
6922903a |
if (bc->state == bc_loaded) {
cli_errmsg("bytecode has to be prepared either for interpreter or JIT!\n");
return CL_EARG; |
7a9022c9 |
} |
d5ffa2ac |
if (bc->state == bc_disabled) {
cli_dbgmsg("bytecode triggered but running bytecodes is disabled\n");
return CL_SUCCESS;
} |
63feb6cd |
if (cctx) |
6a049897 |
cli_event_time_start(cctx->perf, PERFT_BYTECODE); |
d5ffa2ac |
ctx->env = &bcs->env; |
74f5816c |
context_safe(ctx); |
8a8dbd59 |
if (test_mode) {
jit_ev = cli_events_new(BCEV_LASTEVENT);
interp_ev = cli_events_new(BCEV_LASTEVENT); |
110731e7 |
if (!jit_ev || !interp_ev) {
cli_events_free(jit_ev);
cli_events_free(interp_ev); |
8a8dbd59 |
return CL_EMEM; |
110731e7 |
} |
8a8dbd59 |
if (register_events(jit_ev) == -1 ||
register_events(interp_ev) == -1) { |
110731e7 |
cli_events_free(jit_ev);
cli_events_free(interp_ev); |
8a8dbd59 |
return CL_EBYTECODE_TESTFAIL;
}
} |
54402320 |
cli_event_time_start(g_sigevents, bc->sigtime_id); |
8a8dbd59 |
if (bc->state == bc_interp || test_mode) {
ctx->bc_events = interp_ev; |
85a25497 |
memset(&func, 0, sizeof(func));
func.numInsts = 1;
func.numValues = 1; |
c074ecec |
func.numConstants = 0; |
85a25497 |
func.numBytes = ctx->bytes;
memset(ctx->values+ctx->bytes-8, 0, 8); |
7a9022c9 |
|
52dd3a6b |
inst.opcode = OP_BC_CALL_DIRECT;
inst.interp_op = OP_BC_CALL_DIRECT*5; |
85a25497 |
inst.dest = func.numArgs;
inst.type = 0;
inst.u.ops.numOps = ctx->numParams;
inst.u.ops.funcid = ctx->funcid;
inst.u.ops.ops = ctx->operands;
inst.u.ops.opsizes = ctx->opsizes; |
22cb38ed |
cli_dbgmsg("Bytecode %u: executing in interpreter mode\n", bc->id); |
8a8dbd59 |
|
4116c65d |
ctx->on_jit = 0;
|
8a8dbd59 |
cli_event_time_start(interp_ev, BCEV_EXEC_TIME); |
f73212dc |
ret = cli_vm_execute(ctx->bc, ctx, &func, &inst); |
8a8dbd59 |
cli_event_time_stop(interp_ev, BCEV_EXEC_TIME);
cli_event_int(interp_ev, BCEV_EXEC_RETURNVALUE, ret);
cli_event_string(interp_ev, BCEV_VIRUSNAME, ctx->virname);
/* need to be called here to catch any extracted but not yet scanned files
*/ |
d7979d4f |
if (ctx->outfd && (ret != CL_VIRUS || cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES)) |
8a8dbd59 |
cli_bcapi_extract_new(ctx, -1);
}
if (bc->state == bc_jit || test_mode) {
if (test_mode) {
ctx->off = 0;
}
ctx->bc_events = jit_ev;
cli_dbgmsg("Bytecode %u: executing in JIT mode\n", bc->id);
|
4116c65d |
ctx->on_jit = 1; |
8a8dbd59 |
cli_event_time_start(jit_ev, BCEV_EXEC_TIME); |
f73212dc |
ret = cli_vm_execute_jit(bcs, ctx, &bc->funcs[ctx->funcid]); |
8a8dbd59 |
cli_event_time_stop(jit_ev, BCEV_EXEC_TIME);
cli_event_int(jit_ev, BCEV_EXEC_RETURNVALUE, ret);
cli_event_string(jit_ev, BCEV_VIRUSNAME, ctx->virname);
/* need to be called here to catch any extracted but not yet scanned files
*/ |
d7979d4f |
if (ctx->outfd && (ret != CL_VIRUS || cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES)) |
8a8dbd59 |
cli_bcapi_extract_new(ctx, -1);
} |
54402320 |
cli_event_time_stop(g_sigevents, bc->sigtime_id);
if (ctx->virname)
cli_event_count(g_sigevents, bc->sigmatch_id); |
8a8dbd59 |
if (test_mode) {
unsigned interp_errors = cli_event_errors(interp_ev);
unsigned jit_errors = cli_event_errors(jit_ev);
unsigned interp_warns = 0, jit_warns = 0;
int ok = 1;
enum bc_events evid;
if (interp_errors || jit_errors) {
cli_infomsg(cctx, "bytecode %d encountered %u JIT and %u interpreter errors\n",
bc->id, interp_errors, jit_errors);
ok = 0;
} |
4116c65d |
if (!ctx->no_diff && cli_event_diff_all(interp_ev, jit_ev, NULL)) { |
8a8dbd59 |
cli_infomsg(cctx, "bytecode %d execution different with JIT and interpreter, see --debug for details\n",
bc->id);
ok = 0;
}
for (evid=BCEV_API_WARN_BEGIN+1;evid < BCEV_API_WARN_END;evid++) {
union ev_val v;
uint32_t count = 0;
cli_event_get(interp_ev, evid, &v, &count);
interp_warns += count;
count = 0;
cli_event_get(jit_ev, evid, &v, &count);
jit_warns += count;
}
if (interp_warns || jit_warns) {
cli_infomsg(cctx, "bytecode %d encountered %u JIT and %u interpreter warnings\n",
bc->id, interp_warns, jit_warns);
ok = 0;
}
/*cli_event_debug(jit_ev, BCEV_EXEC_TIME); |
54402320 |
cli_event_debug(interp_ev, BCEV_EXEC_TIME);
cli_event_debug(g_sigevents, bc->sigtime_id);*/ |
110731e7 |
if (!ok) {
cli_events_free(jit_ev);
cli_events_free(interp_ev); |
8a8dbd59 |
return CL_EBYTECODE_TESTFAIL; |
110731e7 |
} |
85a25497 |
} |
110731e7 |
cli_events_free(jit_ev);
cli_events_free(interp_ev); |
63feb6cd |
if (cctx) |
6a049897 |
cli_event_time_stop(cctx->perf, PERFT_BYTECODE); |
f73212dc |
return ret; |
f3b2dc9e |
}
uint64_t cli_bytecode_context_getresult_int(struct cli_bc_ctx *ctx)
{ |
09bd9839 |
return *(uint32_t*)ctx->values;/*XXX*/ |
997a1efe |
}
void cli_bytecode_destroy(struct cli_bc *bc)
{
unsigned i, j, k; |
a35cfe51 |
free(bc->metadata.compiler);
free(bc->metadata.sigmaker); |
997a1efe |
|
f6e8bb80 |
if (bc->funcs) {
for (i=0;i<bc->num_func;i++) {
struct cli_bc_func *f = &bc->funcs[i];
if (!f)
continue;
free(f->types);
for (j=0;j<f->numBB;j++) {
struct cli_bc_bb *BB = &f->BB[j];
for(k=0;k<BB->numInsts;k++) {
struct cli_bc_inst *ii = &BB->insts[k];
if (operand_counts[ii->opcode] > 3 ||
ii->opcode == OP_BC_CALL_DIRECT || ii->opcode == OP_BC_CALL_API) {
free(ii->u.ops.ops);
free(ii->u.ops.opsizes);
} |
6922903a |
} |
997a1efe |
} |
f6e8bb80 |
free(f->BB);
free(f->allinsts);
free(f->constants);
}
free(bc->funcs);
}
if (bc->types) {
for (i=NUM_STATIC_TYPES;i<bc->num_types;i++) {
if (bc->types[i].containedTypes)
free(bc->types[i].containedTypes); |
997a1efe |
} |
f6e8bb80 |
free(bc->types);
}
if (bc->globals) {
for (i=0;i<bc->num_globals;i++) {
free(bc->globals[i]);
}
free(bc->globals);
}
if (bc->dbgnodes) {
for (i=0;i<bc->dbgnode_cnt;i++) {
for (j=0;j<bc->dbgnodes[i].numelements;j++) {
struct cli_bc_dbgnode_element *el = &bc->dbgnodes[i].elements[j];
if (el && el->string)
free(el->string);
} |
53bd5bb1 |
} |
f6e8bb80 |
free(bc->dbgnodes); |
53bd5bb1 |
} |
ec077929 |
free(bc->globaltys); |
cf0cd429 |
if (bc->uses_apis)
cli_bitset_free(bc->uses_apis); |
dcee45cc |
free(bc->lsig); |
16c4fcdd |
free(bc->hook_name); |
9cbece5c |
free(bc->globalBytes); |
5475ec2a |
memset(bc, 0, sizeof(*bc)); |
997a1efe |
}
|
6922903a |
#define MAP(val) do { operand_t o = val; \ |
3ff41e32 |
if (o & 0x80000000) {\
o &= 0x7fffffff;\ |
20327f93 |
if (o > bc->num_globals) {\
cli_errmsg("bytecode: global out of range: %u > %u, for instruction %u in function %u\n",\ |
db296754 |
o, (unsigned)bc->num_globals, j, i);\ |
6a9086d2 |
free(map);\
free(gmap);\ |
20327f93 |
return CL_EBYTECODE;\
}\
val = 0x80000000 | gmap[o];\
break;\ |
3ff41e32 |
}\ |
dac4e487 |
if (o >= totValues) {\ |
6922903a |
cli_errmsg("bytecode: operand out of range: %u > %u, for instruction %u in function %u\n", o, totValues, j, i);\ |
6a9086d2 |
free(map);\
free(gmap);\ |
6922903a |
return CL_EBYTECODE;\
}\
val = map[o]; } while (0)
|
0d9b99f4 |
#define MAPPTR(val) {\
if ((val < bcfunc->numValues) && bcfunc->types[val]&0x8000)\
val = map[val] | 0x40000000;\
else\
MAP(val);\
}
|
52d0d8bc |
static inline int64_t ptr_compose(int32_t id, uint32_t offset)
{
uint64_t i = id;
return (i << 32) | offset;
}
|
44e13431 |
static inline int get_geptypesize(const struct cli_bc *bc, uint16_t tid)
{
const struct cli_bc_type *ty;
if (tid >= bc->num_types+65) {
cli_errmsg("bytecode: typeid out of range %u >= %u\n", tid, bc->num_types);
return -1;
}
if (tid <= 64) {
cli_errmsg("bytecode: invalid type for gep (%u)\n", tid);
return -1;
}
ty = &bc->types[tid - 65];
if (ty->kind != DPointerType) {
cli_errmsg("bytecode: invalid gep type, must be pointer: %u\n", tid);
return -1;
}
return typesize(bc, ty->containedTypes[0]);
}
|
b1018ea5 |
static int calc_gepz(struct cli_bc *bc, struct cli_bc_func *func, uint16_t tid, operand_t op)
{
unsigned off = 0, i; |
c09f9b29 |
uint32_t *gepoff; |
b1018ea5 |
const struct cli_bc_type *ty;
if (tid >= bc->num_types + 65) {
cli_errmsg("bytecode: typeid out of range %u >= %u\n", tid, bc->num_types);
return -1;
}
if (tid <= 65) {
cli_errmsg("bytecode: invalid type for gep (%u)\n", tid);
return -1;
}
ty = &bc->types[tid - 65];
if (ty->kind != DPointerType || ty->containedTypes[0] < 65) {
cli_errmsg("bytecode: invalid gep type, must be pointer to nonint: %u\n", tid);
return -1;
}
ty = &bc->types[ty->containedTypes[0] - 65];
if (ty->kind != DStructType && ty->kind != DPackedStructType)
return 0; |
c09f9b29 |
gepoff = (uint32_t*)&func->constants[op - func->numValues]; |
b1018ea5 |
if (*gepoff >= ty->numElements) { |
88d54dcb |
cli_errmsg("bytecode: gep offset out of range: %d >= %d\n",(uint32_t)*gepoff, ty->numElements); |
b1018ea5 |
return -1;
}
for (i=0;i<*gepoff;i++) {
off += typesize(bc, ty->containedTypes[i]);
}
*gepoff = off;
return 1;
}
|
6922903a |
static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
{ |
dc200c6b |
unsigned i, j, k; |
52d0d8bc |
uint64_t *gmap; |
74f5816c |
unsigned bcglobalid = cli_apicall_maxglobal - _FIRST_GLOBAL+2; |
6a049897 |
int ret=CL_SUCCESS; |
20327f93 |
bc->numGlobalBytes = 0;
gmap = cli_malloc(bc->num_globals*sizeof(*gmap)); |
241e7eb1 |
if (!gmap) { |
7e64560c |
cli_errmsg("interpreter: Unable to allocate memory for global map: %zu\n", bc->num_globals*sizeof(*gmap)); |
241e7eb1 |
return CL_EMEM;
} |
20327f93 |
for (j=0;j<bc->num_globals;j++) {
uint16_t ty = bc->globaltys[j];
unsigned align = typealign(bc, ty);
assert(align);
bc->numGlobalBytes = (bc->numGlobalBytes + align-1)&(~(align-1));
gmap[j] = bc->numGlobalBytes;
bc->numGlobalBytes += typesize(bc, ty);
} |
c6c464e5 |
if (bc->numGlobalBytes) {
bc->globalBytes = cli_calloc(1, bc->numGlobalBytes); |
6a049897 |
if (!bc->globalBytes) { |
241e7eb1 |
cli_errmsg("interpreter: Unable to allocate memory for globalBytes: %u\n", bc->numGlobalBytes); |
6a049897 |
free(gmap); |
c6c464e5 |
return CL_EMEM; |
6a049897 |
} |
c6c464e5 |
} else
bc->globalBytes = NULL;
|
52d0d8bc |
for (j=0;j<bc->num_globals;j++) {
struct cli_bc_type *ty;
if (bc->globaltys[j] < 65)
continue;
ty = &bc->types[bc->globaltys[j]-65];
switch (ty->kind) {
case DPointerType: |
74f5816c |
{
uint64_t ptr;
if (bc->globals[j][1] >= _FIRST_GLOBAL) {
ptr = ptr_compose(bc->globals[j][1] - _FIRST_GLOBAL + 1,
bc->globals[j][0]);
} else {
if (bc->globals[j][1] > bc->num_globals)
continue;
ptr = ptr_compose(bcglobalid,
gmap[bc->globals[j][1]] + bc->globals[j][0]);
}
*(uint64_t*)&bc->globalBytes[gmap[j]] = ptr;
break;
}
case DArrayType:
{
unsigned elsize, i, off = gmap[j];
/* TODO: support other than ints in arrays */
elsize = typesize(bc, ty->containedTypes[0]);
switch (elsize) {
case 1:
for(i=0;i<ty->numElements;i++)
bc->globalBytes[off+i] = bc->globals[j][i];
break;
case 2:
for(i=0;i<ty->numElements;i++)
*(uint16_t*)&bc->globalBytes[off+i*2] = bc->globals[j][i];
break;
case 4:
for(i=0;i<ty->numElements;i++)
*(uint32_t*)&bc->globalBytes[off+i*4] = bc->globals[j][i];
break;
case 8:
for(i=0;i<ty->numElements;i++)
*(uint64_t*)&bc->globalBytes[off+i*8] = bc->globals[j][i];
break;
default:
cli_dbgmsg("interpreter: unsupported elsize: %u\n", elsize);
}
break;
} |
52d0d8bc |
default:
/*TODO*/
if (!bc->globals[j][1])
continue; /* null */
break;
}
} |
6eeadbfe |
|
6a049897 |
for (i=0;i<bc->num_func && ret == CL_SUCCESS;i++) { |
6922903a |
struct cli_bc_func *bcfunc = &bc->funcs[i]; |
3ff41e32 |
unsigned totValues = bcfunc->numValues + bcfunc->numConstants + bc->num_globals; |
7e64560c |
unsigned *map = cli_malloc(sizeof(*map) * (size_t)totValues); |
042a78ac |
if (!map) { |
7e64560c |
cli_errmsg("interpreter: Unable to allocate memory for map: %zu\n", sizeof(*map) * (size_t)totValues); |
042a78ac |
free(gmap);
return CL_EMEM;
} |
c074ecec |
bcfunc->numBytes = 0; |
042a78ac |
for (j=0;j<bcfunc->numValues;j++) { |
6922903a |
uint16_t ty = bcfunc->types[j];
unsigned align;
align = typealign(bc, ty); |
d7531f2a |
assert(!ty || typesize(bc, ty)); |
c074ecec |
assert(align); |
6922903a |
bcfunc->numBytes = (bcfunc->numBytes + align-1)&(~(align-1));
map[j] = bcfunc->numBytes; |
15839138 |
/* printf("%d -> %d, %u\n", j, map[j], typesize(bc, ty)); */ |
6922903a |
bcfunc->numBytes += typesize(bc, ty); |
b1018ea5 |
/* TODO: don't allow size 0, it is always a bug! */ |
6922903a |
}
bcfunc->numBytes = (bcfunc->numBytes + 7)&~7;
for (j=0;j<bcfunc->numConstants;j++) {
map[bcfunc->numValues+j] = bcfunc->numBytes;
bcfunc->numBytes += 8;
} |
6a049897 |
for (j=0;j<bcfunc->numInsts && ret == CL_SUCCESS;j++) { |
6922903a |
struct cli_bc_inst *inst = &bcfunc->allinsts[j];
inst->dest = map[inst->dest];
switch (inst->opcode) { |
52dd3a6b |
case OP_BC_ADD:
case OP_BC_SUB:
case OP_BC_MUL:
case OP_BC_UDIV:
case OP_BC_SDIV:
case OP_BC_UREM:
case OP_BC_SREM:
case OP_BC_SHL:
case OP_BC_LSHR:
case OP_BC_ASHR:
case OP_BC_AND:
case OP_BC_OR:
case OP_BC_XOR:
case OP_BC_ICMP_EQ:
case OP_BC_ICMP_NE:
case OP_BC_ICMP_UGT:
case OP_BC_ICMP_UGE:
case OP_BC_ICMP_ULT:
case OP_BC_ICMP_ULE:
case OP_BC_ICMP_SGT:
case OP_BC_ICMP_SGE:
case OP_BC_ICMP_SLT:
case OP_BC_ICMP_SLE:
case OP_BC_COPY:
case OP_BC_STORE: |
6922903a |
MAP(inst->u.binop[0]);
MAP(inst->u.binop[1]);
break; |
52dd3a6b |
case OP_BC_SEXT:
case OP_BC_ZEXT:
case OP_BC_TRUNC: |
6922903a |
MAP(inst->u.cast.source);
break; |
52dd3a6b |
case OP_BC_BRANCH: |
6922903a |
MAP(inst->u.branch.condition);
break; |
52dd3a6b |
case OP_BC_JMP: |
6922903a |
break; |
52dd3a6b |
case OP_BC_RET: |
6922903a |
MAP(inst->u.unaryop);
break; |
52dd3a6b |
case OP_BC_SELECT: |
6922903a |
MAP(inst->u.three[0]);
MAP(inst->u.three[1]);
MAP(inst->u.three[2]);
break; |
52dd3a6b |
case OP_BC_CALL_API:/* fall-through */
case OP_BC_CALL_DIRECT: |
6922903a |
{ |
cf0cd429 |
struct cli_bc_func *target = NULL; |
52dd3a6b |
if (inst->opcode == OP_BC_CALL_DIRECT) { |
cf0cd429 |
target = &bc->funcs[inst->u.ops.funcid];
if (inst->u.ops.funcid > bc->num_func) {
cli_errmsg("bytecode: called function out of range: %u > %u\n", inst->u.ops.funcid, bc->num_func); |
6a049897 |
ret = CL_EBYTECODE; |
cf0cd429 |
} |
7ef9aa8a |
else if (inst->u.ops.numOps != target->numArgs) { |
cf0cd429 |
cli_errmsg("bytecode: call operands don't match function prototype\n"); |
6a049897 |
ret = CL_EBYTECODE; |
cf0cd429 |
}
} else { |
f451cc9a |
/* APIs have at most 2 parameters always */ |
50829fbf |
if (inst->u.ops.numOps > 5) { |
cf0cd429 |
cli_errmsg("bytecode: call operands don't match function prototype\n"); |
6a049897 |
ret = CL_EBYTECODE; |
cf0cd429 |
} |
6922903a |
} |
7ef9aa8a |
if (ret != CL_SUCCESS)
break;
if (inst->u.ops.numOps > 0) { |
6922903a |
inst->u.ops.opsizes = cli_malloc(sizeof(*inst->u.ops.opsizes)*inst->u.ops.numOps);
if (!inst->u.ops.opsizes) {
cli_errmsg("Out of memory when allocating operand sizes\n"); |
6a049897 |
ret = CL_EMEM; |
7ef9aa8a |
break; |
6922903a |
} |
7ef9aa8a |
} else { |
36fab4bd |
inst->u.ops.opsizes = NULL; |
7ef9aa8a |
break;
} |
6922903a |
for (k=0;k<inst->u.ops.numOps;k++) { |
349e6e11 |
MAPPTR(inst->u.ops.ops[k]); |
52dd3a6b |
if (inst->opcode == OP_BC_CALL_DIRECT) |
cf0cd429 |
inst->u.ops.opsizes[k] = typesize(bc, target->types[k]);
else
inst->u.ops.opsizes[k] = 32; /*XXX*/ |
6922903a |
}
break;
} |
52dd3a6b |
case OP_BC_LOAD: |
0d9b99f4 |
MAPPTR(inst->u.unaryop); |
72617ba2 |
break; |
52dd3a6b |
case OP_BC_GEP1: |
a3a6b813 |
if (inst->u.three[1]&0x80000000 ||
bcfunc->types[inst->u.binop[1]]&0x8000) { |
44e13431 |
cli_errmsg("bytecode: gep1 of alloca is not allowed\n"); |
6a049897 |
ret = CL_EBYTECODE; |
44e13431 |
} |
6a049897 |
if (ret != CL_SUCCESS)
break; |
44e13431 |
MAP(inst->u.three[1]);
MAP(inst->u.three[2]);
inst->u.three[0] = get_geptypesize(bc, inst->u.three[0]); |
cd94be7a |
if ((int)(inst->u.three[0]) == -1) |
6a049897 |
ret = CL_EBYTECODE; |
44e13431 |
break; |
9463f9fd |
case OP_BC_GEPZ: |
c0afc517 |
/*three[0] is the type*/ |
a3a6b813 |
if (inst->u.three[1]&0x80000000 ||
bcfunc->types[inst->u.three[1]]&0x8000) |
4993d6cf |
inst->interp_op = 5*(inst->interp_op/5);
else
inst->interp_op = 5*(inst->interp_op/5)+3; |
7a14dc4c |
MAP(inst->u.three[1]); |
b1018ea5 |
if (calc_gepz(bc, bcfunc, inst->u.three[0], inst->u.three[2]) == -1) |
6a049897 |
ret = CL_EBYTECODE;
if (ret == CL_SUCCESS)
MAP(inst->u.three[2]); |
7a14dc4c |
break; |
b1018ea5 |
/* case OP_BC_GEPN:
*TODO
break;*/ |
f451cc9a |
case OP_BC_MEMSET:
case OP_BC_MEMCPY:
case OP_BC_MEMMOVE:
case OP_BC_MEMCMP: |
0d9b99f4 |
MAPPTR(inst->u.three[0]);
MAPPTR(inst->u.three[1]); |
5dfb4bf1 |
MAP(inst->u.three[2]); |
f451cc9a |
break; |
44e13431 |
case OP_BC_RET_VOID: |
f451cc9a |
case OP_BC_ISBIGENDIAN:
case OP_BC_ABORT: |
44e13431 |
/* no operands */ |
f451cc9a |
break;
case OP_BC_BSWAP16:
case OP_BC_BSWAP32:
case OP_BC_BSWAP64: |
6ea339ae |
MAP(inst->u.unaryop); |
f451cc9a |
break;
case OP_BC_PTRDIFF32: |
44e13431 |
MAPPTR(inst->u.binop[0]);
MAPPTR(inst->u.binop[1]);
break;
case OP_BC_PTRTOINT64:
MAPPTR(inst->u.unaryop); |
f451cc9a |
break; |
6922903a |
default: |
b1018ea5 |
cli_warnmsg("Bytecode: unhandled opcode: %d\n", inst->opcode); |
6a049897 |
ret = CL_EBYTECODE; |
6922903a |
}
} |
6a049897 |
if (map)
free(map); |
6922903a |
} |
20327f93 |
free(gmap); |
6922903a |
bc->state = bc_interp; |
6a049897 |
return ret; |
6922903a |
}
|
a5a19f45 |
static int add_selfcheck(struct cli_all_bc *bcs)
{
struct cli_bc_func *func;
struct cli_bc_inst *inst;
struct cli_bc *bc;
bcs->all_bcs = cli_realloc2(bcs->all_bcs, sizeof(*bcs->all_bcs)*(bcs->count+1));
if (!bcs->all_bcs) {
cli_errmsg("cli_loadcbc: Can't allocate memory for bytecode entry\n");
return CL_EMEM;
}
bc = &bcs->all_bcs[bcs->count++];
memset(bc, 0, sizeof(*bc));
bc->trusted = 1;
bc->num_globals = 1;
bc->globals = cli_calloc(1, sizeof(*bc->globals));
if (!bc->globals) {
cli_errmsg("Failed to allocate memory for globals\n");
return CL_EMEM;
}
bc->globals[0] = cli_calloc(1, sizeof(*bc->globals[0]));
if (!bc->globals[0]) {
cli_errmsg("Failed to allocate memory for globals\n");
return CL_EMEM;
}
bc->globaltys = cli_calloc(1, sizeof(*bc->globaltys));
if (!bc->globaltys) {
cli_errmsg("Failed to allocate memory for globaltypes\n");
return CL_EMEM;
}
bc->globaltys[0] = 32;
*bc->globals[0] = 0;
bc->id = ~0;
bc->kind = 0;
bc->num_types = 5;
bc->num_func = 1;
bc->funcs = cli_calloc(1, sizeof(*bc->funcs));
if (!bc->funcs) {
cli_errmsg("Failed to allocate memory for func\n");
return CL_EMEM;
}
func = bc->funcs;
func->numInsts = 2;
func->numLocals = 1;
func->numValues = 1;
func->numConstants = 1;
func->numBB = 1;
func->returnType = 32;
func->types = cli_calloc(1, sizeof(*func->types));
if (!func->types) {
cli_errmsg("Failed to allocate memory for types\n");
return CL_EMEM;
}
func->types[0] = 32;
func->BB = cli_calloc(1, sizeof(*func->BB));
if (!func->BB) {
cli_errmsg("Failed to allocate memory for BB\n");
return CL_EMEM;
}
func->allinsts = cli_calloc(2, sizeof(*func->allinsts));
if (!func->allinsts) {
cli_errmsg("Failed to allocate memory for insts\n");
return CL_EMEM;
}
func->BB->numInsts = 2;
func->BB->insts = func->allinsts;
func->constants = cli_calloc(1, sizeof(*func->constants));
if (!func->constants) {
cli_errmsg("Failed to allocate memory for constants\n");
return CL_EMEM;
}
func->constants[0] = 0xf00d;
inst = func->allinsts;
inst->opcode = OP_BC_CALL_API;
inst->u.ops.numOps = 1;
inst->u.ops.opsizes = NULL;
inst->u.ops.ops = cli_calloc(1, sizeof(*inst->u.ops.ops));
if (!inst->u.ops.ops) {
cli_errmsg("Failed to allocate memory for instructions\n");
return CL_EMEM;
}
inst->u.ops.ops[0] = 1;
inst->u.ops.funcid = 18; /* test2 */
inst->dest = 0;
inst->type = 32;
inst->interp_op = inst->opcode* 5 + 3;
inst = &func->allinsts[1];
inst->opcode = OP_BC_RET;
inst->type = 32;
inst->u.unaryop = 0;
inst->interp_op = inst->opcode* 5;
bc->state = bc_loaded;
return 0;
}
static int run_selfcheck(struct cli_all_bc *bcs)
{
struct cli_bc_ctx *ctx;
struct cli_bc *bc = &bcs->all_bcs[bcs->count-1];
int rc;
if (bc->state != bc_jit && bc->state != bc_interp) {
cli_errmsg("Failed to prepare selfcheck bytecode\n");
return CL_EBYTECODE;
}
ctx = cli_bytecode_context_alloc();
if (!ctx) {
cli_errmsg("Failed to allocate bytecode context\n");
return CL_EMEM;
}
cli_bytecode_context_setfuncid(ctx, bc, 0);
cli_dbgmsg("bytecode self test running\n"); |
71ca3536 |
ctx->bytecode_timeout = 0; |
a5a19f45 |
rc = cli_bytecode_run(bcs, bc, ctx);
cli_bytecode_context_destroy(ctx);
if (rc != CL_SUCCESS) {
cli_errmsg("bytecode self test failed: %s\n",
cl_strerror(rc));
} else {
cli_dbgmsg("bytecode self test succeeded\n");
}
return rc;
}
static int selfcheck(int jit, struct cli_bcengine *engine)
{
struct cli_all_bc bcs;
int rc;
memset(&bcs, 0, sizeof(bcs));
bcs.all_bcs = NULL;
bcs.count = 0;
bcs.engine = engine;
rc = add_selfcheck(&bcs);
if (rc == CL_SUCCESS) {
if (jit) {
if (!bcs.engine) {
cli_dbgmsg("bytecode: JIT disabled\n");
rc = CL_BREAK;/* no JIT - not fatal */
} else {
rc = cli_bytecode_prepare_jit(&bcs);
}
} else {
rc = cli_bytecode_prepare_interpreter(bcs.all_bcs);
}
if (rc == CL_SUCCESS)
rc = run_selfcheck(&bcs);
if (rc == CL_BREAK)
rc = CL_SUCCESS;
}
cli_bytecode_destroy(bcs.all_bcs);
free(bcs.all_bcs);
cli_bytecode_done_jit(&bcs, 1);
if (rc != CL_SUCCESS) {
cli_errmsg("Bytecode: failed to run selfcheck in %s mode: %s\n",
jit ? "JIT" : "interpreter", cl_strerror(rc));
}
return rc;
}
|
d5ffa2ac |
static int set_mode(struct cl_engine *engine, enum bytecode_mode mode)
{
if (engine->bytecode_mode == mode)
return 0;
if (engine->bytecode_mode == CL_BYTECODE_MODE_OFF) {
cli_errmsg("bytecode: already turned off, can't turn it on again!\n");
return -1;
}
cli_dbgmsg("Bytecode: mode changed to %d\n", mode);
if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST) { |
213dfdff |
if (mode == CL_BYTECODE_MODE_OFF || have_clamjit) {
cli_errmsg("bytecode: in test mode but JIT/bytecode is about to be disabled: %d\n", mode);
engine->bytecode_mode = mode;
return -1;
}
return 0; |
d5ffa2ac |
}
if (engine->bytecode_mode == CL_BYTECODE_MODE_JIT) {
cli_errmsg("bytecode: in JIT mode but JIT is about to be disabled: %d\n", mode);
engine->bytecode_mode = mode;
return -1;
}
engine->bytecode_mode = mode;
return 0;
}
/* runs the first bytecode of the specified kind, or the builtin one if no
* bytecode of that kind is loaded */
static int run_builtin_or_loaded(struct cli_all_bc *bcs, uint8_t kind, const char* builtin_cbc, struct cli_bc_ctx *ctx, const char *desc)
{
unsigned i, builtin = 0, rc = 0;
struct cli_bc *bc = NULL;
for (i=0;i<bcs->count;i++) {
bc = &bcs->all_bcs[i];
if (bc->kind == kind)
break;
}
if (i == bcs->count)
bc = NULL;
if (!bc) {
/* no loaded bytecode found, load the builtin one! */
struct cli_dbio dbio;
bc = cli_calloc(1, sizeof(*bc));
if (!bc) {
cli_errmsg("Out of memory allocating bytecode\n");
return CL_EMEM;
}
builtin = 1;
memset(&dbio, 0, sizeof(dbio));
dbio.usebuf = 1;
dbio.bufpt = dbio.buf = (char*)builtin_cbc;
dbio.bufsize = strlen(builtin_cbc)+1;
if (!dbio.bufsize || dbio.bufpt[dbio.bufsize-2] != '\n') {
cli_errmsg("Invalid builtin bytecode: missing terminator\n");
free(bc);
return CL_EMALFDB;
}
|
54402320 |
rc = cli_bytecode_load(bc, NULL, &dbio, 1, 0); |
d5ffa2ac |
if (rc) {
cli_errmsg("Failed to load builtin %s bytecode\n", desc);
free(bc);
return rc;
}
}
rc = cli_bytecode_prepare_interpreter(bc);
if (rc) {
cli_errmsg("Failed to prepare %s %s bytecode for interpreter: %s\n",
builtin ? "builtin" : "loaded", desc, cl_strerror(rc));
}
if (bc->state != bc_interp) {
cli_errmsg("Failed to prepare %s %s bytecode for interpreter\n",
builtin ? "builtin" : "loaded", desc);
rc = CL_EMALFDB;
}
if (!rc) {
cli_bytecode_context_setfuncid(ctx, bc, 0);
cli_dbgmsg("Bytecode: %s running (%s)\n", desc,
builtin ? "builtin" : "loaded");
rc = cli_bytecode_run(bcs, bc, ctx);
}
if (rc) {
cli_errmsg("Failed to execute %s %s bytecode: %s\n",builtin ? "builtin":"loaded",
desc, cl_strerror(rc));
}
if (builtin) {
cli_bytecode_destroy(bc);
free(bc);
}
return rc;
}
|
540fc128 |
int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsigned dconfmask) |
6922903a |
{ |
d5ffa2ac |
unsigned i, interp = 0, jitok = 0, jitcount=0; |
dc200c6b |
int rc; |
d5ffa2ac |
struct cli_bc_ctx *ctx; |
a5a19f45 |
|
7cbc72d3 |
if (!bcs->count) {
cli_dbgmsg("No bytecodes loaded, not running builtin test\n");
return CL_SUCCESS;
}
|
d4d4bce9 |
engine->bytecode_mode = CL_BYTECODE_MODE_AUTO; |
d5ffa2ac |
cli_detect_environment(&bcs->env);
switch (bcs->env.arch) {
case arch_i386:
case arch_x86_64:
if (!(dconfmask & BYTECODE_JIT_X86)) {
cli_dbgmsg("Bytecode: disabled on X86 via DCONF\n");
if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
return CL_EBYTECODE_TESTFAIL;
}
break;
case arch_ppc32:
case arch_ppc64:
if (!(dconfmask & BYTECODE_JIT_PPC)) {
cli_dbgmsg("Bytecode: disabled on PPC via DCONF\n");
if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
return CL_EBYTECODE_TESTFAIL;
}
break;
case arch_arm:
if (!(dconfmask & BYTECODE_JIT_ARM)) {
cli_dbgmsg("Bytecode: disabled on ARM via DCONF\n");
if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
return CL_EBYTECODE_TESTFAIL;
}
break;
default:
cli_dbgmsg("Bytecode: JIT not supported on this architecture, falling back\n");
if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
return CL_EBYTECODE_TESTFAIL;
break;
}
cli_dbgmsg("Bytecode: mode is %d\n", engine->bytecode_mode); |
a5a19f45 |
|
d5ffa2ac |
ctx = cli_bytecode_context_alloc();
if (!ctx) {
cli_errmsg("Bytecode: failed to allocate bytecode context\n");
return CL_EMEM;
} |
0d4c9946 |
rc = run_builtin_or_loaded(bcs, BC_STARTUP, builtin_bc_startup, ctx, "BC_STARTUP"); |
d5ffa2ac |
if (rc != CL_SUCCESS) { |
964a1e73 |
cli_warnmsg("Bytecode: BC_STARTUP failed to run, disabling ALL bytecodes! Please report to https://bugzilla.clamav.net\n"); |
d5ffa2ac |
ctx->bytecode_disable_status = 2;
} else {
cli_dbgmsg("Bytecode: disable status is %d\n", ctx->bytecode_disable_status);
rc = cli_bytecode_context_getresult_int(ctx); |
dbd3ed93 |
/* check magic number, don't use 0 here because it is too easy for a
* buggy bytecode to return 0 */ |
cd94be7a |
if ((unsigned int)rc != (unsigned int)0xda7aba5e) { |
964a1e73 |
cli_warnmsg("Bytecode: selftest failed with code %08x. Please report to https://bugzilla.clamav.net\n", |
d5ffa2ac |
rc);
if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
return CL_EBYTECODE_TESTFAIL;
}
}
switch (ctx->bytecode_disable_status) {
case 1:
if (set_mode(engine, CL_BYTECODE_MODE_INTERPRETER) == -1)
return CL_EBYTECODE_TESTFAIL;
break;
case 2:
if (set_mode(engine, CL_BYTECODE_MODE_OFF) == -1)
return CL_EBYTECODE_TESTFAIL;
break;
default:
break;
}
cli_bytecode_context_destroy(ctx);
|
d732b5aa |
|
d5ffa2ac |
if (engine->bytecode_mode != CL_BYTECODE_MODE_INTERPRETER &&
engine->bytecode_mode != CL_BYTECODE_MODE_OFF) { |
d732b5aa |
selfcheck(1, bcs->engine); |
d5ffa2ac |
rc = cli_bytecode_prepare_jit(bcs);
if (rc == CL_SUCCESS) {
jitok = 1;
cli_dbgmsg("Bytecode: %u bytecode prepared with JIT\n", bcs->count);
if (engine->bytecode_mode != CL_BYTECODE_MODE_TEST)
return CL_SUCCESS;
}
if (engine->bytecode_mode == CL_BYTECODE_MODE_JIT) {
cli_errmsg("Bytecode: JIT required, but not all bytecodes could be prepared with JIT\n");
return CL_EMALFDB;
} |
8a8dbd59 |
if (rc && engine->bytecode_mode == CL_BYTECODE_MODE_TEST) {
cli_errmsg("Bytecode: Test mode, but not all bytecodes could be prepared with JIT\n");
return CL_EBYTECODE_TESTFAIL;
} |
d5ffa2ac |
} else {
cli_bytecode_done_jit(bcs, 0);
}
if (!(dconfmask & BYTECODE_INTERPRETER)) {
cli_dbgmsg("Bytecode: needs interpreter, but interpreter is disabled\n");
if (set_mode(engine, CL_BYTECODE_MODE_OFF) == -1)
return CL_EBYTECODE_TESTFAIL;
}
if (engine->bytecode_mode == CL_BYTECODE_MODE_OFF) {
for (i=0;i<bcs->count;i++)
bcs->all_bcs[i].state = bc_disabled;
cli_dbgmsg("Bytecode: ALL bytecodes disabled\n"); |
d1487222 |
return CL_SUCCESS; |
e86fe7ea |
} |
d5ffa2ac |
|
d1487222 |
for (i=0;i<bcs->count;i++) {
struct cli_bc *bc = &bcs->all_bcs[i]; |
d5ffa2ac |
if (bc->state == bc_jit) {
jitcount++; |
8a8dbd59 |
if (engine->bytecode_mode != CL_BYTECODE_MODE_TEST)
continue; |
d5ffa2ac |
}
if (bc->state == bc_interp) {
interp++; |
6eeadbfe |
continue;
} |
d1487222 |
rc = cli_bytecode_prepare_interpreter(bc); |
d5ffa2ac |
if (rc != CL_SUCCESS) {
bc->state = bc_disabled;
cli_warnmsg("Bytecode: %d failed to prepare for interpreter mode\n", bc->id); |
d1487222 |
return rc; |
d5ffa2ac |
}
interp++; |
6922903a |
} |
e86fe7ea |
cli_dbgmsg("Bytecode: %u bytecode prepared with JIT, " |
8a8dbd59 |
"%u prepared with interpreter, %u total\n", jitcount, interp, bcs->count); |
d1487222 |
return CL_SUCCESS; |
6922903a |
}
|
927d0548 |
int cli_bytecode_init(struct cli_all_bc *allbc) |
6922903a |
{ |
927d0548 |
int ret; |
d1487222 |
memset(allbc, 0, sizeof(*allbc)); |
927d0548 |
ret = cli_bytecode_init_jit(allbc, 0/*XXX*/);
cli_dbgmsg("Bytecode initialized in %s mode\n",
allbc->engine ? "JIT" : "interpreter");
allbc->inited = 1;
return ret; |
d1487222 |
}
int cli_bytecode_done(struct cli_all_bc *allbc)
{ |
a5a19f45 |
return cli_bytecode_done_jit(allbc, 0); |
6922903a |
} |
3b33bd68 |
|
74b00233 |
int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map) |
a1781898 |
{ |
74b00233 |
ctx->fmap = map;
ctx->file_size = map->len + map->offset; |
0fa95ef2 |
ctx->hooks.filesize = &ctx->file_size; |
a1781898 |
return 0;
}
|
762d46e8 |
int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
const struct cli_all_bc *bcs, unsigned bc_idx, |
6ad45a29 |
const uint32_t* lsigcnt, |
762d46e8 |
const uint32_t *lsigsuboff, fmap_t *map) |
d38d6dad |
{
int ret;
struct cli_bc_ctx ctx; |
57f14280 |
const struct cli_bc *bc = &bcs->all_bcs[bc_idx-1]; |
762d46e8 |
struct cli_pe_hook_data pehookdata; |
f4e34215 |
|
60aad52f |
if (bc_idx == 0)
return CL_ENULLARG;
|
d38d6dad |
memset(&ctx, 0, sizeof(ctx));
cli_bytecode_context_setfuncid(&ctx, bc, 0); |
88815fd8 |
ctx.hooks.match_counts = lsigcnt; |
1c4683ac |
ctx.hooks.match_offsets = lsigsuboff; |
92a08a03 |
cli_bytecode_context_setctx(&ctx, cctx); |
74b00233 |
cli_bytecode_context_setfile(&ctx, map); |
762d46e8 |
if (tinfo && tinfo->status == 1) {
ctx.sections = tinfo->exeinfo.section;
memset(&pehookdata, 0, sizeof(pehookdata));
pehookdata.offset = tinfo->exeinfo.offset;
pehookdata.ep = tinfo->exeinfo.ep;
pehookdata.nsections = tinfo->exeinfo.nsections;
pehookdata.hdr_size = tinfo->exeinfo.hdr_size;
ctx.hooks.pedata = &pehookdata;
ctx.resaddr = tinfo->exeinfo.res_addr;
} |
4abbeb3a |
if (bc->hook_lsig_id) {
cli_dbgmsg("hook lsig id %d matched (bc %d)\n", bc->hook_lsig_id, bc->id);
/* this is a bytecode for a hook, defer running it until hook is
* executed, so that it has all the info for the hook */
if (cctx->hook_lsig_matches)
cli_bitset_set(cctx->hook_lsig_matches, bc->hook_lsig_id-1);
/* save match counts */
memcpy(&ctx.lsigcnt, lsigcnt, 64*4);
memcpy(&ctx.lsigoff, lsigsuboff, 64*4); |
f1ea72ae |
cli_bytecode_context_clear(&ctx); |
4abbeb3a |
return CL_SUCCESS;
} |
d38d6dad |
cli_dbgmsg("Running bytecode for logical signature match\n");
ret = cli_bytecode_run(bcs, bc, &ctx);
if (ret != CL_SUCCESS) { |
7cd9337a |
cli_warnmsg("Bytecode %u failed to run: %s\n", bc->id, cl_strerror(ret)); |
f1ea72ae |
cli_bytecode_context_clear(&ctx); |
d38d6dad |
return CL_SUCCESS;
}
if (ctx.virname) { |
d32e0396 |
if (cctx->num_viruses == 0) {
int rc;
cli_dbgmsg("Bytecode found virus: %s\n", ctx.virname);
if (!strncmp(ctx.virname, "BC.Heuristics", 13))
rc = cli_append_possibly_unwanted(cctx, ctx.virname);
else
rc = cli_append_virus(cctx, ctx.virname);
cli_bytecode_context_clear(&ctx);
return rc;
}
else {
return CL_VIRUS;
} |
d38d6dad |
}
ret = cli_bytecode_context_getresult_int(&ctx); |
ab636570 |
cli_dbgmsg("Bytecode %u returned code: %u\n", bc->id, ret); |
d38d6dad |
cli_bytecode_context_clear(&ctx);
return CL_SUCCESS;
} |
ab636570 |
|
f4e34215 |
int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx, |
6ad45a29 |
unsigned id, fmap_t *map) |
ab636570 |
{
const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
int ret; |
8a8dbd59 |
unsigned executed = 0, breakflag = 0, errorflag = 0; |
ab636570 |
|
6a049897 |
if (!cctx)
return CL_ENULLARG;
|
ab636570 |
cli_dbgmsg("Bytecode executing hook id %u (%u hooks)\n", id, hooks_cnt); |
70c222c9 |
/* restore match counts */ |
8a8dbd59 |
cli_bytecode_context_setfile(ctx, map); |
70c222c9 |
ctx->hooks.match_counts = ctx->lsigcnt;
ctx->hooks.match_offsets = ctx->lsigoff; |
ab636570 |
for (i=0;i < hooks_cnt;i++) {
const struct cli_bc *bc = &engine->bcs.all_bcs[hooks[i]]; |
f4e34215 |
if (bc->lsig) {
if (!cctx->hook_lsig_matches ||
!cli_bitset_test(cctx->hook_lsig_matches, bc->hook_lsig_id-1))
continue; |
57f14280 |
cli_dbgmsg("Bytecode: executing bytecode %u (lsig matched)\n" , bc->id); |
f4e34215 |
} |
ab636570 |
cli_bytecode_context_setfuncid(ctx, bc, 0);
ret = cli_bytecode_run(&engine->bcs, bc, ctx); |
e86fe7ea |
executed++; |
ab636570 |
if (ret != CL_SUCCESS) { |
8a8dbd59 |
cli_warnmsg("Bytecode %u failed to run: %s\n", bc->id, cl_strerror(ret));
errorflag = 1; |
e86fe7ea |
continue; |
ab636570 |
}
if (ctx->virname) { |
54402320 |
cli_dbgmsg("Bytecode runhook found virus: %s\n", ctx->virname); |
6ad45a29 |
cli_append_virus(cctx, ctx->virname); |
d7979d4f |
if (!(cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES)) { |
6ad45a29 |
cli_bytecode_context_clear(ctx);
return CL_VIRUS;
}
cli_bytecode_context_reset(ctx);
continue; |
ab636570 |
}
ret = cli_bytecode_context_getresult_int(ctx);
/* TODO: use prefix here */
cli_dbgmsg("Bytecode %u returned %u\n", bc->id, ret); |
f73212dc |
if (ret == 0xcea5e) {
cli_dbgmsg("Bytecode set BREAK flag in hook!\n");
breakflag = 1;
} |
3ae0a76d |
if (!ret) {
char *tempfile;
int fd = cli_bytecode_context_getresult_file(ctx, &tempfile); |
b28845c3 |
if (fd && fd != -1) { |
6a049897 |
if (cctx->engine->keeptmp) |
3ae0a76d |
cli_dbgmsg("Bytecode %u unpacked file saved in %s\n",
bc->id, tempfile);
else
cli_dbgmsg("Bytecode %u unpacked file\n", bc->id);
lseek(fd, 0, SEEK_SET);
cli_dbgmsg("***** Scanning unpacked file ******\n"); |
3d664817 |
cctx->recursion++; |
d39cb658 |
ret = cli_magic_scandesc(fd, tempfile, cctx); |
3d664817 |
cctx->recursion--; |
6a049897 |
if (!cctx->engine->keeptmp) |
d0934caf |
if (ftruncate(fd, 0) == -1) |
b28845c3 |
cli_dbgmsg("ftruncate failed on %d\n", fd); |
3ae0a76d |
close(fd); |
6a049897 |
if (!cctx->engine->keeptmp) { |
b28845c3 |
if (tempfile && cli_unlink(tempfile)) |
3ae0a76d |
ret = CL_EUNLINK;
}
free(tempfile);
if (ret != CL_CLEAN) { |
6ad45a29 |
if (ret == CL_VIRUS) { |
3ae0a76d |
cli_dbgmsg("Scanning unpacked file by bytecode %u found a virus\n", bc->id); |
d7979d4f |
if (cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES) { |
6ad45a29 |
cli_bytecode_context_reset(ctx);
continue;
}
cli_bytecode_context_clear(ctx);
return ret;
} |
3ae0a76d |
}
cli_bytecode_context_reset(ctx);
continue;
}
} |
ab636570 |
cli_bytecode_context_reset(ctx);
} |
e86fe7ea |
if (executed)
cli_dbgmsg("Bytecode: executed %u bytecodes for this hook\n", executed);
else
cli_dbgmsg("Bytecode: no logical signature matched, no bytecode executed\n"); |
6a049897 |
if (errorflag && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST) |
8a8dbd59 |
return CL_EBYTECODE_TESTFAIL; |
f73212dc |
return breakflag ? CL_BREAK : CL_CLEAN; |
ab636570 |
}
|
236fb136 |
int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections) |
ab636570 |
{ |
236fb136 |
ctx->sections = sections; |
ab636570 |
ctx->hooks.pedata = data;
return 0;
} |
3ae0a76d |
void cli_bytecode_context_setctx(struct cli_bc_ctx *ctx, void *cctx)
{
ctx->ctx = cctx; |
b63681a5 |
ctx->bytecode_timeout = ((cli_ctx*)cctx)->engine->bytecode_timeout; |
3ae0a76d |
} |
a35cfe51 |
void cli_bytecode_describe(const struct cli_bc *bc)
{
char buf[128];
int cols;
unsigned i;
time_t stamp;
int had;
if (!bc) {
printf("(null bytecode)\n");
return;
}
stamp = bc->metadata.timestamp; |
3735fda1 |
printf("Bytecode format functionality level: %u\n", bc->metadata.formatlevel); |
a35cfe51 |
printf("Bytecode metadata:\n\tcompiler version: %s\n",
bc->metadata.compiler ? bc->metadata.compiler : "N/A"); |
d5ffa2ac |
printf("\tcompiled on: (%d) %s", |
88d54dcb |
(uint32_t)stamp, |
a35cfe51 |
cli_ctime(&stamp, buf, sizeof(buf)));
printf("\tcompiled by: %s\n", bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A"); |
c0afc517 |
/*TODO: parse and display arch name, also take it into account when
JITing*/ |
a35cfe51 |
printf("\ttarget exclude: %d\n", bc->metadata.targetExclude);
printf("\tbytecode type: ");
switch (bc->kind) {
case BC_GENERIC:
puts("generic, not loadable by clamscan/clamd");
break; |
d5ffa2ac |
case BC_STARTUP:
puts("run on startup (unique)");
break; |
a35cfe51 |
case BC_LOGICAL:
puts("logical only");
break;
case BC_PE_UNPACKER: |
fe54f710 |
puts("PE unpacker hook");
break;
case BC_PE_ALL:
puts("all PE hook");
break;
case BC_PRECLASS:
puts("preclass hook"); |
a35cfe51 |
break;
default:
printf("Unknown (type %u)", bc->kind);
break;
} |
3735fda1 |
/* 0 means no limit */
printf("\tbytecode functionality level: %u - %u\n",
bc->metadata.minfunc, bc->metadata.maxfunc); |
a35cfe51 |
printf("\tbytecode logical signature: %s\n",
bc->lsig ? bc->lsig : "<none>");
printf("\tvirusname prefix: %s\n",
bc->vnameprefix);
printf("\tvirusnames: %u\n", bc->vnames_cnt);
printf("\tbytecode triggered on: ");
switch (bc->kind) {
case BC_GENERIC:
puts("N/A (loaded in clambc only)");
break;
case BC_LOGICAL:
puts("files matching logical signature");
break;
case BC_PE_UNPACKER:
if (bc->lsig) |
4abbeb3a |
puts("PE files matching logical signature (unpacked)");
else
puts("all PE files! (unpacked)");
break;
case BC_PDF:
puts("PDF files");
break;
case BC_PE_ALL:
if (bc->lsig) |
a35cfe51 |
puts("PE files matching logical signature");
else
puts("all PE files!");
break; |
47c2d618 |
case BC_PRECLASS:
if (bc->lsig)
puts("PRECLASS files matching logical signature");
else
puts("all PRECLASS files!");
break; |
a35cfe51 |
default:
puts("N/A (unknown type)\n");
break;
}
printf("\tnumber of functions: %u\n\tnumber of types: %u\n",
bc->num_func, bc->num_types); |
5297d09e |
printf("\tnumber of global constants: %u\n", (unsigned)bc->num_globals); |
a35cfe51 |
printf("\tnumber of debug nodes: %u\n", bc->dbgnode_cnt);
printf("\tbytecode APIs used:");
cols = 0; /* remaining */
had = 0;
for (i=0;i<cli_apicall_maxapi;i++) {
if (cli_bitset_test(bc->uses_apis, i)) {
unsigned len = strlen(cli_apicalls[i].name);
if (had)
printf(","); |
cd94be7a |
if (len > (unsigned int)cols) { |
a35cfe51 |
printf("\n\t");
cols = 72;
}
printf(" %s", cli_apicalls[i].name);
had = 1;
cols -= len;
}
}
printf("\n");
} |
0ff13b31 |
const char *bc_tystr[] = {
"DFunctionType",
"DPointerType",
"DStructType",
"DPackedStructType",
"DArrayType"
};
const char *bc_opstr[] = {
"OP_BC_NULL",
"OP_BC_ADD", /* =1*/
"OP_BC_SUB",
"OP_BC_MUL",
"OP_BC_UDIV",
"OP_BC_SDIV",
"OP_BC_UREM",
"OP_BC_SREM",
"OP_BC_SHL",
"OP_BC_LSHR",
"OP_BC_ASHR",
"OP_BC_AND",
"OP_BC_OR",
"OP_BC_XOR",
"OP_BC_TRUNC",
"OP_BC_SEXT",
"OP_BC_ZEXT",
"OP_BC_BRANCH",
"OP_BC_JMP",
"OP_BC_RET",
"OP_BC_RET_VOID",
"OP_BC_ICMP_EQ",
"OP_BC_ICMP_NE",
"OP_BC_ICMP_UGT",
"OP_BC_ICMP_UGE",
"OP_BC_ICMP_ULT",
"OP_BC_ICMP_ULE",
"OP_BC_ICMP_SGT",
"OP_BC_ICMP_SGE",
"OP_BC_ICMP_SLE",
"OP_BC_ICMP_SLT",
"OP_BC_SELECT",
"OP_BC_CALL_DIRECT",
"OP_BC_CALL_API",
"OP_BC_COPY",
"OP_BC_GEP1",
"OP_BC_GEPZ",
"OP_BC_GEPN",
"OP_BC_STORE",
"OP_BC_LOAD",
"OP_BC_MEMSET",
"OP_BC_MEMCPY",
"OP_BC_MEMMOVE",
"OP_BC_MEMCMP",
"OP_BC_ISBIGENDIAN",
"OP_BC_ABORT",
"OP_BC_BSWAP16",
"OP_BC_BSWAP32",
"OP_BC_BSWAP64",
"OP_BC_PTRDIFF32",
"OP_BC_PTRTOINT64",
"OP_BC_INVALID" /* last */
};
extern unsigned cli_numapicalls;
static void cli_bytetype_helper(const struct cli_bc *bc, unsigned tid)
{
unsigned i, j; |
0b28c748 |
const struct cli_bc_type *ty; |
0ff13b31 |
if (tid & 0x8000) {
printf("alloc ");
tid &= 0x7fff;
}
if (tid < 65) {
printf("i%d", tid);
return;
}
i = tid - 65; |
7c9c4fab |
if (i >= bc->num_types) { |
7cd9337a |
printf("invalid type"); |
7c9c4fab |
return;
} |
0b28c748 |
ty = &bc->types[i]; |
0ff13b31 |
switch (ty->kind) {
case DFunctionType:
cli_bytetype_helper(bc, ty->containedTypes[0]);
printf(" func ( ");
for (j = 1; j < ty->numElements; ++j) {
cli_bytetype_helper(bc, ty->containedTypes[0]);
printf(" ");
}
printf(")");
break;
case DPointerType:
cli_bytetype_helper(bc, ty->containedTypes[0]);
printf("*");
break;
case DStructType:
case DPackedStructType:
printf("{ ");
for (j = 0; j < ty->numElements; ++j) {
cli_bytetype_helper(bc, ty->containedTypes[0]);
printf(" ");
}
printf("}");
break;
case DArrayType:
printf("[");
printf("%d x ", ty->numElements);
cli_bytetype_helper(bc, ty->containedTypes[0]);
printf("]");
break;
default:
printf("unhandled type kind %d, cannot parse", ty->kind);
break;
}
}
void cli_bytetype_describe(const struct cli_bc *bc)
{
unsigned i, tid;
printf("found %d extra types of %d total, starting at tid %d\n",
bc->num_types, 64+bc->num_types, bc->start_tid);
printf("TID KIND INTERNAL\n");
printf("------------------------------------------------------------------------\n");
for (i = 0, tid = 65; i < bc->num_types-1; ++i, ++tid) {
printf("%3d: %-20s", tid, bc_tystr[bc->types[i].kind]);
cli_bytetype_helper(bc, tid);
printf("\n");
}
printf("------------------------------------------------------------------------\n");
}
void cli_bytevalue_describe(const struct cli_bc *bc, unsigned funcid)
{ |
c9a070c9 |
unsigned i, total = 0; |
0ff13b31 |
const struct cli_bc_func *func;
if (funcid >= bc->num_func) { |
7cd9337a |
printf("bytecode diagnostic: funcid [%u] outside bytecode numfuncs [%u]\n", |
0ff13b31 |
funcid, bc->num_func);
return;
}
// globals |
fd43d610 |
printf("found a total of %zu globals\n", bc->num_globals); |
0ff13b31 |
printf("GID ID VALUE\n");
printf("------------------------------------------------------------------------\n");
for (i = 0; i < bc->num_globals; ++i) {
printf("%3u [%3u]: ", i, i);
cli_bytetype_helper(bc, bc->globaltys[i]);
printf(" unknown\n");
}
printf("------------------------------------------------------------------------\n");
// arguments and local values
func = &bc->funcs[funcid];
printf("found %d values with %d arguments and %d locals\n",
func->numValues, func->numArgs, func->numLocals);
printf("VID ID VALUE\n");
printf("------------------------------------------------------------------------\n");
for (i = 0; i < func->numValues; ++i) {
printf("%3u [%3u]: ", i, total++);
cli_bytetype_helper(bc, func->types[i]);
if (i < func->numArgs)
printf("argument");
printf("\n");
}
printf("------------------------------------------------------------------------\n");
// constants
printf("found a total of %d constants\n", func->numConstants);
printf("CID ID VALUE\n");
printf("------------------------------------------------------------------------\n");
for (i = 0; i < func->numConstants; ++i) { |
653b471b |
printf("%3u [%3u]: " STDu64 "(0x" STDx64 ")\n", i, total++, func->constants[i], func->constants[i]); |
0ff13b31 |
}
printf("------------------------------------------------------------------------\n");
printf("found a total of %u total values\n", total);
printf("------------------------------------------------------------------------\n");
return;
}
void cli_byteinst_describe(const struct cli_bc_inst *inst, unsigned *bbnum)
{
unsigned j;
char inst_str[256]; |
5b5be2a6 |
const struct cli_apicall *api; |
0ff13b31 |
if (inst->opcode > OP_BC_INVALID) {
printf("opcode %u[%u] of type %u is not implemented yet!",
inst->opcode, inst->interp_op/5, inst->interp_op%5);
return;
}
|
7c9c4fab |
snprintf(inst_str, sizeof(inst_str), "%-20s[%-3d/%3d/%3d]", bc_opstr[inst->opcode], |
0ff13b31 |
inst->opcode, inst->interp_op, inst->interp_op%inst->opcode);
printf("%-35s", inst_str);
switch (inst->opcode) {
// binary operations
case OP_BC_ADD:
printf("%d = %d + %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_SUB:
printf("%d = %d - %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_MUL:
printf("%d = %d * %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_UDIV:
printf("%d = %d / %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_SDIV:
printf("%d = %d / %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_UREM:
printf("%d = %d %% %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_SREM:
printf("%d = %d %% %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_SHL:
printf("%d = %d << %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_LSHR:
printf("%d = %d >> %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ASHR:
printf("%d = %d >> %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_AND:
printf("%d = %d & %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_OR:
printf("%d = %d | %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_XOR:
printf("%d = %d ^ %d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
// casting operations
case OP_BC_TRUNC: |
653b471b |
printf("%d = %d trunc " STDx64, inst->dest, inst->u.cast.source, inst->u.cast.mask); |
0ff13b31 |
break;
case OP_BC_SEXT: |
653b471b |
printf("%d = %d sext " STDx64, inst->dest, inst->u.cast.source, inst->u.cast.mask); |
0ff13b31 |
break;
case OP_BC_ZEXT: |
653b471b |
printf("%d = %d zext " STDx64, inst->dest, inst->u.cast.source, inst->u.cast.mask); |
0ff13b31 |
break;
// control operations (termination instructions)
case OP_BC_BRANCH:
printf("br %d ? bb.%d : bb.%d", inst->u.branch.condition,
inst->u.branch.br_true, inst->u.branch.br_false);
(*bbnum)++;
break;
case OP_BC_JMP:
printf("jmp bb.%d", inst->u.jump);
(*bbnum)++;
break;
case OP_BC_RET:
printf("ret %d", inst->u.unaryop);
(*bbnum)++;
break;
case OP_BC_RET_VOID:
printf("ret void");
(*bbnum)++;
break;
// comparison operations
case OP_BC_ICMP_EQ:
printf("%d = (%d == %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_NE:
printf("%d = (%d != %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_UGT:
printf("%d = (%d > %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_UGE:
printf("%d = (%d >= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_ULT:
printf("%d = (%d > %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_ULE:
printf("%d = (%d >= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_SGT:
printf("%d = (%d > %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_SGE:
printf("%d = (%d >= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_SLE:
printf("%d = (%d <= %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_ICMP_SLT:
printf("%d = (%d < %d)", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_SELECT:
printf("%d = %d ? %d : %d)", inst->dest, inst->u.three[0],
inst->u.three[1], inst->u.three[2]);
break;
// function calling
case OP_BC_CALL_DIRECT:
printf("%d = call F.%d (", inst->dest, inst->u.ops.funcid);
for (j = 0; j < inst->u.ops.numOps; ++j) {
if (j == inst->u.ops.numOps-1) {
printf("%d", inst->u.ops.ops[j]);
}
else {
printf("%d, ", inst->u.ops.ops[j]);
}
}
printf(")");
break;
case OP_BC_CALL_API:
{
if (inst->u.ops.funcid > cli_numapicalls) {
printf("apicall FID %d not yet implemented!\n", inst->u.ops.funcid);
break;
} |
5b5be2a6 |
api = &cli_apicalls[inst->u.ops.funcid]; |
0ff13b31 |
switch (api->kind) {
case 0:
printf("%d = %s[%d] (%d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1]);
break;
case 1:
printf("%d = %s[%d] (p.%d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1]);
break;
case 2:
printf("%d = %s[%d] (%d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0]);
break;
case 3:
printf("p.%d = %s[%d] (%d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0]);
break;
case 4:
printf("%d = %s[%d] (p.%d, %d, %d, %d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
inst->u.ops.ops[2], inst->u.ops.ops[3], inst->u.ops.ops[4]);
break;
case 5:
printf("%d = %s[%d] ()", inst->dest, api->name,
inst->u.ops.funcid);
break;
case 6:
printf("p.%d = %s[%d] (%d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1]);
break;
case 7:
printf("%d = %s[%d] (%d, %d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
inst->u.ops.ops[2]);
break;
case 8:
printf("%d = %s[%d] (p.%d, %d, p.%d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
inst->u.ops.ops[2], inst->u.ops.ops[3]);
break;
case 9:
printf("%d = %s[%d] (p.%d, %d, %d)", inst->dest, api->name,
inst->u.ops.funcid, inst->u.ops.ops[0], inst->u.ops.ops[1],
inst->u.ops.ops[2]);
break;
default:
printf("type %u apicalls not yet implemented!\n", api->kind);
break;
}
}
break;
// memory operations
case OP_BC_COPY:
printf("cp %d -> %d", inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_GEP1:
printf("%d = gep1 p.%d + (%d * %d)", inst->dest, inst->u.three[1],
inst->u.three[2], inst->u.three[0]);
break;
case OP_BC_GEPZ:
printf("%d = gepz p.%d + (%d)", inst->dest,
inst->u.three[1], inst->u.three[2]);
break;
case OP_BC_GEPN:
printf("illegal opcode, impossible");
break;
case OP_BC_STORE:
printf("store %d -> p.%d", inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_LOAD:
printf("load %d <- p.%d", inst->dest, inst->u.unaryop);
break;
|
7cd9337a |
// llvm intrinsics |
0ff13b31 |
case OP_BC_MEMSET:
printf("%d = memset (p.%d, %d, %d)", inst->dest, inst->u.three[0],
inst->u.three[1], inst->u.three[2]);
break;
case OP_BC_MEMCPY:
printf("%d = memcpy (p.%d, p.%d, %d)", inst->dest, inst->u.three[0],
inst->u.three[1], inst->u.three[2]);
break;
case OP_BC_MEMMOVE:
printf("%d = memmove (p.%d, p.%d, %d)", inst->dest, inst->u.three[0],
inst->u.three[1], inst->u.three[2]);
break;
case OP_BC_MEMCMP:
printf("%d = memcmp (p.%d, p.%d, %d)", inst->dest, inst->u.three[0],
inst->u.three[1], inst->u.three[2]);
break;
// utility operations
case OP_BC_ISBIGENDIAN:
printf("%d = isbigendian()", inst->dest);
break;
case OP_BC_ABORT:
printf("ABORT!!");
break;
case OP_BC_BSWAP16:
printf("%d = bswap16 %d", inst->dest, inst->u.unaryop);
break;
case OP_BC_BSWAP32:
printf("%d = bswap32 %d", inst->dest, inst->u.unaryop);
break;
case OP_BC_BSWAP64:
printf("%d = bswap64 %d", inst->dest, inst->u.unaryop);
break;
case OP_BC_PTRDIFF32:
printf("%d = ptrdiff32 p.%d p.%d", inst->dest, inst->u.binop[0], inst->u.binop[1]);
break;
case OP_BC_PTRTOINT64:
printf("%d = ptrtoint64 p.%d", inst->dest, inst->u.unaryop);
break;
case OP_BC_INVALID: /* last */
printf("INVALID!!");
break;
default:
// redundant check
printf("opcode %u[%u] of type %u is not implemented yet!",
inst->opcode, inst->interp_op/5, inst->interp_op%5);
break;
}
}
void cli_bytefunc_describe(const struct cli_bc *bc, unsigned funcid)
{
unsigned i, bbnum, bbpre;
const struct cli_bc_func *func;
if (funcid >= bc->num_func) { |
7cd9337a |
printf("bytecode diagnostic: funcid [%u] outside bytecode numfuncs [%u]\n", |
0ff13b31 |
funcid, bc->num_func);
return;
}
func = &bc->funcs[funcid];
printf("FUNCTION ID: F.%d -> NUMINSTS %d\n", funcid, func->numInsts);
printf("BB IDX OPCODE [ID /IID/MOD] INST\n");
printf("------------------------------------------------------------------------\n");
bbpre = 0; bbnum = 0;
for (i = 0; i < func->numInsts; ++i) {
if (bbpre != bbnum) {
printf("\n");
bbpre = bbnum;
}
printf("%3d %3d ", bbnum, i);
cli_byteinst_describe(&func->allinsts[i], &bbnum);
printf("\n");
}
printf("------------------------------------------------------------------------\n");
} |