/* File: ntfs_adv.c Copyright (C) 1998-2007 Christophe GRENIER This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #ifdef HAVE_SYS_STAT_H #include #endif #include "types.h" #include "common.h" #include "intrf.h" #include "intrfn.h" #include "dirpart.h" #include "ntfs.h" #include "fnctdsk.h" #include "lang.h" #include "io_redir.h" #include "log.h" #define INTER_NTFS_X 0 #define INTER_NTFS_Y 23 #define MAX_INFO_MFT 10 typedef struct s_info_mft info_mft_t; struct s_info_mft { uint64_t sector; uint64_t mft_lcn; uint64_t mftmirr_lcn; }; static int create_ntfs_boot_sector(disk_t *disk_car, partition_t *partition, const int interface, const unsigned int cluster_size, const uint64_t mft_lcn, const uint64_t mftmirr_lcn, const uint32_t mft_record_size, const uint32_t index_block_size, char**current_cmd); static int dump_2ntfs_info(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2); static int dump_ntfs_info(const struct ntfs_boot_sector *ntfs_header); static int testdisk_ffs(int x); static int read_mft_info(disk_t *disk_car, partition_t *partition, const uint64_t mft_sector, const int debug, unsigned int *sectors_per_cluster, uint64_t *mft_lcn, uint64_t *mftmirr_lcn, unsigned int *mft_record_size); static int create_ntfs_boot_sector(disk_t *disk_car, partition_t *partition, const int interface, const unsigned int cluster_size, const uint64_t mft_lcn, const uint64_t mftmirr_lcn, const uint32_t mft_record_size, const uint32_t index_block_size, char**current_cmd) { unsigned char orgboot[DEFAULT_SECTOR_SIZE]; unsigned char newboot[DEFAULT_SECTOR_SIZE]; struct ntfs_boot_sector *org_ntfs_header=(struct ntfs_boot_sector *)&orgboot; struct ntfs_boot_sector *ntfs_header=(struct ntfs_boot_sector *)&newboot; int error=0; if(disk_car->read(disk_car,sizeof(orgboot), &orgboot, partition->part_offset)!=0) { log_error("create_ntfs_boot_sector: Can't read boot sector.\n"); memset(&orgboot,0,sizeof(orgboot)); } if(cluster_size==0) { error=1; } if(error) { display_message("NTFS Bad extrapolation.\n"); return 1; } memcpy(&newboot,&orgboot,sizeof(newboot)); memcpy(ntfs_header->system_id,"NTFS ",8); ntfs_header->sector_size[0]=disk_car->sector_size & 0xFF; ntfs_header->sector_size[1]=disk_car->sector_size>>8; ntfs_header->sectors_per_cluster=cluster_size/disk_car->sector_size; ntfs_header->reserved=0; ntfs_header->fats=0; ntfs_header->dir_entries[0]=0; ntfs_header->dir_entries[1]=0; ntfs_header->sectors[0]=0; ntfs_header->sectors[1]=0; ntfs_header->media=0xF8; ntfs_header->fat_length=le16(0); ntfs_header->secs_track=le16(disk_car->CHS.sector); ntfs_header->heads=le16(disk_car->CHS.head+1); /* absolute sector address from the beginning of the disk (!= FAT) */ ntfs_header->hidden=le32(partition->part_offset/disk_car->sector_size); ntfs_header->total_sect=le32(0); ntfs_header->sectors_nbr=le64(partition->part_size/disk_car->sector_size-1); ntfs_header->mft_lcn=le64(mft_lcn); ntfs_header->mftmirr_lcn=le64(mftmirr_lcn); ntfs_header->clusters_per_mft_record=(mft_record_size >= cluster_size?mft_record_size / cluster_size: -(testdisk_ffs(mft_record_size) - 1)); ntfs_header->clusters_per_index_record =(index_block_size >= cluster_size?index_block_size / cluster_size: -(testdisk_ffs(index_block_size) - 1)); ntfs_header->reserved0[0]=0; ntfs_header->reserved0[1]=0; ntfs_header->reserved0[2]=0; ntfs_header->reserved1[0]=0; ntfs_header->reserved1[1]=0; ntfs_header->reserved1[2]=0; /* { uint32_t *u; uint32_t checksum; for (checksum = 0,u=(uint32_t*)ntfs_header; u < (uint32_t*)(&ntfs_header->checksum); u++) checksum += NTFS_GETU32(u); ntfs_header->checksum=checksum; } */ ntfs_header->checksum=0; ntfs_header->marker=0xAA55; if(memcmp(newboot,orgboot,sizeof(newboot))) { log_warning(" New / Current boot sector\n"); log_ntfs2_dump(ntfs_header,org_ntfs_header); log_warning("Extrapolated boot sector and current boot sector are different.\n"); } else { log_info("Extrapolated boot sector and current boot sector are identical.\n"); } /* */ if(interface) { struct MenuItem menuSaveBoot[]= { { 'D', "Dump", "Dump sector" }, { 'L', "List", "List directories and files" }, { 'W', "Write","Write boot"}, { 'Q',"Quit","Quit this section"}, { 0, NULL, NULL } }; const char *options="DLQ"; int do_write=0; int do_exit=0; int no_confirm=0; int command; do { aff_copy(stdscr); wmove(stdscr,4,0); wdoprintf(stdscr,"%s",disk_car->description(disk_car)); mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG); wmove(stdscr,6,0); aff_part(stdscr,AFF_PART_ORDER,disk_car,partition); wmove(stdscr,8,0); if(memcmp(newboot,orgboot,sizeof(newboot))) /* Only compare the first sector */ { options="DLWQ"; dump_2ntfs_info(ntfs_header, org_ntfs_header); log_ntfs2_dump(ntfs_header, org_ntfs_header); wdoprintf(stdscr,"Extrapolated boot sector and current boot sector are different.\n"); if(error) log_error("Warning: Extrapolated boot sector have incorrect values.\n"); } else { dump_ntfs_info(ntfs_header); log_ntfs_dump(ntfs_header); wdoprintf(stdscr,"Extrapolated boot sector and current boot sector are identical.\n"); } if(*current_cmd!=NULL) { command='Q'; while(*current_cmd[0]==',') (*current_cmd)++; if(strncmp(*current_cmd,"list",4)==0) { command='L'; (*current_cmd)+=4; } else if(strncmp(*current_cmd,"dump",4)==0) { (*current_cmd)+=4; command='D'; } else if(strncmp(*current_cmd,"noconfirm",9)==0) { command=0; /* do nothing */ no_confirm=1; (*current_cmd)+=9; } else if(strncmp(*current_cmd,"write",5)==0) { command='W'; (*current_cmd)+=5; } } else command=wmenuSelect(stdscr,INTER_DUMP_Y, INTER_DUMP_X, menuSaveBoot,8,options,MENU_HORIZ | MENU_BUTTON, 1); switch(command) { case 'w': case 'W': if(strchr(options,'W')!=NULL) do_write=1; break; case 'd': case 'D': if(strchr(options,'D')!=NULL) { WINDOW *window=newwin(0,0,0,0); /* full screen */ keypad(window, TRUE); /* Need it to get arrow key */ aff_copy(window); wmove(window,4,0); wdoprintf(window,"%s",disk_car->description(disk_car)); wmove(window,5,0); aff_part(window,AFF_PART_ORDER,disk_car,partition); log_info(" Rebuild Boot sector Boot sector\n"); mvwaddstr(window,6,0, " Rebuild Boot sector Boot sector"); dump2(window,newboot,orgboot, sizeof(newboot)); delwin(window); (void) clearok(stdscr, TRUE); #ifdef HAVE_TOUCHWIN touchwin(stdscr); #endif } break; case 'l': case 'L': io_redir_add_redir(disk_car,partition->part_offset,sizeof(newboot),0,newboot); dir_partition(disk_car, partition, 0,current_cmd); io_redir_del_redir(disk_car,partition->part_offset); break; case 'q': case 'Q': do_exit=1; break; } } while(do_write==0 && do_exit==0); if(do_write!=0 && (no_confirm!=0 || ask_confirmation("Write new NTFS boot sector, confirm ? (Y/N)")!=0)) { log_info("Write new boot!\n"); /* Write boot sector and backup boot sector */ if(disk_car->write(disk_car,sizeof(newboot), &newboot, partition->part_offset)) { display_message("Write error: Can't write new NTFS boot sector\n"); } if(disk_car->write(disk_car,sizeof(newboot), &newboot, partition->part_offset+partition->part_size-disk_car->sector_size)!=0) { display_message("Write error: Can't write new NTFS backup boot sector\n"); } return 0; } } log_info("Don't write new NTFS boot sector and backup boot sector!\n"); return 1; } static int read_mft_info(disk_t *disk_car, partition_t *partition, const uint64_t mft_sector, const int debug, 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; if(disk_car->read(disk_car,sizeof(buffer), &buffer, partition->part_offset+(uint64_t)mft_sector*disk_car->sector_size)!=0) { display_message("NTFS: Can't read mft_sector\n"); return 1; } *mft_lcn=ntfs_get_attr(attr,0x80,partition,buffer+8*DEFAULT_SECTOR_SIZE,debug,0,NULL); *mft_record_size=NTFS_GETU32(attr + 0x1C); if(*mft_record_size==0) { if(debug>0) log_warning("read_mft_info failed: mft_record_size=0\n"); return 2; } attr+= NTFS_GETU32(attr + 0x1C); *mftmirr_lcn=ntfs_get_attr(attr,0x80,partition,buffer+8*DEFAULT_SECTOR_SIZE,debug,0,NULL); /* Try to divide by the biggest number first */ if(*mft_lcn<*mftmirr_lcn) { if(*mftmirr_lcn>0 && mft_sector%(*mftmirr_lcn)==0) { *sectors_per_cluster=mft_sector/(*mftmirr_lcn); switch(*sectors_per_cluster) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: return 0; default: break; } } if(*mft_lcn>0 && mft_sector%(*mft_lcn)==0) { *sectors_per_cluster=mft_sector/(*mft_lcn); switch(*sectors_per_cluster) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: return 0; default: break; } } } else { if(*mft_lcn>0 && mft_sector%(*mft_lcn)==0) { *sectors_per_cluster=mft_sector/(*mft_lcn); switch(*sectors_per_cluster) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: return 0; default: break; } } if(*mftmirr_lcn>0 && mft_sector%(*mftmirr_lcn)==0) { *sectors_per_cluster=mft_sector/(*mftmirr_lcn); switch(*sectors_per_cluster) { case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: return 0; default: break; } } } if(debug>0) { log_warning("read_mft_info failed\n"); log_warning("ntfs_find_mft: sectors_per_cluster invalid\n"); log_warning("ntfs_find_mft: mft_lcn %lu\n",(long unsigned int)*mft_lcn); log_warning("ntfs_find_mft: mftmirr_lcn %lu\n",(long unsigned int)*mftmirr_lcn); log_warning("ntfs_find_mft: mft_record_size %u\n",*mft_record_size); log_warning("\n"); } *sectors_per_cluster=0; return 3; } int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int debug, const int dump_ind,const int interface, const unsigned int expert, char **current_cmd) { uint64_t sector; char buffer[8*DEFAULT_SECTOR_SIZE]; int ind_stop=FALSE; unsigned int sectors_per_cluster=0; uint64_t mft_lcn; uint64_t mftmirr_lcn; unsigned int mft_record_size=1024; info_mft_t info_mft[MAX_INFO_MFT]; unsigned int nbr_mft=0; log_info("rebuild_NTFS_BS\n"); if(interface) { aff_copy(stdscr); wmove(stdscr,4,0); wdoprintf(stdscr,"%s",disk_car->description(disk_car)); mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG); wmove(stdscr,6,0); aff_part(stdscr,AFF_PART_ORDER,disk_car,partition); wmove(stdscr,22,0); wattrset(stdscr, A_REVERSE); waddstr(stdscr," Stop "); wattroff(stdscr, A_REVERSE); } /* 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);(sectorpart_size/disk_car->sector_size)&&(sector<=partition->part_size/disk_car->sector_size/2+20)&&(ind_stop==FALSE);sector++) { if(disk_car->read(disk_car,2*DEFAULT_SECTOR_SIZE, &buffer, partition->part_offset+sector*(uint64_t)disk_car->sector_size)==0) { 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 */ { int res; res=ntfs_get_attr(buffer,0x30,partition,buffer+2*DEFAULT_SECTOR_SIZE,debug,0,"$MFT"); 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); tmp=read_mft_info(disk_car, partition, sector, debug, §ors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size); if(tmp==0) { log_info("ntfs_find_mft: mft_lcn %lu\n",(long unsigned int)mft_lcn); log_info("ntfs_find_mft: mftmirr_lcn %lu\n",(long unsigned int)mftmirr_lcn); if(expert==0 || ask_confirmation("Use MFT from %lu, confirm ? (Y/N)",(long unsigned int)mft_lcn)!=0) ind_stop=TRUE; } else if(tmp==3) { if(nbr_mftpart_size/disk_car->sector_size)&&(ind_stop==FALSE);sector++) { if((interface!=0) &&(sector&0xffff)==0) { wmove(stdscr,9,0); wclrtoeol(stdscr); wdoprintf(stdscr,"Search mft %10lu/%lu", (long unsigned)sector, (long unsigned)(partition->part_size/disk_car->sector_size)); wrefresh(stdscr); if(check_enter_or_s(stdscr)) { log_info("Search mft stopped: %10lu/%lu\n", (long unsigned)sector, (long unsigned)(partition->part_size/disk_car->sector_size)); ind_stop=TRUE; } } if(disk_car->read(disk_car,2*DEFAULT_SECTOR_SIZE, &buffer, partition->part_offset+sector*(uint64_t)disk_car->sector_size)==0) { if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42)) { int res; res=ntfs_get_attr(buffer,0x30,partition,buffer+2*DEFAULT_SECTOR_SIZE,debug,0,"$MFT"); 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); tmp=read_mft_info(disk_car, partition, sector, debug, §ors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size); if(tmp==0) { log_info("ntfs_find_mft: mft_lcn %lu\n",(long unsigned int)mft_lcn); log_info("ntfs_find_mft: mftmirr_lcn %lu\n",(long unsigned int)mftmirr_lcn); if(expert==0 || ask_confirmation("Use MFT from %lu, confirm ? (Y/N)",(long unsigned int)mft_lcn)!=0) ind_stop=TRUE; } else if(tmp==3) { if(nbr_mftpart_offset; for(i=0;i info_mft[j].mftmirr_lcn) { if((info_mft[j].sector - info_mft[i].sector)%(info_mft[i].mft_lcn - info_mft[j].mftmirr_lcn)==0) sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[i].mft_lcn - info_mft[j].mftmirr_lcn); } else if(info_mft[i].mft_lcn < info_mft[j].mftmirr_lcn) { if((info_mft[j].sector - info_mft[i].sector)%(info_mft[j].mftmirr_lcn - info_mft[i].mft_lcn)==0) sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[j].mftmirr_lcn - info_mft[i].mft_lcn); } if(sec_per_cluster!=0) { partition->part_offset=partition->part_offset + (info_mft[i].sector - info_mft[i].mft_lcn * sec_per_cluster) * disk_car->sector_size; if(find_partition==0) log_info("Potential partition:\n"); log_partition(disk_car, partition); find_partition=1; } else { if(info_mft[i].mftmirr_lcn > info_mft[j].mft_lcn) { if((info_mft[j].sector - info_mft[i].sector)/(info_mft[i].mftmirr_lcn - info_mft[j].mft_lcn)==0) sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[i].mftmirr_lcn - info_mft[j].mft_lcn); } else if(info_mft[i].mftmirr_lcn < info_mft[j].mft_lcn) { if((info_mft[j].sector - info_mft[i].sector)/(info_mft[j].mft_lcn - info_mft[i].mftmirr_lcn)==0) sec_per_cluster=(info_mft[j].sector - info_mft[i].sector)/(info_mft[j].mft_lcn - info_mft[i].mftmirr_lcn); } if(sec_per_cluster!=0) { partition->part_offset=partition->part_offset + (info_mft[i].sector - info_mft[i].mftmirr_lcn * sec_per_cluster) * disk_car->sector_size; if(find_partition==0) log_info("Potential partition:\n"); log_partition(disk_car, partition); find_partition=1; } } } } partition->part_offset=tmp; } if(interface>0 && expert>0) { wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X); sectors_per_cluster=ask_number(sectors_per_cluster,0,512,"Sectors per cluster "); wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X); mft_lcn=ask_number(mft_lcn,0,0,"MFT LCN "); wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X); mftmirr_lcn=ask_number(mftmirr_lcn,0,0,"MFTMIRR LCN "); wmove(stdscr, INTER_NTFS_Y, INTER_NTFS_X); mft_record_size=ask_number(mft_record_size,0,4096," mft record size "); } /* 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) { unsigned int index_block_size=4096; /* Read "root directory" in MFT */ if(disk_car->read(disk_car,mft_record_size, &buffer, partition->part_offset+(uint64_t)mft_lcn*sectors_per_cluster*disk_car->sector_size+5*(uint64_t)mft_record_size)!=0) { 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,debug,0,NULL); if(index_block_size%512!=0) 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); log_info("ntfs_find_mft: mftmirr_lcn %lu\n",(long unsigned int)mftmirr_lcn); log_info("ntfs_find_mft: mft_record_size %u\n",mft_record_size); 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); /* TODO: ask if the user want to continue the search of MFT */ } else { log_error("Failed to rebuild NTFS boot sector.\n"); } return 0; } static int dump_ntfs_info(const struct ntfs_boot_sector *ntfs_header) { wdoprintf(stdscr,"filesystem size %llu\n", (long long unsigned)(le64(ntfs_header->sectors_nbr)+1)); wdoprintf(stdscr,"sectors_per_cluster %u\n",ntfs_header->sectors_per_cluster); wdoprintf(stdscr,"mft_lcn %lu\n",(long unsigned int)ntfs_header->mft_lcn); wdoprintf(stdscr,"mftmirr_lcn %lu\n",(long unsigned int)ntfs_header->mftmirr_lcn); wdoprintf(stdscr,"clusters_per_mft_record %d\n",ntfs_header->clusters_per_mft_record); wdoprintf(stdscr,"clusters_per_index_record %d\n",ntfs_header->clusters_per_index_record); return 0; } static int testdisk_ffs(int x) { int r = 1; if (!x) return 0; if (!(x & 0xffff)) { x >>= 16; r += 16; } if (!(x & 0xff)) { x >>= 8; r += 8; } if (!(x & 0xf)) { x >>= 4; r += 4; } if (!(x & 3)) { x >>= 2; r += 2; } if (!(x & 1)) { x >>= 1; r += 1; } return r; } static int dump_2ntfs_info(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2) { wdoprintf(stdscr,"filesystem size %llu %llu\n", (long long unsigned)(le64(nh1->sectors_nbr)+1), (long long unsigned)(le64(nh2->sectors_nbr)+1)); wdoprintf(stdscr,"sectors_per_cluster %u %u\n",nh1->sectors_per_cluster,nh2->sectors_per_cluster); wdoprintf(stdscr,"mft_lcn %lu %lu\n",(long unsigned int)le64(nh1->mft_lcn),(long unsigned int)le64(nh2->mft_lcn)); wdoprintf(stdscr,"mftmirr_lcn %lu %lu\n",(long unsigned int)nh1->mftmirr_lcn,(long unsigned int)le64(nh2->mftmirr_lcn)); wdoprintf(stdscr,"clusters_per_mft_record %d %d\n",nh1->clusters_per_mft_record,nh2->clusters_per_mft_record); wdoprintf(stdscr,"clusters_per_index_record %d %d\n",nh1->clusters_per_index_record,nh2->clusters_per_index_record); return 0; } int log_ntfs2_dump(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2) { log_info("filesystem size %llu %llu\n", (long long unsigned)(le64(nh1->sectors_nbr)+1), (long long unsigned)(le64(nh2->sectors_nbr)+1)); log_info("sectors_per_cluster %u %u\n",nh1->sectors_per_cluster,nh2->sectors_per_cluster); log_info("mft_lcn %lu %lu\n",(long unsigned int)le64(nh1->mft_lcn),(long unsigned int)le64(nh2->mft_lcn)); log_info("mftmirr_lcn %lu %lu\n",(long unsigned int)le64(nh1->mftmirr_lcn),(long unsigned int)le64(nh2->mftmirr_lcn)); log_info("clusters_per_mft_record %d %d\n",nh1->clusters_per_mft_record,nh2->clusters_per_mft_record); log_info("clusters_per_index_record %d %d\n",nh1->clusters_per_index_record,nh2->clusters_per_index_record); return 0; }