libclamav: handle Mach-O files with type-9 signatures;

all special offsets are supported for PPC32/64 and x86 executables; for ARM and other archs only section based extensions (Sx[+-]n, SL[+-]n) are supported atm
0.96
Tomasz Kojm 16 years ago
parent 001b408e25
commit 8af7ccd0af
  1. 6
      ChangeLog
  2. 110
      libclamav/macho.c
  3. 4
      libclamav/macho.h
  4. 4
      libclamav/matcher.c
  5. 5
      libclamav/matcher.h
  6. 2
      libclamav/readdb.c
  7. 2
      libclamav/scanners.c

@ -1,3 +1,9 @@
Mon Jul 13 21:24:05 CEST 2009 (tk)
----------------------------------
* libclamav: handle Mach-O files with type-9 signatures; all special offsets are
supported for PPC32/64 and x86 executables; for ARM and other archs
only section based extensions (Sx[+-]n, SL[+-]n) are supported atm
Mon Jul 13 19:34:36 EEST 2009 (edwin) Mon Jul 13 19:34:36 EEST 2009 (edwin)
------------------------------------- -------------------------------------
* clambc/, libclamav/, unit_tests/: Initial draft of bytecode interpreter (bb #1243). * clambc/, libclamav/, unit_tests/: Initial draft of bytecode interpreter (bb #1243).

@ -18,10 +18,6 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
/* TODO:
* - integrate with the matcher
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -158,6 +154,8 @@ struct macho_thread_state_x86
}; };
#define RETURN_BROKEN \ #define RETURN_BROKEN \
if(matcher) \
return -1; \
if(DETECT_BROKEN) { \ if(DETECT_BROKEN) { \
if(ctx->virname) \ if(ctx->virname) \
*ctx->virname = "Broken.Executable"; \ *ctx->virname = "Broken.Executable"; \
@ -186,7 +184,7 @@ static uint32_t cli_rawaddr(uint32_t vaddr, struct cli_exe_section *sects, uint1
return vaddr - sects[i].rva + sects[i].raw; return vaddr - sects[i].rva + sects[i].raw;
} }
int cli_scanmacho(int fd, cli_ctx *ctx) int cli_scanmacho(int fd, cli_ctx *ctx, struct cli_exe_info *fileinfo)
{ {
struct macho_hdr hdr; struct macho_hdr hdr;
struct macho_load_cmd load_cmd; struct macho_load_cmd load_cmd;
@ -194,14 +192,17 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
struct macho_segment_cmd64 segment_cmd64; struct macho_segment_cmd64 segment_cmd64;
struct macho_section section; struct macho_section section;
struct macho_section64 section64; struct macho_section64 section64;
unsigned int i, j, sect = 0, conv, m64, nsects; unsigned int i, j, sect = 0, conv, m64, nsects, matcher = 0;
unsigned int arch = 0, ep = 0, err; unsigned int arch = 0, ep = 0, err;
struct cli_exe_section *sections = NULL; struct cli_exe_section *sections = NULL;
char name[16]; char name[16];
if(fileinfo)
matcher = 1;
if(read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { if(read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
cli_dbgmsg("cli_scanmacho: Can't read header\n"); cli_dbgmsg("cli_scanmacho: Can't read header\n");
return CL_EFORMAT; return matcher ? -1 : CL_EFORMAT;
} }
if(hdr.magic == 0xfeedface) { if(hdr.magic == 0xfeedface) {
@ -218,37 +219,44 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
m64 = 1; m64 = 1;
} else { } else {
cli_dbgmsg("cli_scanmacho: Incorrect magic\n"); cli_dbgmsg("cli_scanmacho: Incorrect magic\n");
return CL_EFORMAT; return matcher ? -1 : CL_EFORMAT;
} }
switch(EC32(hdr.cpu_type, conv)) { switch(EC32(hdr.cpu_type, conv)) {
case 7: case 7:
cli_dbgmsg("MACHO: CPU Type: Intel 32-bit\n"); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: Intel 32-bit\n");
arch = 1; arch = 1;
break; break;
case 7 | 0x1000000: case 7 | 0x1000000:
cli_dbgmsg("MACHO: CPU Type: Intel 64-bit\n"); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: Intel 64-bit\n");
break; break;
case 12: case 12:
cli_dbgmsg("MACHO: CPU Type: ARM\n"); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: ARM\n");
break; break;
case 14: case 14:
cli_dbgmsg("MACHO: CPU Type: SPARC\n"); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: SPARC\n");
break; break;
case 18: case 18:
cli_dbgmsg("MACHO: CPU Type: POWERPC 32-bit\n"); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: POWERPC 32-bit\n");
arch = 2; arch = 2;
break; break;
case 18 | 0x1000000: case 18 | 0x1000000:
cli_dbgmsg("MACHO: CPU Type: POWERPC 64-bit\n"); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: POWERPC 64-bit\n");
arch = 3; arch = 3;
break; break;
default: default:
cli_dbgmsg("MACHO: CPU Type: ** UNKNOWN ** (%u)\n", EC32(hdr.cpu_type, conv)); if(!matcher)
cli_dbgmsg("MACHO: CPU Type: ** UNKNOWN ** (%u)\n", EC32(hdr.cpu_type, conv));
break; break;
} }
switch(EC32(hdr.filetype, conv)) { if(!matcher) switch(EC32(hdr.filetype, conv)) {
case 0x1: /* MH_OBJECT */ case 0x1: /* MH_OBJECT */
cli_dbgmsg("MACHO: Filetype: Relocatable object file\n"); cli_dbgmsg("MACHO: Filetype: Relocatable object file\n");
break; break;
@ -280,8 +288,10 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
cli_dbgmsg("MACHO: Filetype: ** UNKNOWN ** (0x%x)\n", EC32(hdr.filetype, conv)); cli_dbgmsg("MACHO: Filetype: ** UNKNOWN ** (0x%x)\n", EC32(hdr.filetype, conv));
} }
cli_dbgmsg("MACHO: Number of load commands: %u\n", EC32(hdr.ncmds, conv)); if(!matcher) {
cli_dbgmsg("MACHO: Size of load commands: %u\n", EC32(hdr.sizeofcmds, conv)); cli_dbgmsg("MACHO: Number of load commands: %u\n", EC32(hdr.ncmds, conv));
cli_dbgmsg("MACHO: Size of load commands: %u\n", EC32(hdr.sizeofcmds, conv));
}
if(m64) if(m64)
lseek(fd, 4, SEEK_CUR); lseek(fd, 4, SEEK_CUR);
@ -324,22 +334,25 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
nsects = EC32(segment_cmd.nsects, conv); nsects = EC32(segment_cmd.nsects, conv);
strncpy(name, segment_cmd.segname, 16); strncpy(name, segment_cmd.segname, 16);
} }
name[15] = 0; if(!matcher) {
cli_dbgmsg("MACHO: Segment name: %s\n", name); name[15] = 0;
cli_dbgmsg("MACHO: Number of sections: %u\n", nsects); cli_dbgmsg("MACHO: Segment name: %s\n", name);
cli_dbgmsg("MACHO: Number of sections: %u\n", nsects);
}
if(nsects > 255) { if(nsects > 255) {
cli_dbgmsg("cli_scanmacho: Invalid number of sections\n"); cli_dbgmsg("cli_scanmacho: Invalid number of sections\n");
free(sections); free(sections);
RETURN_BROKEN; RETURN_BROKEN;
} }
if(!nsects) { if(!nsects) {
cli_dbgmsg("MACHO: ------------------\n"); if(!matcher)
cli_dbgmsg("MACHO: ------------------\n");
continue; continue;
} }
sections = (struct cli_exe_section *) cli_realloc2(sections, (sect + nsects) * sizeof(struct cli_exe_section)); sections = (struct cli_exe_section *) cli_realloc2(sections, (sect + nsects) * sizeof(struct cli_exe_section));
if(!sections) { if(!sections) {
cli_errmsg("cli_scanmacho: Can't allocate memory for 'sections'\n"); cli_errmsg("cli_scanmacho: Can't allocate memory for 'sections'\n");
return CL_EMEM; return matcher ? -1 : CL_EMEM;
} }
for(j = 0; j < nsects; j++) { for(j = 0; j < nsects; j++) {
@ -364,21 +377,24 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
sections[sect].rva = EC32(section.addr, conv); sections[sect].rva = EC32(section.addr, conv);
sections[sect].vsz = EC32(section.size, conv); sections[sect].vsz = EC32(section.size, conv);
sections[sect].raw = EC32(section.offset, conv); sections[sect].raw = EC32(section.offset, conv);
section64.align = EC32(section64.align, conv); section.align = EC32(section.align, conv);
sections[sect].rsz = sections[sect].vsz + (section64.align - (sections[sect].vsz % section64.align)) % section64.align; sections[sect].rsz = sections[sect].vsz + (section.align - (sections[sect].vsz % section.align)) % section.align;
strncpy(name, section.sectname, 16); strncpy(name, section.sectname, 16);
} }
name[15] = 0; if(!matcher) {
cli_dbgmsg("MACHO: --- Section %u ---\n", sect); name[15] = 0;
cli_dbgmsg("MACHO: Name: %s\n", name); cli_dbgmsg("MACHO: --- Section %u ---\n", sect);
cli_dbgmsg("MACHO: Virtual address: 0x%x\n", (unsigned int) sections[sect].rva); cli_dbgmsg("MACHO: Name: %s\n", name);
cli_dbgmsg("MACHO: Virtual size: %u\n", (unsigned int) sections[sect].vsz); cli_dbgmsg("MACHO: Virtual address: 0x%x\n", (unsigned int) sections[sect].rva);
cli_dbgmsg("MACHO: Raw size: %u\n", (unsigned int) sections[sect].rsz); cli_dbgmsg("MACHO: Virtual size: %u\n", (unsigned int) sections[sect].vsz);
if(sections[sect].raw) cli_dbgmsg("MACHO: Raw size: %u\n", (unsigned int) sections[sect].rsz);
cli_dbgmsg("MACHO: File offset: %u\n", (unsigned int) sections[sect].raw); if(sections[sect].raw)
cli_dbgmsg("MACHO: File offset: %u\n", (unsigned int) sections[sect].raw);
}
sect++; sect++;
} }
cli_dbgmsg("MACHO: ------------------\n"); if(!matcher)
cli_dbgmsg("MACHO: ------------------\n");
} else if(arch && (load_cmd.cmd == 0x4 || load_cmd.cmd == 0x5)) { /* LC_(UNIX)THREAD */ } else if(arch && (load_cmd.cmd == 0x4 || load_cmd.cmd == 0x5)) { /* LC_(UNIX)THREAD */
lseek(fd, 8, SEEK_CUR); lseek(fd, 8, SEEK_CUR);
@ -423,7 +439,7 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
default: default:
cli_errmsg("cli_scanmacho: Invalid arch setting!\n"); cli_errmsg("cli_scanmacho: Invalid arch setting!\n");
free(sections); free(sections);
return CL_EARG; return matcher ? -1 : CL_EARG;
} }
} else { } else {
if(EC32(load_cmd.cmdsize, conv) > sizeof(load_cmd)) if(EC32(load_cmd.cmdsize, conv) > sizeof(load_cmd))
@ -432,18 +448,32 @@ int cli_scanmacho(int fd, cli_ctx *ctx)
} }
if(ep) { if(ep) {
cli_dbgmsg("Entry Point: 0x%x (%u)\n", ep, ep); if(!matcher)
cli_dbgmsg("Entry Point: 0x%x\n", ep);
if(sections) { if(sections) {
ep = cli_rawaddr(ep, sections, sect, &err); ep = cli_rawaddr(ep, sections, sect, &err);
if(err) { if(err) {
cli_dbgmsg("cli_scanmacho: Can't calculate EP offset\n"); cli_dbgmsg("cli_scanmacho: Can't calculate EP offset\n");
free(sections); free(sections);
return CL_EFORMAT; return matcher ? -1 : CL_EFORMAT;
} }
cli_dbgmsg("Entry Point file offset: %u\n", ep); if(!matcher)
cli_dbgmsg("Entry Point file offset: %u\n", ep);
} }
} }
free(sections); if(matcher) {
return CL_SUCCESS; fileinfo->ep = ep;
fileinfo->nsections = sect;
fileinfo->section = sections;
return 0;
} else {
free(sections);
return CL_SUCCESS;
}
}
int cli_machoheader(int fd, struct cli_exe_info *fileinfo)
{
return cli_scanmacho(fd, NULL, fileinfo);
} }

