1 // Scalpel Copyright (C) 2005-11 by Golden G. Richard III and
2 // 2007-11 by Vico Marziale.
3 //
4 // Written by Golden G. Richard III and Vico Marziale.
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 // 02110-1301, USA.
20 //
21 // Thanks to Kris Kendall, Jesse Kornblum, et al for their work
22 // on Foremost. Foremost 0.69 was used as the starting point for
23 // Scalpel, in 2005.
24 //
25
26 #include "scalpel.h"
27
28 // globals defined in scalpel.h
29
30 // signal has been caught by signal handler
31 int signal_caught;
32
33 // current wildcard character
34 char wildcard;
35
36 // width of tty, for progress bar
37 int ttywidth;
38
39 char *__scalpel__progname;
40
usage()41 void usage() {
42
43 printf(
44 "Scalpel carves files or data fragments from a disk image based on a set of\n"
45 "file carving patterns, which include headers, footers, and other information.\n\n"
46
47 "Usage: scalpel [-b] [-c <config file>] [-d] [-e] [-h] [-i <file>]\n"
48 "[-n] [-o <outputdir>] [-O] [-p] [-q <clustersize>] [-r]\n"
49
50 /* "[-s] [-m <blockmap file>] [-M <blocksize>] [-n] [-o <outputdir>]\n" */
51 /* "[-O] [-p] [-q <clustersize>] [-r] [-s <num>] [-u <blockmap file>]\n" */
52
53 "[-v] [-V] <imgfile> [<imgfile>] ...\n\n"
54
55
56
57 "Options:\n"
58
59 "-b Carve files even if defined footers aren't discovered within\n"
60 " maximum carve size for file type [foremost 0.69 compat mode].\n"
61
62 "-c Choose configuration file.\n"
63
64 "-d Generate header/footer database; will bypass certain optimizations\n"
65 " and discover all footers, so performance suffers. Doesn't affect\n"
66 " the set of files carved. **EXPERIMENTAL**\n"
67
68 "-e Do nested header/footer matching, to deal with structured files that may\n"
69 " contain embedded files of the same type. Applicable only to\n"
70 " FORWARD / NEXT patterns.\n"
71
72 "-h Print this help message and exit.\n"
73
74 "-i Read names of disk images from specified file. Note that minimal parsing of\n"
75 " the pathnames is performed and they should be formatted to be compliant C\n"
76 " strings; e.g., under Windows, backslashes must be properly quoted, etc.\n"
77
78 /*
79
80 "-m Use and update carve coverage blockmap file. If the blockmap file does\n"
81 " not exist, it is created. For new blockmap files, 512 bytes is used as\n"
82 " a default blocksize unless the -M option overrides this value. In the\n"
83 " blockmap file, the first 32bit unsigned int in the file identifies the\n"
84 " block size. Thereafter each 32bit unsigned int entry in the blockmap\n"
85 " file corresponds to one block in the image file. Each entry counts how\n"
86 " many carved files contain this block. Requires more system resources.\n"
87 " This feature is currently experimental.\n"
88
89 "-M Set blocksize for new coverage blockmap file.\n"
90
91 */
92
93
94 "-n Don't add extensions to extracted files.\n"
95
96 "-o Set output directory for carved files.\n"
97
98 "-O Don't organize carved files by type. Default is to organize carved files\n"
99 " into subdirectories.\n"
100
101 "-p Perform image file preview; audit log indicates which files\n"
102 " would have been carved, but no files are actually carved. Useful for\n"
103 " indexing file or data fragment locations or supporting in-place file\n"
104 " carving.\n"
105
106 "-q Carve only when header is cluster-aligned.\n"
107
108 "-r Find only first of overlapping headers/footers [foremost 0.69 compat mode].\n"
109
110 /*
111
112 "-s Skip num bytes in each disk image before carving.\n"
113
114
115 "-u Use (but don't update) carve coverage blockmap file when carving.\n"
116 " Carve only sections of the image whose entries in the blockmap are 0.\n"
117 " These areas are treated as contiguous regions. This feature is\n"
118 " currently experimental.\n"
119
120 */
121
122 "-V Print copyright information and exit.\n"
123
124 "-v Verbose mode.\n"
125 );
126 }
127
128
129 // signal handler, sets global variable 'signal_caught' which is
130 // checked periodically during carve operations. Allows clean
131 // shutdown.
catch_alarm(int signum)132 void catch_alarm(int signum) {
133 signal_caught = signum;
134 signal(signum, catch_alarm);
135
136 #ifdef __DEBUG
137 fprintf(stderr, "\nCaught signal: %s.\n", (char *)strsignal(signum));
138 #endif
139
140 fprintf(stderr, "\nKill signal detected. Cleaning up...\n");
141 }
142
143
144 int
extractSearchSpecData(struct scalpelState * state,struct SearchSpecLine * s,char ** tokenarray)145 extractSearchSpecData(struct scalpelState *state,
146 struct SearchSpecLine *s, char **tokenarray) {
147
148 int err = 0;
149
150 // process one line from config file:
151 // token[0] = suffix
152 // token[1] = case sensitive?
153 // token[2] = maximum carve size
154 // token[3] = begintag
155 // token[4] = endtag
156 // token[5] = search type (optional)
157
158 s->suffix = (char *)malloc(MAX_SUFFIX_LENGTH * sizeof(char));
159 checkMemoryAllocation(state, s->suffix, __LINE__, __FILE__, "s->suffix");
160 s->begin = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
161 checkMemoryAllocation(state, s->begin, __LINE__, __FILE__, "s->begin");
162 s->end = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
163 checkMemoryAllocation(state, s->end, __LINE__, __FILE__, "s->end");
164 s->begintext = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
165 checkMemoryAllocation(state, s->begintext, __LINE__, __FILE__, "s->begintext");
166 s->endtext = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
167 checkMemoryAllocation(state, s->endtext, __LINE__, __FILE__, "s->endtext");
168
169 if(!strncasecmp(tokenarray[0],
170 SCALPEL_NOEXTENSION_SUFFIX,
171 strlen(SCALPEL_NOEXTENSION_SUFFIX))) {
172 s->suffix[0] = SCALPEL_NOEXTENSION;
173 s->suffix[1] = 0;
174 }
175 else {
176 memcpy(s->suffix, tokenarray[0], MAX_SUFFIX_LENGTH);
177 }
178
179 // case sensitivity check
180 s->casesensitive = (!strncasecmp(tokenarray[1], "y", 1) ||
181 !strncasecmp(tokenarray[1], "yes", 3));
182
183 //#ifdef _WIN32
184 // s->length = _atoi64(tokenarray[2]);
185 //#else
186 // s->length = atoull(tokenarray[2]);
187 //#endif
188
189
190 char split[MAX_STRING_LENGTH];
191 char *maxcarvelength;
192
193 strcpy(split, tokenarray[2]);
194 maxcarvelength = strchr(split, ':');
195 if(!maxcarvelength) {
196 s->minlength = 0;
197 s->length = strtoull(split, 0, 10);
198 }
199 else {
200 *maxcarvelength = 0;
201 maxcarvelength++;
202 s->minlength = strtoull(split, 0, 10);
203 s->length = strtoull(maxcarvelength, 0, 10);
204 }
205
206 // determine search type for this needle
207 s->searchtype = SEARCHTYPE_FORWARD;
208 if(!strncasecmp(tokenarray[5], "REVERSE", strlen("REVERSE"))) {
209 s->searchtype = SEARCHTYPE_REVERSE;
210 }
211 else if(!strncasecmp(tokenarray[5], "NEXT", strlen("NEXT"))) {
212 s->searchtype = SEARCHTYPE_FORWARD_NEXT;
213 }
214 // FORWARD is the default, but OK if the user defines it explicitly
215 else if(!strncasecmp(tokenarray[5], "FORWARD", strlen("FORWARD"))) {
216 s->searchtype = SEARCHTYPE_FORWARD;
217 }
218
219 // regular expressions must be handled separately
220
221 if(isRegularExpression(tokenarray[3])) {
222
223 #ifdef GPU_THREADING
224 // GPU execution does not support regex needles.
225 fprintf(stderr, "ERROR: GPU search for regex headers is not supported!\n");
226 fprintf(stderr, "Please modify the config file for non-regex headers only.\n");
227 exit(1);
228 #endif
229
230 // copy RE, zap leading/training '/' and prepare for regular expression compilation
231 s->beginisRE = 1;
232 strcpy(s->begin, tokenarray[3]);
233 strcpy(s->begintext, tokenarray[3]);
234 s->beginlength = strlen(tokenarray[3]);
235 s->begin[s->beginlength] = 0;
236 // compile regular expression
237 err = regncomp(&(s->beginstate.re), s->begin+1, s->beginlength-2,
238 REG_EXTENDED | (REG_ICASE * (!s->casesensitive)));
239 if (err) {
240 return SCALPEL_ERROR_BAD_HEADER_REGEX;
241 }
242 }
243 else {
244 // non-regular expression header
245 s->beginisRE = 0;
246 strcpy(s->begintext, tokenarray[3]);
247 s->beginlength = translate(tokenarray[3]);
248 memcpy(s->begin, tokenarray[3], s->beginlength);
249 init_bm_table(s->begin, s->beginstate.bm_table, s->beginlength,
250 s->casesensitive);
251 }
252
253 if(isRegularExpression(tokenarray[4])) {
254
255 #ifdef GPU_THREADING
256 // GPU execution does not support regex needles.
257 fprintf(stderr, "ERROR: GPU search for regex footers is not supported!\n");
258 fprintf(stderr, "Please modify the config file for non-regex footers only.\n");
259 exit(1);
260 #endif
261
262 // copy RE, zap leading/training '/' and prepare for for regular expression compilation
263 s->endisRE = 1;
264 strcpy(s->end, tokenarray[4]);
265 strcpy(s->endtext, tokenarray[4]);
266 s->endlength = strlen(tokenarray[4]);
267 s->end[s->endlength] = 0;
268 // compile regular expression
269 err = regncomp(&(s->endstate.re), s->end+1, s->endlength-2,
270 REG_EXTENDED | (REG_ICASE * (!s->casesensitive)));
271
272 if(err) {
273 return SCALPEL_ERROR_BAD_FOOTER_REGEX;
274 }
275 }
276 else {
277 s->endisRE = 0;
278 strcpy(s->endtext, tokenarray[4]);
279 s->endlength = translate(tokenarray[4]);
280 memcpy(s->end, tokenarray[4], s->endlength);
281 init_bm_table(s->end, s->endstate.bm_table, s->endlength, s->casesensitive);
282 }
283
284 return SCALPEL_OK;
285 }
286
287
288
processSearchSpecLine(struct scalpelState * state,char * buffer,int lineNumber)289 int processSearchSpecLine(struct scalpelState *state, char *buffer, int lineNumber) {
290
291 char *buf = buffer;
292 char *token;
293 char **tokenarray = (char **)malloc(6 * sizeof(char[MAX_STRING_LENGTH + 1]));
294 int i = 0, err = 0, len = strlen(buffer);
295
296 checkMemoryAllocation(state, tokenarray, __LINE__, __FILE__, "tokenarray");
297
298 // murder CTRL-M (0x0d) characters
299 // if(buffer[len - 2] == 0x0d && buffer[len - 1] == 0x0a) {
300 if (len >= 2 && buffer[len - 2] == 0x0d && buffer[len - 1] == 0x0a) {
301 buffer[len - 2] = buffer[len - 1];
302 buffer[len - 1] = buffer[len];
303 }
304
305 buf = (char *)skipWhiteSpace(buf);
306 token = strtok(buf, " \t\n");
307
308 // lines beginning with # are comments
309 if(token == NULL || token[0] == '#') {
310 return SCALPEL_OK;
311 }
312
313 // allow wildcard to be changed
314 if(!strncasecmp(token, "wildcard", 9)) {
315 if((token = strtok(NULL, " \t\n")) != NULL) {
316 translate(token);
317 }
318 else {
319 fprintf(stdout,
320 "Warning: Empty wildcard in configuration file line %d. Ignoring.\n",
321 lineNumber);
322 return SCALPEL_OK;
323 }
324
325 if(strlen(token) > 1) {
326 fprintf(stderr, "Warning: Wildcard can only be one character,"
327 " but you specified %d characters.\n"
328 " Using the first character, \"%c\", as the wildcard.\n",
329 (int)strlen(token), token[0]);
330 }
331
332 wildcard = token[0];
333 return SCALPEL_OK;
334 }
335
336 while (token && (i < NUM_SEARCH_SPEC_ELEMENTS)) {
337 tokenarray[i] = token;
338 i++;
339 token = strtok(NULL, " \t\n");
340 }
341
342 switch (NUM_SEARCH_SPEC_ELEMENTS - i) {
343 case 2:
344 tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 1] = (char *)"";
345 tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 2] = (char *)"";
346 break;
347 case 1:
348 tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 1] = (char *)"";
349 break;
350 case 0:
351 break;
352 default:
353 fprintf(stderr,
354 "\nERROR: In line %d of the configuration file, expected %d tokens,\n"
355 " but instead found only %d.\n",
356 lineNumber, NUM_SEARCH_SPEC_ELEMENTS, i);
357 return SCALPEL_ERROR_NO_SEARCH_SPEC;
358 break;
359
360 }
361
362 if((err =
363 extractSearchSpecData(state, &(state->SearchSpec[state->specLines]),
364 tokenarray))) {
365 switch (err) {
366 case SCALPEL_ERROR_BAD_HEADER_REGEX:
367 fprintf(stderr,
368 "\nERROR: In line %d of the configuration file, bad regular expression for header.\n",
369 lineNumber);
370 break;
371 case SCALPEL_ERROR_BAD_FOOTER_REGEX:
372 fprintf(stderr,
373 "\nERROR: In line %d of the configuration file, bad regular expression for footer.\n",
374 lineNumber);
375 break;
376
377 default:
378 fprintf(stderr,
379 "\nERROR: Unknown error on line %d of the configuration file.\n",
380 lineNumber);
381 }
382 }
383 state->specLines++;
384 return SCALPEL_OK;
385 }
386
387
388 // process configuration file
readSearchSpecFile(struct scalpelState * state)389 int readSearchSpecFile(struct scalpelState *state) {
390
391 int lineNumber = 0, status;
392 FILE *f;
393
394 char *buffer =
395 (char *)malloc((NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH + 1) *
396 sizeof(char));
397 checkMemoryAllocation(state, buffer, __LINE__, __FILE__, "buffer");
398
399 f = fopen(state->conffile, "r");
400 if(f == NULL) {
401 fprintf(stderr,
402 "ERROR: Couldn't open configuration file:\n%s -- %s\n",
403 state->conffile, strerror(errno));
404 free(buffer);
405 return SCALPEL_ERROR_FATAL_READ;
406 }
407
408 while (fgets(buffer, NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH, f)) {
409 lineNumber++;
410
411 if(state->specLines > MAX_FILE_TYPES) {
412 fprintf(stderr, "Your conf file contains too many file types.\n");
413 fprintf(stderr,
414 "This version was compiled with MAX_FILE_TYPES == %d.\n",
415 MAX_FILE_TYPES);
416 fprintf(stderr, "Increase MAX_FILE_TYPES, recompile, and try again.\n");
417 free(buffer);
418 return SCALPEL_ERROR_TOO_MANY_TYPES;
419 }
420
421 if((status =
422 processSearchSpecLine(state, buffer, lineNumber)) != SCALPEL_OK) {
423 free(buffer);
424 return status;
425 }
426 }
427
428 // add an empty object to the end of the list as a marker
429
430 state->SearchSpec[state->specLines].suffix = NULL;
431 state->SearchSpec[state->specLines].casesensitive = 0;
432 state->SearchSpec[state->specLines].length = 0;
433 state->SearchSpec[state->specLines].begin = NULL;
434 state->SearchSpec[state->specLines].beginlength = 0;
435 state->SearchSpec[state->specLines].end = NULL;
436 state->SearchSpec[state->specLines].endlength = 0;
437
438 // GGRIII: offsets field is uninitialized--it doesn't
439 // matter, since we won't use this entry.
440
441 fclose(f);
442 free(buffer);
443 return SCALPEL_OK;
444 }
445
446 // Register the signal-handler that will write to the audit file and
447 // close it if we catch a SIGINT or SIGTERM
registerSignalHandlers()448 void registerSignalHandlers() {
449 if(signal(SIGINT, catch_alarm) == SIG_IGN) {
450 signal(SIGINT, SIG_IGN);
451 }
452 if(signal(SIGTERM, catch_alarm) == SIG_IGN) {
453 signal(SIGTERM, SIG_IGN);
454 }
455
456 #ifndef _WIN32
457 // *****GGRIII: is this problematic?
458 // From foremost 0.69:
459 /* Note: I haven't found a way to get notified of
460 console resize events in Win32. Right now the statusbar
461 will be too long or too short if the user decides to resize
462 their console window while foremost runs.. */
463
464 // signal(SIGWINCH, setttywidth);
465 #endif
466 }
467
468 // initialize state variable and copy command line arguments
initializeState(char ** argv,struct scalpelState * state)469 void initializeState(char **argv, struct scalpelState *state) {
470
471 char **argvcopy = argv;
472 int sss;
473 int i;
474
475 // Allocate memory for state
476 state->imagefile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
477 checkMemoryAllocation(state, state->imagefile, __LINE__, __FILE__,
478 "state->imagefile");
479 state->inputFileList = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
480 checkMemoryAllocation(state, state->inputFileList, __LINE__, __FILE__,
481 "state->inputFileList");
482 state->conffile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
483 checkMemoryAllocation(state, state->conffile, __LINE__, __FILE__,
484 "state->conffile");
485 state->outputdirectory = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
486 checkMemoryAllocation(state, state->conffile, __LINE__, __FILE__,
487 "state->outputdirectory");
488 state->invocation = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
489 checkMemoryAllocation(state, state->invocation, __LINE__, __FILE__,
490 "state->invocation");
491
492 // GGRIII: memory allocation made more sane, because we're storing
493 // more information in Scalpel than foremost had to, for each file
494 // type.
495 sss = (MAX_FILE_TYPES + 1) * sizeof(struct SearchSpecLine);
496 state->SearchSpec = (struct SearchSpecLine *)malloc(sss);
497 checkMemoryAllocation(state, state->SearchSpec, __LINE__, __FILE__,
498 "state->SearchSpec");
499 state->specLines = 0;
500
501 // GGRIII: initialize header/footer offset data, carved file count,
502 // et al. The header/footer database is re-initialized in "dig.c"
503 // after each image file is processed (numfilestocarve and
504 // organizeDirNum are not). Storage for the header/footer offsets
505 // will be reallocated as needed.
506
507 for(i = 0; i < MAX_FILE_TYPES; i++) {
508 state->SearchSpec[i].offsets.headers = 0;
509 state->SearchSpec[i].offsets.footers = 0;
510 state->SearchSpec[i].offsets.numheaders = 0;
511 state->SearchSpec[i].offsets.numfooters = 0;
512 state->SearchSpec[i].offsets.headerstorage = 0;
513 state->SearchSpec[i].offsets.footerstorage = 0;
514 state->SearchSpec[i].numfilestocarve = 0;
515 state->SearchSpec[i].organizeDirNum = 0;
516 }
517
518 state->fileswritten = 0;
519 state->skip = 0;
520 state->organizeMaxFilesPerSub = MAX_FILES_PER_SUBDIRECTORY;
521 state->modeVerbose = FALSE;
522 state->modeNoSuffix = FALSE;
523 state->useInputFileList = FALSE;
524 state->carveWithMissingFooters = FALSE;
525 state->noSearchOverlap = FALSE;
526 state->generateHeaderFooterDatabase = FALSE;
527 state->updateCoverageBlockmap = FALSE;
528 state->useCoverageBlockmap = FALSE;
529 state->coverageblocksize = 0;
530 state->blockAlignedOnly = FALSE;
531 state->organizeSubdirectories = TRUE;
532 state->previewMode = FALSE;
533 state->handleEmbedded = FALSE;
534 state->auditFile = NULL;
535
536 // default values for output directory, config file, wildcard character,
537 // coverage blockmap directory
538 strncpy(state->outputdirectory, SCALPEL_DEFAULT_OUTPUT_DIR,
539 strlen(SCALPEL_DEFAULT_OUTPUT_DIR));
540 strncpy(state->conffile, SCALPEL_DEFAULT_CONFIG_FILE, MAX_STRING_LENGTH);
541 state->coveragefile = state->outputdirectory;
542 wildcard = SCALPEL_DEFAULT_WILDCARD;
543 signal_caught = 0;
544 state->invocation[0] = 0;
545
546 // copy the invocation string into the state
547 do {
548 strncat(state->invocation,
549 *argvcopy, MAX_STRING_LENGTH - strlen(state->invocation));
550 strncat(state->invocation,
551 " ", MAX_STRING_LENGTH - strlen(state->invocation));
552 ++argvcopy;
553 }
554 while (*argvcopy);
555
556 registerSignalHandlers();
557 }
558
559 // parse command line arguments
processCommandLineArgs(int argc,char ** argv,struct scalpelState * state)560 void processCommandLineArgs(int argc, char **argv, struct scalpelState *state) {
561 char i;
562 int numopts = 1;
563
564 while ((i = getopt(argc, argv, "behvVu:ndpq:rc:o:s:i:m:M:O")) != -1) {
565 numopts++;
566 switch (i) {
567
568 case 'V':
569 fprintf(stdout, SCALPEL_COPYRIGHT_STRING);
570 exit(1);
571
572 case 'h':
573 usage();
574 exit(1);
575
576 case 's':
577 numopts++;
578 state->skip = strtoull(optarg, NULL, 10);
579 fprintf(stdout,
580 "Skipping the first %lld bytes of each image file.\n",
581 state->skip);
582 break;
583
584 case 'c':
585 numopts++;
586 strncpy(state->conffile, optarg, MAX_STRING_LENGTH);
587 break;
588
589 case 'd':
590 state->generateHeaderFooterDatabase = TRUE;
591 break;
592
593 case 'e':
594 state->handleEmbedded = TRUE;
595 break;
596
597 /*
598
599 case 'm':
600 numopts++;
601 state->updateCoverageBlockmap = TRUE;
602 state->useCoverageBlockmap = TRUE;
603 state->coveragefile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
604 checkMemoryAllocation(state, state->coveragefile, __LINE__,
605 __FILE__, "state->coveragefile");
606 strncpy(state->coveragefile, optarg, MAX_STRING_LENGTH);
607 break;
608
609 case 'u':
610 numopts++;
611 state->useCoverageBlockmap = TRUE;
612 state->coveragefile = (char *)malloc(MAX_STRING_LENGTH * sizeof(char));
613 checkMemoryAllocation(state, state->coveragefile, __LINE__,
614 __FILE__, "state->coveragefile");
615 strncpy(state->coveragefile, optarg, MAX_STRING_LENGTH);
616 break;
617
618 case 'M':
619 numopts++;
620 state->coverageblocksize = strtoul(optarg, NULL, 10);
621 if(state->coverageblocksize <= 0) {
622 fprintf(stderr,
623 "\nERROR: Invalid blocksize for -M command line option.\n");
624 exit(1);
625 }
626 break;
627
628 */
629
630 case 'o':
631 numopts++;
632 strncpy(state->outputdirectory, optarg, MAX_STRING_LENGTH);
633 break;
634
635 case 'O':
636 state->organizeSubdirectories = FALSE;
637 break;
638
639 case 'p':
640 state->previewMode = TRUE;
641 break;
642
643 case 'b':
644 state->carveWithMissingFooters = TRUE;
645 break;
646
647 case 'i':
648 state->useInputFileList = TRUE;
649 state->inputFileList = optarg;
650 break;
651
652 case 'n':
653 state->modeNoSuffix = TRUE;
654 fprintf(stdout, "Extracting files without filename extensions.\n");
655 break;
656
657 case 'q':
658 numopts++;
659 state->blockAlignedOnly = TRUE;
660 state->alignedblocksize = strtoul(optarg, NULL, 10);
661 if(state->alignedblocksize <= 0) {
662 fprintf(stderr,
663 "\nERROR: Invalid blocksize for -q command line option.\n");
664 exit(1);
665 }
666 break;
667
668 case 'r':
669 state->noSearchOverlap = TRUE;
670 break;
671
672 case 'v':
673 state->modeVerbose = TRUE;
674 break;
675
676 default:
677 exit(1);
678 }
679 }
680
681 // check for incompatible options
682
683 if((state->useInputFileList || argc - numopts > 1) &&
684 (state->updateCoverageBlockmap || state->useCoverageBlockmap)) {
685
686
687 fprintf(stderr, "%d %d\n", argc, numopts);
688
689 fprintf(stderr,
690 "\nCoverage blockmaps can be processed only if a single image filename is\n"
691 "specified on the command line.\n");
692 exit(1);
693 }
694 }
695
696 // full pathnames for all files used
convertFileNames(struct scalpelState * state)697 void convertFileNames(struct scalpelState *state) {
698
699 char fn[MAX_STRING_LENGTH]; // should be [PATH_MAX +1] from limits.h
700
701 if(realpath(state->outputdirectory, fn)) {
702 strncpy(state->outputdirectory, fn, MAX_STRING_LENGTH);
703 }
704 else {
705 // perror("realpath");
706 }
707
708 if(realpath(state->conffile, fn)) {
709 strncpy(state->conffile, fn, MAX_STRING_LENGTH);
710 }
711 else {
712 // perror("realpath");
713 }
714
715 }
716
717
718 // GGRIII: for each file, build header/footer offset database first,
719 // then carve files based on this database. Need to clear the
720 // header/footer offset database after processing of each file.
721
digAllFiles(char ** argv,struct scalpelState * state)722 void digAllFiles(char **argv, struct scalpelState *state) {
723
724 int i = 0, j = 0;
725 FILE *listoffiles = NULL;
726
727 if(state->useInputFileList) {
728 fprintf(stdout, "Batch mode: reading list of images from %s.\n",
729 state->inputFileList);
730 listoffiles = fopen(state->inputFileList, "r");
731 if(listoffiles == NULL) {
732 fprintf(stderr, "Couldn't open file:\n%s -- %s\n",
733 (*(state->inputFileList) ==
734 '\0') ? "<blank>" : state->inputFileList, strerror(errno));
735 handleError(state, SCALPEL_ERROR_FATAL_READ);
736 exit(-1);
737 }
738 j = 0;
739 do {
740 j++;
741
742 if(fgets(state->imagefile, MAX_STRING_LENGTH, listoffiles) == NULL) {
743 if (!feof(listoffiles)) {
744 fprintf(stderr,
745 "Error reading line %d of %s. Skipping line.\n",
746 j, state->inputFileList);
747 }
748 continue;
749 }
750 if(state->imagefile[strlen(state->imagefile) - 1] == '\n') {
751 state->imagefile[strlen(state->imagefile) - 1] = '\x00';
752 }
753
754 // GGRIII: this function now *only* builds the header/footer
755 // database. Carving is handled afterward, in carveImageFile().
756
757 if((i = digImageFile(state)) != SCALPEL_OK) {
758 handleError(state, i);
759 continue;
760 }
761 else {
762 // GGRIII: "digging" is now complete and header/footer database
763 // has been built. The function carveImageFile() performs
764 // extraction of files based on this database.
765
766 if((i = carveImageFile(state)) != SCALPEL_OK) {
767 handleError(state, i);
768 }
769 }
770 }
771 while (!feof(listoffiles));
772 fclose(listoffiles);
773 }
774 else {
775 do {
776 state->imagefile = *argv;
777
778 // GGRIII: this function now *only* builds the header/footer
779 // database. Carving is handled afterward, in carveImageFile().
780
781 if((i = digImageFile(state))) {
782 handleError(state, i);
783 continue;
784 }
785 else {
786 // GGRIII: "digging" is now complete and header/footer database
787 // has been built. The function carveImageFile() performs extraction
788 // of files based on this database.
789
790 if((i = carveImageFile(state))) {
791 handleError(state, i);
792 }
793 }
794 ++argv;
795 }
796 while (*argv);
797 }
798 }
799
main(int argc,char ** argv)800 int main(int argc, char **argv) {
801
802 time_t starttime = time(0);
803 struct scalpelState state;
804
805
806 ///////// JUST A TEST FOR WIN32 TSK LINKAGE ///////////
807 // TSK_TCHAR *imgs=_TSK_T("c:\\128MB.dd");
808 // TSK_IMG_INFO *img = tsk_img_open(_TSK_T("raw"), 1, (const TSK_TCHAR **)&imgs);
809 // printf("TSK size is %d bytes\n", (int)img->size);
810 ///////// END JUST A TEST FOR WIN32 TSK LINKAGE ///////////
811
812
813 if(ldiv(SIZE_OF_BUFFER, SCALPEL_BLOCK_SIZE).rem != 0) {
814 fprintf(stderr, SCALPEL_SIZEOFBUFFER_PANIC_STRING);
815 exit(-1);
816 }
817
818 #ifndef __GLIBC__
819 setProgramName(argv[0]);
820 #endif
821
822 fprintf(stdout, SCALPEL_BANNER_STRING);
823
824 initializeState(argv, &state);
825 processCommandLineArgs(argc, argv, &state);
826 convertFileNames(&state);
827
828 // read configuration file
829 int err;
830 if((err = readSearchSpecFile(&state))) {
831 // problem with config file
832 handleError(&state, err);
833 exit(-1);
834 }
835
836 setttywidth();
837
838 argv += optind;
839 if(*argv != NULL || state.useInputFileList) {
840 // prepare audit file and make sure output directory is empty.
841 int err;
842 if((err = openAuditFile(&state))) {
843 handleError(&state, err);
844 exit(-1);
845 }
846
847 // Initialize the backing store of buffer to read-in, process image data.
848 init_store();
849
850 // Initialize threading model for cpu or gpu search.
851 init_threading_model(&state);
852
853
854 digAllFiles(argv, &state);
855 closeAuditFile(state.auditFile);
856 }
857 else {
858 usage();
859 fprintf(stdout, "\nERROR: No image files specified.\n\n");
860 }
861
862 #ifdef _WIN32
863 fprintf(stdout,
864 "\nScalpel is done, files carved = %I64u, elapsed = %ld secs.\n",
865 state.fileswritten, (int)time(0) - starttime);
866 #else
867 fprintf(stdout,
868 "\nScalpel is done, files carved = %llu, elapsed = %ld secs.\n",
869 state.fileswritten, (int)time(0) - starttime);
870 #endif
871
872 return 0;
873 }
874