Browse code

add support for PE section based MD5 signatures

git-svn: trunk@2310

Tomasz Kojm authored on 2006/09/27 09:36:11
Showing 5 changed files
... ...
@@ -1,3 +1,9 @@
1
+Wed Sep 27 02:34:00 CEST 2006 (tk)
2
+----------------------------------
3
+  * libclamav: add support for PE section based MD5 signatures (stored in .mdb)
4
+	       Requested by Christoph
5
+  * sigtool: handle .mdb databases
6
+
1 7
 Tue Sep 26 17:42:17 BST 2006 (njh)
2 8
 ----------------------------------
3 9
   * libclamav/mbox.c:	Experimental curl removal: fix proxy handling and
... ...
@@ -170,6 +170,9 @@ struct cl_engine {
170 170
     /* MD5 */
171 171
     struct cli_md5_node **md5_hlist;
172 172
 
173
+    /* MD5 list for PE sections */
174
+    struct cli_md5_node *md5_sect;
175
+
173 176
     /* Zip metadata */
174 177
     struct cli_meta_node *zip_mlist;
175 178
 
... ...
@@ -49,6 +49,7 @@
49 49
 #include "rebuildpe.h"
50 50
 #include "str.h"
51 51
 #include "execs.h"
52
+#include "md5.h"
52 53
 
53 54
 #ifndef	O_BINARY
54 55
 #define	O_BINARY	0
... ...
@@ -173,6 +174,49 @@ static int cli_ddump(int desc, int offset, int size, const char *file)
173 173
 }
174 174
 */
175 175
 
176
+static unsigned char *cli_md5sect(int fd, uint32_t offset, uint32_t size)
177
+{
178
+	size_t bread, sum = 0;
179
+	off_t pos;
180
+	char buff[FILEBUFF];
181
+	unsigned char *digest;
182
+	MD5_CTX md5ctx;
183
+
184
+
185
+    if((pos = lseek(fd, 0, SEEK_CUR)) == -1) {
186
+	cli_dbgmsg("cli_md5sect: Invalid descriptor %d\n", fd);
187
+	return NULL;
188
+    }
189
+
190
+    if(lseek(fd, offset, SEEK_SET) == -1) {
191
+	cli_dbgmsg("cli_md5sect: lseek() failed\n");
192
+	lseek(fd, pos, SEEK_SET);
193
+	return NULL;
194
+    }
195
+
196
+    digest = cli_calloc(16, sizeof(char));
197
+    if(!digest) {
198
+	cli_errmsg("cli_md5sect: Can't allocate memory for digest\n");
199
+	return NULL;
200
+    }
201
+
202
+    MD5_Init(&md5ctx);
203
+
204
+    while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) {
205
+	if(sum + bread >= size) {
206
+	    MD5_Update(&md5ctx, buff, size - sum);
207
+	    break;
208
+	} else {
209
+	    MD5_Update(&md5ctx, buff, bread);
210
+	    sum += bread;
211
+	}
212
+    }
213
+
214
+    MD5_Final(digest, &md5ctx);
215
+    lseek(fd, pos, SEEK_SET);
216
+    return digest;
217
+}
218
+
176 219
 int cli_scanpe(int desc, cli_ctx *ctx)
177 220
 {
178 221
 	uint16_t e_magic; /* DOS signature ("MZ") */
... ...
@@ -185,9 +229,10 @@ int cli_scanpe(int desc, cli_ctx *ctx)
185 185
 	struct pe_image_optional_hdr32 optional_hdr32;
186 186
 	struct pe_image_optional_hdr64 optional_hdr64;
187 187
 	struct pe_image_section_hdr *section_hdr;
188
+	struct cli_md5_node *md5_sect;
188 189
 	struct stat sb;
189 190
 	char sname[9], buff[4096], *tempfile;
190
-	unsigned char *ubuff;
191
+	unsigned char *ubuff, *md5_dig;
191 192
 	ssize_t bytes;
192 193
 	unsigned int i, found, upx_success = 0, min = 0, max = 0, err, broken = 0;
193 194
 	unsigned int ssize = 0, dsize = 0, dll = 0, pe_plus = 0;
... ...
@@ -561,6 +606,31 @@ int cli_scanpe(int desc, cli_ctx *ctx)
561 561
 		return CL_VIRUS;
562 562
 	    }
563 563
 	    broken = 1;
564
+
565
+	} else {
566
+	    /* check MD5 section sigs */
567
+	    md5_sect = ctx->engine->md5_sect;
568
+	    while(md5_sect && md5_sect->size < EC32(section_hdr[i].SizeOfRawData))
569
+		md5_sect = md5_sect->next;
570
+
571
+	    if(md5_sect && md5_sect->size == EC32(section_hdr[i].SizeOfRawData)) {
572
+		md5_dig = cli_md5sect(desc, EC32(section_hdr[i].PointerToRawData), EC32(section_hdr[i].SizeOfRawData));
573
+		if(!md5_dig) {
574
+		    cli_errmsg("PE: Can't calculate MD5 for section %d\n", i);
575
+		} else {
576
+		    while(md5_sect && md5_sect->size == EC32(section_hdr[i].SizeOfRawData)) {
577
+			if(!strcmp(md5_dig, md5_sect->md5)) {
578
+			    if(ctx->virname)
579
+				*ctx->virname = md5_sect->virname;
580
+			    free(md5_dig);
581
+			    free(section_hdr);
582
+			    return CL_VIRUS;
583
+			}
584
+			md5_sect = md5_sect->next;
585
+		    }
586
+		    free(md5_dig);
587
+		}
588
+	    }
564 589
 	}
565 590
 
566 591
 	if(!i) {
... ...
@@ -584,6 +654,8 @@ int cli_scanpe(int desc, cli_ctx *ctx)
584 584
 
585 585
     }
586 586
 
587
+
588
+
587 589
     if(pe_plus)
588 590
 	ep = EC32(optional_hdr64.AddressOfEntryPoint);
589 591
     else
... ...
@@ -739,11 +739,11 @@ static int cli_loadndb(FILE *fd, struct cl_engine **engine, unsigned int *signo,
739 739
     return CL_SUCCESS;
740 740
 }
741 741
 
742
-static int cli_loadhdb(FILE *fd, struct cl_engine **engine, unsigned int *signo, unsigned short fp, unsigned int options)
742
+static int cli_loadhdb(FILE *fd, struct cl_engine **engine, unsigned int *signo, unsigned short mode, unsigned int options)
743 743
 {
744 744
 	char buffer[FILEBUFF], *pt;
745 745
 	int line = 0, ret = 0;
746
-	struct cli_md5_node *new;
746
+	struct cli_md5_node *new, *mpt, *last;
747 747
 
748 748
 
749 749
     if((ret = cli_initengine(engine, options))) {
... ...
@@ -761,7 +761,8 @@ static int cli_loadhdb(FILE *fd, struct cl_engine **engine, unsigned int *signo,
761 761
 	    break;
762 762
 	}
763 763
 
764
-	new->fp = fp;
764
+	if(mode == 1) /* fp */
765
+	    new->fp = 1;
765 766
 
766 767
 	if(!(pt = cli_strtok(buffer, 0, ":"))) {
767 768
 	    free(new);
... ...
@@ -796,17 +797,38 @@ static int cli_loadhdb(FILE *fd, struct cl_engine **engine, unsigned int *signo,
796 796
 
797 797
 	new->viralias = cli_strtok(buffer, 3, ":"); /* aliases are optional */
798 798
 
799
-	if(!(*engine)->md5_hlist) {
800
-	    cli_dbgmsg("Initializing md5 list structure\n");
801
-	    (*engine)->md5_hlist = (struct cli_md5_node **) cli_calloc(256, sizeof(struct cli_md5_node *));
799
+	if(mode == 2) { /* section MD5 */
800
+	    if(!(*engine)->md5_sect) {
801
+		(*engine)->md5_sect = new;
802
+	    } else {
803
+		if(new->size < (*engine)->md5_sect->size) {
804
+		    new->next = (*engine)->md5_sect;
805
+		    (*engine)->md5_sect = new;
806
+		} else {
807
+		    mpt = (*engine)->md5_sect;
808
+		    while(mpt) {
809
+			last = mpt;
810
+			if((mpt->size < new->size) && (!mpt->next || new->size < mpt->next->size))
811
+			    break;
812
+			mpt = mpt->next;
813
+		    }
814
+		    new->next = last->next;
815
+		    last->next = new;
816
+		}
817
+	    }
818
+	} else {
802 819
 	    if(!(*engine)->md5_hlist) {
803
-		ret = CL_EMEM;
804
-		break;
820
+		cli_dbgmsg("Initializing md5 list structure\n");
821
+		(*engine)->md5_hlist = (struct cli_md5_node **) cli_calloc(256, sizeof(struct cli_md5_node *));
822
+		if(!(*engine)->md5_hlist) {
823
+		    ret = CL_EMEM;
824
+		    break;
825
+		}
805 826
 	    }
806
-	}
807 827
 
808
-	new->next = (*engine)->md5_hlist[new->md5[0] & 0xff];
809
-	(*engine)->md5_hlist[new->md5[0] & 0xff] = new;
828
+	    new->next = (*engine)->md5_hlist[new->md5[0] & 0xff];
829
+	    (*engine)->md5_hlist[new->md5[0] & 0xff] = new;
830
+	}
810 831
     }
811 832
 
812 833
     if(!line) {
... ...
@@ -1065,6 +1087,9 @@ static int cli_load(const char *filename, struct cl_engine **engine, unsigned in
1065 1065
     } else if(cli_strbcasestr(filename, ".fp")) {
1066 1066
 	ret = cli_loadhdb(fd, engine, signo, 1, options);
1067 1067
 
1068
+    } else if(cli_strbcasestr(filename, ".mdb")) {
1069
+	ret = cli_loadhdb(fd, engine, signo, 2, options);
1070
+
1068 1071
     } else if(cli_strbcasestr(filename, ".ndb")) {
1069 1072
 	if(options & CL_DB_HWACCEL)
1070 1073
 	    skipped = 1;
... ...
@@ -1163,6 +1188,7 @@ static int cli_loaddbdir(const char *dirname, struct cl_engine **engine, unsigne
1163 1163
 	     cli_strbcasestr(dent->d_name, ".db3")  ||
1164 1164
 	     cli_strbcasestr(dent->d_name, ".hdb")  ||
1165 1165
 	     cli_strbcasestr(dent->d_name, ".fp")   ||
1166
+	     cli_strbcasestr(dent->d_name, ".mdb")  ||
1166 1167
 	     cli_strbcasestr(dent->d_name, ".ndb")  ||
1167 1168
 	     cli_strbcasestr(dent->d_name, ".sdb")  ||
1168 1169
 	     cli_strbcasestr(dent->d_name, ".zmd")  ||
... ...
@@ -1283,6 +1309,7 @@ int cl_statinidir(const char *dirname, struct cl_stat *dbstat)
1283 1283
 	    cli_strbcasestr(dent->d_name, ".db3")  || 
1284 1284
 	    cli_strbcasestr(dent->d_name, ".hdb")  || 
1285 1285
 	    cli_strbcasestr(dent->d_name, ".fp")   || 
1286
+	    cli_strbcasestr(dent->d_name, ".mdb")  ||
1286 1287
 	    cli_strbcasestr(dent->d_name, ".ndb")  || 
1287 1288
 	    cli_strbcasestr(dent->d_name, ".sdb")  || 
1288 1289
 	    cli_strbcasestr(dent->d_name, ".zmd")  || 
... ...
@@ -1361,6 +1388,7 @@ int cl_statchkdir(const struct cl_stat *dbstat)
1361 1361
 	    cli_strbcasestr(dent->d_name, ".db3")  || 
1362 1362
 	    cli_strbcasestr(dent->d_name, ".hdb")  || 
1363 1363
 	    cli_strbcasestr(dent->d_name, ".fp")   || 
1364
+	    cli_strbcasestr(dent->d_name, ".mdb")  ||
1364 1365
 	    cli_strbcasestr(dent->d_name, ".ndb")  || 
1365 1366
 	    cli_strbcasestr(dent->d_name, ".sdb")  || 
1366 1367
 	    cli_strbcasestr(dent->d_name, ".zmd")  || 
... ...
@@ -270,7 +270,7 @@ static int writeinfo(const char *db, const char *header)
270 270
 	int i;
271 271
 	struct stat sb;
272 272
 	char file[32], *md5;
273
-	char *extlist[] = { "db", "fp", "hdb", "ndb", "rmd", "zmd", "sdb", NULL };
273
+	char *extlist[] = { "db", "fp", "hdb", "mdb", "ndb", "rmd", "zmd", "sdb", NULL };
274 274
 
275 275
 
276 276
     snprintf(file, sizeof(file), "%s.info", db);
... ...
@@ -345,6 +345,7 @@ static int build(struct optstruct *opt)
345 345
 
346 346
     if(stat("main.db", &foo) == -1 && stat("daily.db", &foo) == -1 &&
347 347
        stat("main.hdb", &foo) == -1 && stat("daily.hdb", &foo) == -1 &&
348
+       stat("main.mdb", &foo) == -1 && stat("daily.mdb", &foo) == -1 &&
348 349
        stat("main.ndb", &foo) == -1 && stat("daily.ndb", &foo) == -1 &&
349 350
        stat("main.sdb", &foo) == -1 && stat("daily.sdb", &foo) == -1 &&
350 351
        stat("main.zmd", &foo) == -1 && stat("daily.zmd", &foo) == -1 &&
... ...
@@ -366,6 +367,7 @@ static int build(struct optstruct *opt)
366 366
     } else {
367 367
 	lines = countlines("main.db") + countlines("daily.db") +
368 368
 		countlines("main.hdb") + countlines("daily.hdb") +
369
+		countlines("main.mdb") + countlines("daily.mdb") +
369 370
 		countlines("main.ndb") + countlines("daily.ndb") +
370 371
 		countlines("main.sdb") + countlines("daily.sdb") +
371 372
 		countlines("main.zmd") + countlines("daily.zmd") +
... ...
@@ -481,7 +483,8 @@ static int build(struct optstruct *opt)
481 481
 				 "main.ndb", "daily.ndb", "main.sdb",
482 482
 				 "daily.sdb", "main.zmd", "daily.zmd",
483 483
 				 "main.rmd", "daily.rmd", "main.fp",
484
-				 "daily.fp", "daily.info", "main.info",
484
+				 "daily.fp", "main.mdb", "daily.mdb",
485
+				 "daily.info", "main.info",
485 486
 #ifdef CL_EXPERIMENTAL
486 487
 				 /* TODO: add support for main.[wp]db */
487 488
 				 "daily.wdb","daily.pdb",
... ...
@@ -817,6 +820,7 @@ static int listdir(const char *dirname)
817 817
 	    if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") &&
818 818
 	    (cli_strbcasestr(dent->d_name, ".db")  ||
819 819
 	     cli_strbcasestr(dent->d_name, ".hdb") ||
820
+	     cli_strbcasestr(dent->d_name, ".mdb") ||
820 821
 	     cli_strbcasestr(dent->d_name, ".ndb") ||
821 822
 	     cli_strbcasestr(dent->d_name, ".sdb") ||
822 823
 	     cli_strbcasestr(dent->d_name, ".zmd") ||
... ...
@@ -944,7 +948,7 @@ static int listdb(const char *filename)
944 944
 	    mprintf("%s\n", start);
945 945
 	}
946 946
 
947
-    } else if(cli_strbcasestr(filename, ".hdb")) { /* hash database */
947
+    } else if(cli_strbcasestr(filename, ".hdb") || cli_strbcasestr(filename, ".mdb")) { /* hash database */
948 948
 
949 949
 	while(fgets(buffer, FILEBUFF, fd)) {
950 950
 	    line++;