"Fossies" - the Fresh Open Source Software archive

Member "scalpel-2.0/src/helpers.c" of archive scalpel-2.0.tar.gz:


// Scalpel Copyright (C) 2005-11 by Golden G. Richard III and 
// 2007-11 by Vico Marziale.
// Written by Golden G. Richard III and Vico Marziale.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
// 
// 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.
//
// Thanks to Kris Kendall, Jesse Kornblum, et al for their work 
// on Foremost.  Foremost 0.69 was used as the starting point for 
// Scalpel, in 2005.

#include "scalpel.h"

// get # of seconds between two specified times
#if defined(_WIN32)
inline double elapsed(LARGE_INTEGER A, LARGE_INTEGER B) {
  LARGE_INTEGER freq;
  QueryPerformanceFrequency(&freq);
  return fabs(((double)A.QuadPart - (double)B.QuadPart) /
	      (double)freq.QuadPart);
}
#else
double elapsed(struct timeval A, struct timeval B) {
  return fabs(A.tv_sec - B.tv_sec) + fabs(A.tv_usec - B.tv_usec) / 1000000;
}
#endif

// determine if a string begins and ends with '/' characters
int isRegularExpression(char *s) {
  return (s && s[0] && s[0] == '/' && s[strlen(s) - 1] == '/');
}


void
checkMemoryAllocation(struct scalpelState *state, void *ptr, int line,
		      const char *file, const char *structure) {
  if(ptr) {
    return;
  }
  else {
    fprintf(stderr, "** MEMORY ALLOCATION FAILURE **\n");
    fprintf(stderr,
	    "ERROR: Memory exhausted at line %d in file %s. Scalpel was \n",
	    line, file);
    fprintf(stderr,
	    "allocating memory for %s when this condition occurred.\n",
	    structure);

    fprintf(state->auditFile,
	    "ERROR: Memory exhausted at line %d in file %s. Scalpel was \n",
	    line, file);
    fprintf(state->auditFile,
	    "allocating memory for %s when this condition occurred.\n",
	    structure);

    handleError(state, SCALPEL_GENERAL_ABORT);	// fatal
  }
}


#ifndef __GLIBC__
void setProgramName(char *s) {
  char *fn = (char *)malloc(sizeof(char) * PATH_MAX);
  if(realpath(s, fn) == NULL) {
    __progname = strdup("scalpel");
    return;
  }

  __progname = basename(fn);
}

#endif /* ifndef __GLIBC__ */


// write entry to both the screen and the audit file (if possible)
void scalpelLog(struct scalpelState *state, const char *format, ...) {

  va_list argp;

  va_start(argp, format);
  vfprintf(stderr, format, argp);
  va_end(argp);

  va_start(argp, format);
  if(state->auditFile) {
    vfprintf(state->auditFile, format, argp);
  }
  va_end(argp);
}

// determine if two characters match, with optional case 
// insensitivity.  If a is the Scalpel wildcard character,
// then a and b will always match.
int charactersMatch(char a, char b, int caseSensitive) {

  if(a == wildcard || a == b) {
    return 1;
  }
  if(caseSensitive || (a < 'A' || a > 'z' || b < 'A' || b > 'z')) {
    return 0;
  }

  /* This line is equivalent to (abs(a-b)) == 'a' - 'A' */
  return (abs(a - b) == 32);
}


// memwildcardcmp is a memcmp() clone, except that single
// character wildcards are supported.  The default wildcard character is '?',
// but this can be redefined in the configuration file.  A wildcard in s1 will 
// match any single character in s2.  
int memwildcardcmp(const void *s1, const void *s2, size_t n, int caseSensitive) {
  if(n != 0) {
    register const unsigned char *p1 = (const unsigned char *)s1,
      *p2 = (const unsigned char *)s2;
    do {
      if(!charactersMatch(*p1++, *p2++, caseSensitive)) {
	return (*--p1 - *--p2);
      }
    }
    while (--n != 0);
  }
  return 0;
}


