... | ... |
@@ -208,71 +208,76 @@ int main(int argc, char *argv[]) |
208 | 208 |
fclose(f); |
209 | 209 |
|
210 | 210 |
printf("Bytecode loaded\n"); |
211 |
- ctx = cli_bytecode_context_alloc(); |
|
212 |
- if (!ctx) { |
|
213 |
- fprintf(stderr,"Out of memory\n"); |
|
214 |
- exit(3); |
|
215 |
- } |
|
216 |
- memset(&dbg_state, 0, sizeof(dbg_state)); |
|
217 |
- dbg_state.file = "<libclamav>"; |
|
218 |
- dbg_state.line = 0; |
|
219 |
- dbg_state.col = 0; |
|
220 |
- dbg_state.showline = !optget(opts, "no-trace-showsource")->enabled; |
|
221 |
- tracelevel = optget(opts, "trace")->numarg; |
|
222 |
- cli_bytecode_context_set_trace(ctx, tracelevel, |
|
223 |
- tracehook, |
|
224 |
- tracehook_op, |
|
225 |
- tracehook_val, |
|
226 |
- tracehook_ptr); |
|
211 |
+ if (optget(opts, "describe")->enabled) { |
|
212 |
+ cli_bytecode_describe(bc); |
|
213 |
+ } else { |
|
227 | 214 |
|
228 |
- if (opts->filename[1]) { |
|
229 |
- funcid = atoi(opts->filename[1]); |
|
230 |
- } |
|
231 |
- cli_bytecode_context_setfuncid(ctx, bc, funcid); |
|
232 |
- printf("Running bytecode function :%u\n", funcid); |
|
215 |
+ ctx = cli_bytecode_context_alloc(); |
|
216 |
+ if (!ctx) { |
|
217 |
+ fprintf(stderr,"Out of memory\n"); |
|
218 |
+ exit(3); |
|
219 |
+ } |
|
220 |
+ memset(&dbg_state, 0, sizeof(dbg_state)); |
|
221 |
+ dbg_state.file = "<libclamav>"; |
|
222 |
+ dbg_state.line = 0; |
|
223 |
+ dbg_state.col = 0; |
|
224 |
+ dbg_state.showline = !optget(opts, "no-trace-showsource")->enabled; |
|
225 |
+ tracelevel = optget(opts, "trace")->numarg; |
|
226 |
+ cli_bytecode_context_set_trace(ctx, tracelevel, |
|
227 |
+ tracehook, |
|
228 |
+ tracehook_op, |
|
229 |
+ tracehook_val, |
|
230 |
+ tracehook_ptr); |
|
233 | 231 |
|
234 |
- if (opts->filename[1]) { |
|
235 |
- i=2; |
|
236 |
- while (opts->filename[i]) { |
|
237 |
- rc = cli_bytecode_context_setparam_int(ctx, i-2, atoi(opts->filename[i])); |
|
238 |
- if (rc != CL_SUCCESS) { |
|
239 |
- fprintf(stderr,"Unable to set param %u: %s\n", i-2, cl_strerror(rc)); |
|
240 |
- } |
|
241 |
- i++; |
|
232 |
+ if (opts->filename[1]) { |
|
233 |
+ funcid = atoi(opts->filename[1]); |
|
242 | 234 |
} |
243 |
- } |
|
235 |
+ cli_bytecode_context_setfuncid(ctx, bc, funcid); |
|
236 |
+ printf("Running bytecode function :%u\n", funcid); |
|
244 | 237 |
|
245 |
- if ((opt = optget(opts,"input"))->enabled) { |
|
246 |
- fmap_t *map; |
|
247 |
- fd = open(opt->strarg, O_RDONLY); |
|
248 |
- if (fd == -1) { |
|
249 |
- fprintf(stderr, "Unable to open input file %s: %s\n", opt->strarg, strerror(errno)); |
|
250 |
- optfree(opts); |
|
251 |
- exit(5); |
|
238 |
+ if (opts->filename[1]) { |
|
239 |
+ i=2; |
|
240 |
+ while (opts->filename[i]) { |
|
241 |
+ rc = cli_bytecode_context_setparam_int(ctx, i-2, atoi(opts->filename[i])); |
|
242 |
+ if (rc != CL_SUCCESS) { |
|
243 |
+ fprintf(stderr,"Unable to set param %u: %s\n", i-2, cl_strerror(rc)); |
|
244 |
+ } |
|
245 |
+ i++; |
|
246 |
+ } |
|
252 | 247 |
} |
253 |
- map = fmap(fd, 0, 0); |
|
254 |
- if (!map) { |
|
255 |
- fprintf(stderr, "Unable to map input file %s\n", opt->strarg); |
|
248 |
+ |
|
249 |
+ if ((opt = optget(opts,"input"))->enabled) { |
|
250 |
+ fmap_t *map; |
|
251 |
+ fd = open(opt->strarg, O_RDONLY); |
|
252 |
+ if (fd == -1) { |
|
253 |
+ fprintf(stderr, "Unable to open input file %s: %s\n", opt->strarg, strerror(errno)); |
|
254 |
+ optfree(opts); |
|
255 |
+ exit(5); |
|
256 |
+ } |
|
257 |
+ map = fmap(fd, 0, 0); |
|
258 |
+ if (!map) { |
|
259 |
+ fprintf(stderr, "Unable to map input file %s\n", opt->strarg); |
|
260 |
+ } |
|
261 |
+ rc = cli_bytecode_context_setfile(ctx, map); |
|
262 |
+ if (rc != CL_SUCCESS) { |
|
263 |
+ fprintf(stderr, "Unable to set file %s: %s\n", opt->strarg, cl_strerror(rc)); |
|
264 |
+ optfree(opts); |
|
265 |
+ exit(5); |
|
266 |
+ } |
|
267 |
+ funmap(map); |
|
256 | 268 |
} |
257 |
- rc = cli_bytecode_context_setfile(ctx, map); |
|
269 |
+ |
|
270 |
+ rc = cli_bytecode_run(&bcs, bc, ctx); |
|
258 | 271 |
if (rc != CL_SUCCESS) { |
259 |
- fprintf(stderr, "Unable to set file %s: %s\n", opt->strarg, cl_strerror(rc)); |
|
260 |
- optfree(opts); |
|
261 |
- exit(5); |
|
272 |
+ fprintf(stderr,"Unable to run bytecode: %s\n", cl_strerror(rc)); |
|
273 |
+ } else { |
|
274 |
+ uint64_t v; |
|
275 |
+ printf("Bytecode run finished\n"); |
|
276 |
+ v = cli_bytecode_context_getresult_int(ctx); |
|
277 |
+ printf("Bytecode returned: 0x%llx\n", (long long)v); |
|
262 | 278 |
} |
263 |
- funmap(map); |
|
264 |
- } |
|
265 |
- |
|
266 |
- rc = cli_bytecode_run(&bcs, bc, ctx); |
|
267 |
- if (rc != CL_SUCCESS) { |
|
268 |
- fprintf(stderr,"Unable to run bytecode: %s\n", cl_strerror(rc)); |
|
269 |
- } else { |
|
270 |
- uint64_t v; |
|
271 |
- printf("Bytecode run finished\n"); |
|
272 |
- v = cli_bytecode_context_getresult_int(ctx); |
|
273 |
- printf("Bytecode returned: 0x%llx\n", (long long)v); |
|
279 |
+ cli_bytecode_context_destroy(ctx); |
|
274 | 280 |
} |
275 |
- cli_bytecode_context_destroy(ctx); |
|
276 | 281 |
cli_bytecode_destroy(bc); |
277 | 282 |
cli_bytecode_done(&bcs); |
278 | 283 |
free(bc); |
... | ... |
@@ -438,14 +438,14 @@ static int parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linel |
438 | 438 |
return CL_BREAK; |
439 | 439 |
} |
440 | 440 |
// Optimistic parsing, check for error only at the end. |
441 |
- bc->verifier = readNumber(buffer, &offset, len, &ok); |
|
442 |
- bc->sigmaker = readString(buffer, &offset, len, &ok); |
|
443 |
- bc->id = readNumber(buffer, &offset, len, &ok); |
|
441 |
+ bc->metadata.timestamp = readNumber(buffer, &offset, len, &ok); |
|
442 |
+ bc->metadata.sigmaker = readString(buffer, &offset, len, &ok); |
|
443 |
+ bc->metadata.targetExclude = readNumber(buffer, &offset, len, &ok); |
|
444 | 444 |
bc->kind = readNumber(buffer, &offset, len, &ok); |
445 | 445 |
bc->metadata.maxStack = readNumber(buffer, &offset, len, &ok); |
446 | 446 |
bc->metadata.maxMem = readNumber(buffer, &offset, len, &ok); |
447 | 447 |
bc->metadata.maxTime = readNumber(buffer, &offset, len, &ok); |
448 |
- bc->metadata.targetExclude = readString(buffer, &offset, len, &ok); |
|
448 |
+ bc->metadata.compiler = readString(buffer, &offset, len, &ok); |
|
449 | 449 |
bc->num_types = readNumber(buffer, &offset, len, &ok); |
450 | 450 |
bc->num_func = readNumber(buffer, &offset, len, &ok); |
451 | 451 |
bc->state = bc_loaded; |
... | ... |
@@ -1405,8 +1405,8 @@ uint64_t cli_bytecode_context_getresult_int(struct cli_bc_ctx *ctx) |
1405 | 1405 |
void cli_bytecode_destroy(struct cli_bc *bc) |
1406 | 1406 |
{ |
1407 | 1407 |
unsigned i, j, k; |
1408 |
- free(bc->sigmaker); |
|
1409 |
- free(bc->metadata.targetExclude); |
|
1408 |
+ free(bc->metadata.compiler); |
|
1409 |
+ free(bc->metadata.sigmaker); |
|
1410 | 1410 |
|
1411 | 1411 |
for (i=0;i<bc->num_func;i++) { |
1412 | 1412 |
struct cli_bc_func *f = &bc->funcs[i]; |
... | ... |
@@ -1742,3 +1742,88 @@ void cli_bytecode_context_setctx(struct cli_bc_ctx *ctx, void *cctx) |
1742 | 1742 |
{ |
1743 | 1743 |
ctx->ctx = cctx; |
1744 | 1744 |
} |
1745 |
+ |
|
1746 |
+void cli_bytecode_describe(const struct cli_bc *bc) |
|
1747 |
+{ |
|
1748 |
+ char buf[128]; |
|
1749 |
+ int cols; |
|
1750 |
+ unsigned i; |
|
1751 |
+ time_t stamp; |
|
1752 |
+ int had; |
|
1753 |
+ |
|
1754 |
+ if (!bc) { |
|
1755 |
+ printf("(null bytecode)\n"); |
|
1756 |
+ return; |
|
1757 |
+ } |
|
1758 |
+ |
|
1759 |
+ stamp = bc->metadata.timestamp; |
|
1760 |
+ printf("Bytecode format functionality level: %u\n", BC_FUNC_LEVEL); |
|
1761 |
+ printf("Bytecode metadata:\n\tcompiler version: %s\n", |
|
1762 |
+ bc->metadata.compiler ? bc->metadata.compiler : "N/A"); |
|
1763 |
+ printf("\tcompiled on: %s\n", |
|
1764 |
+ cli_ctime(&stamp, buf, sizeof(buf))); |
|
1765 |
+ printf("\tcompiled by: %s\n", bc->metadata.sigmaker ? bc->metadata.sigmaker : "N/A"); |
|
1766 |
+ //TODO: parse and display arch name, also take it into account when |
|
1767 |
+ //JITing! |
|
1768 |
+ printf("\ttarget exclude: %d\n", bc->metadata.targetExclude); |
|
1769 |
+ printf("\tbytecode type: "); |
|
1770 |
+ switch (bc->kind) { |
|
1771 |
+ case BC_GENERIC: |
|
1772 |
+ puts("generic, not loadable by clamscan/clamd"); |
|
1773 |
+ break; |
|
1774 |
+ case BC_LOGICAL: |
|
1775 |
+ puts("logical only"); |
|
1776 |
+ break; |
|
1777 |
+ case BC_PE_UNPACKER: |
|
1778 |
+ puts("PE hook"); |
|
1779 |
+ break; |
|
1780 |
+ default: |
|
1781 |
+ printf("Unknown (type %u)", bc->kind); |
|
1782 |
+ break; |
|
1783 |
+ } |
|
1784 |
+ printf("\tbytecode logical signature: %s\n", |
|
1785 |
+ bc->lsig ? bc->lsig : "<none>"); |
|
1786 |
+ printf("\tvirusname prefix: %s\n", |
|
1787 |
+ bc->vnameprefix); |
|
1788 |
+ printf("\tvirusnames: %u\n", bc->vnames_cnt); |
|
1789 |
+ printf("\tbytecode triggered on: "); |
|
1790 |
+ switch (bc->kind) { |
|
1791 |
+ case BC_GENERIC: |
|
1792 |
+ puts("N/A (loaded in clambc only)"); |
|
1793 |
+ break; |
|
1794 |
+ case BC_LOGICAL: |
|
1795 |
+ puts("files matching logical signature"); |
|
1796 |
+ break; |
|
1797 |
+ case BC_PE_UNPACKER: |
|
1798 |
+ if (bc->lsig) |
|
1799 |
+ puts("PE files matching logical signature"); |
|
1800 |
+ else |
|
1801 |
+ puts("all PE files!"); |
|
1802 |
+ break; |
|
1803 |
+ default: |
|
1804 |
+ puts("N/A (unknown type)\n"); |
|
1805 |
+ break; |
|
1806 |
+ } |
|
1807 |
+ printf("\tnumber of functions: %u\n\tnumber of types: %u\n", |
|
1808 |
+ bc->num_func, bc->num_types); |
|
1809 |
+ printf("\tnumber of global constants: %u\n", bc->num_globals); |
|
1810 |
+ printf("\tnumber of debug nodes: %u\n", bc->dbgnode_cnt); |
|
1811 |
+ printf("\tbytecode APIs used:"); |
|
1812 |
+ cols = 0; /* remaining */ |
|
1813 |
+ had = 0; |
|
1814 |
+ for (i=0;i<cli_apicall_maxapi;i++) { |
|
1815 |
+ if (cli_bitset_test(bc->uses_apis, i)) { |
|
1816 |
+ unsigned len = strlen(cli_apicalls[i].name); |
|
1817 |
+ if (had) |
|
1818 |
+ printf(","); |
|
1819 |
+ if (len > cols) { |
|
1820 |
+ printf("\n\t"); |
|
1821 |
+ cols = 72; |
|
1822 |
+ } |
|
1823 |
+ printf(" %s", cli_apicalls[i].name); |
|
1824 |
+ had = 1; |
|
1825 |
+ cols -= len; |
|
1826 |
+ } |
|
1827 |
+ } |
|
1828 |
+ printf("\n"); |
|
1829 |
+} |
... | ... |
@@ -45,11 +45,9 @@ enum bc_state { |
45 | 45 |
}; |
46 | 46 |
|
47 | 47 |
struct cli_bc { |
48 |
- unsigned verifier; |
|
49 |
- char *sigmaker; |
|
48 |
+ struct bytecode_metadata metadata; |
|
50 | 49 |
unsigned id; |
51 | 50 |
unsigned kind; |
52 |
- struct bytecode_metadata metadata; |
|
53 | 51 |
unsigned num_types; |
54 | 52 |
unsigned num_func; |
55 | 53 |
struct cli_bc_func *funcs; |
... | ... |
@@ -105,6 +103,7 @@ int cli_bytecode_prepare(struct cli_all_bc *allbc); |
105 | 105 |
int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx); |
106 | 106 |
void cli_bytecode_destroy(struct cli_bc *bc); |
107 | 107 |
int cli_bytecode_done(struct cli_all_bc *allbc); |
108 |
+void cli_bytecode_describe(const struct cli_bc *bc); |
|
108 | 109 |
|
109 | 110 |
/* Hooks */ |
110 | 111 |
struct cli_exe_info; |
... | ... |
@@ -23,9 +23,12 @@ |
23 | 23 |
#define CLAMBC_H |
24 | 24 |
|
25 | 25 |
struct bytecode_metadata { |
26 |
- unsigned long maxStack, maxMem; |
|
27 |
- unsigned long maxTime; |
|
28 |
- char *targetExclude; |
|
26 |
+ char *compiler; |
|
27 |
+ char *sigmaker; |
|
28 |
+ uint64_t timestamp; |
|
29 |
+ unsigned long maxStack, maxMem; |
|
30 |
+ unsigned long maxTime; |
|
31 |
+ unsigned targetExclude; |
|
29 | 32 |
}; |
30 | 33 |
|
31 | 34 |
#define BC_FUNC_LEVEL 5 |
... | ... |
@@ -1333,6 +1333,7 @@ static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, |
1333 | 1333 |
} |
1334 | 1334 |
bcs->count++; |
1335 | 1335 |
bc = &bcs->all_bcs[bcs->count-1]; |
1336 |
+ bc->id = bcs->count; |
|
1336 | 1337 |
|
1337 | 1338 |
switch (engine->bytecode_security) { |
1338 | 1339 |
case CL_BYTECODE_TRUST_ALL: |
... | ... |
@@ -1362,7 +1363,7 @@ static int cli_loadcbc(FILE *fs, struct cl_engine *engine, unsigned int *signo, |
1362 | 1362 |
cli_errmsg("Bytecode %s has logical kind, but missing logical signature!\n", dbname); |
1363 | 1363 |
return CL_EMALFDB; |
1364 | 1364 |
} |
1365 |
- cli_dbgmsg("Bytecode %s has logical signature: %s\n", dbname, bc->lsig); |
|
1365 |
+ cli_dbgmsg("Bytecode %s(%u) has logical signature: %s\n", dbname, bc->id, bc->lsig); |
|
1366 | 1366 |
rc = load_oneldb(bc->lsig, 0, 0, engine, options, dbname, 0, &sigs, bc, NULL); |
1367 | 1367 |
if (rc != CL_SUCCESS) { |
1368 | 1368 |
cli_errmsg("Problem parsing logical signature %s for bytecode %s: %s\n", |
... | ... |
@@ -121,6 +121,7 @@ const struct clam_option __clam_options[] = { |
121 | 121 |
{ NULL, "generate-config", 'g', TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMCONF, "", "" }, |
122 | 122 |
|
123 | 123 |
{ NULL, "force-interpreter", 'f', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMBC, "Force using the interpreter instead of the JIT", "" }, |
124 |
+ { NULL, "describe", 'd', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMBC, "Load and describe bytecode without executing", ""}, |
|
124 | 125 |
{ NULL, "input", 'i', TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMBC, "Input file to run the bytecode n", ""}, |
125 | 126 |
{ NULL, "trace", 't', TYPE_NUMBER, MATCH_NUMBER, 7, NULL, 0, OPT_CLAMBC, "bytecode trace level",""}, |
126 | 127 |
{ NULL, "no-trace-showsource", 's', TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMBC, "Don't show source line during tracing",""}, |