Browse code

try to detect broken executables

git-svn-id: file:///var/lib/svn/clamav-devel/trunk/clamav-devel@724 77e5149b-7576-45b1-b177-96237e5ba77b

Tomasz Kojm authored on 2004/08/05 05:11:18
Showing 11 changed files
... ...
@@ -1,3 +1,12 @@
1
+Wed Aug  4 22:03:56 CEST 2004 (tk)
2
+----------------------------------
3
+  * libclamav: pe: improve detection of broken executable files
4
+  * libclamav: new scan option CL_BROKEN (report broken executables as
5
+	       Broken.Executable)
6
+  * clamscan: new option --detect-broken
7
+  * clamd: new directive DetectBrokenExecutables
8
+  * docs: update manual pages
9
+
1 10
 Wed Aug  4 19:59:54 BST 2004 (njh)
2 11
 ----------------------------------
3 12
   * libclamav:	Improved the decoding of multipart messages and MIME headers
... ...
@@ -314,6 +314,12 @@ int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *cop
314 314
     if(cfgopt(copt, "ScanPE")) {
315 315
 	logg("Portable Executable support enabled.\n");
316 316
 	options |= CL_PE;
317
+
318
+	if(cfgopt(copt, "DetectBrokenExecutables")) {
319
+	    logg("Detection of broken executables enabled.\n");
320
+	    options |= CL_BROKEN;
321
+	}
322
+
317 323
     } else {
318 324
 	logg("Portable Executable support disabled.\n");
319 325
     }
... ...
@@ -224,10 +224,12 @@ void help(void)
224 224
     mprintf("    --no-ole2                            Disable OLE2 support\n");
225 225
     mprintf("    --no-html                            Disable HTML support\n");
226 226
     mprintf("    --no-archive                         Disable libclamav archive support\n");
227
-    mprintf("    --block-encrypted                    Block encrypted archives.\n");
227
+    mprintf("    --detect-broken                      Try to detect broken executable files\n");
228
+    mprintf("    --block-encrypted                    Block encrypted archives\n");
228 229
     mprintf("    --max-space=#n                       Extract first #n kilobytes only\n");
229 230
     mprintf("    --max-files=#n                       Extract first #n files only\n");
230 231
     mprintf("    --max-recursion=#n                   Maximal recursion level\n");
232
+    mprintf("    --max-ratio=#n                       Maximum compression ratio limit\n");
231 233
     mprintf("    --unzip[=FULLPATH]                   Enable support for .zip files\n");
232 234
     mprintf("    --unrar[=FULLPATH]                   Enable support for .rar files\n");
233 235
     mprintf("    --arj[=FULLPATH]                     Enable support for .arj files\n");
