1 /*
2  *
3  * This is a sample file that shows how to use some of the basic
4  * POSIX-style library functions in The Sleuth Kit (www.sleuthkit.org).
5  * There are also callback-style functions that can be used to read
6  * the data and partitions.
7  *
8  * Copyright (c) 2008-2011, Brian Carrier <carrier <at> sleuthkit <dot> org>
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * - Redistributions of source code must retain the above copyright notice,
16  *   this list of conditions and the following disclaimer.
17  * - Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in the
19  *   documentation and/or other materials provided with the distribution.
20  * - Neither the Sleuth Kit name nor the names of its contributors may be
21  *   used to endorse or promote products derived from this software without
22  *   specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 
41 #include <tsk/libtsk.h>
42 
43 
44 /**
45  * Open a directory and cycle through its contents.  Read each file and recurse
46  * into each directory.
47  *
48  * @param fs_info File system to process
49  * @param stack Stack to prevent infinite recursion loops
50  * @param dir_inum Metadata address of directory to open
51  * @param path Path of directory being opened
52  * @returns 1 on error
53  */
54 static uint8_t
proc_dir(TSK_FS_INFO * fs_info,TSK_STACK * stack,TSK_INUM_T dir_inum,const char * path)55 proc_dir(TSK_FS_INFO * fs_info, TSK_STACK * stack,
56     TSK_INUM_T dir_inum, const char *path)
57 {
58     TSK_FS_DIR *fs_dir;
59     size_t i;
60     char *path2 = NULL;
61     char *buf = NULL;
62 
63     // open the directory
64     if ((fs_dir = tsk_fs_dir_open_meta(fs_info, dir_inum)) == NULL) {
65         fprintf(stderr, "Error opening directory: %" PRIuINUM "\n",
66             dir_inum);
67         tsk_error_print(stderr);
68         return 1;
69     }
70 
71     /* These should be dynamic lengths, but this is just a sample program.
72      * Allocate heap space instead of stack to prevent overflow for deep
73      * directories. */
74     if ((path2 = (char *) malloc(4096)) == NULL) {
75         return 1;
76     }
77 
78     if ((buf = (char *) malloc(2048)) == NULL) {
79         free(path2);
80         return 1;
81     }
82 
83     // cycle through each entry
84     for (i = 0; i < tsk_fs_dir_getsize(fs_dir); i++) {
85         TSK_FS_FILE *fs_file;
86         TSK_OFF_T off = 0;
87         size_t len = 0;
88 
89         // get the entry
90         if ((fs_file = tsk_fs_dir_get(fs_dir, i)) == NULL) {
91             fprintf(stderr,
92                 "Error getting directory entry %" PRIuSIZE
93                 " in directory %" PRIuINUM "\n", i, dir_inum);
94             tsk_error_print(stderr);
95 
96             free(path2);
97             free(buf);
98             return 1;
99         }
100 
101         /* Ignore NTFS System files */
102         if ((TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype)) &&
103             (fs_file->name->name[0] == '$')) {
104             tsk_fs_file_close(fs_file);
105             continue;
106         }
107 
108         //printf("Processing %s/%s\n", path, fs_file->name->name);
109 
110         // make sure it's got metadata and not only a name
111         if (fs_file->meta) {
112             ssize_t cnt;
113 
114             /* Note that we could also cycle through all of the attributes in the
115              * file by using one of the tsk_fs_attr_get() functions and reading it
116              * with tsk_fs_attr_read().  See the File Systems section of the Library
117              * User's Guide for more details:
118              * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */
119 
120             // read file contents
121             if (fs_file->meta->type == TSK_FS_META_TYPE_REG) {
122                 int myflags = 0;
123 
124                 for (off = 0; off < fs_file->meta->size; off += len) {
125                     if (fs_file->meta->size - off < 2048)
126                         len = (size_t) (fs_file->meta->size - off);
127                     else
128                         len = 2048;
129 
130                     cnt = tsk_fs_file_read(fs_file, off, buf, len,
131                         (TSK_FS_FILE_READ_FLAG_ENUM) myflags);
132                     if (cnt == -1) {
133                         // could check tsk_errno here for a recovery error (TSK_ERR_FS_RECOVER)
134                         fprintf(stderr, "Error reading %s file: %s\n",
135                             ((fs_file->name->
136                                     flags & TSK_FS_NAME_FLAG_UNALLOC)
137                                 || (fs_file->meta->
138                                     flags & TSK_FS_META_FLAG_UNALLOC)) ?
139                             "unallocated" : "allocated",
140                             fs_file->name->name);
141                         tsk_error_print(stderr);
142                         break;
143                     }
144                     else if (cnt != (ssize_t) len) {
145                         fprintf(stderr,
146                             "Warning: %" PRIuSIZE " of %" PRIuSIZE
147                             " bytes read from %s file %s\n", cnt, len,
148                             ((fs_file->name->
149                                     flags & TSK_FS_NAME_FLAG_UNALLOC)
150                                 || (fs_file->meta->
151                                     flags & TSK_FS_META_FLAG_UNALLOC)) ?
152                             "unallocated" : "allocated",
153                             fs_file->name->name);
154                     }
155 
156                     // do something with the data...
157                 }
158             }
159 
160             // recurse into another directory (unless it is a '.' or '..')
161             else if (TSK_FS_IS_DIR_META(fs_file->meta->type)){
162                 if (TSK_FS_ISDOT(fs_file->name->name) == 0) {
163 
164                     // only go in if it is not on our stack
165                     if (tsk_stack_find(stack, fs_file->meta->addr) == 0) {
166                         // add the address to the top of the stack
167                         tsk_stack_push(stack, fs_file->meta->addr);
168 
169                         snprintf(path2, 4096, "%s/%s", path,
170                             fs_file->name->name);
171                         if (proc_dir(fs_info, stack, fs_file->meta->addr,
172                                 path2)) {
173                             tsk_fs_file_close(fs_file);
174                             tsk_fs_dir_close(fs_dir);
175                             free(path2);
176                             free(buf);
177                             return 1;
178                         }
179 
180                         // pop the address
181                         tsk_stack_pop(stack);
182                     }
183                 }
184             }
185         }
186         tsk_fs_file_close(fs_file);
187     }
188     tsk_fs_dir_close(fs_dir);
189 
190     free(path2);
191     free(buf);
192     return 0;
193 }
194 
195 
196 
197 
198 
199 /**
200 * Analyze the volume starting at byte offset 'start' and look
201 * for a file system.  When found, the files will be analyzed.
202 *
203 * @param img Disk image to be analyzed.
204 * @param start Byte offset of volume starting location.
205 *
206 * @return 1 on error and 0 on success
207 */
208 static uint8_t
proc_fs(TSK_IMG_INFO * img_info,TSK_OFF_T start)209 proc_fs(TSK_IMG_INFO * img_info, TSK_OFF_T start)
210 {
211     TSK_FS_INFO *fs_info;
212     TSK_STACK *stack;
213 
214     /* Try it as a file system */
215     if ((fs_info =
216             tsk_fs_open_img(img_info, start, TSK_FS_TYPE_DETECT)) == NULL)
217     {
218         fprintf(stderr,
219             "Error opening file system in partition at offset %" PRIdOFF
220             "\n", start);
221         tsk_error_print(stderr);
222 
223         /* We could do some carving on the volume data at this point */
224 
225         return 1;
226     }
227 
228     // create a stack to prevent infinite loops
229     stack = tsk_stack_create();
230 
231     // Process the directories
232     if (proc_dir(fs_info, stack, fs_info->root_inum, "")) {
233         fprintf(stderr,
234             "Error processing file system in partition at offset %" PRIdOFF
235             "\n", start);
236         tsk_fs_close(fs_info);
237         return 1;
238     }
239 
240     tsk_stack_free(stack);
241 
242     /* We could do some analysis of unallocated blocks at this point...  */
243 
244 
245     tsk_fs_close(fs_info);
246     return 0;
247 }
248 
249 
250 /**
251 * Process the data as a volume system to find the partitions
252  * and volumes.
253  * File system analysis will be performed on each partition.
254  *
255  * @param img Image file information structure for data to analyze
256  * @param start Byte offset to start analyzing from.
257  *
258  * @return 1 on error and 0 on success
259  */
260 static uint8_t
proc_vs(TSK_IMG_INFO * img_info,TSK_OFF_T start)261 proc_vs(TSK_IMG_INFO * img_info, TSK_OFF_T start)
262 {
263     TSK_VS_INFO *vs_info;
264 
265     // Open the volume system
266     if ((vs_info =
267             tsk_vs_open(img_info, start, TSK_VS_TYPE_DETECT)) == NULL) {
268         if (tsk_verbose)
269             fprintf(stderr,
270                 "Error determining volume system -- trying file systems\n");
271 
272         /* There was no volume system, but there could be a file system */
273         tsk_error_reset();
274         if (proc_fs(img_info, start)) {
275             return 1;
276         }
277     }
278     else {
279         fprintf(stderr, "Volume system open, examining each\n");
280 
281         // cycle through the partitions
282         for (TSK_PNUM_T i = 0; i < vs_info->part_count; i++) {
283             const TSK_VS_PART_INFO *vs_part;
284 
285             if ((vs_part = tsk_vs_part_get(vs_info, i)) == NULL) {
286                 fprintf(stderr, "Error getting volume %" PRIuPNUM "\n", i);
287                 continue;
288             }
289 
290             // ignore the metadata partitions
291             if (vs_part->flags & TSK_VS_PART_FLAG_META)
292                 continue;
293 
294             // could do something with unallocated volumes
295             else if (vs_part->flags & TSK_VS_PART_FLAG_UNALLOC) {
296 
297             }
298             else {
299                 if (proc_fs(img_info,
300                         vs_part->start * vs_info->block_size)) {
301                     // We could do more fancy error checking here to see the cause
302                     // of the error or consider the allocation status of the volume...
303                     tsk_error_reset();
304                 }
305             }
306         }
307         tsk_vs_close(vs_info);
308     }
309     return 0;
310 }
311 
312 
313 int
main(int argc,char ** argv1)314 main(int argc, char **argv1)
315 {
316     TSK_IMG_INFO *img_info;
317     TSK_TCHAR **argv;
318 
319 #ifdef TSK_WIN32
320     // On Windows, get the wide arguments (mingw doesn't support wmain)
321     argv = CommandLineToArgvW(GetCommandLineW(), &argc);
322     if (argv == NULL) {
323         fprintf(stderr, "Error getting wide arguments\n");
324         exit(1);
325     }
326 #else
327     argv = (TSK_TCHAR **) argv1;
328 #endif
329 
330     if (argc != 2) {
331         fprintf(stderr, "Missing image name\n");
332         exit(1);
333     }
334 
335     // open the disk image
336     img_info =
337         tsk_img_open_sing((const TSK_TCHAR *) argv[1],
338         TSK_IMG_TYPE_DETECT, 0);
339     if (img_info == NULL) {
340         fprintf(stderr, "Error opening file\n");
341         tsk_error_print(stderr);
342         exit(1);
343     }
344 
345     // process the volume starting at sector 0
346     if (proc_vs(img_info, 0)) {
347         tsk_error_print(stderr);
348         tsk_img_close(img_info);
349         exit(1);
350     }
351 
352     // close the image
353     tsk_img_close(img_info);
354     return 0;
355 }
356