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