// initialize Boyer-Moore "jump table" for search. Dependence
// on search type (e.g., FORWARD, REVERSE, etc.) from Foremost 
// has been removed, because Scalpel always performs searches across
// a buffer in a forward direction.
void
init_bm_table(char *needle, size_t table[UCHAR_MAX + 1],
	      size_t len, int casesensitive) {

  size_t i = 0, j = 0, currentindex = 0;

  for(i = 0; i <= UCHAR_MAX; i++) {
    table[i] = len;
  }

  for(i = 0; i < len; i++) {
    currentindex = len - i - 1;	//Count from the back of string
    //No skip entry can advance us past the last wildcard in the string
    if(needle[i] == wildcard) {
      for(j = 0; j <= UCHAR_MAX; j++) {
	table[j] = currentindex;
      }
    }
    table[(unsigned char)needle[i]] = currentindex;
    if(!casesensitive && needle[i] > 0) {
      table[tolower(needle[i])] = currentindex;
      table[toupper(needle[i])] = currentindex;
    }
  }
}

#ifdef USE_SIMPLE_STRING_SEARCH

// Perform a simple string search, supporting wildcards,
// case-insensitive searches, and specifiable start locations in the buffer.
// The parameter 'table' is ignored.
char *bm_needleinhaystack_skipnchars(char *needle, size_t needle_len,
				     char *haystack, size_t haystack_len,
				     size_t table[UCHAR_MAX + 1],
				     int casesensitive, int start_pos) {

  register int i, j, go;

  if(needle_len == 0) {
    return haystack;
  }

  for(i = start_pos; i < haystack_len; i++) {
    go = 1;
    j = 0;
    while (go) {
      go = (i + j) < haystack_len && j < needle_len &&
	charactersMatch(needle[j], haystack[i + j], casesensitive);
      j++;
    }

    if(j > needle_len) {
      return haystack + i;
    }
  }

  return NULL;

}

#endif



#ifdef USE_FAST_STRING_SEARCH

// Perform a modified Boyer-Moore string search, supporting wildcards,
// case-insensitive searches, and specifiable start locations in the buffer.
// Dependence on search type (e.g., FORWARD, REVERSe, etc.) from Foremost has 
// been removed, because Scalpel always performs forward searching.
char *bm_needleinhaystack_skipnchars(char *needle, size_t needle_len,
				     char *haystack, size_t haystack_len,
				     size_t table[UCHAR_MAX + 1],
				     int casesensitive, int start_pos) {

  register size_t shift = 0;
  register size_t pos = start_pos;
  char *here;

  if(needle_len == 0) {
    return haystack;
  }

  while (pos < haystack_len) {
    while (pos < haystack_len
	   && (shift = table[(unsigned char)haystack[pos]]) > 0) {
      pos += shift;
    }
    if(0 == shift) {
      if(0 ==
	 memwildcardcmp(needle, here =
			(char *)&haystack[pos - needle_len + 1],
			needle_len, casesensitive)) {
	return (here);
      }
      else {
	pos++;
      }
    }
  }
  return NULL;
}

#endif


char *bm_needleinhaystack(char *needle, size_t needle_len,
			  char *haystack, size_t haystack_len,
			  size_t table[UCHAR_MAX + 1], int casesensitive) {

  return bm_needleinhaystack_skipnchars(needle,
					needle_len,
					haystack,
					haystack_len,
					table, casesensitive, needle_len - 1);
}



// find longest header OR footer.  Headers or footers which are
// regular expressions are assigned LARGEST_REGEXP_OVERLAP lengths, to
// allow for regular expressions spanning SIZE_OF_BUFFER-sized chunks
// of the disk image.
int findLongestNeedle(struct SearchSpecLine *SearchSpec) {
  int longest = 0;
  int i = 0;
  int lenb, lene;
  for(i = 0; SearchSpec[i].suffix != NULL; i++) {
    lenb =
      SearchSpec[i].beginisRE ? LARGEST_REGEXP_OVERLAP : SearchSpec[i].
      beginlength;
    lene =
      SearchSpec[i].endisRE ? LARGEST_REGEXP_OVERLAP : SearchSpec[i].endlength;
    if(lenb > longest) {
      longest = lenb;
    }
    if(lene > longest) {
      longest = lene;
    }
  }
  return longest;
}


