关于Linux下文件恢复,安全销毁的研究

关于这个删除文件再恢复的问题网上有很多这方面的资料,也有很多工具和方法,本文将以Linux下的ext2文件系统为例介绍文件恢复和销毁背后的原理。

我们知道,windows下删除文件可以从回收站恢复,其实即使清空了回收站,被删除的文件和数据还是能够恢复的,windows下有一些专业的恢复数据的工具,比如easyrecovery等。Linux下也是如此,我们用rm命令删除了某个文件也是可以恢复的,即使加了参数-f,可以用debugfs 设备名;logdump等命令恢复被删除的文件,个人觉得不是很好用,而且成功率不高,不过本文的重点在于阐述文件恢复背后的深层原理。


文件和数据都是存放在文件系统中的,每个分区对应一个文件系统,不管Linux还是windows都是使用的树形的文件系统,就是我们学过的数据结构中的树,ext2也是如此,该文件系统有几个重要的数据结构,在/usr/include/linux/ext2_fs.h中可以找到,这几个数据结构是ext文件系统的基础

第一个:组描述符  
 一个文件系统由很多个块(block)组成,每个块一般为1024,2048或者4096个字节,用fdisk分区的时候也可以指定块的大小,把这些块分成不同的组,还有就是要记录该文件系统中有了多少个inode,多少block,还有多少保留着等信息就是用组描述符这个数据结构来记录的。
 * Structure of a blocks group descriptor
 */
struct ext2_group_desc
{
__le32 bg_block_bitmap; /* Blocks bitmap block */
__le32 bg_inode_bitmap; /* Inodes bitmap block */
__le32 bg_inode_table; /* Inodes table block */   记录了inode占用的block
__le16 bg_free_blocks_count; /* Free blocks count */   没有使用的block
__le16 bg_free_inodes_count; /* Free inodes count */   没有使用的inodes
__le16 bg_used_dirs_count; /* Directories count */
__le16 bg_pad;
__le32 bg_reserved[3];
};

/*

第二个:i节点(linux用它来表示每个文件,它就好比文件的身份证号)
ext类的文件系统都是用Inode来标示每个文件的,内核中使用的都是inode而不是文件名,可以使用命令ls -i 来查看文件的inode号,他的大小一般为128,256个字节,一个文件的i节点记录了这个文件的几乎所有信息,包括文件属性,权限,访问时间拥有者等等,可以从下面这个数据结构看到

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
__le16 i_mode; /* File mode */   文件模式,是目录,还是普通文件,设备文件等?
__le16 i_uid; /* Low 16 bits of Owner Uid */ 文件所属这UID
__le32 i_size; /* Size in bytes */     文件大小
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Creation time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
__le32 i_blocks; /* Blocks count */
__le32 i_flags; /* File flags */
 ............
};

第三个:超级块(该数据结构描述了对应文件系统的详细信息,比如块的大小数量,inode的大小数量等等)
超级块记录了一个文件系统的详细信息, 如果一个文件系统崩溃了,fsck修复程序会首先从超级块中读取一些信息作为参考,还记录了文件系统的inode的大小(是128还是256),block的大小(是1204还是2048还是4096),多少没有使用的block,inode,总共有多少inode,多少block,通过这些我们就可以算出我们的block在磁盘中的物理位置,怎么算,看后面

/*
 * Structure of the super block
 */
struct ext2_super_block {
__le32 s_inodes_count; /* Inodes count */
__le32 s_blocks_count; /* Blocks count */
__le32 s_r_blocks_count; /* Reserved blocks count */
__le32 s_free_blocks_count; /* Free blocks count */
__le32 s_free_inodes_count; /* Free inodes count */
__le32 s_first_data_block; /* First Data Block */
__le32 s_log_block_size; /* Block size */
__le32 s_log_frag_size; /* Fragment size */
__le32 s_blocks_per_group; /* # Blocks per group */
__le32 s_frags_per_group; /* # Fragments per group */
__le32 s_inodes_per_group; /* # Inodes per group */
__le32 s_mtime; /* Mount time */
__le32 s_wtime; /* Write time */
__le16 s_mnt_count; /* Mount count */
__le16 s_max_mnt_count; /* Maximal mount count */
__le16 s_magic; /* Magic signature */
__le16 s_state; /* File system state */
__le16 s_errors; /* Behaviour when detecting errors */
__le16 s_minor_rev_level; /* minor revision level */
__le32 s_lastcheck; /* time of last check */
__le32 s_checkinterval; /* max. time between checks */
.......
};

