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 */ }
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 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_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; }
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; }
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; }
static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize) { struct gpt_partition_entry gpe; int ret = CL_CLEAN, detection = CL_CLEAN; size_t maplen, part_size = 0; off_t pos = 0, part_off = 0; unsigned i = 0, j = 0; uint32_t max_prtns = 0; /* convert endian to host */ hdr.signature = be64_to_host(hdr.signature); hdr.revision = be32_to_host(hdr.revision); hdr.headerSize = le32_to_host(hdr.headerSize); hdr.headerCRC32 = le32_to_host(hdr.headerCRC32); hdr.reserved = le32_to_host(hdr.reserved); hdr.currentLBA = le64_to_host(hdr.currentLBA); hdr.backupLBA = le64_to_host(hdr.backupLBA); hdr.firstUsableLBA = le64_to_host(hdr.firstUsableLBA); hdr.lastUsableLBA = le64_to_host(hdr.lastUsableLBA); hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA); hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries); hdr.tableEntrySize = le32_to_host(hdr.tableEntrySize); hdr.tableCRC32 = le32_to_host(hdr.tableCRC32); /* print header info for the debug */ cli_dbgmsg("GPT Header:\n"); cli_dbgmsg("Signature: 0x%llx\n", (long long unsigned)hdr.signature); cli_dbgmsg("Revision: %x\n", hdr.revision); gpt_printGUID(hdr.DiskGUID, "DISK GUID"); cli_dbgmsg("Partition Entry Count: %u\n", hdr.tableNumEntries); cli_dbgmsg("Partition Entry Size: %u\n", hdr.tableEntrySize); maplen = (*ctx->fmap)->real_len; /* check engine maxpartitions limit */ if (hdr.tableNumEntries < ctx->engine->maxpartitions) { max_prtns = hdr.tableNumEntries; } else { max_prtns = ctx->engine->maxpartitions; } /* use the partition tables to pass partitions to cli_map_scan */ pos = hdr.tableStartLBA * sectorsize; for (i = 0; i < max_prtns; ++i) { /* read in partition entry */ if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) { cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n"); return CL_EFORMAT; } /* convert the endian to host */ gpe.firstLBA = le64_to_host(gpe.firstLBA); gpe.lastLBA = le64_to_host(gpe.lastLBA); gpe.attributes = le64_to_host(gpe.attributes); for (j = 0; j < 36; ++j) { gpe.name[i] = le16_to_host(gpe.name[i]); } /* check that partition is not empty and within a valid location */ if (gpe.firstLBA == 0) { /* empty partition, invalid */ } else if ((gpe.firstLBA > gpe.lastLBA) || (gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) { cli_dbgmsg("cli_scangpt: GPT partition exists outside specified bounds\n"); gpt_parsemsg("%llu < %llu, %llu > %llu\n", gpe.firstLBA, hdr.firstUsableLBA, gpe.lastLBA, hdr.lastUsableLBA); /* partition exists outside bounds specified by header or invalid */ } else if (((gpe.lastLBA+1) * sectorsize) > maplen) { /* partition exists outside bounds of the file map */ } else { /* print partition entry data for debug */ cli_dbgmsg("GPT Partition Entry %u:\n", i); gpt_printName(gpe.name, "Name"); gpt_printGUID(gpe.typeGUID, "Type GUID"); gpt_printGUID(gpe.uniqueGUID, "Unique GUID"); cli_dbgmsg("Attributes: %llx\n", (long long unsigned)gpe.attributes); cli_dbgmsg("Blocks: [%llu(%llu) -> %llu(%llu)]\n", (long long unsigned)gpe.firstLBA, (long long unsigned)(gpe.firstLBA * sectorsize), (long long unsigned)gpe.lastLBA, (long long unsigned)((gpe.lastLBA+1) * sectorsize)); /* send the partition to cli_map_scan */ part_off = gpe.firstLBA * sectorsize; part_size = (gpe.lastLBA - gpe.firstLBA + 1) * sectorsize; ret = cli_map_scan(*ctx->fmap, part_off, part_size, ctx, CL_TYPE_PART_ANY); if (ret != CL_CLEAN) { if (SCAN_ALLMATCHES && (ret == CL_VIRUS)) detection = CL_VIRUS; else return ret; } } /* increment the offsets to next partition entry */ pos += hdr.tableEntrySize; } if (i >= ctx->engine->maxpartitions) { cli_dbgmsg("cli_scangpt: max partitions reached\n"); } return detection; }
int cli_scandmg(cli_ctx *ctx) { struct dmg_koly_block hdr; int ret, namelen, ofd; size_t maplen, nread; off_t pos = 0; char *dirname, *tmpfile; const char *outdata; unsigned int file = 0; struct dmg_mish_with_stripes *mish_list = NULL, *mish_list_tail = NULL; enum dmgReadState state = DMG_FIND_BASE_PLIST; int stateDepth[DMG_MAX_STATE]; #if HAVE_LIBXML2 xmlTextReaderPtr reader; #endif if (!ctx || !ctx->fmap) { cli_errmsg("cli_scandmg: Invalid context\n"); return CL_ENULLARG; } maplen = (*ctx->fmap)->real_len; pos = maplen - 512; if (pos <= 0) { cli_dbgmsg("cli_scandmg: Sizing problem for DMG archive.\n"); return CL_CLEAN; } /* Grab koly block */ if (fmap_readn(*ctx->fmap, &hdr, pos, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scandmg: Invalid DMG trailer block\n"); return CL_EFORMAT; } /* Check magic */ hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == 0x6b6f6c79) { cli_dbgmsg("cli_scandmg: Found koly block @ %ld\n", (long) pos); } else { cli_dbgmsg("cli_scandmg: No koly magic, %8x\n", hdr.magic); return CL_EFORMAT; } hdr.dataForkOffset = be64_to_host(hdr.dataForkOffset); hdr.dataForkLength = be64_to_host(hdr.dataForkLength); cli_dbgmsg("cli_scandmg: data offset %lu len %d\n", (unsigned long)hdr.dataForkOffset, (int)hdr.dataForkLength); hdr.xmlOffset = be64_to_host(hdr.xmlOffset); hdr.xmlLength = be64_to_host(hdr.xmlLength); if (hdr.xmlLength > (uint64_t)INT_MAX) { cli_dbgmsg("cli_scandmg: The embedded XML is way larger than necessary, and probably corrupt or tampered with.\n"); return CL_EFORMAT; } if ((hdr.xmlOffset > (uint64_t)maplen) || (hdr.xmlLength > (uint64_t)maplen) || (hdr.xmlOffset + hdr.xmlLength) > (uint64_t)maplen) { cli_dbgmsg("cli_scandmg: XML out of range for this file\n"); return CL_EFORMAT; } cli_dbgmsg("cli_scandmg: XML offset %lu len %d\n", (unsigned long)hdr.xmlOffset, (int)hdr.xmlLength); if (hdr.xmlLength == 0) { cli_dbgmsg("cli_scandmg: Embedded XML length is zero.\n"); return CL_EFORMAT; } /* Create temp folder for contents */ if (!(dirname = cli_gentemp(ctx->engine->tmpdir))) { return CL_ETMPDIR; } if (mkdir(dirname, 0700)) { cli_errmsg("cli_scandmg: Cannot create temporary directory %s\n", dirname); free(dirname); return CL_ETMPDIR; } cli_dbgmsg("cli_scandmg: Extracting into %s\n", dirname); /* Dump XML to tempfile, if needed */ if (ctx->engine->keeptmp && !(ctx->engine->engine_options & ENGINE_OPTIONS_FORCE_TO_DISK)) { int xret; xret = dmg_extract_xml(ctx, dirname, &hdr); if (xret != CL_SUCCESS) { /* Printed err detail inside dmg_extract_xml */ free(dirname); return xret; } } /* scan XML with cli_map_scandesc */ ret = cli_map_scan(*ctx->fmap, (off_t)hdr.xmlOffset, (size_t)hdr.xmlLength, ctx); if (ret != CL_CLEAN) { cli_dbgmsg("cli_scandmg: retcode from scanning TOC xml: %s\n", cl_strerror(ret)); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return ret; } /* page data from map */ outdata = fmap_need_off_once_len(*ctx->fmap, hdr.xmlOffset, hdr.xmlLength, &nread); if (!outdata || (nread != hdr.xmlLength)) { cli_errmsg("cli_scandmg: Failed getting XML from map, len %d\n", (int)hdr.xmlLength); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return CL_EMAP; } /* time to walk the tree */ /* plist -> dict -> (key:resource_fork) dict -> (key:blkx) array -> dict */ /* each of those bottom level dict should have 4 parts */ /* [ Attributes, Data, ID, Name ], where Data is Base64 mish block */ /* This is the block where we require libxml2 */ #if HAVE_LIBXML2 /* XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_COMPACT */ #define DMG_XML_PARSE_OPTS (1 << 1 | 1 << 11 | 1 << 16) reader = xmlReaderForMemory(outdata, (int)hdr.xmlLength, "toc.xml", NULL, DMG_XML_PARSE_OPTS); if (!reader) { cli_dbgmsg("cli_scandmg: Failed parsing XML!\n"); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return CL_EFORMAT; } stateDepth[DMG_FIND_BASE_PLIST] = -1; // May need to check for (xmlTextReaderIsEmptyElement(reader) == 0) /* Break loop if have return code or reader can't read any more */ while ((ret == CL_CLEAN) && (xmlTextReaderRead(reader) == 1)) { xmlReaderTypes nodeType; nodeType = xmlTextReaderNodeType(reader); if (nodeType == XML_READER_TYPE_ELEMENT) { // New element, do name check xmlChar *nodeName; int depth; depth = xmlTextReaderDepth(reader); if (depth < 0) { break; } if ((depth > 50) && SCAN_ALGO) { // Possible heuristic, should limit runaway cli_dbgmsg("cli_scandmg: Excessive nesting in DMG TOC.\n"); break; } nodeName = xmlTextReaderLocalName(reader); if (!nodeName) continue; dmg_parsemsg("read: name %s depth %d\n", nodeName, depth); if ((state == DMG_FIND_DATA_MISH) && (depth == stateDepth[state-1])) { xmlChar * textValue; struct dmg_mish_with_stripes *mish_set; /* Reset state early, for continue cases */ stateDepth[DMG_FIND_KEY_DATA] = -1; state--; if (xmlStrcmp(nodeName, "data") != 0) { cli_dbgmsg("cli_scandmg: Not blkx data element\n"); xmlFree(nodeName); continue; } dmg_parsemsg("read: Found blkx data element\n"); /* Pull out data content from text */ if (xmlTextReaderIsEmptyElement(reader)) { cli_dbgmsg("cli_scandmg: blkx data element is empty\n"); xmlFree(nodeName); continue; } if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Next node not text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { xmlFree(nodeName); continue; } /* Have encoded mish block */ mish_set = cli_malloc(sizeof(struct dmg_mish_with_stripes)); if (mish_set == NULL) { ret = CL_EMEM; xmlFree(textValue); xmlFree(nodeName); break; } ret = dmg_decode_mish(ctx, &file, textValue, mish_set); xmlFree(textValue); if (ret == CL_EFORMAT) { /* Didn't decode, or not a mish block */ ret = CL_CLEAN; xmlFree(nodeName); continue; } else if (ret != CL_CLEAN) { xmlFree(nodeName); continue; } /* Add mish block to list */ if (mish_list_tail != NULL) { mish_list_tail->next = mish_set; mish_list_tail = mish_set; } else { mish_list = mish_set; mish_list_tail = mish_set; } mish_list_tail->next = NULL; } if ((state == DMG_FIND_KEY_DATA) && (depth > stateDepth[state-1]) && (xmlStrcmp(nodeName, "key") == 0)) { xmlChar * textValue; dmg_parsemsg("read: Found key - checking for Data\n"); if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Key node no text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n"); xmlFree(nodeName); continue; } if (xmlStrcmp(textValue, "Data") == 0) { dmg_parsemsg("read: Matched data\n"); stateDepth[DMG_FIND_KEY_DATA] = depth; state++; } else { dmg_parsemsg("read: text value is %s\n", textValue); } xmlFree(textValue); } if ((state == DMG_FIND_BLKX_CONTAINER) && (depth == stateDepth[state-1])) { if (xmlStrcmp(nodeName, "array") == 0) { dmg_parsemsg("read: Found array blkx\n"); stateDepth[DMG_FIND_BLKX_CONTAINER] = depth; state++; } else if (xmlStrcmp(nodeName, "dict") == 0) { dmg_parsemsg("read: Found dict blkx\n"); stateDepth[DMG_FIND_BLKX_CONTAINER] = depth; state++; } else { cli_dbgmsg("cli_scandmg: Bad blkx, not container\n"); stateDepth[DMG_FIND_KEY_BLKX] = -1; state--; } } if ((state == DMG_FIND_KEY_BLKX) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "key") == 0)) { xmlChar * textValue; dmg_parsemsg("read: Found key - checking for blkx\n"); if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Key node no text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n"); xmlFree(nodeName); continue; } if (xmlStrcmp(textValue, "blkx") == 0) { cli_dbgmsg("cli_scandmg: Matched blkx\n"); stateDepth[DMG_FIND_KEY_BLKX] = depth; state++; } else { cli_dbgmsg("cli_scandmg: wanted blkx, text value is %s\n", textValue); } xmlFree(textValue); } if ((state == DMG_FIND_DICT_RESOURCE_FORK) && (depth == stateDepth[state-1])) { if (xmlStrcmp(nodeName, "dict") == 0) { dmg_parsemsg("read: Found resource-fork dict\n"); stateDepth[DMG_FIND_DICT_RESOURCE_FORK] = depth; state++; } else { dmg_parsemsg("read: Not resource-fork dict\n"); stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = -1; state--; } } if ((state == DMG_FIND_KEY_RESOURCE_FORK) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "key") == 0)) { dmg_parsemsg("read: Found resource-fork key\n"); stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = depth; state++; } if ((state == DMG_FIND_BASE_DICT) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "dict") == 0)) { dmg_parsemsg("read: Found dict start\n"); stateDepth[DMG_FIND_BASE_DICT] = depth; state++; } if ((state == DMG_FIND_BASE_PLIST) && (xmlStrcmp(nodeName, "plist") == 0)) { dmg_parsemsg("read: Found plist start\n"); stateDepth[DMG_FIND_BASE_PLIST] = depth; state++; } xmlFree(nodeName); } else if ((nodeType == XML_READER_TYPE_END_ELEMENT) && (state > DMG_FIND_BASE_PLIST)) { int significantEnd = 0; int depth = xmlTextReaderDepth(reader); if (depth < 0) { break; } else if (depth < stateDepth[state-1]) { significantEnd = 1; } else if ((depth == stateDepth[state-1]) && (state-1 == DMG_FIND_BLKX_CONTAINER)) { /* Special case, ending blkx container */ significantEnd = 1; } if (significantEnd) { dmg_parsemsg("read: significant end tag, state %d\n", state); stateDepth[state-1] = -1; state--; if ((state-1 == DMG_FIND_KEY_RESOURCE_FORK) || (state-1 == DMG_FIND_KEY_BLKX)) { /* Keys end their own tag (validly) and the next state depends on the following tag */ // cli_dbgmsg("read: significant end tag ending prior key state\n"); stateDepth[state-1] = -1; state--; } } else { dmg_parsemsg("read: not significant end tag, state %d depth %d prior depth %d\n", state, depth, stateDepth[state-1]); } } } xmlFreeTextReader(reader); #else cli_dbgmsg("cli_scandmg: libxml2 support is compiled out. It is required for full DMG support.\n"); #endif /* Loop over mish array */ file = 0; while ((ret == CL_CLEAN) && (mish_list != NULL)) { /* Handle & scan mish block */ ret = dmg_handle_mish(ctx, file++, dirname, hdr.xmlOffset, mish_list); free(mish_list->mish); mish_list_tail = mish_list; mish_list = mish_list->next; free(mish_list_tail); } /* Cleanup */ /* If error occurred, need to free mish items and mish blocks */ while (mish_list != NULL) { free(mish_list->mish); mish_list_tail = mish_list; mish_list = mish_list->next; free(mish_list_tail); } if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return ret; }