Browse code

print bytecode metadata.

Török Edvin authored on 2010/01/22 23:50:16
Showing 7 changed files
... ...
@@ -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
... ...
@@ -170,6 +170,7 @@ CLAMAV_PRIVATE {
170 170
     cli_bytecode_context_set_trace;
171 171
     cli_bytecode_debug_printsrc;
172 172
     cli_bytecode_printversion;
173
+    cli_bytecode_describe;
173 174
   local:
174 175
     *;
175 176
 };
... ...
@@ -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",""},