1 /*
2 * The Sleuth Kit
3 *
4 * Contact: Brian Carrier [carrier <at> sleuthkit [dot] org]
5 * Copyright (c) 2010-2012 Basis Technology Corporation. All Rights
6 * reserved.
7 *
8 * This software is distributed under the Common Public License 1.0
9 */
10
11 #include <string>
12 #include <sstream>
13 #include <string.h>
14
15 #include "TskAutoImpl.h"
16 #include "tsk/framework/services/TskServices.h"
17
18 #define TSK_SCHEMA_VER 1
19
TSKAutoImpl()20 TSKAutoImpl::TSKAutoImpl() : m_db(TskServices::Instance().getImgDB()), m_numFilesSeen(0)
21 {
22 m_curFsId = 0;
23 m_curVsId = 0;
24 m_vsSeen = false;
25 m_lastUpdateMsg = 0;
26
27 setVolFilterFlags((TSK_VS_PART_FLAG_ENUM)(TSK_VS_PART_FLAG_ALLOC | TSK_VS_PART_FLAG_UNALLOC));
28 setFileFilterFlags((TSK_FS_DIR_WALK_FLAG_ENUM)(TSK_FS_DIR_WALK_FLAG_ALLOC|TSK_FS_DIR_WALK_FLAG_UNALLOC));
29
30 // add the version to the DB
31 m_db.addToolInfo("Sleuth Kit", tsk_version_get_str());
32 }
33
~TSKAutoImpl()34 TSKAutoImpl::~TSKAutoImpl()
35 {
36 }
37
openImage(TSK_IMG_INFO * a_img_info)38 uint8_t TSKAutoImpl::openImage(TSK_IMG_INFO *a_img_info)
39 {
40 m_curFsId = 0;
41 m_curVsId = 0;
42
43 return TskAuto::openImageHandle(a_img_info);
44 }
45
46 void
closeImage()47 TSKAutoImpl::closeImage()
48 {
49 TskAuto::closeImage();
50 }
51
52
53 /**
54 * Main method to call for this class after image has been opened as it takes care of the transactions.
55 */
extractFiles()56 uint8_t TSKAutoImpl::extractFiles()
57 {
58 m_db.begin();
59 uint8_t retval = findFilesInImg();
60 commitAndSchedule();
61 return retval;
62 }
63
64 /**
65 * Scan the image for file systems creating allocated volumes for file systems found
66 * and unallocated volumes for areas in the image that do not contain file systems.
67 * Will initially look for file system in first sect_count sectors. If a file system
68 * is found then it will continue to process the remainder of the image for other
69 * file systems.
70 *
71 * @param sect_start Start looking for file systems starting at this sector.
72 * @param sect_count The initial number of sectors to scan for file systems.
73 * @return 0 on success, 1 on failure
74 */
scanImgForFs(const uint64_t sect_start,const uint64_t sect_count)75 uint8_t TSKAutoImpl::scanImgForFs(const uint64_t sect_start, const uint64_t sect_count)
76 {
77 if (m_img_info == NULL)
78 {
79 LOGERROR(L"TSKAutoImpl::scanImgForFs - Image not open.");
80 return 1;
81 }
82
83 LOGINFO(L"TSKAutoImpl::scanImgForFs - Starting file system scan.");
84
85 // Initialize current offset to our starting byte location.
86 TSK_OFF_T current_offset = sect_start * m_img_info->sector_size;
87
88 TSK_OFF_T end_offset = current_offset + (sect_count * m_img_info->sector_size);
89
90 // Last offset keeps track of byte location where we last saw file system
91 // data. It gets initialized to our starting location.
92 TSK_OFF_T last_offset = current_offset;
93
94 while (current_offset < end_offset)
95 {
96 TSK_FS_INFO * fs_info;
97
98 if ((fs_info = tsk_fs_open_img(m_img_info,
99 current_offset,
100 TSK_FS_TYPE_DETECT)) == NULL)
101 {
102 // We didn't find a file system so we move on to the next sector.
103 current_offset += m_img_info->sector_size;
104 }
105 else
106 {
107 // We found a file system so we will continue to search for file
108 // systems beyond the initial sectors.
109 end_offset = m_img_info->size;
110
111 // If there is a gap between the location of this file system and
112 // where we last saw file system data, an unallocated volume entry
113 // needs to be created for the gap.
114 if (fs_info->offset > last_offset)
115 {
116 createDummyVolume(last_offset / m_img_info->sector_size,
117 (fs_info->offset - last_offset) / m_img_info->sector_size,
118 "Dummy volume for carving purposes",
119 TSK_VS_PART_FLAG_UNALLOC);
120 }
121
122 /* The call to findFilesInFs will take care of creating a
123 * dummy volume for the file system.*/
124 /* errors encountered during this phase will have been
125 * logged. */
126 findFilesInFs(fs_info);
127
128 // Move the current offset past the file system we just found.
129 current_offset += ((fs_info->block_count + 1) * fs_info->block_size);
130
131 // Update the last location we saw file system data.
132 last_offset = current_offset;
133
134 tsk_fs_close(fs_info);
135 }
136 }
137
138 // Finally, create a dummy unallocated volume for the area between the
139 // last offset and the end of the image.
140 if (last_offset < m_img_info->size)
141 {
142 createDummyVolume(last_offset / m_img_info->sector_size,
143 (m_img_info->size - last_offset) / m_img_info->sector_size,
144 "Dummy volume for carving purposes",
145 TSK_VS_PART_FLAG_UNALLOC);
146 }
147
148 LOGINFO(L"TSKAutoImpl::scanImgForFs - File system scan complete.");
149
150 return 0;
151 }
152
filterVol(const TSK_VS_PART_INFO * a_vsPart)153 TSK_FILTER_ENUM TSKAutoImpl::filterVol(const TSK_VS_PART_INFO * a_vsPart)
154 {
155 // flag that this image has a volume system
156 m_vsSeen = true;
157 m_db.addVolumeInfo(a_vsPart);
158
159 m_curVsId = a_vsPart->addr;
160
161 std::wstringstream msg;
162 msg << L"TSKAutoImpl::filterVol - Discovered " << a_vsPart->desc
163 << L" partition (sectors " << a_vsPart->start << L"-"
164 << ((a_vsPart->start + a_vsPart->len) - 1) << L")";
165 LOGINFO(msg.str());
166
167 // we only want to process the allocated volumes
168 if ((a_vsPart->flags & TSK_VS_PART_FLAG_ALLOC) == 0)
169 return TSK_FILTER_SKIP;
170
171 return TSK_FILTER_CONT;
172 }
173
174
filterFs(TSK_FS_INFO * a_fsInfo)175 TSK_FILTER_ENUM TSKAutoImpl::filterFs(TSK_FS_INFO * a_fsInfo)
176 {
177 // add a volume entry if there is no file system
178 if (m_vsSeen == false)
179 {
180 TSK_DADDR_T start_sect = a_fsInfo->offset / a_fsInfo->img_info->sector_size;
181 TSK_DADDR_T end_sect = start_sect +
182 ((a_fsInfo->block_count * a_fsInfo->block_size) / a_fsInfo->img_info->sector_size);
183
184 createDummyVolume(start_sect, (end_sect - start_sect) + 1,
185 "Dummy volume for file system",
186 TSK_VS_PART_FLAG_ALLOC);
187 }
188
189 m_curFsId++;
190 m_db.addFsInfo(m_curVsId, m_curFsId, a_fsInfo);
191
192 /* Process the root directory so that its contents are added to
193 * the DB. We won't see it during the dir_walk. */
194 TSK_FS_FILE *fs_file = tsk_fs_file_open(a_fsInfo, NULL, "/");
195 if (fs_file != NULL)
196 {
197 processFile(fs_file, "\\");
198 }
199
200 // make sure that flags are set to get all files -- we need this to
201 // find parent directory
202 setFileFilterFlags((TSK_FS_DIR_WALK_FLAG_ENUM)
203 (TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC));
204
205 std::wstringstream msg;
206 msg << L"TSKAutoImpl::filterFs - Discovered " << tsk_fs_type_toname(a_fsInfo->ftype)
207 << L" file system at offset " << a_fsInfo->offset << L" with Id : " << m_curFsId;
208 LOGINFO(msg.str());
209
210 return TSK_FILTER_CONT;
211 }
212
213 /* Insert the file data into the file table.
214 * @returns OK on success, COR on error because of the data (and we should keep on processing more files),
215 * and ERR because of system error (and we shoudl proabably stop processing)
216 */
insertFileData(TSK_FS_FILE * a_fsFile,const TSK_FS_ATTR * a_fsAttr,const char * a_path,uint64_t & fileId)217 TSK_RETVAL_ENUM TSKAutoImpl::insertFileData(TSK_FS_FILE * a_fsFile,
218 const TSK_FS_ATTR * a_fsAttr, const char * a_path, uint64_t & fileId)
219 {
220 int type = TSK_FS_ATTR_TYPE_NOT_FOUND;
221 int idx = 0;
222 fileId = 0;
223
224 if (a_fsFile->name == NULL) {
225 LOGERROR(L"TSKAutoImpl::insertFileData name value is NULL");
226 return TSK_COR;
227 }
228
229 size_t attr_len = 0;
230 if (a_fsAttr) {
231 type = a_fsAttr->type;
232 idx = a_fsAttr->id;
233 if (a_fsAttr->name)
234 {
235 if ((a_fsAttr->type != TSK_FS_ATTR_TYPE_NTFS_IDXROOT) ||
236 (strcmp(a_fsAttr->name, "$I30") != 0))
237 {
238 attr_len = strlen(a_fsAttr->name);
239 }
240 }
241 }
242
243 // clean up special characters in name before we insert
244 size_t len = strlen(a_fsFile->name->name);
245 char *name;
246 size_t nlen = 2 * (len + attr_len);
247 if ((name = (char *) malloc(nlen + 1)) == NULL)
248 {
249 LOGERROR(L"Error allocating memory");
250 return TSK_ERR;
251 }
252 memset(name, 0, nlen+1);
253
254 size_t j = 0;
255 for (size_t i = 0; i < len && j < nlen; i++)
256 {
257 // ' is special in SQLite
258 if (a_fsFile->name->name[i] == '\'')
259 {
260 name[j++] = '\'';
261 name[j++] = '\'';
262 }
263 else
264 {
265 name[j++] = a_fsFile->name->name[i];
266 }
267 }
268
269 // Add the attribute name
270 if (attr_len > 0) {
271 name[j++] = ':';
272
273 for (unsigned i = 0; i < attr_len && j < nlen; i++) {
274 // ' is special in SQLite
275 if (a_fsAttr->name[i] == '\'')
276 {
277 name[j++] = '\'';
278 name[j++] = '\'';
279 }
280 else
281 {
282 name[j++] = a_fsAttr->name[i];
283 }
284 }
285 }
286
287 int result = m_db.addFsFileInfo(m_curFsId, a_fsFile, name, type, idx, fileId, a_path);
288 free(name);
289
290 // Message was already logged
291 if (result) {
292 return TSK_COR;
293 }
294
295 Scheduler::task_struct task;
296 task.task = Scheduler::FileAnalysis;
297 task.id = fileId;
298 m_filesToSchedule.push(task);
299
300 return TSK_OK;
301 }
302
303
304 /* Based on the error handling design, we only return OK or STOP. All
305 * other errors have been handled, so we don't return ERROR to TSK. */
processFile(TSK_FS_FILE * a_fsFile,const char * a_path)306 TSK_RETVAL_ENUM TSKAutoImpl::processFile(TSK_FS_FILE * a_fsFile, const char * a_path)
307 {
308 // skip the . and .. dirs
309 if (isDotDir(a_fsFile) == 1)
310 {
311 return TSK_OK;
312 }
313
314 TSK_RETVAL_ENUM retval;
315 // process the attributes if there are more than 1
316 if (tsk_fs_file_attr_getsize(a_fsFile) == 0)
317 {
318 uint64_t fileId;
319 // If COR is returned, then keep on going.
320 if (insertFileData(a_fsFile, NULL, a_path, fileId) == TSK_ERR) {
321 retval = TSK_STOP;
322 }
323 else {
324 m_numFilesSeen++;
325 retval = TSK_OK;
326 }
327 }
328 else
329 {
330 retval = processAttributes(a_fsFile, a_path);
331 }
332
333 time_t timeNow = time(NULL);
334 if ((timeNow - m_lastUpdateMsg) > 3600)
335 {
336 m_lastUpdateMsg = timeNow;
337 std::wstringstream msg;
338 msg << L"TSKAutoImpl::processFile : Processed " << m_numFilesSeen << " files.";
339 LOGINFO(msg.str());
340 }
341
342 if (m_filesToSchedule.size() > m_numOfFilesToQueue) {
343 commitAndSchedule();
344 m_db.begin();
345 }
346
347 return retval;
348 }
349
350 /**
351 * commits the open transaction and schedules the files that
352 * were queued up as being part of that transaction.
353 * Does not create a new transaction.
354 */
commitAndSchedule()355 void TSKAutoImpl::commitAndSchedule()
356 {
357 m_db.commit();
358
359 while (m_filesToSchedule.size() > 0) {
360 Scheduler::task_struct &task = m_filesToSchedule.front();
361 if (TskServices::Instance().getScheduler().schedule(task)) {
362 LOGERROR(L"Error adding file for scheduling");
363 }
364 m_filesToSchedule.pop();
365 }
366 }
367
handleError()368 uint8_t TSKAutoImpl::handleError()
369 {
370 const char * tskMsg = tsk_error_get();
371
372 // @@@ Possibly test tsk_errno to determine how the message should be logged.
373 if (tskMsg != NULL)
374 {
375 std::wstringstream msg;
376 msg << L"TskAutoImpl::handleError " << tsk_error_get();
377
378 LOGWARN(msg.str());
379 }
380 return 0;
381 }
382
383
384
385 /* Based on the error handling design, we only return OK or STOP. All
386 * other errors have been handled, so we don't return ERROR to TSK. */
processAttribute(TSK_FS_FILE * a_fsFile,const TSK_FS_ATTR * a_fsAttr,const char * a_path)387 TSK_RETVAL_ENUM TSKAutoImpl::processAttribute(TSK_FS_FILE * a_fsFile,
388 const TSK_FS_ATTR * a_fsAttr, const char * a_path)
389 {
390 uint64_t mFileId = 0;
391
392 // add the file metadata for the default attribute type
393 if (isDefaultType(a_fsFile, a_fsAttr))
394 {
395 // if COR is returned, then keep on going.
396 if (insertFileData(a_fsAttr->fs_file, a_fsAttr, a_path, mFileId) == TSK_ERR)
397 return TSK_STOP;
398 }
399
400 // add the block map, if the file is non-resident
401 if (isNonResident(a_fsAttr))
402 {
403 TSK_FS_ATTR_RUN *run;
404 int count = 0;
405 for (run = a_fsAttr->nrd.run; run != NULL; run = run->next)
406 {
407 // ignore sparse blocks
408 if (run->flags & TSK_FS_ATTR_RUN_FLAG_SPARSE)
409 continue;
410
411 if (m_db.addFsBlockInfo(m_curFsId, mFileId, count++, run->addr, run->len))
412 {
413 // this error should have been logged.
414 // we'll continue to try processing the file
415 }
416 }
417 }
418
419 return TSK_OK;
420 }
421
createDummyVolume(const TSK_DADDR_T sect_start,const TSK_DADDR_T sect_len,const char * desc,TSK_VS_PART_FLAG_ENUM flags)422 void TSKAutoImpl::createDummyVolume(const TSK_DADDR_T sect_start, const TSK_DADDR_T sect_len,
423 const char * desc, TSK_VS_PART_FLAG_ENUM flags)
424 {
425 m_curVsId++;
426
427 TSK_VS_PART_INFO part;
428 part.addr = m_curVsId;
429 part.len = sect_len;
430 part.start = sect_start;
431 part.flags = flags;
432 part.desc = (char *)desc; // remove the cast when TSK_VS_PART_INFO.desc is const char *
433
434 if (m_db.addVolumeInfo(&part))
435 {
436 LOGERROR(L"TSKAutoImpl::createDummyVolume - Error creating volume.");
437 }
438 }
439