Initial support for loading .cbc files from DB.

0.96
Török Edvin 16 years ago
parent dcee45cce2
commit 52dd3a6bda
  1. 3
      clamd/clamd.c
  2. 3
      clamscan/manager.c
  3. 128
      libclamav/bytecode.c
  4. 2
      libclamav/bytecode_api_decl.c
  5. 124
      libclamav/bytecode_vm.c
  6. 103
      libclamav/c++/bytecode2llvm.cpp
  7. 3
      libclamav/clamav.h
  8. 84
      libclamav/clambc.h
  9. 28
      libclamav/dconf.c
  10. 9
      libclamav/dconf.h
  11. 4
      libclamav/others.h
  12. 55
      libclamav/readdb.c
  13. 1
      libclamav/readdb.h
  14. 2
      shared/optparser.c
  15. 8
      unit_tests/check_bytecode.c

@ -400,6 +400,9 @@ int main(int argc, char **argv)
else
logg("#Not loading phishing signatures.\n");
if(optget(opts,"Bytecode")->enabled)
dboptions |= CL_DB_BYTECODE;
if(optget(opts,"PhishingScanURLs")->enabled)
dboptions |= CL_DB_PHISHING_URLS;
else

@ -352,6 +352,9 @@ int scanmanager(const struct optstruct *opts)
if(optget(opts,"phishing-scan-urls")->enabled)
dboptions |= CL_DB_PHISHING_URLS;
if(optget(opts,"bytecode")->enabled)
dboptions |= CL_DB_BYTECODE;
if((ret = cl_init(CL_INIT_DEFAULT))) {
logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
return 50;

@ -420,7 +420,7 @@ static int parseLSig(struct cli_bc *bc, unsigned char *buffer)
return CL_EMALFDB;
}
bc->lsig = NULL;
bc->lsig = cli_strdup(buffer[1]);
bc->lsig = cli_strdup(buffer + 1);
return CL_SUCCESS;
}
@ -890,26 +890,26 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
cli_errmsg("Invalid type or operand\n");
return CL_EMALFDB;
}
if (inst.opcode >= OP_INVALID) {
if (inst.opcode >= OP_BC_INVALID) {
cli_errmsg("Invalid opcode: %u\n", inst.opcode);
return CL_EMALFDB;
}
switch (inst.opcode) {
case OP_JMP:
case OP_BC_JMP:
inst.u.jump = readBBID(bcfunc, buffer, &offset, len, &ok);
break;
case OP_RET:
case OP_BC_RET:
inst.type = readNumber(buffer, &offset, len, &ok);
inst.u.unaryop = readOperand(bcfunc, buffer, &offset, len, &ok);
break;
case OP_BRANCH:
case OP_BC_BRANCH:
inst.u.branch.condition = readOperand(bcfunc, buffer, &offset, len, &ok);
inst.u.branch.br_true = readBBID(bcfunc, buffer, &offset, len, &ok);
inst.u.branch.br_false = readBBID(bcfunc, buffer, &offset, len, &ok);
break;
case OP_CALL_API:/* fall-through */
case OP_CALL_DIRECT:
case OP_BC_CALL_API:/* fall-through */
case OP_BC_CALL_DIRECT:
numOp = readFixedNumber(buffer, &offset, len, &ok, 1);
if (ok) {
inst.u.ops.numOps = numOp;
@ -919,7 +919,7 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
cli_errmsg("Out of memory allocating operands\n");
return CL_EMALFDB;
}
if (inst.opcode == OP_CALL_DIRECT)
if (inst.opcode == OP_BC_CALL_DIRECT)
inst.u.ops.funcid = readFuncID(bc, buffer, &offset, len, &ok);
else
inst.u.ops.funcid = readAPIFuncID(bc, buffer, &offset, len, &ok);
@ -928,9 +928,9 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
}
}
break;
case OP_ZEXT:
case OP_SEXT:
case OP_TRUNC:
case OP_BC_ZEXT:
case OP_BC_SEXT:
case OP_BC_TRUNC:
inst.u.cast.source = readOperand(bcfunc, buffer, &offset, len, &ok);
inst.u.cast.mask = bcfunc->types[inst.u.cast.source];
if (inst.u.cast.mask == 1)
@ -944,21 +944,21 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
else if (inst.u.cast.mask <= 64)
inst.u.cast.size = 4;
/* calculate mask */
if (inst.opcode != OP_SEXT)
if (inst.opcode != OP_BC_SEXT)
inst.u.cast.mask = inst.u.cast.mask != 64 ?
(1ull<<inst.u.cast.mask)-1 :
~0ull;
break;
case OP_ICMP_EQ:
case OP_ICMP_NE:
case OP_ICMP_UGT:
case OP_ICMP_UGE:
case OP_ICMP_ULT:
case OP_ICMP_ULE:
case OP_ICMP_SGT:
case OP_ICMP_SGE:
case OP_ICMP_SLE:
case OP_ICMP_SLT:
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:
/* instruction type must be correct before readOperand! */
inst.type = readNumber(buffer, &offset, len, &ok);
/* fall-through */
@ -1160,8 +1160,8 @@ int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, stru
func.numBytes = ctx->bytes;
memset(ctx->values+ctx->bytes-8, 0, 8);
inst.opcode = OP_CALL_DIRECT;
inst.interp_op = OP_CALL_DIRECT*5;
inst.opcode = OP_BC_CALL_DIRECT;
inst.interp_op = OP_BC_CALL_DIRECT*5;
inst.dest = func.numArgs;
inst.type = 0;
inst.u.ops.numOps = ctx->numParams;
@ -1194,7 +1194,7 @@ void cli_bytecode_destroy(struct cli_bc *bc)
for(k=0;k<BB->numInsts;k++) {
struct cli_bc_inst *ii = &BB->insts[k];
if (operand_counts[ii->opcode] > 3 ||
ii->opcode == OP_CALL_DIRECT || ii->opcode == OP_CALL_API) {
ii->opcode == OP_BC_CALL_DIRECT || ii->opcode == OP_BC_CALL_API) {
free(ii->u.ops.ops);
free(ii->u.ops.opsizes);
}
@ -1251,57 +1251,57 @@ static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
struct cli_bc_inst *inst = &bcfunc->allinsts[j];
inst->dest = map[inst->dest];
switch (inst->opcode) {
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_UDIV:
case OP_SDIV:
case OP_UREM:
case OP_SREM:
case OP_SHL:
case OP_LSHR:
case OP_ASHR:
case OP_AND:
case OP_OR:
case OP_XOR:
case OP_ICMP_EQ:
case OP_ICMP_NE:
case OP_ICMP_UGT:
case OP_ICMP_UGE:
case OP_ICMP_ULT:
case OP_ICMP_ULE:
case OP_ICMP_SGT:
case OP_ICMP_SGE:
case OP_ICMP_SLT:
case OP_ICMP_SLE:
case OP_COPY:
case OP_STORE:
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:
MAP(inst->u.binop[0]);
MAP(inst->u.binop[1]);
break;
case OP_SEXT:
case OP_ZEXT:
case OP_TRUNC:
case OP_BC_SEXT:
case OP_BC_ZEXT:
case OP_BC_TRUNC:
MAP(inst->u.cast.source);
break;
case OP_BRANCH:
case OP_BC_BRANCH:
MAP(inst->u.branch.condition);
break;
case OP_JMP:
case OP_BC_JMP:
break;
case OP_RET:
case OP_BC_RET:
MAP(inst->u.unaryop);
break;
case OP_SELECT:
case OP_BC_SELECT:
MAP(inst->u.three[0]);
MAP(inst->u.three[1]);
MAP(inst->u.three[2]);
break;
case OP_CALL_API:/* fall-through */
case OP_CALL_DIRECT:
case OP_BC_CALL_API:/* fall-through */
case OP_BC_CALL_DIRECT:
{
struct cli_bc_func *target = NULL;
if (inst->opcode == OP_CALL_DIRECT) {
if (inst->opcode == OP_BC_CALL_DIRECT) {
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);
@ -1328,21 +1328,21 @@ static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
inst->u.ops.opsizes = NULL;
for (k=0;k<inst->u.ops.numOps;k++) {
MAP(inst->u.ops.ops[k]);
if (inst->opcode == OP_CALL_DIRECT)
if (inst->opcode == OP_BC_CALL_DIRECT)
inst->u.ops.opsizes[k] = typesize(bc, target->types[k]);
else
inst->u.ops.opsizes[k] = 32; /*XXX*/
}
break;
}
case OP_LOAD:
case OP_BC_LOAD:
MAP(inst->u.unaryop);
break;
case OP_GEP1:
case OP_BC_GEP1:
MAP(inst->u.binop[0]);
MAP(inst->u.binop[1]);
break;
case OP_GEP2:
case OP_BC_GEP2:
MAP(inst->u.three[0]);
MAP(inst->u.three[1]);
MAP(inst->u.three[2]);

@ -28,7 +28,7 @@ uint32_t cli_bcapi_test0(struct cli_bc_ctx *ctx, struct foo*, uint32_t);
uint32_t cli_bcapi_test1(struct cli_bc_ctx *ctx, uint32_t, uint32_t);
int32_t cli_bcapi_read(struct cli_bc_ctx *ctx, uint8_t*, int32_t);
int32_t cli_bcapi_seek(struct cli_bc_ctx *ctx, int32_t, uint32_t);
uint32_t cli_bcapi_debug_print_str(struct cli_bc_ctx *ctx, const uint8_t*, uint32_t);
uint32_t cli_bcapi_debug_print_str(struct cli_bc_ctx *ctx, const const uint8_t*, uint32_t);
uint32_t cli_bcapi_debug_print_uint(struct cli_bc_ctx *ctx, uint32_t, uint32_t);
static uint16_t cli_tmp0[]={32, 70, 32};

@ -298,7 +298,7 @@ static always_inline struct stack_entry *pop_stack(struct stack *stack,
#define BINOP(i) inst->u.binop[i]
#define DEFINE_BINOP_HELPER(opc, OP, W0, W1, W2, W3, W4) \
#define DEFINE_BINOP_BC_HELPER(opc, OP, W0, W1, W2, W3, W4) \
case opc*5: {\
uint8_t op0, op1, res;\
int8_t sop0, sop1;\
@ -350,8 +350,8 @@ static always_inline struct stack_entry *pop_stack(struct stack *stack,
break;\
}
#define DEFINE_BINOP(opc, OP) DEFINE_BINOP_HELPER(opc, OP, WRITE8, WRITE8, WRITE16, WRITE32, WRITE64)
#define DEFINE_ICMPOP(opc, OP) DEFINE_BINOP_HELPER(opc, OP, WRITE8, WRITE8, WRITE8, WRITE8, WRITE8)
#define DEFINE_BINOP(opc, OP) DEFINE_BINOP_BC_HELPER(opc, OP, WRITE8, WRITE8, WRITE16, WRITE32, WRITE64)
#define DEFINE_ICMPOP(opc, OP) DEFINE_BINOP_BC_HELPER(opc, OP, WRITE8, WRITE8, WRITE8, WRITE8, WRITE8)
#define CHECK_OP(cond, msg) if((cond)) { cli_dbgmsg(msg); stop = CL_EBYTECODE; break;}
@ -409,7 +409,7 @@ static always_inline struct stack_entry *pop_stack(struct stack *stack,
default: CHECK_UNREACHABLE;\
}
#define DEFINE_OP_RET_N(OP, T, R0, W0) \
#define DEFINE_OP_BC_RET_N(OP, T, R0, W0) \
case OP: {\
T tmp;\
R0(tmp, inst->u.unaryop);\
@ -447,77 +447,77 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
do {
pc++;
switch (inst->interp_op) {
DEFINE_BINOP(OP_ADD, res = op0 + op1);
DEFINE_BINOP(OP_SUB, res = op0 - op1);
DEFINE_BINOP(OP_MUL, res = op0 * op1);
DEFINE_BINOP(OP_BC_ADD, res = op0 + op1);
DEFINE_BINOP(OP_BC_SUB, res = op0 - op1);
DEFINE_BINOP(OP_BC_MUL, res = op0 * op1);
DEFINE_BINOP(OP_UDIV, CHECK_OP(op1 == 0, "bytecode attempted to execute udiv#0\n");
DEFINE_BINOP(OP_BC_UDIV, CHECK_OP(op1 == 0, "bytecode attempted to execute udiv#0\n");
res=op0/op1);
DEFINE_BINOP(OP_SDIV, CHECK_OP(check_sdivops(sop0, sop1), "bytecode attempted to execute sdiv#0\n");
DEFINE_BINOP(OP_BC_SDIV, CHECK_OP(check_sdivops(sop0, sop1), "bytecode attempted to execute sdiv#0\n");
res=sop0/sop1);
DEFINE_BINOP(OP_UREM, CHECK_OP(op1 == 0, "bytecode attempted to execute urem#0\n");
DEFINE_BINOP(OP_BC_UREM, CHECK_OP(op1 == 0, "bytecode attempted to execute urem#0\n");
res=op0 % op1);
DEFINE_BINOP(OP_SREM, CHECK_OP(check_sdivops(sop0,sop1), "bytecode attempted to execute urem#0\n");
DEFINE_BINOP(OP_BC_SREM, CHECK_OP(check_sdivops(sop0,sop1), "bytecode attempted to execute urem#0\n");
res=sop0 % sop1);
DEFINE_BINOP(OP_SHL, CHECK_OP(op1 > inst->type, "bytecode attempted to execute shl greater than bitwidth\n");
DEFINE_BINOP(OP_BC_SHL, CHECK_OP(op1 > inst->type, "bytecode attempted to execute shl greater than bitwidth\n");
res = op0 << op1);
DEFINE_BINOP(OP_LSHR, CHECK_OP(op1 > inst->type, "bytecode attempted to execute lshr greater than bitwidth\n");
DEFINE_BINOP(OP_BC_LSHR, CHECK_OP(op1 > inst->type, "bytecode attempted to execute lshr greater than bitwidth\n");
res = op0 >> op1);
DEFINE_BINOP(OP_ASHR, CHECK_OP(op1 > inst->type, "bytecode attempted to execute ashr greater than bitwidth\n");
DEFINE_BINOP(OP_BC_ASHR, CHECK_OP(op1 > inst->type, "bytecode attempted to execute ashr greater than bitwidth\n");
res = CLI_SRS(sop0, op1));
DEFINE_BINOP(OP_AND, res = op0 & op1);
DEFINE_BINOP(OP_OR, res = op0 | op1);
DEFINE_BINOP(OP_XOR, res = op0 ^ op1);
DEFINE_BINOP(OP_BC_AND, res = op0 & op1);
DEFINE_BINOP(OP_BC_OR, res = op0 | op1);
DEFINE_BINOP(OP_BC_XOR, res = op0 ^ op1);
DEFINE_CASTOP(OP_SEXT,
DEFINE_CASTOP(OP_BC_SEXT,
CHOOSE(READ1(sres, inst->u.cast.source); res = sres ? ~0ull : 0,
READ8(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask),
READ16(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask),
READ32(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask),
READ64(sres, inst->u.cast.source); res=sres=SIGNEXT(sres, inst->u.cast.mask)));
DEFINE_CASTOP(OP_ZEXT,
DEFINE_CASTOP(OP_BC_ZEXT,
CHOOSE(READ1(res, inst->u.cast.source),
READ8(res, inst->u.cast.source),
READ16(res, inst->u.cast.source),
READ32(res, inst->u.cast.source),
READ64(res, inst->u.cast.source)));
DEFINE_CASTOP(OP_TRUNC,
DEFINE_CASTOP(OP_BC_TRUNC,
CHOOSE(READ1(res, inst->u.cast.source),
READ8(res, inst->u.cast.source),
READ16(res, inst->u.cast.source),
READ32(res, inst->u.cast.source),
READ64(res, inst->u.cast.source)));
DEFINE_OP(OP_BRANCH)
DEFINE_OP(OP_BC_BRANCH)
stop = jump(func, (values[inst->u.branch.condition]&1) ?
inst->u.branch.br_true : inst->u.branch.br_false,
&bb, &inst, &bb_inst);
continue;
DEFINE_OP(OP_JMP)
DEFINE_OP(OP_BC_JMP)
stop = jump(func, inst->u.jump, &bb, &inst, &bb_inst);
continue;
DEFINE_OP_RET_N(OP_RET*5, uint8_t, READ1, WRITE8);
DEFINE_OP_RET_N(OP_RET*5+1, uint8_t, READ8, WRITE8);
DEFINE_OP_RET_N(OP_RET*5+2, uint16_t, READ16, WRITE16);
DEFINE_OP_RET_N(OP_RET*5+3, uint32_t, READ32, WRITE32);
DEFINE_OP_RET_N(OP_RET*5+4, uint64_t, READ64, WRITE64);
DEFINE_ICMPOP(OP_ICMP_EQ, res = (op0 == op1));
DEFINE_ICMPOP(OP_ICMP_NE, res = (op0 != op1));
DEFINE_ICMPOP(OP_ICMP_UGT, res = (op0 > op1));
DEFINE_ICMPOP(OP_ICMP_UGE, res = (op0 >= op1));
DEFINE_ICMPOP(OP_ICMP_ULT, res = (op0 < op1));
DEFINE_ICMPOP(OP_ICMP_ULE, res = (op0 <= op1));
DEFINE_ICMPOP(OP_ICMP_SGT, res = (sop0 > sop1));
DEFINE_ICMPOP(OP_ICMP_SGE, res = (sop0 >= sop1));
DEFINE_ICMPOP(OP_ICMP_SLE, res = (sop0 <= sop1));
DEFINE_ICMPOP(OP_ICMP_SLT, res = (sop0 < sop1));
case OP_SELECT*5:
DEFINE_OP_BC_RET_N(OP_BC_RET*5, uint8_t, READ1, WRITE8);
DEFINE_OP_BC_RET_N(OP_BC_RET*5+1, uint8_t, READ8, WRITE8);
DEFINE_OP_BC_RET_N(OP_BC_RET*5+2, uint16_t, READ16, WRITE16);
DEFINE_OP_BC_RET_N(OP_BC_RET*5+3, uint32_t, READ32, WRITE32);
DEFINE_OP_BC_RET_N(OP_BC_RET*5+4, uint64_t, READ64, WRITE64);
DEFINE_ICMPOP(OP_BC_ICMP_EQ, res = (op0 == op1));
DEFINE_ICMPOP(OP_BC_ICMP_NE, res = (op0 != op1));
DEFINE_ICMPOP(OP_BC_ICMP_UGT, res = (op0 > op1));
DEFINE_ICMPOP(OP_BC_ICMP_UGE, res = (op0 >= op1));
DEFINE_ICMPOP(OP_BC_ICMP_ULT, res = (op0 < op1));
DEFINE_ICMPOP(OP_BC_ICMP_ULE, res = (op0 <= op1));
DEFINE_ICMPOP(OP_BC_ICMP_SGT, res = (sop0 > sop1));
DEFINE_ICMPOP(OP_BC_ICMP_SGE, res = (sop0 >= sop1));
DEFINE_ICMPOP(OP_BC_ICMP_SLE, res = (sop0 <= sop1));
DEFINE_ICMPOP(OP_BC_ICMP_SLT, res = (sop0 < sop1));
case OP_BC_SELECT*5:
{
uint8_t t0, t1, t2;
READ1(t0, inst->u.three[0]);
@ -526,7 +526,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
WRITE8(inst->dest, t0 ? t1 : t2);
break;
}
case OP_SELECT*5+1:
case OP_BC_SELECT*5+1:
{
uint8_t t0, t1, t2;
READ1(t0, inst->u.three[0]);
@ -535,7 +535,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
WRITE8(inst->dest, t0 ? t1 : t2);
break;
}
case OP_SELECT*5+2:
case OP_BC_SELECT*5+2:
{
uint8_t t0;
uint16_t t1, t2;
@ -545,7 +545,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
WRITE16(inst->dest, t0 ? t1 : t2);
break;
}
case OP_SELECT*5+3:
case OP_BC_SELECT*5+3:
{
uint8_t t0;
uint32_t t1, t2;
@ -555,7 +555,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
WRITE32(inst->dest, t0 ? t1 : t2);
break;
}
case OP_SELECT*5+4:
case OP_BC_SELECT*5+4:
{
uint8_t t0;
uint64_t t1, t2;
@ -566,7 +566,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
break;
}
DEFINE_OP(OP_CALL_API) {
DEFINE_OP(OP_BC_CALL_API) {
const struct cli_apicall *api = &cli_apicalls[inst->u.ops.funcid];
int32_t res;
CHECK_APIID(inst->u.ops.funcid);
@ -594,7 +594,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
break;
}
DEFINE_OP(OP_CALL_DIRECT)
DEFINE_OP(OP_BC_CALL_DIRECT)
CHECK_FUNCID(inst->u.ops.funcid);
func2 = &bc->funcs[inst->u.ops.funcid];
CHECK_EQ(func2->numArgs, inst->u.ops.numOps);
@ -657,35 +657,35 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
stack_depth++;
continue;
case OP_COPY*5:
case OP_BC_COPY*5:
{
uint8_t op;
READ1(op, BINOP(0));
WRITE8(BINOP(1), op);
break;
}
case OP_COPY*5+1:
case OP_BC_COPY*5+1:
{
uint8_t op;
READ8(op, BINOP(0));
WRITE8(BINOP(1), op);
break;
}
case OP_COPY*5+2:
case OP_BC_COPY*5+2:
{
uint16_t op;
READ16(op, BINOP(0));
WRITE16(BINOP(1), op);
break;
}
case OP_COPY*5+3:
case OP_BC_COPY*5+3:
{
uint32_t op;
READ32(op, BINOP(0));
WRITE32(BINOP(1), op);
break;
}
case OP_COPY*5+4:
case OP_BC_COPY*5+4:
{
uint64_t op;
READ32(op, BINOP(0));
@ -693,29 +693,29 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
break;
}
case OP_LOAD*5:
case OP_LOAD*5+1:
case OP_BC_LOAD*5:
case OP_BC_LOAD*5+1:
{
uint8_t *ptr;
READP(ptr, inst->u.unaryop);
WRITE8(inst->dest, (*ptr));
break;
}
case OP_LOAD*5+2:
case OP_BC_LOAD*5+2:
{
const union unaligned_16 *ptr;
READP(ptr, inst->u.unaryop);
WRITE16(inst->dest, (ptr->una_u16));
break;
}
case OP_LOAD*5+3:
case OP_BC_LOAD*5+3:
{
const union unaligned_32 *ptr;
READP(ptr, inst->u.unaryop);
WRITE32(inst->dest, (ptr->una_u32));
break;
}
case OP_LOAD*5+4:
case OP_BC_LOAD*5+4:
{
const union unaligned_64 *ptr;
READP(ptr, inst->u.unaryop);
@ -723,7 +723,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
break;
}
case OP_STORE*5:
case OP_BC_STORE*5:
{
uint8_t *ptr;
uint8_t v;
@ -732,7 +732,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
*ptr = v;
break;
}
case OP_STORE*5+1:
case OP_BC_STORE*5+1:
{
uint8_t *ptr;
uint8_t v;
@ -741,7 +741,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
*ptr = v;
break;
}
case OP_STORE*5+2:
case OP_BC_STORE*5+2:
{
union unaligned_16 *ptr;
uint16_t v;
@ -750,7 +750,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
ptr->una_s16 = v;
break;
}
case OP_STORE*5+3:
case OP_BC_STORE*5+3:
{
union unaligned_32 *ptr;
uint32_t v;
@ -759,7 +759,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
ptr->una_u32 = v;
break;
}
case OP_STORE*5+4:
case OP_BC_STORE*5+4:
{
union unaligned_64 *ptr;
uint64_t v;
@ -768,7 +768,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
ptr->una_u64 = v;
break;
}
/* TODO: implement OP_GEP1, OP_GEP2, OP_GEPN */
/* TODO: implement OP_BC_GEP1, OP_BC_GEP2, OP_BC_GEPN */
default:
cli_errmsg("Opcode %u of type %u is not implemented yet!\n",
inst->interp_op/5, inst->interp_op%5);

@ -425,20 +425,20 @@ public:
const struct cli_bc_inst *inst = &bb->insts[j];
Value *Op0, *Op1, *Op2;
// libclamav has already validated this.
assert(inst->opcode < OP_INVALID && "Invalid opcode");
assert(inst->opcode < OP_BC_INVALID && "Invalid opcode");
switch (inst->opcode) {
case OP_JMP:
case OP_BRANCH:
case OP_CALL_API:
case OP_CALL_DIRECT:
case OP_ZEXT:
case OP_SEXT:
case OP_TRUNC:
case OP_GEP1:
case OP_GEP2:
case OP_GEPN:
case OP_STORE:
case OP_COPY:
case OP_BC_JMP:
case OP_BC_BRANCH:
case OP_BC_CALL_API:
case OP_BC_CALL_DIRECT:
case OP_BC_ZEXT:
case OP_BC_SEXT:
case OP_BC_TRUNC:
case OP_BC_GEP1:
case OP_BC_GEP2:
case OP_BC_GEPN:
case OP_BC_STORE:
case OP_BC_COPY:
// these instructions represents operands differently
break;
default:
@ -459,23 +459,23 @@ public:
}
switch (inst->opcode) {
case OP_ADD:
case OP_BC_ADD:
Store(inst->dest, Builder.CreateAdd(Op0, Op1));
break;
case OP_SUB:
case OP_BC_SUB:
Store(inst->dest, Builder.CreateSub(Op0, Op1));
break;
case OP_MUL:
case OP_BC_MUL:
Store(inst->dest, Builder.CreateMul(Op0, Op1));
break;
case OP_UDIV:
case OP_BC_UDIV:
{
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
InsertVerify(Bad, Fail, FHandler, F);
Store(inst->dest, Builder.CreateUDiv(Op0, Op1));
break;
}
case OP_SDIV:
case OP_BC_SDIV:
{
//TODO: also verify Op0 == -1 && Op1 = INT_MIN
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
@ -483,14 +483,14 @@ public:
Store(inst->dest, Builder.CreateSDiv(Op0, Op1));
break;
}
case OP_UREM:
case OP_BC_UREM:
{
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
InsertVerify(Bad, Fail, FHandler, F);
Store(inst->dest, Builder.CreateURem(Op0, Op1));
break;
}
case OP_SREM:
case OP_BC_SREM:
{
//TODO: also verify Op0 == -1 && Op1 = INT_MIN
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
@ -498,46 +498,46 @@ public:
Store(inst->dest, Builder.CreateSRem(Op0, Op1));
break;
}
case OP_SHL:
case OP_BC_SHL:
Store(inst->dest, Builder.CreateShl(Op0, Op1));
break;
case OP_LSHR:
case OP_BC_LSHR:
Store(inst->dest, Builder.CreateLShr(Op0, Op1));
break;
case OP_ASHR:
case OP_BC_ASHR:
Store(inst->dest, Builder.CreateAShr(Op0, Op1));
break;
case OP_AND:
case OP_BC_AND:
Store(inst->dest, Builder.CreateAnd(Op0, Op1));
break;
case OP_OR:
case OP_BC_OR:
Store(inst->dest, Builder.CreateOr(Op0, Op1));
break;
case OP_XOR:
case OP_BC_XOR:
Store(inst->dest, Builder.CreateXor(Op0, Op1));
break;
case OP_TRUNC:
case OP_BC_TRUNC:
{
Value *Src = convertOperand(func, inst, inst->u.cast.source);
const Type *Ty = mapType(func->types[inst->dest]);
Store(inst->dest, Builder.CreateTrunc(Src, Ty));
break;
}
case OP_ZEXT:
case OP_BC_ZEXT:
{
Value *Src = convertOperand(func, inst, inst->u.cast.source);
const Type *Ty = mapType(func->types[inst->dest]);
Store(inst->dest, Builder.CreateZExt(Src, Ty));
break;
}
case OP_SEXT:
case OP_BC_SEXT:
{
Value *Src = convertOperand(func, inst, inst->u.cast.source);
const Type *Ty = mapType(func->types[inst->dest]);
Store(inst->dest, Builder.CreateSExt(Src, Ty));
break;
}
case OP_BRANCH:
case OP_BC_BRANCH:
{
Value *Cond = convertOperand(func, inst, inst->u.branch.condition);
BasicBlock *True = BB[inst->u.branch.br_true];
@ -549,46 +549,46 @@ public:
Builder.CreateCondBr(Cond, True, False);
break;
}
case OP_JMP:
case OP_BC_JMP:
{
BasicBlock *Jmp = BB[inst->u.jump];
Builder.CreateBr(Jmp);
break;
}
case OP_RET:
case OP_BC_RET:
Builder.CreateRet(Op0);
break;
case OP_ICMP_EQ:
case OP_BC_ICMP_EQ:
Store(inst->dest, Builder.CreateICmpEQ(Op0, Op1));
break;
case OP_ICMP_NE:
case OP_BC_ICMP_NE:
Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
break;
case OP_ICMP_UGT:
case OP_BC_ICMP_UGT:
Store(inst->dest, Builder.CreateICmpUGT(Op0, Op1));
break;
case OP_ICMP_UGE:
case OP_BC_ICMP_UGE:
Store(inst->dest, Builder.CreateICmpUGE(Op0, Op1));
break;
case OP_ICMP_ULT:
case OP_BC_ICMP_ULT:
Store(inst->dest, Builder.CreateICmpULT(Op0, Op1));
break;
case OP_ICMP_ULE:
case OP_BC_ICMP_ULE:
Store(inst->dest, Builder.CreateICmpULE(Op0, Op1));
break;
case OP_ICMP_SGT:
case OP_BC_ICMP_SGT:
Store(inst->dest, Builder.CreateICmpSGT(Op0, Op1));
break;
case OP_ICMP_SGE:
case OP_BC_ICMP_SGE:
Store(inst->dest, Builder.CreateICmpSGE(Op0, Op1));
break;
case OP_ICMP_SLT:
case OP_BC_ICMP_SLT:
Store(inst->dest, Builder.CreateICmpSLT(Op0, Op1));
break;
case OP_SELECT:
case OP_BC_SELECT:
Store(inst->dest, Builder.CreateSelect(Op0, Op1, Op2));
break;
case OP_COPY:
case OP_BC_COPY:
{
Value *Dest = Values[inst->u.binop[1]];
const PointerType *PTy = cast<PointerType>(Dest->getType());
@ -596,7 +596,7 @@ public:
Builder.CreateStore(Op0, Dest);
break;
}
case OP_CALL_DIRECT:
case OP_BC_CALL_DIRECT:
{
Function *DestF = Functions[inst->u.ops.funcid];
SmallVector<Value*, 2> args;
@ -610,7 +610,7 @@ public:
Store(inst->dest, CI);
break;
}
case OP_CALL_API:
case OP_BC_CALL_API:
{
assert(inst->u.ops.funcid < cli_apicall_maxapi && "APICall out of range");
const struct cli_apicall *api = &cli_apicalls[inst->u.ops.funcid];
@ -624,14 +624,14 @@ public:
Store(inst->dest, Builder.CreateCall(DestF, args.begin(), args.end()));
break;
}
case OP_GEP1:
case OP_BC_GEP1:
{
Value *V = Values[inst->u.binop[0]];
Value *Op = convertOperand(func, I32Ty, inst->u.binop[1]);
Store(inst->dest, Builder.CreateGEP(V, Op));
break;
}
case OP_GEP2:
case OP_BC_GEP2:
{
std::vector<Value*> Idxs;
Value *V = Values[inst->u.three[0]];
@ -640,7 +640,7 @@ public:
Store(inst->dest, Builder.CreateGEP(V, Idxs.begin(), Idxs.end()));
break;
}
case OP_GEPN:
case OP_BC_GEPN:
{
std::vector<Value*> Idxs;
assert(inst->u.ops.numOps > 1);
@ -650,7 +650,7 @@ public:
Store(inst->dest, Builder.CreateGEP(V, Idxs.begin(), Idxs.end()));
break;
}
case OP_STORE:
case OP_BC_STORE:
{
Value *Dest = convertOperand(func, inst, inst->u.binop[1]);
const Type *ETy = cast<PointerType>(Dest->getType())->getElementType();
@ -658,7 +658,7 @@ public:
Dest);
break;
}
case OP_LOAD:
case OP_BC_LOAD:
Op0 = Builder.CreateLoad(Op0);
Store(inst->dest, Op0);
break;
@ -858,7 +858,9 @@ int bytecode_init(void)
if (llvm_is_multithreaded())
return 0;
llvm_install_error_handler(llvm_error_handler);
#ifdef CL_DEBUG
sys::PrintStackTraceOnErrorSignal();
#endif
atexit(do_shutdown);
llvm_start_multithreaded();
@ -876,6 +878,7 @@ int cli_bytecode_init_jit(struct cli_all_bc *bcs)
bcs->engine = new(std::nothrow) struct cli_bcengine;
if (!bcs->engine)
return CL_EMEM;
bcs->engine->EE = 0;
return 0;
}

@ -80,9 +80,10 @@ typedef enum {
#define CL_DB_PUA_EXCLUDE 0x200
#define CL_DB_COMPILED 0x400 /* internal */
#define CL_DB_DIRECTORY 0x800 /* internal */
#define CL_DB_BYTECODE 0x1000
/* recommended db settings */
#define CL_DB_STDOPT (CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_CVDNOTMP)
#define CL_DB_STDOPT (CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_CVDNOTMP | CL_DB_BYTECODE)
/* scan options */
#define CL_SCAN_RAW 0x0

@ -32,49 +32,49 @@ struct bytecode_metadata {
#define BC_HEADER "ClamBC"
enum bc_opcode {
OP_ADD=1,
OP_SUB,
OP_MUL,
OP_UDIV,
OP_SDIV,
OP_UREM,
OP_SREM,
OP_SHL,
OP_LSHR,
OP_ASHR,
OP_AND,
OP_OR,
OP_XOR,
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_TRUNC,
OP_SEXT,
OP_ZEXT,
OP_BC_TRUNC,
OP_BC_SEXT,
OP_BC_ZEXT,
OP_BRANCH,
OP_JMP,
OP_RET,
OP_RET_VOID,
OP_BC_BRANCH,
OP_BC_JMP,
OP_BC_RET,
OP_BC_RET_VOID,
OP_ICMP_EQ,
OP_ICMP_NE,
OP_ICMP_UGT,
OP_ICMP_UGE,
OP_ICMP_ULT,
OP_ICMP_ULE,
OP_ICMP_SGT,
OP_ICMP_SGE,
OP_ICMP_SLE,
OP_ICMP_SLT,
OP_SELECT,
OP_CALL_DIRECT,
OP_CALL_API,
OP_COPY,
OP_GEP1,
OP_GEP2,
OP_GEPN,
OP_STORE,
OP_LOAD,
OP_INVALID /* last */
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_GEP2,
OP_BC_GEPN,
OP_BC_STORE,
OP_BC_LOAD,
OP_BC_INVALID /* last */
};
static const unsigned char operand_counts[] = {
@ -91,9 +91,9 @@ static const unsigned char operand_counts[] = {
3,
/* CALLs have variable number of operands */
0, 0,
/* OP_COPY */
/* OP_BC_COPY */
2,
/* OP_GEP1, OP_GEP2, OP_GEPN, OP_STORE, OP_LOAD*/
/* OP_BC_GEP1, OP_BC_GEP2, OP_BC_GEPN, OP_BC_STORE, OP_BC_LOAD*/
2, 3, 0, 2, 1
};

@ -114,6 +114,11 @@ static struct dconf_module modules[] = {
{ "PHISHING", "ENGINE", PHISHING_CONF_ENGINE, 1 },
{ "PHISHING", "ENTCONV", PHISHING_CONF_ENTCONV, 1 },
{ "BYTECODE", "INTERPRETER", BYTECODE_INTERPRETER, 1 },
{ "BYTECODE", "JIT X86", BYTECODE_JIT_X86, 1 },
{ "BYTECODE", "JIT PPC", BYTECODE_JIT_PPC, 1 },
{ "BYTECODE", "JIT ARM", BYTECODE_JIT_ARM, 0 },
{ NULL, NULL, 0, 0 }
};
@ -161,6 +166,9 @@ struct cli_dconf *cli_dconf_init(void)
} else if(!strcmp(modules[i].mname, "PHISHING")) {
if(modules[i].state)
dconf->phishing |= modules[i].bflag;
} else if(!strcmp(modules[i].mname, "BYTECODE")) {
if (modules[i].state)
dconf->bytecode |= modules[i].bflag;
}
}
@ -170,7 +178,7 @@ struct cli_dconf *cli_dconf_init(void)
void cli_dconf_print(struct cli_dconf *dconf)
{
unsigned int pe = 0, elf = 0, macho = 0, arch = 0, doc = 0, mail = 0;
unsigned int other = 0, phishing = 0, i;
unsigned int other = 0, phishing = 0, i, bytecode=0;
cli_dbgmsg("Dynamic engine configuration settings:\n");
@ -247,6 +255,15 @@ void cli_dconf_print(struct cli_dconf *dconf)
cli_dbgmsg(" * Submodule %10s:\t%s\n", modules[i].sname, (dconf->phishing & modules[i].bflag) ? "On" : "** Off **");
else
continue;
} else if(!strcmp(modules[i].mname, "BYTECODE")) {
if(!bytecode) {
cli_dbgmsg("Module BYTECODE %s\n", dconf->phishing ? "On" : "Off");
bytecode = 1;
}
if(dconf->bytecode)
cli_dbgmsg(" * Submodule %10s:\t%s\n", modules[i].sname, (dconf->bytecode & modules[i].bflag) ? "On" : "** Off **");
else
continue;
}
}
}
@ -370,6 +387,15 @@ int cli_dconf_load(FILE *fs, struct cl_engine *engine, unsigned int options, str
break;
}
}
if(!strncmp(buffer, "BYTECODE:", 9) && chkflevel(buffer, 2)) {
if(sscanf(buffer + 9, "0x%x", &val) == 1) {
engine->dconf->bytecode = val;
} else {
ret = CL_EMALFDB;
break;
}
}
}
if(ret) {

@ -39,6 +39,7 @@ struct cli_dconf {
uint32_t mail;
uint32_t other;
uint32_t phishing;
uint32_t bytecode;
};
/* PE flags */
@ -103,6 +104,14 @@ struct cli_dconf {
#define PHISHING_CONF_ENGINE 0x1
#define PHISHING_CONF_ENTCONV 0x2
/* Bytecode flags */
#define BYTECODE_INTERPRETER 0x1
#define BYTECODE_JIT_X86 0x2
#define BYTECODE_JIT_PPC 0x4
#define BYTECODE_JIT_ARM 0x8
#define BYTECODE_ENGINE_MASK (BYTECODE_INTERPRETER | BYTECODE_JIT_X86 | BYTECODE_JIT_PPC | BYTECODE_JIT_ARM)
#ifdef USE_MPOOL
struct cli_dconf *cli_dconf_init(mpool_t *);
#define cli_mpool_dconf_init(a) cli_dconf_init(a)

@ -39,6 +39,7 @@
#include "dconf.h"
#include "libclamunrar_iface/unrar_iface.h"
#include "regex/regex.h"
#include "bytecode.h"
/*
* CL_FLEVEL is the signature f-level specific to the current code and
@ -175,6 +176,9 @@ struct cl_engine {
/* Used for memory pools */
mpool_t *mempool;
/* Used for bytecode */
struct cli_all_bc bcs;
};
struct cl_settings {

@ -70,7 +70,8 @@
#endif
#include "mpool.h"
#include "bytecode.h"
#include "bytecode_priv.h"
#ifdef CL_THREAD_SAFE
# include <pthread.h>
static pthread_mutex_t cli_ref_mutex = PTHREAD_MUTEX_INITIALIZER;
@ -1013,6 +1014,29 @@ static int cli_loadldb(FILE *fs, struct cl_engine *engine, unsigned int *signo,
return CL_SUCCESS;
}
static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, const char *dbname)
{
int rc;
struct cli_all_bc *bcs = &engine->bcs;
struct cli_bc *bc;
if(!(engine->dconf->bytecode & BYTECODE_ENGINE_MASK)) {
return CL_SUCCESS;
}
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;
}
bcs->count++;
bc = &bcs->all_bcs[bcs->count-1];
rc = cli_bytecode_load(bc, fs, dbio);
if (rc != CL_SUCCESS) {
fprintf(stderr,"Unable to load %s bytecode: %s\n", dbname, cl_strerror(rc));
return rc;
}
return CL_SUCCESS;
}
#define FTM_TOKENS 8
static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options, unsigned int internal, struct cli_dbio *dbio)
{
@ -1608,7 +1632,11 @@ int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo
ret = cli_loadldb(fs, engine, signo, options | CL_DB_PUA_MODE, dbio, dbname);
else
skipped = 1;
} else if(cli_strbcasestr(filename, ".cbc")) {
if(options & CL_DB_BYTECODE)
ret = cli_loadcbc(fs, engine, signo, options, dbio, dbname);
else
skipped = 1;
} else if(cli_strbcasestr(dbname, ".sdb")) {
ret = cli_loadndb(fs, engine, signo, 1, options, dbio, dbname);
@ -1782,10 +1810,17 @@ int cl_load(const char *path, struct cl_engine *engine, unsigned int *signo, uns
if((ret = phishing_init(engine)))
return ret;
if((dboptions & CL_DB_BYTECODE) && !engine->bcs.engine && (engine->dconf->bytecode & BYTECODE_ENGINE_MASK)) {
if((ret = cli_bytecode_init(&engine->bcs)))
return ret;
} else {
cli_dbgmsg("Bytecode engine disabled\n");
}
engine->dboptions |= dboptions;
switch(sb.st_mode & S_IFMT) {
case S_IFREG:
case S_IFREG:
ret = cli_load(path, engine, signo, dboptions, NULL);
break;
@ -2092,6 +2127,14 @@ int cl_engine_free(struct cl_engine *engine)
mpool_free(engine->mempool, metah);
}
if(engine->dconf->bytecode & BYTECODE_ENGINE_MASK) {
unsigned i;
if (engine->bcs.all_bcs)
for(i=0;i<engine->bcs.count;i++)
cli_bytecode_destroy(&engine->bcs.all_bcs[i]);
cli_bytecode_done(&engine->bcs);
free(engine->bcs.all_bcs);
}
if(engine->dconf->phishing & PHISHING_CONF_ENGINE)
phishing_done(engine);
if(engine->dconf)
@ -2168,6 +2211,12 @@ int cl_engine_compile(struct cl_engine *engine)
cli_dconf_print(engine->dconf);
mpool_flush(engine->mempool);
/* Compile bytecode */
if((ret = cli_bytecode_prepare(&engine->bcs))) {
fprintf(stderr,"Unable to compile/load bytecode: %s\n", cl_strerror(ret));
return ret;
}
engine->dboptions |= CL_DB_COMPILED;
return CL_SUCCESS;
}

@ -47,6 +47,7 @@
cli_strbcasestr(ext, ".pdb") || \
cli_strbcasestr(ext, ".gdb") || \
cli_strbcasestr(ext, ".wdb") || \
cli_strbcasestr(ext, ".cbc") || \
cli_strbcasestr(ext, ".ftm") || \
cli_strbcasestr(ext, ".ign") || \
cli_strbcasestr(ext, ".cfg") || \

@ -233,7 +233,7 @@ const struct clam_option clam_options[] = {
{ "AllowSupplementaryGroups", NULL, 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM | OPT_MILTER, "Initialize a supplementary group access (the process must be started by root).", "no" },
/* Scan options */
{ "Bytecode", "bytecode", 0, TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "With this option enabled ClamAV will load bytecode from the database. It is highly recommended you keep this option on, otherwise you'll miss detections for many new viruses.", "yes" },
{ "DetectPUA", "detect-pua", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Detect Potentially Unwanted Applications.", "yes" },
{ "ExcludePUA", "exclude-pua", 0, TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD | OPT_CLAMSCAN, "Exclude a specific PUA category. This directive can be used multiple times.\nSee http://www.clamav.net/support/pua for the complete list of PUA\ncategories.", "NetTool\nPWTool" },

@ -129,6 +129,14 @@ START_TEST (test_div0)
}
END_TEST
START_TEST (test_lsig)
{
cl_init(CL_INIT_DEFAULT);
runtest("input/lsig.cbc", 0, CL_EBYTECODE, 0);
// runtest("input/lsig.cbc", 0, CL_EBYTECODE, 1);
}
END_TEST
Suite *test_bytecode_suite(void)
{
Suite *s = suite_create("bytecode");

Loading…
Cancel
Save