int cli_scanmacho_unibin(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; fmap_t *map = *ctx->fmap; ssize_t at; if(fmap_readn(map, &fat_header, 0, sizeof(fat_header)) != sizeof(fat_header)) { cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_header\n"); return CL_EFORMAT; } at = sizeof(fat_header); 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 & 0xffff) >= 39) /* Java Bytecode */ return CL_CLEAN; if(fat_header.nfats > 32) { cli_dbgmsg("cli_scanmacho_unibin: Invalid number of architectures\n"); return CL_EFORMAT; } cli_dbgmsg("UNIBIN: Number of architectures: %u\n", (unsigned int) fat_header.nfats); for(i = 0; i < fat_header.nfats; i++) { if(fmap_readn(map, &fat_arch, at, sizeof(fat_arch)) != sizeof(fat_arch)) { cli_dbgmsg("cli_scanmacho_unibin: Can't read fat_arch\n"); RETURN_BROKEN; } at += sizeof(fat_arch); 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_map_scan(map, fat_arch.offset, fat_arch.size, ctx); if(ret == CL_VIRUS) break; } return ret; /* result from the last binary */ }
int32_t cli_bcapi_file_find_limit(struct cli_bc_ctx *ctx , const uint8_t* data, uint32_t len, int32_t limit) { char buf[4096]; fmap_t *map = ctx->fmap; uint32_t off = ctx->off; int n; if (!map || len > sizeof(buf)/4 || len <= 0 || limit <= 0) { cli_dbgmsg("bcapi_file_find_limit preconditions not met\n"); API_MISUSE(); return -1; } cli_event_int(EV, BCEV_OFFSET, off); cli_event_fastdata(EV, BCEV_FIND, data, len); for (;;) { const char *p; int32_t readlen = sizeof(buf); if (off + readlen > limit) { readlen = limit - off; if (readlen < 0) return -1; } n = fmap_readn(map, buf, off, readlen); if ((unsigned)n < len || n < 0) return -1; p = cli_memmem(buf, n, data, len); if (p) return off + p - buf; off += n; } return -1; }
static int jpeg_check_photoshop_8bim(cli_ctx *ctx, off_t *off) { const unsigned char *buf; uint16_t ntmp; uint8_t nlength, id[2]; uint32_t size; off_t offset = *off; int retval; fmap_t *map = *ctx->fmap; if(!(buf = fmap_need_off_once(map, offset, 4 + 2 + 1))) { cli_dbgmsg("read bim failed\n"); return -1; } if (memcmp(buf, "8BIM", 4) != 0) { cli_dbgmsg("missed 8bim\n"); return -1; } id[0] = (uint8_t)buf[4]; id[1] = (uint8_t)buf[5]; cli_dbgmsg("ID: 0x%.2x%.2x\n", id[0], id[1]); nlength = buf[6]; ntmp = nlength + ((((uint16_t)nlength)+1) & 0x01); offset += 4 + 2 + 1 + ntmp; if (fmap_readn(map, &size, offset, 4) != 4) { return -1; } size = special_endian_convert_32(size); if (size == 0) { return -1; } if ((size & 0x01) == 1) { size++; } *off = offset + 4 + size; /* Is it a thumbnail image: 0x0409 or 0x040c */ if ((id[0] == 0x04) && ((id[1] == 0x09) || (id[1] == 0x0c))) { /* Yes */ cli_dbgmsg("found thumbnail\n"); } else { /* No - Seek past record */ return 0; } /* Jump past header */ offset += 4 + 28; retval = cli_check_jpeg_exploit(ctx, offset); if (retval == 1) { cli_dbgmsg("Exploit found in thumbnail\n"); } return retval; }
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize) { #ifdef DEBUG_GPT_PARSE struct gpt_header phdr, shdr; off_t ppos = 0, spos = 0; size_t pptable_len, sptable_len, maplen; uint64_t ptableLastLBA, stableLastLBA; /* sector size calculation */ sectorsize = GPT_DEFAULT_SECTOR_SIZE; maplen = (*ctx->fmap)->real_len; ppos = 1 * sectorsize; /* sector 1 (second sector) is the primary gpt header */ spos = maplen - sectorsize; /* last sector is the secondary gpt header */ /* read in the primary and secondary gpt headers */ if (fmap_readn(*ctx->fmap, &phdr, ppos, sizeof(phdr)) != sizeof(phdr)) { cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n"); return; } if (fmap_readn(*ctx->fmap, &shdr, spos, sizeof(shdr)) != sizeof(shdr)) { cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n"); return; } pptable_len = phdr.tableNumEntries * phdr.tableEntrySize; sptable_len = shdr.tableNumEntries * shdr.tableEntrySize; ptableLastLBA = (phdr.tableStartLBA + (pptable_len / sectorsize)) - 1; stableLastLBA = (shdr.tableStartLBA + (sptable_len / sectorsize)) - 1; gpt_parsemsg("0: MBR\n"); gpt_parsemsg("%llu: Primary GPT Header\n", phdr.currentLBA); gpt_parsemsg("%llu-%llu: Primary GPT Partition Table\n", phdr.tableStartLBA, ptableLastLBA); gpt_parsemsg("%llu-%llu: Usable LBAs\n", phdr.firstUsableLBA, phdr.lastUsableLBA); gpt_parsemsg("%llu-%llu: Secondary GPT Partition Table\n", shdr.tableStartLBA, stableLastLBA); gpt_parsemsg("%llu: Secondary GPT Header\n", phdr.backupLBA); #else UNUSEDPARAM(ctx); UNUSEDPARAM(sectorsize); return; #endif }
static int tnef_header(fmap_t *map, off_t *pos, uint8_t *part, uint16_t *type, uint16_t *tag, int32_t *length) { uint32_t i32; int rc; if (fmap_readn(map, part, *pos, 1) != 1) return 0; (*pos)++; if(*part == (uint8_t)0) return 0; rc = fmap_readn(map, &i32, *pos, sizeof(uint32_t)); if (rc != sizeof(uint32_t)) { if(((*part == '\n') || (*part == '\r')) && (rc == 0)) { /* * trailing newline in the file, could be caused by * broken quoted-printable encoding in the source * message missing a final '=' */ cli_dbgmsg("tnef_header: ignoring trailing newline\n"); return 0; } return -1; } (*pos) += sizeof(uint32_t); i32 = host32(i32); *tag = (uint16_t)(i32 & 0xFFFF); *type = (uint16_t)((i32 & 0xFFFF0000) >> 16); if(fmap_readn(map, &i32, *pos, sizeof(uint32_t)) != sizeof(uint32_t)) return -1; (*pos) += sizeof(uint32_t); *length = (int32_t)host32(i32); cli_dbgmsg("message tag 0x%x, type 0x%x, length %d\n", *tag, *type, (int)*length); return 1; }
static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize) { struct mbr_boot_record pmbr; off_t pos = 0, mbr_base = 0; int ret = CL_CLEAN; unsigned i = 0; /* read the mbr */ mbr_base = sectorsize - sizeof(struct mbr_boot_record); pos = (MBR_SECTOR * sectorsize) + mbr_base; if (fmap_readn(*ctx->fmap, &pmbr, pos, sizeof(pmbr)) != sizeof(pmbr)) { cli_dbgmsg("cli_scangpt: Invalid primary MBR header\n"); return CL_EFORMAT; } /* convert mbr */ mbr_convert_to_host(&pmbr); /* check the protective mbr - warning */ if (pmbr.entries[0].type == MBR_PROTECTIVE) { /* check the efi partition matches the gpt spec */ if (pmbr.entries[0].firstLBA != GPT_PRIMARY_HDR_LBA) { cli_warnmsg("cli_scangpt: protective MBR first LBA is incorrect %u\n", pmbr.entries[0].firstLBA); } /* other entries are empty */ for (i = 1; i < MBR_MAX_PARTITION_ENTRIES; ++i) { if (pmbr.entries[i].type != MBR_EMPTY) { cli_warnmsg("cli_scangpt: protective MBR has non-empty partition\n"); break; } } } else if (pmbr.entries[0].type == MBR_HYBRID) { /* hybrid mbr detected */ cli_warnmsg("cli_scangpt: detected a hybrid MBR\n"); } else { /* non-protective mbr detected */ cli_warnmsg("cli_scangpt: detected a non-protective MBR\n"); } /* scan the bootloader segment - pushed to scanning mbr */ /* check if MBR size matches GPT size */ /* check if the MBR and GPT partitions align - heuristic */ /* scan the MBR partitions - additional scans */ return ret; }
int32_t cli_bcapi_file_byteat(struct cli_bc_ctx *ctx, uint32_t off) { unsigned char c; if (!ctx->fmap) { cli_dbgmsg("bcapi_file_byteat: no fmap\n"); return -1; } cli_event_int(EV, BCEV_OFFSET, off); if (fmap_readn(ctx->fmap, &c, off, 1) != 1) { cli_dbgmsg("bcapi_file_byteat: fmap_readn failed at %u\n", off); return -1; } return c; }
static SRes FileInStream_fmap_Read(void *pp, void *buf, size_t *size) { CFileInStream *p = (CFileInStream *)pp; int read_sz; if (*size == 0) return 0; read_sz = fmap_readn(p->file.fmap, buf, p->s.curpos, *size); if(read_sz < 0) { *size = 0; return SZ_ERROR_READ; } p->s.curpos += read_sz; *size = read_sz; return SZ_OK; }
int cli_mbr_check2(cli_ctx *ctx, size_t sectorsize) { struct mbr_boot_record mbr; off_t pos = 0, mbr_base = 0; size_t maplen; if (!ctx || !ctx->fmap) { cli_errmsg("cli_scanmbr: Invalid context\n"); return CL_ENULLARG; } /* sector size calculation, actual value is OS dependent */ if (sectorsize == 0) sectorsize = MBR_SECTOR_SIZE; mbr_base = sectorsize - sizeof(struct mbr_boot_record); /* size of total file must be a multiple of the sector size */ maplen = (*ctx->fmap)->real_len; if ((maplen % sectorsize) != 0) { cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n", (unsigned long)maplen, (unsigned long)sectorsize); return CL_EFORMAT; } /* sector 0 (first sector) is the master boot record */ pos = (MBR_SECTOR * sectorsize) + mbr_base; /* read the master boot record */ if (fmap_readn(*ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) { cli_dbgmsg("cli_scanmbr: Invalid master boot record\n"); return CL_EFORMAT; } /* convert the little endian to host, include the internal */ mbr_convert_to_host(&mbr); if ((mbr.entries[0].type == MBR_PROTECTIVE) || (mbr.entries[0].type == MBR_HYBRID)) return CL_TYPE_GPT; return mbr_check_mbr(&mbr, maplen, sectorsize); }
int32_t cli_bcapi_read(struct cli_bc_ctx* ctx, uint8_t *data, int32_t size) { int n; if (!ctx->fmap) { API_MISUSE(); return -1; } if (size < 0 || size > CLI_MAX_ALLOCATION) { cli_warnmsg("bytecode: negative read size: %d\n", size); API_MISUSE(); return -1; } n = fmap_readn(ctx->fmap, data, ctx->off, size); if (n <= 0) { cli_dbgmsg("bcapi_read: fmap_readn failed (requested %d)\n", size); cli_event_count(EV, BCEV_READ_ERR); return n; } cli_event_int(EV, BCEV_OFFSET, ctx->off); cli_event_fastdata(EV, BCEV_READ, data, size); //cli_event_data(EV, BCEV_READ, data, n); ctx->off += n; return n; }
/* Fetch a node's contents into the buffer */ static int hfsplus_fetch_node (cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader, hfsHeaderRecord *extHeader, uint32_t node, uint8_t *buff) { int foundBlock = 0; uint64_t catalogOffset; uint32_t fetchBlock, fetchStart; uint32_t extentNum = 0, realFileBlock; size_t fileOffset = 0; hfsPlusForkData *catFork; UNUSEDPARAM(extHeader); /* Make sure node is in range */ if (node >= catHeader->totalNodes) { cli_dbgmsg("hfsplus_fetch_node: invalid node number " STDu32 "\n", node); return CL_EFORMAT; } catFork = &(volHeader->catalogFile); /* Do we need one block or more? */ if (catHeader->nodeSize <= volHeader->blockSize) { /* Need one block */ /* First, calculate the node's offset within the catalog */ catalogOffset = (uint64_t)node * catHeader->nodeSize; /* Determine which block of the catalog we need */ fetchBlock = (uint32_t) (catalogOffset / volHeader->blockSize); fetchStart = (uint32_t) (catalogOffset % volHeader->blockSize); cli_dbgmsg("hfsplus_fetch_node: need catalog block " STDu32 "\n", fetchBlock); if (fetchBlock >= catFork->totalBlocks) { cli_dbgmsg("hfsplus_fetch_node: block number invalid!\n"); return CL_EFORMAT; } /* Find which extent has that block */ for (extentNum = 0; extentNum < 8; extentNum++) { hfsPlusExtentDescriptor *currExt = &(catFork->extents[extentNum]); /* Beware empty extent */ if ((currExt->startBlock == 0) || (currExt->blockCount == 0)) { cli_dbgmsg("hfsplus_fetch_node: extent " STDu32 " empty!\n", extentNum); return CL_EFORMAT; } /* Beware too long extent */ if ((currExt->startBlock & 0x10000000) && (currExt->blockCount & 0x10000000)) { cli_dbgmsg("hfsplus_fetch_node: extent " STDu32 " illegal!\n", extentNum); return CL_EFORMAT; } /* Check if block found in current extent */ if (fetchBlock < currExt->blockCount) { cli_dbgmsg("hfsplus_fetch_node: found block in extent " STDu32 "\n", extentNum); realFileBlock = currExt->startBlock + fetchBlock; foundBlock = 1; break; } else { cli_dbgmsg("hfsplus_fetch_node: not in extent " STDu32 "\n", extentNum); fetchBlock -= currExt->blockCount; } } if (foundBlock == 0) { cli_dbgmsg("hfsplus_fetch_node: not in first 8 extents\n"); cli_dbgmsg("hfsplus_fetch_node: finding this node requires extent overflow support\n"); return CL_EFORMAT; } /* Block found */ if (realFileBlock >= volHeader->totalBlocks) { cli_dbgmsg("hfsplus_fetch_node: block past end of volume\n"); return CL_EFORMAT; } fileOffset = realFileBlock * volHeader->blockSize; } else { /* Need more than one block for this node */ cli_dbgmsg("hfsplus_fetch_node: nodesize bigger than blocksize, is this allowed?\n"); return CL_EFORMAT; } if (fileOffset) { if (fmap_readn(*ctx->fmap, buff, fileOffset, catHeader->nodeSize) != catHeader->nodeSize) { cli_dbgmsg("hfsplus_fetch_node: not all bytes read\n"); return CL_EFORMAT; } } else { cli_dbgmsg("hfsplus_fetch_node: nodesize bigger than blocksize, is this allowed?\n"); return CL_EFORMAT; } return CL_CLEAN; }
int cli_scanapm(cli_ctx *ctx) { struct apm_driver_desc_map ddm; struct apm_partition_info aptable, apentry; int ret = CL_CLEAN, detection = CL_CLEAN, old_school = 0; size_t sectorsize, maplen, partsize, sectorcheck; off_t pos = 0, partoff = 0; unsigned i; uint32_t max_prtns = 0; if (!ctx || !ctx->fmap) { cli_errmsg("cli_scanapm: Invalid context\n"); return CL_ENULLARG; } /* read driver description map at sector 0 */ if (fmap_readn(*ctx->fmap, &ddm, pos, sizeof(ddm)) != sizeof(ddm)) { cli_dbgmsg("cli_scanapm: Invalid Apple driver description map\n"); return CL_EFORMAT; } /* convert driver description map big-endian to host */ ddm.signature = be16_to_host(ddm.signature); ddm.blockSize = be16_to_host(ddm.blockSize); ddm.blockCount = be32_to_host(ddm.blockCount); /* check DDM signature */ if (ddm.signature != DDM_SIGNATURE) { cli_dbgmsg("cli_scanapm: Apple driver description map signature mismatch\n"); return CL_EFORMAT; } /* sector size is determined by the ddm */ sectorsize = ddm.blockSize; /* size of total file must be described by the ddm */ maplen = (*ctx->fmap)->real_len; if ((ddm.blockSize * ddm.blockCount) != maplen) { cli_dbgmsg("cli_scanapm: File described %u size does not match %lu actual size\n", (ddm.blockSize * ddm.blockCount), (unsigned long)maplen); return CL_EFORMAT; } /* check for old-school partition map */ if (sectorsize == 2048) { if (fmap_readn(*ctx->fmap, &aptable, APM_FALLBACK_SECTOR_SIZE, sizeof(aptable)) != sizeof(aptable)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n"); return CL_EFORMAT; } aptable.signature = be16_to_host(aptable.signature); if (aptable.signature == APM_SIGNATURE) { sectorsize = APM_FALLBACK_SECTOR_SIZE; old_school = 1; } } /* read partition table at sector 1 (or after the ddm if old-school) */ pos = APM_PTABLE_BLOCK * sectorsize; if (fmap_readn(*ctx->fmap, &aptable, pos, sizeof(aptable)) != sizeof(aptable)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition table\n"); return CL_EFORMAT; } /* convert partition table big endian to host */ aptable.signature = be16_to_host(aptable.signature); aptable.numPartitions = be32_to_host(aptable.numPartitions); aptable.pBlockStart = be32_to_host(aptable.pBlockStart); aptable.pBlockCount = be32_to_host(aptable.pBlockCount); /* check the partition entry signature */ if (aptable.signature != APM_SIGNATURE) { cli_dbgmsg("cli_scanapm: Apple partition table signature mismatch\n"); return CL_EFORMAT; } /* check if partition table partition */ if (strncmp((char*)aptable.type, "Apple_Partition_Map", 32) && strncmp((char*)aptable.type, "Apple_partition_map", 32) && strncmp((char*)aptable.type, "Apple_patition_map", 32)) { cli_dbgmsg("cli_scanapm: Initial Apple Partition Map partition is not detected\n"); return CL_EFORMAT; } /* check that the partition table fits in the space specified - HEURISTICS */ if ((ctx->options & CL_SCAN_PARTITION_INTXN) && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) { ret = apm_prtn_intxn(ctx, &aptable, sectorsize, old_school); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } /* print debugging info on partition tables */ cli_dbgmsg("APM Partition Table:\n"); cli_dbgmsg("Name: %s\n", (char*)aptable.name); cli_dbgmsg("Type: %s\n", (char*)aptable.type); cli_dbgmsg("Signature: %x\n", aptable.signature); cli_dbgmsg("Partition Count: %u\n", aptable.numPartitions); cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n", aptable.pBlockStart, aptable.pBlockCount, (unsigned long)(aptable.pBlockStart * sectorsize), (unsigned long)(aptable.pBlockCount * sectorsize)); /* check engine maxpartitions limit */ if (aptable.numPartitions < ctx->engine->maxpartitions) { max_prtns = aptable.numPartitions; } else { max_prtns = ctx->engine->maxpartitions; } /* partition table is a partition [at index 1], so skip it */ for (i = 2; i <= max_prtns; ++i) { /* read partition table entry */ pos = i * sectorsize; if (fmap_readn(*ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n"); return CL_EFORMAT; } /* convert partition entry big endian to host */ apentry.signature = be16_to_host(apentry.signature); apentry.reserved = be16_to_host(apentry.reserved); apentry.numPartitions = be32_to_host(apentry.numPartitions); apentry.pBlockStart = be32_to_host(apentry.pBlockStart); apentry.pBlockCount = be32_to_host(apentry.pBlockCount); /* check the partition entry signature */ if (aptable.signature != APM_SIGNATURE) { cli_dbgmsg("cli_scanapm: Apple partition entry signature mismatch\n"); return CL_EFORMAT; } /* check if a out-of-order partition map */ if (!strncmp((char*)apentry.type, "Apple_Partition_Map", 32) || !strncmp((char*)apentry.type, "Apple_partition_map", 32) || !strncmp((char*)apentry.type, "Apple_patition_map", 32)) { cli_dbgmsg("cli_scanapm: Out of order Apple Partition Map partition\n"); continue; } partoff = apentry.pBlockStart * sectorsize; partsize = apentry.pBlockCount * sectorsize; /* re-calculate if old_school and aligned [512 * 4 => 2048] */ if (old_school && ((i % 4) == 0)) { if (!strncmp((char*)apentry.type, "Apple_Driver", 32) || !strncmp((char*)apentry.type, "Apple_Driver43", 32) || !strncmp((char*)apentry.type, "Apple_Driver43_CD", 32) || !strncmp((char*)apentry.type, "Apple_Driver_ATA", 32) || !strncmp((char*)apentry.type, "Apple_Driver_ATAPI", 32) || !strncmp((char*)apentry.type, "Apple_Patches", 32)) { partsize = apentry.pBlockCount * 2048;; } } /* check if invalid partition */ if ((partoff == 0) || (partoff+partsize > maplen)) { cli_dbgmsg("cli_scanapm: Detected invalid Apple partition entry\n"); continue; } /* print debugging info on partition */ cli_dbgmsg("APM Partition Entry %u:\n", i); cli_dbgmsg("Name: %s\n", (char*)apentry.name); cli_dbgmsg("Type: %s\n", (char*)apentry.type); cli_dbgmsg("Signature: %x\n", apentry.signature); cli_dbgmsg("Partition Count: %u\n", apentry.numPartitions); cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n", apentry.pBlockStart, apentry.pBlockCount, (long unsigned)partoff, (long unsigned)partsize); /* send the partition to cli_map_scan */ ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } if (i >= ctx->engine->maxpartitions) { cli_dbgmsg("cli_scanapm: max partitions reached\n"); } return detection; }
static int apm_prtn_intxn(cli_ctx *ctx, struct apm_partition_info *aptable, size_t sectorsize, int old_school) { prtn_intxn_list_t prtncheck; struct apm_partition_info apentry; unsigned i, pitxn; int ret = CL_CLEAN, tmp = CL_CLEAN; off_t pos; uint32_t max_prtns = 0; prtn_intxn_list_init(&prtncheck); /* check engine maxpartitions limit */ if (aptable->numPartitions < ctx->engine->maxpartitions) { max_prtns = aptable->numPartitions; } else { max_prtns = ctx->engine->maxpartitions; } for (i = 1; i <= max_prtns; ++i) { /* read partition table entry */ pos = i * sectorsize; if (fmap_readn(*ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) { cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n"); prtn_intxn_list_free(&prtncheck); return CL_EFORMAT; } /* convert necessary info big endian to host */ apentry.pBlockStart = be32_to_host(apentry.pBlockStart); apentry.pBlockCount = be32_to_host(apentry.pBlockCount); /* re-calculate if old_school and aligned [512 * 4 => 2048] */ if (old_school && ((i % 4) == 0)) { if (!strncmp((char*)apentry.type, "Apple_Driver", 32) || !strncmp((char*)apentry.type, "Apple_Driver43", 32) || !strncmp((char*)apentry.type, "Apple_Driver43_CD", 32) || !strncmp((char*)apentry.type, "Apple_Driver_ATA", 32) || !strncmp((char*)apentry.type, "Apple_Driver_ATAPI", 32) || !strncmp((char*)apentry.type, "Apple_Patches", 32)) { apentry.pBlockCount = apentry.pBlockCount * 4;; } } tmp = prtn_intxn_list_check(&prtncheck, &pitxn, apentry.pBlockStart, apentry.pBlockCount); if (tmp != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) { apm_parsemsg("Name: %s\n", (char*)aptable.name); apm_parsemsg("Type: %s\n", (char*)aptable.type); cli_dbgmsg("cli_scanapm: detected intersection with partitions " "[%u, %u]\n", pitxn, i); cli_append_virus(ctx, PRTN_INTXN_DETECTION); ret = tmp; tmp = 0; } else if (tmp == CL_VIRUS) { apm_parsemsg("Name: %s\n", (char*)aptable.name); apm_parsemsg("Type: %s\n", (char*)aptable.type); cli_dbgmsg("cli_scanapm: detected intersection with partitions " "[%u, %u]\n", pitxn, i); cli_append_virus(ctx, PRTN_INTXN_DETECTION); prtn_intxn_list_free(&prtncheck); return CL_VIRUS; } else { prtn_intxn_list_free(&prtncheck); return tmp; } } pos += sectorsize; } prtn_intxn_list_free(&prtncheck); return ret; }
int cli_tnef(const char *dir, cli_ctx *ctx) { uint32_t i32; uint16_t i16; fileblob *fb; int ret, alldone; off_t fsize, pos = 0; STATBUF statb; fsize = ctx->fmap[0]->len; if(fsize < (off_t) MIN_SIZE) { cli_dbgmsg("cli_tngs: file too small, ignoring\n"); return CL_CLEAN; } if (fmap_readn(*ctx->fmap, &i32, pos, sizeof(uint32_t)) != sizeof(uint32_t)) { /* The file is at least MIN_SIZE bytes, so it "can't" fail */ return CL_EREAD; } pos += sizeof(uint32_t); if(host32(i32) != TNEF_SIGNATURE) { return CL_EFORMAT; } if(fmap_readn(*ctx->fmap, &i16, pos, sizeof(uint16_t)) != sizeof(uint16_t)) { /* The file is at least MIN_SIZE bytes, so it "can't" fail */ return CL_EREAD; } pos += sizeof(uint16_t); fb = NULL; ret = CL_CLEAN; /* we don't know if it's clean or not :-) */ alldone = 0; do { uint8_t part = 0; uint16_t type = 0, tag = 0; int32_t length = 0; switch(tnef_header(*ctx->fmap, &pos, &part, &type, &tag, &length)) { case 0: alldone = 1; break; case 1: break; default: /* * Assume truncation, not file I/O error */ cli_warnmsg("cli_tnef: file truncated, returning CLEAN\n"); ret = CL_CLEAN; alldone = 1; break; } if(length == 0) continue; if(length < 0) { cli_warnmsg("Corrupt TNEF header detected - length %d\n", (int)length); ret = CL_EFORMAT; break; } if(alldone) break; switch(part) { case LVL_MESSAGE: cli_dbgmsg("TNEF - found message\n"); if(fb != NULL) { fileblobDestroy(fb); fb = NULL; } fb = fileblobCreate(); if(tnef_message(*ctx->fmap, &pos, type, tag, length, fsize) != 0) { cli_dbgmsg("TNEF: Error reading TNEF message\n"); ret = CL_EFORMAT; alldone = 1; } break; case LVL_ATTACHMENT: cli_dbgmsg("TNEF - found attachment\n"); if(tnef_attachment(*ctx->fmap, &pos, type, tag, length, dir, &fb, fsize) != 0) { cli_dbgmsg("TNEF: Error reading TNEF attachment\n"); ret = CL_EFORMAT; alldone = 1; } break; case 0: break; default: cli_warnmsg("TNEF - unknown level %d tag 0x%x\n", (int)part, (int)tag); /* * Dump the file incase it was part of an * email that's about to be deleted */ if(cli_debug_flag) { int fout = -1; char *filename = cli_gentemp(ctx->engine->tmpdir); char buffer[BUFSIZ]; if(filename) fout = open(filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if(fout >= 0) { int count; cli_warnmsg("Saving dump to %s: refer to http://www.clamav.net/bugs\n", filename); pos = 0; while ((count = fmap_readn(*ctx->fmap, buffer, pos, sizeof(buffer))) > 0) { pos += count; cli_writen(fout, buffer, count); } close(fout); } free(filename); } ret = CL_EFORMAT; alldone = 1; break; } } while(!alldone); if(fb) { cli_dbgmsg("cli_tnef: flushing final data\n"); if(fileblobGetFilename(fb) == NULL) { cli_dbgmsg("Saving TNEF portion with an unknown name\n"); fileblobSetFilename(fb, dir, "tnef"); } fileblobDestroy(fb); fb = NULL; } cli_dbgmsg("cli_tnef: returning %d\n", ret); return ret; }
int cli_scancpio_old(cli_ctx *ctx) { struct cpio_hdr_old hdr_old; char name[513]; unsigned int file = 0, trailer = 0; uint32_t filesize, namesize, hdr_namesize; int ret, conv; off_t pos = 0; while(fmap_readn(*ctx->fmap, &hdr_old, pos, sizeof(hdr_old)) == sizeof(hdr_old)) { pos += sizeof(hdr_old); if(!hdr_old.magic && trailer) return CL_SUCCESS; if(hdr_old.magic == 070707) { conv = 0; } else if(hdr_old.magic == 0143561) { conv = 1; } else { cli_dbgmsg("cli_scancpio_old: Invalid magic number\n"); return CL_EFORMAT; } cli_dbgmsg("CPIO: -- File %u --\n", ++file); if(hdr_old.namesize) { hdr_namesize = EC16(hdr_old.namesize, conv); namesize = MIN(sizeof(name), hdr_namesize); if ((uint32_t)fmap_readn(*ctx->fmap, &name, pos, namesize) != namesize) { cli_dbgmsg("cli_scancpio_old: Can't read file name\n"); return CL_EFORMAT; } pos += namesize; name[namesize - 1] = 0; sanitname(name); cli_dbgmsg("CPIO: Name: %s\n", name); if(!strcmp(name, "TRAILER!!!")) trailer = 1; if(namesize < hdr_namesize) { if(hdr_namesize % 2) hdr_namesize++; pos += hdr_namesize - namesize; } else if(hdr_namesize % 2) pos++; } filesize = (uint32_t) (EC16(hdr_old.filesize[0], conv) << 16 | EC16(hdr_old.filesize[1], conv)); cli_dbgmsg("CPIO: Filesize: %u\n", filesize); if(!filesize) continue; if(cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) return CL_VIRUS; if((EC16(hdr_old.mode, conv) & 0170000) != 0100000) { cli_dbgmsg("CPIO: Not a regular file, skipping\n"); } else { ret = cli_checklimits("cli_scancpio_old", ctx, filesize, 0, 0); if(ret == CL_EMAXFILES) { return ret; } else if(ret == CL_SUCCESS) { ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY); if(ret == CL_VIRUS) return ret; } } if(filesize % 2) filesize++; pos += filesize; } return CL_CLEAN; }
static int scanzws(cli_ctx *ctx, struct swf_file_hdr *hdr) { struct CLI_LZMA lz; unsigned char inbuff[FILEBUFF], outbuff[FILEBUFF]; fmap_t *map = *ctx->fmap; /* strip off header */ off_t offset = 8; uint32_t d_insize; size_t outsize = 8; int ret, lret, count; char *tmpname; int fd; if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_errmsg("scanzws: Can't generate temporary file\n"); return ret; } hdr->signature[0] = 'F'; if(cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) { cli_errmsg("scanzws: Can't write to file %s\n", tmpname); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } /* read 4 bytes (for compressed 32-bit filesize) [not used for LZMA] */ if (fmap_readn(map, &d_insize, offset, sizeof(d_insize)) != sizeof(d_insize)) { cli_errmsg("scanzws: Error reading SWF file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EREAD; } offset += sizeof(d_insize); /* check if declared input size matches actual output size */ /* map->len = header (8 bytes) + d_insize (4 bytes) + flags (5 bytes) + compressed stream */ if (d_insize != (map->len - 17)) { cli_warnmsg("SWF: declared input length != compressed stream size, %u != %llu\n", d_insize, (long long unsigned)(map->len - 17)); } else { cli_dbgmsg("SWF: declared input length == compressed stream size, %u == %llu\n", d_insize, (long long unsigned)(map->len - 17)); } /* first buffer required for initializing LZMA */ ret = fmap_readn(map, inbuff, offset, FILEBUFF); if (ret < 0) { cli_errmsg("scanzws: Error reading SWF file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } /* nothing written, likely truncated */ if (!ret) { cli_errmsg("scanzws: possibly truncated file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EFORMAT; } offset += ret; memset(&lz, 0, sizeof(lz)); lz.next_in = inbuff; lz.next_out = outbuff; lz.avail_in = ret; lz.avail_out = FILEBUFF; lret = cli_LzmaInit(&lz, hdr->filesize); if (lret != LZMA_RESULT_OK) { cli_errmsg("scanzws: LzmaInit() failed\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } while (lret == LZMA_RESULT_OK) { if (lz.avail_in == 0) { lz.next_in = inbuff; ret = fmap_readn(map, inbuff, offset, FILEBUFF); if (ret < 0) { cli_errmsg("scanzws: Error reading SWF file\n"); cli_LzmaShutdown(&lz); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } if (!ret) break; lz.avail_in = ret; offset += ret; } lret = cli_LzmaDecode(&lz); count = FILEBUFF - lz.avail_out; if (count) { if (cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS) break; if (cli_writen(fd, outbuff, count) != count) { cli_errmsg("scanzws: Can't write to file %s\n", tmpname); cli_LzmaShutdown(&lz); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } outsize += count; } lz.next_out = outbuff; lz.avail_out = FILEBUFF; } cli_LzmaShutdown(&lz); if (lret != LZMA_STREAM_END && lret != LZMA_RESULT_OK) { /* outsize starts at 8, therefore, if its still 8, nothing was decompressed */ if (outsize == 8) { cli_infomsg(ctx, "scanzws: Error decompressing SWF file. No data decompressed.\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } cli_infomsg(ctx, "scanzws: Error decompressing SWF file. Scanning what was decompressed.\n"); } cli_dbgmsg("SWF: Decompressed[LZMA] to %s, size %llu\n", tmpname, (long long unsigned)outsize); /* check if declared output size matches actual output size */ if (hdr->filesize != outsize) { cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n", hdr->filesize, (long long unsigned)outsize); } else { cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n", hdr->filesize, (long long unsigned)outsize); } ret = cli_magic_scandesc(fd, ctx); close(fd); if (!(ctx->engine->keeptmp)) { if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } } free(tmpname); return ret; }
/* 64-bit version of section header parsing */ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo, struct elf_file_hdr64 *file_hdr, uint8_t conv) { struct elf_section_hdr64 *section_hdr = NULL; uint16_t shnum, shentsize; uint32_t i; uint64_t shoff; shnum = file_hdr->e_shnum; cli_dbgmsg("ELF: Number of sections: %d\n", shnum); if(ctx && (shnum > 2048)) { cli_dbgmsg("ELF: Number of sections > 2048, skipping\n"); return CL_BREAK; } else if(elfinfo && (shnum > 256)) { cli_dbgmsg("ELF: Suspicious number of sections\n"); return CL_BREAK; } if(elfinfo) { elfinfo->nsections = shnum; } shentsize = file_hdr->e_shentsize; /* Sanity check */ if(shentsize != sizeof(struct elf_section_hdr64)) { cli_dbgmsg("ELF: shentsize != sizeof(struct elf_section_hdr64)\n"); if(ctx && DETECT_BROKEN) { cli_append_virus(ctx, "Heuristics.Broken.Executable"); return CL_VIRUS; } return CL_EFORMAT; } if(elfinfo && !shnum) { return CL_CLEAN; } shoff = file_hdr->e_shoff; if(ctx) cli_dbgmsg("ELF: Section header table offset: " STDu64 "\n", shoff); if(elfinfo) { elfinfo->section = (struct cli_exe_section *)cli_calloc(shnum, sizeof(struct cli_exe_section)); if(!elfinfo->section) { cli_dbgmsg("ELF: Can't allocate memory for section headers\n"); return CL_EMEM; } } if(shnum) { section_hdr = (struct elf_section_hdr64 *) cli_calloc(shnum, shentsize); if(!section_hdr) { cli_errmsg("ELF: Can't allocate memory for section headers\n"); if(elfinfo) { free(elfinfo->section); elfinfo->section = NULL; } return CL_EMEM; } if(ctx) { cli_dbgmsg("------------------------------------\n"); } } /* Loop over section headers */ for(i = 0; i < shnum; i++) { uint32_t sh_type, sh_flags; if(fmap_readn(map, §ion_hdr[i], shoff, sizeof(struct elf_section_hdr64)) != sizeof(struct elf_section_hdr64)) { cli_dbgmsg("ELF: Can't read section header\n"); if(ctx) { cli_dbgmsg("ELF: Possibly broken ELF file\n"); } free(section_hdr); if(elfinfo) { free(elfinfo->section); elfinfo->section = NULL; } if(ctx && DETECT_BROKEN) { cli_append_virus(ctx, "Heuristics.Broken.Executable"); return CL_VIRUS; } return CL_BREAK; } shoff += sizeof(struct elf_section_hdr64); if(elfinfo) { elfinfo->section[i].rva = EC64(section_hdr[i].sh_addr, conv); elfinfo->section[i].raw = EC64(section_hdr[i].sh_offset, conv); elfinfo->section[i].rsz = EC64(section_hdr[i].sh_size, conv); } if(ctx) { cli_dbgmsg("ELF: Section %u\n", i); cli_dbgmsg("ELF: Section offset: " STDu64 "\n", EC64(section_hdr[i].sh_offset, conv)); cli_dbgmsg("ELF: Section size: " STDu64 "\n", EC64(section_hdr[i].sh_size, conv)); sh_type = EC32(section_hdr[i].sh_type, conv); sh_flags = (uint32_t)(EC64(section_hdr[i].sh_flags, conv) & ELF_SHF_MASK); cli_elf_sectionlog(sh_type, sh_flags); cli_dbgmsg("------------------------------------\n"); } } free(section_hdr); return CL_CLEAN; }
/* checks internal logical partitions */ static int mbr_extended_prtn_intxn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t sectorsize) { struct mbr_boot_record ebr; prtn_intxn_list_t prtncheck; unsigned i, pitxn; int ret = CL_CLEAN, tmp = CL_CLEAN, mbr_base = 0; off_t pos = 0, logiclba = 0; mbr_base = sectorsize - sizeof(struct mbr_boot_record); prtn_intxn_list_init(&prtncheck); logiclba = 0; i = 0; do { pos = extlba * sectorsize; /* start of extended partition */ /* read the extended boot record */ pos += (logiclba * sectorsize) + mbr_base; if (fmap_readn(*ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) { cli_dbgmsg("cli_scanebr: Invalid extended boot record\n"); prtn_intxn_list_free(&prtncheck); return CL_EFORMAT; } /* convert the little endian to host */ mbr_convert_to_host(&ebr); /* update state */ (*prtncount)++; /* assume that logical record is first and extended is second */ tmp = prtn_intxn_list_check(&prtncheck, &pitxn, logiclba, ebr.entries[0].numLBA); if (tmp != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) { cli_dbgmsg("cli_scanebr: detected intersection with partitions " "[%u, %u]\n", pitxn, i); cli_append_virus(ctx, PRTN_INTXN_DETECTION); ret = tmp; tmp = 0; } else if (tmp == CL_VIRUS) { cli_dbgmsg("cli_scanebr: detected intersection with partitions " "[%u, %u]\n", pitxn, i); cli_append_virus(ctx, PRTN_INTXN_DETECTION); prtn_intxn_list_free(&prtncheck); return CL_VIRUS; } else { prtn_intxn_list_free(&prtncheck); return tmp; } } /* assume extended is second entry */ if (ebr.entries[1].type != MBR_EXTENDED) { cli_dbgmsg("cli_scanebr: second entry for EBR is not an extended partition\n"); break; } logiclba = ebr.entries[1].firstLBA; ++i; } while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions); prtn_intxn_list_free(&prtncheck); return ret; }
static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t extlbasize, size_t sectorsize) { struct mbr_boot_record ebr; enum MBR_STATE state = SEEN_NOTHING; int ret = CL_CLEAN, detection = CL_CLEAN; off_t pos = 0, mbr_base = 0, logiclba = 0, extoff = 0, partoff = 0; size_t partsize, extsize; unsigned i = 0, j = 0; ebr_parsemsg("The start of something exhausting: EBR parsing\n"); mbr_base = sectorsize - sizeof(struct mbr_boot_record); logiclba = 0; extoff = extlba * sectorsize; extsize = extlbasize * sectorsize; do { pos = extlba * sectorsize; /* start of extended partition */ /* read the extended boot record */ pos += (logiclba * sectorsize) + mbr_base; if (fmap_readn(*ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) { cli_dbgmsg("cli_scanebr: Invalid extended boot record\n"); return CL_EFORMAT; } /* convert the little endian to host */ mbr_convert_to_host(&ebr); /* EBR checks */ ret = mbr_check_ebr(&ebr); if (ret != CL_CLEAN) { return ret; } /* update state */ state = SEEN_NOTHING; (*prtncount)++; /* EBR is valid, examine partitions */ cli_dbgmsg("EBR Partition Entry %u:\n", i++); cli_dbgmsg("EBR Signature: %x\n", ebr.signature); for (j = 0; j < MBR_MAX_PARTITION_ENTRIES; ++j) { if (j < 2) { cli_dbgmsg("Logical Partition Entry %u:\n", j); cli_dbgmsg("Status: %u\n", ebr.entries[j].status); cli_dbgmsg("Type: %x\n", ebr.entries[j].type); cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n", ebr.entries[j].firstLBA, ebr.entries[j].numLBA, (unsigned long)(ebr.entries[j].firstLBA * sectorsize), (unsigned long)(ebr.entries[j].numLBA * sectorsize)); if (ebr.entries[j].type == MBR_EMPTY) { /* empty partiton entry */ switch(state) { case SEEN_NOTHING: state = SEEN_EMPTY; break; case SEEN_PARTITION: logiclba = 0; break; case SEEN_EMPTY: logiclba = 0; /* fall-through */ case SEEN_EXTENDED: cli_warnmsg("cli_scanebr: detected a logical boot record " "without a partition record\n"); break; default: cli_warnmsg("cli_scanebr: undefined state for EBR parsing\n"); return CL_EPARSE; } } else if (ebr.entries[j].type == MBR_EXTENDED) { switch(state) { case SEEN_NOTHING: state = SEEN_EXTENDED; break; case SEEN_PARTITION: break; case SEEN_EMPTY: cli_warnmsg("cli_scanebr: detected a logical boot record " "without a partition record\n"); break; case SEEN_EXTENDED: cli_warnmsg("cli_scanebr: detected a logical boot record " "with multiple extended partition records\n"); return CL_EFORMAT; default: cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n"); return CL_EPARSE; } logiclba = ebr.entries[j].firstLBA; } else { switch(state) { case SEEN_NOTHING: state = SEEN_PARTITION; break; case SEEN_PARTITION: cli_warnmsg("cli_scanebr: detected a logical boot record " "with multiple partition records\n"); logiclba = 0; /* no extended partitions are possible */ break; case SEEN_EXTENDED: cli_warnmsg("cli_scanebr: detected a logical boot record " "with extended partition record first\n"); break; case SEEN_EMPTY: cli_warnmsg("cli_scanebr: detected a logical boot record " "with empty partition record first\n"); logiclba = 0; /* no extended partitions are possible */ break; default: cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n"); return CL_EPARSE; } partoff = (extlba + logiclba + ebr.entries[j].firstLBA) * sectorsize; partsize = ebr.entries[j].numLBA * sectorsize; if (partoff + partsize > extoff + extsize) { cli_dbgmsg("cli_scanebr: Invalid extended partition entry\n"); return CL_EFORMAT; } ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } } else { /* check the last two entries to be empty */ if (ebr.entries[j].type != MBR_EMPTY) { cli_dbgmsg("cli_scanebr: detected a non-empty partition " "entry at index %u\n", j); /* should we attempt to use these entries? */ return CL_EFORMAT; } } } } while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions); cli_dbgmsg("cli_scanmbr: examined %u logical partitions\n", i); return detection; }
/* sets sectorsize to default value if specified to be 0 */ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize) { struct mbr_boot_record mbr; enum MBR_STATE state = SEEN_NOTHING; int ret = CL_CLEAN, detection = CL_CLEAN; off_t pos = 0, mbr_base = 0, partoff = 0; unsigned i = 0, prtncount = 0; size_t maplen, partsize; mbr_parsemsg("The start of something magnificant: MBR parsing\n"); if (!ctx || !ctx->fmap) { cli_errmsg("cli_scanmbr: Invalid context\n"); return CL_ENULLARG; } /* sector size calculation, actual value is OS dependent */ if (sectorsize == 0) sectorsize = MBR_SECTOR_SIZE; mbr_base = sectorsize - sizeof(struct mbr_boot_record); /* size of total file must be a multiple of the sector size */ maplen = (*ctx->fmap)->real_len; if ((maplen % sectorsize) != 0) { cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n", (unsigned long)maplen, (unsigned long)sectorsize); return CL_EFORMAT; } /* sector 0 (first sector) is the master boot record */ pos = (MBR_SECTOR * sectorsize) + mbr_base; /* read the master boot record */ if (fmap_readn(*ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) { cli_dbgmsg("cli_scanmbr: Invalid master boot record\n"); return CL_EFORMAT; } /* convert the little endian to host, include the internal */ mbr_convert_to_host(&mbr); /* MBR checks */ ret = mbr_check_mbr(&mbr, maplen, sectorsize); if (ret != CL_CLEAN) { return ret; } /* MBR is valid, examine bootstrap code */ ret = cli_map_scan(*ctx->fmap, 0, sectorsize, ctx, CL_TYPE_ANY); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } /* check that the partition table has no intersections - HEURISTICS */ if ((ctx->options & CL_SCAN_PARTITION_INTXN) && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) { ret = mbr_primary_prtn_intxn(ctx, mbr, sectorsize); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } /* MBR is valid, examine partitions */ prtncount = 0; cli_dbgmsg("MBR Signature: %x\n", mbr.signature); for (i = 0; i < MBR_MAX_PARTITION_ENTRIES && prtncount < ctx->engine->maxpartitions; ++i) { cli_dbgmsg("MBR Partition Entry %u:\n", i); cli_dbgmsg("Status: %u\n", mbr.entries[i].status); cli_dbgmsg("Type: %x\n", mbr.entries[i].type); cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n", mbr.entries[i].firstLBA, mbr.entries[i].numLBA, (unsigned long)(mbr.entries[i].firstLBA * sectorsize), (unsigned long)(mbr.entries[i].numLBA * sectorsize)); /* Handle MBR entry based on type */ if (mbr.entries[i].type == MBR_EMPTY) { /* empty partiton entry */ prtncount++; } else if (mbr.entries[i].type == MBR_EXTENDED) { if (state == SEEN_EXTENDED) { cli_dbgmsg("cli_scanmbr: detected a master boot record " "with multiple extended partitions\n"); } state = SEEN_EXTENDED; /* used only to detect multiple extended partitions */ ret = mbr_scanextprtn(ctx, &prtncount, mbr.entries[i].firstLBA, mbr.entries[i].numLBA, sectorsize); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } else { prtncount++; partoff = mbr.entries[i].firstLBA * sectorsize; partsize = mbr.entries[i].numLBA * sectorsize; mbr_parsemsg("cli_map_scan: [%u, +%u)\n", partoff, partsize); ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY); if (ret != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } } if (prtncount >= ctx->engine->maxpartitions) { cli_dbgmsg("cli_scanmbr: maximum partitions reached\n"); } return detection; }
int cli_scanxar(cli_ctx *ctx) { int rc = CL_SUCCESS; unsigned int cksum_fails = 0; unsigned int extract_errors = 0; #if HAVE_LIBXML2 int fd = -1; struct xar_header hdr; fmap_t *map = *ctx->fmap; long length, offset, size, at; int encoding; z_stream strm; char *toc, *tmpname; xmlTextReaderPtr reader = NULL; int a_hash, e_hash; unsigned char *a_cksum = NULL, *e_cksum = NULL; memset(&strm, 0x00, sizeof(z_stream)); /* retrieve xar header */ if (fmap_readn(*ctx->fmap, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scanxar: Invalid header, too short.\n"); return CL_EFORMAT; } hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == XAR_HEADER_MAGIC) { cli_dbgmsg("cli_scanxar: Matched magic\n"); } else { cli_dbgmsg("cli_scanxar: Invalid magic\n"); return CL_EFORMAT; } hdr.size = be16_to_host(hdr.size); hdr.version = be16_to_host(hdr.version); hdr.toc_length_compressed = be64_to_host(hdr.toc_length_compressed); hdr.toc_length_decompressed = be64_to_host(hdr.toc_length_decompressed); hdr.chksum_alg = be32_to_host(hdr.chksum_alg); /* cli_dbgmsg("hdr.magic %x\n", hdr.magic); */ /* cli_dbgmsg("hdr.size %i\n", hdr.size); */ /* cli_dbgmsg("hdr.version %i\n", hdr.version); */ /* cli_dbgmsg("hdr.toc_length_compressed %lu\n", hdr.toc_length_compressed); */ /* cli_dbgmsg("hdr.toc_length_decompressed %lu\n", hdr.toc_length_decompressed); */ /* cli_dbgmsg("hdr.chksum_alg %i\n", hdr.chksum_alg); */ /* Uncompress TOC */ strm.next_in = (unsigned char *)fmap_need_off_once(*ctx->fmap, hdr.size, hdr.toc_length_compressed); if (strm.next_in == NULL) { cli_dbgmsg("cli_scanxar: fmap_need_off_once fails on TOC.\n"); return CL_EREAD; } strm.avail_in = hdr.toc_length_compressed; toc = cli_malloc(hdr.toc_length_decompressed+1); if (toc == NULL) { cli_dbgmsg("cli_scanxar: cli_malloc fails on TOC decompress buffer.\n"); return CL_EMEM; } toc[hdr.toc_length_decompressed] = '\0'; strm.avail_out = hdr.toc_length_decompressed; strm.next_out = (unsigned char *)toc; rc = inflateInit(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateInit error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflate(&strm, Z_SYNC_FLUSH); if (rc != Z_OK && rc != Z_STREAM_END) { cli_dbgmsg("cli_scanxar:inflate error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflateEnd(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateEnd error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } /* cli_dbgmsg("cli_scanxar: TOC xml:\n%s\n", toc); */ /* printf("cli_scanxar: TOC xml:\n%s\n", toc); */ /* cli_dbgmsg("cli_scanxar: TOC end:\n"); */ /* printf("cli_scanxar: TOC end:\n"); */ /* scan the xml */ cli_dbgmsg("cli_scanxar: scanning xar TOC xml in memory.\n"); rc = cli_mem_scandesc(toc, hdr.toc_length_decompressed, ctx); if (rc != CL_SUCCESS) { if (rc != CL_VIRUS || !SCAN_ALL) goto exit_toc; } /* make a file to leave if --leave-temps in effect */ if(ctx->engine->keeptmp) { if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't create temporary file for TOC.\n"); goto exit_toc; } if (cli_writen(fd, toc, hdr.toc_length_decompressed) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing TOC.\n"); rc = CL_EWRITE; xar_cleanup_temp_file(ctx, fd, tmpname); goto exit_toc; } rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_toc; } reader = xmlReaderForMemory(toc, hdr.toc_length_decompressed, "noname.xml", NULL, 0); if (reader == NULL) { cli_dbgmsg("cli_scanxar: xmlReaderForMemory error for TOC\n"); goto exit_toc; } rc = xar_scan_subdocuments(reader, ctx); if (rc != CL_SUCCESS) { cli_dbgmsg("xar_scan_subdocuments returns %i.\n", rc); goto exit_reader; } /* Walk the TOC XML and extract files */ fd = -1; tmpname = NULL; while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding, &a_cksum, &a_hash, &e_cksum, &e_hash))) { int do_extract_cksum = 1; unsigned char * blockp; void *a_sc, *e_sc; void *a_mc, *e_mc; void *a_hash_ctx, *e_hash_ctx; char result[SHA1_HASH_SIZE]; char * expected; /* clean up temp file from previous loop iteration */ if (fd > -1 && tmpname) { rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_reader; } at = offset + hdr.toc_length_compressed + hdr.size; if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't generate temporary file.\n"); goto exit_reader; } cli_dbgmsg("cli_scanxar: decompress into temp file:\n%s, size %li,\n" "from xar heap offset %li length %li\n", tmpname, size, offset, length); a_hash_ctx = xar_hash_init(a_hash, &a_sc, &a_mc); e_hash_ctx = xar_hash_init(e_hash, &e_sc, &e_mc); switch (encoding) { case CL_TYPE_GZ: /* inflate gzip directly because file segments do not contain magic */ memset(&strm, 0, sizeof(strm)); if ((rc = inflateInit(&strm)) != Z_OK) { cli_dbgmsg("cli_scanxar: InflateInit failed: %d\n", rc); rc = CL_EFORMAT; extract_errors++; break; } while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { unsigned long avail_in; void * next_in; unsigned int bytes = MIN(map->len - at, map->pgsz); bytes = MIN(length, bytes); if(!(strm.next_in = next_in = (void*)fmap_need_off_once(map, at, bytes))) { cli_dbgmsg("cli_scanxar: Can't read %u bytes @ %lu.\n", bytes, (long unsigned)at); inflateEnd(&strm); rc = CL_EREAD; goto exit_tmpfile; } at += bytes; strm.avail_in = avail_in = bytes; do { int inf, outsize = 0; unsigned char buff[FILEBUFF]; strm.avail_out = sizeof(buff); strm.next_out = buff; inf = inflate(&strm, Z_SYNC_FLUSH); if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) { cli_dbgmsg("cli_scanxar: inflate error %i %s.\n", inf, strm.msg?strm.msg:""); rc = CL_EFORMAT; extract_errors++; break; } bytes = sizeof(buff) - strm.avail_out; xar_hash_update(e_hash_ctx, buff, bytes, e_hash); if (cli_writen(fd, buff, bytes) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error file %s.\n", tmpname); inflateEnd(&strm); rc = CL_EWRITE; goto exit_tmpfile; } outsize += sizeof(buff) - strm.avail_out; if (cli_checklimits("cli_scanxar", ctx, outsize, 0, 0) != CL_CLEAN) { break; } if (inf == Z_STREAM_END) { break; } } while (strm.avail_out == 0); if (rc != CL_SUCCESS) break; avail_in -= strm.avail_in; xar_hash_update(a_hash_ctx, next_in, avail_in, a_hash); } inflateEnd(&strm); break; case CL_TYPE_7Z: #define CLI_LZMA_OBUF_SIZE 1024*1024 #define CLI_LZMA_HDR_SIZE LZMA_PROPS_SIZE+8 #define CLI_LZMA_IBUF_SIZE CLI_LZMA_OBUF_SIZE>>2 /* estimated compression ratio 25% */ { struct CLI_LZMA lz; unsigned long in_remaining = length; unsigned long out_size = 0; unsigned char * buff = __lzma_wrap_alloc(NULL, CLI_LZMA_OBUF_SIZE); int lret; memset(&lz, 0, sizeof(lz)); if (buff == NULL) { cli_dbgmsg("cli_scanxar: memory request for lzma decompression buffer fails.\n"); rc = CL_EMEM; goto exit_tmpfile; } blockp = (void*)fmap_need_off_once(map, at, CLI_LZMA_HDR_SIZE); if (blockp == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); goto exit_tmpfile; } lz.next_in = blockp; lz.avail_in = CLI_LZMA_HDR_SIZE; xar_hash_update(a_hash_ctx, blockp, CLI_LZMA_HDR_SIZE, a_hash); lret = cli_LzmaInit(&lz, 0); if (lret != LZMA_RESULT_OK) { cli_dbgmsg("cli_scanxar: cli_LzmaInit() fails: %i.\n", lret); rc = CL_EFORMAT; __lzma_wrap_free(NULL, buff); extract_errors++; break; } at += CLI_LZMA_HDR_SIZE; in_remaining -= CLI_LZMA_HDR_SIZE; while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { SizeT avail_in; SizeT avail_out; void * next_in; unsigned long in_consumed; lz.next_out = buff; lz.avail_out = CLI_LZMA_OBUF_SIZE; lz.avail_in = avail_in = MIN(CLI_LZMA_IBUF_SIZE, in_remaining); lz.next_in = next_in = (void*)fmap_need_off_once(map, at, lz.avail_in); if (lz.next_in == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno: %s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); goto exit_tmpfile; } lret = cli_LzmaDecode(&lz); if (lret != LZMA_RESULT_OK && lret != LZMA_STREAM_END) { cli_dbgmsg("cli_scanxar: cli_LzmaDecode() fails: %i.\n", lret); rc = CL_EFORMAT; extract_errors++; break; } in_consumed = avail_in - lz.avail_in; in_remaining -= in_consumed; at += in_consumed; avail_out = CLI_LZMA_OBUF_SIZE - lz.avail_out; if (avail_out == 0) cli_dbgmsg("cli_scanxar: cli_LzmaDecode() produces no output for " "avail_in %lu, avail_out %lu.\n", avail_in, avail_out); xar_hash_update(a_hash_ctx, next_in, in_consumed, a_hash); xar_hash_update(e_hash_ctx, buff, avail_out, e_hash); /* Write a decompressed block. */ /* cli_dbgmsg("Writing %li bytes to LZMA decompress temp file, " */ /* "consumed %li of %li available compressed bytes.\n", */ /* avail_out, in_consumed, avail_in); */ if (cli_writen(fd, buff, avail_out) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing lzma temp file for %li bytes.\n", avail_out); __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); rc = CL_EWRITE; goto exit_tmpfile; } /* Check file size limitation. */ out_size += avail_out; if (cli_checklimits("cli_scanxar", ctx, out_size, 0, 0) != CL_CLEAN) { break; } if (lret == LZMA_STREAM_END) break; } cli_LzmaShutdown(&lz); __lzma_wrap_free(NULL, buff); } break; case CL_TYPE_ANY: default: case CL_TYPE_BZ: case CL_TYPE_XZ: /* for uncompressed, bzip2, xz, and unknown, just pull the file, cli_magic_scandesc does the rest */ do_extract_cksum = 0; { unsigned long write_len; if (ctx->engine->maxfilesize) write_len = MIN((size_t)(ctx->engine->maxfilesize), (size_t)length); else write_len = length; if (!(blockp = (void*)fmap_need_off_once(map, at, length))) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; goto exit_tmpfile; } xar_hash_update(a_hash_ctx, blockp, length, a_hash); if (cli_writen(fd, blockp, write_len) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error %li bytes @ %li.\n", length, at); rc = CL_EWRITE; goto exit_tmpfile; } /*break;*/ } } if (rc == CL_SUCCESS) { xar_hash_final(a_hash_ctx, result, a_hash); if (a_cksum != NULL) { expected = cli_hex2str((char *)a_cksum); if (xar_hash_check(a_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: archived-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: archived-checksum matched.\n"); } free(expected); } if (e_cksum != NULL) { if (do_extract_cksum) { xar_hash_final(e_hash_ctx, result, e_hash); expected = cli_hex2str((char *)e_cksum); if (xar_hash_check(e_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: extracted-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: extracted-checksum matched.\n"); } free(expected); } } rc = cli_magic_scandesc(fd, ctx); if (rc != CL_SUCCESS) { if (rc == CL_VIRUS) { cli_dbgmsg("cli_scanxar: Infected with %s\n", cli_get_last_virus(ctx)); if (!SCAN_ALL) goto exit_tmpfile; } else if (rc != CL_BREAK) { cli_dbgmsg("cli_scanxar: cli_magic_scandesc error %i\n", rc); goto exit_tmpfile; } } } if (a_cksum != NULL) { xmlFree(a_cksum); a_cksum = NULL; } if (e_cksum != NULL) { xmlFree(e_cksum); e_cksum = NULL; } } exit_tmpfile: xar_cleanup_temp_file(ctx, fd, tmpname); exit_reader: if (a_cksum != NULL) xmlFree(a_cksum); if (e_cksum != NULL) xmlFree(e_cksum); xmlTextReaderClose(reader); xmlFreeTextReader(reader); exit_toc: free(toc); if (rc == CL_BREAK) rc = CL_SUCCESS; #else cli_dbgmsg("cli_scanxar: can't scan xar files, need libxml2.\n"); #endif if (cksum_fails + extract_errors != 0) { cli_warnmsg("cli_scanxar: %u checksum errors and %u extraction errors, use --debug for more info.\n", cksum_fails, extract_errors); } return rc; }
/* Read callback for lzx compressed data */ static int chm_readn(struct cab_file *file, unsigned char *buffer, int bytes) { int ret = fmap_readn(file->cab->map, buffer, file->cab->cur_offset, bytes); if(ret > 0) file->cab->cur_offset += ret; return ret; }
static int scancws(cli_ctx *ctx, struct swf_file_hdr *hdr) { z_stream stream; char inbuff[FILEBUFF], outbuff[FILEBUFF]; fmap_t *map = *ctx->fmap; int offset = 8, ret, zret, outsize = 8, count, zend; char *tmpname; int fd; if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_errmsg("scancws: Can't generate temporary file\n"); return ret; } hdr->signature[0] = 'F'; if(cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) { cli_errmsg("scancws: Can't write to file %s\n", tmpname); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } stream.avail_in = 0; stream.next_in = (Bytef *)inbuff; stream.next_out = (Bytef *)outbuff; stream.zalloc = (alloc_func) NULL; stream.zfree = (free_func) NULL; stream.opaque = (voidpf) 0; stream.avail_out = FILEBUFF; zret = inflateInit(&stream); if(zret != Z_OK) { cli_errmsg("scancws: inflateInit() failed\n"); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } do { if(stream.avail_in == 0) { stream.next_in = (Bytef *)inbuff; ret = fmap_readn(map, inbuff, offset, FILEBUFF); if(ret < 0) { cli_errmsg("scancws: Error reading SWF file\n"); close(fd); inflateEnd(&stream); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } if(!ret) break; stream.avail_in = ret; offset += ret; } zret = inflate(&stream, Z_SYNC_FLUSH); count = FILEBUFF - stream.avail_out; if(count) { if(cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS) break; if(cli_writen(fd, outbuff, count) != count) { cli_errmsg("scancws: Can't write to file %s\n", tmpname); inflateEnd(&stream); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } outsize += count; } stream.next_out = (Bytef *)outbuff; stream.avail_out = FILEBUFF; } while(zret == Z_OK); zend = inflateEnd(&stream); if((zret != Z_STREAM_END && zret != Z_OK) || zend != Z_OK) { /* * outsize is initialized to 8, it being 8 here means that we couldn't even read a single byte. * If outsize > 8, then we have data. Let's scan what we have. */ if (outsize == 8) { cli_infomsg(ctx, "scancws: Error decompressing SWF file. No data decompressed.\n"); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } cli_infomsg(ctx, "scancws: Error decompressing SWF file. Scanning what was decompressed.\n"); } cli_dbgmsg("SWF: Decompressed[zlib] to %s, size %d\n", tmpname, outsize); /* check if declared output size matches actual output size */ if (hdr->filesize != outsize) { cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n", hdr->filesize, (long long unsigned)outsize); } else { cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n", hdr->filesize, (long long unsigned)outsize); } ret = cli_magic_scandesc(fd, ctx); close(fd); if(!ctx->engine->keeptmp) { if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } } free(tmpname); return ret; }
int cli_scancpio_odc(cli_ctx *ctx) { struct cpio_hdr_odc hdr_odc; char name[513], buff[12]; unsigned int file = 0, trailer = 0; uint32_t filesize, namesize, hdr_namesize; int ret = CL_CLEAN; off_t pos = 0; int virus_found = 0; while(fmap_readn(*ctx->fmap, &hdr_odc, pos, sizeof(hdr_odc)) == sizeof(hdr_odc)) { pos += sizeof(hdr_odc); if(!hdr_odc.magic[0] && trailer) goto leave; if(strncmp(hdr_odc.magic, "070707", 6)) { cli_dbgmsg("cli_scancpio_odc: Invalid magic string\n"); ret = CL_EFORMAT; goto leave; } cli_dbgmsg("CPIO: -- File %u --\n", ++file); strncpy(buff, hdr_odc.namesize, 6); buff[6] = 0; if(sscanf(buff, "%o", &hdr_namesize) != 1) { cli_dbgmsg("cli_scancpio_odc: Can't convert name size\n"); ret = CL_EFORMAT; goto leave; } if(hdr_namesize) { namesize = MIN(sizeof(name), hdr_namesize); if ((uint32_t)fmap_readn(*ctx->fmap, &name, pos, namesize) != namesize) { cli_dbgmsg("cli_scancpio_odc: Can't read file name\n"); ret = CL_EFORMAT; goto leave; } pos += namesize; name[namesize - 1] = 0; sanitname(name); cli_dbgmsg("CPIO: Name: %s\n", name); if(!strcmp(name, "TRAILER!!!")) trailer = 1; if(namesize < hdr_namesize) pos += hdr_namesize - namesize; } strncpy(buff, hdr_odc.filesize, 11); buff[11] = 0; if(sscanf(buff, "%o", &filesize) != 1) { cli_dbgmsg("cli_scancpio_odc: Can't convert file size\n"); ret = CL_EFORMAT; goto leave; } cli_dbgmsg("CPIO: Filesize: %u\n", filesize); if(!filesize) continue; if(cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) { if (!SCAN_ALL) return CL_VIRUS; virus_found = 1; } ret = cli_checklimits("cli_scancpio_odc", ctx, filesize, 0, 0); if(ret == CL_EMAXFILES) { goto leave; } else if(ret == CL_SUCCESS) { ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY); if(ret == CL_VIRUS) { if (!SCAN_ALL) return ret; virus_found = 1; } } pos += filesize; } leave: if (virus_found != 0) return CL_VIRUS; return ret; }
int cli_scanswf(cli_ctx *ctx) { struct swf_file_hdr file_hdr; fmap_t *map = *ctx->fmap; unsigned int bitpos, bitbuf, getbits_n, nbits, getword_1, getword_2, getdword_1, getdword_2; const char *pt; unsigned char get_c; size_t offset = 0; unsigned int val, foo, tag_hdr, tag_type, tag_len; unsigned long int bits; cli_dbgmsg("in cli_scanswf()\n"); if(fmap_readn(map, &file_hdr, offset, sizeof(file_hdr)) != sizeof(file_hdr)) { cli_dbgmsg("SWF: Can't read file header\n"); return CL_CLEAN; } offset += sizeof(file_hdr); if(!strncmp(file_hdr.signature, "CWS", 3)) { cli_dbgmsg("SWF: zlib compressed file\n"); return scancws(ctx, &file_hdr); } else if(!strncmp(file_hdr.signature, "ZWS", 3)) { cli_dbgmsg("SWF: LZMA compressed file\n"); return scanzws(ctx, &file_hdr); } else if(!strncmp(file_hdr.signature, "FWS", 3)) { cli_dbgmsg("SWF: Uncompressed file\n"); } else { cli_dbgmsg("SWF: Not a SWF file\n"); return CL_CLEAN; } cli_dbgmsg("SWF: Version: %u\n", file_hdr.version); cli_dbgmsg("SWF: File size: %u\n", EC32(file_hdr.filesize)); INITBITS; GETBITS(nbits, 5); cli_dbgmsg("SWF: FrameSize RECT size bits: %u\n", nbits); { uint32_t xMin = 0, xMax = 0, yMin = 0, yMax = 0; GETBITS(xMin, nbits); /* Should be zero */ GETBITS(xMax, nbits); GETBITS(yMin, nbits); /* Should be zero */ GETBITS(yMax, nbits); cli_dbgmsg("SWF: FrameSize xMin %u xMax %u yMin %u yMax %u\n", xMin, xMax, yMin, yMax); } GETWORD(foo); GETWORD(val); cli_dbgmsg("SWF: Frames total: %d\n", val); /* Skip Flash tag walk unless debug mode */ if(!cli_debug_flag) { return CL_CLEAN; } while(offset < map->len) { GETWORD(tag_hdr); tag_type = tag_hdr >> 6; if(tag_type == 0) break; tag_len = tag_hdr & 0x3f; if(tag_len == 0x3f) GETDWORD(tag_len); pt = tagname(tag_type); cli_dbgmsg("SWF: %s\n", pt ? pt : "UNKNOWN TAG"); cli_dbgmsg("SWF: Tag length: %u\n", tag_len); if (tag_len > map->len) { cli_dbgmsg("SWF: Invalid tag length.\n"); return CL_EFORMAT; } if ((offset + tag_len) < offset) { cli_warnmsg("SWF: Tag length too large.\n"); break; } if(!pt) { offset += tag_len; continue; } switch(tag_type) { case TAG_SCRIPTLIMITS: { unsigned int recursion, timeout; GETWORD(recursion); GETWORD(timeout); cli_dbgmsg("SWF: scriptLimits recursion %u timeout %u\n", recursion, timeout); break; } case TAG_FILEATTRIBUTES: GETDWORD(val); cli_dbgmsg("SWF: File attributes:\n"); if(val & SWF_ATTR_USENETWORK) cli_dbgmsg(" * Use network\n"); if(val & SWF_ATTR_RELATIVEURLS) cli_dbgmsg(" * Relative URLs\n"); if(val & SWF_ATTR_SUPPRESSCROSSDOMAINCACHE) cli_dbgmsg(" * Suppress cross domain cache\n"); if(val & SWF_ATTR_ACTIONSCRIPT3) cli_dbgmsg(" * ActionScript 3.0\n"); if(val & SWF_ATTR_HASMETADATA) cli_dbgmsg(" * Has metadata\n"); if(val & SWF_ATTR_USEDIRECTBLIT) cli_dbgmsg(" * Use hardware acceleration\n"); if(val & SWF_ATTR_USEGPU) cli_dbgmsg(" * Use GPU\n"); break; default: offset += tag_len; continue; } } return CL_CLEAN; }
int cli_scancpio_newc(cli_ctx *ctx, int crc) { struct cpio_hdr_newc hdr_newc; char name[513], buff[9]; unsigned int file = 0, trailer = 0; uint32_t filesize, namesize, hdr_namesize, pad; int ret; off_t pos = 0; while(fmap_readn(*ctx->fmap, &hdr_newc, pos, sizeof(hdr_newc)) == sizeof(hdr_newc)) { pos += sizeof(hdr_newc); if(!hdr_newc.magic[0] && trailer) return CL_SUCCESS; if((!crc && strncmp(hdr_newc.magic, "070701", 6)) || (crc && strncmp(hdr_newc.magic, "070702", 6))) { cli_dbgmsg("cli_scancpio_newc: Invalid magic string\n"); return CL_EFORMAT; } cli_dbgmsg("CPIO: -- File %u --\n", ++file); strncpy(buff, hdr_newc.namesize, 8); buff[8] = 0; if(sscanf(buff, "%x", &hdr_namesize) != 1) { cli_dbgmsg("cli_scancpio_newc: Can't convert name size\n"); return CL_EFORMAT; } if(hdr_namesize) { namesize = MIN(sizeof(name), hdr_namesize); if ((uint32_t)fmap_readn(*ctx->fmap, &name, pos, namesize) != namesize) { cli_dbgmsg("cli_scancpio_newc: Can't read file name\n"); return CL_EFORMAT; } pos += namesize; name[namesize - 1] = 0; sanitname(name); cli_dbgmsg("CPIO: Name: %s\n", name); if(!strcmp(name, "TRAILER!!!")) trailer = 1; pad = (4 - (sizeof(hdr_newc) + hdr_namesize) % 4) % 4; if(namesize < hdr_namesize) { if(pad) hdr_namesize += pad; pos += hdr_namesize - namesize; } else if(pad) pos += pad; } strncpy(buff, hdr_newc.filesize, 8); buff[8] = 0; if(sscanf(buff, "%x", &filesize) != 1) { cli_dbgmsg("cli_scancpio_newc: Can't convert file size\n"); return CL_EFORMAT; } cli_dbgmsg("CPIO: Filesize: %u\n", filesize); if(!filesize) continue; if(cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) return CL_VIRUS; ret = cli_checklimits("cli_scancpio_newc", ctx, filesize, 0, 0); if(ret == CL_EMAXFILES) { return ret; } else if(ret == CL_SUCCESS) { ret = cli_map_scan(*ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY); if(ret == CL_VIRUS) return ret; } if((pad = filesize % 4)) filesize += (4 - pad); pos += filesize; } return CL_CLEAN; }
/* Read 64-bit program headers */ static int cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo, struct elf_file_hdr64 *file_hdr, uint8_t conv) { struct elf_program_hdr64 *program_hdr = NULL; uint16_t phnum, phentsize; uint64_t entry, fentry = 0, phoff; uint32_t i; uint8_t err; /* Program headers and Entry */ phnum = file_hdr->e_phnum; cli_dbgmsg("ELF: Number of program headers: %d\n", phnum); if(phnum > 128) { cli_dbgmsg("ELF: Suspicious number of program headers\n"); if(ctx && DETECT_BROKEN) { cli_append_virus(ctx, "Heuristics.Broken.Executable"); return CL_VIRUS; } return CL_EFORMAT; } entry = file_hdr->e_entry; if(phnum && entry) { phentsize = file_hdr->e_phentsize; /* Sanity check */ if (phentsize != sizeof(struct elf_program_hdr64)) { cli_dbgmsg("ELF: phentsize != sizeof(struct elf_program_hdr64)\n"); if(ctx && DETECT_BROKEN) { cli_append_virus(ctx, "Heuristics.Broken.Executable"); return CL_VIRUS; } return CL_EFORMAT; } phoff = file_hdr->e_phoff; if(ctx) { cli_dbgmsg("ELF: Program header table offset: " STDu64 "\n", phoff); } if(phnum) { program_hdr = (struct elf_program_hdr64 *) cli_calloc(phnum, sizeof(struct elf_program_hdr64)); if(!program_hdr) { cli_errmsg("ELF: Can't allocate memory for program headers\n"); return CL_EMEM; } if(ctx) { cli_dbgmsg("------------------------------------\n"); } } for(i = 0; i < phnum; i++) { err = 0; if(fmap_readn(map, &program_hdr[i], phoff, sizeof(struct elf_program_hdr64)) != sizeof(struct elf_program_hdr64)) err = 1; phoff += sizeof(struct elf_program_hdr64); if(err) { cli_dbgmsg("ELF: Can't read segment #%d\n", i); if(ctx) { cli_dbgmsg("ELF: Possibly broken ELF file\n"); } free(program_hdr); if(ctx && DETECT_BROKEN) { cli_append_virus(ctx, "Heuristics.Broken.Executable"); return CL_VIRUS; } return CL_BREAK; } if(ctx) { cli_dbgmsg("ELF: Segment #%d\n", i); cli_dbgmsg("ELF: Segment type: 0x%x\n", EC32(program_hdr[i].p_type, conv)); cli_dbgmsg("ELF: Segment offset: 0x" STDx64 "\n", EC64(program_hdr[i].p_offset, conv)); cli_dbgmsg("ELF: Segment virtual address: 0x" STDx64 "\n", EC64(program_hdr[i].p_vaddr, conv)); cli_dbgmsg("ELF: Segment real size: 0x" STDx64 "\n", EC64(program_hdr[i].p_filesz, conv)); cli_dbgmsg("ELF: Segment virtual size: 0x" STDx64 "\n", EC64(program_hdr[i].p_memsz, conv)); cli_dbgmsg("------------------------------------\n"); } } fentry = cli_rawaddr64(entry, program_hdr, phnum, conv, &err); free(program_hdr); if(err) { cli_dbgmsg("ELF: Can't calculate file offset of entry point\n"); if(ctx && DETECT_BROKEN) { cli_append_virus(ctx, "Heuristics.Broken.Executable"); return CL_VIRUS; } return CL_EFORMAT; } if(ctx) { cli_dbgmsg("ELF: Entry point address: 0x%.16" PRIx64 "\n", entry); cli_dbgmsg("ELF: Entry point offset: 0x%.16" PRIx64 " (" STDi64 ")\n", fentry, fentry); } } if(elfinfo) { elfinfo->ep = fentry; } return CL_CLEAN; }
static int tnef_message(fmap_t *map, off_t *pos, uint16_t type, uint16_t tag, int32_t length, off_t fsize) { uint16_t i16; off_t offset; #ifdef CL_DEBUG uint32_t i32; char *string; #endif cli_dbgmsg("message tag 0x%x, type 0x%x, length %d\n", tag, type, (int)length); offset = *pos; /* * a lot of this stuff should be only discovered in debug mode... */ switch(tag) { case attBODY: cli_warnmsg("TNEF body not being scanned - if you believe this file contains a virus, submit it to www.clamav.net\n"); break; #ifdef CL_DEBUG case attTNEFVERSION: /*assert(length == sizeof(uint32_t))*/ if(fmap_readn(map, &i32, *pos, sizeof(uint32_t)) != sizeof(uint32_t)) return -1; (*pos) += sizeof(uint32_t); i32 = host32(i32); cli_dbgmsg("TNEF version %d\n", i32); break; case attOEMCODEPAGE: /* 8 bytes, but just print the first 4 */ /*assert(length == sizeof(uint32_t))*/ if(fmap_readn(map, &i32, *pos, sizeof(uint32_t)) != sizeof(uint32_t)) return -1; (*pos) += sizeof(uint32_t); i32 = host32(i32); cli_dbgmsg("TNEF codepage %d\n", i32); break; case attDATEMODIFIED: /* 14 bytes, long */ break; case attMSGCLASS: if(length <= 0) return -1; string = cli_malloc(length + 1); if(string == NULL) { cli_errmsg("tnef_message: Unable to allocate memory for string\n"); return -1; } if(fmap_readn(map, string, *pos, (uint32_t)length) != (uint32_t)length) { free(string); return -1; } (*pos) += (uint32_t)length; string[length] = '\0'; cli_dbgmsg("TNEF class %s\n", string); free(string); break; default: cli_dbgmsg("TNEF - unsupported message tag 0x%x type 0x%d length %d\n", tag, type, length); break; #endif } /*cli_dbgmsg("%lu %lu\n", (long)(offset + length), ftell(fp));*/ if(!CLI_ISCONTAINED2(0, fsize, (off_t)offset, (off_t)length)) { cli_dbgmsg("TNEF: Incorrect length field in tnef_message\n"); return -1; } (*pos) = offset + length; /* Checksum - TODO, verify */ (*pos) += 2; return 0; }
/* Return converted endian-fixed header, or error code */ static int cli_elf_fileheader(cli_ctx *ctx, fmap_t *map, union elf_file_hdr *file_hdr, uint8_t *do_convert, uint8_t *is64) { uint8_t format64, conv; /* Load enough for smaller header first */ if(fmap_readn(map, file_hdr, 0, sizeof(struct elf_file_hdr32)) != sizeof(struct elf_file_hdr32)) { /* Not an ELF file? */ cli_dbgmsg("ELF: Can't read file header\n"); return CL_BREAK; } if(memcmp(file_hdr->hdr64.e_ident, "\x7f\x45\x4c\x46", 4)) { cli_dbgmsg("ELF: Not an ELF file\n"); return CL_BREAK; } switch(file_hdr->hdr64.e_ident[4]) { case 1: cli_dbgmsg("ELF: ELF class 1 (32-bit)\n"); format64 = 0; break; case 2: cli_dbgmsg("ELF: ELF class 2 (64-bit)\n"); format64 = 1; break; default: cli_dbgmsg("ELF: Unknown ELF class (%u)\n", file_hdr->hdr64.e_ident[4]); return CL_EFORMAT; } /* Need to know to endian convert */ if(file_hdr->hdr64.e_ident[5] == 1) { #if WORDS_BIGENDIAN == 0 if(ctx) cli_dbgmsg("ELF: File is little-endian - conversion not required\n"); conv = 0; #else if(ctx) cli_dbgmsg("ELF: File is little-endian - data conversion enabled\n"); conv = 1; #endif } else { #if WORDS_BIGENDIAN == 0 if(ctx) cli_dbgmsg("ELF: File is big-endian - data conversion enabled\n"); conv = 1; #else if(ctx) cli_dbgmsg("ELF: File is big-endian - conversion not required\n"); conv = 0; #endif } *do_convert = conv; *is64 = format64; /* Solve bit-size and conversion pronto */ file_hdr->hdr64.e_type = EC16(file_hdr->hdr64.e_type, conv); file_hdr->hdr64.e_machine = EC16(file_hdr->hdr64.e_machine, conv); file_hdr->hdr64.e_version = EC32(file_hdr->hdr64.e_version, conv); if(format64) { /* Read rest of 64-bit header */ if(fmap_readn(map, file_hdr->hdr32.pad, sizeof(struct elf_file_hdr32), ELF_HDR_SIZEDIFF) != ELF_HDR_SIZEDIFF) { /* Not an ELF file? */ cli_dbgmsg("ELF: Can't read file header\n"); return CL_BREAK; } /* Now endian convert, if needed */ if(conv) { file_hdr->hdr64.e_entry = EC64(file_hdr->hdr64.e_entry, conv); file_hdr->hdr64.e_phoff = EC64(file_hdr->hdr64.e_phoff, conv); file_hdr->hdr64.e_shoff = EC64(file_hdr->hdr64.e_shoff, conv); file_hdr->hdr64.e_flags = EC32(file_hdr->hdr64.e_flags, conv); file_hdr->hdr64.e_ehsize = EC16(file_hdr->hdr64.e_ehsize, conv); file_hdr->hdr64.e_phentsize = EC16(file_hdr->hdr64.e_phentsize, conv); file_hdr->hdr64.e_phnum = EC16(file_hdr->hdr64.e_phnum, conv); file_hdr->hdr64.e_shentsize = EC16(file_hdr->hdr64.e_shentsize, conv); file_hdr->hdr64.e_shnum = EC16(file_hdr->hdr64.e_shnum, conv); file_hdr->hdr64.e_shstrndx = EC16(file_hdr->hdr64.e_shstrndx, conv); } } else { /* Convert 32-bit structure, if needed */ if(conv) { file_hdr->hdr32.hdr.e_entry = EC32(file_hdr->hdr32.hdr.e_entry, conv); file_hdr->hdr32.hdr.e_phoff = EC32(file_hdr->hdr32.hdr.e_phoff, conv); file_hdr->hdr32.hdr.e_shoff = EC32(file_hdr->hdr32.hdr.e_shoff, conv); file_hdr->hdr32.hdr.e_flags = EC32(file_hdr->hdr32.hdr.e_flags, conv); file_hdr->hdr32.hdr.e_ehsize = EC16(file_hdr->hdr32.hdr.e_ehsize, conv); file_hdr->hdr32.hdr.e_phentsize = EC16(file_hdr->hdr32.hdr.e_phentsize, conv); file_hdr->hdr32.hdr.e_phnum = EC16(file_hdr->hdr32.hdr.e_phnum, conv); file_hdr->hdr32.hdr.e_shentsize = EC16(file_hdr->hdr32.hdr.e_shentsize, conv); file_hdr->hdr32.hdr.e_shnum = EC16(file_hdr->hdr32.hdr.e_shnum, conv); file_hdr->hdr32.hdr.e_shstrndx = EC16(file_hdr->hdr32.hdr.e_shstrndx, conv); } /* Wipe pad for safety */ memset(file_hdr->hdr32.pad, 0, ELF_HDR_SIZEDIFF); } return CL_CLEAN; }
static int tnef_attachment(fmap_t *map, off_t *pos, uint16_t type, uint16_t tag, int32_t length, const char *dir, fileblob **fbref, off_t fsize) { uint32_t todo; uint16_t i16; off_t offset; char *string; cli_dbgmsg("attachment tag 0x%x, type 0x%x, length %d\n", tag, type, (int)length); offset = *pos; switch(tag) { case attATTACHTITLE: if(length <= 0) return -1; string = cli_malloc(length + 1); if(string == NULL) { cli_errmsg("tnef_attachment: Unable to allocate memory for string\n"); return -1; } if(fmap_readn(map, string, *pos, (uint32_t)length) != (uint32_t)length) { free(string); return -1; } (*pos) += (uint32_t)length; string[length] = '\0'; cli_dbgmsg("TNEF filename %s\n", string); if(*fbref == NULL) { *fbref = fileblobCreate(); if(*fbref == NULL) { free(string); return -1; } } fileblobSetFilename(*fbref, dir, string); free(string); break; case attATTACHDATA: if(*fbref == NULL) { *fbref = fileblobCreate(); if(*fbref == NULL) return -1; } todo = length; while(todo) { unsigned char buf[BUFSIZ]; int32_t got = fmap_readn(map, buf, *pos, MIN(sizeof(buf), todo)); if (got <= 0) break; (*pos) += got; fileblobAddData(*fbref, buf, got); todo -= got; } break; default: cli_dbgmsg("TNEF - unsupported attachment tag 0x%x type 0x%d length %d\n", tag, type, (int)length); break; } /*cli_dbgmsg("%lu %lu\n", (long)(offset + length), ftell(fp));*/ if(!CLI_ISCONTAINED2(0, fsize, (off_t)offset, (off_t)length)) { cli_dbgmsg("TNEF: Incorrect length field in tnef_attachment\n"); return -1; } (*pos) = (long)(offset + length); /* shouldn't be needed */ (*pos) += 2; return 0; }