mirror of
https://github.com/Cisco-Talos/clamav.git
synced 2025-10-19 10:23:17 +00:00
libclamav: add support for Universal Binaries
(archives with Mach-O files for different architectures, bb#1592)
This commit is contained in:
parent
548a7c7213
commit
3222a09656
10 changed files with 149 additions and 69 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue