| ... | ... |
@@ -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 },
|
| ... | ... |
@@ -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 |
+} |
| ... | ... |
@@ -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); |