📄 ntfs.c
字号:
/*** ntfs** The Sleuth Kit **** Content and meta data layer support for the NTFS file system**** Brian Carrier [carrier <at> sleuthkit [dot] org]** Copyright (c) 2006-2008 Brian Carrier, Basis Technology. All Rights reserved** Copyright (c) 2003-2005 Brian Carrier. All rights reserved**** TASK** Copyright (c) 2002 Brian Carrier, @stake Inc. All rights reserved**** This software is distributed under the Common Public License 1.0**** Unicode added with support from I.D.E.A.L. Technology Corp (Aug '05)***/#include "tsk_fs_i.h"#include "tsk_ntfs.h"#include <ctype.h>/** * \file ntfs.c * Contains the TSK internal general NTFS processing code *//* * NOTES TO SELF: * * - multiple ".." entries may exist *//* * How are we to handle the META flag? Is the MFT $Data Attribute META? *//* needs to be predefined for proc_attrseq */static uint8_t ntfs_proc_attrlist(NTFS_INFO *, TSK_FS_FILE *, const TSK_FS_ATTR *);/* mini-design note: * The MFT has entries for every file and dir in the fs. * The first entry ($MFT) is for the MFT itself and it is used to find * the location of the entire table because it can become fragmented. * Therefore, the $Data attribute of $MFT is saved in the NTFS_INFO * structure for easy access. We also use the size of the MFT as * a way to calculate the maximum MFT entry number (last_inum). * * Ok, that is simple, but getting the full $Data attribute can be tough * because $MFT may not fit into one MFT entry (i.e. an attribute list). * We need to process the attribute list attribute to find out which * other entries to process. But, the attribute list attribute comes * before any $Data attribute (so it could refer to an MFT that has not * yet been 'defined'). Although, the $Data attribute seems to always * exist and define at least the run for the entry in the attribute list. * * So, the way this is solved is that generic mft_lookup is used to get * any MFT entry, even $MFT. If $MFT is not cached then we calculate * the address of where to read based on mutliplication and guessing. * When we are loading the $MFT, we set 'loading_the_MFT' to 1 so * that we can update things as we go along. When we read $MFT we * read all the attributes and save info about the $Data one. If * there is an attribute list, we will have the location of the * additional MFT in the cached $Data location, which will be * updated as we process the attribute list. After each MFT * entry that we process while loading the MFT, the 'final_inum' * value is updated to reflect what we can currently load so * that the sanity checks still work. *//********************************************************************** * * MISC FUNCS * **********************************************************************//* convert the NT Time (UTC hundred nanoseconds from 1/1/1601) * to UNIX (UTC seconds from 1/1/1970) * * The basic calculation is to remove the nanoseconds and then * subtract the number of seconds between 1601 and 1970 * i.e. TIME - DELTA * */uint32_tnt2unixtime(uint64_t ntdate){// (369*365 + 89) * 24 * 3600 * 10000000#define NSEC_BTWN_1601_1970 (uint64_t)(116444736000000000ULL) ntdate -= (uint64_t) NSEC_BTWN_1601_1970; ntdate /= (uint64_t) 10000000; return (uint32_t) ntdate;}/********************************************************************** * * Lookup Functions * **********************************************************************//** * Read an MFT entry and save it in raw form in the given buffer. * NOTE: This will remove the update sequence integrity checks in the * structure. * * @param a_ntfs File system to read from * @param a_mft Buffer to save raw data to * @param a_mftnum Address of MFT entry to read * * @returns Error value */static TSK_RETVAL_ENUMntfs_dinode_lookup(NTFS_INFO * a_ntfs, ntfs_mft * a_mft, TSK_INUM_T a_mftnum){ TSK_OFF_T mftaddr_b, mftaddr2_b, offset; size_t mftaddr_len = 0; int i; TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_ntfs->fs_info; TSK_FS_ATTR_RUN *data_run; ntfs_upd *upd; uint16_t sig_seq; /* sanity checks */ if (!a_mft) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "mft_lookup: null mft buffer"); return TSK_ERR; } if (a_mftnum < fs->first_inum) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "mft_lookup: inode number is too small (%" PRIuINUM ")", a_mftnum); return TSK_ERR; } /* Because this code reads teh actual MFT, we need to make sure we * decrement the last_inum because the last value is a special value * for the ORPHANS directory */ if (a_mftnum > fs->last_inum - 1) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "mft_lookup: inode number is too large (%" PRIuINUM ")", a_mftnum); return TSK_ERR; } if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dinode_lookup: Processing MFT %" PRIuINUM "\n", a_mftnum); /* If mft_data (the cached $Data attribute of $MFT) is not there yet, * then we have not started to load $MFT yet. In that case, we will * 'cheat' and calculate where it goes. This should only be for * $MFT itself, in which case the calculation is easy */ if (!a_ntfs->mft_data) { /* This is just a random check with the assumption being that * we don't want to just do a guess calculation for a very large * MFT entry */ if (a_mftnum > NTFS_LAST_DEFAULT_INO) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, "Error trying to load a high MFT entry when the MFT itself has not been loaded (%" PRIuINUM ")", a_mftnum); return TSK_ERR; } mftaddr_b = a_ntfs->root_mft_addr + a_mftnum * a_ntfs->mft_rsize_b; mftaddr2_b = 0; } else { /* The MFT may not be in consecutive clusters, so we need to use its * data attribute run list to find out what address to read * * This is why we cached it */ // will be set to the address of the MFT entry mftaddr_b = mftaddr2_b = 0; /* The byte offset within the $Data stream */ offset = a_mftnum * a_ntfs->mft_rsize_b; /* NOTE: data_run values are in clusters * * cycle through the runs in $Data and identify which * has the MFT entry that we want */ for (data_run = a_ntfs->mft_data->nrd.run; data_run != NULL; data_run = data_run->next) { /* The length of this specific run */ TSK_OFF_T run_len = data_run->len * a_ntfs->csize_b; /* Is our MFT entry is in this run somewhere ? */ if (offset < run_len) { if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dinode_lookup: Found in offset: %" PRIuDADDR " size: %" PRIuDADDR " at offset: %" PRIuOFF "\n", data_run->addr, data_run->len, offset); /* special case where the MFT entry crosses * a run (only happens when cluster size is 512-bytes * and there are an odd number of clusters in the run) */ if (run_len < offset + a_ntfs->mft_rsize_b) { if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dinode_lookup: Entry crosses run border\n"); if (data_run->next == NULL) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "mft_lookup: MFT entry crosses a cluster and there are no more clusters!"); return TSK_COR; } /* Assign address where the remainder of the entry is */ mftaddr2_b = data_run->next->addr * a_ntfs->csize_b; /* this should always be 512, but just in case */ mftaddr_len = (size_t) (run_len - offset); } /* Assign address of where the MFT entry starts */ mftaddr_b = data_run->addr * a_ntfs->csize_b + offset; if (tsk_verbose) tsk_fprintf(stderr, "ntfs_dinode_lookup: Entry address at: %" PRIuOFF "\n", mftaddr_b); break; } /* decrement the offset we are looking for */ offset -= run_len; } /* Did we find it? */ if (!mftaddr_b) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_INODE_NUM; snprintf(tsk_errstr, TSK_ERRSTR_L, "mft_lookup: Error finding MFT entry %" PRIuINUM " in $MFT", a_mftnum); return TSK_ERR; } } /* can we do just one read or do we need multiple? */ if (mftaddr2_b) { ssize_t cnt; /* read the first part into mft */ cnt = tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, (char *) a_mft, mftaddr_len); if (cnt != mftaddr_len) { if (cnt >= 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "ntfs_dinode_lookup: Error reading MFT Entry (part 1) at %" PRIuOFF, mftaddr_b); return TSK_ERR; } /* read the second part into mft */ cnt = tsk_fs_read (&a_ntfs->fs_info, mftaddr2_b, (char *) ((uintptr_t) a_mft + (uintptr_t) mftaddr_len), a_ntfs->mft_rsize_b - mftaddr_len); if (cnt != a_ntfs->mft_rsize_b - mftaddr_len) { if (cnt >= 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "ntfs_dinode_lookup: Error reading MFT Entry (part 2) at %" PRIuOFF, mftaddr2_b); return TSK_ERR; } } else { ssize_t cnt; /* read the raw entry into mft */ cnt = tsk_fs_read(&a_ntfs->fs_info, mftaddr_b, (char *) a_mft, a_ntfs->mft_rsize_b); if (cnt != a_ntfs->mft_rsize_b) { if (cnt >= 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, "ntfs_dinode_lookup: Error reading MFT Entry at %" PRIuOFF, mftaddr_b); return TSK_ERR; } } /* if we are saving into the NTFS_INFO structure, assign mnum too */ if ((uintptr_t) a_mft == (uintptr_t) a_ntfs->mft) a_ntfs->mnum = a_mftnum; /* Sanity Check */#if 0 /* This is no longer applied because it caused too many problems * with images that had 0 and 1 etc. as values. Testing shows that * even Windows XP doesn't care if entries have an invalid entry, so * this is no longer checked. The update sequence check should find * corrupt entries * */ if ((tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC) && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_BAAD) && (tsk_getu32(fs->endian, mft->magic) != NTFS_MFT_MAGIC_ZERO)) { tsk_errno = TSK_ERR_FS_INODE_COR; snprintf(tsk_errstr, TSK_ERRSTR_L, "entry %d has an invalid MFT magic: %x", mftnum, tsk_getu32(fs->endian, mft->magic)); return 1; }#endif /* The MFT entries have error and integrity checks in them * called update sequences. They must be checked and removed * so that later functions can process the data as normal. * They are located in the last 2 bytes of each 512-byte sector * * We first verify that the the 2-byte value is a give value and * then replace it with what should be there */ /* sanity check so we don't run over in the next loop */ if ((tsk_getu16(fs->endian, a_mft->upd_cnt) > 0) && (((uint32_t) (tsk_getu16(fs->endian,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -