libclamav/matcher-bm.c
8000d078
 /*
2023340a
  *  Copyright (C) 2007-2008 Sourcefire, Inc.
  *
  *  Authors: Tomasz Kojm
8000d078
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
8000d078
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
8000d078
  */
 
bedc58de
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
563582a1
 #include <assert.h>
8000d078
 #include "clamav.h"
 #include "memory.h"
 #include "others.h"
 #include "cltypes.h"
b68d11d2
 #include "matcher.h"
079229d6
 #include "matcher-bm.h"
73218de2
 #include "filetypes.h"
8000d078
 
b94e66c4
 #include "mpool.h"
 
7fd366a3
 #define BM_MIN_LENGTH	3
242efc14
 #define BM_BLOCK_SIZE	3
4e9ab8ed
 #define HASH(a,b,c) (211 * a + 37 * b + c)
8000d078
 
5612732c
 int cli_bm_addpatt(struct cli_matcher *root, struct cli_bm_patt *pattern)
8000d078
 {
ab1db3b3
 	uint16_t idx, i;
4e9ab8ed
 	const unsigned char *pt = pattern->pattern;
8000d078
 	struct cli_bm_patt *prev, *next = NULL;
 
 
     if(pattern->length < BM_MIN_LENGTH) {
871177cd
 	cli_errmsg("cli_bm_addpatt: Signature for %s is too short\n", pattern->virname);
 	return CL_EMALFDB;
8000d078
     }
 
ab1db3b3
 #if BM_MIN_LENGTH == BM_BLOCK_SIZE
     /* try to load balance bm_suffix (at the cost of bm_shift) */
     for(i = 0; i < pattern->length - BM_BLOCK_SIZE + 1; i++) {
242efc14
 	idx = HASH(pt[i], pt[i + 1], pt[i + 2]);
ab1db3b3
 	if(!root->bm_suffix[idx]) {
 	    if(i) {
 		pattern->prefix = pattern->pattern;
 		pattern->prefix_length = i;
 		pattern->pattern = &pattern->pattern[i];
 		pattern->length -= i;
 		pt = pattern->pattern;
 	    }
 	    break;
 	}
8000d078
     }
ab1db3b3
 #endif
8000d078
 
ab1db3b3
     for(i = 0; i <= BM_MIN_LENGTH - BM_BLOCK_SIZE; i++) {
 	idx = HASH(pt[i], pt[i + 1], pt[i + 2]);
 	root->bm_shift[idx] = MIN(root->bm_shift[idx], BM_MIN_LENGTH - BM_BLOCK_SIZE - i);
     }
8000d078
 
     prev = next = root->bm_suffix[idx];
     while(next) {
f70b93e1
 	if(pt[0] >= next->pattern0)
8000d078
 	    break;
 	prev = next;
 	next = next->next;
     }
 
c54133a1
     if(next == root->bm_suffix[idx]) {
8000d078
 	pattern->next = root->bm_suffix[idx];
ab1db3b3
 	if(root->bm_suffix[idx])
 	    pattern->cnt = root->bm_suffix[idx]->cnt;
8000d078
 	root->bm_suffix[idx] = pattern;
     } else {
 	pattern->next = prev->next;
 	prev->next = pattern;
     }
f70b93e1
     pattern->pattern0 = pattern->pattern[0];
ab1db3b3
     root->bm_suffix[idx]->cnt++;
8000d078
 
4addba22
     root->bm_patterns++;
ab1db3b3
     return CL_SUCCESS;
8000d078
 }
 
5612732c
 int cli_bm_init(struct cli_matcher *root)
8000d078
 {
ab1db3b3
 	uint16_t i, size = HASH(255, 255, 255) + 1;
563582a1
 #ifdef USE_MPOOL
     assert (root->mempool && "mempool must be initialized");
 #endif
47d40feb
     if(!(root->bm_shift = (uint8_t *) mpool_calloc(root->mempool, size, sizeof(uint8_t))))
8000d078
 	return CL_EMEM;
 
47d40feb
     if(!(root->bm_suffix = (struct cli_bm_patt **) mpool_calloc(root->mempool, size, sizeof(struct cli_bm_patt *)))) {
 	mpool_free(root->mempool, root->bm_shift);
8000d078
 	return CL_EMEM;
     }
 
e6e7bbee
     for(i = 0; i < size; i++)
8000d078
 	root->bm_shift[i] = BM_MIN_LENGTH - BM_BLOCK_SIZE + 1;
 
ab1db3b3
     return CL_SUCCESS;
8000d078
 }
 
5612732c
 void cli_bm_free(struct cli_matcher *root)
