NTFS文件系统学习笔记挺全的网上搜的ntfs学习笔记.docx
《NTFS文件系统学习笔记挺全的网上搜的ntfs学习笔记.docx》由会员分享,可在线阅读,更多相关《NTFS文件系统学习笔记挺全的网上搜的ntfs学习笔记.docx(89页珍藏版)》请在冰豆网上搜索。
NTFS文件系统学习笔记挺全的网上搜的ntfs学习笔记
NTFS研究笔记[466字节wuguan2009-8-2 23:
19:
8回复:
8/336版块:
Delphi]引用
1前言
NTFS是现在流行的磁盘格式.想想当年FAT32的时代都差不多过去了,现在装机差不多都是NTFS.我们平时编程时使用文件操作函数基于操作系统,所以不管磁盘是何种格式,都只是用相同的几个函数就可搞掂,似乎分析NTFS文件系统没有什么必要.但是如果要搞些底层一点的东西,比如数据恢复啊,磁盘分析啊等等,甚至搞一些高级病毒之类,了解NTFS文件系统都很有必要!
现在我准备开始这个研究,现在的我对于这个是一窍不通,所以也不知从哪里开始,只能见一步学一步.所以借爱delphi()网站发一些杂乱无章的笔记.
先定一个目标,要实现NTFS文件系统解析,写出一个程序,能在NTFS下1.读取所有文件目录结构. 2.建立新文件/目录 3.删除文件/目录. 4.读出存在文件的数据. 5.向存在文件写入数据.
从今天起,将要开始这个笔记,直到完成目标.每有什么新的发现或研究成果我都会到这个贴子下跟贴发表,其间可能遇到好多好多的困难,希望得到各位高手的帮助.在此先谢谢各位!
[Q][U]作者:
wuguan主题:
NTFS研究笔记[/U][BR]1前言NTFS是现在流行的磁盘格式.想想当年FAT32的时代都差不多过去了,现在装机差不多都是NTFS.我们平时编程时使用文件操作函数基于操作系统,所以不管磁盘是何种格式,都只是用相同的几个函数就可搞掂,似乎分析NTFS文件系统没有什么必要.但是如果要搞些底层一点的东西,比如数据恢复啊,磁盘分析啊等等,甚至搞一些高级病毒之类,了解NTFS文件系统都很有必要!
现在我准备开始这......[/Q]
NTFS研究笔记
签名:
1
<精华>2磁盘数据[2634字节wuguan2009-8-2 23:
24:
50回复:
0/316版块:
Delphi]引用
要分析NTFS文件系统,必须先获得分区上的数据.可以写一个函数获取指定分区上指定扇区(512字节)的数据.
添加一个单元用来做这个任务.
unit Wu_DiskSectorUnit;
interface
uses
Windows,SysUtils,Wu_BasicUnit;
function Wu_OpenDiskSector(Drive:
char):
boolean; //打开指定分区,准备读取数据 Drive:
代表指定分区A-Z 例:
Wu_OpenDiskSector('C');
function Wu_ReadDiskSector(SectorNum:
cardinal;var retbuf:
Pchar):
boolean; //读取分区指定扇区的数据(512字节),并将数据指针返回retbuf
function Wu_WriteDiskSector(SectorNum:
cardinal;inbuf:
Pchar):
boolean; //将数据写入分区指定扇区(512字节),inbuf代表要写入内容的指针
procedure Wu_CloseDiskSector(); //退出时用来结束打开指定分区
implementation
var
hDeviceHandle:
cardinal;
ReadBuf:
array[0..511] of char;
WriteBuf:
array[0..511] of char;
procedure Wu_CloseDiskSector();
begin
if closehandle(hDeviceHandle) then
begin
hDeviceHandle:
=INVALID_HANDLE_VALUE;
end;
end;
function Wu_OpenDiskSector(Drive:
char):
boolean;
var
D:
string;
begin
Wu_CloseDiskSector();
D:
='\\.\'+Drive+':
';
hDeviceHandle :
= CreateFile(Pchar(D), GENERIC_ALL,FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING,0, 0);
if (hDeviceHandle <> INVALID_HANDLE_VALUE) then
begin
result:
=true;
end
else
begin
result:
=false;
end;
end;
function Wu_ReadDiskSector(SectorNum:
cardinal;var retbuf:
Pchar):
boolean;
var
n:
integer;
I_LARGE:
LARGE_INTEGER;
I_INT64:
INT64;
I_SectorNum:
INT64;
begin
for n:
=0 to 511 do
begin
ReadBuf[n]:
=#0;
end;
if (hDeviceHandle <> INVALID_HANDLE_VALUE) then
begin
//FileSeek(hDevicehandle,SectorNum*512,0);这样写有一个错误,就是不支持大于2146959360的数
I_SectorNum:
=SectorNum;
I_INT64:
=I_SectorNum*512;
Wu_Copy(@I_INT64,1,8,@I_LARGE,1);
SetFilePointer(hDevicehandle,I_LARGE.LowPart,@I_LARGE.highpart,0);
if FileRead(hDevicehandle,ReadBuf[0],512)=512 then
begin
result:
=true;
end
else
begin
result:
=false;
end;
end
else
begin
result:
=false;
end;
retbuf:
=@ReadBuf[0];
end;
function Wu_WriteDiskSector(SectorNum:
cardinal;inbuf:
Pchar):
boolean;
var
n:
integer;
I_LARGE:
LARGE_INTEGER;
I_INT64:
INT64;
I_SectorNum:
INT64;
begin
for n:
=0 to 511 do
begin
WriteBuf[n]:
=inbuf[n];
end;
if (hDeviceHandle <> INVALID_HANDLE_VALUE) then
begin
//FileSeek(hDevicehandle,SectorNum*512,0);这样写有一个错误,就是不支持大于2146959360的数
I_SectorNum:
=SectorNum;
I_INT64:
=I_SectorNum*512;
Wu_Copy(@I_INT64,1,8,@I_LARGE,1);
SetFilePointer(hDevicehandle,I_LARGE.LowPart,@I_LARGE.highpart,0);
if FileWrite(hDevicehandle,WriteBuf[0],512)=512 then
begin
result:
=true;
end
else
begin
result:
=false;
end;
end
else
begin
result:
=false;
end;
end;
end.
[Q][U]作者:
wuguan主题:
2磁盘数据[/U][BR]要分析NTFS文件系统,必须先获得分区上的数据.可以写一个函数获取指定分区上指定扇区(512字节)的数据.添加一个单元用来做这个任务.[COLOR=red]unitWu_DiskSectorUnit;interfaceusesWindows,SysUtils,Wu_BasicUnit;functionWu_OpenDiskSector(D......[/Q]
2磁盘数据
签名:
2
<精华>3引导扇区[2029字节wuguan2009-8-2 23:
27:
35回复:
0/273版块:
Delphi]引用
现在我们用Wu_ReadDiskSector读取分区第一个扇区的数据
var
P:
Pchar;
Wu_OpenDiskSector('G'); //打开G盘
Wu_ReadDiskSector(0,P); //读取G盘0扇区
Wu_CloseDiskSector;
获得这个扇区称为引导扇区,512个字节数据的意义如下表(这个表从网上拿来的)
字节偏移 长度(字节) 常用值 意义
0X00 3 0XEB5290 JMP指令
0X03 4 NTFS 文件系统 ID
0X0B 2 0X0002 每扇区字节数
0X0D 1 0X08 每簇扇区数
0X0E 2 0X0000 保留扇区
0X10 3 0X000000 总为0
0X13 2 0X0000 NTFS未使用,为0
0X15 1 0XF8 介质描述
0X16 1 0X0000 总为0
0X18 2 0X3F00 每磁道扇区数
0X1A 2 0XFF00 磁头数
0X1C 4 0X3F000000 隐含扇区
0X20 4 0X00000000 NTFS未使用,为0
0X24 4 0X80008000 NTFS未使用,为0
0X28 8 0X4AF57F0000000000 扇区总数
0X30 8 0X0400000000000000 $MFT的逻辑簇号
0X38 8 0X54FF070000000000 $MFTMirr的逻辑簇号
0X40 4 0XF6000000 每MFT记录簇数
0X44 4 0X0100000 每索引簇数
0X48 8 0X14A51B74C91B741C 卷标
0X50 4 0X00000000 校验和
0X54 430 略 引导代码
0X1FE 2 0X55AA 签名
根据此表我们写出数据结构.
type
R_Wu_NTFSBPB=packed record
bJmp:
array[0..2] of byte; //跳转指令[0]
bNTFlags:
array[0..3] of char; //"NTFS"标识[3]
breserve1:
array[0..3] of char; //这个上表没有说明[7]
wBytePerSector:
word; //每扇区字节数(一般都是512)(重要)[B]
bSectorPerCluster:
byte; //每簇扇区数(重要)[D]
wReserveSectors:
word; //保留扇区数(重要)[E]
bFatNum:
byte; //?
?
?
未知[10]
wRootDirNum:
word; //?
?
?
未知[11]
wSectorOfParti:
word; //?
?
?
未知[13]
bMedium:
byte; //介质描述[15]
wSectorPerFat:
word; //?
?
?
未知[16]
wSectorPerTrack:
word; //每磁道扇区数[18]
wHeadNum:
word; //磁头数[1A]
dwHideSector:
cardinal; //隐含扇区[1C]
dwSectoOfParti:
cardinal;//NTFS未使用,为0[20]
bDeviceFlag:
byte; //?
?
?
未知[24]
bReserve2:
byte; //?
?
?
未知[25]
wReserve3:
word; //?
?
?
未知[26]
ullSectorsOfParti:
int64;//扇区总数(这里理论上是一个64位无符号类型,不知用int64是否有错)(重要)[28]
ullMFTAddr:
int64; //$MFT的起始逻辑簇号(这里理论上是一个64位无符号类型,不知用int64是否有错)(重要)[30]
ullMFTMirrAddr:
int64; //$MFTMirr的起始逻辑簇号(这里理论上是一个64位无符号类型,不知用int64是否有错)(重要)[38]
bClusterPerFile:
cardinal;//每MFT记录簇数[40]
dwClusterPerINDX:
cardinal;//每索引簇数[44]
bSerialID:
array[0..7] of char;//卷标[48]
//[50]
end;
[Q][U]作者:
wuguan主题:
3引导扇区[/U][BR]现在我们用Wu_ReadDiskSector读取分区第一个扇区的数据[COLOR=red]varP:
Pchar;Wu_OpenDiskSector(\39G\39);//打开G盘Wu_ReadDiskSector(0,P);//读取G盘0扇区Wu_CloseDiskSector;[/COLOR]获得这个扇区称为引导扇区,512个字节数据......[/Q]
3引导扇区
签名:
3
<精华>引导扇区[1024字节wuguan2009-8-6 0:
48:
56回复:
0/282版块:
Delphi]引用
现在我们已经可以分析引导扇区所记录的内容.
var
P:
Pchar;
BPR:
R_Wu_NTFSBPB;
Wu_OpenDiskSector('G'); //打开G盘
Wu_ReadDiskSector(0,P); //读取G盘0扇区
wu_Copy(P,1,80,@BPR,1); //将数据直接拷贝到结构变量中即可
Wu_CloseDiskSector;
下面我们可以根据BPR的数据来进行一些初始化的设置
unit Wu_NTFSLook;
interface
uses
Wu_DiskSectorUnit,Wu_NTFSTypeUnit,Wu_BasicUnit;
procedure OpenDisk(Disk:
Char); //打开某个盘
procedure CloseDisk; //退出时关闭
implementation
var
BPR:
R_Wu_NTFSBPR; //用来保存当前打开分区的引导数据
ClusterSize:
cardinal;//定义当前打开分区中每一个簇等于多少个基本扇区
MFTRecordSize:
cardinal=2; //定义当前打开分区中每一个MFT等于多少个基本扇区
MFTBegin:
int64; //定义当前打开分区中MFT的起始位置(每几个基本扇区)
procedure OpenDisk(Disk:
Char);
var
P:
Pchar;
begin
Wu_CloseDiskSector;
Wu_OpenDiskSector(Disk);
Wu_ReadDiskSector(0,P); //读取指定盘0扇区
Wu_Copy(P,1,80,@BPR,1);
ClusterSize:
=BPR.wBytePerSector div 512 * BPR.bSectorPerCluster;
MFTBegin:
=BPR.ullMFTAddr * ClusterSize;
end;
procedure CloseDisk;
begin
Wu_CloseDiskSector;
end;
end.
[Q][U]作者:
wuguan主题:
引导扇区[/U][BR]现在我们已经可以分析引导扇区所记录的内容.[COLOR=red]varP:
Pchar;BPR:
R_Wu_NTFSBPB;Wu_OpenDiskSector(\39G\39);//打开G盘Wu_ReadDiskSector(0,P);//读取G盘0扇区wu_Copy(P,1,80,@BPR,1);//将数据直接拷贝到结构变量中即可Wu_......[/Q]
引导扇区
签名:
4
<精华>MFT记录[2093字节wuguan2009-8-6 0:
52:
7回复:
0/253版块:
Delphi]引用
下一步就可以通过[$MFT的起始逻辑簇号]来访问$MFT.
MFT应该是一个数据结构,由每一条MFT整齐排列组成一整个MFT列表,用来记录磁盘内所有文件目录的关系.
既然MFT是一个接着一个排列的,那么我们必须要知道每一个MFT的大小,以方便我们的指针对应位置.
(注:
这里MFT的大小是一个很奇怪的问题,现在所查到的资料都说得不清不楚,有些说固定大小为1024字节,有些又说不固定,有待研究)
我们暂且定义MFT的大小为1024字节.
const
MFTRecordSize=1024; //不管怎样都一定是512的整数倍这是肯定的
然后[$MFT的起始逻辑簇号]指向了列表第一项的位置.
BPR.ullMFTAddr;
下面是从网上找来的MFT文件记录属性头结构
偏移 长度 描述
0X00 4 固定值“FILE”
0X04 2 更新序列号偏移,与操作系统有关
0X06 2 固定列表大小
0X08 8 日志文件序列号
0X10 2 序列号(用于记录文件被反复使用的次数)
0X12 2 硬连接数,跟目录中的项目关联,非常重要的参数
0X14 2 第一个属性的偏移
0X16 2 标志字节①
0X18 4 文件记录实时大小(字节)②
0X1C 4 文件记录分配大小(字节)
0C20 8 基础记录 (0:
itself)
0X28 2 下一个自由ID号
0X2A 2 边界
0X2C 4 WINDOWS XP中使用,本MFT记录号
0X30 4 MFT的使用标记③
经详细研究,MFT记录中前52($34)个字节的内容所表达的意思是固定的,那么我们又可以写数据结构了
type
R_Wu_MFT1=packed record
bHeadID:
array [0..3] of char; //MFT标志,'FILE'[0]
usFixupOffset:
word; //更新序列号偏移,与操作系统有关[4]
usFixupNum:
word; //更新序列号的大小,固定列表大小[6]
bReserve1:
array [0..7] of byte; //日志文件序列号[8]
wUnknownSeqNum:
word; //序列号(用于记录文件被反复使用的次数)[10]
usLinkNum:
word; //硬连接数[12]
usAttrOffset:
word; //第一个属性的偏移地址(重要)[14]
wResident:
word; //文件属性标志(重要,表示这个MFT文件的状态1:
普通文件 0:
文件被删除 3:
普通目录 2:
目录被删除)[16]
ulMFTSize:
cardinal; //文件记录的实际长度,用来记录本MFT的实时大小(重要,用于MFT中每个属性分析到结束)[18]
ulMFTAllocSize:
cardinal; //记录分配的大小(重要?
?
)[1C]
ullMainMFT:
int64; //基本文件记录的文件索引号(重要?
?
)[20]
wNextFreeID:
word; //下一个自由ID号(重要?
?
)[28]
wBianJie:
word; //边界[2A]
ulXpMFTNum:
cardinal; //WINDOWS XP中使用,本MFT记录号[2C]
MFTusesID:
word; //MFT的使用标记(重要,它在MFT记录的两个扇区中与每扇区的最末两个字节相对应,如若不然,系统将示此记录为非法记录)[30]
//[32]
MFTUsesTemp:
array of word; //MFT的使用标记(重要,上面由于MFT记录扇区将每个扇区中最后的两个字节占用掉了,所以在这里补回来)[32]
end;
MFT是由一个固定的头52($34)字节和一系列属性(不固定,每一条属性长度不固定,属性列表是由哪一些属性组成也不固定)组成,但总大小不超过1024字节(超过时由多条记录组成).
那么现在我们就可以读取指定一项MFT的记录
[Q][U]作者:
wuguan主题:
MFT记录[/U][BR]下一步就可以通过[$MFT的起始逻辑簇号]来访问$MFT.MFT应该是一个数据结构,由每一条MFT整齐排列组成一整个MFT列表,用来记录磁盘内所有文件目录的关系.既然MFT是一个接着一个排列的,那么我们必须要知道每一个MFT的大小,以方便我们的指针对应位置.(注:
这里MFT的大小是一个很奇怪的问题,现在所查到的资料都说得不清不楚,有些说固定大小为1024字节,有些又说不固定,有待研究)......[/Q]
MFT记录
签名:
★
5
<精华>MFT属性[880字节wuguan2009-8-10 23:
53:
32回复:
0/598版块:
Delphi]引用
我们写一个函数用来获得指定第几个MFT的MFT头数据.
var
MFTHead:
R_Wu_MFT1; //用来保存调用GetMFTHead所获得的MFT记录基本头数据
functi