/* Stripe handling: zero block (type 0x0 or 0x2) */ static int dmg_stripe_zeroes(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set) { int ret = CL_CLEAN; size_t len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE; ssize_t written; uint8_t obuf[BUFSIZ]; cli_dbgmsg("dmg_stripe_zeroes: stripe " STDu32 "\n", index); if (len == 0) return CL_CLEAN; memset(obuf, 0, sizeof(obuf)); while (len > sizeof(obuf)) { written = cli_writen(fd, obuf, sizeof(obuf)); if (written != sizeof(obuf)) { ret = CL_EWRITE; break; } len -= sizeof(obuf); } if ((ret == CL_CLEAN) && (len > 0)) { written = cli_writen(fd, obuf, len); if (written != len) { ret = CL_EWRITE; } } if (ret != CL_CLEAN) { cli_errmsg("dmg_stripe_zeroes: error writing bytes to file (out of disk space?)\n"); return CL_EWRITE; } return CL_CLEAN; }
/* Stripe handling: stored block (type 0x1) */ static int dmg_stripe_store(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set) { const void *obuf; int ret; size_t off = mish_set->stripes[index].dataOffset; size_t len = mish_set->stripes[index].dataLength; ssize_t written; cli_dbgmsg("dmg_stripe_store: stripe " STDu32 "\n", index); if (len == 0) return CL_CLEAN; obuf = (void *)fmap_need_off_once(*ctx->fmap, off, len); if (!obuf) { cli_warnmsg("dmg_stripe_store: fmap need failed on stripe " STDu32 "\n", index); return CL_EMAP; } written = cli_writen(fd, obuf, len); if (written < 0) { cli_errmsg("dmg_stripe_store: error writing bytes to file (out of disk space?)\n"); return CL_EWRITE; } else if (written != len) { cli_errmsg("dmg_stripe_store: error writing bytes to file (out of disk space?)\n"); return CL_EWRITE; } return CL_CLEAN; }
/* xar_process_subdocument - check TOC for xml subdocument. If found, extract and scan in memory. Parameters: reader - xmlTextReaderPtr ctx - pointer to cli_ctx Returns: CL_SUCCESS - subdoc found and clean scan (or virus found and SCAN_ALL), or no subdocument other - error return code from cli_mem_scandesc() */ static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx) { int rc = CL_SUCCESS, subdoc_len, fd; xmlChar * subdoc; const xmlChar *name; char * tmpname; while (xmlTextReaderRead(reader) == 1) { name = xmlTextReaderConstLocalName(reader); if (name == NULL) { cli_dbgmsg("cli_scanxar: xmlTextReaderConstLocalName() no name.\n"); rc = CL_EFORMAT; break; } if (xmlStrEqual(name, (const xmlChar *)"toc") && xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) return CL_SUCCESS; if (xmlStrEqual(name, (const xmlChar *)"subdoc") && xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) { subdoc = xmlTextReaderReadInnerXml(reader); if (subdoc == NULL) { cli_dbgmsg("cli_scanxar: no content in subdoc element.\n"); xmlTextReaderNext(reader); continue; } subdoc_len = xmlStrlen(subdoc); cli_dbgmsg("cli_scanxar: in-memory scan of xml subdocument, len %i.\n", subdoc_len); rc = cli_mem_scandesc(subdoc, subdoc_len, ctx); if (rc == CL_VIRUS && SCAN_ALL) rc = CL_SUCCESS; /* make a file to leave if --leave-temps in effect */ if(ctx->engine->keeptmp) { if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't create temporary file for subdocument.\n"); } else { cli_dbgmsg("cli_scanxar: Writing subdoc to temp file %s.\n", tmpname); if (cli_writen(fd, subdoc, subdoc_len) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing subdoc temporary file.\n"); rc = CL_EWRITE; } rc = xar_cleanup_temp_file(ctx, fd, tmpname); } } xmlFree(subdoc); if (rc != CL_SUCCESS) return rc; xmlTextReaderNext(reader); } } return rc; }
static int dmg_extract_xml(cli_ctx *ctx, char *dir, struct dmg_koly_block *hdr) { char * xmlfile; const char *outdata; size_t namelen, nread; int ofd; /* Prep TOC XML for output */ 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 " STDu64 "\n", hdr->xmlLength); return CL_EMAP; } namelen = strlen(dir) + 1 + 7 + 1; if (!(xmlfile = cli_malloc(namelen))) { return CL_EMEM; } snprintf(xmlfile, namelen, "%s"PATHSEP"toc.xml", dir); cli_dbgmsg("cli_scandmg: Extracting XML as %s\n", xmlfile); /* Write out TOC XML */ if ((ofd = open(xmlfile, O_CREAT|O_RDWR|O_EXCL|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { char err[128]; cli_errmsg("cli_scandmg: Can't create temporary file %s: %s\n", xmlfile, cli_strerror(errno, err, sizeof(err))); free(xmlfile); return CL_ETMPFILE; } if (cli_writen(ofd, outdata, hdr->xmlLength) != hdr->xmlLength) { cli_errmsg("cli_scandmg: Not all bytes written!\n"); close(ofd); free(xmlfile); return CL_EWRITE; } close(ofd); free(xmlfile); return CL_SUCCESS; }
static uint64_t chm_copy_file_data(int ifd, int ofd, uint64_t len) { unsigned char data[8192]; uint64_t count, rem; unsigned int todo; rem = len; while (rem > 0) { todo = MIN(8192, rem); count = cli_readn(ifd, data, todo); if (count != todo) { return len-rem; } if (cli_writen(ofd, data, count) != (int64_t)count) { return len-rem-count; } rem -= count; } return len; }
/* TODO: field in ctx, id of last bytecode that called magicscandesc, reset * after hooks/other bytecodes are run. TODO: need a more generic solution * to avoid uselessly recursing on bytecode-unpacked files, but also a way to * override the limit if we need it in a special situation */ int32_t cli_bcapi_write(struct cli_bc_ctx *ctx, uint8_t*data, int32_t len) { char err[128]; int32_t res; cli_ctx *cctx = (cli_ctx*)ctx->ctx; if (len < 0) { cli_warnmsg("Bytecode API: called with negative length!\n"); API_MISUSE(); return -1; } if (!ctx->outfd) { ctx->tempfile = cli_gentemp(cctx ? cctx->engine->tmpdir : NULL); if (!ctx->tempfile) { cli_dbgmsg("Bytecode API: Unable to allocate memory for tempfile\n"); cli_event_error_oom(EV, 0); return -1; } ctx->outfd = open(ctx->tempfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if (ctx->outfd == -1) { ctx->outfd = 0; cli_warnmsg("Bytecode API: Can't create file %s: %s\n", ctx->tempfile, cli_strerror(errno, err, sizeof(err))); cli_event_error_str(EV, "cli_bcapi_write: Can't create temporary file"); free(ctx->tempfile); return -1; } cli_dbgmsg("bytecode opened new tempfile: %s\n", ctx->tempfile); } cli_event_fastdata(ctx->bc_events, BCEV_WRITE, data, len); if (cli_checklimits("bytecode api", cctx, ctx->written + len, 0, 0)) return -1; res = cli_writen(ctx->outfd, data, len); if (res > 0) ctx->written += res; if (res == -1) { cli_warnmsg("Bytecode API: write failed: %s\n", cli_strerror(errno, err, sizeof(err))); cli_event_error_str(EV, "cli_bcapi_write: write failed"); } return res; }
int fmap_dump_to_file(fmap_t *map, const char *tmpdir, char **outname, int *outfd) { char *tmpname; int tmpfd, ret; size_t pos = 0, len; cli_dbgmsg("fmap_dump_to_file: dumping fmap not backed by file...\n"); ret = cli_gentempfd(tmpdir, &tmpname, &tmpfd); if(ret != CL_SUCCESS) { cli_dbgmsg("fmap_dump_to_file: failed to generate temporary file.\n"); return ret; } do { const char *b; len = 0; b = fmap_need_off_once_len(map, pos, BUFSIZ, &len); pos += len; if(b && (len > 0)) { if ((size_t)cli_writen(tmpfd, b, len) != len) { cli_warnmsg("fmap_dump_to_file: write failed to %s!\n", tmpname); close(tmpfd); unlink(tmpname); free(tmpname); return CL_EWRITE; } } } while (len > 0); if(lseek(tmpfd, 0, SEEK_SET) == -1) { cli_dbgmsg("fmap_dump_to_file: lseek failed\n"); } *outname = tmpname; *outfd = tmpfd; return CL_SUCCESS; }
static int cab_unstore(struct cab_file *file, int bytes) { int todo, bread; unsigned char buff[4096]; if(bytes < 0) { cli_warnmsg("cab_unstore: bytes < 0\n"); return CL_EFORMAT; } todo = MIN((unsigned int) bytes, file->max_size); while(1) { if((unsigned int) todo <= sizeof(buff)) bread = todo; else bread = sizeof(buff); if((bread = cab_read(file, buff, bread)) == -1) { cli_dbgmsg("cab_unstore: cab_read failed for descriptor %d\n", file->fd); return file->error; } else if(cli_writen(file->ofd, buff, bread) != bread) { cli_warnmsg("cab_unstore: Can't write %d bytes to descriptor %d\n", bread, file->ofd); return CL_EWRITE; } todo -= bread; if(!bread || todo <= 0) break; } return CL_SUCCESS; }
/* Read and dump a file for scanning */ static int hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *extHeader, hfsPlusForkData *fork, const char *dirname) { hfsPlusExtentDescriptor *currExt; const uint8_t *mPtr = NULL; char *tmpname = NULL; int ofd, ret = CL_CLEAN; uint64_t targetSize; uint32_t outputBlocks = 0; uint8_t ext; UNUSEDPARAM(extHeader); /* bad record checks */ if (!fork || (fork->logicalSize == 0) || (fork->totalBlocks == 0)) { cli_dbgmsg("hfsplus_dumpfile: Empty file.\n"); return CL_CLEAN; } /* check limits */ targetSize = fork->logicalSize; #if SIZEOF_LONG < 8 if (targetSize > ULONG_MAX) { cli_dbgmsg("hfsplus_dumpfile: File too large for limit check.\n"); return CL_EFORMAT; } #endif ret = cli_checklimits("hfsplus_scanfile", ctx, (unsigned long)targetSize, 0, 0); if (ret != CL_CLEAN) { return ret; } /* open file */ ret = cli_gentempfd(dirname, &tmpname, &ofd); if (ret != CL_CLEAN) { cli_dbgmsg("hfsplus_dumpfile: Cannot generate temporary file.\n"); return ret; } cli_dbgmsg("hfsplus_dumpfile: Extracting to %s\n", tmpname); ext = 0; /* Dump file, extent by extent */ do { uint32_t currBlock, endBlock, outputSize = 0; if (targetSize == 0) { cli_dbgmsg("hfsplus_dumpfile: output complete\n"); break; } if (outputBlocks >= fork->totalBlocks) { cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); break; } /* Prepare extent */ if (ext < 8) { currExt = &(fork->extents[ext]); cli_dbgmsg("hfsplus_dumpfile: extent %u\n", ext); } else { cli_dbgmsg("hfsplus_dumpfile: need next extent from ExtentOverflow\n"); /* Not implemented yet */ ret = CL_EFORMAT; break; } /* have extent, so validate and get block range */ if ((currExt->startBlock == 0) || (currExt->blockCount == 0)) { cli_dbgmsg("hfsplus_dumpfile: next extent empty, done\n"); break; } if ((currExt->startBlock & 0x10000000) && (currExt->blockCount & 0x10000000)) { cli_dbgmsg("hfsplus_dumpfile: next extent illegal!\n"); ret = CL_EFORMAT; break; } currBlock = currExt->startBlock; endBlock = currExt->startBlock + currExt->blockCount - 1; if ((currBlock > volHeader->totalBlocks) || (endBlock > volHeader->totalBlocks) || (currExt->blockCount > volHeader->totalBlocks)) { cli_dbgmsg("hfsplus_dumpfile: bad extent!\n"); ret = CL_EFORMAT; break; } /* Write the blocks, walking the map */ while (currBlock <= endBlock) { size_t to_write = MIN(targetSize, volHeader->blockSize); ssize_t written; off_t offset = currBlock * volHeader->blockSize; /* move map to next block */ mPtr = fmap_need_off_once(*ctx->fmap, offset, volHeader->blockSize); if (!mPtr) { cli_errmsg("hfsplus_dumpfile: map error\n"); ret = CL_EMAP; break; } written = cli_writen(ofd, mPtr, to_write); if ((size_t)written != to_write) { cli_errmsg("hfsplus_dumpfile: write error\n"); ret = CL_EWRITE; break; } targetSize -= to_write; outputSize += to_write; currBlock++; if (targetSize == 0) { cli_dbgmsg("hfsplus_dumpfile: all data written\n"); break; } if (outputBlocks >= fork->totalBlocks) { cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); break; } } /* Finished the extent, move to next */ ext++; } while (ret == CL_CLEAN); /* if successful so far, scan the output */ if (ret == CL_CLEAN) { ret = cli_magic_scandesc(ofd, tmpname, ctx); } if (ofd >= 0) { close(ofd); } if (!ctx->engine->keeptmp) { if (cli_unlink(tmpname)) { ret = CL_EUNLINK; } } free(tmpname); return ret; }
/* static const unsigned char* parse_dispatch_cmd(client_conn_t *conn, struct fd_buf *buf, size_t *ppos, int *error, const struct optstruct *opts, int readtimeout) */ static int handle_stream(client_conn_t *conn, struct fd_buf *buf, const struct optstruct *opts, int *error, size_t *ppos, int readtimeout) { int rc; size_t pos = *ppos; size_t cmdlen; logg("$mode == MODE_STREAM\n"); /* we received some data, set readtimeout */ time(&buf->timeout_at); buf->timeout_at += readtimeout; while (pos <= buf->off) { if (!buf->chunksize) { /* read chunksize */ if (buf->off-pos >= 4) { uint32_t cs; memmove(&cs, buf->buffer + pos, 4); pos += 4; buf->chunksize = ntohl(cs); logg("$Got chunksize: %u\n", buf->chunksize); if (!buf->chunksize) { /* chunksize 0 marks end of stream */ conn->scanfd = buf->dumpfd; conn->term = buf->term; buf->dumpfd = -1; buf->mode = buf->group ? MODE_COMMAND : MODE_WAITREPLY; if (buf->mode == MODE_WAITREPLY) buf->fd = -1; logg("$Chunks complete\n"); buf->dumpname = NULL; if ((rc = execute_or_dispatch_command(conn, COMMAND_INSTREAMSCAN, NULL)) < 0) { logg("!Command dispatch failed\n"); if(rc == -1 && optget(opts, "ExitOnOOM")->enabled) { pthread_mutex_lock(&exit_mutex); progexit = 1; pthread_mutex_unlock(&exit_mutex); } *error = 1; } else { memmove (buf->buffer, &buf->buffer[pos], buf->off - pos); buf->off -= pos; *ppos = 0; buf->id++; return 0; } } if (buf->chunksize > buf->quota) { logg("^INSTREAM: Size limit reached, (requested: %lu, max: %lu)\n", (unsigned long)buf->chunksize, (unsigned long)buf->quota); conn_reply_error(conn, "INSTREAM size limit exceeded."); *error = 1; *ppos = pos; return -1; } else { buf->quota -= buf->chunksize; } logg("$Quota Remaining: %lu\n", buf->quota); } else { /* need more data, so return and wait for some */ memmove (buf->buffer, &buf->buffer[pos], buf->off - pos); buf->off -= pos; *ppos = 0; return -1; } } if (pos + buf->chunksize < buf->off) cmdlen = buf->chunksize; else cmdlen = buf->off - pos; buf->chunksize -= cmdlen; if (cli_writen(buf->dumpfd, buf->buffer + pos, cmdlen) < 0) { conn_reply_error(conn, "Error writing to temporary file"); logg("!INSTREAM: Can't write to temporary file.\n"); *error = 1; } logg("$Processed %lu bytes of chunkdata, pos %lu\n", cmdlen, pos); pos += cmdlen; if (pos == buf->off) { buf->off = 0; pos = 0; /* need more data, so return and wait for some */ *ppos = pos; return -1; } } *ppos = pos; return 0; }
cl_error_t fmap_dump_to_file(fmap_t* map, const char* filepath, const char* tmpdir, char** outname, int* outfd, size_t start_offset, size_t end_offset) { cl_error_t ret = CL_EARG; char* filebase = NULL; char* prefix = NULL; char* tmpname = NULL; int tmpfd = -1; size_t pos = 0, len = 0, bytes_remaining = 0, write_size = 0; if ((start_offset > map->real_len) || (end_offset < start_offset)) { cli_dbgmsg("fmap_dump_to_file: Invalid offset arguments: start %zu, end %zu\n", start_offset, end_offset); return ret; } pos = start_offset; end_offset = MIN(end_offset, map->real_len); bytes_remaining = end_offset - start_offset; /* Create a filename prefix that includes the original filename, if available */ if (filepath != NULL) { if (CL_SUCCESS != cli_basename(filepath, strlen(filepath), &filebase)) { cli_dbgmsg("fmap_dump_to_file: Unable to determine basename from filepath.\n"); } else if ((start_offset != 0) && (end_offset != map->real_len)) { /* If we're only dumping a portion of the file, inlcude the offsets in the prefix,... * e.g. tmp filename will become something like: filebase.500-1200.<randhex> */ uint32_t prefix_len = strlen(filebase) + 1 + SIZE_T_CHARLEN + 1 + SIZE_T_CHARLEN + 1; prefix = malloc(prefix_len); if (NULL == prefix) { cli_errmsg("fmap_dump_to_file: Failed to allocate memory for tempfile prefix.\n"); if (NULL != filebase) free(filebase); return CL_EMEM; } snprintf(prefix, prefix_len, "%s.%zu-%zu", filebase, start_offset, end_offset); free(filebase); filebase = NULL; } else { /* Else if we're dumping the whole thing, use the filebase as the prefix */ prefix = filebase; filebase = NULL; } } cli_dbgmsg("fmap_dump_to_file: dumping fmap not backed by file...\n"); ret = cli_gentempfd_with_prefix(tmpdir, prefix, &tmpname, &tmpfd); if (ret != CL_SUCCESS) { cli_dbgmsg("fmap_dump_to_file: failed to generate temporary file.\n"); if (NULL != prefix) { free(prefix); prefix = NULL; } return ret; } if (NULL != prefix) { free(prefix); prefix = NULL; } do { const char* b; len = 0; write_size = MIN(BUFSIZ, bytes_remaining); b = fmap_need_off_once_len(map, pos, write_size, &len); pos += len; if (b && (len > 0)) { if ((size_t)cli_writen(tmpfd, b, len) != len) { cli_warnmsg("fmap_dump_to_file: write failed to %s!\n", tmpname); close(tmpfd); unlink(tmpname); free(tmpname); return CL_EWRITE; } } if (len <= bytes_remaining) { bytes_remaining -= len; } else { bytes_remaining = 0; } } while ((len > 0) && (bytes_remaining > 0)); if (lseek(tmpfd, 0, SEEK_SET) == -1) { cli_dbgmsg("fmap_dump_to_file: lseek failed\n"); } *outname = tmpname; *outfd = tmpfd; return CL_SUCCESS; }
int cli_tnef(const char *dir, cli_ctx *ctx) { uint32_t i32; uint16_t i16; fileblob *fb; int ret, alldone; off_t fsize, pos = 0; STATBUF statb; fsize = ctx->fmap[0]->len; if(fsize < (off_t) MIN_SIZE) { cli_dbgmsg("cli_tngs: file too small, ignoring\n"); return CL_CLEAN; } if (fmap_readn(*ctx->fmap, &i32, pos, sizeof(uint32_t)) != sizeof(uint32_t)) { /* The file is at least MIN_SIZE bytes, so it "can't" fail */ return CL_EREAD; } pos += sizeof(uint32_t); if(host32(i32) != TNEF_SIGNATURE) { return CL_EFORMAT; } if(fmap_readn(*ctx->fmap, &i16, pos, sizeof(uint16_t)) != sizeof(uint16_t)) { /* The file is at least MIN_SIZE bytes, so it "can't" fail */ return CL_EREAD; } pos += sizeof(uint16_t); fb = NULL; ret = CL_CLEAN; /* we don't know if it's clean or not :-) */ alldone = 0; do { uint8_t part = 0; uint16_t type = 0, tag = 0; int32_t length = 0; switch(tnef_header(*ctx->fmap, &pos, &part, &type, &tag, &length)) { case 0: alldone = 1; break; case 1: break; default: /* * Assume truncation, not file I/O error */ cli_warnmsg("cli_tnef: file truncated, returning CLEAN\n"); ret = CL_CLEAN; alldone = 1; break; } if(length == 0) continue; if(length < 0) { cli_warnmsg("Corrupt TNEF header detected - length %d\n", (int)length); ret = CL_EFORMAT; break; } if(alldone) break; switch(part) { case LVL_MESSAGE: cli_dbgmsg("TNEF - found message\n"); if(fb != NULL) { fileblobDestroy(fb); fb = NULL; } fb = fileblobCreate(); if(tnef_message(*ctx->fmap, &pos, type, tag, length, fsize) != 0) { cli_dbgmsg("TNEF: Error reading TNEF message\n"); ret = CL_EFORMAT; alldone = 1; } break; case LVL_ATTACHMENT: cli_dbgmsg("TNEF - found attachment\n"); if(tnef_attachment(*ctx->fmap, &pos, type, tag, length, dir, &fb, fsize) != 0) { cli_dbgmsg("TNEF: Error reading TNEF attachment\n"); ret = CL_EFORMAT; alldone = 1; } break; case 0: break; default: cli_warnmsg("TNEF - unknown level %d tag 0x%x\n", (int)part, (int)tag); /* * Dump the file incase it was part of an * email that's about to be deleted */ if(cli_debug_flag) { int fout = -1; char *filename = cli_gentemp(ctx->engine->tmpdir); char buffer[BUFSIZ]; if(filename) fout = open(filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if(fout >= 0) { int count; cli_warnmsg("Saving dump to %s: refer to http://www.clamav.net/bugs\n", filename); pos = 0; while ((count = fmap_readn(*ctx->fmap, buffer, pos, sizeof(buffer))) > 0) { pos += count; cli_writen(fout, buffer, count); } close(fout); } free(filename); } ret = CL_EFORMAT; alldone = 1; break; } } while(!alldone); if(fb) { cli_dbgmsg("cli_tnef: flushing final data\n"); if(fileblobGetFilename(fb) == NULL) { cli_dbgmsg("Saving TNEF portion with an unknown name\n"); fileblobSetFilename(fb, dir, "tnef"); } fileblobDestroy(fb); fb = NULL; } cli_dbgmsg("cli_tnef: returning %d\n", ret); return ret; }
int yc_decrypt(cli_ctx *ctx, char *fbuf, unsigned int filesize, struct cli_exe_section *sections, unsigned int sectcount, uint32_t peoffset, int desc, uint32_t ecx,int16_t offset) { uint32_t ycsect = sections[sectcount].raw+offset; unsigned int i; struct pe_image_file_hdr *pe = (struct pe_image_file_hdr*) (fbuf + peoffset); char *sname = (char *)pe + EC16(pe->SizeOfOptionalHeader) + 0x18; uint32_t max_emu; unsigned int ofilesize = filesize; /* First layer (decryptor of the section decryptor) in last section Start offset for analyze: Start of yC Section + 0x93 End offset for analyze: Start of yC Section + 0xC3 Length to decrypt - ECX = 0xB97 */ cli_dbgmsg("yC: offset: %x, length: %x\n", offset, ecx); cli_dbgmsg("yC: decrypting decryptor on sect %d\n", sectcount); switch (yc_poly_emulator(ctx, fbuf, filesize, fbuf + ycsect + 0x93, fbuf + ycsect + 0xc6, ecx, ecx)) { case 2: return CL_VIRUS; case 1: return CL_EUNPACK; } filesize-=sections[sectcount].ursz; /* Second layer (decryptor of the sections) in last section Start offset for analyze: Start of yC Section + 0x457 End offset for analyze: Start of yC Section + 0x487 Length to decrypt - ECX = Raw Size of Section */ /* Loop through all sections and decrypt them... */ for(i=0;i<sectcount;i++) { uint32_t name = (uint32_t) cli_readint32(sname+i*0x28); if (!sections[i].raw || !sections[i].rsz || name == 0x63727372 || /* rsrc */ name == 0x7273722E || /* .rsr */ name == 0x6F6C6572 || /* relo */ name == 0x6C65722E || /* .rel */ name == 0x6164652E || /* .eda */ name == 0x6164722E || /* .rda */ name == 0x6164692E || /* .ida */ name == 0x736C742E || /* .tls */ (name&0xffff) == 0x4379 /* yC */ ) continue; cli_dbgmsg("yC: decrypting sect%d\n",i); max_emu = filesize - sections[i].raw; if (max_emu > filesize) { cli_dbgmsg("yC: bad emulation length limit %u\n", max_emu); return 1; } switch (yc_poly_emulator(ctx, fbuf, ofilesize, fbuf + ycsect + (offset == -0x18 ? 0x3ea : 0x457), fbuf + sections[i].raw, sections[i].ursz, max_emu)) { case 2: return CL_VIRUS; case 1: return CL_EUNPACK; } } /* Remove yC section */ pe->NumberOfSections=EC16(sectcount); /* Remove IMPORT_DIRECTORY information */ memset((char *)pe + sizeof(struct pe_image_file_hdr) + 0x68, 0, 8); /* OEP resolving */ /* OEP = DWORD PTR [ Start of yC section+ A0F] */ cli_writeint32((char *)pe + sizeof(struct pe_image_file_hdr) + 16, cli_readint32(fbuf + ycsect + 0xa0f)); /* Fix SizeOfImage */ cli_writeint32((char *)pe + sizeof(struct pe_image_file_hdr) + 0x38, cli_readint32((char *)pe + sizeof(struct pe_image_file_hdr) + 0x38) - sections[sectcount].vsz); if (cli_writen(desc, fbuf, filesize)==-1) { cli_dbgmsg("yC: Cannot write unpacked file\n"); return CL_EUNPACK; } return CL_SUCCESS; }
static int nsis_unpack_next(struct nsis_st *n, cli_ctx *ctx) { const unsigned char *ibuf; uint32_t size, loops; int ret, gotsome=0, opened=0; unsigned char obuf[BUFSIZ]; if (n->eof) { cli_dbgmsg("NSIS: extraction complete\n"); return CL_BREAK; } if ((ret=cli_checklimits("NSIS", ctx, 0, 0, 0))!=CL_CLEAN) return ret; if (n->fno) snprintf(n->ofn, 1023, "%s"PATHSEP"content.%.3u", n->dir, n->fno); else snprintf(n->ofn, 1023, "%s"PATHSEP"headers", n->dir); n->fno++; n->opened = 0; if (!n->solid) { if (fmap_readn(n->map, &size, n->curpos, 4)!=4) { cli_dbgmsg("NSIS: reached EOF - extraction complete\n"); return CL_BREAK; } n->curpos += 4; if (n->asz==4) { cli_dbgmsg("NSIS: reached CRC - extraction complete\n"); return CL_BREAK; } loops = EC32(size); if (!(size = (loops&~0x80000000))) { cli_dbgmsg("NSIS: empty file found\n"); return CL_SUCCESS; } if (n->asz <4 || size > n->asz-4) { cli_dbgmsg("NSIS: next file is outside the archive\n"); return CL_BREAK; } n->asz -= size+4; if ((ret=cli_checklimits("NSIS", ctx, size, 0, 0))!=CL_CLEAN) { n->curpos += size; return ret; } if (!(ibuf = fmap_need_off_once(n->map, n->curpos, size))) { cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", size); return CL_EREAD; } if ((n->ofd=open(n->ofn, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600))==-1) { cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn); return CL_ECREAT; } n->opened = 1; n->curpos += size; if (loops==size) { if (cli_writen(n->ofd, ibuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); close(n->ofd); return CL_EWRITE; } } else { if ((ret=nsis_init(n))!=CL_SUCCESS) { cli_dbgmsg("NSIS: decompressor init failed"__AT__"\n"); close(n->ofd); return ret; } n->nsis.avail_in = size; n->nsis.next_in = (void*)ibuf; n->nsis.next_out = obuf; n->nsis.avail_out = BUFSIZ; loops=0; while ((ret=nsis_decomp(n))==CL_SUCCESS) { if ((size = n->nsis.next_out - obuf)) { gotsome=1; if (cli_writen(n->ofd, obuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); close(n->ofd); nsis_shutdown(n); return CL_EWRITE; } n->nsis.next_out = obuf; n->nsis.avail_out = BUFSIZ; loops=0; if ((ret=cli_checklimits("NSIS", ctx, size, 0, 0))!=CL_CLEAN) { close(n->ofd); nsis_shutdown(n); return ret; } } else if (++loops > 20) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_EFORMAT; break; } } nsis_shutdown(n); if (n->nsis.next_out - obuf) { gotsome=1; if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); close(n->ofd); return CL_EWRITE; } } if (ret != CL_SUCCESS && ret != CL_BREAK) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); if (gotsome) { ret = CL_SUCCESS; } else { ret = CL_EMAXSIZE; close(n->ofd); } return ret; } } return CL_SUCCESS; } else { if (!n->freeme) { if ((ret=nsis_init(n))!=CL_SUCCESS) { cli_dbgmsg("NSIS: decompressor init failed\n"); return ret; } if(!(n->freeme = fmap_need_off_once(n->map, n->curpos, n->asz))) { cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", n->asz); return CL_EREAD; } n->nsis.next_in = (void*)n->freeme; n->nsis.avail_in = n->asz; } if (n->nsis.avail_in<=4) { cli_dbgmsg("NSIS: extraction complete\n"); return CL_BREAK; } n->nsis.next_out = obuf; n->nsis.avail_out = 4; loops = 0; while ((ret=nsis_decomp(n))==CL_SUCCESS) { if (n->nsis.next_out - obuf == 4) break; if (++loops > 20) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret != CL_SUCCESS) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); return CL_EFORMAT; } size=cli_readint32(obuf); if ((ret=cli_checklimits("NSIS", ctx, size, 0, 0))!=CL_CLEAN) { return ret; } if (size == 0) { cli_dbgmsg("NSIS: Empty file found.\n"); return CL_SUCCESS; } n->nsis.next_out = obuf; n->nsis.avail_out = MIN(BUFSIZ,size); loops = 0; if ((n->ofd=open(n->ofn, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600))==-1) { cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn); return CL_ECREAT; } n->opened = 1; while (size && (ret=nsis_decomp(n))==CL_SUCCESS) { unsigned int wsz; if ((wsz = n->nsis.next_out - obuf)) { gotsome=1; if (cli_writen(n->ofd, obuf, wsz) != (ssize_t) wsz) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); close(n->ofd); return CL_EWRITE; } size-=wsz; loops=0; n->nsis.next_out = obuf; n->nsis.avail_out = MIN(size,BUFSIZ); } else if ( ++loops > 20 ) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_EFORMAT; break; } } if (n->nsis.next_out - obuf) { gotsome=1; if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); close(n->ofd); return CL_EWRITE; } } if (ret == CL_EFORMAT) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); if (!gotsome) { close(n->ofd); return CL_EMAXSIZE; } } if (ret == CL_EFORMAT || ret == CL_BREAK) { n->eof=1; } else if (ret != CL_SUCCESS) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); close(n->ofd); return CL_EFORMAT; } return CL_SUCCESS; } }
/* Stripe handling: deflate block (type 0x80000005) */ static int dmg_stripe_inflate(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set) { int ret = CL_CLEAN, zstat; z_stream strm; size_t off = mish_set->stripes[index].dataOffset; size_t len = mish_set->stripes[index].dataLength; uint64_t size_so_far = 0; uint64_t expected_len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE; uint8_t obuf[BUFSIZ]; cli_dbgmsg("dmg_stripe_inflate: stripe " STDu32 "\n", index); if (len == 0) return CL_CLEAN; memset(&strm, 0, sizeof(strm)); strm.next_in = (void*)fmap_need_off_once(*ctx->fmap, off, len); if (!strm.next_in) { cli_warnmsg("dmg_stripe_inflate: fmap need failed on stripe " STDu32 "\n", index); return CL_EMAP; } strm.avail_in = len; strm.next_out = obuf; strm.avail_out = sizeof(obuf); zstat = inflateInit(&strm); if(zstat != Z_OK) { cli_warnmsg("dmg_stripe_inflate: inflateInit failed\n"); return CL_EMEM; } while(strm.avail_in) { int written; if (size_so_far > expected_len) { cli_warnmsg("dmg_stripe_inflate: expected size exceeded!\n"); inflateEnd(&strm); return CL_EFORMAT; } zstat = inflate(&strm, Z_NO_FLUSH); /* zlib */ switch(zstat) { case Z_OK: if(strm.avail_out == 0) { if ((written=cli_writen(fd, obuf, sizeof(obuf)))!=sizeof(obuf)) { cli_errmsg("dmg_stripe_inflate: failed write to output file\n"); inflateEnd(&strm); return CL_EWRITE; } size_so_far += written; strm.next_out = (Bytef *)obuf; strm.avail_out = sizeof(obuf); } continue; case Z_STREAM_END: default: written = sizeof(obuf) - strm.avail_out; if (written) { if ((cli_writen(fd, obuf, written))!=written) { cli_errmsg("dmg_stripe_inflate: failed write to output file\n"); inflateEnd(&strm); return CL_EWRITE; } size_so_far += written; strm.next_out = (Bytef *)obuf; strm.avail_out = sizeof(obuf); if (zstat == Z_STREAM_END) break; } if(strm.msg) cli_dbgmsg("dmg_stripe_inflate: after writing " STDu64 " bytes, " "got error \"%s\" inflating stripe " STDu32 "\n", size_so_far, strm.msg, index); else cli_dbgmsg("dmg_stripe_inflate: after writing " STDu64 " bytes, " "got error %d inflating stripe " STDu32 "\n", size_so_far, zstat, index); inflateEnd(&strm); return CL_EFORMAT; } break; } if(strm.avail_out != sizeof(obuf)) { if(cli_writen(fd, obuf, sizeof(obuf) - strm.avail_out) < 0) { cli_errmsg("dmg_stripe_inflate: failed write to output file\n"); inflateEnd(&strm); return CL_EWRITE; } } inflateEnd(&strm); return CL_CLEAN; }
/* Stripe handling: bzip block (type 0x80000006) */ static int dmg_stripe_bzip(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set) { int ret = CL_CLEAN; size_t off = mish_set->stripes[index].dataOffset; size_t len = mish_set->stripes[index].dataLength; uint64_t size_so_far = 0; uint64_t expected_len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE; #if HAVE_BZLIB_H int rc; bz_stream strm; uint8_t obuf[BUFSIZ]; #endif cli_dbgmsg("dmg_stripe_bzip: stripe " STDu32 " initial len " STDu64 " expected len " STDu64 "\n", index, len, expected_len); #if HAVE_BZLIB_H memset(&strm, 0, sizeof(strm)); strm.next_out = obuf; strm.avail_out = sizeof(obuf); if (BZ2_bzDecompressInit(&strm, 0, 0) != BZ_OK) { cli_dbgmsg("dmg_stripe_bzip: bzDecompressInit failed\n"); return CL_EOPEN; } do { if (size_so_far > expected_len) { cli_warnmsg("dmg_stripe_bzip: expected size exceeded!\n"); ret = CL_EFORMAT; break; } if (strm.avail_in == 0) { size_t next_len = (len > sizeof(obuf)) ? sizeof(obuf) : len; dmg_bzipmsg("dmg_stripe_bzip: off %lu len %lu next_len %lu\n", off, len, next_len); strm.next_in = (void*)fmap_need_off_once(*ctx->fmap, off, next_len); if (strm.next_in == NULL) { cli_dbgmsg("dmg_stripe_bzip: expected more stream\n"); ret = CL_EMAP; break; } strm.avail_in = next_len; len -= next_len; off += next_len; } dmg_bzipmsg("dmg_stripe_bzip: before = strm.avail_in %lu strm.avail_out: %lu\n", strm.avail_in, strm.avail_out); rc = BZ2_bzDecompress(&strm); if ((rc != BZ_OK) && (rc != BZ_STREAM_END)) { cli_dbgmsg("dmg_stripe_bzip: decompress error: %d\n", rc); ret = CL_EFORMAT; break; } dmg_bzipmsg("dmg_stripe_bzip: after = strm.avail_in %lu strm.avail_out: %lu rc: %d %d\n", strm.avail_in, strm.avail_out, rc, BZ_STREAM_END); /* Drain output buffer */ if (!strm.avail_out) { size_t next_write = sizeof(obuf); do { size_so_far += next_write; dmg_bzipmsg("dmg_stripe_bzip: size_so_far: " STDu64 " next_write: %lu\n", size_so_far, next_write); if (size_so_far > expected_len) { cli_warnmsg("dmg_stripe_bzip: expected size exceeded!\n"); ret = CL_EFORMAT; rc = BZ_DATA_ERROR; /* prevent stream end block */ break; } ret = cli_checklimits("dmg_stripe_bzip", ctx, (unsigned long)(size_so_far + sizeof(obuf)), 0, 0); if (ret != CL_CLEAN) { break; } if (cli_writen(fd, obuf, next_write) != next_write) { cli_dbgmsg("dmg_stripe_bzip: error writing to tmpfile\n"); ret = CL_EWRITE; break; } strm.next_out = obuf; strm.avail_out = sizeof(obuf); if (rc == BZ_OK) rc = BZ2_bzDecompress(&strm); if ((rc != BZ_OK) && (rc != BZ_STREAM_END)) { cli_dbgmsg("dmg_stripe_bzip: decompress error: %d\n", rc); ret = CL_EFORMAT; break; } } while (!strm.avail_out); } /* Stream end, so write data if any remains in buffer */ if (rc == BZ_STREAM_END) { size_t next_write = sizeof(obuf) - strm.avail_out; size_so_far += next_write; dmg_bzipmsg("dmg_stripe_bzip: size_so_far: " STDu64 " next_write: %lu\n", size_so_far, next_write); ret = cli_checklimits("dmg_stripe_bzip", ctx, (unsigned long)(size_so_far + sizeof(obuf)), 0, 0); if (ret != CL_CLEAN) { break; } if (cli_writen(fd, obuf, next_write) != next_write) { cli_dbgmsg("dmg_stripe_bzip: error writing to tmpfile\n"); ret = CL_EWRITE; break; } strm.next_out = obuf; strm.avail_out = sizeof(obuf); } } while ((rc == BZ_OK) && (len > 0)); BZ2_bzDecompressEnd(&strm); #endif if (ret == CL_CLEAN) { if (size_so_far != expected_len) { cli_dbgmsg("dmg_stripe_bzip: output does not match expected size!\n"); } } return ret; }
int cli_rebuildpe(char *buffer, struct cli_exe_section *sections, int sects, uint32_t base, uint32_t ep, uint32_t ResRva, uint32_t ResSize, int file) { uint32_t datasize=0, rawbase=PESALIGN(0x148+0x80+0x28*sects, 0x200); char *pefile=NULL, *curpe; struct IMAGE_PE_HEADER *fakepe; int i, gotghost=(sections[0].rva > PESALIGN(rawbase, 0x1000)); if (gotghost) rawbase=PESALIGN(0x148+0x80+0x28*(sects+1), 0x200); if(sects+gotghost > 96) return 0; for (i=0; i < sects; i++) datasize+=PESALIGN(sections[i].rsz, 0x200); if(datasize > CLI_MAX_ALLOCATION) return 0; if((pefile = (char *) cli_calloc(rawbase+datasize, 1))) { memcpy(pefile, HEADERS, 0x148); datasize = PESALIGN(rawbase, 0x1000); fakepe = (struct IMAGE_PE_HEADER *)(pefile+0xd0); fakepe->NumberOfSections = EC16(sects+gotghost); fakepe->AddressOfEntryPoint = EC32(ep); fakepe->ImageBase = EC32(base); fakepe->SizeOfHeaders = EC32(rawbase); memset(pefile+0x148, 0, 0x80); cli_writeint32(pefile+0x148+0x10, ResRva); cli_writeint32(pefile+0x148+0x14, ResSize); curpe = pefile+0x148+0x80; if (gotghost) { snprintf(curpe, 8, "empty"); cli_writeint32(curpe+8, sections[0].rva-datasize); /* vsize */ cli_writeint32(curpe+12, datasize); /* rva */ cli_writeint32(curpe+0x24, 0xffffffff); curpe+=40; datasize+=PESALIGN(sections[0].rva-datasize, 0x1000); } for (i=0; i < sects; i++) { snprintf(curpe, 8, ".clam%.2d", i+1); cli_writeint32(curpe+8, sections[i].vsz); cli_writeint32(curpe+12, sections[i].rva); cli_writeint32(curpe+16, sections[i].rsz); cli_writeint32(curpe+20, rawbase); /* already zeroed cli_writeint32(curpe+24, 0); cli_writeint32(curpe+28, 0); cli_writeint32(curpe+32, 0); */ cli_writeint32(curpe+0x24, 0xffffffff); memcpy(pefile+rawbase, buffer+sections[i].raw, sections[i].rsz); rawbase+=PESALIGN(sections[i].rsz, 0x200); curpe+=40; datasize+=PESALIGN(sections[i].vsz, 0x1000); } fakepe->SizeOfImage = EC32(datasize); } else { return 0; } i = (cli_writen(file, pefile, rawbase)!=-1); free(pefile); return i; }
static int unz(const uint8_t *src, uint32_t csize, uint32_t usize, uint16_t method, uint16_t flags, unsigned int *fu, cli_ctx *ctx, char *tmpd, zip_cb zcb) { char name[1024], obuf[BUFSIZ]; char *tempfile = name; int of, ret=CL_CLEAN; unsigned int res=1, written=0; if(tmpd) { snprintf(name, sizeof(name), "%s"PATHSEP"zip.%03u", tmpd, *fu); name[sizeof(name)-1]='\0'; } else { if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM; } if((of = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR))==-1) { cli_warnmsg("cli_unzip: failed to create temporary file %s\n", tempfile); if(!tmpd) free(tempfile); return CL_ECREAT; } switch (method) { case ALG_STORED: if(csize<usize) { unsigned int fake = *fu + 1; cli_dbgmsg("cli_unzip: attempting to inflate stored file with inconsistent size\n"); if ((ret=unz(src, csize, usize, ALG_DEFLATE, 0, &fake, ctx, tmpd, zcb))==CL_CLEAN) { (*fu)++; res=fake-(*fu); } else break; } if(res==1) { if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize); csize = ctx->engine->maxfilesize; } if(cli_writen(of, src, csize)!=(int)csize) ret = CL_EWRITE; else res=0; } break; case ALG_DEFLATE: case ALG_DEFLATE64: { union { z_stream64 strm64; z_stream strm; } strm; typedef int (*unz_init_) (void *, int); typedef int (*unz_unz_) (void *, int); typedef int (*unz_end_) (void *); unz_init_ unz_init; unz_unz_ unz_unz; unz_end_ unz_end; int wbits; void **next_in; void **next_out; unsigned int *avail_in; unsigned int *avail_out; if(method == ALG_DEFLATE64) { unz_init = (unz_init_)inflate64Init2; unz_unz = (unz_unz_)inflate64; unz_end = (unz_end_)inflate64End; next_in = (void *)&strm.strm64.next_in; next_out = (void *)&strm.strm64.next_out; avail_in = &strm.strm64.avail_in; avail_out = &strm.strm64.avail_out; wbits=MAX_WBITS64; } else { unz_init = (unz_init_)wrap_inflateinit2; unz_unz = (unz_unz_)inflate; unz_end = (unz_end_)inflateEnd; next_in = (void *)&strm.strm.next_in; next_out = (void *)&strm.strm.next_out; avail_in = &strm.strm.avail_in; avail_out = &strm.strm.avail_out; wbits=MAX_WBITS; } memset(&strm, 0, sizeof(strm)); *next_in = (void*) src; *next_out = obuf; *avail_in = csize; *avail_out = sizeof(obuf); if (unz_init(&strm, -wbits)!=Z_OK) { cli_dbgmsg("cli_unzip: zinit failed\n"); break; } while(1) { while((res = unz_unz(&strm, Z_NO_FLUSH))==Z_OK) {}; if(*avail_out!=sizeof(obuf)) { written+=sizeof(obuf)-(*avail_out); if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (long unsigned int) ctx->engine->maxfilesize); res = Z_STREAM_END; break; } if(cli_writen(of, obuf, sizeof(obuf)-(*avail_out)) != (int)(sizeof(obuf)-(*avail_out))) { cli_warnmsg("cli_unzip: falied to write %lu inflated bytes\n", (unsigned long int)sizeof(obuf)-(*avail_out)); ret = CL_EWRITE; res = 100; break; } *next_out = obuf; *avail_out = sizeof(obuf); continue; } break; } unz_end(&strm); if (res == Z_STREAM_END) res=0; break; } #if HAVE_BZLIB_H #ifdef NOBZ2PREFIX #define BZ2_bzDecompress bzDecompress #define BZ2_bzDecompressEnd bzDecompressEnd #define BZ2_bzDecompressInit bzDecompressInit #endif case ALG_BZIP2: { bz_stream strm; memset(&strm, 0, sizeof(strm)); strm.next_in = (char *)src; strm.next_out = obuf; strm.avail_in = csize; strm.avail_out = sizeof(obuf); if (BZ2_bzDecompressInit(&strm, 0, 0)!=BZ_OK) { cli_dbgmsg("cli_unzip: bzinit failed\n"); break; } while((res = BZ2_bzDecompress(&strm))==BZ_OK || res==BZ_STREAM_END) { if(strm.avail_out!=sizeof(obuf)) { written+=sizeof(obuf)-strm.avail_out; if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize); res = BZ_STREAM_END; break; } if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { cli_warnmsg("cli_unzip: falied to write %lu bunzipped bytes\n", (long unsigned int)sizeof(obuf)-strm.avail_out); ret = CL_EWRITE; res = 100; break; } strm.next_out = obuf; strm.avail_out = sizeof(obuf); if (res == BZ_OK) continue; /* after returning BZ_STREAM_END once, decompress returns an error */ } break; } BZ2_bzDecompressEnd(&strm); if (res == BZ_STREAM_END) res=0; break; } #endif /* HAVE_BZLIB_H */ case ALG_IMPLODE: { struct xplstate strm; strm.next_in = (void*)src; strm.next_out = (uint8_t *)obuf; strm.avail_in = csize; strm.avail_out = sizeof(obuf); if (explode_init(&strm, flags)!=EXPLODE_OK) { cli_dbgmsg("cli_unzip: explode_init() failed\n"); break; } while((res = explode(&strm))==EXPLODE_OK) { if(strm.avail_out!=sizeof(obuf)) { written+=sizeof(obuf)-strm.avail_out; if(ctx->engine->maxfilesize && written > ctx->engine->maxfilesize) { cli_dbgmsg("cli_unzip: trimming output size to maxfilesize (%lu)\n", (unsigned long int) ctx->engine->maxfilesize); res = 0; break; } if(cli_writen(of, obuf, sizeof(obuf)-strm.avail_out) != (int)(sizeof(obuf)-strm.avail_out)) { cli_warnmsg("cli_unzip: falied to write %lu exploded bytes\n", (unsigned long int) sizeof(obuf)-strm.avail_out); ret = CL_EWRITE; res = 100; break; } strm.next_out = (uint8_t *)obuf; strm.avail_out = sizeof(obuf); continue; } break; } break; } case ALG_LZMA: /* easy but there's not a single sample in the zoo */ #if !HAVE_BZLIB_H case ALG_BZIP2: #endif case ALG_SHRUNK: case ALG_REDUCE1: case ALG_REDUCE2: case ALG_REDUCE3: case ALG_REDUCE4: case ALG_TOKENZD: case ALG_OLDTERSE: case ALG_RSVD1: case ALG_RSVD2: case ALG_RSVD3: case ALG_RSVD4: case ALG_RSVD5: case ALG_NEWTERSE: case ALG_LZ77: case ALG_WAVPACK: case ALG_PPMD: cli_dbgmsg("cli_unzip: unsupported method (%d)\n", method); break; default: cli_dbgmsg("cli_unzip: unknown method (%d)\n", method); break; } if(!res) { (*fu)++; cli_dbgmsg("cli_unzip: extracted to %s\n", tempfile); if (lseek(of, 0, SEEK_SET) == -1) { cli_dbgmsg("cli_unzip: call to lseek() failed\n"); if (!(tmpd)) free(tempfile); close(of); return CL_ESEEK; } ret = zcb(of, ctx); close(of); if(!ctx->engine->keeptmp) if(cli_unlink(tempfile)) ret = CL_EUNLINK; if(!tmpd) free(tempfile); return ret; } close(of); if(!ctx->engine->keeptmp) if(cli_unlink(tempfile)) ret = CL_EUNLINK; if(!tmpd) free(tempfile); cli_dbgmsg("cli_unzip: extraction failed\n"); return ret; }
int cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx) { int size = 0, ret, fout=-1; int in_block = 0; int last_header_bad = 0; int limitnear = 0; unsigned int files = 0; char fullname[NAME_MAX + 1]; size_t pos = 0; size_t currsize = 0; char zero[BLOCKSIZE]; unsigned int num_viruses = 0; cli_dbgmsg("In untar(%s)\n", dir); memset(zero, 0, sizeof(zero)); for(;;) { const char *block; size_t nread; block = fmap_need_off_once_len(*ctx->fmap, pos, BLOCKSIZE, &nread); cli_dbgmsg("cli_untar: pos = %lu\n", (unsigned long)pos); if(!in_block && !nread) break; if (!nread) block = zero; if(!block) { if(fout>=0) close(fout); cli_errmsg("cli_untar: block read error\n"); return CL_EREAD; } pos += nread; if(!in_block) { char type; int directory, skipEntry = 0; int checksum = -1; char magic[7], name[101], osize[TARSIZELEN + 1]; currsize = 0; if(fout>=0) { lseek(fout, 0, SEEK_SET); ret = cli_magic_scandesc(fout, ctx); close(fout); if (!ctx->engine->keeptmp) if (cli_unlink(fullname)) return CL_EUNLINK; if (ret==CL_VIRUS) { if (!SCAN_ALL) return CL_VIRUS; else num_viruses++; } fout = -1; } if(block[0] == '\0') /* We're done */ break; if((ret=cli_checklimits("cli_untar", ctx, 0, 0, 0))!=CL_CLEAN) return ret; checksum = getchecksum(block); cli_dbgmsg("cli_untar: Candidate checksum = %d, [%o in octal]\n", checksum, checksum); if(testchecksum(block, checksum) != 0) { // If checksum is bad, dump and look for next header block cli_dbgmsg("cli_untar: Invalid checksum in tar header. Skip to next...\n"); if (last_header_bad == 0) { last_header_bad++; cli_dbgmsg("cli_untar: Invalid checksum found inside archive!\n"); } continue; } else { last_header_bad = 0; cli_dbgmsg("cli_untar: Checksum %d is valid.\n", checksum); } /* Notice assumption that BLOCKSIZE > 262 */ if(posix) { strncpy(magic, block+257, 5); magic[5] = '\0'; if(strcmp(magic, "ustar") != 0) { cli_dbgmsg("cli_untar: Incorrect magic string '%s' in tar header\n", magic); return CL_EFORMAT; } } type = block[TARFILETYPEOFFSET]; switch(type) { default: cli_dbgmsg("cli_untar: unknown type flag %c\n", type); case '0': /* plain file */ case '\0': /* plain file */ case '7': /* contiguous file */ case 'M': /* continuation of a file from another volume; might as well scan it. */ files++; directory = 0; break; case '1': /* Link to already archived file */ case '5': /* directory */ case '2': /* sym link */ case '3': /* char device */ case '4': /* block device */ case '6': /* fifo special */ case 'V': /* Volume header */ directory = 1; break; case 'K': case 'L': /* GNU extension - ././@LongLink * Discard the blocks with the extended filename, * the last header will contain parts of it anyway */ case 'N': /* Old GNU format way of storing long filenames. */ case 'A': /* Solaris ACL */ case 'E': /* Solaris Extended attribute s*/ case 'I': /* Inode only */ case 'g': /* Global extended header */ case 'x': /* Extended attributes */ case 'X': /* Extended attributes (POSIX) */ directory = 0; skipEntry = 1; break; } if(directory) { in_block = 0; continue; } strncpy(osize, block+TARSIZEOFFSET, TARSIZELEN); osize[TARSIZELEN] = '\0'; size = octal(osize); if(size < 0) { cli_dbgmsg("cli_untar: Invalid size in tar header\n"); skipEntry++; } else { cli_dbgmsg("cli_untar: size = %d\n", size); ret = cli_checklimits("cli_untar", ctx, size, 0, 0); switch(ret) { case CL_EMAXFILES: // Scan no more files skipEntry++; limitnear = 0; break; case CL_EMAXSIZE: // Either single file limit or total byte limit would be exceeded cli_dbgmsg("cli_untar: would exceed limit, will try up to max"); limitnear = 1; break; default: // Ok based on reported content size limitnear = 0; break; } } if(skipEntry) { const int nskip = (size % BLOCKSIZE || !size) ? size + BLOCKSIZE - (size % BLOCKSIZE) : size; if(nskip < 0) { cli_dbgmsg("cli_untar: got negative skip size, giving up\n"); return CL_CLEAN; } cli_dbgmsg("cli_untar: skipping entry\n"); pos += nskip; continue; } strncpy(name, block, 100); name[100] = '\0'; if(cli_matchmeta(ctx, name, size, size, 0, files, 0, NULL) == CL_VIRUS) { if (!SCAN_ALL) return CL_VIRUS; else num_viruses++; } snprintf(fullname, sizeof(fullname)-1, "%s"PATHSEP"tar%02u", dir, files); fullname[sizeof(fullname)-1] = '\0'; fout = open(fullname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if(fout < 0) { char err[128]; cli_errmsg("cli_untar: Can't create temporary file %s: %s\n", fullname, cli_strerror(errno, err, sizeof(err))); return CL_ETMPFILE; } cli_dbgmsg("cli_untar: extracting to %s\n", fullname); in_block = 1; } else { /* write or continue writing file contents */ int nbytes, nwritten; int skipwrite = 0; char err[128]; nbytes = size>512? 512:size; if (nread && nread < (size_t)nbytes) nbytes = nread; if (limitnear > 0) { currsize += nbytes; cli_dbgmsg("cli_untar: Approaching limit...\n"); if (cli_checklimits("cli_untar", ctx, (unsigned long)currsize, 0, 0) != CL_SUCCESS) { // Limit would be exceeded by this file, suppress writing beyond limit // Need to keep reading to get to end of file chunk skipwrite++; } } if (skipwrite == 0) { nwritten = (int)cli_writen(fout, block, (size_t)nbytes); if(nwritten != nbytes) { cli_errmsg("cli_untar: only wrote %d bytes to file %s (out of disc space?): %s\n", nwritten, fullname, cli_strerror(errno, err, sizeof(err))); close(fout); return CL_EWRITE; } } size -= nbytes; if ((size != 0) && (nread == 0)) { // Truncated tar file, so end file content like tar behavior cli_dbgmsg("cli_untar: No bytes read! Forcing end of file content.\n"); size = 0; } } if (size == 0) in_block = 0; } if(fout>=0) { lseek(fout, 0, SEEK_SET); ret = cli_magic_scandesc(fout, ctx); close(fout); if (!ctx->engine->keeptmp) if (cli_unlink(fullname)) return CL_EUNLINK; if (ret==CL_VIRUS) return CL_VIRUS; } if (num_viruses) return CL_VIRUS; return CL_CLEAN; }
static int scancws(cli_ctx *ctx, struct swf_file_hdr *hdr) { z_stream stream; char inbuff[FILEBUFF], outbuff[FILEBUFF]; fmap_t *map = *ctx->fmap; int offset = 8, ret, zret, outsize = 8, count, zend; char *tmpname; int fd; if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_errmsg("scancws: Can't generate temporary file\n"); return ret; } hdr->signature[0] = 'F'; if(cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) { cli_errmsg("scancws: Can't write to file %s\n", tmpname); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } stream.avail_in = 0; stream.next_in = (Bytef *)inbuff; stream.next_out = (Bytef *)outbuff; stream.zalloc = (alloc_func) NULL; stream.zfree = (free_func) NULL; stream.opaque = (voidpf) 0; stream.avail_out = FILEBUFF; zret = inflateInit(&stream); if(zret != Z_OK) { cli_errmsg("scancws: inflateInit() failed\n"); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } do { if(stream.avail_in == 0) { stream.next_in = (Bytef *)inbuff; ret = fmap_readn(map, inbuff, offset, FILEBUFF); if(ret < 0) { cli_errmsg("scancws: Error reading SWF file\n"); close(fd); inflateEnd(&stream); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } if(!ret) break; stream.avail_in = ret; offset += ret; } zret = inflate(&stream, Z_SYNC_FLUSH); count = FILEBUFF - stream.avail_out; if(count) { if(cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS) break; if(cli_writen(fd, outbuff, count) != count) { cli_errmsg("scancws: Can't write to file %s\n", tmpname); inflateEnd(&stream); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } outsize += count; } stream.next_out = (Bytef *)outbuff; stream.avail_out = FILEBUFF; } while(zret == Z_OK); zend = inflateEnd(&stream); if((zret != Z_STREAM_END && zret != Z_OK) || zend != Z_OK) { /* * outsize is initialized to 8, it being 8 here means that we couldn't even read a single byte. * If outsize > 8, then we have data. Let's scan what we have. */ if (outsize == 8) { cli_infomsg(ctx, "scancws: Error decompressing SWF file. No data decompressed.\n"); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } cli_infomsg(ctx, "scancws: Error decompressing SWF file. Scanning what was decompressed.\n"); } cli_dbgmsg("SWF: Decompressed[zlib] to %s, size %d\n", tmpname, outsize); /* check if declared output size matches actual output size */ if (hdr->filesize != outsize) { cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n", hdr->filesize, (long long unsigned)outsize); } else { cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n", hdr->filesize, (long long unsigned)outsize); } ret = cli_magic_scandesc(fd, ctx); close(fd); if(!ctx->engine->keeptmp) { if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } } free(tmpname); return ret; }
static int scanzws(cli_ctx *ctx, struct swf_file_hdr *hdr) { struct CLI_LZMA lz; unsigned char inbuff[FILEBUFF], outbuff[FILEBUFF]; fmap_t *map = *ctx->fmap; /* strip off header */ off_t offset = 8; uint32_t d_insize; size_t outsize = 8; int ret, lret, count; char *tmpname; int fd; if((ret = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_errmsg("scanzws: Can't generate temporary file\n"); return ret; } hdr->signature[0] = 'F'; if(cli_writen(fd, hdr, sizeof(struct swf_file_hdr)) != sizeof(struct swf_file_hdr)) { cli_errmsg("scanzws: Can't write to file %s\n", tmpname); close(fd); if(cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } /* read 4 bytes (for compressed 32-bit filesize) [not used for LZMA] */ if (fmap_readn(map, &d_insize, offset, sizeof(d_insize)) != sizeof(d_insize)) { cli_errmsg("scanzws: Error reading SWF file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EREAD; } offset += sizeof(d_insize); /* check if declared input size matches actual output size */ /* map->len = header (8 bytes) + d_insize (4 bytes) + flags (5 bytes) + compressed stream */ if (d_insize != (map->len - 17)) { cli_warnmsg("SWF: declared input length != compressed stream size, %u != %llu\n", d_insize, (long long unsigned)(map->len - 17)); } else { cli_dbgmsg("SWF: declared input length == compressed stream size, %u == %llu\n", d_insize, (long long unsigned)(map->len - 17)); } /* first buffer required for initializing LZMA */ ret = fmap_readn(map, inbuff, offset, FILEBUFF); if (ret < 0) { cli_errmsg("scanzws: Error reading SWF file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } /* nothing written, likely truncated */ if (!ret) { cli_errmsg("scanzws: possibly truncated file\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EFORMAT; } offset += ret; memset(&lz, 0, sizeof(lz)); lz.next_in = inbuff; lz.next_out = outbuff; lz.avail_in = ret; lz.avail_out = FILEBUFF; lret = cli_LzmaInit(&lz, hdr->filesize); if (lret != LZMA_RESULT_OK) { cli_errmsg("scanzws: LzmaInit() failed\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } while (lret == LZMA_RESULT_OK) { if (lz.avail_in == 0) { lz.next_in = inbuff; ret = fmap_readn(map, inbuff, offset, FILEBUFF); if (ret < 0) { cli_errmsg("scanzws: Error reading SWF file\n"); cli_LzmaShutdown(&lz); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } if (!ret) break; lz.avail_in = ret; offset += ret; } lret = cli_LzmaDecode(&lz); count = FILEBUFF - lz.avail_out; if (count) { if (cli_checklimits("SWF", ctx, outsize + count, 0, 0) != CL_SUCCESS) break; if (cli_writen(fd, outbuff, count) != count) { cli_errmsg("scanzws: Can't write to file %s\n", tmpname); cli_LzmaShutdown(&lz); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EWRITE; } outsize += count; } lz.next_out = outbuff; lz.avail_out = FILEBUFF; } cli_LzmaShutdown(&lz); if (lret != LZMA_STREAM_END && lret != LZMA_RESULT_OK) { /* outsize starts at 8, therefore, if its still 8, nothing was decompressed */ if (outsize == 8) { cli_infomsg(ctx, "scanzws: Error decompressing SWF file. No data decompressed.\n"); close(fd); if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } free(tmpname); return CL_EUNPACK; } cli_infomsg(ctx, "scanzws: Error decompressing SWF file. Scanning what was decompressed.\n"); } cli_dbgmsg("SWF: Decompressed[LZMA] to %s, size %llu\n", tmpname, (long long unsigned)outsize); /* check if declared output size matches actual output size */ if (hdr->filesize != outsize) { cli_warnmsg("SWF: declared output length != inflated stream size, %u != %llu\n", hdr->filesize, (long long unsigned)outsize); } else { cli_dbgmsg("SWF: declared output length == inflated stream size, %u == %llu\n", hdr->filesize, (long long unsigned)outsize); } ret = cli_magic_scandesc(fd, ctx); close(fd); if (!(ctx->engine->keeptmp)) { if (cli_unlink(tmpname)) { free(tmpname); return CL_EUNLINK; } } free(tmpname); return ret; }
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_scanxar(cli_ctx *ctx) { int rc = CL_SUCCESS; unsigned int cksum_fails = 0; unsigned int extract_errors = 0; #if HAVE_LIBXML2 int fd = -1; struct xar_header hdr; fmap_t *map = *ctx->fmap; long length, offset, size, at; int encoding; z_stream strm; char *toc, *tmpname; xmlTextReaderPtr reader = NULL; int a_hash, e_hash; unsigned char *a_cksum = NULL, *e_cksum = NULL; memset(&strm, 0x00, sizeof(z_stream)); /* retrieve xar header */ if (fmap_readn(*ctx->fmap, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scanxar: Invalid header, too short.\n"); return CL_EFORMAT; } hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == XAR_HEADER_MAGIC) { cli_dbgmsg("cli_scanxar: Matched magic\n"); } else { cli_dbgmsg("cli_scanxar: Invalid magic\n"); return CL_EFORMAT; } hdr.size = be16_to_host(hdr.size); hdr.version = be16_to_host(hdr.version); hdr.toc_length_compressed = be64_to_host(hdr.toc_length_compressed); hdr.toc_length_decompressed = be64_to_host(hdr.toc_length_decompressed); hdr.chksum_alg = be32_to_host(hdr.chksum_alg); /* cli_dbgmsg("hdr.magic %x\n", hdr.magic); */ /* cli_dbgmsg("hdr.size %i\n", hdr.size); */ /* cli_dbgmsg("hdr.version %i\n", hdr.version); */ /* cli_dbgmsg("hdr.toc_length_compressed %lu\n", hdr.toc_length_compressed); */ /* cli_dbgmsg("hdr.toc_length_decompressed %lu\n", hdr.toc_length_decompressed); */ /* cli_dbgmsg("hdr.chksum_alg %i\n", hdr.chksum_alg); */ /* Uncompress TOC */ strm.next_in = (unsigned char *)fmap_need_off_once(*ctx->fmap, hdr.size, hdr.toc_length_compressed); if (strm.next_in == NULL) { cli_dbgmsg("cli_scanxar: fmap_need_off_once fails on TOC.\n"); return CL_EREAD; } strm.avail_in = hdr.toc_length_compressed; toc = cli_malloc(hdr.toc_length_decompressed+1); if (toc == NULL) { cli_dbgmsg("cli_scanxar: cli_malloc fails on TOC decompress buffer.\n"); return CL_EMEM; } toc[hdr.toc_length_decompressed] = '\0'; strm.avail_out = hdr.toc_length_decompressed; strm.next_out = (unsigned char *)toc; rc = inflateInit(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateInit error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflate(&strm, Z_SYNC_FLUSH); if (rc != Z_OK && rc != Z_STREAM_END) { cli_dbgmsg("cli_scanxar:inflate error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflateEnd(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateEnd error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } /* cli_dbgmsg("cli_scanxar: TOC xml:\n%s\n", toc); */ /* printf("cli_scanxar: TOC xml:\n%s\n", toc); */ /* cli_dbgmsg("cli_scanxar: TOC end:\n"); */ /* printf("cli_scanxar: TOC end:\n"); */ /* scan the xml */ cli_dbgmsg("cli_scanxar: scanning xar TOC xml in memory.\n"); rc = cli_mem_scandesc(toc, hdr.toc_length_decompressed, ctx); if (rc != CL_SUCCESS) { if (rc != CL_VIRUS || !SCAN_ALL) goto exit_toc; } /* make a file to leave if --leave-temps in effect */ if(ctx->engine->keeptmp) { if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't create temporary file for TOC.\n"); goto exit_toc; } if (cli_writen(fd, toc, hdr.toc_length_decompressed) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing TOC.\n"); rc = CL_EWRITE; xar_cleanup_temp_file(ctx, fd, tmpname); goto exit_toc; } rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_toc; } reader = xmlReaderForMemory(toc, hdr.toc_length_decompressed, "noname.xml", NULL, 0); if (reader == NULL) { cli_dbgmsg("cli_scanxar: xmlReaderForMemory error for TOC\n"); goto exit_toc; } rc = xar_scan_subdocuments(reader, ctx); if (rc != CL_SUCCESS) { cli_dbgmsg("xar_scan_subdocuments returns %i.\n", rc); goto exit_reader; } /* Walk the TOC XML and extract files */ fd = -1; tmpname = NULL; while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding, &a_cksum, &a_hash, &e_cksum, &e_hash))) { int do_extract_cksum = 1; unsigned char * blockp; void *a_sc, *e_sc; void *a_mc, *e_mc; void *a_hash_ctx, *e_hash_ctx; char result[SHA1_HASH_SIZE]; char * expected; /* clean up temp file from previous loop iteration */ if (fd > -1 && tmpname) { rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_reader; } at = offset + hdr.toc_length_compressed + hdr.size; if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't generate temporary file.\n"); goto exit_reader; } cli_dbgmsg("cli_scanxar: decompress into temp file:\n%s, size %li,\n" "from xar heap offset %li length %li\n", tmpname, size, offset, length); a_hash_ctx = xar_hash_init(a_hash, &a_sc, &a_mc); e_hash_ctx = xar_hash_init(e_hash, &e_sc, &e_mc); switch (encoding) { case CL_TYPE_GZ: /* inflate gzip directly because file segments do not contain magic */ memset(&strm, 0, sizeof(strm)); if ((rc = inflateInit(&strm)) != Z_OK) { cli_dbgmsg("cli_scanxar: InflateInit failed: %d\n", rc); rc = CL_EFORMAT; extract_errors++; break; } while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { unsigned long avail_in; void * next_in; unsigned int bytes = MIN(map->len - at, map->pgsz); bytes = MIN(length, bytes); if(!(strm.next_in = next_in = (void*)fmap_need_off_once(map, at, bytes))) { cli_dbgmsg("cli_scanxar: Can't read %u bytes @ %lu.\n", bytes, (long unsigned)at); inflateEnd(&strm); rc = CL_EREAD; goto exit_tmpfile; } at += bytes; strm.avail_in = avail_in = bytes; do { int inf, outsize = 0; unsigned char buff[FILEBUFF]; strm.avail_out = sizeof(buff); strm.next_out = buff; inf = inflate(&strm, Z_SYNC_FLUSH); if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) { cli_dbgmsg("cli_scanxar: inflate error %i %s.\n", inf, strm.msg?strm.msg:""); rc = CL_EFORMAT; extract_errors++; break; } bytes = sizeof(buff) - strm.avail_out; xar_hash_update(e_hash_ctx, buff, bytes, e_hash); if (cli_writen(fd, buff, bytes) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error file %s.\n", tmpname); inflateEnd(&strm); rc = CL_EWRITE; goto exit_tmpfile; } outsize += sizeof(buff) - strm.avail_out; if (cli_checklimits("cli_scanxar", ctx, outsize, 0, 0) != CL_CLEAN) { break; } if (inf == Z_STREAM_END) { break; } } while (strm.avail_out == 0); if (rc != CL_SUCCESS) break; avail_in -= strm.avail_in; xar_hash_update(a_hash_ctx, next_in, avail_in, a_hash); } inflateEnd(&strm); break; case CL_TYPE_7Z: #define CLI_LZMA_OBUF_SIZE 1024*1024 #define CLI_LZMA_HDR_SIZE LZMA_PROPS_SIZE+8 #define CLI_LZMA_IBUF_SIZE CLI_LZMA_OBUF_SIZE>>2 /* estimated compression ratio 25% */ { struct CLI_LZMA lz; unsigned long in_remaining = length; unsigned long out_size = 0; unsigned char * buff = __lzma_wrap_alloc(NULL, CLI_LZMA_OBUF_SIZE); int lret; memset(&lz, 0, sizeof(lz)); if (buff == NULL) { cli_dbgmsg("cli_scanxar: memory request for lzma decompression buffer fails.\n"); rc = CL_EMEM; goto exit_tmpfile; } blockp = (void*)fmap_need_off_once(map, at, CLI_LZMA_HDR_SIZE); if (blockp == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); goto exit_tmpfile; } lz.next_in = blockp; lz.avail_in = CLI_LZMA_HDR_SIZE; xar_hash_update(a_hash_ctx, blockp, CLI_LZMA_HDR_SIZE, a_hash); lret = cli_LzmaInit(&lz, 0); if (lret != LZMA_RESULT_OK) { cli_dbgmsg("cli_scanxar: cli_LzmaInit() fails: %i.\n", lret); rc = CL_EFORMAT; __lzma_wrap_free(NULL, buff); extract_errors++; break; } at += CLI_LZMA_HDR_SIZE; in_remaining -= CLI_LZMA_HDR_SIZE; while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { SizeT avail_in; SizeT avail_out; void * next_in; unsigned long in_consumed; lz.next_out = buff; lz.avail_out = CLI_LZMA_OBUF_SIZE; lz.avail_in = avail_in = MIN(CLI_LZMA_IBUF_SIZE, in_remaining); lz.next_in = next_in = (void*)fmap_need_off_once(map, at, lz.avail_in); if (lz.next_in == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno: %s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); goto exit_tmpfile; } lret = cli_LzmaDecode(&lz); if (lret != LZMA_RESULT_OK && lret != LZMA_STREAM_END) { cli_dbgmsg("cli_scanxar: cli_LzmaDecode() fails: %i.\n", lret); rc = CL_EFORMAT; extract_errors++; break; } in_consumed = avail_in - lz.avail_in; in_remaining -= in_consumed; at += in_consumed; avail_out = CLI_LZMA_OBUF_SIZE - lz.avail_out; if (avail_out == 0) cli_dbgmsg("cli_scanxar: cli_LzmaDecode() produces no output for " "avail_in %lu, avail_out %lu.\n", avail_in, avail_out); xar_hash_update(a_hash_ctx, next_in, in_consumed, a_hash); xar_hash_update(e_hash_ctx, buff, avail_out, e_hash); /* Write a decompressed block. */ /* cli_dbgmsg("Writing %li bytes to LZMA decompress temp file, " */ /* "consumed %li of %li available compressed bytes.\n", */ /* avail_out, in_consumed, avail_in); */ if (cli_writen(fd, buff, avail_out) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing lzma temp file for %li bytes.\n", avail_out); __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); rc = CL_EWRITE; goto exit_tmpfile; } /* Check file size limitation. */ out_size += avail_out; if (cli_checklimits("cli_scanxar", ctx, out_size, 0, 0) != CL_CLEAN) { break; } if (lret == LZMA_STREAM_END) break; } cli_LzmaShutdown(&lz); __lzma_wrap_free(NULL, buff); } break; case CL_TYPE_ANY: default: case CL_TYPE_BZ: case CL_TYPE_XZ: /* for uncompressed, bzip2, xz, and unknown, just pull the file, cli_magic_scandesc does the rest */ do_extract_cksum = 0; { unsigned long write_len; if (ctx->engine->maxfilesize) write_len = MIN((size_t)(ctx->engine->maxfilesize), (size_t)length); else write_len = length; if (!(blockp = (void*)fmap_need_off_once(map, at, length))) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; goto exit_tmpfile; } xar_hash_update(a_hash_ctx, blockp, length, a_hash); if (cli_writen(fd, blockp, write_len) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error %li bytes @ %li.\n", length, at); rc = CL_EWRITE; goto exit_tmpfile; } /*break;*/ } } if (rc == CL_SUCCESS) { xar_hash_final(a_hash_ctx, result, a_hash); if (a_cksum != NULL) { expected = cli_hex2str((char *)a_cksum); if (xar_hash_check(a_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: archived-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: archived-checksum matched.\n"); } free(expected); } if (e_cksum != NULL) { if (do_extract_cksum) { xar_hash_final(e_hash_ctx, result, e_hash); expected = cli_hex2str((char *)e_cksum); if (xar_hash_check(e_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: extracted-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: extracted-checksum matched.\n"); } free(expected); } } rc = cli_magic_scandesc(fd, ctx); if (rc != CL_SUCCESS) { if (rc == CL_VIRUS) { cli_dbgmsg("cli_scanxar: Infected with %s\n", cli_get_last_virus(ctx)); if (!SCAN_ALL) goto exit_tmpfile; } else if (rc != CL_BREAK) { cli_dbgmsg("cli_scanxar: cli_magic_scandesc error %i\n", rc); goto exit_tmpfile; } } } if (a_cksum != NULL) { xmlFree(a_cksum); a_cksum = NULL; } if (e_cksum != NULL) { xmlFree(e_cksum); e_cksum = NULL; } } exit_tmpfile: xar_cleanup_temp_file(ctx, fd, tmpname); exit_reader: if (a_cksum != NULL) xmlFree(a_cksum); if (e_cksum != NULL) xmlFree(e_cksum); xmlTextReaderClose(reader); xmlFreeTextReader(reader); exit_toc: free(toc); if (rc == CL_BREAK) rc = CL_SUCCESS; #else cli_dbgmsg("cli_scanxar: can't scan xar files, need libxml2.\n"); #endif if (cksum_fails + extract_errors != 0) { cli_warnmsg("cli_scanxar: %u checksum errors and %u extraction errors, use --debug for more info.\n", cksum_fails, extract_errors); } return rc; }
static int nsis_unpack_next(struct nsis_st *n, cli_ctx *ctx) { unsigned char *ibuf; uint32_t size, loops; int ret; unsigned char obuf[BUFSIZ]; if (n->eof) { cli_dbgmsg("NSIS: extraction complete\n"); return CL_BREAK; } if (ctx->limits && ctx->limits->maxfiles && n->fno >= ctx->limits->maxfiles) { cli_dbgmsg("NSIS: Files limit reached (max: %u)\n", ctx->limits->maxfiles); return CL_EMAXFILES; } if (n->fno) snprintf(n->ofn, 1023, "%s/content.%.3u", n->dir, n->fno); else snprintf(n->ofn, 1023, "%s/headers", n->dir); n->fno++; if ((n->ofd=open(n->ofn, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600))==-1) { cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn); return CL_EIO; } if (!n->solid) { if (cli_readn(n->ifd, &size, 4)!=4) { cli_dbgmsg("NSIS: reached EOF - extraction complete\n"); close(n->ofd); return CL_BREAK; } if (n->asz==4) { cli_dbgmsg("NSIS: reached CRC - extraction complete\n"); close(n->ofd); return CL_BREAK; } loops = EC32(size); if (!(size = (loops&~0x80000000))) { cli_dbgmsg("NSIS: empty file found\n"); return CL_SUCCESS; } if (n->asz <4 || size > n->asz-4) { cli_dbgmsg("NSIS: next file is outside the archive\n"); close(n->ofd); return CL_BREAK; } n->asz -= size+4; if (ctx->limits && ctx->limits->maxfilesize && size > ctx->limits->maxfilesize) { cli_dbgmsg("NSIS: Skipping file due to size limit (%u, max: %lu)\n", size, ctx->limits->maxfilesize); close(n->ofd); if (lseek(n->ifd, size, SEEK_CUR)==-1) return CL_EIO; return CL_EMAXSIZE; } if (!(ibuf= (unsigned char *) cli_malloc(size))) { cli_dbgmsg("NSIS: out of memory"__AT__"\n"); close(n->ofd); return CL_EMEM; } if (cli_readn(n->ifd, ibuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", size); free(ibuf); close(n->ofd); return CL_EIO; } if (loops==size) { if (cli_writen(n->ofd, ibuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EIO; } } else { if ((ret=nsis_init(n))!=CL_SUCCESS) { cli_dbgmsg("NSIS: decompressor init failed"__AT__"\n"); free(ibuf); close(n->ofd); return ret; } n->nsis.avail_in = size; n->nsis.next_in = ibuf; n->nsis.next_out = obuf; n->nsis.avail_out = BUFSIZ; loops=0; while ((ret=nsis_decomp(n))==CL_SUCCESS) { if ((size = n->nsis.next_out - obuf)) { if (cli_writen(n->ofd, obuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EIO; } n->nsis.next_out = obuf; n->nsis.avail_out = BUFSIZ; loops=0; if (ctx->limits && ctx->limits->maxfilesize && size > ctx->limits->maxfilesize) { cli_dbgmsg("NSIS: Skipping file due to size limit (%u, max: %lu)\n", size, ctx->limits->maxfilesize); free(ibuf); close(n->ofd); nsis_shutdown(n); return CL_EMAXSIZE; } } else if (++loops > 10) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret != CL_BREAK) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EFORMAT; } if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EIO; } nsis_shutdown(n); } free(ibuf); return CL_SUCCESS; } else { if (!n->freeme) { if ((ret=nsis_init(n))!=CL_SUCCESS) { cli_dbgmsg("NSIS: decompressor init failed\n"); close(n->ofd); return ret; } if (!(n->freeme= (unsigned char *) cli_malloc(n->asz))) { cli_dbgmsg("NSIS: out of memory\n"); close(n->ofd); return CL_EMEM; } if (cli_readn(n->ifd, n->freeme, n->asz) != (ssize_t) n->asz) { cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", n->asz); close(n->ofd); return CL_EIO; } n->nsis.next_in = n->freeme; n->nsis.avail_in = n->asz; } if (n->nsis.avail_in<=4) { cli_dbgmsg("NSIS: extraction complete\n"); close(n->ofd); return CL_BREAK; } n->nsis.next_out = obuf; n->nsis.avail_out = 4; loops = 0; while ((ret=nsis_decomp(n))==CL_SUCCESS) { if (n->nsis.next_out - obuf == 4) break; if (++loops > 20) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret != CL_SUCCESS) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); close(n->ofd); return CL_EFORMAT; } size=cli_readint32(obuf); if (ctx->limits && ctx->limits->maxfilesize && size > ctx->limits->maxfilesize) { cli_dbgmsg("NSIS: Breaking out due to filesize limit (%u, max: %lu) in solid archive\n", size, ctx->limits->maxfilesize); close(n->ofd); return CL_EFORMAT; } n->nsis.next_out = obuf; n->nsis.avail_out = MIN(BUFSIZ,size); loops = 0; while (size && (ret=nsis_decomp(n))==CL_SUCCESS) { unsigned int wsz; if ((wsz = n->nsis.next_out - obuf)) { if (cli_writen(n->ofd, obuf, wsz) != (ssize_t) wsz) { close(n->ofd); return CL_EIO; } size-=wsz; n->nsis.next_out = obuf; n->nsis.avail_out = MIN(size,BUFSIZ); } else if ( ++loops > 20 ) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret == CL_BREAK) { if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { close(n->ofd); return CL_EIO; } n->eof=1; } else if (ret != CL_SUCCESS) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); close(n->ofd); return CL_EFORMAT; } return CL_SUCCESS; } }
static int rtf_object_process(struct rtf_state* state, const unsigned char* input,const size_t len) { struct rtf_object_data* data = state->cb_data; unsigned char outdata[BUFF_SIZE]; const unsigned char* out_data; size_t out_cnt = 0; size_t i; int ret; if(!data || !len) return 0; if(data->has_partial) { for(i=0;i<len && !isxdigit(input[i]);i++) ; if(i<len) { outdata[out_cnt++] = data->partial | hextable[input[i++]]; data->has_partial = 0; } else return 0; } else i = 0; for(;i<len;i++) { if(isxdigit(input[i])) { const unsigned char byte = hextable[ input[i++] ] << 4; while(i<len && !isxdigit(input[i])) i++; if(i == len) { data->partial = byte; data->has_partial = 1; break; } outdata[out_cnt++] = byte | hextable[ input[i] ]; } } out_data = outdata; while(out_data && out_cnt) { switch(data->internal_state) { case WAIT_MAGIC: { cli_dbgmsg("RTF: waiting for magic\n"); for(i=0; i<out_cnt && data->bread < rtf_data_magic_len; i++, data->bread++) if(rtf_data_magic[data->bread] != out_data[i]) { cli_dbgmsg("Warning: rtf objdata magic number not matched, expected:%d, got: %d, at pos:%lu\n",rtf_data_magic[i],out_data[i], (unsigned long int) data->bread); } out_cnt -= i; if(data->bread == rtf_data_magic_len) { out_data += i; data->bread = 0; data->internal_state = WAIT_DESC_LEN; } break; } case WAIT_DESC_LEN: { if(data->bread == 0) data->desc_len = 0; for(i=0; i<out_cnt && data->bread < 4; i++,data->bread++) data->desc_len |= ((size_t)out_data[i]) << (data->bread*8); out_cnt -= i; if(data->bread == 4) { out_data += i; data->bread=0; if(data->desc_len > 64) { cli_dbgmsg("Description length too big (%lu), showing only 64 bytes of it\n", (unsigned long int) data->desc_len); data->desc_name = cli_malloc(65); } else data->desc_name = cli_malloc(data->desc_len+1); if(!data->desc_name) { cli_errmsg("rtf_object_process: Unable to allocate memory for data->desc_name\n"); return CL_EMEM; } data->internal_state = WAIT_DESC; cli_dbgmsg("RTF: description length:%lu\n", (unsigned long int) data->desc_len); } break; } case WAIT_DESC:{ cli_dbgmsg("RTF: in WAIT_DESC\n"); for(i=0;i<out_cnt && data->bread < data->desc_len && data->bread < 64;i++, data->bread++) data->desc_name[data->bread] = out_data[i]; out_cnt -= i; out_data += i; if(data->bread < data->desc_len && data->bread < 64) { cli_dbgmsg("RTF: waiting for more data(1)\n"); return 0;/* wait for more data */ } data->desc_name[data->bread] = '\0'; if(data->desc_len - data->bread > out_cnt) { data->desc_len -= out_cnt; cli_dbgmsg("RTF: waiting for more data(2)\n"); return 0;/* wait for more data */ } out_cnt -= data->desc_len - data->bread; if(data->bread >= data->desc_len) { out_data += data->desc_len - data->bread; data->bread = 0; cli_dbgmsg("Preparing to dump rtf embedded object, description:%s\n",data->desc_name); free(data->desc_name); data->desc_name = NULL; data->internal_state = WAIT_ZERO; } break; } case WAIT_ZERO:{ if(out_cnt < 8-data->bread) { out_cnt = 0; data->bread += out_cnt; } else { out_cnt -= 8-data->bread; data->bread = 8; } if(data->bread == 8) { out_data += 8; data->bread = 0; cli_dbgmsg("RTF: next state: wait_data_size\n"); data->internal_state = WAIT_DATA_SIZE; } break; } case WAIT_DATA_SIZE: { cli_dbgmsg("RTF: in WAIT_DATA_SIZE\n"); if(data->bread == 0) data->desc_len = 0; for(i=0; i<out_cnt && data->bread < 4; i++,data->bread++) data->desc_len |= ((size_t)out_data[i]) << (8*data->bread); out_cnt -= i; if(data->bread == 4) { out_data += i; data->bread=0; cli_dbgmsg("Dumping rtf embedded object of size:%lu\n", (unsigned long int) data->desc_len); if((ret = cli_gentempfd(data->tmpdir, &data->name, &data->fd))) return ret; data->internal_state = DUMP_DATA; cli_dbgmsg("RTF: next state: DUMP_DATA\n"); } break; } case DUMP_DATA: { ssize_t out_want = out_cnt < data->desc_len ? out_cnt : data->desc_len; if(!data->bread) { if(out_data[0] != 0xd0 || out_data[1]!=0xcf) { /* this is not an ole2 doc, but some ole (stream?) to be * decoded by cli_decode_ole_object*/ char out[4]; data->bread = 1;/* flag to indicate this needs to be scanned with cli_decode_ole_object*/ cli_writeint32(out,data->desc_len); if(cli_writen(data->fd,out,4)!=4) return CL_EWRITE; } else data->bread = 2; } data->desc_len -= out_want; if(cli_writen(data->fd,out_data,out_want) != out_want) { return CL_EWRITE; } out_data += out_want; out_cnt -= out_want; if(!data->desc_len) { int rc; if(( rc = decode_and_scan(data, data->ctx) )) return rc; data->bread=0; data->internal_state = WAIT_MAGIC; } break; } case DUMP_DISCARD: default: out_cnt = 0; ; } } return 0; }
int wwunpack(uint8_t *exe, uint32_t exesz, uint8_t *wwsect, struct cli_exe_section *sects, uint16_t scount, uint32_t pe, int desc) { uint8_t *structs = wwsect + 0x2a1, *compd, *ccur, *unpd, *ucur, bc; uint32_t src, srcend, szd, bt, bits; int error=0, i; cli_dbgmsg("in wwunpack\n"); while (1) { if (!CLI_ISCONTAINED(wwsect, sects[scount].rsz, structs, 17)) { cli_dbgmsg("WWPack: Array of structs out of section\n"); break; } src = sects[scount].rva - cli_readint32(structs); /* src delta / dst delta - not used / dwords / end of src */ structs+=8; szd = cli_readint32(structs) * 4; structs+=4; srcend = cli_readint32(structs); structs+=4; unpd = ucur = exe+src+srcend+4-szd; if (!szd || !CLI_ISCONTAINED(exe, exesz, unpd, szd)) { cli_dbgmsg("WWPack: Compressed data out of file\n"); break; } cli_dbgmsg("WWP: src: %x, szd: %x, srcend: %x - %x\n", src, szd, srcend, srcend+4-szd); if (!(compd = cli_malloc(szd))) { cli_dbgmsg("WWPack: Unable to allocate memory for compd\n"); break; } memcpy(compd, unpd, szd); memset(unpd, -1, szd); /*FIXME*/ ccur=compd; RESEED; while(!error) { uint32_t backbytes, backsize; uint8_t saved; BIT; if (!bits) { /* BYTE copy */ if(ccur-compd>=szd || !CLI_ISCONTAINED(exe, exesz, ucur, 1)) error=1; else *ucur++=*ccur++; continue; } BITS(2); if(bits==3) { /* WORD backcopy */ uint8_t shifted, subbed = 31; BITS(2); shifted = bits + 5; if(bits>=2) { shifted++; subbed += 0x80; } backbytes = (1<<shifted)-subbed; /* 1h, 21h, 61h, 161h */ BITS(shifted); /* 5, 6, 8, 9 */ if(error || bits == 0x1ff) break; backbytes+=bits; if(!CLI_ISCONTAINED(exe, exesz, ucur, 2) || !CLI_ISCONTAINED(exe, exesz, ucur-backbytes, 2)) { error=1; } else { ucur[0]=*(ucur-backbytes); ucur[1]=*(ucur-backbytes+1); ucur+=2; } continue; } /* BLOCK backcopy */ saved = bits; /* cmp al, 1 / pushf */ BITS(3); if (bits<6) { backbytes = bits; switch(bits) { case 4: /* 10,11 */ backbytes++; case 3: /* 8,9 */ BIT; backbytes+=bits; case 0: case 1: case 2: /* 5,6,7 */ backbytes+=5; break; case 5: /* 12 */ backbytes=12; break; } BITS(backbytes); bits+=(1<<backbytes)-31; } else if(bits==6) { BITS(0x0e); bits+=0x1fe1; } else { BITS(0x0f); bits+=0x5fe1; } backbytes = bits; /* popf / jb */ if (!saved) { BIT; if(!bits) { BIT; bits+=5; } else { BITS(3); if(bits) { bits+=6; } else { BITS(4); if(bits) { bits+=13; } else { uint8_t cnt = 4; uint16_t shifted = 0x0d; do { if(cnt==7) { cnt = 0x0e; shifted = 0; break; } shifted=((shifted+2)<<1)-1; BIT; cnt++; } while(!bits); BITS(cnt); bits+=shifted; } } } backsize = bits; } else { backsize = saved+2; } if(!CLI_ISCONTAINED(exe, exesz, ucur, backsize) || !CLI_ISCONTAINED(exe, exesz, ucur-backbytes, backsize)) error=1; else while(backsize--) { *ucur=*(ucur-backbytes); ucur++; } } free(compd); if(error) { cli_dbgmsg("WWPack: decompression error\n"); break; } if (error || !*structs++) break; } if(!error) { exe[pe+6]=(uint8_t)scount; exe[pe+7]=(uint8_t)(scount>>8); cli_writeint32(&exe[pe+0x28], cli_readint32(wwsect+0x295)+sects[scount].rva+0x299); cli_writeint32(&exe[pe+0x50], cli_readint32(&exe[pe+0x50])-sects[scount].vsz); structs = &exe[(0xffff&cli_readint32(&exe[pe+0x14]))+pe+0x18]; for(i=0 ; i<scount ; i++) { if (!CLI_ISCONTAINED(exe, exesz, structs, 0x28)) { cli_dbgmsg("WWPack: structs pointer out of bounds\n"); return CL_EFORMAT; } cli_writeint32(structs+8, sects[i].vsz); cli_writeint32(structs+12, sects[i].rva); cli_writeint32(structs+16, sects[i].vsz); cli_writeint32(structs+20, sects[i].rva); structs+=0x28; } if (!CLI_ISCONTAINED(exe, exesz, structs, 0x28)) { cli_dbgmsg("WWPack: structs pointer out of bounds\n"); return CL_EFORMAT; } memset(structs, 0, 0x28); error = (uint32_t)cli_writen(desc, exe, exesz)!=exesz; }
int yc_decrypt(char *fbuf, unsigned int filesize, struct cli_exe_section *sections, unsigned int sectcount, uint32_t peoffset, int desc) { uint32_t ycsect = sections[sectcount].raw; unsigned int i; struct pe_image_file_hdr *pe = (struct pe_image_file_hdr*) (fbuf + peoffset); char *sname = (char *)pe + EC16(pe->SizeOfOptionalHeader) + 0x18; /* First layer (decryptor of the section decryptor) in last section Start offset for analyze: Start of yC Section + 0x93 End offset for analyze: Start of yC Section + 0xC3 Lenght to decrypt - ECX = 0xB97 */ cli_dbgmsg("yC: decrypting decryptor on sect %d\n", sectcount); if (yc_poly_emulator(fbuf + ycsect + 0x93, fbuf + ycsect + 0xc6 ,0xB97)) return 1; filesize-=sections[sectcount].ursz; /* Second layer (decryptor of the sections) in last section Start offset for analyze: Start of yC Section + 0x457 End offset for analyze: Start of yC Section + 0x487 Lenght to decrypt - ECX = Raw Size of Section */ /* Loop through all sections and decrypt them... */ for(i=0;i<sectcount;i++) { uint32_t name = (uint32_t) cli_readint32(sname+i*0x28); if ( !sections[i].raw || !sections[i].rsz || name == 0x63727372 || /* rsrc */ name == 0x7273722E || /* .rsr */ name == 0x6F6C6572 || /* relo */ name == 0x6C65722E || /* .rel */ name == 0x6164652E || /* .eda */ name == 0x6164722E || /* .rda */ name == 0x6164692E || /* .ida */ name == 0x736C742E || /* .tls */ (name&0xffff) == 0x4379 /* yC */ ) continue; cli_dbgmsg("yC: decrypting sect%d\n",i); if (yc_poly_emulator(fbuf + ycsect + 0x457, fbuf + sections[i].raw, sections[i].ursz)) return 1; } /* Remove yC section */ pe->NumberOfSections=EC16(sectcount); /* Remove IMPORT_DIRECTORY information */ memset((char *)pe + sizeof(struct pe_image_file_hdr) + 0x68, 0, 8); /* OEP resolving */ /* OEP = DWORD PTR [ Start of yC section+ A0F] */ cli_writeint32((char *)pe + sizeof(struct pe_image_file_hdr) + 16, cli_readint32(fbuf + ycsect + 0xa0f)); /* Fix SizeOfImage */ cli_writeint32((char *)pe + sizeof(struct pe_image_file_hdr) + 0x38, cli_readint32((char *)pe + sizeof(struct pe_image_file_hdr) + 0x38) - sections[sectcount].vsz); if (cli_writen(desc, fbuf, filesize)==-1) { cli_dbgmsg("yC: Cannot write unpacked file\n"); return 1; } return 0; }
/* Stripe handling: ADC block (type 0x80000004) */ static int dmg_stripe_adc(cli_ctx *ctx, int fd, uint32_t index, struct dmg_mish_with_stripes *mish_set) { int ret = CL_CLEAN, adcret; adc_stream strm; size_t off = mish_set->stripes[index].dataOffset; size_t len = mish_set->stripes[index].dataLength; uint64_t size_so_far = 0; uint64_t expected_len = mish_set->stripes[index].sectorCount * DMG_SECTOR_SIZE; uint8_t obuf[BUFSIZ]; cli_dbgmsg("dmg_stripe_adc: stripe " STDu32 " initial len " STDu64 " expected len " STDu64 "\n", index, (uint64_t)len, (uint64_t)expected_len); if (len == 0) return CL_CLEAN; memset(&strm, 0, sizeof(strm)); strm.next_in = (uint8_t *)fmap_need_off_once(*ctx->fmap, off, len); if (!strm.next_in) { cli_warnmsg("dmg_stripe_adc: fmap need failed on stripe " STDu32 "\n", index); return CL_EMAP; } strm.avail_in = len; strm.next_out = obuf; strm.avail_out = sizeof(obuf); adcret = adc_decompressInit(&strm); if(adcret != ADC_OK) { cli_warnmsg("dmg_stripe_adc: adc_decompressInit failed\n"); return CL_EMEM; } while(adcret == ADC_OK) { int written; if (size_so_far > expected_len) { cli_warnmsg("dmg_stripe_adc: expected size exceeded!\n"); adc_decompressEnd(&strm); return CL_EFORMAT; } adcret = adc_decompress(&strm); switch(adcret) { case ADC_OK: if(strm.avail_out == 0) { if ((written=cli_writen(fd, obuf, sizeof(obuf)))!=sizeof(obuf)) { cli_errmsg("dmg_stripe_adc: failed write to output file\n"); adc_decompressEnd(&strm); return CL_EWRITE; } size_so_far += written; strm.next_out = obuf; strm.avail_out = sizeof(obuf); } continue; case ADC_STREAM_END: default: written = sizeof(obuf) - strm.avail_out; if (written) { if ((cli_writen(fd, obuf, written))!=written) { cli_errmsg("dmg_stripe_adc: failed write to output file\n"); adc_decompressEnd(&strm); return CL_EWRITE; } size_so_far += written; strm.next_out = obuf; strm.avail_out = sizeof(obuf); } if (adcret == ADC_STREAM_END) break; cli_dbgmsg("dmg_stripe_adc: after writing " STDu64 " bytes, " "got error %d decompressing stripe " STDu32 "\n", size_so_far, adcret, index); adc_decompressEnd(&strm); return CL_EFORMAT; } break; } adc_decompressEnd(&strm); cli_dbgmsg("dmg_stripe_adc: stripe " STDu32 " actual len " STDu64 " expected len " STDu64 "\n", index, size_so_far, expected_len); return CL_CLEAN; }