Imported Upstream version 1.60

parents
CC = gcc
CC_OPTS = -Wall -O2
GOAL = scalpel
CC += $(CC_OPTS)
.c.o:
$(CC) -c $<
HEADER_FILES = scalpel.h prioque.h dirname.h
SRC = helpers.c files.c scalpel.c dig.c prioque.c base_name.c
OBJS = helpers.o scalpel.o files.o dig.o prioque.o base_name.o
all: linux
linux: CC += -D__LINUX
linux: $(GOAL)
bsd: CC += -D__OPENBSD
bsd: $(GOAL)
win32: CC += -D__WIN32 -Ic:\PThreads\include
win32: $(SRC) $(HEADER_FILES)
$(CC) -o $(GOAL).exe $(SRC) -liberty -Lc:\PThreads\lib -lpthreadGC1
$(GOAL): $(OBJS)
$(CC) -o $(GOAL) $(OBJS) -lm
scalpel.o: scalpel.c $(HEADER_FILES) Makefile
dig.o: dig.c $(HEADER_FILES) Makefile
helpers.o: helpers.c $(HEADER_FILES) Makefile
files.o: files.c $(HEADER_FILES) Makefile
prioque.o: prioque.c prioque.h Makefile
nice:
rm -f *~
rm -rf scalpel-output
clean: nice
rm -f $(OBJS) $(GOAL) $(GOAL).exe core *.core
Scalpel is a complete rewrite of the Foremost 0.69 file carver. This
version of Scalpel reads Foremost 0.69 configuration files--see the
default configuration file, scalpel.conf, for more details.
Important note: The default configuration file has all supported file
patterns commented out--you must edit this before before running
Scalpel.
More details on execution options can be found in the Scalpel man
page, "scalpel.1".
Currently supported operating systems: Linux, Windows, Mac OS X.
If you decide to compile Scalpel on win32, you'll need to install
pthreads-win32 and hack the Makefile to reflect where you've installed
the pthreads include and lib directories. If you want to run Scalpel
on win32 w/o these hassles, just use the win32 executable that's
provided in the distribution.
COMPILE INSTRUCTIONS:
Linux: make
Win32: make win32 [or mingw32-make win32]
Mac OS X: make bsd
and enjoy. If you want to install the binary and man page in a more
permanent place, just copy "scalpel" and "scalpel.1" to appropriate
locations, e.g., on Linux, "/usr/local/bin" and "/usr/local/man/man1",
respectively. On Windows, you'll also need to copy "pthreadGC1.dll"
into the same directory as "scalpel.exe".
LIMITATIONS:
Carving Windows physical and logical device files (e.g.,
\\.\physicaldrive0 or \\.\c:) isn't currently supported, but will be supported
in a future release.
SUGGESTIONS:
Bug reports, comments, complaints, and feature requests should be
directed to the author at golden@digitalforensicssolutions.com.
The latest version of Scalpel is always available at
http://www.digitalforensicssolutions.com/Scalpel.
Cheers,
--Golden
/* basename.c -- return the last element in a path
Copyright (C) 1990, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include "dirname.h"
/* In general, we can't use the builtin `basename' function if available,
since it has different meanings in different environments.
In some environments the builtin `basename' modifies its argument.
Return the address of the last file name component of NAME. If
NAME has no file name components because it is all slashes, return
NAME if it is empty, the address of its last slash otherwise. */
char *
base_name (char const *name)
{
char const *base = name + FILESYSTEM_PREFIX_LEN (name);
char const *p;
for (p = base; *p; p++)
{
if (ISSLASH (*p))
{
/* Treat multiple adjacent slashes like a single slash. */
do p++;
while (ISSLASH (*p));
/* If the file name ends in slash, use the trailing slash as
the basename if no non-slashes have been found. */
if (! *p)
{
if (ISSLASH (*base))
base = p - 1;
break;
}
/* *P is a non-slash preceded by a slash. */
base = p;
}
}
return (char *) base;
}
/* Return the length of of the basename NAME. Typically NAME is the
value returned by base_name. Act like strlen (NAME), except omit
redundant trailing slashes. */
size_t
base_len (char const *name)
{
size_t len;
for (len = strlen (name); 1 < len && ISSLASH (name[len - 1]); len--)
continue;
return len;
}
char *base_name (char const *name);
1.4: First public beta release.
1.5: Added "-b" option for complete carving compability with
foremost 0.69.
1.51: Fixed a problem with Scalpel not carving all files near the end of
some large images. The locations of the files was detected, but
an optimization to minimize reads on pass 2 caused the files not to
be written (and not to be noted in the Scalpel log).
Also, more work to bring the Win32 version into compliance. :(
Fixed problem with Win32 version reporting
"Couldn't measure size of image file" for large images--required
ftello64()/fseeko64() under mingw. 40+GB images have now been
carved successfully under Win32.
Fixed format of offset in log file for large offsets under Win32;
the printf() implementation in mingw under Windows doesn't support
"%llu" for unsigned long long integers--needs "%I64u". Blech.
1.52 Foremost 0.69 didn't discover overlapping headers/footers. If you want
this behavior, use "-r" on the command line. Scalpel's default
behavior is now to discover all headers/footers, even if they overlap.
Different Quicktime needles (from the 'file' command's magic file)
are now used in the configuration file, because the default Foremost
needles didn't seem to match valid Quicktime files. The needles
are still weak, in that they cause a bunch of false positives.
The configuration file now uses more reasonable (in my opinion)
maximum carve sizes. By default, all file types are still
commented out.
1.53 Verbose mode implemented. "-v" outputs copious (too much?) information
about what Scalpel is doing as files are carved.
Mac OS X is now supported, including the use of raw block devices
(e.g., /dev/disk0) as carve targets. Use "make bsd" for an
OS X compilation.
1.54 Maximum size for carved files is now 18 exabytes. Minor changes to
Makefile. Fixed minor formatting problem in audit log. Scalpel
man page created. Eliminated duplicate error messages when
targets could not be opened. Thanks to John Vigo for additional
MAC OS X testing on this release.
1.60 Changes in this release include:
o Some of the headers and footers in the default
'scalpel.conf' file have been improved. In particular, the
GIF89 footer was made less restrictive; GIF89 files were
encountered in the wild for which the legacy Foremost footer
was too restrictive.
o REVERSE carving semantics now fixed as "helpers.c"
functions continue to evolve from their Foremost roots; this
is mostly useful for PDF carving and when some carved files
appear to be truncated--e.g., many times GIF files will
contain the footer string internally and with FORWARD carving,
will be truncated. REVERSE carving can help, but at the
expense of occasionally carving overly large files.
o Removed obsolete option "-f".
o Maximum number of files that can be carved per file type is
now 18,446,744,073,709,551,616. This is also the maximum
number of files of all combined types that can be carved in
a single run. This is unlikely to be a problematic upper
bound in the near term.
o The "-q" command line option is now available to force
block-aligned carving; only files with headers that begin on
block boundaries are carved. The block size for this mode is
user-specified.
o The "-r" option was not accepted in recent releases due to
an omission in Scalpel's command line argument parsing. This
option isn't generally used except for compatibility testing
with Foremost 0.69 (it actually forces Scalpel to mimic some
buggy Foremost behavior) and should be considered deprecated.
In a future release of Scalpel, it will be removed.
o Scalpel now organizes carved files into subdirectories by
type (and for each type, additional subdirectories are created
with a maximum of MAX_FILES_PER_SUBDIRECTORY [default: 5000]
files each). This will allow you to more comfortably explore
the set of carved files using the Windows or Linux file
explorers without having them choke on large subdirectories.
The files are organized within the Scalpel output directory
into subdirectories with the following naming convention:
suffix-rule#-counter
suffix-rule#-counter
...
suffix-rule#-counter
where 'suffix' matches the suffix specified in the Scalpel
configuration file (e.g, "jpg"), 'rule#' is the index of the
corresponding rule in the configuration file (with the first
uncommented rule being 0), and 'counter' being incremented after
every MAX_FILES_PER_SUBDIRECTORY files corresponding to a rule are
carved.
You can turn off this default organization by using the "-O"
command line flag (then Scalpel acts as in previous releases
and dumps all carved files into a single output directory).
o The "-p" performs a "preview" carve, creating an audit file
indicating which files would be carved, but without performing
carving operations. This substantially increases execution speed
and also supports "in-line" carving, when used in conjunction with
our custom FUSE filesystem.
o A memory corruption error when Scalpel had large numbers of
files open (typically, when a large number of file types were
specified in the configuration file) was corrected. Thanks to
both Doug Koster and Peter Theobald for pointing out this error.
o In this release some unused functions were removed
from the Scalpel source.
o A bug which arose for case-insensitive header/footer
specifications in the legacy Foremost Boyer-Moore string
search initialization was corrected. Thanks to Doug Koster
for pointing out this bug.
o Fixed (slightly) garbled invocation string in audit file; this
resulted from an unitialized string in legacy Foremost code.
o The "-d", "-m", "-t", and "-u" options are experimental and are
part of evolving support for interaction between Scalpel and
external tools, with the goal being better support for recovery
of fragmented files. Please contact the author -->
golden@digitalforensicssolutions.com <-- for more information if
these options appear useful to you. More information will be provided
when the necessary external tools are released.
This diff is collapsed.
/* Copyright (C) 1998, 2001 Free Software Foundation, Inc.
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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include <stddef.h>
#ifndef DIRNAME_H_
# define DIRNAME_H_ 1
# ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
# define PARAMS(Args) Args
# else
# define PARAMS(Args) ()
# endif
# endif
# ifndef DIRECTORY_SEPARATOR
# define DIRECTORY_SEPARATOR '/'
# endif
# ifndef ISSLASH
# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
# endif
# ifndef FILESYSTEM_PREFIX_LEN
# define FILESYSTEM_PREFIX_LEN(Filename) 0
# endif
char *base_name PARAMS ((char const *path));
char *dir_name PARAMS ((char const *path));
size_t base_len PARAMS ((char const *path));
size_t dir_len PARAMS ((char const *path));
int strip_trailing_slashes PARAMS ((char *path));
#endif /* not DIRNAME_H_ */
// Scalpel Copyright (C) 2005-6 by Golden G. Richard III.
// Written by Golden G. Richard III.
//
// 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.
// Scalpel is a complete rewrite of the foremost 0.69 file carver to
// increase speed and support execution on machines with minimal
// resources (e.g., < 256MB RAM).
//
// Thanks to Kris Kendall, Jesse Kornblum, et al for their work on
// foremost.
#include "scalpel.h"
// Returns TRUE if the directory exists and is empty.
// If the directory does not exist, an attempt is made to
// create it. On error, returns FALSE
int outputDirectoryOK(char *dir) {
DIR *temp;
struct dirent *entry;
int i;
mode_t newDirectoryMode;
if ((temp = opendir(dir)) == NULL) {
// If the directory doesn't exist (ENOENT), we will create it
if (errno == ENOENT) {
// The directory mode values come from the chmod(2) man page
#ifdef __MINGW32__
newDirectoryMode = 0;
if(mkdir(dir)){
#else
newDirectoryMode = (S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IWGRP | S_IXGRP |
S_IROTH | S_IWOTH);
if (mkdir(dir,newDirectoryMode)) {
#endif
fprintf (stderr,"An error occured while trying to create %s - %s\n",
dir,strerror(errno));
return FALSE;
}
// try to open directory
if ((temp = opendir(dir)) == NULL) {
fprintf (stderr,"An error occured while trying to open %s - %s\n",
dir,strerror(errno));
return FALSE;
}
}
else {
fprintf (stderr,"An error occured while trying to open %s - %s\n",
dir,strerror(errno));
return FALSE;
}
}
// verify directory is empty--there should be only two entries,
// "." and ".."
i=0;
while((entry = readdir(temp))) {
if (i>1) {
fprintf(stderr, NONEMPTYDIR_ERROR_MSG);
return FALSE;
}
i++;
}
closedir(temp);
return TRUE;
}
// open audit file
int openAuditFile(struct scalpelState* state){
time_t now = time(NULL);
char* timestring = ctime(&now);
char fn[MAX_STRING_LENGTH];
if (!outputDirectoryOK(state->outputdirectory)) {
return SCALPEL_ERROR_FILE_OPEN;
}
snprintf(fn,MAX_STRING_LENGTH,"%s/audit.txt",
state->outputdirectory);
if (!(state->auditFile = fopen(fn,"w"))) {
fprintf(stderr,"Couldn't open %s -- %s\n",fn,strerror(errno));
return SCALPEL_ERROR_FILE_OPEN;
}
fprintf (state->auditFile,
"\nScalpel version %s audit file\n"
"Started at %sCommand line:\n%s\n\n"
"Output directory: %s\n"
"Configuration file: %s\n",
SCALPEL_VERSION, timestring, state->invocation,
state->outputdirectory,state->conffile);
return SCALPEL_OK;
}
int closeFile(FILE* f) {
time_t now = time(NULL);
char* timestring = ctime(&now);
fprintf (f, "\n\nCompleted at %s", timestring);
if (fclose(f)) {
return SCALPEL_ERROR_FILE_CLOSE;
}
return SCALPEL_OK;
}
// helper function for measureOpenFile(), based on e2fsprogs utility
// function valid_offset()
static int valid_offset(int fd, off64_t offset) {
char ch;
if (lseek(fd, offset, SEEK_SET) < 0) {
return 0;
}
if (read(fd, &ch, 1) < 1) {
return 0;
}
return 1;
}
// Return the remaining size, in bytes, of an open file stream. On
// error, return -1. Handling raw device files is substantially more
// complicated than image files. For Linux, an ioctl() is used. For
// other operating systems, a "binary search" technique similar to
// that in e2fsprogs getsize.c is used.
unsigned long long measureOpenFile(FILE *f, struct scalpelState* state) {
unsigned long long total = 0, original = ftello(f);
int descriptor = 0;
struct stat *info;
unsigned long long numsectors = 0;
if ((fseeko(f,0,SEEK_END))) {
if (state->modeVerbose) {
fprintf(stdout, "fseeko() call failed on image file.\n");
fprintf(stdout, "Diagnosis: %s\n", strerror(errno));
}
return -1;
}
total = ftello(f);
// for block devices (e.g., raw disk devices), calculating size by
// seeking the end of the opened stream doesn't work. For Linux, we use
// an ioctl() call. For others (e.g., OS X), we use binary search.
// is it a block device?
descriptor = fileno(f);
info = (struct stat*)malloc(sizeof(struct stat));
fstat(descriptor,info);
if (S_ISBLK(info->st_mode)) {
#if defined (__LINUX)
if (ioctl(descriptor, BLKGETSIZE, &numsectors) < 0) {
if (state->modeVerbose) {
fprintf(stdout, "Using ioctl() call to measure block device size.\n");
}
#if defined(__DEBUG)
perror("BLKGETSIZE failed");
#endif
}
#else // non-Linux, use binary search
{
unsigned long long low, high, mid;
fprintf(stdout, "Using binary search to measure block device size.\n");
low = 0;
for (high = 512; valid_offset(descriptor, high); high *= 2) {
low = high;
}
while (low < high - 1) {
mid = (low + high) / 2;
if (valid_offset(descriptor, mid)) {
low = mid;
}
else {
high = mid;
}
}
numsectors = (low + 1) >> 9;
}
#endif
// assume device has 512 byte sectors
total = numsectors * 512;
free(info);
}
// restore file position
if ((fseeko(f,original,SEEK_SET))) {
if (state->modeVerbose) {
fprintf(stdout, "fseeko() call to restore file position failed on image file.\n");
}
return -1;
}
return (total - original);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
//
// priority queue header file "prioque.h" */
// (c) 198x/1998/2001/2004 (minor updates) by Golden G. Richard III, Ph.D. */
//
//
// Major update October 2004: The package is now thread-safe. The
// package now depends on the pthreads library (for access to
// semaphores). All functions now take one or more pointers to a
// queue or context, so existing applications will need to be
// modified slightly.
// Additionally, new functions are now provided for walking the queue
// "locally"--that is, more than one thread can maintain a position in
// the queue using a local Context. The old 'rewind_queue()',
// 'pointer_to_current()', etc. are maintained for a single position
// during a global walk.
//
// Finally, a long-standing memory leak was corrected.
//
#include <pthread.h>
#define TRUE 1
#define FALSE 0
#define CONSISTENCY_CHECKING
#if ! defined(QUEUE_TYPE_DEFINED)
#define QUEUE_TYPE_DEFINED
/* type of one element in a queue */
typedef struct _Queue_element {
void *info;
int priority;
struct _Queue_element *next;
} *Queue_element;
/* basic queue type */
typedef struct Queue {
Queue_element queue; /* linked list of elements */
Queue_element current; /* current position for sequential access functions */
Queue_element previous; /* one step back from current */
int queuelength; /* # of elements in queue */
int elementsize; /* 'sizeof()' one element */
int duplicates; /* are duplicates allowed? */
int (*compare)(void *e1, void *e2); /* element comparision function */
pthread_mutex_t lock;
} Queue;
typedef struct Context {
Queue_element current; /* current position for local seq access functions */
Queue_element previous; /* one step back from current */
Queue *queue; /* queue associated with this context */
} Context;
/********/
/*
NOTE: init_queue() must be called for a new queue before any other "prioque.c"
functions are called.
*/
/********/
/* function prototypes and descriptions for visible "prioque.c" functions
*/
////////////////////////////
// SECTION 1
////////////////////////////
/* initializes a new queue 'q' to have elements of size 'elementsize'.
If 'duplicates' is true, then duplicate elements in the queue are
allowed, otherwise duplicates are silently deleted. The
element-comparing function 'compare' is required only if
duplicates==FALSE or either equal_queues() or element_in_queue()
are used (otherwise, a null function is acceptable). 'compare'
should be a standard qsort()-style element comparison function:
returns 0 if elements match, otherwise a non-0 result (<, > cases
are not used).
NOTE:Only the 'compare' function is used for duplicate
detection--priority is not considered (i.e., attempting to add a
"duplicate" element that has a different priority than the existing
element will have no effect!)
*/
void init_queue(Queue *q, int elementsize, int duplicates,
int (*compare)(void *e1, void *e2));
/* destroys all elements in 'q'
*/
void destroy_queue(Queue *q);
/* adds 'element' to the 'q' with position based on 'priority'.
Elements with lower-numbered priorities are placed closer to the
front of the queue, with strict 'to the rear' placement for items
with equal priority [that is, given two items with equal priority,
the one most recently added will appear closer to the rear of the
queue].
*/
void add_to_queue(Queue *q, void *element, int priority);
/* removes the element at the front of the 'q' and places it in 'element'.
*/
void remove_from_front(Queue *q, void *element);
/* returns TRUE if the 'element' exists in the 'q', otherwise false.
The 'compare' function is used for matching. As a side-effect, the
current position in the queue is set to matching element, so
'update_current()' can be used to update the value of the
'element'. If the element is not found, the current position is
set to the first element in the queue.
*/
int element_in_queue(Queue *q, void *element);
/* returns TRUE if 'q' is empty, FALSE otherwise
*/
int empty_queue(Queue *q);
/* returns the number of elements in the 'q'.
*/
int queue_length(Queue *q);
/* makes a copy of 'q2' into 'q1'. 'q2' is not modified.
*/
void copy_queue(Queue *q1, Queue *q2);
/* determines if 'q1' and 'q2' are equivalent. Uses the 'compare'
function of the first queue, which should match the 'compare' for
the second! Returns TRUE if the queues are equal, otherwise
returns FALSE.
*/
int equal_queues(Queue *q1, Queue *q2);
/* merge 'q2' into 'q1'. 'q2' is not modified.
*/
void merge_queues(Queue *q1, Queue *q2);
////////////////////////////
// SECTION 2
////////////////////////////
/* the following are functions used to "walk" the queue (globally)
like a linked list, examining or deleting elements along the way.
Current position is rewound to the beginning by functions above (in
SECTION 1), except for 'empty_queue()' and 'queue_length()', which
do not modify the global current position.
*/
/********************/
/********************/
/* move to the first element in the 'q' */
void rewind_queue(Queue *q);
/* move to the next element in the 'q' */
void next_element(Queue *q);
/* allows update of current element. The priority should not
be changed by this function!
*/
void update_current(Queue *q, void *element);
/* retrieve the element stored at the current position in the 'q' */
void peek_at_current(Queue *q, void *element);
/* return a pointer to the data portion of the current element */
void *pointer_to_current(Queue *q);