libclamav: add support for Universal Binaries

(archives with Mach-O files for different architectures, bb#1592)
This commit is contained in:
Tomasz Kojm 2009-07-14 18:19:54 +02:00
parent 548a7c7213
commit 3222a09656
10 changed files with 149 additions and 69 deletions

View file

@ -1,3 +1,8 @@
Tue Jul 14 18:17:59 CEST 2009 (tk)
----------------------------------
* libclamav: add support for Universal Binaries (archives with Mach-O files for
different architectures, bb#1592)
Mon Jul 13 21:40:51 CEST 2009 (tk)
----------------------------------
* docs/signatures.pdf: cover Mach-O files

View file

@ -80,70 +80,7 @@ struct cpio_hdr_newc {
char check[8];
};
#ifndef O_BINARY
#define O_BINARY 0
#endif
static int cpio_scanfile(int fd, uint32_t size, cli_ctx *ctx)
{
int newfd, bread, sum = 0, ret;
char buff[FILEBUFF];
char *name;
if(!(name = cli_gentemp(ctx->engine->tmpdir)))
return CL_EMEM;
if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
cli_errmsg("cpio_scanfile: Can't create file %s\n", name);
free(name);
return CL_ECREAT;
}
while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) {
if((uint32_t) (sum + bread) >= size) {
if(write(newfd, buff, size - sum) == -1) {
cli_errmsg("cpio_scanfile: Can't write to %s\n", name);
cli_unlink(name);
free(name);
close(newfd);
return CL_EWRITE;
}
break;
} else {
if(write(newfd, buff, bread) == -1) {
cli_errmsg("cpio_scanfile: Can't write to %s\n", name);
cli_unlink(name);
free(name);
close(newfd);
return CL_EWRITE;
}
}
sum += bread;
}
cli_dbgmsg("CPIO: Extracted to %s\n", name);
lseek(newfd, 0, SEEK_SET);
if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS)
cli_dbgmsg("cpio_scanfile: Infected with %s\n", *ctx->virname);
close(newfd);
if(!ctx->engine->keeptmp) {
if(cli_unlink(name)) {
free(name);
return CL_EUNLINK;
}
}
free(name);
return ret;
}
static inline uint16_t EC16(uint16_t v, int c)
{
if(!c)
return v;
else
return ((v >> 8) + (v << 8));
}
#define EC16(v, conv) (conv ? cbswap16(v) : v)
static void sanitname(char *name)
{
@ -213,7 +150,7 @@ int cli_scancpio_old(int fd, cli_ctx *ctx)
if(ret == CL_EMAXFILES) {
return ret;
} else if(ret == CL_SUCCESS) {
ret = cpio_scanfile(fd, filesize, ctx);
ret = cli_dumpscan(fd, 0, filesize, ctx);
if(ret == CL_VIRUS)
return ret;
}
@ -287,7 +224,7 @@ int cli_scancpio_odc(int fd, cli_ctx *ctx)
if(ret == CL_EMAXFILES) {
return ret;
} else if(ret == CL_SUCCESS) {
ret = cpio_scanfile(fd, filesize, ctx);
ret = cli_dumpscan(fd, 0, filesize, ctx);
if(ret == CL_VIRUS)
return ret;
}
@ -363,7 +300,7 @@ int cli_scancpio_newc(int fd, cli_ctx *ctx, int crc)
if(ret == CL_EMAXFILES) {
return ret;
} else if(ret == CL_SUCCESS) {
ret = cpio_scanfile(fd, filesize, ctx);
ret = cli_dumpscan(fd, 0, filesize, ctx);
if(ret == CL_VIRUS)
return ret;
}

View file

@ -57,6 +57,7 @@ static const struct ftmap_s {
{ "CL_TYPE_MSEXE", CL_TYPE_MSEXE },
{ "CL_TYPE_ELF", CL_TYPE_ELF },
{ "CL_TYPE_MACHO", CL_TYPE_MACHO },
{ "CL_TYPE_MACHO_UNIBIN", CL_TYPE_MACHO_UNIBIN },
{ "CL_TYPE_POSIX_TAR", CL_TYPE_POSIX_TAR },
{ "CL_TYPE_OLD_TAR", CL_TYPE_OLD_TAR },
{ "CL_TYPE_CPIO_OLD", CL_TYPE_CPIO_OLD },

View file

@ -42,6 +42,7 @@ typedef enum {
CL_TYPE_PE_DISASM,
CL_TYPE_ELF,
CL_TYPE_MACHO,
CL_TYPE_MACHO_UNIBIN,
CL_TYPE_POSIX_TAR,
CL_TYPE_OLD_TAR,
CL_TYPE_CPIO_OLD,

View file

@ -148,6 +148,7 @@ static const char *ftypes_int[] = {
"0:0:cffaedfe:Mach-O LE 64-bit:CL_TYPE_ANY:CL_TYPE_MACHO:45",
"0:0:feedface:Mach-O BE:CL_TYPE_ANY:CL_TYPE_MACHO:45",
"0:0:feedfacf:Mach-O BE 64-bit:CL_TYPE_ANY:CL_TYPE_MACHO:45",
"0:0:cafebabe:Universal Binary:CL_TYPE_ANY:CL_TYPE_MACHO_UNIBIN:46",
NULL
};

View file

@ -153,6 +153,21 @@ struct macho_thread_state_x86
uint32_t gs;
};
struct macho_fat_header
{
uint32_t magic;
uint32_t nfats;
};
struct macho_fat_arch
{
uint32_t cputype;
uint32_t cpusubtype;
uint32_t offset;
uint32_t size;
uint32_t align;
};
#define RETURN_BROKEN \
if(matcher) \
return -1; \
@ -477,3 +492,57 @@ int cli_machoheader(int fd, struct cli_exe_info *fileinfo)
{
return cli_scanmacho(fd, NULL, fileinfo);
}
int cli_scanmacho_unibin(int fd, cli_ctx *ctx)
{
struct macho_fat_header fat_header;
struct macho_fat_arch fat_arch;
unsigned int conv, i, matcher = 0;
int ret = CL_CLEAN;
struct stat sb;
off_t pos;
if(fstat(fd, &sb) == -1) {
cli_dbgmsg("cli_scanmacho_unibin: fstat failed for fd %d\n", fd);
return CL_ESTAT;
}
if(read(fd, &fat_header, sizeof(fat_header)) != sizeof(fat_header)) {
cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_header\n");
return CL_EFORMAT;
}
if(fat_header.magic == 0xcafebabe) {
conv = 0;
} else if(fat_header.magic == 0xbebafeca) {
conv = 1;
} else {
cli_dbgmsg("cli_scanmacho_unibin: Incorrect magic\n");
return CL_EFORMAT;
}
fat_header.nfats = EC32(fat_header.nfats, conv);
if(fat_header.nfats > 32) {
cli_dbgmsg("cli_scanmacho_unibin: Invalid number of architectures\n");
RETURN_BROKEN;
}
cli_dbgmsg("UNIBIN: Number of architectures: %u\n", (unsigned int) fat_header.nfats);
for(i = 0; i < fat_header.nfats; i++) {
if(read(fd, &fat_arch, sizeof(fat_arch)) != sizeof(fat_arch)) {
cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_arch\n");
RETURN_BROKEN;
}
pos = lseek(fd, 0, SEEK_CUR);
fat_arch.offset = EC32(fat_arch.offset, conv);
fat_arch.size = EC32(fat_arch.size, conv);
cli_dbgmsg("UNIBIN: Binary %u of %u\n", i + 1, fat_header.nfats);
cli_dbgmsg("UNIBIN: File offset: %u\n", fat_arch.offset);
cli_dbgmsg("UNIBIN: File size: %u\n", fat_arch.size);
ret = cli_dumpscan(fd, fat_arch.offset, fat_arch.size, ctx);
lseek(fd, pos, SEEK_SET);
if(ret == CL_VIRUS)
break;
}
return ret; /* result from the last binary */
}

View file

@ -26,5 +26,6 @@
int cli_scanmacho(int fd, cli_ctx *ctx, struct cli_exe_info *fileinfo);
int cli_machoheader(int fd, struct cli_exe_info *fileinfo);
int cli_scanmacho_unibin(int fd, cli_ctx *ctx);
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2007-2008 Sourcefire, Inc.
* Copyright (C) 2007-2009 Sourcefire, Inc.
*
* Authors: Tomasz Kojm, Trog
*
@ -837,6 +837,65 @@ int cli_rmdirs(const char *dirname)
}
#endif
int cli_dumpscan(int fd, off_t offset, size_t size, cli_ctx *ctx)
{
int newfd, bread, sum = 0, ret;
char buff[FILEBUFF];
char *name;
if(offset) {
if(lseek(fd, offset, SEEK_SET) == -1) {
cli_dbgmsg("cli_dumpscan: Can't lseek to %u\n", (unsigned int) offset);
return CL_EFORMAT; /* most likely because of corrupted file */
}
}
if(!(name = cli_gentemp(ctx->engine->tmpdir)))
return CL_EMEM;
if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
cli_errmsg("cli_dumpscan: Can't create file %s\n", name);
free(name);
return CL_ECREAT;
}
while((bread = cli_readn(fd, buff, FILEBUFF)) > 0) {
if((uint32_t) (sum + bread) >= size) {
if(write(newfd, buff, size - sum) == -1) {
cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
cli_unlink(name);
free(name);
close(newfd);
return CL_EWRITE;
}
break;
} else {
if(write(newfd, buff, bread) == -1) {
cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
cli_unlink(name);
free(name);
close(newfd);
return CL_EWRITE;
}
}
sum += bread;
}
cli_dbgmsg("DUMP&SCAN: File extracted to %s\n", name);
lseek(newfd, 0, SEEK_SET);
if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS)
cli_dbgmsg("cli_dumpscan: Infected with %s\n", *ctx->virname);
close(newfd);
if(!ctx->engine->keeptmp) {
if(cli_unlink(name)) {
free(name);
return CL_EUNLINK;
}
}
free(name);
return ret;
}
/* Implement a generic bitset, trog@clamav.net */
#define BITS_PER_CHAR (8)

View file

@ -45,7 +45,7 @@
* in re-enabling affected modules.
*/
#define CL_FLEVEL 45
#define CL_FLEVEL 46
#define CL_FLEVEL_DCONF CL_FLEVEL
extern uint8_t cli_debug_flag;
@ -381,6 +381,7 @@ char *cli_gentemp(const char *dir);
int cli_gentempfd(const char *dir, char **name, int *fd);
unsigned int cli_rndnum(unsigned int max);
int cli_filecopy(const char *src, const char *dest);
int cli_dumpscan(int fd, off_t offset, size_t size, cli_ctx *ctx);
bitset_t *cli_bitset_init(void);
void cli_bitset_free(bitset_t *bs);
int cli_bitset_set(bitset_t *bs, unsigned long bit_offset);

View file

@ -2090,6 +2090,11 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
ret = cli_scanmacho(desc, ctx, NULL);
break;
case CL_TYPE_MACHO_UNIBIN:
if(ctx->dconf->macho)
ret = cli_scanmacho_unibin(desc, ctx);
break;
case CL_TYPE_SIS:
if(SCAN_ARCHIVE && (DCONF_ARCH & ARCH_CONF_SIS))
ret = cli_scansis(desc, ctx);