// do a regular expression search using the Tre regular expression
// library.  The needle is a previously compiled regular expression
// (via Tre regcomp()).  The caller must free the memory associated 
// with the returned regmatch_t structure.
regmatch_t *re_needleinhaystack(regex_t * needle, char *haystack,
				size_t haystack_len) {

  regmatch_t *match = (regmatch_t *) malloc(sizeof(regmatch_t));


  // LMIII temp fix till working with g++
  if(!regnexec(needle, haystack, (size_t) haystack_len, (size_t) 1, match, 0)) {
    // match
    return match;
  }
  else {
    // no match
    return NULL;
  }
}


// decode strings with embedded escape sequences and return the total length of the
// translated string.

int translate(char *str) {
  char next;
  char *rd = str, *wr = str, *bad;
  char temp[1 + 3 + 1];
  char ch;

  if(!*rd) {			//If it's a null string just return
    return 0;
  }

  while (*rd) {
    // Is it an escaped character?
    if(*rd == '\\') {
      rd++;
      switch (*rd) {
      case '\\':
	rd++;
	*(wr++) = '\\';
	break;
      case 'a':
	rd++;
	*(wr++) = '\a';
	break;
      case 's':
	rd++;
	*(wr++) = ' ';
	break;
      case 'n':
	rd++;
	*(wr++) = '\n';
	break;
      case 'r':
	rd++;
	*(wr++) = '\r';
	break;
      case 't':
	rd++;
	*(wr++) = '\t';
	break;
      case 'v':
	rd++;
	*(wr++) = '\v';
	break;
	// Hexadecimal/Octal values are treated in one place using strtoul() 
      case 'x':
      case '0':
      case '1':
      case '2':
      case '3':
	next = *(rd + 1);
      if(next < 48 || (57 < next && next < 65) ||
	 (70 < next && next < 97) || next > 102)
	break;		//break if not a digit or a-f, A-F 
      next = *(rd + 2);
      if(next < 48 || (57 < next && next < 65) ||
	 (70 < next && next < 97) || next > 102)
	break;		//break if not a digit or a-f, A-F 

      temp[0] = '0';
      bad = temp;
      strncpy(temp + 1, rd, 3);
      temp[4] = '\0';
      ch = strtoul(temp, &bad, 0);
      if(*bad == '\0') {
	*(wr++) = ch;
	rd += 3;
      }			// else INVALID CHARACTER IN INPUT ('\\' followed by *rd) 
      break;
      default:		// INVALID CHARACTER IN INPUT (*rd)
	*(wr++) = '\\';
	break;
      }
    }
    // just copy un-escaped characters
    else {
      *(wr++) = *(rd++);
    }
  }
  *wr = '\0';			// null termination
  return wr - str;
}

// skip leading whitespace
char *skipWhiteSpace(char *str) {
  while (isspace(str[0])) {
    str++;
  }
  return str;
}

