diff --git a/libclamav/bytecode.c b/libclamav/bytecode.c index d6869983f..71484b7a4 100644 --- a/libclamav/bytecode.c +++ b/libclamav/bytecode.c @@ -26,6 +26,7 @@ #include "clamav.h" #include "others.h" +#include "pe.h" #include "bytecode.h" #include "bytecode_priv.h" #include "readdb.h" @@ -45,6 +46,7 @@ struct cli_bc_ctx *cli_bytecode_context_alloc(void) ctx->fd = -1; ctx->off = 0; ctx->hooks.match_counts = nomatch; + /* TODO: init all hooks with safe values */ ctx->virname = NULL; return ctx; } @@ -55,11 +57,18 @@ void cli_bytecode_context_destroy(struct cli_bc_ctx *ctx) free(ctx); } -int cli_bytecode_context_clear(struct cli_bc_ctx *ctx) +/* resets bytecode state, so you can run another bytecode with same ctx */ +int cli_bytecode_context_reset(struct cli_bc_ctx *ctx) { free(ctx->opsizes); free(ctx->values); free(ctx->operands); + return CL_SUCCESS; +} + +int cli_bytecode_context_clear(struct cli_bc_ctx *ctx) +{ + cli_bytecode_context_reset(ctx); memset(ctx, 0, sizeof(ctx)); return CL_SUCCESS; } @@ -1435,7 +1444,46 @@ int cli_bytecode_runlsig(const struct cli_all_bc *bcs, const struct cli_bc *bc, return CL_VIRUS; } ret = cli_bytecode_context_getresult_int(&ctx); - cli_dbgmsg("Bytecode returned code: %u\n", ret); + cli_dbgmsg("Bytecode %u returned code: %u\n", bc->id, ret); cli_bytecode_context_clear(&ctx); return CL_SUCCESS; } + +int cli_bytecode_runhook(const struct cl_engine *engine, struct cli_bc_ctx *ctx, + unsigned id, int fd, const char **virname) +{ + const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS]; + unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS]; + int ret; + + cli_bytecode_context_setfile(ctx, fd); + cli_dbgmsg("Bytecode executing hook id %u (%u hooks)\n", id, hooks_cnt); + for (i=0;i < hooks_cnt;i++) { + const struct cli_bc *bc = &engine->bcs.all_bcs[hooks[i]]; + cli_bytecode_context_setfuncid(ctx, bc, 0); + ret = cli_bytecode_run(&engine->bcs, bc, ctx); + if (ret != CL_SUCCESS) { + cli_warnmsg("Bytecode failed to run: %s\n", cl_strerror(ret)); + return CL_SUCCESS; + } + if (ctx->virname) { + cli_dbgmsg("Bytecode found virus: %s\n", ctx->virname); + if (virname) + *virname = ctx->virname; + cli_bytecode_context_clear(ctx); + return CL_VIRUS; + } + ret = cli_bytecode_context_getresult_int(ctx); + /* TODO: use prefix here */ + cli_dbgmsg("Bytecode %u returned %u\n", bc->id, ret); + cli_bytecode_context_reset(ctx); + } + return CL_CLEAN; +} + +int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data) +{ + ctx->hooks.exeinfo = &data->exe_info; + ctx->hooks.pedata = data; + return 0; +} diff --git a/libclamav/bytecode.h b/libclamav/bytecode.h index 1cf9d98ea..a10a13388 100644 --- a/libclamav/bytecode.h +++ b/libclamav/bytecode.h @@ -32,6 +32,7 @@ struct cli_bc_inst; struct cli_bc_type; struct cli_bc_engine; struct bitset_tag; +struct cl_engine; enum bc_state { bc_skip, @@ -65,11 +66,13 @@ struct cli_all_bc { struct cli_bcengine *engine; }; +struct cli_pe_hook_data; struct cli_bc_ctx *cli_bytecode_context_alloc(void); int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid); int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c); int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen); int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, int fd); +int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data); int cli_bytecode_context_clear(struct cli_bc_ctx *ctx); uint64_t cli_bytecode_context_getresult_int(struct cli_bc_ctx *ctx); void cli_bytecode_context_destroy(struct cli_bc_ctx *ctx); @@ -83,7 +86,9 @@ void cli_bytecode_destroy(struct cli_bc *bc); int cli_bytecode_done(struct cli_all_bc *allbc); /* Hooks */ +struct cli_exe_info; int cli_bytecode_runlsig(const struct cli_all_bc *bcs, const struct cli_bc* bc, const char **virname, const uint32_t* lsigcnt, int fd); +int cli_bytecode_runhook(const struct cl_engine *engine, struct cli_bc_ctx *ctx, unsigned id, int fd, const char **virname); #ifdef __cplusplus extern "C" { diff --git a/libclamav/bytecode_api.h b/libclamav/bytecode_api.h index ae736a383..88c44ad05 100644 --- a/libclamav/bytecode_api.h +++ b/libclamav/bytecode_api.h @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ +#ifndef BYTECODE_API_H +#define BYTECODE_API_H #ifdef __CLAMBC__ #include "bytecode_execs.h" @@ -32,16 +34,19 @@ struct foo { struct foo *nxt; }; +enum BytecodeKind { + BC_GENERIC=0,/* generic bytecode, not tied to a specific hook */ + _BC_START_HOOKS=256, + BC_LOGICAL=256,/* triggered by a logical signature */ + BC_PE_UNPACKER,/* a PE unpacker */ + _BC_LAST_HOOK +}; + #ifdef __CLAMBC__ extern const uint32_t __clambc_match_counts[64]; extern const struct cli_exe_info __clambc_exeinfo; -enum BytecodeKind { - BC_GENERIC=0,/* generic bytecode, not tied to a specific hook */ - BC_LOGICAL,/* triggered by a logical signature */ - BC_PE_UNPACKER,/* a PE unpacker */ -}; const uint8_t __clambc_kind; uint32_t test0(struct foo*, uint32_t); @@ -68,3 +73,4 @@ uint32_t debug_print_uint(uint32_t a, uint32_t b); //const char *LogicalSignature; #endif +#endif diff --git a/libclamav/bytecode_hooks.h b/libclamav/bytecode_hooks.h index 47305f553..a068dc223 100644 --- a/libclamav/bytecode_hooks.h +++ b/libclamav/bytecode_hooks.h @@ -24,7 +24,8 @@ struct cli_bc_hooks { const uint32_t* match_counts; - const struct cli_exe_info exeinfo; + const struct cli_exe_info *exeinfo; + const struct cli_pe_hook_data *pedata; const uint8_t kind; }; #endif diff --git a/libclamav/others.h b/libclamav/others.h index e74038b57..b1dd94f35 100644 --- a/libclamav/others.h +++ b/libclamav/others.h @@ -41,6 +41,7 @@ #include "libclamunrar_iface/unrar_iface.h" #include "regex/regex.h" #include "bytecode.h" +#include "bytecode_api.h" /* * CL_FLEVEL is the signature f-level specific to the current code and @@ -181,6 +182,8 @@ struct cl_engine { /* Used for bytecode */ struct cli_all_bc bcs; + unsigned *hooks[_BC_LAST_HOOK - _BC_START_HOOKS]; + unsigned hooks_cnt[_BC_LAST_HOOK - _BC_START_HOOKS]; }; struct cl_settings { diff --git a/libclamav/pe.c b/libclamav/pe.c index c8b10298d..ae3285f9e 100644 --- a/libclamav/pe.c +++ b/libclamav/pe.c @@ -439,18 +439,20 @@ int cli_scanpe(int desc, cli_ctx *ctx) char *src = NULL, *dest = NULL; int ndesc, ret = CL_CLEAN, upack = 0, native=0; size_t fsize; - uint32_t valign, falign, hdr_size, j; + uint32_t valign, falign, hdr_size, j, offset; struct cli_exe_section *exe_sections; struct cli_matcher *md5_sect; char timestr[32]; struct pe_image_data_dir *dirs; + struct cli_bc_ctx *bc_ctx; + struct cli_pe_hook_data pedata; if(!ctx) { cli_errmsg("cli_scanpe: ctx == NULL\n"); return CL_ENULLARG; } - + offset = lseek(desc, 0, SEEK_CUR); if(cli_readn(desc, &e_magic, sizeof(e_magic)) != sizeof(e_magic)) { cli_dbgmsg("Can't read DOS signature\n"); return CL_CLEAN; @@ -2252,6 +2254,27 @@ int cli_scanpe(int desc, cli_ctx *ctx) /* to be continued ... */ + /* Bytecode */ + bc_ctx = cli_bytecode_context_alloc(); + if (!bc_ctx) { + cli_errmsg("cli_scanpe: can't allocate memory for bc_ctx\n"); + return CL_EMEM; + } + pedata.exe_info.section = exe_sections; + pedata.exe_info.nsections = nsections; + pedata.exe_info.ep = ep; + pedata.exe_info.offset = offset; + pedata.file_hdr = &file_hdr; + pedata.opt32 = &pe_opt.opt32; + pedata.opt64 = &pe_opt.opt64; + pedata.dirs = dirs; + pedata.overlays = overlays; + pedata.overlays_sz = fsize - overlays; + cli_bytecode_context_setpe(bc_ctx, &pedata); + if (cli_bytecode_runhook(ctx->engine, bc_ctx, BC_PE_UNPACKER, desc, ctx->virname) == CL_VIRUS) + return CL_VIRUS; + cli_bytecode_context_destroy(bc_ctx); + free(exe_sections); return CL_CLEAN; } diff --git a/libclamav/pe.h b/libclamav/pe.h index a8ad47a2e..35cab9e15 100644 --- a/libclamav/pe.h +++ b/libclamav/pe.h @@ -128,6 +128,16 @@ struct pe_image_section_hdr { uint32_t Characteristics; }; +struct cli_pe_hook_data { + struct cli_exe_info exe_info; + struct pe_image_file_hdr *file_hdr; + struct pe_image_optional_hdr32 *opt32; + struct pe_image_optional_hdr64 *opt64; + struct pe_image_data_dir *dirs; + uint32_t overlays; + int32_t overlays_sz; +}; + int cli_scanpe(int desc, cli_ctx *ctx); int cli_peheader(int desc, struct cli_exe_info *peinfo); diff --git a/libclamav/readdb.c b/libclamav/readdb.c index 4251d754b..cf4e15ef8 100644 --- a/libclamav/readdb.c +++ b/libclamav/readdb.c @@ -70,6 +70,7 @@ #include "mpool.h" #include "bytecode.h" +#include "bytecode_api.h" #include "bytecode_priv.h" #ifdef CL_THREAD_SAFE # include @@ -1044,6 +1045,7 @@ static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, struct cli_bc *bc; unsigned sigs = 0; + /* TODO: virusname have a common prefix, and whitelist by that */ if((rc = cli_initroots(engine, options))) return rc; @@ -1059,18 +1061,42 @@ static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, 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)); + cli_errmsg("Unable to load %s bytecode: %s\n", dbname, cl_strerror(rc)); return rc; } sigs += 2;/* the bytecode itself and the logical sig */ - if (bc->lsig) { + if (bc->kind == BC_LOGICAL) { + if (!bc->lsig) { + cli_errmsg("Bytecode %s has logical kind, but missing logical signature!\n", dbname); + return CL_EMALFDB; + } cli_dbgmsg("Bytecode %s has logical signature: %s\n", dbname, bc->lsig); rc = load_oneldb(bc->lsig, 0, 0, engine, options, dbname, 0, &sigs, bc, NULL); if (rc != CL_SUCCESS) { - fprintf(stderr,"Problem parsing logical signature %s for bytecode %s: %s\n", - bc->lsig, dbname, cl_strerror(rc)); + cli_errmsg("Problem parsing logical signature %s for bytecode %s: %s\n", + bc->lsig, dbname, cl_strerror(rc)); return rc; } + } else { + if (bc->lsig) { + cli_errmsg("Bytecode %s has logical signature but is not logical kind!\n", dbname); + return CL_EMALFDB; + } + if (bc->kind >= _BC_START_HOOKS && bc->kind < _BC_LAST_HOOK) { + unsigned hook = bc->kind - _BC_START_HOOKS; + unsigned cnt = ++engine->hooks_cnt[hook]; + engine->hooks[hook] = cli_realloc2(engine->hooks[hook], + sizeof(*engine->hooks[0])*cnt); + if (!engine->hooks[hook]) { + cli_errmsg("Out of memory allocating memory for hook %u", hook); + return CL_EMEM; + } + engine->hooks[hook][cnt-1] = bcs->count-1; + } else switch (bc->kind) { + default: + cli_errmsg("Bytecode: unhandled bytecode kind %u\n", bc->kind); + return CL_EMALFDB; + } } if (signo) *signo += sigs; @@ -2199,6 +2225,9 @@ int cl_engine_free(struct cl_engine *engine) cli_bytecode_destroy(&engine->bcs.all_bcs[i]); cli_bytecode_done(&engine->bcs); free(engine->bcs.all_bcs); + for (i=0;i<_BC_LAST_HOOK - _BC_START_HOOKS;i++) { + free (engine->hooks[i]); + } } if(engine->dconf->phishing & PHISHING_CONF_ENGINE) phishing_done(engine); @@ -2286,7 +2315,7 @@ int cl_engine_compile(struct cl_engine *engine) /* Compile bytecode */ if((ret = cli_bytecode_prepare(&engine->bcs))) { - fprintf(stderr,"Unable to compile/load bytecode: %s\n", cl_strerror(ret)); + cli_errmsg("Unable to compile/load bytecode: %s\n", cl_strerror(ret)); return ret; }