下面首先说明文件恢复的原理:
  当我们平时在linux下用rm命令删除任何东西时,内核仅仅只是释放了被删除文件或数据的Inode,以便留给以后要新建的文件用,上面已经提到了inode这个数据结构,它就好比我们每个人的身份证,人死了之后公安局也会注销该人的身份证,道理一样的,但不同的是,文件可以恢复,人死不能复生。我们知道文件或者目录(也是一种文件)肯定存放有数据,这个数据毫无疑问是存放在磁盘中的,系统仅仅释放了文件的inode,文件的内容还是完完整整的存放在硬盘中的,除非存放在这些磁盘上的文件的磁盘位置又存放了新的文件内容,所以一些恢复软件可以恢复你删除的文件,这就是背后的原理。

  知道了背后的原理就有了前进的方向,现在问题的关键就变成怎么样找到被删除文件在磁盘上被存放的物理位置,找到了这些位置,即使你文件被删除,我还是能从这些位置中找到北你删除文件的内容。怎么找?
就靠上面列出的三个数据结构

 下面是具体的操作步骤,包含部分源码
 1. 首先我们要知道文件对应的文件系统对应哪个设备文件(device_name),是/dev/sda1,抑或其它?这个可以通过mount命令的输出轻易的获得,因为后面我们是通过打开文件对应的设备文件找到读取文件的内容的,因为文件删除了之后我们不可能通过read()函数来读文件内容的

 2. 我们要知道该文件对应的文件系统中inode的大小,一般linux的inode有128字节和256字节两种,现在大多数系统的inode大小为256,还有Block的大小,因为磁盘分区格式化的时候,block是一个最小的文件系统存储单元,注意和磁盘存储单元的区别,磁盘最小的存储单元是一个扇区,一般是512bytes,还有该文件系统有多少个inode,多少个block,这些我们都可以通过ext2_super_block数据结构里面对应的字段获得,下面是两个获得inode大小和block大小的函数
static struct ext2_super_block TheSuperBlock;//变量声明
static int get_block_size(char *device_name)
{
int fd;
fd=open(device_name, O_RDONLY, 0);
//这里的device_name就是文件系统对应的设备名,/dev/sda1之类的东东
lseek(fd,1024, 0);       
//这里便宜1024个字节是因为超级块在引导块后面,看下面那幅图,而引导块为1024个字节
read(fd, (unsigned char *) &TheSuperBlock, sizeof(TheSuperBlock));
close(fd);
printf("The Block size in your system is:%d\n",(1024*(1<<(TheSuperBlock.s_log_block_size))));
return(1024*(1<<(TheSuperBlock.s_log_block_size)));   //从超级块中得到block的大小

}

static int get_inode_size()
{
  printf("The inode size in your system is:%d\n",TheSuperBlock.s_inode_size);
  return TheSuperBlock.s_inode_size;   //从超级块中得到inode的大小
}

static void get_super_block_info(char* device_name)
{
  BlockSize = get_block_size(device_name);
  InodeSize = get_inode_size();
}