8000d078
 {
ab1db3b3
 	struct cli_bm_patt *patt, *prev;
 	uint16_t i, size = HASH(255, 255, 255) + 1;
d4fb658e
 
 
8000d078
     if(root->bm_shift)
47d40feb
 	mpool_free(root->mempool, root->bm_shift);
8000d078
 
d4fb658e
     if(root->bm_suffix) {
e6e7bbee
 	for(i = 0; i < size; i++) {
ab1db3b3
 	    patt = root->bm_suffix[i];
 	    while(patt) {
 		prev = patt;
 		patt = patt->next;
 		if(prev->prefix)
47d40feb
 		    mpool_free(root->mempool, prev->prefix);
ab1db3b3
 		else
47d40feb
 		    mpool_free(root->mempool, prev->pattern);
ab1db3b3
 		if(prev->virname)
47d40feb
 		    mpool_free(root->mempool, prev->virname);
ab1db3b3
 		if(prev->offset)
47d40feb
 		    mpool_free(root->mempool, prev->offset);
 		mpool_free(root->mempool, prev);
d4fb658e
 	    }
 	}
47d40feb
 	mpool_free(root->mempool, root->bm_suffix);
d4fb658e
     }
8000d078
 }
 
bedc58de
 int cli_bm_scanbuff(const unsigned char *buffer, uint32_t length, const char **virname, const struct cli_matcher *root, uint32_t offset, cli_file_t ftype, int fd)
8000d078
 {
ab1db3b3
 	uint32_t i, j, off;
 	uint8_t found, pchain, shift;
 	uint16_t idx, idxchk;
8000d078
 	struct cli_bm_patt *p;
ab1db3b3
 	const unsigned char *bp, *pt;
4e9ab8ed
 	unsigned char prefix;
841161e0
 	struct cli_target_info info;
8000d078
 
 
1f2ebf0c
     if(!root || !root->bm_shift)
cdbf8c8e
 	return CL_CLEAN;
 
cd4db869
     if(length < BM_MIN_LENGTH)
 	return CL_CLEAN;
 
841161e0
     memset(&info, 0, sizeof(info));
 
621939fa
     for(i = BM_MIN_LENGTH - BM_BLOCK_SIZE; i < length - BM_BLOCK_SIZE + 1; ) {
242efc14
 	idx = HASH(buffer[i], buffer[i + 1], buffer[i + 2]);
8000d078
 	shift = root->bm_shift[idx];
 
 	if(shift == 0) {
 	    prefix = buffer[i - BM_MIN_LENGTH + BM_BLOCK_SIZE];
 	    p = root->bm_suffix[idx];
ab1db3b3
 	    pchain = 0;
 	    while(p) {
f70b93e1
 		if(p->pattern0 != prefix) {
ab1db3b3
 		    if(pchain)
 			break;
 		    p = p->next;
 		    continue;
 		} else pchain = 1;
8000d078
 
 		off = i - BM_MIN_LENGTH + BM_BLOCK_SIZE;
 		bp = buffer + off;
242efc14
 
ab1db3b3
 		if((off + p->length > length) || (p->prefix_length > off)) {
242efc14
 		    p = p->next;
 		    continue;
 		}
 
ab1db3b3
 		idxchk = MIN(p->length, length - off) - 1;
 		if(idxchk) {
 		    if((bp[idxchk] != p->pattern[idxchk]) ||  (bp[idxchk / 2] != p->pattern[idxchk / 2])) {
dd7c2206
 			p = p->next;
 			continue;
 		    }
 		}
 
ab1db3b3
 		if(p->prefix_length) {
 		    off -= p->prefix_length;
 		    bp -= p->prefix_length;
 		    pt = p->prefix;
 		} else {
 		    pt = p->pattern;
 		}
 
8000d078
 		found = 1;
ab1db3b3
 		for(j = 0; j < p->length + p->prefix_length && off < length; j++, off++) {
 		    if(bp[j] != pt[j]) {
8000d078
 			found = 0;
 			break;
 		    }
 		}
5afda272
 
ab1db3b3
 		if(found && p->length + p->prefix_length == j) {
7ec67e94
 
64b9b982
 		    if(p->offset) {
ab1db3b3
 			off = offset + i - p->prefix_length - BM_MIN_LENGTH + BM_BLOCK_SIZE;
64b9b982
 			if(!cli_validatesig(ftype, p->offset, off, &info, fd, p->virname)) {
7ec67e94
 			    p = p->next;
 			    continue;
 			}
b68d11d2
 		    }
 
8000d078
 		    if(virname)
 			*virname = p->virname;
 
841161e0
 		    if(info.exeinfo.section)
 			free(info.exeinfo.section);
 
8000d078
 		    return CL_VIRUS;
 		}
5afda272
 
8000d078
 		p = p->next;
 	    }
 
 	    shift = 1;
 	}
 
 	i += shift;
     }
 
841161e0
     if(info.exeinfo.section)
 	free(info.exeinfo.section);
 
     return CL_CLEAN;
8000d078
 }