1 /*
2 *
3 * The Sleuth Kit
4 *
5 * Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
6 * Copyright (c) 2011-2012 Basis Technology Corporation. All Rights
7 * reserved.
8 *
9 * This software is distributed under the Common Public License 1.0
10 */
11 #include <iostream>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <string>
15 #include <sstream>
16 #include <time.h>
17 #include <memory>
18
19 #include "tsk/tsk_tools_i.h" // Needed for tsk_getopt
20 #include "tsk/framework/framework.h"
21 #include "tsk/framework/services/TskSchedulerQueue.h"
22 #include "tsk/framework/services/TskSystemPropertiesImpl.h"
23 #include "tsk/framework/services/TskImgDBSqlite.h"
24 #include "tsk/framework/file/TskFileManagerImpl.h"
25 #include "tsk/framework/extraction/TskCarvePrepSectorConcat.h"
26 #include "tsk/framework/extraction/TskCarveExtractScalpel.h"
27 #include "tsk/framework/extraction/TskExtract.h"
28
29 #include "Poco/Path.h"
30 #include "Poco/File.h"
31
32 #ifdef TSK_WIN32
33 #include <Windows.h>
34 #else
35 #include <sys/stat.h>
36 #endif
37
38 #include "Poco/File.h"
39 #include "Poco/UnicodeConverter.h"
40
41 static uint8_t
makeDir(const char * dir)42 makeDir(const char *dir)
43 {
44 Poco::File path(dir);
45 try {
46 if (!path.createDirectory()) {
47 fprintf(stderr, "Error creating directory: %s\n", dir);
48 return 1;
49 }
50 } catch (const Poco::Exception &ex) {
51 std::stringstream msg;
52 msg << "Error creating directory: " << dir << " Poco exception: " << ex.displayText();
53 fprintf(stderr, "%s\n", msg.str().c_str());
54 return 1;
55 }
56 return 0;
57 }
58
59 /**
60 * Logs all messages to a log file and prints
61 * error messages to STDERR
62 */
63 class StderrLog : public Log
64 {
65 public:
StderrLog()66 StderrLog() : Log() {
67 }
68
~StderrLog()69 ~StderrLog() {
70 }
71
log(Channel a_channel,const std::wstring & a_msg)72 void log(Channel a_channel, const std::wstring &a_msg)
73 {
74 Log::log(a_channel, a_msg);
75 if (a_channel != Error) {
76 return;
77 }
78 fprintf(stderr, "%S\n", a_msg.c_str());
79 }
80 };
81
82 void
usage(const char * program)83 usage(const char *program)
84 {
85 fprintf(stderr, "%s [-c framework_config_file] [-p pipeline_config_file] [-d outdir] [-C] [-v] [-V] [-L] image_name\n", program);
86 fprintf(stderr, "\t-c framework_config_file: Path to XML framework config file\n");
87 fprintf(stderr, "\t-p pipeline_config_file: Path to XML pipeline config file (overrides pipeline config specified with -c)\n");
88 fprintf(stderr, "\t-d outdir: Path to output directory\n");
89 fprintf(stderr, "\t-C: Disable carving, overriding framework config file settings\n");
90 fprintf(stderr, "\t-u: Enable unused sector file creation\n");
91 fprintf(stderr, "\t-v: Enable verbose mode to get more debug information\n");
92 fprintf(stderr, "\t-V: Display the tool version\n");
93 fprintf(stderr, "\t-L: Print no error messages to STDERR -- only log them\n");
94 exit(1);
95 }
96
main(int argc,char ** argv1)97 int main(int argc, char **argv1)
98 {
99 TSK_TCHAR **argv;
100 extern int OPTIND;
101 int ch;
102 std::string pipeline_config;
103 std::string framework_config;
104 std::string outDirPath;
105 bool suppressSTDERR = false;
106 bool doCarving = true;
107 bool createUnusedSectorFiles = false;
108
109 #ifdef TSK_WIN32
110 // On Windows, get the wide arguments (mingw doesn't support wmain)
111 argv = CommandLineToArgvW(GetCommandLineW(), &argc);
112 if (argv == NULL) {
113 fprintf(stderr, "Error getting wide arguments\n");
114 exit(1);
115 }
116 #else
117 argv = (TSK_TCHAR **) argv1;
118 #endif
119
120 while ((ch =
121 GETOPT(argc, argv, _TSK_T("d:c:p:vuVLC"))) > 0) {
122 switch (ch) {
123 case _TSK_T('c'):
124 #ifdef TSK_WIN32
125 framework_config.assign(TskUtilities::toUTF8(std::wstring(OPTARG)));
126 #else
127 framework_config.assign(OPTARG);
128 #endif
129 break;
130
131 case _TSK_T('p'):
132 #ifdef TSK_WIN32
133 pipeline_config.assign(TskUtilities::toUTF8(std::wstring(OPTARG)));
134 #else
135 pipeline_config.assign(OPTARG);
136 #endif
137 break;
138 case _TSK_T('u'):
139 createUnusedSectorFiles = true;
140 break;
141 case _TSK_T('v'):
142 tsk_verbose++;
143 break;
144
145 case _TSK_T('V'):
146 tsk_version_print(stdout);
147 exit(0);
148 break;
149
150 case _TSK_T('d'):
151 #ifdef TSK_WIN32
152 outDirPath.assign(TskUtilities::toUTF8(std::wstring(OPTARG)));
153 #else
154 outDirPath.assign(OPTARG);
155 #endif
156 break;
157
158 case _TSK_T('C'):
159 doCarving = false;
160 break;
161
162 case _TSK_T('L'):
163 suppressSTDERR = true;
164 break;
165
166 case _TSK_T('?'):
167 default:
168 TFPRINTF(stderr, _TSK_T("Invalid argument: %s\n"),
169 argv[OPTIND]);
170 usage(argv1[0]);
171 }
172 }
173
174 /* We need at least one more argument */
175 if (OPTIND == argc) {
176 tsk_fprintf(stderr, "Missing image name\n");
177 usage(argv1[0]);
178 }
179
180 std::string imagePath;
181 #ifdef TSK_WIN32
182 imagePath = TskUtilities::toUTF8(std::wstring(argv[OPTIND]));
183 #else
184 imagePath = argv1[OPTIND];
185 #endif
186
187 if (!Poco::File(imagePath).exists()) {
188 std::stringstream msg;
189 msg << "Image file not found: " << imagePath;
190 LOGERROR(msg.str());
191 return 1;
192 }
193
194 // Load the framework config if they specified it
195 try
196 {
197 // try the one specified on the command line
198 if (framework_config.size()) {
199 TskSystemPropertiesImpl *systemProperties = new TskSystemPropertiesImpl();
200 systemProperties->initialize(framework_config);
201 TskServices::Instance().setSystemProperties(*systemProperties);
202 }
203 // try the one in the current directory
204 else if (Poco::File("framework_config.xml").exists()) {
205 TskSystemPropertiesImpl *systemProperties = new TskSystemPropertiesImpl();
206 systemProperties->initialize("framework_config.xml");
207 TskServices::Instance().setSystemProperties(*systemProperties);
208 }
209 // try one back up a few directories for the use case that we built this in
210 // the source tree.
211 else {
212 TskSystemPropertiesImpl *systemProperties = new TskSystemPropertiesImpl();
213 systemProperties->initialize();
214 std::string progdir = systemProperties->get(TskSystemProperties::PROG_DIR);
215 std::string configPath = progdir + "../../../runtime/framework_config.xml";
216 if (Poco::File(configPath).exists()) {
217 systemProperties->initialize(configPath);
218 TskServices::Instance().setSystemProperties(*systemProperties);
219 } else {
220 fprintf(stderr, "No framework config file found\n");
221 }
222 }
223 }
224 catch (TskException& ex)
225 {
226 fprintf(stderr, "Loading framework config file: %s\n", ex.message().c_str());
227 return 1;
228 }
229
230 // if they didn't specify the output directory, make one
231 if (outDirPath == "") {
232 outDirPath.assign(imagePath);
233 outDirPath.append("_tsk_out");
234 }
235 if (Poco::File(outDirPath).exists()) {
236 std::stringstream msg;
237 msg << "Output directory already exists " << outDirPath;
238 LOGERROR(msg.str());
239 return 1;
240 }
241
242 SetSystemProperty(TskSystemProperties::OUT_DIR, outDirPath);
243 // make the output dirs, makeDir() logs the error.
244 if (makeDir(outDirPath.c_str())) {
245 return 1;
246 }
247
248 if (makeDir(GetSystemProperty(TskSystemProperties::SYSTEM_OUT_DIR).c_str())) {
249 return 1;
250 }
251
252 if (makeDir(GetSystemProperty(TskSystemProperties::MODULE_OUT_DIR).c_str())) {
253 return 1;
254 }
255
256 std::string logDir = GetSystemProperty(TskSystemProperties::LOG_DIR);
257 if (makeDir(logDir.c_str())) {
258 return 1;
259 }
260
261
262 // Create a log object
263 struct tm * newtime;
264 time_t aclock;
265
266 time(&aclock); // Get time in seconds
267 newtime = localtime(&aclock);
268 char filename[MAX_BUFF_LENGTH];
269 snprintf(filename, MAX_BUFF_LENGTH, "/log_%.4d-%.2d-%.2d-%.2d-%.2d-%.2d.txt",
270 newtime->tm_year + 1900, newtime->tm_mon+1, newtime->tm_mday,
271 newtime->tm_hour, newtime->tm_min, newtime->tm_sec);
272
273 logDir.append(filename);
274 std::auto_ptr<Log> log(NULL);
275
276 if(suppressSTDERR)
277 log = std::auto_ptr<Log>(new Log());
278 else
279 log = std::auto_ptr<Log>(new StderrLog());
280
281 log->open(logDir.c_str());
282 TskServices::Instance().setLog(*log);
283
284 // Create and register our SQLite ImgDB class
285 std::auto_ptr<TskImgDB> pImgDB(NULL);
286 pImgDB = std::auto_ptr<TskImgDB>(new TskImgDBSqlite(outDirPath.c_str()));
287 if (pImgDB->initialize() != 0) {
288 std::stringstream msg;
289 msg << "Error initializing SQLite database: " << outDirPath;
290 LOGERROR(msg.str());
291 return 1;
292 }
293
294 // @@@ Call pImgDB->addToolInfo() as needed to set version info...
295
296 TskServices::Instance().setImgDB(*pImgDB);
297
298 // Create a Blackboard and register it with the framework.
299 TskServices::Instance().setBlackboard((TskBlackboard &) TskDBBlackboard::instance());
300
301 if (pipeline_config.size())
302 SetSystemProperty(TskSystemProperties::PIPELINE_CONFIG_FILE, pipeline_config);
303
304 // Create a Scheduler and register it
305 TskSchedulerQueue scheduler;
306 TskServices::Instance().setScheduler(scheduler);
307
308 // Create a FileManager and register it with the framework.
309 TskServices::Instance().setFileManager(TskFileManagerImpl::instance());
310
311 TskImageFileTsk imageFileTsk;
312
313 // Check to see if input image is actually a container file
314 TskArchiveExtraction::ExtractorPtr containerExtractor = TskArchiveExtraction::createExtractor(imagePath);
315
316 if (containerExtractor.isNull())
317 {
318 // Create an ImageFile and register it with the framework.
319 if (imageFileTsk.open(imagePath) != 0) {
320 std::stringstream msg;
321 msg << "Error opening image: " << imagePath;
322 LOGERROR(msg.str());
323 return 1;
324 }
325 TskServices::Instance().setImageFile(imageFileTsk);
326 }
327
328 // Let's get the pipelines setup to make sure there are no errors.
329 TskPipelineManager pipelineMgr;
330 TskPipeline *filePipeline;
331 try {
332 filePipeline = pipelineMgr.createPipeline(TskPipelineManager::FILE_ANALYSIS_PIPELINE);
333 }
334 catch (const TskException &e ) {
335 std::stringstream msg;
336 msg << "Error creating file analysis pipeline: " << e.message();
337 LOGERROR(msg.str());
338 filePipeline = NULL;
339 }
340
341 TskPipeline *reportPipeline;
342 try {
343 reportPipeline = pipelineMgr.createPipeline(TskPipelineManager::POST_PROCESSING_PIPELINE);
344 }
345 catch (const TskException &e ) {
346 std::stringstream msg;
347 msg << "Error creating reporting pipeline: " << e.message();
348 LOGERROR(msg.str());
349 reportPipeline = NULL;
350 }
351
352 if ((filePipeline == NULL) && (reportPipeline == NULL)) {
353 std::stringstream msg;
354 msg << "No pipelines configured. Stopping";
355 LOGERROR(msg.str());
356 exit(1);
357 }
358
359 // Now we analyze the data.
360
361 std::auto_ptr<TskCarveExtractScalpel> carver(new TskCarveExtractScalpel(createUnusedSectorFiles));
362
363 // Extract
364 if (!containerExtractor.isNull()) // Input is an archive file
365 {
366 if (containerExtractor->extractFiles() != 0)
367 {
368 std::wstringstream msg;
369 msg << L"Error adding archived file info to database";
370 LOGERROR(msg.str());
371 return 1;
372 }
373 }
374 else // Input is an image file
375 {
376 if (imageFileTsk.extractFiles() != 0)
377 {
378 std::wstringstream msg;
379 msg << L"Error adding file system info to database";
380 LOGERROR(msg.str());
381 return 1;
382 }
383
384 if (doCarving && !GetSystemProperty("SCALPEL_DIR").empty())
385 {
386 TskCarvePrepSectorConcat carvePrep;
387 carvePrep.processSectors();
388 carver.reset(new TskCarveExtractScalpel());
389 }
390 }
391
392 TskSchedulerQueue::task_struct *task;
393 while ((task = scheduler.nextTask()) != NULL)
394 {
395 try
396 {
397 if (task->task == Scheduler::FileAnalysis && filePipeline && !filePipeline->isEmpty())
398 {
399 filePipeline->run(task->id);
400 }
401 else if (task->task == Scheduler::Carve && carver.get())
402 {
403 carver->processFile(static_cast<int>(task->id));
404 }
405 else
406 {
407 std::stringstream msg;
408 msg << "WARNING: Skipping task: " << task->task;
409 LOGWARN(msg.str());
410 }
411 delete task;
412 }
413 catch (...)
414 {
415 // Error message has been logged already.
416 }
417 }
418
419 if (filePipeline && !filePipeline->isEmpty())
420 {
421 filePipeline->logModuleExecutionTimes();
422 }
423
424 // Do image analysis tasks.
425 if (reportPipeline)
426 {
427 try
428 {
429 reportPipeline->run();
430 }
431 catch (...)
432 {
433 std::stringstream msg;
434 msg << "Error running reporting pipeline";
435 LOGERROR(msg.str());
436 return 1;
437 }
438
439 if (!reportPipeline->isEmpty())
440 {
441 reportPipeline->logModuleExecutionTimes();
442 }
443 }
444
445 std::stringstream msg;
446 msg << "image analysis complete";
447 LOGINFO(msg.str());
448 cout << "Results saved to " << outDirPath << std::endl;
449 return 0;
450 }
451
452