3. 下面就是要找被删除文件在磁盘中的物理位置(所占据磁盘的block号码),通过刚才所得到super_block的信息,结合下面这幅图(ext2文件系统磁盘数据结构)进行理解,这幅图取自著名的<<深入理解Linux内核>>第三版,陈莉君等翻译,还有<<Unix环境高级编程>>,千锤百炼的经典,一本讲内核,一本讲应用层编程,主要是linux下的系统调用等
下面的函数实现这个功能,找到文件对应的磁盘的物理物质,linux也有相应的命令可以做到,用debugfs 设备文件名 然后通过文件的inode就可以做到,这里用源码表示,更容易理解背后的含义,这个函数写的比较乱,(^_^)
static int GetTheFinalBlock(int inode_num, char* device_name)
{
 int fd,i;
 int GroupTotal,BlocksForGD ; // GD: Group Descriptor
 int TheNumberOfGroup;
// which group the inode is in.
 struct ext2_inode TheInodeStruct;
 struct ext2_group_desc TheGroup;
 off64_t TheInodeLocation,TheGroupLocation, inode_table_64, offset_64;
 GroupTotal= (TheSuperBlock.s_blocks_count / TheSuperBlock.s_blocks_per_group);
  //从超级块中得到总共有多少个block和每一组有多少个block,相除就可以得到多少个组
 if (GroupTotal*TheSuperBlock.s_blocks_per_group != TheSuperBlock.s_blocks_count) GroupTotal++;
//所有的组*每组的block数=文件系统中总的block数,如果不等,说明最后一组的block跟前面的block数不相等,我们来想想其中的道理,因为我们的分区的大小事我们任意设定的,其中所有的块的总数肯定是跟我们的分区大小有关系,而每一组block一般是固定的,一般不可能恰好能分成固定的组,肯定有一组而且是最后一组跟其他组的block数不一样
 printf (" the total group is %d. \n", GroupTotal);
// 这里就得出了总共有多少个组   
 BlocksForGD = (GroupTotal * 32) / BlockSize;                     
//计算组描述符在磁盘中占了多少个block,每一个组描述符32个字节
 if (GroupTotal * 32 != BlockSize*BlocksForGD) BlocksForGD++;
//因为一个块为4096个字节,每个组描述只有32个字节,所以肯定有一个block没有装满
 printf("The blocksforGD is %d.\n", BlocksForGD);                       
 TheNumberOfGroup = ((inode_num - 1) / TheSuperBlock.s_inodes_per_group) + 1;
//根据文件的inode号算出该文件属于哪一组
 printf("The file which inode number %d group number is %d.\n", inode_num, TheNumberOfGroup - 1);
 fd=open(device_name, O_RDONLY, 0);
 printf("\n the group size is %d \n", sizeof(struct ext2_group_desc));
 if (BlockSize > 1024)
    TheGroupLocation = BlockSize+32*(TheNumberOfGroup-1); 
 else 
    TheGroupLocation = 1024+BlockSize+32*(TheNumberOfGroup-1); 
//找出组描述在磁盘中存放的位置
lseek(fd,TheGroupLocation, 0);
read(fd, &TheGroup, sizeof(struct ext2_group_desc));
inode_table_64 = TheGroup.bg_inode_table;
//从文件所处组描述符读出inode_table占有的block号
offset_64 = ((inode_num-1) % TheSuperBlock.s_inodes_per_group)*TheSuperBlock.s_inode_size;
TheInodeLocation = inode_table_64 * BlockSize + offset_64;
//找到文件的inode在磁盘中存放的位置
lseek64(fd,TheInodeLocation, 0);
read(fd, (unsigned char *) &TheInodeStruct, sizeof(struct ext2_inode));
//从磁盘设备文件中读出文件的Inode的数据结构到TheInodeStruct
close(fd);
TheFinalBlock = TheInodeStruct.i_block[0];
//从文件Inode的数据结构读出该文件具体在磁盘上占用的block号
}

4. 上面这些步骤完成之后,就可以得到这个文件的内容了,看看下面的代码
char* read_disk(int inode_num, char* device_name)
  off64_t BlockSize_64, TheFinalBlock_64, TheOffsize_64;
  unsigned char buf[100];
  get_super_block_info(device_name);
  GetTheFinalBlock(inode_num, device_name);
  int fd = open(device_name, O_RDONLY, 0);
  BlockSize_64 = BlockSize;
  TheFinalBlock_64 = TheFinalBlock;
  TheOffsize_64 = BlockSize_64 * TheFinalBlock_64;
  lseek64(fd, TheOffsize_64,0);
  read(fd, &buf, 100);
  printf(" fd = %d, and the first 20 characters of the block is %s. \n", fd, buf);
  close(fd);
}

大功告成,注意程序的输入是文件名,这似乎跟文章的本意恢复删除文件有些不符,既然是恢复,那文件肯定是删除了之后恢复,那程序的输入为什么还有文件名呢?不要迷糊,注意上面的操做读取文件的内容跟普通的通过系统调用read()读文件内容有着根本的区别,即使将文件删除,将上面的代码稍加修改,输入被删除文件的i节点号,也是可以独到文件内容的,相信聪明的读者您一定能够明白其中的道理。
 
下面谈谈文件的安全销毁,如果懂了上面这些,文件彻底销毁就很容易了
既然已经找到了被删除文件在磁盘中的位置,我们将这个位置重新写上数据覆盖掉原来的内容不就彻底销毁了被删除的文件了吗?没错,就是这样,不同的销毁数据的软件无非就是写入数据的不同,写入的次数不同而已。

冠希哥一边流泪一边说,"如果我当年知道这个该多好啊!!!"

推荐阅读:CU的一篇博客, 细说ext2文件系统  http://blog.chinaunix.net/uid-23069658-id-3475672.html

参考资料:深入理解Linux内核第三版


<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
阅读(905) | 评论(2) | 转发(5) |
给主人留下些什么吧!~~

fuliangcheng2013-01-11 09:11:06

柳拂风: 现在已经都在用ext4了.....
ext3,ext4跟ext2没有太大差别,只不过增加了日志等功能而已,这些基本的数据都没有变化

柳拂风2013-01-10 15:38:01

现在已经都在用ext4了

评论热议
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值