mirror of https://github.com/Cisco-Talos/clamav
commit
bced84530b
@ -0,0 +1,291 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Sourcefire, Inc. |
||||||
|
* |
||||||
|
* Authors: David Raynor <draynor@sourcefire.com> |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License version 2 as |
||||||
|
* published by the Free Software Foundation. |
||||||
|
* |
||||||
|
* 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 |
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||||
|
* MA 02110-1301, USA. |
||||||
|
*/ |
||||||
|
|
||||||
|
#if HAVE_CONFIG_H |
||||||
|
#include "clamav-config.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <errno.h> |
||||||
|
#if HAVE_STRING_H |
||||||
|
#include <string.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "cltypes.h" |
||||||
|
#include "others.h" |
||||||
|
#include "adc.h" |
||||||
|
|
||||||
|
/* #define DEBUG_ADC */ |
||||||
|
|
||||||
|
#ifdef DEBUG_ADC |
||||||
|
# define adc_dbgmsg(...) cli_dbgmsg( __VA_ARGS__ ) |
||||||
|
#else |
||||||
|
# define adc_dbgmsg(...) ; |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Initialize values and collect buffer
|
||||||
|
* NOTE: buffer size must be larger than largest lookback offset */ |
||||||
|
int adc_decompressInit(adc_stream *strm) |
||||||
|
{ |
||||||
|
if (strm == NULL) { |
||||||
|
return ADC_IO_ERROR; |
||||||
|
} |
||||||
|
if (strm->state != ADC_STATE_UNINIT) { |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
|
||||||
|
/* Have to buffer maximum backward lookup */ |
||||||
|
strm->buffer = (uint8_t *)calloc(ADC_BUFF_SIZE, 1); |
||||||
|
if (strm->buffer == NULL) { |
||||||
|
return ADC_MEM_ERROR; |
||||||
|
} |
||||||
|
strm->buffered = 0; |
||||||
|
strm->state = ADC_STATE_GETTYPE; |
||||||
|
strm->length = 0; |
||||||
|
strm->offset = 0; |
||||||
|
strm->curr = strm->buffer; |
||||||
|
|
||||||
|
return ADC_OK; |
||||||
|
} |
||||||
|
|
||||||
|
/* Decompress routine
|
||||||
|
* NOTE: Reaching end of input buffer does not mean end of output. |
||||||
|
* It may fill the output buffer but have more to output. |
||||||
|
* It will only return ADC_STREAM_END if output buffer is not full. |
||||||
|
* It will return ADC_DATA_ERROR if it ends in the middle of a phrase |
||||||
|
* (i.e. in the middle of a lookback code or data run) |
||||||
|
*/ |
||||||
|
int adc_decompress(adc_stream *strm) |
||||||
|
{ |
||||||
|
uint8_t bData; |
||||||
|
uint8_t didNothing = 1; |
||||||
|
|
||||||
|
/* first, the error returns based on strm */ |
||||||
|
if ((strm == NULL) || (strm->next_in == NULL) || (strm->next_out == NULL)) { |
||||||
|
return ADC_IO_ERROR; |
||||||
|
} |
||||||
|
if (strm->state == ADC_STATE_UNINIT) { |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
|
||||||
|
cli_dbgmsg("adc_decompress: avail_in %lu avail_out %lu state %u\n", strm->avail_in, strm->avail_out, strm->state); |
||||||
|
|
||||||
|
while (strm->avail_out) { |
||||||
|
/* Exit if needs more in bytes and none available */ |
||||||
|
int needsInput; |
||||||
|
switch (strm->state) { |
||||||
|
case ADC_STATE_SHORTLOOK: |
||||||
|
case ADC_STATE_LONGLOOK: |
||||||
|
needsInput = 0; |
||||||
|
break; |
||||||
|
default: |
||||||
|
needsInput = 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
if (needsInput && (strm->avail_in == 0)) { |
||||||
|
break; |
||||||
|
} |
||||||
|
else { |
||||||
|
didNothing = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* Find or execute statecode */ |
||||||
|
switch (strm->state) { |
||||||
|
case ADC_STATE_GETTYPE: { |
||||||
|
/* Grab action code */ |
||||||
|
bData = *(strm->next_in); |
||||||
|
strm->next_in++; |
||||||
|
strm->avail_in--; |
||||||
|
if (bData & 0x80) { |
||||||
|
strm->state = ADC_STATE_RAWDATA; |
||||||
|
strm->offset = 0; |
||||||
|
strm->length = (bData & 0x7F) + 1; |
||||||
|
} |
||||||
|
else if (bData & 0x40) { |
||||||
|
strm->state = ADC_STATE_LONGOP2; |
||||||
|
strm->offset = 0; |
||||||
|
strm->length = (bData & 0x3F) + 4; |
||||||
|
} |
||||||
|
else { |
||||||
|
strm->state = ADC_STATE_SHORTOP; |
||||||
|
strm->offset = (bData & 0x3) * 0x100; |
||||||
|
strm->length = ((bData & 0x3C) >> 2) + 3; |
||||||
|
} |
||||||
|
adc_dbgmsg("adc_decompress: GETTYPE bData %x state %u offset %u length %u\n", |
||||||
|
bData, strm->state, strm->offset, strm->length); |
||||||
|
break; |
||||||
|
} |
||||||
|
case ADC_STATE_LONGOP2: { |
||||||
|
/* Grab first offset byte */ |
||||||
|
bData = *(strm->next_in); |
||||||
|
strm->next_in++; |
||||||
|
strm->avail_in--; |
||||||
|
strm->offset = bData * 0x100; |
||||||
|
strm->state = ADC_STATE_LONGOP1; |
||||||
|
adc_dbgmsg("adc_decompress: LONGOP2 bData %x state %u offset %u length %u\n", |
||||||
|
bData, strm->state, strm->offset, strm->length); |
||||||
|
break; |
||||||
|
} |
||||||
|
case ADC_STATE_LONGOP1: { |
||||||
|
/* Grab second offset byte */ |
||||||
|
bData = *(strm->next_in); |
||||||
|
strm->next_in++; |
||||||
|
strm->avail_in--; |
||||||
|
strm->offset += bData + 1; |
||||||
|
strm->state = ADC_STATE_LONGLOOK; |
||||||
|
adc_dbgmsg("adc_decompress: LONGOP1 bData %x state %u offset %u length %u\n", |
||||||
|
bData, strm->state, strm->offset, strm->length); |
||||||
|
break; |
||||||
|
} |
||||||
|
case ADC_STATE_SHORTOP: { |
||||||
|
/* Grab offset byte */ |
||||||
|
bData = *(strm->next_in); |
||||||
|
strm->next_in++; |
||||||
|
strm->avail_in--; |
||||||
|
strm->offset += bData + 1; |
||||||
|
strm->state = ADC_STATE_SHORTLOOK; |
||||||
|
adc_dbgmsg("adc_decompress: SHORTOP bData %x state %u offset %u length %u\n", |
||||||
|
bData, strm->state, strm->offset, strm->length); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case ADC_STATE_RAWDATA: { |
||||||
|
/* Grab data */ |
||||||
|
adc_dbgmsg("adc_decompress: RAWDATA offset %u length %u\n", strm->offset, strm->length); |
||||||
|
while ((strm->avail_in > 0) && (strm->avail_out > 0) && (strm->length > 0)) { |
||||||
|
bData = *(strm->next_in); |
||||||
|
strm->next_in++; |
||||||
|
strm->avail_in--; |
||||||
|
/* store to output */ |
||||||
|
*(strm->next_out) = bData; |
||||||
|
strm->next_out++; |
||||||
|
strm->avail_out--; |
||||||
|
/* store to buffer */ |
||||||
|
if (strm->curr >= (strm->buffer + ADC_BUFF_SIZE)) { |
||||||
|
strm->curr = strm->buffer; |
||||||
|
} |
||||||
|
*(strm->curr) = bData; |
||||||
|
strm->curr++; |
||||||
|
if (strm->buffered < ADC_BUFF_SIZE) { |
||||||
|
strm->buffered++; |
||||||
|
} |
||||||
|
strm->length--; |
||||||
|
} |
||||||
|
if (strm->length == 0) { |
||||||
|
/* adc_dbgmsg("adc_decompress: RAWDATADONE buffered %u avail_in %u avail_out %u \n",
|
||||||
|
strm->buffered, strm->avail_in, strm->avail_out); */ |
||||||
|
strm->state = ADC_STATE_GETTYPE; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case ADC_STATE_SHORTLOOK: |
||||||
|
case ADC_STATE_LONGLOOK: { |
||||||
|
/* Copy data */ |
||||||
|
adc_dbgmsg("adc_decompress: LOOKBACK offset %u length %u avail_in %u avail_out %u\n", |
||||||
|
strm->offset, strm->length, strm->avail_in, strm->avail_out); |
||||||
|
while ((strm->avail_out > 0) && (strm->length > 0)) { |
||||||
|
/* state validation first */ |
||||||
|
if (strm->offset > 0x10000) { |
||||||
|
cli_dbgmsg("adc_decompress: bad LOOKBACK offset %u\n", strm->offset); |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
else if ((strm->state == ADC_STATE_SHORTLOOK) && (strm->offset > 0x400)) { |
||||||
|
cli_dbgmsg("adc_decompress: bad LOOKBACK offset %u\n", strm->offset); |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
if (strm->offset > strm->buffered) { |
||||||
|
cli_dbgmsg("adc_decompress: too large LOOKBACK offset %u\n", strm->offset); |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
/* retrieve byte */ |
||||||
|
if (strm->curr >= (strm->buffer + ADC_BUFF_SIZE)) { |
||||||
|
strm->curr = strm->buffer; |
||||||
|
} |
||||||
|
if (strm->curr > (strm->buffer + strm->offset)) { |
||||||
|
bData = *(uint8_t *)(strm->curr - strm->offset); |
||||||
|
} |
||||||
|
else { |
||||||
|
bData = *(uint8_t *)(strm->curr + ADC_BUFF_SIZE - strm->offset); |
||||||
|
} |
||||||
|
/* store to output */ |
||||||
|
*(strm->next_out) = bData; |
||||||
|
strm->next_out++; |
||||||
|
strm->avail_out--; |
||||||
|
/* store to buffer */ |
||||||
|
*(strm->curr) = bData; |
||||||
|
strm->curr++; |
||||||
|
if (strm->buffered < ADC_BUFF_SIZE) { |
||||||
|
strm->buffered++; |
||||||
|
} |
||||||
|
strm->length--; |
||||||
|
} |
||||||
|
if (strm->length == 0) { |
||||||
|
strm->state = ADC_STATE_GETTYPE; |
||||||
|
/* adc_dbgmsg("adc_decompress: LOOKBACKDONE buffered %u avail_in %u avail_out %u \n",
|
||||||
|
strm->buffered, strm->avail_in, strm->avail_out); */ |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
default: { |
||||||
|
/* bad state */ |
||||||
|
cli_errmsg("adc_decompress: invalid state %u\n", strm->state); |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
} /* end switch */ |
||||||
|
} /* end while */ |
||||||
|
|
||||||
|
/* There really isn't a terminator, just end of data */ |
||||||
|
if (didNothing && strm->avail_out) { |
||||||
|
if (strm->state == ADC_STATE_GETTYPE) { |
||||||
|
/* Nothing left to do */ |
||||||
|
return ADC_STREAM_END; |
||||||
|
} |
||||||
|
else { |
||||||
|
/* Ended mid phrase */ |
||||||
|
cli_dbgmsg("adc_decompress: stream ended mid-phrase, state %u\n", strm->state); |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
} |
||||||
|
return ADC_OK; |
||||||
|
} |
||||||
|
|
||||||
|
/* Cleanup routine, frees buffer */ |
||||||
|
int adc_decompressEnd(adc_stream *strm) |
||||||
|
{ |
||||||
|
if (strm == NULL) { |
||||||
|
return ADC_IO_ERROR; |
||||||
|
} |
||||||
|
if (strm->state == ADC_STATE_UNINIT) { |
||||||
|
return ADC_DATA_ERROR; |
||||||
|
} |
||||||
|
|
||||||
|
if (strm->buffer != NULL) { |
||||||
|
free(strm->buffer); |
||||||
|
} |
||||||
|
strm->buffered = 0; |
||||||
|
strm->state = ADC_STATE_UNINIT; |
||||||
|
strm->length = 0; |
||||||
|
strm->offset = 0; |
||||||
|
|
||||||
|
return ADC_OK; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,55 @@ |
|||||||
|
|
||||||
|
|
||||||
|
#ifndef CLAM_ADC_H |
||||||
|
#define CLAM_ADC_H |
||||||
|
|
||||||
|
struct adc_stream { |
||||||
|
uint8_t *next_in; |
||||||
|
size_t avail_in; |
||||||
|
size_t total_in; |
||||||
|
|
||||||
|
uint8_t *next_out; |
||||||
|
size_t avail_out; |
||||||
|
size_t total_out; |
||||||
|
|
||||||
|
/* internals */ |
||||||
|
uint8_t *buffer; |
||||||
|
uint8_t *curr; |
||||||
|
|
||||||
|
uint32_t buffered; |
||||||
|
uint16_t state; |
||||||
|
uint16_t length; |
||||||
|
uint32_t offset; |
||||||
|
}; |
||||||
|
typedef struct adc_stream adc_stream; |
||||||
|
|
||||||
|
#define ADC_BUFF_SIZE 65536 |
||||||
|
|
||||||
|
#define ADC_MEM_ERROR -1 |
||||||
|
#define ADC_DATA_ERROR -2 |
||||||
|
#define ADC_IO_ERROR -3 |
||||||
|
#define ADC_OK 0 |
||||||
|
#define ADC_STREAM_END 1 |
||||||
|
|
||||||
|
enum adc_state { |
||||||
|
ADC_STATE_UNINIT = 0, |
||||||
|
ADC_STATE_GETTYPE = 1, |
||||||
|
ADC_STATE_RAWDATA = 2, |
||||||
|
ADC_STATE_SHORTOP = 3, |
||||||
|
ADC_STATE_LONGOP2 = 4, |
||||||
|
ADC_STATE_LONGOP1 = 5, |
||||||
|
ADC_STATE_SHORTLOOK = 6, |
||||||
|
ADC_STATE_LONGLOOK = 7 |
||||||
|
}; |
||||||
|
|
||||||
|
/* Compression phrases
|
||||||
|
* store phrase - 1 byte header + data, first byte 0x80-0xFF, max length 0x80 (7 bits + 1), no offset |
||||||
|
* short phrase - 2 byte header + data, first byte 0x00-0x3F, max length 0x12 (4 bits + 3), max offset 0x3FF (10 bits) |
||||||
|
* long phrase - 3 byte header + data, first byte 0x40-0x7F, max length 0x43 (6 bits + 4), max offset 0xFFFF (16 bits) |
||||||
|
*/ |
||||||
|
|
||||||
|
int adc_decompressInit(adc_stream *strm); |
||||||
|
int adc_decompress(adc_stream *strm); |
||||||
|
int adc_decompressEnd(adc_stream *strm); |
||||||
|
|
||||||
|
#endif |
||||||
Loading…
Reference in new issue