/* 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 && SCAN_HEURISTIC_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 && SCAN_HEURISTIC_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 && SCAN_HEURISTIC_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" STDx32 "\n", (uint32_t) EC32(program_hdr[i].p_type, conv)); cli_dbgmsg("ELF: Segment offset: 0x" STDx64 "\n", (uint64_t) EC64(program_hdr[i].p_offset, conv)); cli_dbgmsg("ELF: Segment virtual address: 0x" STDx64 "\n", (uint64_t) EC64(program_hdr[i].p_vaddr, conv)); cli_dbgmsg("ELF: Segment real size: 0x" STDx64 "\n", (uint64_t) EC64(program_hdr[i].p_filesz, conv)); cli_dbgmsg("ELF: Segment virtual size: 0x" STDx64 "\n", (uint64_t) 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 && SCAN_HEURISTIC_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; }
/* 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 && SCAN_HEURISTIC_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 && SCAN_HEURISTIC_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 " STDu32 "\n", (uint32_t) i); cli_dbgmsg("ELF: Section offset: " STDu64 "\n", (uint64_t) EC64(section_hdr[i].sh_offset, conv)); cli_dbgmsg("ELF: Section size: " STDu64 "\n", (uint64_t) 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; }
static inline int matcher_run(const struct cli_matcher *root, const unsigned char *buffer, uint32_t length, const char **virname, struct cli_ac_data *mdata, uint32_t offset, const struct cli_target_info *tinfo, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, fmap_t *map, struct cli_bm_off *offdata, uint32_t *viroffset, cli_ctx *ctx) { int ret; int32_t pos = 0; struct filter_match_info info; uint32_t orig_length, orig_offset; const unsigned char* orig_buffer; unsigned int viruses_found = 0; if (root->filter) { if(filter_search_ext(root->filter, buffer, length, &info) == -1) { /* for safety always scan last maxpatlen bytes */ pos = length - root->maxpatlen - 1; if (pos < 0) pos = 0; PERF_LOG_FILTER(pos, length, root->type); } else { /* must not cut buffer for 64[4-4]6161, because we must be able to check * 64! */ pos = info.first_match - root->maxpatlen - 1; if (pos < 0) pos = 0; PERF_LOG_FILTER(pos, length, root->type); } } else { PERF_LOG_FILTER(0, length, root->type); } orig_length = length; orig_buffer = buffer; orig_offset = offset; length -= pos; buffer += pos; offset += pos; if (!root->ac_only) { PERF_LOG_TRIES(0, 1, length); if (root->bm_offmode) { /* Don't use prefiltering for BM offset mode, since BM keeps tracks * of offsets itself, and doesn't work if we skip chunks of input * data */ ret = cli_bm_scanbuff(orig_buffer, orig_length, virname, NULL, root, orig_offset, tinfo, offdata, viroffset); } else { ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata, viroffset); } if (ret == CL_VIRUS) { if (ctx) { cli_append_virus(ctx, *virname); if (SCAN_ALL) viruses_found++; else return ret; } } } PERF_LOG_TRIES(acmode, 0, length); ret = cli_ac_scanbuff(buffer, length, virname, NULL, acres, root, mdata, offset, ftype, ftoffset, acmode, ctx); if (ctx && !SCAN_ALL && ret == CL_VIRUS) cli_append_virus(ctx, *virname); if (ctx && SCAN_ALL && viruses_found) return CL_VIRUS; return ret; }
int cli_7unz (cli_ctx *ctx, size_t offset) { CFileInStream archiveStream; CLookToRead lookStream; CSzArEx db; SRes res; UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf; int namelen = UTFBUFSZ, found = CL_CLEAN; Int64 begin_of_archive = offset; UInt32 viruses_found = 0; /* Replacement for FileInStream_CreateVTable(&archiveStream); */ archiveStream.s.Read = FileInStream_fmap_Read; archiveStream.s.Seek = FileInStream_fmap_Seek; archiveStream.s.curpos = 0; archiveStream.file.fmap = *ctx->fmap; LookToRead_CreateVTable(&lookStream, False); if(archiveStream.s.Seek(&archiveStream.s, &begin_of_archive, SZ_SEEK_SET) != 0) return CL_CLEAN; lookStream.realStream = &archiveStream.s; LookToRead_Init(&lookStream); SzArEx_Init(&db); res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); if(res == SZ_OK) { UInt32 i, blockIndex = 0xFFFFFFFF; Byte *outBuffer = 0; size_t outBufferSize = 0; unsigned int encrypted = 0; for (i = 0; i < db.db.NumFiles; i++) { size_t offset = 0; size_t outSizeProcessed = 0; const CSzFileItem *f = db.db.Files + i; char *name; size_t j; int newnamelen, fd; if((found = cli_checklimits("7unz", ctx, 0, 0, 0))) break; if (f->IsDir) continue; if(cli_checklimits("7unz", ctx, f->Size, 0, 0)) continue; if (!db.FileNameOffsets) newnamelen = 0; /* no filename */ else { newnamelen = SzArEx_GetFileNameUtf16(&db, i, NULL); if (newnamelen > namelen) { if(namelen > UTFBUFSZ) free(utf16name); utf16name = cli_malloc(newnamelen*2); if(!utf16name) { found = CL_EMEM; break; } namelen = newnamelen; } SzArEx_GetFileNameUtf16(&db, i, utf16name); } name = (char *)utf16name; for(j=0; j<(size_t)newnamelen; j++) /* FIXME */ name[j] = utf16name[j]; name[j] = 0; cli_dbgmsg("cli_7unz: extracting %s\n", name); res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp); if(res == SZ_ERROR_ENCRYPTED) { encrypted = 1; if(DETECT_ENCRYPTED) { cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n"); cli_append_virus(ctx, "Heuristics.Encrypted.7Zip"); viruses_found++; if(!SCAN_ALL) { found = CL_VIRUS; break; } } } if(cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) { found = CL_VIRUS; viruses_found++; if (!SCAN_ALL) break; } if (res != SZ_OK) cli_dbgmsg("cli_unz: extraction failed with %d\n", res); else { if((found = cli_gentempfd(ctx->engine->tmpdir, &name, &fd))) break; cli_dbgmsg("cli_7unz: Saving to %s\n", name); if((size_t)cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed) found = CL_EWRITE; else if ((found = cli_magic_scandesc(fd, ctx)) == CL_VIRUS) viruses_found++; close(fd); if(!ctx->engine->keeptmp && cli_unlink(name)) found = CL_EUNLINK; free(name); if(found != CL_CLEAN) if (!(SCAN_ALL && found == CL_VIRUS)) break; } } IAlloc_Free(&allocImp, outBuffer); } SzArEx_Free(&db, &allocImp); if(namelen > UTFBUFSZ) free(utf16name); if (res == SZ_OK) cli_dbgmsg("cli_7unz: completed successfully\n"); else if (res == SZ_ERROR_UNSUPPORTED) cli_dbgmsg("cli_7unz: unsupported\n"); else if (res == SZ_ERROR_MEM) cli_dbgmsg("cli_7unz: oom\n"); else if (res == SZ_ERROR_CRC) cli_dbgmsg("cli_7unz: crc mismatch\n"); else cli_dbgmsg("cli_7unz: error %d\n", res); if (SCAN_ALL && viruses_found) return CL_VIRUS; return found; }
int cli_lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash) { unsigned int i, evalcnt; uint64_t evalids; fmap_t *map = *ctx->fmap; unsigned int viruses_found = 0; for(i = 0; i < root->ac_lsigs; i++) { evalcnt = 0; evalids = 0; cli_ac_chkmacro(root, acdata, i); if(cli_ac_chklsig(root->ac_lsigtable[i]->logic, root->ac_lsigtable[i]->logic + strlen(root->ac_lsigtable[i]->logic), acdata->lsigcnt[i], &evalcnt, &evalids, 0) == 1) { if(root->ac_lsigtable[i]->tdb.container && root->ac_lsigtable[i]->tdb.container[0] != ctx->container_type) continue; if(root->ac_lsigtable[i]->tdb.filesize && (root->ac_lsigtable[i]->tdb.filesize[0] > map->len || root->ac_lsigtable[i]->tdb.filesize[1] < map->len)) continue; if(root->ac_lsigtable[i]->tdb.ep || root->ac_lsigtable[i]->tdb.nos) { if(!target_info || target_info->status != 1) continue; if(root->ac_lsigtable[i]->tdb.ep && (root->ac_lsigtable[i]->tdb.ep[0] > target_info->exeinfo.ep || root->ac_lsigtable[i]->tdb.ep[1] < target_info->exeinfo.ep)) continue; if(root->ac_lsigtable[i]->tdb.nos && (root->ac_lsigtable[i]->tdb.nos[0] > target_info->exeinfo.nsections || root->ac_lsigtable[i]->tdb.nos[1] < target_info->exeinfo.nsections)) continue; } if(hash && root->ac_lsigtable[i]->tdb.handlertype) { if(memcmp(ctx->handlertype_hash, hash, 16)) { ctx->recursion++; memcpy(ctx->handlertype_hash, hash, 16); if(cli_magic_scandesc_type(ctx, root->ac_lsigtable[i]->tdb.handlertype[0]) == CL_VIRUS) { ctx->recursion--; if (SCAN_ALL) { viruses_found++; continue; } return CL_VIRUS; } ctx->recursion--; continue; } } if(root->ac_lsigtable[i]->tdb.icongrp1 || root->ac_lsigtable[i]->tdb.icongrp2) { if(!target_info || target_info->status != 1) continue; if(matchicon(ctx, &target_info->exeinfo, root->ac_lsigtable[i]->tdb.icongrp1, root->ac_lsigtable[i]->tdb.icongrp2) == CL_VIRUS) { if(!root->ac_lsigtable[i]->bc_idx) { cli_append_virus(ctx, root->ac_lsigtable[i]->virname); if (SCAN_ALL) { viruses_found++; continue; } return CL_VIRUS; } else if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) { if (SCAN_ALL) { viruses_found++; continue; } return CL_VIRUS; } } continue; } if(!root->ac_lsigtable[i]->bc_idx) { cli_append_virus(ctx, root->ac_lsigtable[i]->virname); if (SCAN_ALL) { viruses_found++; continue; } return CL_VIRUS; } if(cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, root->ac_lsigtable[i]->bc_idx, acdata->lsigcnt[i], acdata->lsigsuboff_first[i], map) == CL_VIRUS) { if (SCAN_ALL) { viruses_found++; continue; } return CL_VIRUS; } } } if (SCAN_ALL && viruses_found) return CL_VIRUS; return CL_CLEAN; }
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash) { const unsigned char *buff; int ret = CL_CLEAN, type = CL_CLEAN, bytes, compute_hash[CLI_HASH_AVAIL_TYPES]; unsigned int i = 0, bm_offmode = 0; uint32_t maxpatlen, offset = 0; struct cli_ac_data gdata, tdata; struct cli_bm_off toff; cli_md5_ctx md5ctx; SHA256_CTX sha256ctx; SHA1Context sha1ctx; unsigned char digest[CLI_HASH_AVAIL_TYPES][32]; struct cli_matcher *groot = NULL, *troot = NULL; struct cli_target_info info; fmap_t *map = *ctx->fmap; struct cli_matcher *hdb, *fp; const char *virname = NULL; uint32_t viroffset = 0; uint32_t viruses_found = 0; if(!ctx->engine) { cli_errmsg("cli_scandesc: engine == NULL\n"); return CL_ENULLARG; } if(!ftonly) groot = ctx->engine->root[0]; /* generic signatures */ if(ftype) { for(i = 1; i < CLI_MTARGETS; i++) { if(cli_mtargets[i].target == ftype) { troot = ctx->engine->root[i]; break; } } } if(ftonly) { if(!troot) return CL_CLEAN; maxpatlen = troot->maxpatlen; } else { if(troot) maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen); else maxpatlen = groot->maxpatlen; } targetinfo(&info, i, map); if(!ftonly) if((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) { if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } if(troot) { if((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(troot, &tdata, &info))) { if(!ftonly) cli_ac_freedata(&gdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } if(troot->bm_offmode) { if(map->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) { if((ret = cli_bm_initoff(troot, &toff, &info))) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } bm_offmode = 1; } } } hdb = ctx->engine->hm_hdb; fp = ctx->engine->hm_fp; if(!ftonly && hdb) { if(!refhash) { if(cli_hm_have_size(hdb, CLI_HASH_MD5, map->len) || cli_hm_have_size(fp, CLI_HASH_MD5, map->len)) { cli_md5_init(&md5ctx); compute_hash[CLI_HASH_MD5] = 1; } else compute_hash[CLI_HASH_MD5] = 0; } else { compute_hash[CLI_HASH_MD5] = 0; memcpy(digest[CLI_HASH_MD5], refhash, 16); } if(cli_hm_have_size(hdb, CLI_HASH_SHA1, map->len) || cli_hm_have_wild(hdb, CLI_HASH_SHA1) || cli_hm_have_size(fp, CLI_HASH_SHA1, map->len) || cli_hm_have_wild(fp, CLI_HASH_SHA1) ) { SHA1Init(&sha1ctx); compute_hash[CLI_HASH_SHA1] = 1; } else compute_hash[CLI_HASH_SHA1] = 0; if(cli_hm_have_size(hdb, CLI_HASH_SHA256, map->len) || cli_hm_have_wild(hdb, CLI_HASH_SHA256) || cli_hm_have_size(fp, CLI_HASH_SHA256, map->len) || cli_hm_have_wild(fp, CLI_HASH_SHA256)) { sha256_init(&sha256ctx); compute_hash[CLI_HASH_SHA256] = 1; } else compute_hash[CLI_HASH_SHA256] = 0; } while(offset < map->len) { bytes = MIN(map->len - offset, SCANBUFF); if(!(buff = fmap_need_off_once(map, offset, bytes))) break; if(ctx->scanned) *ctx->scanned += bytes / CL_COUNT_PRECISION; if (ctx->engine->cb_progress && map->handle_is_fd && !ctx->engine->cb_progress((ssize_t) map->handle, bytes, ctx->engine->cb_progress_ctx)) return CL_BREAK; if(troot) { virname = NULL; viroffset = 0; ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, acres, map, bm_offmode ? &toff : NULL, &viroffset, ctx); if (virname) { /* virname already appended by matcher_run */ viruses_found = 1; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } } if(!ftonly) { virname = NULL; viroffset = 0; ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, acres, map, NULL, &viroffset, ctx); if (virname) { /* virname already appended by matcher_run */ viruses_found = 1; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { cli_ac_freedata(&gdata); if(troot) { cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } else if((acmode & AC_SCAN_FT) && ret >= CL_TYPENO) { if(ret > type) type = ret; } if(hdb && !SCAN_ALL) { const void *data = buff + maxpatlen * (offset!=0); uint32_t data_len = bytes - maxpatlen * (offset!=0); if(compute_hash[CLI_HASH_MD5]) cli_md5_update(&md5ctx, data, data_len); if(compute_hash[CLI_HASH_SHA1]) SHA1Update(&sha1ctx, data, data_len); if(compute_hash[CLI_HASH_SHA256]) sha256_update(&sha256ctx, data, data_len); } } if(SCAN_ALL && viroffset) { offset = viroffset; continue; } if(bytes < SCANBUFF) break; offset += bytes - maxpatlen; } if(!ftonly && hdb) { enum CLI_HASH_TYPE hashtype, hashtype2; if(compute_hash[CLI_HASH_MD5]) cli_md5_final(digest[CLI_HASH_MD5], &md5ctx); if(refhash) compute_hash[CLI_HASH_MD5] = 1; if(compute_hash[CLI_HASH_SHA1]) SHA1Final(&sha1ctx, digest[CLI_HASH_SHA1]); if(compute_hash[CLI_HASH_SHA256]) sha256_final(&sha256ctx, digest[CLI_HASH_SHA256]); virname = NULL; for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) { const char * virname_w = NULL; int found = 0; /* If no hash, skip to next type */ if(!compute_hash[hashtype]) continue; /* Do hash scan */ if((ret = cli_hm_scan(digest[hashtype], map->len, &virname, hdb, hashtype)) == CL_VIRUS) { found += 1; } if(!found || SCAN_ALL) { if ((ret = cli_hm_scan_wild(digest[hashtype], &virname_w, hdb, hashtype)) == CL_VIRUS) found += 2; } /* If found, do immediate hash-only FP check */ if (found && fp) { for(hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) { if(!compute_hash[hashtype2]) continue; if(cli_hm_scan(digest[hashtype2], map->len, NULL, fp, hashtype2) == CL_VIRUS) { found = 0; ret = CL_CLEAN; break; } else if(cli_hm_scan_wild(digest[hashtype2], NULL, fp, hashtype2) == CL_VIRUS) { found = 0; ret = CL_CLEAN; break; } } } /* If matched size-based hash ... */ if (found % 2) { viruses_found = 1; cli_append_virus(ctx, virname); if (!SCAN_ALL) break; virname = NULL; } /* If matched size-agnostic hash ... */ if (found > 1) { viruses_found = 1; cli_append_virus(ctx, virname_w); if (!SCAN_ALL) break; } } } if(troot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, troot, &tdata, &info, refhash); if (ret == CL_VIRUS) viruses_found++; cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(groot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, groot, &gdata, &info, refhash); cli_ac_freedata(&gdata); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); if (SCAN_ALL && viruses_found) return CL_VIRUS; if(ret == CL_VIRUS) return CL_VIRUS; return (acmode & AC_SCAN_FT) ? type : CL_CLEAN; }
static inline int matcher_run(const struct cli_matcher *root, const unsigned char *buffer, uint32_t length, const char **virname, struct cli_ac_data *mdata, uint32_t offset, const struct cli_target_info *tinfo, cli_file_t ftype, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, fmap_t *map, struct cli_bm_off *offdata, uint32_t *viroffset, cli_ctx *ctx) { cli_infomsg(NULL,"DEBUG: in matcher_run\n");//CHR int ret; int32_t pos = 0; struct filter_match_info info; uint32_t orig_length, orig_offset; const unsigned char* orig_buffer; unsigned int viruses_found = 0; if (root->filter) { cli_infomsg(NULL,"DEBUG: has filter\n");//CHR // CHR function filter_search_ext will do pre-matchon prefix for a given pattern if(filter_search_ext(root->filter, buffer, length, &info) == -1) { cli_infomsg(NULL,"DEBUG: filter and in if\n");//CHR /* for safety always scan last maxpatlen bytes */ pos = length - root->maxpatlen - 1; if (pos < 0) pos = 0; PERF_LOG_FILTER(pos, length, root->type); } else { // CHR FSM match cli_infomsg(NULL,"DEBUG: filter and in else\n");//CHR /* must not cut buffer for 64[4-4]6161, because we must be able to check * 64! */ //CHR if first_match is far more beyond max pattern length //CHR we need to move forward a little bit to speed up the following scan pos = info.first_match - root->maxpatlen - 1; cli_infomsg(NULL,"DEBUG: pos=%d\n",pos);//CHR if (pos < 0) pos = 0; PERF_LOG_FILTER(pos, length, root->type); } } else { PERF_LOG_FILTER(0, length, root->type); } orig_length = length; orig_buffer = buffer; orig_offset = offset; //CHR pos is 0 in this case length -= pos; buffer += pos; offset += pos; if (!root->ac_only) { cli_infomsg(NULL,"DEBUG: !ac_only, bm scan first\n");//CHR PERF_LOG_TRIES(0, 1, length); if (root->bm_offmode) { /* Don't use prefiltering for BM offset mode, since BM keeps tracks * of offsets itself, and doesn't work if we skip chunks of input * data */ cli_infomsg(NULL,"DEBUG: scan in bm_offmode=1 mode\n"); //CHR ret = cli_bm_scanbuff(orig_buffer, orig_length, virname, NULL, root, orig_offset, tinfo, offdata, viroffset); } else { cli_infomsg(NULL,"DEBUG: scan in bm_offmode=0 mode\n"); //CHR cli_infomsg(NULL,"DEBUG: pass into cli_bm_scanbuff with length=%d,offset=%d\n",length,offset);//CHR ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata, viroffset); } if (ret == CL_VIRUS) { cli_infomsg(NULL,"DEBUG: bm scan find virus, won't do ac scan\n");//CHR if (ctx) { cli_append_virus(ctx, *virname); if (SCAN_ALL) viruses_found++; else return ret; } } } PERF_LOG_TRIES(acmode, 0, length); cli_infomsg(NULL,"DEBUG: ac scan\n");//CHR ret = cli_ac_scanbuff(buffer, length, virname, NULL, acres, root, mdata, offset, ftype, ftoffset, acmode, NULL); if (ctx && ret == CL_VIRUS) cli_append_virus(ctx, *virname); if (ctx && SCAN_ALL && viruses_found) return CL_VIRUS; return ret; }
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; }
/* 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; }
int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash) { //CHR ftype => type == CL_TYPE_TEXT_ASCII ? 0 : type //CHR ftonly => 0 cli_infomsg(NULL,"DEBUG: in cli_fmap_scandesc\n");//CHR const unsigned char *buff; int ret = CL_CLEAN, type = CL_CLEAN, bytes, compute_hash[CLI_HASH_AVAIL_TYPES]; unsigned int i = 0, bm_offmode = 0; uint32_t maxpatlen, offset = 0; struct cli_ac_data gdata, tdata; struct cli_bm_off toff; cli_md5_ctx md5ctx; SHA256_CTX sha256ctx; SHA1Context sha1ctx; unsigned char digest[CLI_HASH_AVAIL_TYPES][32]; struct cli_matcher *groot = NULL, *troot = NULL; struct cli_target_info info; fmap_t *map = *ctx->fmap; struct cli_matcher *hdb, *fp; const char *virname = NULL; uint32_t viroffset = 0; uint32_t viruses_found = 0; if(!ctx->engine) { cli_errmsg("cli_scandesc: engine == NULL\n"); return CL_ENULLARG; } cli_infomsg(NULL,"DEBUG: ftype=%d, ftonly=%d(using generic signatures for groot)\n",ftype,ftonly);//CHR if(!ftonly) groot = ctx->engine->root[0]; /* generic signatures */ //CHR in test.txt case, ftype=0, troot is for sepcific types: from 1 to CLI_MTARGETS if(ftype) { for(i = 1; i < CLI_MTARGETS; i++) { if(cli_mtargets[i].target == ftype) { troot = ctx->engine->root[i]; break; } } } if(ftonly) { if(!troot) return CL_CLEAN; maxpatlen = troot->maxpatlen; } else { if(troot) maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen); else{ maxpatlen = groot->maxpatlen; cli_infomsg(NULL,"DEBUG: no troot, maxpatlen using groot->maxpatlen=%d\n",groot->maxpatlen);//CHR } } targetinfo(&info, i, map); if(!ftonly) // CHR init AC scan structure? if((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) { if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } // not run in this case test.txt if(troot) { if((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(troot, &tdata, &info))) { if(!ftonly) cli_ac_freedata(&gdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } if(troot->bm_offmode) {// CHR offset mode if(map->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) { if((ret = cli_bm_initoff(troot, &toff, &info))) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } bm_offmode = 1; } } } hdb = ctx->engine->hm_hdb; fp = ctx->engine->hm_fp; // hdb related if(!ftonly && hdb) { //CHR if have hdb should init mn5 contex if(!refhash) { if(cli_hm_have_size(hdb, CLI_HASH_MD5, map->len) || cli_hm_have_size(fp, CLI_HASH_MD5, map->len)) { cli_md5_init(&md5ctx); compute_hash[CLI_HASH_MD5] = 1; } else compute_hash[CLI_HASH_MD5] = 0; } else { compute_hash[CLI_HASH_MD5] = 0; memcpy(digest[CLI_HASH_MD5], refhash, 16); } if(cli_hm_have_size(hdb, CLI_HASH_SHA1, map->len) || cli_hm_have_size(fp, CLI_HASH_SHA1, map->len)) { SHA1Init(&sha1ctx); compute_hash[CLI_HASH_SHA1] = 1; } else compute_hash[CLI_HASH_SHA1] = 0; if(cli_hm_have_size(hdb, CLI_HASH_SHA256, map->len) || cli_hm_have_size(fp, CLI_HASH_SHA256, map->len)) { sha256_init(&sha256ctx); compute_hash[CLI_HASH_SHA256] = 1; } else compute_hash[CLI_HASH_SHA256] = 0; } while(offset < map->len) { bytes = MIN(map->len - offset, SCANBUFF); if(!(buff = fmap_need_off_once(map, offset, bytes))) break; if(ctx->scanned) *ctx->scanned += bytes / CL_COUNT_PRECISION; // CHR not troot for this case if(troot) { cli_infomsg(NULL,"DEBUG: not troot, should not show this msg\n");//CHR virname = NULL; viroffset = 0; // CHR run match ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, acres, map, bm_offmode ? &toff : NULL, &viroffset, ctx); if (virname) { viruses_found++; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { if(!ftonly) cli_ac_freedata(&gdata); cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } } if(!ftonly) { cli_infomsg(NULL,"DEBUG: !ftonly=true, will run matcher_run with groot with generic sigs\n");//CHR virname = NULL; viroffset = 0; ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, acres, map, NULL, &viroffset, ctx); if (virname) { viruses_found++; } if((ret == CL_VIRUS && !SCAN_ALL) || ret == CL_EMEM) { cli_ac_freedata(&gdata); if(troot) { cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); return ret; } else if((acmode & AC_SCAN_FT) && ret >= CL_TYPENO) { if(ret > type) type = ret; } if(hdb && !SCAN_ALL) { const void *data = buff + maxpatlen * (offset!=0); uint32_t data_len = bytes - maxpatlen * (offset!=0); if(compute_hash[CLI_HASH_MD5]) cli_md5_update(&md5ctx, data, data_len); if(compute_hash[CLI_HASH_SHA1]) SHA1Update(&sha1ctx, data, data_len); if(compute_hash[CLI_HASH_SHA256]) sha256_update(&sha256ctx, data, data_len); } } if(SCAN_ALL && viroffset) { offset = viroffset; continue; } if(bytes < SCANBUFF) break; offset += bytes - maxpatlen; } if(!ftonly && hdb) { enum CLI_HASH_TYPE hashtype, hashtype2; if(compute_hash[CLI_HASH_MD5]) cli_md5_final(digest[CLI_HASH_MD5], &md5ctx); if(refhash) compute_hash[CLI_HASH_MD5] = 1; if(compute_hash[CLI_HASH_SHA1]) SHA1Final(&sha1ctx, digest[CLI_HASH_SHA1]); if(compute_hash[CLI_HASH_SHA256]) sha256_final(&sha256ctx, digest[CLI_HASH_SHA256]); virname = NULL; for(hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) { if(compute_hash[hashtype] && (ret = cli_hm_scan(digest[hashtype], map->len, &virname, hdb, hashtype)) == CL_VIRUS) { if(fp) { for(hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) { if(compute_hash[hashtype2] && cli_hm_scan(digest[hashtype2], map->len, NULL, fp, hashtype2) == CL_VIRUS) { ret = CL_CLEAN; break; } } } if (ret == CL_VIRUS) { viruses_found++; cli_append_virus(ctx, virname); if (!SCAN_ALL) break; } virname = NULL; } } } if(troot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, troot, &tdata, &info, refhash); if (ret == CL_VIRUS) viruses_found++; cli_ac_freedata(&tdata); if(bm_offmode) cli_bm_freeoff(&toff); } if(groot) { if(ret != CL_VIRUS || SCAN_ALL) ret = cli_lsig_eval(ctx, groot, &gdata, &info, refhash); cli_ac_freedata(&gdata); } if(info.exeinfo.section) free(info.exeinfo.section); cli_hashset_destroy(&info.exeinfo.vinfo); if (SCAN_ALL && viruses_found) return CL_VIRUS; if(ret == CL_VIRUS) return CL_VIRUS; return (acmode & AC_SCAN_FT) ? type : CL_CLEAN; }
/* this includes the overall bounds of extended partitions */ static int mbr_primary_prtn_intxn(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize) { prtn_intxn_list_t prtncheck; unsigned i = 0, pitxn = 0, prtncount = 0; int ret = CL_CLEAN, tmp = CL_CLEAN; prtn_intxn_list_init(&prtncheck); for (i = 0; i < MBR_MAX_PARTITION_ENTRIES && prtncount < ctx->engine->maxpartitions; ++i) { if (mbr.entries[i].type == MBR_EMPTY) { /* empty partiton entry */ prtncount++; } else { tmp = prtn_intxn_list_check(&prtncheck, &pitxn, mbr.entries[i].firstLBA, mbr.entries[i].numLBA); if (tmp != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) { cli_dbgmsg("cli_scanmbr: 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_scanmbr: 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; } } if (mbr.entries[i].type == MBR_EXTENDED) { /* check the logical partitions */ tmp = mbr_extended_prtn_intxn(ctx, &prtncount, mbr.entries[i].firstLBA, sectorsize); if (tmp != CL_CLEAN) { if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) { ret = tmp; tmp = 0; } else if (tmp == CL_VIRUS) { prtn_intxn_list_free(&prtncheck); return CL_VIRUS; } else { prtn_intxn_list_free(&prtncheck); return tmp; } } } else { prtncount++; } } } prtn_intxn_list_free(&prtncheck); return ret; }
static unsigned int lhdr(fmap_t *map, uint32_t loff,uint32_t zsize, unsigned int *fu, unsigned int fc, const uint8_t *ch, int *ret, cli_ctx *ctx, char *tmpd, int detect_encrypted, zip_cb zcb) { const uint8_t *lh, *zip; char name[256]; uint32_t csize, usize; if(!(lh = fmap_need_off(map, loff, SIZEOF_LH))) { cli_dbgmsg("cli_unzip: lh - out of file\n"); return 0; } if(LH_magic != 0x04034b50) { if (!ch) cli_dbgmsg("cli_unzip: lh - wrkcomplete\n"); else cli_dbgmsg("cli_unzip: lh - bad magic\n"); fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } zip = lh + SIZEOF_LH; zsize-=SIZEOF_LH; if(zsize<=LH_flen) { cli_dbgmsg("cli_unzip: lh - fname out of file\n"); fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } if(ctx->engine->cdb || cli_debug_flag) { uint32_t nsize = (LH_flen>=sizeof(name))?sizeof(name)-1:LH_flen; const char *src; if(nsize && (src = fmap_need_ptr_once(map, zip, nsize))) { memcpy(name, zip, nsize); name[nsize]='\0'; } else name[0] = '\0'; } zip+=LH_flen; zsize-=LH_flen; cli_dbgmsg("cli_unzip: lh - ZMDNAME:%d:%s:%u:%u:%x:%u:%u:%u\n", ((LH_flags & F_ENCR)!=0), name, LH_usize, LH_csize, LH_crc32, LH_method, fc, ctx->recursion); /* ZMDfmt virname:encrypted(0-1):filename(exact|*):usize(exact|*):csize(exact|*):crc32(exact|*):method(exact|*):fileno(exact|*):maxdepth(exact|*) */ if(cli_matchmeta(ctx, name, LH_csize, LH_usize, (LH_flags & F_ENCR)!=0, fc, LH_crc32, NULL) == CL_VIRUS) { *ret = CL_VIRUS; return 0; } if(LH_flags & F_MSKED) { cli_dbgmsg("cli_unzip: lh - header has got unusable masked data\n"); /* FIXME: need to find/craft a sample */ fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } if(detect_encrypted && (LH_flags & F_ENCR) && DETECT_ENCRYPTED) { cli_dbgmsg("cli_unzip: Encrypted files found in archive.\n"); cli_append_virus(ctx, "Heuristics.Encrypted.Zip"); *ret = CL_VIRUS; fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } if(LH_flags & F_USEDD) { cli_dbgmsg("cli_unzip: lh - has data desc\n"); if(!ch) { fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } else { usize = CH_usize; csize = CH_csize; } } else { usize = LH_usize; csize = LH_csize; } if(zsize<=LH_elen) { cli_dbgmsg("cli_unzip: lh - extra out of file\n"); fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } zip+=LH_elen; zsize-=LH_elen; if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */ cli_dbgmsg("cli_unzip: lh - skipping empty file\n"); } else { if(zsize<csize) { cli_dbgmsg("cli_unzip: lh - stream out of file\n"); fmap_unneed_off(map, loff, SIZEOF_LH); return 0; } if(LH_flags & F_ENCR) { cli_dbgmsg("cli_unzip: lh - skipping encrypted file\n"); } else { if(fmap_need_ptr_once(map, zip, csize)) *ret = unz(zip, csize, usize, LH_method, LH_flags, fu, ctx, tmpd, zcb); } zip+=csize; zsize-=csize; } fmap_unneed_off(map, loff, SIZEOF_LH); /* unneed now. block is guaranteed to exists till the next need */ if(LH_flags & F_USEDD) { if(zsize<12) { cli_dbgmsg("cli_unzip: lh - data desc out of file\n"); return 0; } zsize-=12; if(fmap_need_ptr_once(map, zip, 4)) { if(cli_readint32(zip)==0x08074b50) { if(zsize<4) { cli_dbgmsg("cli_unzip: lh - data desc out of file\n"); return 0; } zip+=4; } } zip+=12; } return zip-lh; }
int cli_pcre_scanbuf(const unsigned char *buffer, uint32_t length, const char **virname, struct cli_ac_result **res, const struct cli_matcher *root, struct cli_ac_data *mdata, const struct cli_pcre_off *data, cli_ctx *ctx) { struct cli_pcre_meta **metatable = root->pcre_metatable, *pm = NULL; struct cli_pcre_data *pd; struct cli_ac_result *newres; uint32_t adjbuffer, adjshift, adjlength; unsigned int i, evalcnt = 0; uint64_t maxfilesize, evalids = 0; uint32_t global, encompass, rolling; int rc, lrc, offset, options=0, ovector[OVECCOUNT]; uint8_t viruses_found = 0; if ((!root->pcre_metatable) || (ctx && ctx->dconf && !(ctx->dconf->pcre & PCRE_CONF_SUPPORT))) { return CL_SUCCESS; } /* NOTE: moved pcre maxfilesize limit check to caller [matcher_run] */ for (i = 0; i < root->pcre_metas; ++i) { pm = root->pcre_metatable[i]; pd = &(pm->pdata); /* skip checking and running disabled pcres */ if (pm->flags & CLI_PCRE_DISABLED) { cli_dbgmsg("cli_pcre_scanbuf: skipping disabled regex /%s/\n", pd->expression); continue; } /* skip checking and running CLI_OFF_NONE pcres */ if (data && data->offset[i] == CLI_OFF_NONE) { pm_dbgmsg("cli_pcre_scanbuf: skipping CLI_OFF_NONE regex /%s/\n", pd->expression); continue; } /* evaluate trigger */ if (pm->lsigid[0]) { cli_dbgmsg("cli_pcre_scanbuf: checking %s; running regex /%s/\n", pm->trigger, pd->expression); #ifdef PCRE_BYPASS if (strcmp(pm->trigger, PCRE_BYPASS)) #endif if (cli_ac_chklsig(pm->trigger, pm->trigger + strlen(pm->trigger), mdata->lsigcnt[pm->lsigid[1]], &evalcnt, &evalids, 0) != 1) continue; } else { cli_dbgmsg("cli_pcre_scanbuf: skipping %s check due to unintialized lsigid\n", pm->trigger); /* fall-through to unconditional execution - sigtool-only */ } global = (pm->flags & CLI_PCRE_GLOBAL); /* globally search for all matches (within bounds) */ encompass = (pm->flags & CLI_PCRE_ENCOMPASS); /* encompass search to offset->offset+maxshift */ rolling = (pm->flags & CLI_PCRE_ROLLING); /* rolling search (unanchored) */ offset = pd->search_offset; /* this is usually 0 */ cli_dbgmsg("cli_pcre_scanbuf: triggered %s; running regex /%s/%s%s\n", pm->trigger, pd->expression, global ? " (global)":"", rolling ? " (rolling)":""); /* adjust the buffer sent to cli_pcre_match for offset and maxshift */ if (!data) { if (cli_pcre_qoff(pm, length, &adjbuffer, &adjshift) != CL_SUCCESS) continue; } else { adjbuffer = data->offset[i]; adjshift = data->shift[i]; } /* check for need to anchoring */ if (!rolling && !adjshift && (adjbuffer != CLI_OFF_ANY)) options |= PCRE_ANCHORED; else options = 0; if (adjbuffer == CLI_OFF_ANY) adjbuffer = 0; /* check the offset bounds */ if (adjbuffer < length) { /* handle encompass flag */ if (encompass && adjshift != 0 && adjshift != CLI_OFF_NONE) { if (adjbuffer+adjshift > length) adjlength = length - adjbuffer; else adjlength = adjshift; } else { /* NOTE - if using non-encompass method 2, alter shift universally */ /* TODO - limitations on non-encompassed buffers? */ adjlength = length - adjbuffer; } } else { /* starting offset is outside bounds of file, skip pcre execution silently */ pm_dbgmsg("cli_pcre_scanbuf: starting offset is outside bounds of file %u >= %u\n", adjbuffer, length); continue; } pm_dbgmsg("cli_pcre_scanbuf: passed buffer adjusted to %u +%u(%u)[%u]%s\n", adjbuffer, adjlength, adjbuffer+adjlength, adjshift, encompass ? " (encompass)":""); /* if the global flag is set, loop through the scanning */ do { /* performance metrics */ cli_event_time_start(p_sigevents, pm->sigtime_id); rc = cli_pcre_match(pd, buffer+adjbuffer, adjlength, offset, options, ovector, OVECCOUNT); cli_event_time_stop(p_sigevents, pm->sigtime_id); /* if debug, generate a match report */ if (cli_debug_flag) cli_pcre_report(pd, buffer+adjbuffer, adjlength, rc, ovector, OVECCOUNT); /* matched, rc shouldn't be >0 unless a full match occurs */ if (rc > 0) { cli_dbgmsg("cli_pcre_scanbuf: located regex match @ %d\n", adjbuffer+ovector[0]); /* check if we've gone over offset+shift */ if (!encompass && adjshift) { if (ovector[0] > adjshift) { /* ignore matched offset (outside of maxshift) */ cli_dbgmsg("cli_pcre_scanbuf: match found outside of maxshift @%u\n", adjbuffer+ovector[0]); break; } } /* track the detection count */ cli_event_count(p_sigevents, pm->sigmatch_id); /* for logical signature evaluation */ if (pm->lsigid[0]) { pm_dbgmsg("cli_pcre_scanbuf: assigning lsigcnt[%d][%d], located @ %d\n", pm->lsigid[1], pm->lsigid[2], adjbuffer+ovector[0]); lrc = lsig_sub_matched(root, mdata, pm->lsigid[1], pm->lsigid[2], adjbuffer+ovector[0], 0); if (lrc != CL_SUCCESS) return lrc; } else { /* for raw match data - sigtool only */ if(res) { newres = (struct cli_ac_result *)cli_calloc(1, sizeof(struct cli_ac_result)); if(!newres) { cli_errmsg("cli_pcre_scanbuff: Can't allocate memory for new result\n"); return CL_EMEM; } newres->virname = pm->virname; newres->customdata = NULL; /* get value? */ newres->next = *res; newres->offset = adjbuffer+ovector[0]; *res = newres; } else { if (ctx && SCAN_ALL) { viruses_found = 1; cli_append_virus(ctx, (const char *)pm->virname); } if (virname) *virname = pm->virname; if (!ctx || !SCAN_ALL) return CL_VIRUS; } } } /* move off to the end of the match for next match; offset is relative to adjbuffer * NOTE: misses matches starting within the last match; TODO: start from start of last match? */ offset = ovector[1]; /* clear the ovector results (they fall through the pcre_match) */ memset(ovector, 0, sizeof(ovector)); } while (global && rc > 0 && offset < adjlength); /* handle error codes */ if (rc < 0 && rc != PCRE_ERROR_NOMATCH) { switch (rc) { case PCRE_ERROR_CALLOUT: break; case PCRE_ERROR_NOMEMORY: cli_errmsg("cli_pcre_scanbuf: cli_pcre_match: pcre_exec: out of memory\n"); return CL_EMEM; case PCRE_ERROR_MATCHLIMIT: cli_dbgmsg("cli_pcre_scanbuf: cli_pcre_match: pcre_exec: match limit exceeded\n"); break; case PCRE_ERROR_RECURSIONLIMIT: cli_dbgmsg("cli_pcre_scanbuf: cli_pcre_match: pcre_exec: recursive limit exceeded\n"); break; default: cli_errmsg("cli_pcre_scanbuf: cli_pcre_match: pcre_exec: returned error %d\n", rc); return CL_BREAK; } } } if (viruses_found) return CL_VIRUS; return CL_SUCCESS; }
static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize) { prtn_intxn_list_t prtncheck; struct gpt_partition_entry gpe; unsigned i, pitxn; int ret = CL_CLEAN, tmp = CL_CLEAN; off_t pos; size_t maplen; uint32_t max_prtns = 0; int virus_found = 0; maplen = (*ctx->fmap)->real_len; /* convert endian to host to check partition table */ hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA); hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries); prtn_intxn_list_init(&prtncheck); /* check engine maxpartitions limit */ if (hdr.tableNumEntries < ctx->engine->maxpartitions) { max_prtns = hdr.tableNumEntries; } else { max_prtns = ctx->engine->maxpartitions; } 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"); prtn_intxn_list_free(&prtncheck); return CL_EFORMAT; } /* convert the endian to host */ gpe.firstLBA = le64_to_host(gpe.firstLBA); gpe.lastLBA = le64_to_host(gpe.lastLBA); if (gpe.firstLBA == 0) { /* empty partition, invalid */ } else if ((gpe.firstLBA > gpe.lastLBA) || (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 { tmp = prtn_intxn_list_check(&prtncheck, &pitxn, gpe.firstLBA, gpe.lastLBA - gpe.firstLBA + 1); if (tmp != CL_CLEAN) { if (tmp == CL_VIRUS) { cli_dbgmsg("cli_scangpt: detected intersection with partitions " "[%u, %u]\n", pitxn, i); ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION); if (ret == CL_VIRUS) virus_found = 1; if (SCAN_ALLMATCHES || ret == CL_CLEAN) tmp = 0; else goto leave; } else { ret = tmp; goto leave; } } } /* increment the offsets to next partition entry */ pos += hdr.tableEntrySize; } leave: prtn_intxn_list_free(&prtncheck); if (virus_found) return CL_VIRUS; return ret; }
/* -------end runtime disable---------*/ int phishingScan(cli_ctx* ctx,tag_arguments_t* hrefs) { /* TODO: get_host and then apply regex, etc. */ int i; struct phishcheck* pchk = (struct phishcheck*) ctx->engine->phishcheck; /* check for status of whitelist fatal error, etc. */ if(!pchk || pchk->is_disabled) return CL_CLEAN; if(!ctx->found_possibly_unwanted && !SCAN_ALL) *ctx->virname=NULL; #if 0 FILE *f = fopen("/home/edwin/quarantine/urls","r"); if(!f) abort(); while(!feof(f)) { struct url_check urls; char line1[4096]; char line2[4096]; char line3[4096]; fgets(line1, sizeof(line1), f); fgets(line2, sizeof(line2), f); fgets(line3, sizeof(line3), f); if(strcmp(line3, "\n") != 0) { strcpy(line1, line2); strcpy(line2, line3); fgets(line3, sizeof(line3), f); while(strcmp(line3, "\n") != 0) { fgets(line3, sizeof(line3),f); } } urls.flags = CL_PHISH_ALL_CHECKS; urls.link_type = 0; string_init_c(&urls.realLink, line1); string_init_c(&urls.displayLink, line2); string_init_c(&urls.pre_fixup.pre_displayLink, NULL); urls.realLink.refcount=-1; urls.displayLink.refcount=-1; int rc = phishingCheck(ctx->engine, &urls); } fclose(f); return 0; #endif for(i=0;i<hrefs->count;i++) { struct url_check urls; enum phish_status rc; urls.flags = strncmp((char*)hrefs->tag[i],href_text,href_text_len)? (CL_PHISH_ALL_CHECKS&~CHECK_SSL): CL_PHISH_ALL_CHECKS; urls.link_type = 0; if(!strncmp((char*)hrefs->tag[i],src_text,src_text_len)) { if (!(urls.flags&CHECK_IMG_URL)) continue; urls.link_type |= LINKTYPE_IMAGE; } urls.always_check_flags = 0; if (ctx->options & CL_SCAN_PHISHING_BLOCKSSL) { urls.always_check_flags |= CHECK_SSL; } if (ctx->options & CL_SCAN_PHISHING_BLOCKCLOAK) { urls.always_check_flags |= CHECK_CLOAKING; } string_init_c(&urls.realLink,(char*)hrefs->value[i]); string_init_c(&urls.displayLink, (char*)hrefs->contents[i]); string_init_c(&urls.pre_fixup.pre_displayLink, NULL); urls.realLink.refcount=-1; urls.displayLink.refcount=-1;/*don't free these, caller will free*/ if(strcmp((char*)hrefs->tag[i],"href")) { char *url; url = urls.realLink.data; urls.realLink.data = urls.displayLink.data; urls.displayLink.data = url; } rc = phishingCheck(ctx->engine,&urls); if(pchk->is_disabled) return CL_CLEAN; free_if_needed(&urls); cli_dbgmsg("Phishcheck: Phishing scan result: %s\n",phishing_ret_toString(rc)); switch(rc)/*TODO: support flags from ctx->options,*/ { case CL_PHISH_CLEAN: continue; case CL_PHISH_NUMERIC_IP: cli_append_virus(ctx, "Heuristics.Phishing.Email.Cloaked.NumericIP"); break; case CL_PHISH_CLOAKED_NULL: cli_append_virus(ctx, "Heuristics.Phishing.Email.Cloaked.Null");/*fakesite%01%[email protected]*/ break; case CL_PHISH_SSL_SPOOF: cli_append_virus(ctx, "Heuristics.Phishing.Email.SSL-Spoof"); break; case CL_PHISH_CLOAKED_UIU: cli_append_virus(ctx, "Heuristics.Phishing.Email.Cloaked.Username");/*http://[email protected]*/ break; case CL_PHISH_HASH0: cli_append_virus(ctx, "Heuristics.Safebrowsing.Suspected-malware_safebrowsing.clamav.net"); break; case CL_PHISH_HASH1: cli_append_virus(ctx, "Heuristics.Phishing.URL.Blacklisted"); break; case CL_PHISH_HASH2: cli_append_virus(ctx, "Heuristics.Safebrowsing.Suspected-phishing_safebrowsing.clamav.net"); break; case CL_PHISH_NOMATCH: default: cli_append_virus(ctx, "Heuristics.Phishing.Email.SpoofedDomain"); break; } return cli_found_possibly_unwanted(ctx); } return CL_CLEAN; }