summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Grenier <grenier@cgsecurity.org>2013-08-31 19:23:28 +0200
committerChristophe Grenier <grenier@cgsecurity.org>2013-08-31 19:23:28 +0200
commit94f6943be7eb1a54b2013a94844dfe4667c2d9f4 (patch)
treeca739cb6c96e55eeb614c786de81f6fe5a39cc3d
parentdb079c0e26853e58a6998054ccd6c2071aaec726 (diff)
TestDisk: replace ntfs_get_attr() by several functions to parse MFT record
-rw-r--r--src/ntfs.c348
-rw-r--r--src/ntfs.h174
-rw-r--r--src/ntfs_adv.c86
3 files changed, 367 insertions, 241 deletions
diff --git a/src/ntfs.c b/src/ntfs.c
index 957fd3e..342808c 100644
--- a/src/ntfs.c
+++ b/src/ntfs.c
@@ -46,9 +46,8 @@
/* #include "guid_cmp.h" */
extern const arch_fnct_t arch_i386;
-static int set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,partition_t *partition,const int verbose);
-static int ntfs_read_MFT(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header, const int my_type, const int verbose);
-static int ntfs_get_attr_aux(const char *attr_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find);
+static void set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header, partition_t *partition);
+static void ntfs_get_volume_name(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header);
unsigned int ntfs_sector_size(const struct ntfs_boot_sector *ntfs_header)
{ return (ntfs_header->sector_size[1]<<8)+ntfs_header->sector_size[0]; }
@@ -67,7 +66,7 @@ int check_NTFS(disk_t *disk_car,partition_t *partition,const int verbose,const i
free(buffer);
return 1;
}
- set_NTFS_info(disk_car, (struct ntfs_boot_sector*)buffer, partition, verbose);
+ set_NTFS_info(disk_car, (struct ntfs_boot_sector*)buffer, partition);
free(buffer);
return 0;
}
@@ -106,11 +105,11 @@ int recover_NTFS(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,par
partition->part_size=part_size;
partition->part_type_i386=P_NTFS;
partition->part_type_gpt=GPT_ENT_TYPE_MS_BASIC_DATA;
- set_NTFS_info(disk_car, ntfs_header, partition, verbose);
+ set_NTFS_info(disk_car, ntfs_header, partition);
return 0;
}
-static int set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,partition_t *partition,const int verbose)
+static void set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header, partition_t *partition)
{
partition->fsname[0]='\0';
partition->blocksize=ntfs_header->sectors_per_cluster*ntfs_sector_size(ntfs_header);
@@ -118,7 +117,7 @@ static int set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_he
snprintf(partition->info, sizeof(partition->info), "NTFS, blocksize=%u", partition->blocksize);
else
snprintf(partition->info, sizeof(partition->info), "NTFS found using backup sector, blocksize=%u", partition->blocksize);
- return ntfs_read_MFT(disk_car, partition, ntfs_header, 0x60, verbose);
+ ntfs_get_volume_name(disk_car, partition, ntfs_header);
}
int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header, partition_t *partition,const int verbose, const int dump_ind)
@@ -185,231 +184,134 @@ int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header,
return 0;
}
-/* */
-int ntfs_get_attr(const char *mft_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find)
+const ntfs_attribheader *ntfs_getattributeheaders(const ntfs_recordheader* record)
{
- const char *attr_record;
- /* Only check for magic DWORD here, fixup should have happened before */
- if(memcmp(mft_record,"FILE",4)) return 2; /* NTFS_RECORD_TYPES == magic_FILE ?*/
- if(NTFS_GETU16(mft_record + 0x14)%8!=0)
- return 2;
- if(NTFS_GETU16(mft_record + 0x14)<42) /* sizeof(MFT_RECORD)>=42 */
- return 2;
- /* screen_buffer_add("FILE\n"); */
- /* screen_buffer_add("seq nbr %lu ",NTFS_GETU16(mft_record+0x10)); */
- /* screen_buffer_add("main MFT record %lu ",NTFS_GETU64(mft_record+0x20)); */
- /* location of first attribute */
- attr_record= mft_record + NTFS_GETU16(mft_record + 0x14);
- return ntfs_get_attr_aux(attr_record, my_type, partition, end, verbose, file_name_to_find);
+ const char* location = (const char*)record;
+ if(le32(record->magic)!=NTFS_Magic ||
+ le16(record->attrs_offset)%8!=0 ||
+ le16(record->attrs_offset)<42)
+ return NULL;
+ location += le16(record->attrs_offset);
+ return (const ntfs_attribheader *)location;
}
-static int ntfs_get_attr_aux(const char *attr_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find)
+static const ntfs_attribheader* ntfs_searchattribute(const ntfs_attribheader* attrib, uint32_t attrType, const char* end, int skip)
{
- while(1)
+ if(attrib==NULL)
+ return NULL;
+ /* Now we should be at attributes */
+ while((const char *)attrib + sizeof(ntfs_attribheader) < end &&
+ le32(attrib->type)!= -1)
{
- int attr_type;
- /* Resident attributes attr_len>=24(0x18), non resident is bigger */
- unsigned int attr_len;
- if(attr_record+0x18>=end)
+ const unsigned int attr_len=le32(attrib->cbAttribute);
+ if(attr_len%8!=0 || attr_len<0x18 || attr_len>0x10000000 ||
+ (const char *)attrib + attr_len >= end)
+ return NULL;
+ if(!skip)
{
- if(verbose>1)
- {
- log_error("ntfs_get_attr attr_record+0x18>=end\n");
- }
- return 2;
+ if(attrib->type == attrType)
+ return attrib;
}
- attr_type=NTFS_GETU32(attr_record);
- if(attr_type==-1) /* attribute list end with type -1 */
- return 0;
- attr_len=NTFS_GETU16(attr_record+4);
- if((attr_len%8!=0)||(attr_len<0x18))
- {
- if(verbose>1)
- {
- log_error("ntfs_get_attr attr_type=%x attr_len=%u (attr_len%%8!0)||(attr_len<0x18)\n",attr_type,attr_len);
- }
- return 2;
- }
- if(verbose>1)
- {
- log_info("attr_type=%x %s\n",attr_type,(NTFS_GETU8(attr_record+8)==0?"resident":"non resident"));
- dump_log(attr_record,attr_len);
- }
- if(NTFS_GETU8(attr_record+8)==0) /* attribute is resident */
+ else
+ skip = 0;
+ attrib=(const ntfs_attribheader*)((const char*)attrib + attr_len);
+ }
+ return NULL;
+}
+
+const ntfs_attribheader* ntfs_findattribute(const ntfs_recordheader* record, uint32_t attrType, const char* end)
+{
+ const ntfs_attribheader *attrib = ntfs_getattributeheaders(record);
+ return ntfs_searchattribute(attrib, attrType, end, 0);
+}
+
+const ntfs_attribheader* ntfs_nextattribute(const ntfs_attribheader* attrib, uint32_t attrType, const char* end)
+{
+ return ntfs_searchattribute(attrib, attrType, end, 1);
+}
+
+const char* ntfs_getattributedata(const ntfs_attribresident* attrib, const char* end)
+{
+ const char* data = ((const char*)attrib) + le16(attrib->offAttribData);
+ if(le16(attrib->offAttribData)+le32(attrib->cbAttribData) > le32(attrib->header.cbAttribute) ||
+ data > end)
+ return NULL;
+ return data;
+}
+
+long int ntfs_get_first_rl_element(const ntfs_attribnonresident *attrnr, const char* end)
+{
+ /* return first element of the run_list */
+ /* buf must be unsigned! */
+ const unsigned char *buf;
+ uint8_t b; /* Current byte offset in buf. */
+ const unsigned char*attr_end; /* End of attribute. */
+ long lcn=0;
+ int64_t deltaxcn = (int64_t)-1; /* Change in [vl]cn. */
+ buf=(const unsigned char*)attrnr + le16(attrnr->offDataRuns);
+ attr_end = (const unsigned char*)attrnr + le32(attrnr->header.cbAttribute);
+ if((const char *)attr_end > end)
+ return 0;
+ b = *buf & 0xf;
+ if(b==0)
+ {
+ log_error("Missing length entry in mapping pairs array.\n");
+ return 0;
+ }
+ if (buf + b > attr_end)
+ {
+ log_error("Attribut AT_DATA: bad size\n");
+ return 0;
+ }
+ for (deltaxcn = (int8_t)buf[b--]; b; b--)
+ deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
+ /* Assume a negative length to indicate data corruption */
+ if (deltaxcn < 0)
+ {
+ log_error("Invalid length in mapping pairs array.\n");
+ return 0;
+ }
+ if (!(*buf & 0xf0))
+ {
+ log_info("LCN_HOLE\n");
+ return 0;
+ }
+ {
+ /* Get the lcn change which really can be negative. */
+ const uint8_t b2 = *buf & 0xf;
+ b = b2 + ((*buf >> 4) & 0xf);
+ if (buf + b > attr_end)
{
- unsigned int attr_value_length=NTFS_GETU16(attr_record+0x10);
- unsigned int attr_value_offset=NTFS_GETU16(attr_record+0x14);
- const char *attr_td_list_entry=attr_record+attr_value_offset;
- if(attr_value_offset%8!=0)
- {
-#ifdef NTFS_DEBUG
- log_debug("ntfs_get_attr attr_value_offset=%u (%%8!=0)\n",attr_value_offset);
-#endif
- return 2;
- }
- if(attr_td_list_entry+26>=end)
- {
-#ifdef NTFS_DEBUG
- log_debug("ntfs_get_attr attr_td_list_entry+26=%p, end=%p\n",attr_td_list_entry+26,end);
-#endif
- return 2;
- }
- /* We found the attribute type. Is the name correct, too? */
- if((attr_value_offset+attr_value_length>attr_len) || (attr_td_list_entry+attr_len >= end))
- {
-#ifdef NTFS_DEBUG
- // log_debug("ntfs_get_attr \n");
-#endif
- return 2;
- }
- if((attr_type==my_type)&&(attr_value_offset!=0))
- {
- switch(attr_type)
- {
- case 0x30: /* AT_FILE_NAME */
- {
- const char *file_name_attr=attr_td_list_entry;
- unsigned int file_name_length;
- const char *name_it;
- if(file_name_attr+0x42>=end)
- return 2;
- file_name_length=NTFS_GETU8(file_name_attr+0x40); /* size in unicode char */
- if(file_name_attr+0x42+2*file_name_length>=end)
- return 2;
- {
- char file_name[256+1]; /* used size is file_name_length+1 */
- unsigned int i;
- /* screen_buffer_add("MFT record nbr %lu ",NTFS_GETU64(file_name_attr)); */
- for(name_it=file_name_attr+0x42,i=0;i<file_name_length; name_it+=2,i++)
- file_name[i]=*name_it;
- file_name[i]='\0';
- if(verbose>1)
- {
- log_verbose("file_name=%s\n",file_name);
- }
- if(file_name_to_find!=NULL)
- {
- if(attr_type==my_type)
- {
- if(strcmp(file_name_to_find,file_name)==0)
- return 1;
- else
- return 2;
- }
- } else
- screen_buffer_add("%s\n",file_name);
- }
- }
- break;
- case 0x60: /* AT_VOLUME_NAME */
- {
- unsigned int volume_name_length=attr_value_length;
- const char *name_it;
- char *dest=partition->fsname;
- volume_name_length/=2; /* Unicode */
- if(volume_name_length>sizeof(partition->fsname)-1)
- volume_name_length=sizeof(partition->fsname)-1;
- for(name_it=attr_td_list_entry;(volume_name_length>0) && (*name_it!='\0') && (name_it[1]=='\0'); name_it+=2,volume_name_length--)
- *dest++=*name_it;
- *dest='\0'; /* 27 january 2003: Correct a bug found by Andreas du Plessis-Denz */
- }
- return 1;
- case 0x90: /* AT_INDEX_ROOT */
- return NTFS_GETU32(attr_td_list_entry+8); /* index_block_size */
- }
- }
+ log_error("Attribut AT_DATA: bad size\n");
+ return 0;
}
- else
- { /* attribute is not resident */
- if(attr_type==my_type)
- {
- switch(attr_type)
- {
- case 0x80: /* AT_DATA */
- {
- /* buf must be unsigned! */
- const unsigned char *buf;
- uint8_t b; /* Current byte offset in buf. */
- uint16_t mapping_pairs_offset;
- const unsigned char*attr_end; /* End of attribute. */
- long lcn;
- int64_t deltaxcn = (int64_t)-1; /* Change in [vl]cn. */
- mapping_pairs_offset=NTFS_GETU16(attr_record+32);
- buf=(const unsigned char*)attr_record + mapping_pairs_offset;
- attr_end = (const unsigned char*)attr_record + attr_len;
- lcn = 0;
- /* return first element of the run_list */
- b = *buf & 0xf;
- if (b){
- if (buf + b > attr_end)
- {
- log_error("Attribut AT_DATA: bad size\n");
- return 0;
- }
- for (deltaxcn = (int8_t)buf[b--]; b; b--)
- deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
- /* Assume a negative length to indicate data corruption */
- if (deltaxcn < 0)
- log_error("Invalid length in mapping pairs array.\n");
- } else { /* The length entry is compulsory. */
- log_error("Missing length entry in mapping pairs array.\n");
- }
- if (deltaxcn >= 0)
- {
- if (!(*buf & 0xf0))
- {
- log_info("LCN_HOLE\n");
- }
- else
- {
- /* Get the lcn change which really can be negative. */
- uint8_t b2 = *buf & 0xf;
- b = b2 + ((*buf >> 4) & 0xf);
- if (buf + b > attr_end)
- {
- log_error("Attribut AT_DATA: bad size\n");
- return 0;
- }
- for (deltaxcn = (int8_t)buf[b--]; b > b2; b--)
- deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
- /* Change the current lcn to it's new value. */
- lcn += deltaxcn;
- /* Check lcn is not below -1. */
- if (lcn < -1) {
- log_error("Invalid LCN < -1 in mapping pairs array.");
- return 0;
- }
- if(verbose>1)
- {
- log_verbose("LCN %ld\n",lcn);
- }
- if(attr_type==my_type)
- return lcn;
- }
- }
- }
- break;
- }
- }
+ for (deltaxcn = (int8_t)buf[b--]; b > b2; b--)
+ deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
+ /* Change the current lcn to it's new value. */
+ lcn += deltaxcn;
+ /* Check lcn is not below -1. */
+ if (lcn < -1) {
+ log_error("Invalid LCN < -1 in mapping pairs array.");
+ return 0;
}
- attr_record+=attr_len;
+ return lcn;
}
}
-static int ntfs_read_MFT(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header, const int my_type, const int verbose)
+static void ntfs_get_volume_name(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header)
{
unsigned char *buffer;
- char *attr;
uint64_t mft_pos;
unsigned int mft_record_size;
unsigned int mft_size;
- mft_pos=partition->part_offset+(uint64_t)(le16(ntfs_header->reserved)+le64(ntfs_header->mft_lcn)*ntfs_header->sectors_per_cluster)*ntfs_sector_size(ntfs_header);
if(ntfs_header->clusters_per_mft_record>0)
mft_record_size=ntfs_header->sectors_per_cluster*ntfs_header->clusters_per_mft_record;
else
mft_record_size=1<<(-ntfs_header->clusters_per_mft_record);
- /* Only need the first 4 MFT record */
- mft_size=4*mft_record_size*ntfs_sector_size(ntfs_header);
+ mft_size=mft_record_size*ntfs_sector_size(ntfs_header);
+ mft_pos=partition->part_offset+(uint64_t)(le16(ntfs_header->reserved)+le64(ntfs_header->mft_lcn)*ntfs_header->sectors_per_cluster)*ntfs_sector_size(ntfs_header);
+ /* Record 3 = $Volume */
+ mft_pos+=3*mft_size;
#ifdef NTFS_DEBUG
log_debug("NTFS cluster size = %u\n",ntfs_header->sectors_per_cluster);
log_debug("NTFS MFT cluster = %lu\n",le64(ntfs_header->mft_lcn));
@@ -419,28 +321,34 @@ static int ntfs_read_MFT(disk_t *disk_car, partition_t *partition, const struct
if(mft_size==0)
{
log_error("Invalid MFT record size or NTFS sector size\n");
- return 1;
+ return;
}
buffer=(unsigned char *)MALLOC(mft_size);
if((unsigned)disk_car->pread(disk_car, buffer, mft_size, mft_pos) != mft_size)
{
log_error("NTFS: Can't read MFT\n");
free(buffer);
- return 1;
+ return;
}
- attr=(char*)buffer;
- while(attr+0x30<=(char*)(buffer+mft_size))
{
- int res=ntfs_get_attr(attr, my_type, partition, (char*)buffer+mft_size, verbose, NULL);
- if((res>0)|| (NTFS_GETU32(attr + 0x1C)<0x30))
+ const ntfs_attribresident *attrib=(const ntfs_attribresident *)ntfs_findattribute((const ntfs_recordheader*)buffer, 0x60, (char*)buffer+mft_size);
+ if(attrib && attrib->header.bNonResident==0) /* attribute is resident */
{
- free(buffer);
- return res;
+ char *dest=partition->fsname;
+ const char *name_it;
+ unsigned int volume_name_length=le32(attrib->cbAttribData);
+ volume_name_length/=2; /* Unicode */
+ if(volume_name_length>sizeof(partition->fsname)-1)
+ volume_name_length=sizeof(partition->fsname)-1;
+ for(name_it=ntfs_getattributedata(attrib, (char*)(buffer+mft_size));
+ volume_name_length>0 && *name_it!='\0' && name_it[1]=='\0';
+ name_it+=2,volume_name_length--)
+ *dest++=*name_it;
+ *dest='\0'; /* 27 january 2003: Correct a bug found by Andreas du Plessis-Denz */
}
- attr+= NTFS_GETU32(attr + 0x1C);
}
free(buffer);
- return 0;
+ return;
}
int is_part_ntfs(const partition_t *partition)
diff --git a/src/ntfs.h b/src/ntfs.h
index a4b380a..7d51b62 100644
--- a/src/ntfs.h
+++ b/src/ntfs.h
@@ -56,6 +56,8 @@ struct ntfs_boot_sector {
uint16_t marker; /* 0x1FE */
} __attribute__ ((__packed__));
+#define NTFS_Magic 0x454c4946 /* FILE */
+
struct ntfs_mft_record {
uint32_t magic; /* FILE */
uint16_t usa_ofs;
@@ -73,12 +75,177 @@ struct ntfs_mft_record {
uint32_t mft_record_number; /* NTFS 3.1+ */
} __attribute__ ((__packed__));
+typedef struct ntfs_mft_record ntfs_recordheader;
+
+typedef struct _ntfs_attribheader
+{
+ uint32_t type; /* Attribute Type (e.g. 0x10, 0x60) */
+ uint32_t cbAttribute; /* Length (including this header) */
+ uint8_t bNonResident; /* Non-resident flag */
+ uint8_t cName; /* Name length */
+ uint16_t offName; /* Offset to the Attribute */
+ uint16_t flags; /* Flags */
+ uint16_t idAttribute; /* Attribute Id (a) */
+}
+ntfs_attribheader;
+
+typedef struct _ntfs_attribresident
+{
+ ntfs_attribheader header;
+ uint32_t cbAttribData; /* Length of the Attribute */
+ uint16_t offAttribData; /* Offset to the Attribute */
+ uint8_t bIndexed; /* Indexed flag */
+ uint8_t padding; /* 0x00 Padding */
+}
+ntfs_attribresident;
+
+typedef struct _ntfs_attribnonresident
+{
+ ntfs_attribheader header;
+ uint64_t startVCN; /* Starting VCN */
+ uint64_t lastVCN; /* Last VCN */
+ uint16_t offDataRuns; /* Offset to the Data Runs */
+ uint16_t compUnitSize; /* Compression Unit Size (b) */
+ uint32_t padding; /* Padding */
+ uint64_t cbAllocated; /* Allocated size of the attribute (c) */
+ uint64_t cbAttribData; /* Real size of the attribute */
+ uint64_t cbInitData; /* Initialized data size of the stream (d) */
+}
+ntfs_attribnonresident;
+
+/* The original definitions come from ntfs-3g/layout.h */
+/**
+ * struct INDEX_HEADER -
+ *
+ * This is the header for indexes, describing the INDEX_ENTRY records, which
+ * follow the INDEX_HEADER. Together the index header and the index entries
+ * make up a complete index.
+ *
+ * IMPORTANT NOTE: The offset, length and size structure members are counted
+ * relative to the start of the index header structure and not relative to the
+ * start of the index root or index allocation structures themselves.
+ */
+typedef struct {
+/* 0*/ uint32_t entries_offset;/* Byte offset from the INDEX_HEADER to first
+ INDEX_ENTRY, aligned to 8-byte boundary. */
+/* 4*/ uint32_t index_length; /* Data size in byte of the INDEX_ENTRY's,
+ including the INDEX_HEADER, aligned to 8. */
+/* 8*/ uint32_t allocated_size;/* Allocated byte size of this index (block),
+ multiple of 8 bytes. See more below. */
+ /*
+ For the index root attribute, the above two numbers are always
+ equal, as the attribute is resident and it is resized as needed.
+
+ For the index allocation attribute, the attribute is not resident
+ and the allocated_size is equal to the index_block_size specified
+ by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK
+ size not counting the INDEX_HEADER part (i.e. minus -24).
+ */
+/* 12*/ uint8_t ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */
+/* 13*/ uint8_t reserved[3]; /* Reserved/align to 8-byte boundary.*/
+/* sizeof() == 16 */
+} __attribute__((__packed__)) TD_INDEX_HEADER;
+
+/**
+ * struct FILE_NAME_ATTR - Attribute: Filename (0x30).
+ *
+ * NOTE: Always resident.
+ * NOTE: All fields, except the parent_directory, are only updated when the
+ * filename is changed. Until then, they just become out of sync with
+ * reality and the more up to date values are present in the standard
+ * information attribute.
+ * NOTE: There is conflicting information about the meaning of each of the time
+ * fields but the meaning as defined below has been verified to be
+ * correct by practical experimentation on Windows NT4 SP6a and is hence
+ * assumed to be the one and only correct interpretation.
+ */
+typedef struct {
+/*hex ofs*/
+/* 0*/ uint64_t parent_directory; /* Directory this filename is
+ referenced from. */
+/* 8*/ int64_t creation_time; /* Time file was created. */
+/* 10*/ int64_t last_data_change_time; /* Time the data attribute was last
+ modified. */
+/* 18*/ int64_t last_mft_change_time; /* Time this mft record was last
+ modified. */
+/* 20*/ int64_t last_access_time; /* Last time this mft record was
+ accessed. */
+/* 28*/ int64_t allocated_size; /* Byte size of on-disk allocated space
+ for the data attribute. So for
+ normal $DATA, this is the
+ allocated_size from the unnamed
+ $DATA attribute and for compressed
+ and/or sparse $DATA, this is the
+ compressed_size from the unnamed
+ $DATA attribute. NOTE: This is a
+ multiple of the cluster size. */
+/* 30*/ int64_t data_size; /* Byte size of actual data in data
+ attribute. */
+/* 38*/ uint32_t file_attributes; /* Flags describing the file. */
+/* 3c*/ union {
+ /* 3c*/ struct {
+ /* 3c*/ uint16_t packed_ea_size; /* Size of the buffer needed to
+ pack the extended attributes
+ (EAs), if such are present.*/
+ /* 3e*/ uint16_t reserved; /* Reserved for alignment. */
+ } __attribute__((__packed__));
+ /* 3c*/ uint32_t reparse_point_tag; /* Type of reparse point,
+ present only in reparse
+ points and only if there are
+ no EAs. */
+ } __attribute__((__packed__));
+/* 40*/ uint8_t file_name_length; /* Length of file name in
+ (Unicode) characters. */
+/* 41*/ uint8_t file_name_type; /* Namespace of the file name.*/
+/* 42*/ char *file_name[0]; /* File name in Unicode. */
+} __attribute__((__packed__)) TD_FILE_NAME_ATTR;
+
+
+/**
+ * struct INDEX_ROOT - Attribute: Index root (0x90).
+ *
+ * NOTE: Always resident.
+ *
+ * This is followed by a sequence of index entries (INDEX_ENTRY structures)
+ * as described by the index header.
+ *
+ * When a directory is small enough to fit inside the index root then this
+ * is the only attribute describing the directory. When the directory is too
+ * large to fit in the index root, on the other hand, two additional attributes
+ * are present: an index allocation attribute, containing sub-nodes of the B+
+ * directory tree (see below), and a bitmap attribute, describing which virtual
+ * cluster numbers (vcns) in the index allocation attribute are in use by an
+ * index block.
+ *
+ * NOTE: The root directory (FILE_root) contains an entry for itself. Other
+ * directories do not contain entries for themselves, though.
+ */
+typedef struct {
+/* 0*/ uint32_t type; /* Type of the indexed attribute. Is
+ $FILE_NAME for directories, zero
+ for view indexes. No other values
+ allowed. */
+/* 4*/ uint32_t collation_rule; /* Collation rule used to sort the
+ index entries. If type is $FILE_NAME,
+ this must be COLLATION_FILE_NAME. */
+/* 8*/ uint32_t index_block_size; /* Size of index block in bytes (in
+ the index allocation attribute). */
+/* 12*/ int8_t clusters_per_index_block;/* Size of index block in clusters (in
+ the index allocation attribute), when
+ an index block is >= than a cluster,
+ otherwise sectors per index block. */
+/* 13*/ uint8_t reserved[3]; /* Reserved/align to 8-byte boundary. */
+/* 16*/ TD_INDEX_HEADER index; /* Index header describing the
+ following index entries. */
+/* sizeof()= 32 bytes */
+} __attribute__((__packed__)) TD_INDEX_ROOT;
+
+
int check_NTFS(disk_t *disk_car,partition_t *partition,const int verbose,const int dump_ind);
int log_ntfs2_info(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2);
int log_ntfs_info(const struct ntfs_boot_sector *ntfs_header);
int is_ntfs(const partition_t *partition);
int is_part_ntfs(const partition_t *partition);
-int ntfs_get_attr(const char *mft_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find);
int recover_NTFS(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,partition_t *partition,const int verbose, const int dump_ind, const int backup);
int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header, partition_t *partition,const int verbose, const int dump_ind);
#define NTFS_GETU8(p) (*(const uint8_t*)(p))
@@ -87,6 +254,11 @@ int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header,
#define NTFS_GETU64(p) (le64(*(const uint64_t*)(p)))
unsigned int ntfs_sector_size(const struct ntfs_boot_sector *ntfs_header);
int rebuild_NTFS_BS(disk_t *disk_car,partition_t *partition, const int verbose, const int interface, const unsigned int expert, char**current_cmd);
+const ntfs_attribheader *ntfs_getattributeheaders(const ntfs_recordheader* record);
+const ntfs_attribheader* ntfs_findattribute(const ntfs_recordheader* record, uint32_t attrType, const char* end);
+const ntfs_attribheader* ntfs_nextattribute(const ntfs_attribheader* attrib, uint32_t attrType, const char* end);
+const char* ntfs_getattributedata(const ntfs_attribresident* attrib, const char* end);
+long int ntfs_get_first_rl_element(const ntfs_attribnonresident *attrnr, const char* end);
#ifdef __cplusplus
} /* closing brace for extern "C" */
diff --git a/src/ntfs_adv.c b/src/ntfs_adv.c
index 1877ee2..9cadaae 100644
--- a/src/ntfs_adv.c
+++ b/src/ntfs_adv.c
@@ -361,28 +361,37 @@ static int create_ntfs_boot_sector(disk_t *disk_car, partition_t *partition, con
static int read_mft_info(disk_t *disk_car, partition_t *partition, const uint64_t mft_sector, const int verbose, unsigned int *sectors_per_cluster, uint64_t *mft_lcn, uint64_t *mftmirr_lcn, unsigned int *mft_record_size)
{
char buffer[8*DEFAULT_SECTOR_SIZE];
- const char *attr=buffer;
+ const struct ntfs_mft_record *record=(const struct ntfs_mft_record *)buffer;
+ const ntfs_attribnonresident *attr80;
if(disk_car->pread(disk_car, &buffer, sizeof(buffer), partition->part_offset + (uint64_t)mft_sector * disk_car->sector_size) != sizeof(buffer))
{
display_message("NTFS: Can't read mft_sector\n");
return 1;
}
- *mft_lcn=ntfs_get_attr(attr, 0x80, partition, buffer+sizeof(buffer), verbose, NULL);
- *mft_record_size=NTFS_GETU32(attr + 0x1C);
+ *mft_record_size=le32(record->bytes_allocated);
if(*mft_record_size==0)
{
if(verbose>0)
log_warning("read_mft_info failed: mft_record_size=0\n");
return 2;
}
- attr+= NTFS_GETU32(attr + 0x1C);
- if(attr < buffer || attr > buffer+sizeof(buffer))
+ attr80=(const ntfs_attribnonresident *)ntfs_findattribute(record, 0x80, buffer+sizeof(buffer));
+ if(attr80 && attr80->header.bNonResident)
+ {
+ *mft_lcn=ntfs_get_first_rl_element(attr80, buffer+sizeof(buffer));
+ }
+ record=(const struct ntfs_mft_record *)(buffer + (*mft_record_size));
+ if((const char *)record< buffer || (const char *)record> buffer+sizeof(buffer))
{
if(verbose<0)
log_warning("read_mft_info failed: bad record.\n");
return 2;
}
- *mftmirr_lcn=ntfs_get_attr(attr, 0x80, partition,buffer+sizeof(buffer), verbose, NULL);
+ attr80=(const ntfs_attribnonresident *)ntfs_findattribute(record, 0x80, buffer+sizeof(buffer));
+ if(attr80 && attr80->header.bNonResident)
+ {
+ *mftmirr_lcn=ntfs_get_first_rl_element(attr80, buffer+sizeof(buffer));
+ }
/* Try to divide by the biggest number first */
if(*mft_lcn<*mftmirr_lcn)
{
@@ -475,19 +484,35 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
}
#endif
/* try to find MFT Backup first */
- for(sector=(partition->part_size/disk_car->sector_size/2-20>0?partition->part_size/disk_car->sector_size/2-20:1);(sector<partition->part_size/disk_car->sector_size)&&(sector<=partition->part_size/disk_car->sector_size/2+20)&&(ind_stop==0);sector++)
+ for(sector=(partition->part_size/disk_car->sector_size/2-20>0?partition->part_size/disk_car->sector_size/2-20:1);
+ sector<partition->part_size/disk_car->sector_size &&
+ sector<=partition->part_size/disk_car->sector_size/2+20 &&
+ ind_stop==0;
+ sector++)
{
- if(disk_car->pread(disk_car, &buffer, 2 * DEFAULT_SECTOR_SIZE, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 2 * DEFAULT_SECTOR_SIZE)
+ if(disk_car->pread(disk_car, &buffer, 0x400, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 0x400)
{
- if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42)
- &&(NTFS_GETU16(buffer+22)==1)) /* MFT_RECORD_IN_USE */
+ const struct ntfs_mft_record *record=(const struct ntfs_mft_record *)&buffer;
+ if(memcmp(buffer,"FILE",4)==0 &&
+ le16(record->attrs_offset)%8==0 &&
+ le16(record->attrs_offset)>=42 &&
+ le16(record->flags)==1) /* MFT_RECORD_IN_USE */
{
- int res;
- res=ntfs_get_attr(buffer, 0x30, partition, buffer+2*DEFAULT_SECTOR_SIZE, verbose, "$MFT");
+ const ntfs_attribheader *attr30;
+ int res=0;
+ attr30=ntfs_findattribute(record, 0x30, buffer+0x400);
+ if(attr30 && attr30->bNonResident==0)
+ {
+ const TD_FILE_NAME_ATTR *file_name_attr=(const TD_FILE_NAME_ATTR *)ntfs_getattributedata((const ntfs_attribresident *)attr30, buffer+0x400);
+ if(file_name_attr->file_name_length==4 &&
+ (const char*)&file_name_attr->file_name[0]+8 <= buffer+0x400 &&
+ memcmp(file_name_attr->file_name,"$\0M\0F\0T\0", 8)==0)
+ res=1;
+ }
if(res==1)
{
int tmp;
- log_info("mft at %lu, seq=%u, main=%u res=%d\n",(long unsigned)sector,NTFS_GETU8(buffer+0x10),(unsigned int)NTFS_GETU32(buffer+0x20),res);
+ log_info("mft at %lu\n",(long unsigned)sector);
tmp=read_mft_info(disk_car, partition, sector, verbose, &sectors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size);
if(tmp==0)
{
@@ -532,16 +557,29 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
}
}
#endif
- if(disk_car->pread(disk_car, &buffer, 2 * DEFAULT_SECTOR_SIZE, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 2 * DEFAULT_SECTOR_SIZE)
+ if(disk_car->pread(disk_car, &buffer, 0x400, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 0x400)
{
- if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42))
+ const struct ntfs_mft_record *record=(const struct ntfs_mft_record *)&buffer;
+ if(memcmp(buffer,"FILE",4)==0 &&
+ le16(record->attrs_offset)%8==0 &&
+ le16(record->attrs_offset)>=42 &&
+ le16(record->flags)==1) /* MFT_RECORD_IN_USE */
{
- int res;
- res=ntfs_get_attr(buffer, 0x30, partition, buffer+2*DEFAULT_SECTOR_SIZE, verbose, "$MFT");
+ const ntfs_attribheader *attr30;
+ int res=0;
+ attr30=ntfs_findattribute(record, 0x30, buffer+0x400);
+ if(attr30 && attr30->bNonResident==0)
+ {
+ const TD_FILE_NAME_ATTR *file_name_attr=(const TD_FILE_NAME_ATTR *)ntfs_getattributedata((const ntfs_attribresident *)attr30, buffer+0x400);
+ if(file_name_attr->file_name_length==4 &&
+ (const char*)&file_name_attr->file_name[0]+8 <= buffer+0x400 &&
+ memcmp(file_name_attr->file_name,"$\0M\0F\0T\0", 8)==0)
+ res=1;
+ }
if(res==1)
{
int tmp;
- log_info("mft at %lu, seq=%u, main=%u res=%d\n",(long unsigned)sector,NTFS_GETU8(buffer+0x10),(unsigned int)NTFS_GETU32(buffer+0x20),res);
+ log_info("mft at %lu\n", (long unsigned)sector);
tmp=read_mft_info(disk_car, partition, sector, verbose, &sectors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size);
if(tmp==0)
{
@@ -617,6 +655,8 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
/* TODO read_mft_info(partition,sector,*sectors_per_cluster,*mft_lcn,*mftmirr_lcn,*mft_record_size); */
if(sectors_per_cluster>0 && mft_record_size>0)
{
+ // 0x90 AT_INDEX_ROOT
+ const ntfs_attribheader *attr90;
unsigned int index_block_size=4096;
log_info("ntfs_find_mft: sectors_per_cluster %u\n",sectors_per_cluster);
log_info("ntfs_find_mft: mft_lcn %lu\n",(long unsigned int)mft_lcn);
@@ -628,8 +668,14 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
display_message("NTFS Can't read \"root directory\" in MFT\n");
return 1;
}
- index_block_size=ntfs_get_attr(buffer, 0x90, partition, buffer+mft_record_size, verbose, NULL);
- if(index_block_size%512!=0)
+ attr90=ntfs_findattribute((const ntfs_recordheader*)buffer, 0x90, buffer+mft_record_size);
+ if(attr90 && attr90->bNonResident==0)
+ {
+ const TD_INDEX_ROOT *index_root=(const TD_INDEX_ROOT *)ntfs_getattributedata((const ntfs_attribresident *)attr90, buffer+mft_record_size);
+ if(index_root)
+ index_block_size=le32(index_root->index_block_size);
+ }
+ if(index_block_size%512!=0 || index_block_size==0)
index_block_size=4096;
log_info("ntfs_find_mft: index_block_size %u\n",index_block_size);
create_ntfs_boot_sector(disk_car,partition, interface, sectors_per_cluster*disk_car->sector_size, mft_lcn, mftmirr_lcn, mft_record_size, index_block_size,current_cmd);