... ...
@@ -421,6 +421,9 @@ int scanfile(const char *filename, struct cl_node *root, const struct passwd *us
421 421
     else
422 422
 	options |= CL_ARCHIVE;
423 423
 
424
+    if(optl(opt, "detect-broken"))
425
+	options |= CL_BROKEN;
426
+
424 427
     if(optl(opt, "block-encrypted"))
425 428
 	options |= CL_ENCRYPTED;
426 429
 
... ...
@@ -89,6 +89,7 @@ int main(int argc, char **argv)
89 89
 	    {"max-recursion", 1, 0, 0},
90 90
 	    {"disable-archive", 0, 0, 0},
91 91
 	    {"no-archive", 0, 0, 0},
92
+	    {"detect-broken", 0, 0, 0},
92 93
 	    {"block-encrypted", 0, 0, 0},
93 94
 	    {"no-pe", 0, 0, 0},
94 95
 	    {"no-ole2", 0, 0, 0},
... ...
@@ -1,5 +1,5 @@
1 1
 .\" Manual page created by Tomasz Kojm, 20021001.
2
-.TH "clamav.conf" "5" "March 14, 2004" "Tomasz Kojm" "Clam AntiVirus"
2
+.TH "clamav.conf" "5" "August 4, 2004" "Tomasz Kojm" "Clam AntiVirus"
3 3
 .SH "NAME"
4 4
 .LP 
5 5
 \fBclamav.conf\fR \- a configuration file for Clam AntiVirus Daemon
... ...
@@ -164,6 +164,11 @@ PE stands for Portable Executable \- it's an executable file format used in all
164 164
 .br 
165 165
 Default: enabled.
166 166
 .TP 
167
+\fBDetectBrokenExecutables\fR
168
+With this option clamav will try to detect broken executables and mark them as Broken.Executable.
169
+.br 
170
+Default: disabled.
171
+.TP 
167 172
 \fBScanOLE2\fR
168 173
 Enables scanning of Microsoft Office document macros.
169 174
 .br 
... ...
@@ -1,5 +1,5 @@
1 1
 .\" Manual page created by Tomasz Kojm, 14/15 IV 2002
2
-.TH "clamscan" "1" "March 14, 2004" "Tomasz Kojm" "Clam AntiVirus"
2
+.TH "clamscan" "1" "August 4, 2004" "Tomasz Kojm" "Clam AntiVirus"
3 3
 .SH "NAME"
4 4
 .LP 
5 5
 clamscan \- scan files and directories against viruses
... ...
@@ -82,6 +82,9 @@ Disable support for HTML detection and normalisation.
82 82
 \fB\-\-no\-archive\fR
83 83
 Disable archive support built in libclamav.
84 84
 .TP 
85
+\fB\-\-detect\-broken\fR
86
+Mark broken executables as viruses (Broken.Executable).
87
+.TP 
85 88
 \fB\-\-block\-encrypted\fR
86 89
 Mark encrypted archives as viruses (Encrypted.Zip, Encrypted.RAR).
87 90
 .TP 
... ...
@@ -144,6 +144,11 @@ MaxDirectoryRecursion 15
144 144
 # required for decompression of popular executable packers such as UPX.
145 145
 ScanPE
146 146
 
147
+# With this option clamav will try to detect broken executables and mark
148
+# them as Broken.Executable
149
+#DetectBrokenExecutables
150
+
151
+
147 152
 ##
148 153
 ## Documents
149 154
 ##
... ...
@@ -73,6 +73,7 @@ extern "C"
73 73
 #define CL_ENCRYPTED    16
74 74
 #define CL_HTML		32
75 75
 #define CL_PE		64
76
+#define CL_BROKEN	128
76 77
 
77 78
 
78 79
 struct cli_bm_patt {
... ...
@@ -42,6 +42,8 @@
42 42
 #define IMAGE_NT_SIGNATURE	    0x00004550
43 43
 #define IMAGE_OPTIONAL_SIGNATURE    0x010b
44 44
 
45
+#define DETECT_BROKEN		    (options & CL_BROKEN)
46
+
45 47
 #define UPX_NRV2B "\x11\xdb\x11\xc9\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9\x11\xc9\x75\x20\x41\x01\xdb"
46 48
 #define UPX_NRV2D "\x83\xf0\xff\x74\x78\xd1\xf8\x89\xc5\xeb\x0b\x01\xdb\x75\x07\x8b\x1e\x83\xee\xfc\x11\xdb\x11\xc9"
47 49
 #define UPX_NRV2E "\xeb\x52\x31\xc9\x83\xe8\x03\x72\x11\xc1\xe0\x08\x8a\x06\x46\x83\xf0\xff\x74\x75\xd1\xf8\x89\xc5"
... ...
@@ -213,7 +215,6 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
213 213
 	return CL_CLEAN;
214 214
     }
215 215
 
216
-    /* cli_dbgmsg("Machine type: "); */
217 216
     switch(EC16(file_hdr.Machine)) {
218 217
 	case 0x14c:
219 218
 	    cli_dbgmsg("Machine type: 80386\n");
... ...
@@ -253,12 +254,22 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
253 253
 
254 254
     if(EC16(file_hdr.SizeOfOptionalHeader) != sizeof(struct pe_image_optional_hdr)) {
255 255
 	cli_warnmsg("Broken PE header detected.\n");
256
+	if(DETECT_BROKEN) {
257
+	    if(virname)
258
+		*virname = "Broken.Executable";
259
+	    return CL_VIRUS;
260
+	}
256 261
 	return CL_CLEAN;
257 262
     }
258 263
 
259 264
     if(read(desc, &optional_hdr, sizeof(struct pe_image_optional_hdr)) != sizeof(struct pe_image_optional_hdr)) {
260 265
 	cli_dbgmsg("Can't optional file header\n");
261
-	return CL_EIO;
266
+	if(DETECT_BROKEN) {
267
+	    if(virname)
268
+		*virname = "Broken.Executable";
269
+	    return CL_VIRUS;
270
+	}
271
+	return CL_CLEAN;
262 272
     }
263 273
 
264 274
     cli_dbgmsg("MajorLinkerVersion: %d\n", optional_hdr.MajorLinkerVersion);
... ...
@@ -304,12 +315,23 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
304 304
 	return CL_EMEM;
305 305
     }
306 306
 
307
+    if(fstat(desc, &sb) == -1) {
308
+	cli_dbgmsg("fstat failed\n");
309
+	free(section_hdr);
310
+	return CL_EIO;
311
+    }
312
+
307 313
     for(i = 0; i < nsections; i++) {
308 314
 
309 315
 	if(read(desc, &section_hdr[i], sizeof(struct pe_image_section_hdr)) != sizeof(struct pe_image_section_hdr)) {
310 316
 	    cli_dbgmsg("Can't read section header\n");
311 317
 	    cli_dbgmsg("Possibly broken PE file\n");
312 318
 	    free(section_hdr);
319
+	    if(DETECT_BROKEN) {
320
+		if(virname)
321
+		    *virname = "Broken.Executable";
322
+		return CL_VIRUS;
323
+	    }
313 324
 	    return CL_CLEAN;
314 325
 	}
315 326
 
... ...
@@ -339,6 +361,17 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
339 339
 	    cli_dbgmsg("Section's memory is executable\n");
340 340
 	cli_dbgmsg("------------------------------------\n");
341 341
 
342
+	if(EC32(section_hdr[i].PointerToRawData) + EC32(section_hdr[i].SizeOfRawData) > sb.st_size) {
343
+	    cli_dbgmsg("Possibly broken PE file - Section %d out of file (Offset@ %d, Rsize %d, Total filesize %d)\n", i, EC32(section_hdr[i].PointerToRawData), EC32(section_hdr[i].SizeOfRawData), sb.st_size);
344
+	    free(section_hdr);
345
+	    if(DETECT_BROKEN) {
346
+		if(virname)
347
+		    *virname = "Broken.Executable";
348
+		return CL_VIRUS;
349
+	    }
350
+	    return CL_CLEAN;
351
+	}
352
+
342 353
 	if(!i) {
343 354
 	    min = EC32(section_hdr[i].VirtualAddress);
344 355
 	    max = EC32(section_hdr[i].VirtualAddress) + EC32(section_hdr[i].SizeOfRawData);
... ...
@@ -349,33 +382,22 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
349 349
 	    if(EC32(section_hdr[i].VirtualAddress) + EC32(section_hdr[i].SizeOfRawData) > max)
350 350
 		max = EC32(section_hdr[i].VirtualAddress) + EC32(section_hdr[i].SizeOfRawData);
351 351
 	}
352
-    }
353 352
 
354
-    if(fstat(desc, &sb) == -1) {
355
-	cli_dbgmsg("fstat failed\n");
356
-	free(section_hdr);
357
-	return CL_EIO;
358 353
     }
359 354
 
360 355
     if((ep = cli_rawaddr(EC32(optional_hdr.AddressOfEntryPoint), section_hdr, nsections)) == -1) {
361 356
 	cli_dbgmsg("Possibly broken PE file\n");
362
-	broken = 1;
363
-    }
364
-
365
-    /* simple sanity check */
366
-    if(!broken && EC32(section_hdr[nsections - 1].PointerToRawData) + EC32(section_hdr[nsections - 1].SizeOfRawData) > sb.st_size) {
367
-	cli_dbgmsg("Possibly broken PE file - Section %d out of file (Offset@ %d, Rsize %d, Total filesize %d, EP@ %d)\n", nsections - 1, EC32(section_hdr[nsections - 1].PointerToRawData), EC32(section_hdr[nsections - 1].SizeOfRawData), sb.st_size, ep);
368
-	broken = 1;
369
-    }
370
-
371
-    if(broken) {
372 357
 	free(section_hdr);
358
+	if(DETECT_BROKEN) {
359
+	    if(virname)
360
+		*virname = "Broken.Executable";
361
+	    return CL_VIRUS;
362
+	}
373 363
 	return CL_CLEAN;
374 364
     }
375 365
 
376 366
     cli_dbgmsg("EntryPoint offset: 0x%x (%d)\n", ep, ep);
377 367
 
378
-
379 368
     /* UPX support */
380 369
 
381 370
     /* try to find the first section with physical size == 0 */
... ...
@@ -559,8 +581,10 @@ int cli_scanpe(int desc, const char **virname, long int *scanned, const struct c
559 559
     found = 2;
560 560
 
561 561
     lseek(desc, ep, SEEK_SET);
562
-    if(read(desc, buff, 200) != 200)
562
+    if(read(desc, buff, 200) != 200) {
563
+	free(section_hdr);
563 564
 	return CL_EIO;
565
+    }
564 566
 
565 567
     if(buff[0] != '\xb8' || cli_readint32(buff + 1) != EC32(section_hdr[nsections - 1].VirtualAddress) + EC32(optional_hdr.ImageBase)) {
566 568
 	if(buff[0] != '\xb8' || cli_readint32(buff + 1) != EC32(section_hdr[nsections - 2].VirtualAddress) + EC32(optional_hdr.ImageBase))
... ...
@@ -63,6 +63,7 @@ struct cfgstruct *parsecfg(const char *cfgfile, int messages)
63 63
 	    {"TemporaryDirectory", OPT_STR},
64 64
 	    {"MaxFileSize", OPT_COMPSIZE},
65 65
 	    {"ScanPE", OPT_NOARG},
66
+	    {"DetectBrokenExecutables", OPT_NOARG},
66 67
 	    {"ScanMail", OPT_NOARG},
67 68
 	    {"ScanHTML", OPT_NOARG},
68 69
 	    {"ScanOLE2", OPT_NOARG},