示例#1
0
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 */
}
示例#2
0
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;
}
示例#3
0
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;
}
示例#4
0
/* 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;
}
示例#5
0
文件: cpio.c 项目: Distrotech/clamav
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;
}
示例#6
0
文件: cpio.c 项目: Distrotech/clamav
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;
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
0
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;
}