mirror of
https://github.com/Cisco-Talos/clamav.git
synced 2026-05-07 23:26:27 -04:00
Introduce cli_map_scandesc to scan a portion of the existing file
And switch CPIO, MACHO, and SWF to use it. Now they no longer need to dump a tempfile and remap. To investigate if it is possible to do this with TAR.
This commit is contained in:
@@ -157,7 +157,7 @@ int cli_scancpio_old(cli_ctx *ctx)
|
||||
if(ret == CL_EMAXFILES) {
|
||||
return ret;
|
||||
} else if(ret == CL_SUCCESS) {
|
||||
ret = cli_dumpscan(*ctx->fmap, pos, filesize, ctx);
|
||||
ret = cli_map_scandesc(*ctx->fmap, pos, filesize, ctx);
|
||||
if(ret == CL_VIRUS)
|
||||
return ret;
|
||||
}
|
||||
@@ -234,7 +234,7 @@ int cli_scancpio_odc(cli_ctx *ctx)
|
||||
if(ret == CL_EMAXFILES) {
|
||||
return ret;
|
||||
} else if(ret == CL_SUCCESS) {
|
||||
ret = cli_dumpscan(*ctx->fmap, pos, filesize, ctx);
|
||||
ret = cli_map_scandesc(*ctx->fmap, pos, filesize, ctx);
|
||||
if(ret == CL_VIRUS)
|
||||
return ret;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ int cli_scancpio_newc(cli_ctx *ctx, int crc)
|
||||
if(ret == CL_EMAXFILES) {
|
||||
return ret;
|
||||
} else if(ret == CL_SUCCESS) {
|
||||
ret = cli_dumpscan(*ctx->fmap, pos, filesize, ctx);
|
||||
ret = cli_map_scandesc(*ctx->fmap, pos, filesize, ctx);
|
||||
if(ret == CL_VIRUS)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -257,7 +257,8 @@ extern cl_fmap_t *cl_fmap_open_handle(void *handle, size_t offset, size_t len,
|
||||
m->pread_cb = pread_cb;
|
||||
m->aging = use_aging;
|
||||
m->offset = offset;
|
||||
m->len = len;
|
||||
m->len = len;/* m->nested_offset + m->len = m->real_len */
|
||||
m->real_len = len;
|
||||
m->pages = pages;
|
||||
m->hdrsz = hdrsz;
|
||||
m->pgsz = pgsz;
|
||||
@@ -451,8 +452,8 @@ static int fmap_readpage(fmap_t *m, unsigned int first_page, unsigned int count,
|
||||
pptr = (char *)m + page * m->pgsz + m->hdrsz;
|
||||
first_page = page;
|
||||
}
|
||||
if((page == m->pages - 1) && (m->len % m->pgsz))
|
||||
readsz += m->len % m->pgsz;
|
||||
if((page == m->pages - 1) && (m->real_len % m->pgsz))
|
||||
readsz += m->real_len % m->pgsz;
|
||||
else
|
||||
readsz += m->pgsz;
|
||||
if(lock) /* lock requested: set paged, lock page and set lock count to 1 */
|
||||
@@ -472,7 +473,9 @@ static const void *handle_need(fmap_t *m, size_t at, size_t len, int lock) {
|
||||
if(!len)
|
||||
return NULL;
|
||||
|
||||
if(!CLI_ISCONTAINED(0, m->len, at, len))
|
||||
at += m->nested_offset;
|
||||
|
||||
if(!CLI_ISCONTAINED(0, m->real_len, at, len))
|
||||
return NULL;
|
||||
|
||||
fmap_aging(m);
|
||||
@@ -519,7 +522,7 @@ static void handle_unneed_off(fmap_t *m, size_t at, size_t len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!CLI_ISCONTAINED(0, m->len, at, len)) {
|
||||
if(!CLI_ISCONTAINED(0, m->real_len, at, len)) {
|
||||
cli_warnmsg("fmap: attempted oof unneed\n");
|
||||
return;
|
||||
}
|
||||
@@ -550,10 +553,10 @@ static const void *handle_need_offstr(fmap_t *m, size_t at, size_t len_hint) {
|
||||
unsigned int i, first_page, last_page;
|
||||
void *ptr = (void *)((char *)m + m->hdrsz + at);
|
||||
|
||||
if(!len_hint || len_hint > m->len - at)
|
||||
len_hint = m->len - at;
|
||||
if(!len_hint || len_hint > m->real_len - at)
|
||||
len_hint = m->real_len - at;
|
||||
|
||||
if(!CLI_ISCONTAINED(0, m->len, at, len_hint))
|
||||
if(!CLI_ISCONTAINED(0, m->real_len, at, len_hint))
|
||||
return NULL;
|
||||
|
||||
fmap_aging(m);
|
||||
@@ -588,9 +591,9 @@ static const void *handle_need_offstr(fmap_t *m, size_t at, size_t len_hint) {
|
||||
static const void *handle_gets(fmap_t *m, char *dst, size_t *at, size_t max_len) {
|
||||
unsigned int i, first_page, last_page;
|
||||
char *src = (void *)((char *)m + m->hdrsz + *at), *endptr = NULL;
|
||||
size_t len = MIN(max_len-1, m->len - *at), fullen = len;
|
||||
size_t len = MIN(max_len-1, m->real_len - *at), fullen = len;
|
||||
|
||||
if(!len || !CLI_ISCONTAINED(0, m->len, *at, len))
|
||||
if(!len || !CLI_ISCONTAINED(0, m->real_len, *at, len))
|
||||
return NULL;
|
||||
|
||||
fmap_aging(m);
|
||||
@@ -650,6 +653,7 @@ extern cl_fmap_t *cl_fmap_open_memory(const void *start, size_t len)
|
||||
}
|
||||
m->data = start;
|
||||
m->len = len;
|
||||
m->real_len = len;
|
||||
m->pgsz = pgsz;
|
||||
m->pages = fmap_align_items(len, pgsz);
|
||||
m->unmap = unmap_none;
|
||||
@@ -665,7 +669,8 @@ static const void *mem_need(fmap_t *m, size_t at, size_t len, int lock) { /* WIN
|
||||
if(!len) {
|
||||
return NULL;
|
||||
}
|
||||
if(!CLI_ISCONTAINED(0, m->len, at, len)) {
|
||||
at += m->nested_offset;
|
||||
if(!CLI_ISCONTAINED(0, m->real_len, at, len)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -677,10 +682,10 @@ static void mem_unneed_off(fmap_t *m, size_t at, size_t len) {}
|
||||
static const void *mem_need_offstr(fmap_t *m, size_t at, size_t len_hint) {
|
||||
char *ptr = (char *)m->data + at;
|
||||
|
||||
if(!len_hint || len_hint > m->len - at)
|
||||
len_hint = m->len - at;
|
||||
if(!len_hint || len_hint > m->real_len - at)
|
||||
len_hint = m->real_len - at;
|
||||
|
||||
if(!CLI_ISCONTAINED(0, m->len, at, len_hint))
|
||||
if(!CLI_ISCONTAINED(0, m->real_len, at, len_hint))
|
||||
return NULL;
|
||||
|
||||
if(memchr(ptr, 0, len_hint))
|
||||
@@ -690,9 +695,9 @@ static const void *mem_need_offstr(fmap_t *m, size_t at, size_t len_hint) {
|
||||
|
||||
static const void *mem_gets(fmap_t *m, char *dst, size_t *at, size_t max_len) {
|
||||
char *src = (char *)m->data + *at, *endptr = NULL;
|
||||
size_t len = MIN(max_len-1, m->len - *at);
|
||||
size_t len = MIN(max_len-1, m->real_len - *at);
|
||||
|
||||
if(!len || !CLI_ISCONTAINED(0, m->len, *at, len))
|
||||
if(!len || !CLI_ISCONTAINED(0, m->real_len, *at, len))
|
||||
return NULL;
|
||||
|
||||
if((endptr = memchr(src, '\n', len))) {
|
||||
|
||||
@@ -52,8 +52,18 @@ struct cl_fmap {
|
||||
const void *data;
|
||||
|
||||
/* common interface */
|
||||
size_t offset;
|
||||
size_t len;
|
||||
size_t offset;/* file offset */
|
||||
size_t nested_offset;/* buffer offset for nested scan*/
|
||||
size_t real_len;/* amount of data mapped from file, starting at offset */
|
||||
size_t len;/* length of data accessible via current fmap */
|
||||
|
||||
/* real_len = nested_offset + len
|
||||
* file_offset = offset + nested_offset + need_offset
|
||||
* maximum offset, length accessible via fmap API: len
|
||||
* offset in cached buffer: nested_offset + need_offset
|
||||
*
|
||||
* This allows to scan a portion of an already mapped file without dumping
|
||||
* to disk and remapping (for uncompressed archives for example) */
|
||||
|
||||
/* vtable for implementation */
|
||||
void (*unmap)(fmap_t*);
|
||||
|
||||
@@ -552,7 +552,7 @@ int cli_scanmacho_unibin(cli_ctx *ctx)
|
||||
cli_dbgmsg("UNIBIN: Binary %u of %u\n", i + 1, fat_header.nfats);
|
||||
cli_dbgmsg("UNIBIN: File offset: %u\n", fat_arch.offset);
|
||||
cli_dbgmsg("UNIBIN: File size: %u\n", fat_arch.size);
|
||||
ret = cli_dumpscan(map, fat_arch.offset, fat_arch.size, ctx);
|
||||
ret = cli_map_scandesc(map, fat_arch.offset, fat_arch.size, ctx);
|
||||
if(ret == CL_VIRUS)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -915,60 +915,6 @@ int cli_rmdirs(const char *dirname)
|
||||
}
|
||||
#endif
|
||||
|
||||
int cli_dumpscan(fmap_t *map, off_t offset, size_t size, cli_ctx *ctx)
|
||||
{
|
||||
int newfd, sum = 0, ret;
|
||||
ssize_t bread;
|
||||
char *name;
|
||||
const char *buff;
|
||||
|
||||
if(!(name = cli_gentemp(ctx->engine->tmpdir)))
|
||||
return CL_EMEM;
|
||||
|
||||
if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY|O_EXCL, S_IRWXU)) < 0) {
|
||||
cli_errmsg("cli_dumpscan: Can't create file %s\n", name);
|
||||
free(name);
|
||||
return CL_ECREAT;
|
||||
}
|
||||
|
||||
while (( buff = fmap_need_off_once_len(map, offset, FILEBUFF, &bread) )) {
|
||||
offset += bread;
|
||||
if((uint32_t) (sum + bread) >= size) {
|
||||
if(cli_writen(newfd, buff, size - sum) == -1) {
|
||||
cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
|
||||
close(newfd);
|
||||
cli_unlink(name);
|
||||
free(name);
|
||||
return CL_EWRITE;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if(cli_writen(newfd, buff, bread) == -1) {
|
||||
cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
|
||||
close(newfd);
|
||||
cli_unlink(name);
|
||||
free(name);
|
||||
return CL_EWRITE;
|
||||
}
|
||||
}
|
||||
sum += bread;
|
||||
}
|
||||
cli_dbgmsg("DUMP&SCAN: File extracted to %s\n", name);
|
||||
lseek(newfd, 0, SEEK_SET);
|
||||
if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS)
|
||||
cli_dbgmsg("cli_dumpscan: Infected with %s\n", *ctx->virname);
|
||||
|
||||
close(newfd);
|
||||
if(!ctx->engine->keeptmp) {
|
||||
if(cli_unlink(name)) {
|
||||
free(name);
|
||||
return CL_EUNLINK;
|
||||
}
|
||||
}
|
||||
free(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Implement a generic bitset, trog@clamav.net */
|
||||
|
||||
#define BITS_PER_CHAR (8)
|
||||
|
||||
@@ -522,7 +522,7 @@ char *cli_gentemp(const char *dir);
|
||||
int cli_gentempfd(const char *dir, char **name, int *fd);
|
||||
unsigned int cli_rndnum(unsigned int max);
|
||||
int cli_filecopy(const char *src, const char *dest);
|
||||
int cli_dumpscan(fmap_t *map, off_t offset, size_t size, cli_ctx *ctx);
|
||||
int cli_mapscan(fmap_t *map, off_t offset, size_t size, cli_ctx *ctx);
|
||||
bitset_t *cli_bitset_init(void);
|
||||
void cli_bitset_free(bitset_t *bs);
|
||||
int cli_bitset_set(bitset_t *bs, unsigned long bit_offset);
|
||||
|
||||
@@ -2537,20 +2537,48 @@ int cl_scandesc(int desc, const char **virname, unsigned long int *scanned, cons
|
||||
return cl_scandesc_callback(desc, virname, scanned, engine, scanoptions, NULL);
|
||||
}
|
||||
|
||||
static int cli_map_scandesc(cl_fmap_t *map, cli_ctx *ctx)
|
||||
/* length = 0, till the end */
|
||||
int cli_map_scandesc(cl_fmap_t *map, off_t offset, size_t length, cli_ctx *ctx)
|
||||
{
|
||||
off_t old_off = map->nested_offset;
|
||||
size_t old_len = map->len;
|
||||
int ret;
|
||||
|
||||
if (map->len <= 5) {
|
||||
cli_dbgmsg("Small data (%u bytes)\n", (unsigned int) map->len);
|
||||
cli_dbgmsg("cli_map_scandesc: [%ld, +%ld), [%ld, +%ld)\n",
|
||||
old_off, old_len, offset, length);
|
||||
if (offset < 0 || offset >= length) {
|
||||
cli_dbgmsg("Invalid offset: %ld\n", (long)offset);
|
||||
return CL_CLEAN;
|
||||
}
|
||||
|
||||
if (!length) length = old_len - offset;
|
||||
if (length > old_len - offset) {
|
||||
cli_dbgmsg("Data truncated: %ld -> %ld\n",
|
||||
length, old_len - offset);
|
||||
length = old_len - offset;
|
||||
}
|
||||
|
||||
if (length <= 5) {
|
||||
cli_dbgmsg("Small data (%u bytes)\n", (unsigned int) length);
|
||||
return CL_CLEAN;
|
||||
}
|
||||
ctx->fmap++;
|
||||
*ctx->fmap = map;
|
||||
|
||||
ret = magic_scandesc(ctx, CL_TYPE_ANY);
|
||||
/* can't change offset because then we'd have to discard/move cached
|
||||
* data, instead use another offset to reuse the already cached data */
|
||||
map->nested_offset += offset;
|
||||
map->len = length;
|
||||
map->real_len = map->nested_offset + length;
|
||||
if (CLI_ISCONTAINED(old_off, old_len, map->nested_offset, map->len)) {
|
||||
ret = magic_scandesc(ctx, CL_TYPE_ANY);
|
||||
} else {
|
||||
cli_warnmsg("internal map error: %ld, %ld; %ld, %ld\n", old_off, old_off + old_len,
|
||||
map->offset,map->nested_offset+map->len);
|
||||
}
|
||||
|
||||
ctx->fmap--;
|
||||
map->nested_offset = old_off;
|
||||
map->len = old_len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2594,7 +2622,7 @@ static int scan_common(int desc, cl_fmap_t *map, const char **virname, unsigned
|
||||
#endif
|
||||
|
||||
cli_logg_setup(&ctx);
|
||||
rc = map ? cli_map_scandesc(map, &ctx) : cli_magic_scandesc(desc, &ctx);
|
||||
rc = map ? cli_map_scandesc(map, 0, map->len, &ctx) : cli_magic_scandesc(desc, &ctx);
|
||||
|
||||
cli_bitset_free(ctx.hook_lsig_matches);
|
||||
free(ctx.fmap);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
int cli_magic_scandesc(int desc, cli_ctx *ctx);
|
||||
int cli_magic_scandesc_type(cli_ctx *ctx, cli_file_t type);
|
||||
int cli_map_scandesc(cl_fmap_t *map, off_t offset, size_t length, cli_ctx *ctx);
|
||||
int cli_found_possibly_unwanted(cli_ctx* ctx);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -242,22 +242,12 @@ static const char *tagname(tag_id id)
|
||||
|
||||
static int dumpscan(fmap_t *map, unsigned int offset, unsigned int size, const char *obj, int version, cli_ctx *ctx)
|
||||
{
|
||||
int newfd, ret = CL_CLEAN;
|
||||
unsigned int bread, sum = 0;
|
||||
char buff[FILEBUFF];
|
||||
char *name;
|
||||
int ret = CL_CLEAN;
|
||||
char buff[16];
|
||||
|
||||
if(!(name = cli_gentemp(ctx->engine->tmpdir)))
|
||||
return CL_EMEM;
|
||||
|
||||
if((newfd = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
|
||||
cli_errmsg("dumpscan: Can't create file %s\n", name);
|
||||
free(name);
|
||||
return CL_ECREAT;
|
||||
}
|
||||
|
||||
while((bread = fmap_readn(map, buff, offset, sizeof(buff))) > 0) {
|
||||
if(!sum && ctx->img_validate) {
|
||||
memset(buff, 0, sizeof(buff));
|
||||
fmap_readn(map, buff, offset, sizeof(buff));
|
||||
if(ctx->img_validate) {
|
||||
if(!memcmp(buff, "\xff\xd8", 2)) {
|
||||
cli_dbgmsg("SWF: JPEG image data\n");
|
||||
} else if(!memcmp(buff, "\xff\xd9\xff\xd8", 4)) {
|
||||
@@ -284,46 +274,10 @@ static int dumpscan(fmap_t *map, unsigned int offset, unsigned int size, const c
|
||||
ret = CL_VIRUS;
|
||||
}
|
||||
if(ret == CL_VIRUS) {
|
||||
close(newfd);
|
||||
cli_unlink(name);
|
||||
free(name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if(sum + bread >= size) {
|
||||
if(cli_writen(newfd, buff, size - sum) == -1) {
|
||||
cli_errmsg("dumpscan: Can't write to %s\n", name);
|
||||
close(newfd);
|
||||
cli_unlink(name);
|
||||
free(name);
|
||||
return CL_EWRITE;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if(cli_writen(newfd, buff, bread) == -1) {
|
||||
cli_errmsg("cli_dumpscan: Can't write to %s\n", name);
|
||||
close(newfd);
|
||||
cli_unlink(name);
|
||||
free(name);
|
||||
return CL_EWRITE;
|
||||
}
|
||||
}
|
||||
sum += bread;
|
||||
offset += bread;
|
||||
}
|
||||
cli_dbgmsg("SWF: %s data extracted to %s\n", obj, name);
|
||||
lseek(newfd, 0, SEEK_SET);
|
||||
if((ret = cli_magic_scandesc(newfd, ctx)) == CL_VIRUS)
|
||||
cli_dbgmsg("cli_dumpscan: Infected with %s\n", *ctx->virname);
|
||||
|
||||
close(newfd);
|
||||
if(!ctx->engine->keeptmp) {
|
||||
if(cli_unlink(name)) {
|
||||
free(name);
|
||||
return CL_EUNLINK;
|
||||
}
|
||||
}
|
||||
free(name);
|
||||
ret = cli_map_scandesc(map, offset, size, ctx);
|
||||
if(ctx->img_validate && ret == CL_EPARSE && SCAN_ALGO) {
|
||||
*ctx->virname = "Heuristics.SWF.SuspectImage.E";
|
||||
return CL_VIRUS;
|
||||
|
||||
Reference in New Issue
Block a user