Browse code

libclamav: add support for Universal Binaries (archives with Mach-O files for different architectures, bb#1592)

Tomasz Kojm authored on 2009/07/15 01:19:54
Showing 10 changed files
... ...
@@ -1,3 +1,8 @@
1
+Tue Jul 14 18:17:59 CEST 2009 (tk)
2
+----------------------------------
3
+ * libclamav: add support for Universal Binaries (archives with Mach-O files for
4
+	      different architectures, bb#1592)
5
+
1 6
 Mon Jul 13 21:40:51 CEST 2009 (tk)
2 7
 ----------------------------------
3 8
  * docs/signatures.pdf: cover Mach-O files
... ...
@@ -80,70 +80,7 @@ struct cpio_hdr_newc {
80 80
     char check[8];
81 81
 };
82 82
 
83
-#ifndef O_BINARY
84
-#define O_BINARY    0
85
-#endif
86
-
87
-static int cpio_scanfile(int fd, uint32_t size, cli_ctx *ctx)
88
-{
89
-	int newfd, bread, sum = 0, ret;
90
-	char buff[FILEBUFF];
91
-	char *name;
92
-
93
-
94
-    if(!(name = cli_gentemp(ctx->engine->tmpdir)))
95
-	return CL_EMEM;
96
-
97
-    if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
98
-	cli_errmsg("cpio_scanfile: Can't create file %s\n", name);
99
-	free(name);
100
-	return CL_ECREAT;
101
-    }
102
-
103
-    while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) {
104
-	if((uint32_t) (sum + bread) >= size) {
105
-	    if(write(newfd, buff, size - sum) == -1) {
106
-		cli_errmsg("cpio_scanfile: Can't write to %s\n", name);
107
-		cli_unlink(name);
108
-		free(name);
109
-		close(newfd);
110
-		return CL_EWRITE;
111
-	    }
112
-	    break;
113
-	} else {
114
-	    if(write(newfd, buff, bread) == -1) {
115
-		cli_errmsg("cpio_scanfile: Can't write to %s\n", name);
116
-		cli_unlink(name);
117
-		free(name);
118
-		close(newfd);
119
-		return CL_EWRITE;
120
-	    }
121
-	}
122
-	sum += bread;
123
-    }
124
-    cli_dbgmsg("CPIO: Extracted to %s\n", name);
125
-    lseek(newfd, 0, SEEK_SET);
126
-    if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS)
127
-	cli_dbgmsg("cpio_scanfile: Infected with %s\n", *ctx->virname);
128
-
129
-    close(newfd);
130
-    if(!ctx->engine->keeptmp) {
131
-	if(cli_unlink(name)) {
132
-	    free(name);
133
-	    return CL_EUNLINK;
134
-	}
135
-    }
136
-    free(name);
137
-    return ret;
138
-}
139
-
140
-static inline uint16_t EC16(uint16_t v, int c)
141
-{
142
-    if(!c)
143
-	return v;
144
-    else
145
-	return ((v >> 8) + (v << 8));
146
-}
83
+#define EC16(v, conv)   (conv ? cbswap16(v) : v)
147 84
 
148 85
 static void sanitname(char *name)
