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:
Török Edvin
2011-06-17 23:08:31 +03:00
parent 0eafa898e9
commit 87f763991b
9 changed files with 79 additions and 135 deletions

View File

@@ -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;
}

View File

@@ -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))) {

View File

@@ -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*);

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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;