// describe Scalpel error conditions.  Some errors are fatal, while
// others are advisory.
void handleError(struct scalpelState *state, int error) {

  switch (error) {

  case SCALPEL_ERROR_PTHREAD_FAILURE:
    // fatal
    scalpelLog(state,
	       "Scalpel was unable to create threads and will abort.\n");
    closeAuditFile(state->auditFile);
    exit(-1);
    break;

  case SCALPEL_ERROR_FILE_OPEN:
    // non-fatal
    if (state->imagefile == 0 || *(state->imagefile) == '\0') {
      scalpelLog(state, "Scalpel was unable to open the image file: <blank>....\n"
		 "Skipping...\n");
    }
    else {
      scalpelLog(state, "Scalpel was unable to open the image file: %s...%s\n"
		 "Skipping...\n", state->imagefile, strerror(errno));
    }
    break;
  case SCALPEL_ERROR_FILE_TOO_SMALL:
    // non-fatal
    scalpelLog(state,
	       "The image file %s is smaller than the longest header/footer and cannot be processed.\n"
	       "Skipping...\n", state->imagefile);
    break;

  case SCALPEL_ERROR_FILE_READ:
    // non-fatal
    scalpelLog(state, "Scalpel was unable to read the image file: %s\n"
	       "Skipping...\n", state->imagefile);
    break;

  case SCALPEL_ERROR_FATAL_READ:
    // fatal
    scalpelLog(state,
	       "Scalpel was unable to read a needed file and will abort.\n");
    closeAuditFile(state->auditFile);
    exit(-1);
    break;

  case SCALPEL_ERROR_NONEMPTY_DIRECTORY:
    // fatal
    fprintf(stderr,
	    "Scalpel will write only to empty output directories to avoid\n");
    fprintf(stderr, "mixing the output from multiple carving operations.\n");
    fprintf(stderr,
	    "Please specify a different output directory or delete the specified\noutput directory.\n");
    closeAuditFile(state->auditFile);
    exit(-1);
    break;

  case SCALPEL_ERROR_FILE_WRITE:
    // fatal--unable to write files, which may mean that disk space is exhausted.
    fprintf(stderr,
	    "Scalpel was unable to write output files and will abort.\n");
    fprintf(stderr,
	    "This error generally indicates that disk space is exhausted.\n");
    closeAuditFile(state->auditFile);
    exit(-1);
    break;

  case SCALPEL_ERROR_NO_SEARCH_SPEC:
    // fatal, configuration file didn't specify anything to carve
    scalpelLog(state,
	       "ERROR: The configuration file didn't specify any file types to carve.\n");
    scalpelLog(state,
	       "(If you're using the default configuration file, you'll have to\n");
    scalpelLog(state, "uncomment some of the file types.)\n");
    closeAuditFile(state->auditFile);
    exit(-1);
    break;

  case SCALPEL_GENERAL_ABORT:
    // fatal
    scalpelLog(state, "Scalpel will abort.\n");
    closeAuditFile(state->auditFile);
    exit(-1);
    break;

  default:
    // fatal
    closeAuditFile(state->auditFile);
    exit(-1);
  }
}


void setttywidth(void) {
#if defined (_WIN32)
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  if(!GetConsoleScreenBufferInfo(hConsole, &csbi)) {
    ttywidth = 80;
  }
  else {
    ttywidth = csbi.dwSize.X;
  }
#else
  //#if defined(__BSD)
  //  ttywidth = 80;
  //#else
  struct winsize winsize;
  if(ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) {
    ttywidth = winsize.ws_col;
  }
  else {
    // 80 is a reasonable default
    ttywidth = 80;
  }
  //#endif
#endif
}


int skipInFile(struct scalpelState *state, FILE * infile) {

  int retries = 0;
  while (TRUE) {
    if((fseeko(infile, state->skip, SEEK_SET))) {

#ifdef _WIN32
      fprintf(stderr,
	      "ERROR: Couldn't skip %I64u bytes at the start of image file %s\n",
	      state->skip, state->imagefile);
#else
      fprintf(stderr,
	      "ERROR: Couldn't skip %lld bytes at the start of image file %s\n",
	      state->skip, state->imagefile);
#endif

      if(retries++ > 3) {
	fprintf(stderr, "Sorry, maximum retries exceeded...\n");
	return FALSE;
      }
      else {
	fprintf(stderr, "Waiting to try again... \n");
	sleep(3);
      }
    }
    else {
#ifdef _WIN32
      fprintf(stderr, "Skipped the first %I64u bytes of %s...\n",
	      state->skip, state->imagefile);
#else
      fprintf(stderr, "Skipped the first %lld bytes of %s...\n",
	      state->skip, state->imagefile);
#endif


      return TRUE;
    }
  }
}