/* * Class: edu_uw_apl_commons_tsk4j_filesys_File * Method: walk * Signature: (JILedu/uw/apl/commons/tsk4j/filesys/File/Walk;)I */ JNIEXPORT jint JNICALL Java_edu_uw_apl_commons_tsk4j_filesys_File_walk (JNIEnv *env, jobject thiz, jlong nativePtr, jint flags, jobject walk ) { // We need three handles in the callback, so package them here. Gruesome! void* vs[3] = { env, thiz, walk }; TSK_FS_FILE* fsFile = (TSK_FS_FILE*)nativePtr; return (jint)tsk_fs_file_walk( fsFile, flags, fileWalkCallback, (void*)vs ); }
static TSK_WALK_RET_ENUM count_slack_inode_act(TSK_FS_FILE * fs_file, void *ptr) { BLKCALC_DATA *data = (BLKCALC_DATA *) ptr; if (tsk_verbose) tsk_fprintf(stderr, "count_slack_inode_act: Processing meta data: %" PRIuINUM "\n", fs_file->meta->addr); /* We will now do a file walk on the content */ if (TSK_FS_TYPE_ISNTFS(fs_file->fs_info->ftype) == 0) { data->flen = fs_file->meta->size; if (tsk_fs_file_walk(fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, count_slack_file_act, ptr)) { /* ignore any errors */ if (tsk_verbose) tsk_fprintf(stderr, "Error walking file %" PRIuINUM, fs_file->meta->addr); tsk_error_reset(); } } /* For NTFS we go through each non-resident attribute */ else { int i, cnt; cnt = tsk_fs_file_attr_getsize(fs_file); for (i = 0; i < cnt; i++) { const TSK_FS_ATTR *fs_attr = tsk_fs_file_attr_get_idx(fs_file, i); if (!fs_attr) continue; if (fs_attr->flags & TSK_FS_ATTR_NONRES) { data->flen = fs_attr->size; if (tsk_fs_file_walk_type(fs_file, fs_attr->type, fs_attr->id, TSK_FS_FILE_WALK_FLAG_SLACK, count_slack_file_act, ptr)) { /* ignore any errors */ if (tsk_verbose) tsk_fprintf(stderr, "Error walking file %" PRIuINUM, fs_file->meta->addr); tsk_error_reset(); } } } } return TSK_WALK_CONT; }
/** * Returns a string containing the md5 hash of the given file * * @param a_fs_file The file to calculate the hash of * @param a_hash_results The results will be stored here (must be allocated beforehand) * @param a_flags Indicates which hash algorithm(s) to use * @returns 0 on success or 1 on error */ extern uint8_t tsk_fs_file_hash_calc(TSK_FS_FILE * a_fs_file, TSK_FS_HASH_RESULTS * a_hash_results, TSK_BASE_HASH_ENUM a_flags) { TSK_FS_HASH_DATA hash_data; if ((a_fs_file == NULL) || (a_fs_file->fs_info == NULL) || (a_fs_file->meta == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_file_hash_calc: fs_info is NULL"); return 1; } if (a_hash_results == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_hash_calc: hash_results is NULL"); return 1; } if (a_flags & TSK_BASE_HASH_MD5) { TSK_MD5_Init(&(hash_data.md5_context)); } if (a_flags & TSK_BASE_HASH_SHA1) { TSK_SHA_Init(&(hash_data.sha1_context)); } hash_data.flags = a_flags; if (tsk_fs_file_walk(a_fs_file, TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_file_hash_calc_callback, (void *) &hash_data)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_file_hash_calc: error in file walk"); return 1; } a_hash_results->flags = a_flags; if (a_flags & TSK_BASE_HASH_MD5) { TSK_MD5_Final(a_hash_results->md5_digest, &(hash_data.md5_context)); } if (a_flags & TSK_BASE_HASH_SHA1) { TSK_SHA_Final(a_hash_results->sha1_digest, &(hash_data.sha1_context)); } return 0; }
/* Place journal data in *fs * * Return 0 on success and 1 on error * */ uint8_t ext2fs_jopen(TSK_FS_INFO * fs, TSK_INUM_T inum) { EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; EXT2FS_JINFO *jinfo; // clean up any error messages that are lying around tsk_error_reset(); if (!fs) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jopen: fs is null"); return 1; } ext2fs->jinfo = jinfo = (EXT2FS_JINFO *) tsk_malloc(sizeof(EXT2FS_JINFO)); if (jinfo == NULL) { return 1; } jinfo->j_inum = inum; jinfo->fs_file = tsk_fs_file_open_meta(fs, NULL, inum); if (!jinfo->fs_file) { free(jinfo); return 1; // error("error finding journal inode %" PRIu32, inum); } if (tsk_fs_file_walk(jinfo->fs_file, 0, load_sb_action, NULL)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_FWALK; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error loading ext3 journal"); tsk_fs_file_close(jinfo->fs_file); free(jinfo); return 1; } if (tsk_verbose) tsk_fprintf(stderr, "journal opened at inode %" PRIuINUM " bsize: %" PRIu32 " First JBlk: %" PRIuDADDR " Last JBlk: %" PRIuDADDR "\n", inum, jinfo->bsize, jinfo->first_block, jinfo->last_block); return 0; }
/* Return 1 on error and 0 on success */ uint8_t tsk_fs_icat(TSK_FS_INFO * fs, TSK_INUM_T inum, TSK_FS_ATTR_TYPE_ENUM type, uint8_t type_used, uint16_t id, uint8_t id_used, TSK_FS_FILE_WALK_FLAG_ENUM flags) { TSK_FS_FILE *fs_file; #ifdef TSK_WIN32 if (-1 == _setmode(_fileno(stdout), _O_BINARY)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WRITE; snprintf(tsk_errstr, TSK_ERRSTR_L, "icat_lib: error setting stdout to binary: %s", strerror(errno)); return 1; } #endif fs_file = tsk_fs_file_open_meta(fs, NULL, inum); if (!fs_file) { return 1; } if (type_used) { if (id_used == 0) { flags |= TSK_FS_FILE_WALK_FLAG_NOID; } if (tsk_fs_file_walk_type(fs_file, type, id, flags, icat_action, NULL)) { tsk_fs_file_close(fs_file); return 1; } } else { if (tsk_fs_file_walk(fs_file, flags, icat_action, NULL)) { tsk_fs_file_close(fs_file); return 1; } } tsk_fs_file_close(fs_file); return 0; }
int testfile(TSK_FS_INFO * a_fs, TSK_INUM_T a_inum) { TSK_FS_FILE *file1 = NULL; if ((s_buf = (char *) malloc(a_fs->block_size)) == NULL) { fprintf(stderr, "Error allocating memory\n"); return 1; } file1 = tsk_fs_file_open_meta(a_fs, NULL, a_inum); if (file1 == NULL) { fprintf(stderr, "Error opening inode %" PRIuINUM "\n", a_inum); return 1; } s_file2 = tsk_fs_file_open_meta(a_fs, NULL, a_inum); if (s_file2 == NULL) { fprintf(stderr, "Error opening inode %" PRIuINUM "\n", a_inum); return 1; } s_off = 0; if (tsk_fs_file_walk(file1, (TSK_FS_FILE_WALK_FLAG_ENUM) 0, fw_action1, NULL)) { fprintf(stderr, "Error walking file inode: %" PRIuINUM "\n", a_inum); tsk_error_print(stderr); tsk_error_reset(); return 1; } free(s_buf); tsk_fs_file_close(file1); tsk_fs_file_close(s_file2); return 0; }
/** \internal * Process a directory and load up FS_DIR with the entries. If a pointer to * an already allocated FS_DIR struture is given, it will be cleared. If no existing * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return * value is error or corruption, then the FS_DIR structure could * have entries (depending on when the error occured). * * @param a_fs File system to analyze * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated * structure or a new structure. * @param a_addr Address of directory to process. * @returns error, corruption, ok etc. */ TSK_RETVAL_ENUM ffs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_OFF_T size; FFS_INFO *ffs = (FFS_INFO *) a_fs; char *dirbuf; int nchnk, cidx; TSK_FS_LOAD_FILE load_file; TSK_FS_DIR *fs_dir; /* If we get corruption in one of the blocks, then continue processing. * retval_final will change when corruption is detected. Errors are * returned immediately. */ TSK_RETVAL_ENUM retval_tmp; TSK_RETVAL_ENUM retval_final = TSK_OK; if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("ffs_dir_open_meta: Invalid inode value: %" PRIuINUM, a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("ffs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ffs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { return tsk_fs_dir_find_orphans(a_fs, fs_dir); } if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_reset(); tsk_error_errstr2_concat("- ffs_dir_open_meta"); return TSK_COR; } /* make a copy of the directory contents that we can process */ /* round up cause we want the slack space too */ size = roundup(fs_dir->fs_file->meta->size, FFS_DIRBLKSIZ); if ((dirbuf = tsk_malloc((size_t) size)) == NULL) { return TSK_ERR; } load_file.total = load_file.left = (size_t) size; load_file.base = load_file.cur = dirbuf; if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { tsk_error_reset(); tsk_error_errstr2_concat("- ffs_dir_open_meta"); free(dirbuf); return TSK_COR; } /* Not all of the directory was copied, so we return */ if (load_file.left > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr("ffs_dir_open_meta: Error reading directory %" PRIuINUM, a_addr); free(dirbuf); return TSK_COR; } /* Directory entries are written in chunks of DIRBLKSIZ ** determine how many chunks of this size we have to read to ** get a full block ** ** Entries do not cross over the DIRBLKSIZ boundary */ nchnk = (int) (size) / (FFS_DIRBLKSIZ) + 1; for (cidx = 0; cidx < nchnk && (int64_t) size > 0; cidx++) { int len = (FFS_DIRBLKSIZ < size) ? FFS_DIRBLKSIZ : (int) size; retval_tmp = ffs_dent_parse_block(ffs, fs_dir, (fs_dir->fs_file-> meta->flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, dirbuf + cidx * FFS_DIRBLKSIZ, len); if (retval_tmp == TSK_ERR) { retval_final = TSK_ERR; break; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } size -= len; } free(dirbuf); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == NULL) return TSK_ERR; if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); } return retval_final; }
static uint8_t process_tsk_file(TSK_FS_FILE * fs_file, const char *path) { /* Use a flag to determine if a file is generically fit for plugins. */ bool can_run_plugin; /* Make sure that the SleuthKit structures are properly set */ if (fs_file->name == NULL) return 1; if (fs_file->meta == NULL && opt_debug) printf("File: %s %s has no meta\n", path, fs_file->name->name); /* SleuthKit meta types are defined in tsk_fs.h.*/ if (opt_debug) printf("Processing %s%s type=%s (0x%x) \n", path, fs_file->name->name, tsk_fs_name_type_str[fs_file->name->type],fs_file->name->type); /* Recover the filename from the fs_dent, if it is provided */ content ci(fs_file->fs_info->img_info); // where the content will go ci.evidence_dirname = path; ci.set_filename(fs_file->name->name); /* If we are filtering and we have a filename, see if we want this file. */ if (ci.name_filtered()) return 0; /* Looks like we are processing */ if(a) a->new_row(); // tell ARFF we are starting a new row if(x) x->push("fileobject"); // tell XML we are starting a new XML object if(opt_parent_tracking) { if(fs_file->name->par_addr) { if(x) { x->push("parent_object"); file_info("inode", fs_file->name->par_addr); if(x) x->pop(); } if((t||a) && !opt_body_file) { file_info("parent_inode", fs_file->name->par_addr); } } } if(fs_file->meta != NULL) { /* Get the content if needed */ if(ci.need_file_walk() && (opt_maxgig==0 || fs_file->meta->size/1000000000 < opt_maxgig)) { int myflags = TSK_FS_FILE_WALK_FLAG_NOID; if (opt_no_data) myflags |= TSK_FS_FILE_WALK_FLAG_AONLY; if (tsk_fs_file_walk (fs_file, (TSK_FS_FILE_WALK_FLAG_ENUM) myflags, file_act, (void *) &ci)) { // ignore errors from deleted files that were being recovered //if (tsk_errno != TSK_ERR_FS_RECOVER) { if (tsk_error_get_errno() != TSK_ERR_FS_RECOVER) { if(opt_debug) { fprintf(stderr,"Processing: %s/%s (%" PRIuINUM ")\n", path, fs_file->name->name, fs_file->meta->addr); tsk_error_print(stderr); } } tsk_error_reset(); } } } if(file_count_max && file_count>file_count_max) return TSK_WALK_STOP; file_count++; /* Send through to the plugin if we were doing that. * Currently results only go to ARFF file, not to the XML file. */ /* Finally output the informaton */ if(opt_body_file && (fs_file->meta != NULL)) { char ls[64]; tsk_fs_meta_make_ls(fs_file->meta,ls,sizeof(ls)); fprintf(t,"%s|%s|%" PRId64 "|%s|%d|%d|%" PRId64 "|%d|%d|%d|%d\n", ci.h_md5.final().hexdigest().c_str(),ci.filename().c_str(),fs_file->meta->addr, ls,fs_file->meta->uid,fs_file->meta->gid, fs_file->meta->size, (uint32_t)(fs_file->meta->atime), (uint32_t)fs_file->meta->mtime, (uint32_t)fs_file->meta->ctime, (uint32_t)fs_file->meta->crtime); return TSK_WALK_CONT; }
/** * Populates the buffer with the contents of the specified filename. * * \param id_number The id number of the file (in our case the inode) * \param filename The real filename for this file. * \param buf The buffer to write data into * \param size The size of the buffer * \param offset The offset into the file the data should be taken from. * \returns The amount of bytes read, or -1 on error. */ int do_read(unsigned int id_number, const char *filename, char *buf, size_t size, off_t offset) { //FILE* outfile = fopen("/tmp/sleuth.txt", "a"); //fprintf(outfile, "do_read(%u, %s, %p, %zu, %zu);\n", id_number, filename, buf, size, offset); //fflush(outfile); //fprintf(outfile, "inode_buffer=%p, inode_buffered=%lld, inode_buf_pos=%llu, end=%llu\n", inode_buffer, inode_buffered, inode_buffer_pos, inode_buffer_pos + inode_buffer_size); //fflush(outfile); // If the data is already cached in our buffer then read from that instead if ((inode_buffer != NULL) && (id_number == inode_buffered) && (offset >= inode_buffer_pos) && (offset < inode_buffer_pos + inode_buffer_size)) { if ((offset + size) <= (inode_buffer_pos + inode_buffer_size)) { offset = offset % BUFFER_AMOUNT; memcpy(buf, inode_buffer + offset, size); //fprintf(outfile, "Fully cached!\n"); //fclose(outfile); return size; } // If we're here then we've cached part of the data needed, but not all of it // (ie, the data requested spans a block boundary) offset = offset % BUFFER_AMOUNT; memcpy(buf, inode_buffer + offset, inode_buffer_size - offset); int ret_size = inode_buffer_size - offset; ret_size += do_read(id_number, filename, buf + ret_size, size - ret_size, inode_buffer_pos + inode_buffer_size); //fprintf(outfile, "Partially cached: Size = %d\n", ret_size); //fclose(outfile); return ret_size; } TSK_FS_FILE *fs_file = tsk_fs_file_open_meta(fs_info, NULL, id_number); if (fs_file == NULL) { //fprintf(outfile, "FS file not found?\n"); //fclose(outfile); return -1; } // If the file is larger than FILE_SIZE_THRESHOLD, and the offset is larger // than FILE_OFFSET_THRESHOLD, buffer up BUFFER_AMOUNT and return from that if (fs_file->meta != NULL) { //fprintf(outfile, "File size is %" PRIdDADDR" bytes\n", fs_file->meta->size); //fflush(outfile); if (offset >= fs_file->meta->size) { //fprintf(outfile, "Reading beyond end of file\n"); //fclose(outfile); return 0; } if ((fs_file->meta->size >= FILE_SIZE_THRESHOLD) && (offset >= FILE_OFFSET_THRESHOLD)) { if (inode_buffer == NULL) { inode_buffer = (unsigned char *) g_malloc(BUFFER_AMOUNT); } inode_buffered = id_number; inode_buffer_pos = (offset / BUFFER_AMOUNT) * BUFFER_AMOUNT; struct read_struct r; r.buf = inode_buffer; r.offset = inode_buffer_pos; r.size = BUFFER_AMOUNT; r.read = 0; //fprintf(outfile, "Caching data...offset = %zu, size =%zu\n", r.offset, r.size); //fflush(outfile); tsk_fs_file_walk(fs_file, TSK_FS_FILE_WALK_FLAG_NONE, read_file_content_callback, &r); inode_buffer_size = r.read; //fprintf(outfile, "Recursing to read data\n"); //fclose(outfile); return do_read(id_number, filename, buf, size, offset); } } struct read_struct r; r.buf = (unsigned char *) buf; r.offset = offset; r.size = size; r.read = 0; tsk_fs_file_walk(fs_file, TSK_FS_FILE_WALK_FLAG_NONE, read_file_content_callback, &r); tsk_fs_file_close(fs_file); //fprintf(outfile, "Simple read %zu\n", r.read); //fclose(outfile); return r.read; }
/** * Populates the result reference with the supplied file and its details. * * \param r The result reference to populate. * \param fs_file The file to populate into the result structure * \param a_path The path containing the file. */ static void populate_sleuth_result(result_t r, TSK_FS_FILE * fs_file, const char *a_path) { if ((fs_file->meta == NULL) || (fs_file->meta->size == 0)) { return; } if (fs_file->name->flags == TSK_FS_NAME_FLAG_UNALLOC) { debug_log("Not allocated: %" PRIdINUM " -> %s", fs_file->name->meta_addr, fs_file->name->name); return; } // Removing '.' and '..' entries if (fs_file->name->name[0] == '.') { if (fs_file->name->name[1] == '\0') { return; } else if (fs_file->name->name[1] == '.') { if (fs_file->name->name[2] == '\0') { return; } } } if (inode_lookup == NULL) { inode_lookup = g_tree_new_full((GCompareDataFunc) unsigned_long_long_compare, NULL, g_free, NULL); } if (g_tree_lookup(inode_lookup, &(fs_file->name->meta_addr)) != NULL) { // This can happen if we have a directory and a child of that directory called '..' // Or we have a hard link on the filesystem debug_log("Duplicate inode detected: %" PRIdINUM " -> %s", fs_file->name->meta_addr, fs_file->name->name); return; } unsigned long long *l = (unsigned long long *) g_malloc(sizeof(unsigned long long)); *l = fs_file->name->meta_addr; g_tree_insert(inode_lookup, l, NULL); // No point walking the filesystem if it's not needed if (r != NULL) { contract_t c = contract_init(NULL, 0); char *inode_string = g_strdup_printf("%s/%" PRIdINUM, mountpoint, fs_file->name->meta_addr); contract_set_path(c, inode_string); g_free(inode_string); // We can't guarantee the file is contiguous contract_set_contiguous(c, 0); // Only do this if the original_contract is on contiguous space if ((is_contiguous == 1) && (absolute_offset >= 0)) { // Getting the block list tsk_fs_file_walk(fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, (TSK_FS_FILE_WALK_CB) walk_file, c); } if (fs_file->meta->type == TSK_FS_META_TYPE_REG) { // Only add it if it's actually a file result_add_new_contract(r, c); } contract_close(c); } // Add it to the virtual filesystem char* full_file_path = g_strdup_printf("%s%s", a_path, fs_file->name->name); add_file(fs_file->name->meta_addr, full_file_path, fs_file->meta->size); g_free(full_file_path); }
TSK_RETVAL_ENUM fatfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { TSK_OFF_T size, len; FATFS_INFO *fatfs = (FATFS_INFO *) a_fs; char *dirbuf; TSK_DADDR_T *addrbuf; FATFS_LOAD_DIR load; int retval; TSK_FS_DIR *fs_dir; if ((a_addr < a_fs->first_inum) || (a_addr > a_fs->last_inum)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("fatfs_dir_open_meta: invalid a_addr value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { return tsk_fs_dir_find_orphans(a_fs, fs_dir); } fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr); if (fs_dir->fs_file == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_NUM); tsk_error_set_errstr("fatfs_dir_open_meta: %" PRIuINUM " is not a valid inode", a_addr); return TSK_COR; } size = fs_dir->fs_file->meta->size; len = roundup(size, fatfs->ssize); if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); if (size == 0) { if (tsk_verbose) tsk_fprintf(stderr, "fatfs_dir_open_meta: directory has 0 size\n"); return TSK_OK; } /* Make a copy of the directory contents using file_walk */ if ((dirbuf = tsk_malloc((size_t) len)) == NULL) { return TSK_ERR; } load.curdirptr = dirbuf; load.dirleft = (size_t) size; /* We are going to save the address of each sector in the directory * in a stack - they are needed to determine the inode address. */ load.addrsize = (size_t) (len / fatfs->ssize); addrbuf = (TSK_DADDR_T *) tsk_malloc(load.addrsize * sizeof(TSK_DADDR_T)); if (addrbuf == NULL) { free(dirbuf); return TSK_ERR; } /* Set the variables that are used during the copy */ load.addridx = 0; load.addrbuf = addrbuf; /* save the directory contents into dirbuf */ if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, fatfs_dent_action, (void *) &load)) { tsk_error_errstr2_concat("- fatfs_dir_open_meta"); free(dirbuf); free(addrbuf); return TSK_COR; } /* We did not copy the entire directory, which occurs if an error occured */ if (load.dirleft > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr ("fatfs_dir_open_meta: Error reading directory %" PRIuINUM, a_addr); /* Free the local buffers */ free(dirbuf); free(addrbuf); return TSK_COR; } if (tsk_verbose) fprintf(stderr, "fatfs_dir_open_meta: Parsing directory %" PRIuINUM "\n", a_addr); retval = fatfs_dent_parse_buf(fatfs, fs_dir, dirbuf, len, addrbuf); free(dirbuf); free(addrbuf); // if we are listing the root directory, add the Orphan directory and special FAT file entries if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == NULL) return TSK_ERR; // MBR Entry strncpy(fs_name->name, FATFS_MBRNAME, fs_name->name_size); fs_name->meta_addr = FATFS_MBRINO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } // FAT1 Entry strncpy(fs_name->name, FATFS_FAT1NAME, fs_name->name_size); fs_name->meta_addr = FATFS_FAT1INO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } // FAT2 Entry strncpy(fs_name->name, FATFS_FAT2NAME, fs_name->name_size); fs_name->meta_addr = FATFS_FAT2INO(a_fs); fs_name->type = TSK_FS_NAME_TYPE_VIRT; fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } // orphan directory if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); } return retval; }
TSK_RETVAL_ENUM ext2fs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) a_fs; char *dirbuf, *dirptr; TSK_OFF_T size; TSK_FS_LOAD_FILE load_file; TSK_FS_DIR *fs_dir; TSK_LIST *list_seen = NULL; /* If we get corruption in one of the blocks, then continue processing. * retval_final will change when corruption is detected. Errors are * returned immediately. */ TSK_RETVAL_ENUM retval_tmp; TSK_RETVAL_ENUM retval_final = TSK_OK; if (a_addr < a_fs->first_inum || a_addr > a_fs->last_inum) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WALK_RNG); tsk_error_set_errstr("ext2fs_dir_open_meta: inode value: %" PRIuINUM "\n", a_addr); return TSK_ERR; } else if (a_fs_dir == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("ext2fs_dir_open_meta: NULL fs_attr argument given"); return TSK_ERR; } if (tsk_verbose) { tsk_fprintf(stderr, "ext2fs_dir_open_meta: Processing directory %" PRIuINUM "\n", a_addr); #ifdef Ext4_DBG tsk_fprintf(stderr, "ext2fs_dir_open_meta: $OrphanFiles Inum %" PRIuINUM " == %" PRIuINUM ": %d\n", TSK_FS_ORPHANDIR_INUM(a_fs), a_addr, a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)); #endif } fs_dir = *a_fs_dir; if (fs_dir) { tsk_fs_dir_reset(fs_dir); fs_dir->addr = a_addr; } else { if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(a_fs, a_addr, 128)) == NULL) { return TSK_ERR; } } // handle the orphan directory if its contents were requested if (a_addr == TSK_FS_ORPHANDIR_INUM(a_fs)) { #ifdef Ext4_DBG tsk_fprintf(stderr, "DEBUG: Getting ready to process ORPHANS\n"); #endif return tsk_fs_dir_find_orphans(a_fs, fs_dir); } else { #ifdef Ext4_DBG tsk_fprintf(stderr, "DEBUG: not orphan %" PRIuINUM "!=%" PRIuINUM "\n", a_addr, TSK_FS_ORPHANDIR_INUM(a_fs)); #endif } if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_reset(); tsk_error_errstr2_concat("- ext2fs_dir_open_meta"); return TSK_COR; } size = roundup(fs_dir->fs_file->meta->size, a_fs->block_size); if ((dirbuf = tsk_malloc((size_t) size)) == NULL) { return TSK_ERR; } /* make a copy of the directory contents that we can process */ load_file.left = load_file.total = (size_t) size; load_file.base = load_file.cur = dirbuf; if (tsk_fs_file_walk(fs_dir->fs_file, TSK_FS_FILE_WALK_FLAG_SLACK, tsk_fs_load_file_action, (void *) &load_file)) { tsk_error_reset(); tsk_error_errstr2_concat("- ext2fs_dir_open_meta"); free(dirbuf); return TSK_COR; } /* Not all of the directory was copied, so we exit */ if (load_file.left > 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_FWALK); tsk_error_set_errstr ("ext2fs_dir_open_meta: Error reading directory contents: %" PRIuINUM "\n", a_addr); free(dirbuf); return TSK_COR; } dirptr = dirbuf; while ((int64_t) size > 0) { int len = (a_fs->block_size < size) ? a_fs->block_size : (int) size; retval_tmp = ext2fs_dent_parse_block(ext2fs, fs_dir, (fs_dir->fs_file->meta-> flags & TSK_FS_META_FLAG_UNALLOC) ? 1 : 0, &list_seen, dirptr, len); if (retval_tmp == TSK_ERR) { retval_final = TSK_ERR; break; } else if (retval_tmp == TSK_COR) { retval_final = TSK_COR; } size -= len; dirptr = (char *) ((uintptr_t) dirptr + len); } free(dirbuf); // if we are listing the root directory, add the Orphan directory entry if (a_addr == a_fs->root_inum) { TSK_FS_NAME *fs_name = tsk_fs_name_alloc(256, 0); if (fs_name == NULL) return TSK_ERR; if (tsk_fs_dir_make_orphan_dir_name(a_fs, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } if (tsk_fs_dir_add(fs_dir, fs_name)) { tsk_fs_name_free(fs_name); return TSK_ERR; } tsk_fs_name_free(fs_name); } return retval_final; }
/* * Limitations for 1st version: start must equal end and action is ignored * * Return 0 on success and 1 on error */ uint8_t ext2fs_jblk_walk(TSK_FS_INFO * fs, TSK_DADDR_T start, TSK_DADDR_T end, int flags, TSK_FS_JBLK_WALK_CB action, void *ptr) { EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; EXT2FS_JINFO *jinfo = ext2fs->jinfo; char *journ; TSK_FS_LOAD_FILE buf1; TSK_DADDR_T i; ext2fs_journ_head *head; // clean up any error messages that are lying around tsk_error_reset(); if ((jinfo == NULL) || (jinfo->fs_file == NULL) || (jinfo->fs_file->meta == NULL)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jblk_walk: journal is not open"); return 1; } if (jinfo->last_block < end) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WALK_RNG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jblk_walk: end is too large "); return 1; } if (start != end) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_blk_walk: only start == end is currently supported"); return 1; } if (jinfo->fs_file->meta->size != (jinfo->last_block + 1) * jinfo->bsize) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_UNSUPFUNC; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jblk_walk: journal file size is different from size reported in journal super block"); return 1; } /* Load into buffer and then process it * Only get the minimum needed */ buf1.left = buf1.total = (size_t) ((end + 1) * jinfo->bsize); journ = buf1.cur = buf1.base = tsk_malloc(buf1.left); if (journ == NULL) { return 1; } if (tsk_fs_file_walk(jinfo->fs_file, 0, tsk_fs_load_file_action, (void *) &buf1)) { free(journ); return 1; } if (buf1.left > 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_FWALK; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jblk_walk: Buffer not fully copied"); free(journ); return 1; } head = (ext2fs_journ_head *) & journ[end * jinfo->bsize]; /* Check if our target block is a journal data structure. * * If not, * we need to look for its descriptor to see if it has been * escaped */ if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) { /* cycle backwards until we find a desc block */ for (i = end - 1; i >= 0; i--) { ext2fs_journ_dentry *dentry; TSK_DADDR_T diff; head = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) continue; /* If we get a commit, then any desc we find will not * be for our block, so forget about it */ if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_COM) break; /* Skip any other data structure types */ if (big_tsk_getu32(head->entry_type) != EXT2_J_ETYPE_DESC) continue; /* We now have the previous descriptor * * NOTE: We have no clue if this is the correct * descriptor if it is not the current 'run' of * transactions, but this is the best we can do */ diff = end - i; dentry = (ext2fs_journ_dentry *) (&journ[i * jinfo->bsize] + sizeof(ext2fs_journ_head)); while ((uintptr_t) dentry <= ((uintptr_t) & journ[(i + 1) * jinfo->bsize] - sizeof(ext2fs_journ_head))) { if (--diff == 0) { if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_ESC) { journ[end * jinfo->bsize] = 0xC0; journ[end * jinfo->bsize + 1] = 0x3B; journ[end * jinfo->bsize + 2] = 0x39; journ[end * jinfo->bsize + 3] = 0x98; } break; } /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */ if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_SAMEID) dentry = (ext2fs_journ_dentry *) ((uintptr_t) dentry + sizeof(ext2fs_journ_dentry)); else dentry = (ext2fs_journ_dentry *) ((uintptr_t) dentry + sizeof(ext2fs_journ_dentry) + 16); } break; } } if (fwrite(&journ[end * jinfo->bsize], jinfo->bsize, 1, stdout) != 1) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_WRITE; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jblk_walk: error writing buffer block"); free(journ); return 1; } free(journ); return 0; }
/* Limitations: does not use the action or any flags * * return 0 on success and 1 on error * */ uint8_t ext2fs_jentry_walk(TSK_FS_INFO * fs, int flags, TSK_FS_JENTRY_WALK_CB action, void *ptr) { EXT2FS_INFO *ext2fs = (EXT2FS_INFO *) fs; EXT2FS_JINFO *jinfo = ext2fs->jinfo; char *journ; TSK_FS_LOAD_FILE buf1; TSK_DADDR_T i; int b_desc_seen = 0; // clean up any error messages that are lying around tsk_error_reset(); if ((jinfo == NULL) || (jinfo->fs_file == NULL) || (jinfo->fs_file->meta == NULL)) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jentry_walk: journal is not open"); return 1; } if (jinfo->fs_file->meta->size != (jinfo->last_block + 1) * jinfo->bsize) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jentry_walk: journal file size is different from \nsize reported in journal super block"); return 1; } /* Load the journal into a buffer */ buf1.left = buf1.total = (size_t) jinfo->fs_file->meta->size; journ = buf1.cur = buf1.base = tsk_malloc(buf1.left); if (journ == NULL) { return 1; } if (tsk_fs_file_walk(jinfo->fs_file, 0, tsk_fs_load_file_action, (void *) &buf1)) { free(journ); return 1; } if (buf1.left > 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_FWALK; snprintf(tsk_errstr, TSK_ERRSTR_L, "ext2fs_jentry_walk: Buffer not fully copied"); free(journ); return 1; } /* Process the journal * Cycle through each block */ tsk_printf("JBlk\tDescriptrion\n"); /* Note that 'i' is incremented when we find a descriptor block and * process its contents. */ for (i = 0; i < jinfo->last_block; i++) { ext2fs_journ_head *head; /* if there is no magic, then it is a normal block * These should be accounted for when we see its corresponding * descriptor. We get the 'unknown' when its desc has * been reused, it is in the next batch to be overwritten, * or if it has not been used before */ head = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; if (big_tsk_getu32(head->magic) != EXT2_JMAGIC) { if (i < jinfo->first_block) { tsk_printf("%" PRIuDADDR ":\tUnused\n", i); } #if 0 /* For now, we ignore the case of the iitial entries before a descriptor, it is too hard ... */ else if (b_desc_seen == 0) { ext2fs_journ_head *head2 = NULL; TSK_DADDR_T a; int next_head = 0, next_seq = 0; ext2fs_journ_dentry *dentry; /* This occurs when the log cycled around * We need to find out where the descriptor is * and where we need to end */ b_desc_seen = 1; for (a = i; a < jinfo->last_block; a++) { head2 = (ext2fs_journ_head *) & journ[a * jinfo->bsize]; if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC)) { next_head = a; next_seq = big_tsk_getu32(head2->entry_seq); break; } } if (next_head == 0) { tsk_printf("%" PRIuDADDR ":\tFS Block Unknown\n", i); } /* Find the last descr in the journ */ for (a = jinfo->last_block; a > i; a--) { head2 = (ext2fs_journ_head *) & journ[a * jinfo->bsize]; if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC) && (big_tsk_getu32(head2->entry_type) == EXT2_J_ETYPE_DESC) && (next_seq == big_tsk_getu32(head2->entry_seq))) { break; // @@@@ We should abort if we reach a commit before descriptor } } /* We did not find a descriptor in the journ! * print unknown for the rest of the journ */ if (a == i) { tsk_printf("%" PRIuDADDR ":\tFS Block Unknown\n", i); continue; } dentry = (ext2fs_journ_dentry *) ((uintptr_t) head2 + sizeof(ext2fs_journ_head));; /* Cycle through the descriptor entries */ while ((uintptr_t) dentry <= ((uintptr_t) head2 + jinfo->bsize - sizeof(ext2fs_journ_head))) { /* Only start to look after the index in the desc has looped */ if (++a <= jinfo->last_block) { ext2fs_journ_head *head3; /* Look at the block that this entry refers to */ head3 = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; if ((big_tsk_getu32(head3->magic) == EXT2_JMAGIC)) { i--; break; } /* If it doesn't have the magic, then it is a * journal entry and we print the FS info */ tsk_printf("%" PRIuDADDR ":\tFS Block %" PRIu32 "\n", i, big_tsk_getu32(dentry->fs_blk)); /* Our counter is over the end of the journ */ if (++i > jinfo->last_block) break; } /* Increment to the next */ if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_LAST) break; /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */ else if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_SAMEID) dentry = (ext2fs_journ_dentry *) ((uintptr_t) dentry + sizeof(ext2fs_journ_dentry)); else dentry = (ext2fs_journ_dentry *) ((uintptr_t) dentry + sizeof(ext2fs_journ_dentry) + 16); } } #endif else { tsk_printf("%" PRIuDADDR ":\tUnallocated FS Block Unknown\n", i); } } /* The super block */ else if ((big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_SB1) || (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_SB2)) { tsk_printf("%" PRIuDADDR ":\tSuperblock (seq: %" PRIu32 ")\n", i, big_tsk_getu32(head->entry_seq)); } /* Revoke Block */ else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_REV) { tsk_printf("%" PRIuDADDR ":\t%sRevoke Block (seq: %" PRIu32 ")\n", i, ((i < jinfo->start_blk) || (big_tsk_getu32(head->entry_seq) < jinfo->start_seq)) ? "Unallocated " : "Allocated ", big_tsk_getu32(head->entry_seq)); } /* The commit is the end of the entries */ else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_COM) { tsk_printf("%" PRIuDADDR ":\t%sCommit Block (seq: %" PRIu32 ")\n", i, ((i < jinfo->start_blk) || (big_tsk_getu32(head->entry_seq) < jinfo->start_seq)) ? "Unallocated " : "Allocated ", big_tsk_getu32(head->entry_seq)); } /* The descriptor describes the FS blocks that follow it */ else if (big_tsk_getu32(head->entry_type) == EXT2_J_ETYPE_DESC) { ext2fs_journ_dentry *dentry; ext2fs_journ_head *head2; int unalloc = 0; b_desc_seen = 1; /* Is this an unallocated journ block or sequence */ if ((i < jinfo->start_blk) || (big_tsk_getu32(head->entry_seq) < jinfo->start_seq)) unalloc = 1; tsk_printf("%" PRIuDADDR ":\t%sDescriptor Block (seq: %" PRIu32 ")\n", i, (unalloc) ? "Unallocated " : "Allocated ", big_tsk_getu32(head->entry_seq)); dentry = (ext2fs_journ_dentry *) ((uintptr_t) head + sizeof(ext2fs_journ_head));; /* Cycle through the descriptor entries to account for the journal blocks */ while ((uintptr_t) dentry <= ((uintptr_t) head + jinfo->bsize - sizeof(ext2fs_journ_head))) { /* Our counter is over the end of the journ */ if (++i > jinfo->last_block) break; /* Look at the block that this entry refers to */ head2 = (ext2fs_journ_head *) & journ[i * jinfo->bsize]; if ((big_tsk_getu32(head2->magic) == EXT2_JMAGIC) && (big_tsk_getu32(head2->entry_seq) >= big_tsk_getu32(head->entry_seq))) { i--; break; } /* If it doesn't have the magic, then it is a * journal entry and we print the FS info */ tsk_printf("%" PRIuDADDR ":\t%sFS Block %" PRIu32 "\n", i, (unalloc) ? "Unallocated " : "Allocated ", big_tsk_getu32(dentry->fs_blk)); /* Increment to the next */ if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_LAST) break; /* If the SAMEID value is set, then we advance by the size of the entry, otherwise add 16 for the ID */ else if (big_tsk_getu32(dentry->flag) & EXT2_J_DENTRY_SAMEID) dentry = (ext2fs_journ_dentry *) ((uintptr_t) dentry + sizeof(ext2fs_journ_dentry)); else dentry = (ext2fs_journ_dentry *) ((uintptr_t) dentry + sizeof(ext2fs_journ_dentry) + 16); } } } free(journ); return 0; }
/** * Process the contents of a file. * * @return 1 on error and 0 on success */ static uint8_t proc_file(TSK_FS_FILE * fs_file, const char *path) { TSK_MD5_CTX md; if ((fs_file->meta == NULL) || (fs_file->name == NULL)) return 1; if (fs_file->meta->type != TSK_FS_META_TYPE_REG) return 0; //printf("Processing %s%s\n", path, fs_file->name->name); int myflags = TSK_FS_FILE_WALK_FLAG_NOID; TSK_MD5_Init(&md); /* Note that we could also cycle through all of the attributes in the * file by using one of the tsk_fs_attr_get() functions and walking it * with tsk_fs_attr_walk(). See the File Systems section of the Library * User's Guide for more details: * http://www.sleuthkit.org/sleuthkit/docs/api-docs/ */ if (tsk_fs_file_walk (fs_file, (TSK_FS_FILE_WALK_FLAG_ENUM) myflags, file_act, (void *) &md)) { // ignore errors from deleted files that were being recovered if (tsk_error_get_errno() != TSK_ERR_FS_RECOVER) { printf("Processing: %s/%s (%" PRIuINUM ")\n", path, fs_file->name->name, fs_file->meta->addr); tsk_error_print(stderr); } tsk_error_reset(); } // otherwise, compute the hash of the file. else { unsigned char hash[16]; TSK_MD5_Final(hash, &md); #if 0 { int i; printf("Hash of %s/%s: ", path, fs_file->name->name); for (i = 0; i < 16; i++) { printf("%x%x", (hash[i] >> 4) & 0xf, hash[i] & 0xf); } printf("\n"); } #endif #if DO_HASHLOOKUP { int retval; retval = tsk_hdb_lookup_raw(hdb_info, (uint8_t *) hash, 16, TSK_HDB_FLAG_QUICK, NULL, NULL); if (retval == 1) { //printf("Ignoring file %s\n", fs_dent->name); } else if (retval == 0) { // printf("Not Ignoring: %s/%s\n", path, name); } } #endif } return 0; }