@ -24,7 +24,7 @@
#include "others.h" #include "others.h"
#include "execs.h" #include "execs.h"
int cli_scanmacho(int fd, cli_ctx *ctx); int cli_scanmacho(int fd, cli_ctx *ctx, struct cli_exe_info *fileinfo);
int cli_machoheader(int fd, struct cli_exe_info *elfinfo); int cli_machoheader(int fd, struct cli_exe_info *fileinfo);
#endif #endif

@ -44,7 +44,7 @@
#include "str.h" #include "str.h"
#include "cltypes.h" #include "cltypes.h"
#include "default.h" #include "default.h"
#include "macho.h"
int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata) int cli_scanbuff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata)
{ {
@ -131,6 +131,8 @@ off_t cli_caloff(const char *offstr, struct cli_target_info *info, int fd, cli_f
einfo = cli_peheader; einfo = cli_peheader;
else if(ftype == CL_TYPE_ELF) else if(ftype == CL_TYPE_ELF)
einfo = cli_elfheader; einfo = cli_elfheader;
else if(ftype == CL_TYPE_MACHO)
einfo = cli_machoheader;
if(einfo) { if(einfo) {
if((pos = lseek(fd, 0, SEEK_CUR)) == -1) { if((pos = lseek(fd, 0, SEEK_CUR)) == -1) {

@ -104,7 +104,7 @@ struct cli_mtarget {
uint8_t ac_only; uint8_t ac_only;
}; };
#define CLI_MTARGETS 9 #define CLI_MTARGETS 10
static const struct cli_mtarget cli_mtargets[CLI_MTARGETS] = { static const struct cli_mtarget cli_mtargets[CLI_MTARGETS] = {
{ 0, "GENERIC", 0, 0 }, { 0, "GENERIC", 0, 0 },
{ CL_TYPE_MSEXE, "PE", 1, 0 }, { CL_TYPE_MSEXE, "PE", 1, 0 },
@ -114,7 +114,8 @@ static const struct cli_mtarget cli_mtargets[CLI_MTARGETS] = {
{ CL_TYPE_GRAPHICS, "GRAPHICS", 5, 1 }, { CL_TYPE_GRAPHICS, "GRAPHICS", 5, 1 },
{ CL_TYPE_ELF, "ELF", 6, 1 }, { CL_TYPE_ELF, "ELF", 6, 1 },
{ CL_TYPE_TEXT_ASCII, "ASCII", 7, 1 }, { CL_TYPE_TEXT_ASCII, "ASCII", 7, 1 },
{ CL_TYPE_PE_DISASM, "DISASM", 8, 1 } { CL_TYPE_PE_DISASM, "DISASM", 8, 1 },
{ CL_TYPE_MACHO, "MACH-O", 9, 1 }
}; };
struct cli_target_info { struct cli_target_info {

@ -532,7 +532,7 @@ static int cli_checkoffset(const char *offset, unsigned int type)
if(!strncmp(offset, "EOF-", 4)) if(!strncmp(offset, "EOF-", 4))
return 0; return 0;
if((type == 1 || type == 6) && (!strncmp(offset, "EP+", 3) || !strncmp(offset, "EP-", 3) || (sscanf(offset, "SL+%u", &foo) == 1) || (sscanf(offset, "S%u+%u", &foo, &foo) == 2))) if((type == 1 || type == 6 || type == 9) && (!strncmp(offset, "EP+", 3) || !strncmp(offset, "EP-", 3) || (sscanf(offset, "SL+%u", &foo) == 1) || (sscanf(offset, "S%u+%u", &foo, &foo) == 2)))
return 0; return 0;
return 1; return 1;

@ -2087,7 +2087,7 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
case CL_TYPE_MACHO: case CL_TYPE_MACHO:
if(ctx->dconf->macho) if(ctx->dconf->macho)
ret = cli_scanmacho(desc, ctx); ret = cli_scanmacho(desc, ctx, NULL);
break; break;
case CL_TYPE_SIS: case CL_TYPE_SIS:

Loading…
Cancel
Save