149 86
 {
... ...
@@ -213,7 +150,7 @@ int cli_scancpio_old(int fd, cli_ctx *ctx)
213 213
 	    if(ret == CL_EMAXFILES) {
214 214
 		return ret;
215 215
 	    } else if(ret == CL_SUCCESS) {
216
-		ret = cpio_scanfile(fd, filesize, ctx);
216
+		ret = cli_dumpscan(fd, 0, filesize, ctx);
217 217
 		if(ret == CL_VIRUS)
218 218
 		    return ret;
219 219
 	    }
... ...
@@ -287,7 +224,7 @@ int cli_scancpio_odc(int fd, cli_ctx *ctx)
287 287
 	if(ret == CL_EMAXFILES) {
288 288
 	    return ret;
289 289
 	} else if(ret == CL_SUCCESS) {
290
-	    ret = cpio_scanfile(fd, filesize, ctx);
290
+	    ret = cli_dumpscan(fd, 0, filesize, ctx);
291 291
 	    if(ret == CL_VIRUS)
292 292
 		return ret;
293 293
 	}
... ...
@@ -363,7 +300,7 @@ int cli_scancpio_newc(int fd, cli_ctx *ctx, int crc)
363 363
 	if(ret == CL_EMAXFILES) {
364 364
 	    return ret;
365 365
 	} else if(ret == CL_SUCCESS) {
366
-	    ret = cpio_scanfile(fd, filesize, ctx);
366
+	    ret = cli_dumpscan(fd, 0, filesize, ctx);
367 367
 	    if(ret == CL_VIRUS)
368 368
 		return ret;
369 369
 	}
... ...
@@ -57,6 +57,7 @@ static const struct ftmap_s {
57 57
     { "CL_TYPE_MSEXE",		CL_TYPE_MSEXE		},
58 58
     { "CL_TYPE_ELF",		CL_TYPE_ELF		},
59 59
     { "CL_TYPE_MACHO",		CL_TYPE_MACHO		},
60
+    { "CL_TYPE_MACHO_UNIBIN",	CL_TYPE_MACHO_UNIBIN	},
60 61
     { "CL_TYPE_POSIX_TAR",	CL_TYPE_POSIX_TAR	},
61 62
     { "CL_TYPE_OLD_TAR",	CL_TYPE_OLD_TAR		},
62 63
     { "CL_TYPE_CPIO_OLD",	CL_TYPE_CPIO_OLD	},
... ...
@@ -42,6 +42,7 @@ typedef enum {
42 42
     CL_TYPE_PE_DISASM,
43 43
     CL_TYPE_ELF,
44 44
     CL_TYPE_MACHO,
45
+    CL_TYPE_MACHO_UNIBIN,
45 46
     CL_TYPE_POSIX_TAR,
46 47
     CL_TYPE_OLD_TAR,
47 48
     CL_TYPE_CPIO_OLD,
... ...
@@ -148,6 +148,7 @@ static const char *ftypes_int[] = {
148 148
   "0:0:cffaedfe:Mach-O LE 64-bit:CL_TYPE_ANY:CL_TYPE_MACHO:45",
149 149
   "0:0:feedface:Mach-O BE:CL_TYPE_ANY:CL_TYPE_MACHO:45",
150 150
   "0:0:feedfacf:Mach-O BE 64-bit:CL_TYPE_ANY:CL_TYPE_MACHO:45",
151
+  "0:0:cafebabe:Universal Binary:CL_TYPE_ANY:CL_TYPE_MACHO_UNIBIN:46",
151 152
   NULL
152 153
 };
153 154
 
... ...
@@ -153,6 +153,21 @@ struct macho_thread_state_x86
153 153
     uint32_t gs;
154 154
 };
155 155
 
156
+struct macho_fat_header
157
+{
158
+    uint32_t magic;
159
+    uint32_t nfats;
160
+};
161
+
162
+struct macho_fat_arch
163
+{
164
+    uint32_t cputype;
165
+    uint32_t cpusubtype;
166
+    uint32_t offset;
167
+    uint32_t size;
168
+    uint32_t align;
169
+};
170
+
156 171
 #define RETURN_BROKEN					    \
157 172
     if(matcher)						    \
158 173
 	return -1;					    \
... ...
@@ -477,3 +492,57 @@ int cli_machoheader(int fd, struct cli_exe_info *fileinfo)
477 477
 {
478 478
     return cli_scanmacho(fd, NULL, fileinfo);
479 479
 }
480
+
481
+int cli_scanmacho_unibin(int fd, cli_ctx *ctx)
482
+{
483
+	struct macho_fat_header fat_header;
484
+	struct macho_fat_arch fat_arch;
485
+	unsigned int conv, i, matcher = 0;
486
+	int ret = CL_CLEAN;
487
+	struct stat sb;
488
+	off_t pos;
489
+
490
+    if(fstat(fd, &sb) == -1) {
491
+	cli_dbgmsg("cli_scanmacho_unibin: fstat failed for fd %d\n", fd);
492
+	return CL_ESTAT;
493
+    }
494
+
495
+    if(read(fd, &fat_header, sizeof(fat_header)) != sizeof(fat_header)) {
496
+	cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_header\n");
497
+	return CL_EFORMAT;
498
+    }
499
+
500
+    if(fat_header.magic == 0xcafebabe) {
501
+	conv = 0;
502
+    } else if(fat_header.magic == 0xbebafeca) {
503
+	conv = 1;
504
+    } else {
505
+	cli_dbgmsg("cli_scanmacho_unibin: Incorrect magic\n");
506
+	return CL_EFORMAT;
507
+    }
508
+
509
+    fat_header.nfats = EC32(fat_header.nfats, conv);
510
+    if(fat_header.nfats > 32) {
511
+	cli_dbgmsg("cli_scanmacho_unibin: Invalid number of architectures\n");
512
+	RETURN_BROKEN;
513
+    }
514
+    cli_dbgmsg("UNIBIN: Number of architectures: %u\n", (unsigned int) fat_header.nfats);
515
+    for(i = 0; i < fat_header.nfats; i++) {
516
+	if(read(fd, &fat_arch, sizeof(fat_arch)) != sizeof(fat_arch)) {
517
+	    cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_arch\n");
518
+	    RETURN_BROKEN;
519
+	}
520
+	pos = lseek(fd, 0, SEEK_CUR);
521
+	fat_arch.offset = EC32(fat_arch.offset, conv);
522
+	fat_arch.size = EC32(fat_arch.size, conv);
523
+	cli_dbgmsg("UNIBIN: Binary %u of %u\n", i + 1, fat_header.nfats);
524
+	cli_dbgmsg("UNIBIN: File offset: %u\n", fat_arch.offset);
525
+	cli_dbgmsg("UNIBIN: File size: %u\n", fat_arch.size);
526
+	ret = cli_dumpscan(fd, fat_arch.offset, fat_arch.size, ctx);
527
+	lseek(fd, pos, SEEK_SET);
528
+	if(ret == CL_VIRUS)
529
+	    break;
530
+    }
531
+
532
+    return ret; /* result from the last binary */
533
+}
... ...
@@ -26,5 +26,6 @@
26 26
 
27 27
 int cli_scanmacho(int fd, cli_ctx *ctx, struct cli_exe_info *fileinfo);
28 28
 int cli_machoheader(int fd, struct cli_exe_info *fileinfo);
29
+int cli_scanmacho_unibin(int fd, cli_ctx *ctx);
29 30
 
30 31
 #endif
... ...
@@ -1,5 +1,5 @@
1 1
 /*
2
- *  Copyright (C) 2007-2008 Sourcefire, Inc.
2
+ *  Copyright (C) 2007-2009 Sourcefire, Inc.
3 3
  *
4 4
  *  Authors: Tomasz Kojm, Trog
5 5
  *
... ...
@@ -837,6 +837,65 @@ int cli_rmdirs(const char *dirname)
837 837
 }
838 838
 #endif
839 839
 
840
+int cli_dumpscan(int fd, off_t offset, size_t size, cli_ctx *ctx)
841
+{
842
+	int newfd, bread, sum = 0, ret;
843
+	char buff[FILEBUFF];
844
+	char *name;
845
+
846
+    if(offset) {
847
+	if(lseek(fd, offset, SEEK_SET) == -1) {
848
+	    cli_dbgmsg("cli_dumpscan: Can't lseek to %u\n", (unsigned int) offset);
849
+	    return CL_EFORMAT; /* most likely because of corrupted file */
850
+	}
851
+    }
852
+
853
+    if(!(name = cli_gentemp(ctx->engine->tmpdir)))
854
+	return CL_EMEM;
855
+
856
+    if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
857
+	cli_errmsg("cli_dumpscan: Can't create file %s\n", name);
858
+	free(name);
859
+	return CL_ECREAT;
860
+    }
861
+
862
+    while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) {
863
+	if((uint32_t) (sum + bread) >= size) {
864
+	    if(write(newfd, buff, size - sum) == -1) {
865
+		cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
866
+		cli_unlink(name);
867
+		free(name);
868
+		close(newfd);
869
+		return CL_EWRITE;
870
+	    }
871
+	    break;
872
+	} else {
873
+	    if(write(newfd, buff, bread) == -1) {
874
+		cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
875
+		cli_unlink(name);
876
+		free(name);
877
+		close(newfd);
878
+		return CL_EWRITE;
879
+	    }
880
+	}
881
+	sum += bread;
882
+    }
883
+    cli_dbgmsg("DUMP&SCAN: File extracted to %s\n", name);
884
+    lseek(newfd, 0, SEEK_SET);
885
+    if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS)
886
+	cli_dbgmsg("cli_dumpscan: Infected with %s\n", *ctx->virname);
887
+
888
+    close(newfd);
889
+    if(!ctx->engine->keeptmp) {
890
+	if(cli_unlink(name)) {
891
+	    free(name);
892
+	    return CL_EUNLINK;
893
+	}
894
+    }
895
+    free(name);
896
+    return ret;
897
+}
898
+
840 899
 /* Implement a generic bitset, trog@clamav.net */
841 900
 
842 901
 #define BITS_PER_CHAR (8)
... ...
@@ -45,7 +45,7 @@
45 45
  * in re-enabling affected modules.
46 46
  */
47 47
 
48
-#define CL_FLEVEL 45
48
+#define CL_FLEVEL 46
49 49
 #define CL_FLEVEL_DCONF	CL_FLEVEL
50 50
 
51 51
 extern uint8_t cli_debug_flag;
... ...
@@ -381,6 +381,7 @@ char *cli_gentemp(const char *dir);
381 381
 int cli_gentempfd(const char *dir, char **name, int *fd);
382 382
 unsigned int cli_rndnum(unsigned int max);
383 383
 int cli_filecopy(const char *src, const char *dest);
384
+int cli_dumpscan(int fd, off_t offset, size_t size, cli_ctx *ctx);
384 385
 bitset_t *cli_bitset_init(void);
385 386
 void cli_bitset_free(bitset_t *bs);
386 387
 int cli_bitset_set(bitset_t *bs, unsigned long bit_offset);
... ...
@@ -2090,6 +2090,11 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
2090 2090
 		ret = cli_scanmacho(desc, ctx, NULL);
2091 2091
 	    break;
2092 2092
 
2093
+	case CL_TYPE_MACHO_UNIBIN:
2094
+	    if(ctx->dconf->macho)
2095
+		ret = cli_scanmacho_unibin(desc, ctx);
2096
+	    break;
2097
+
2093 2098
 	case CL_TYPE_SIS:
2094 2099
 	    if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_SIS))
2095 2100
 		ret = cli_scansis(desc, ctx);