NTFS文件结构实习(2)代码实现

打开磁盘:

procedure  OpenDiskEx(drive:string);
begin
   hDriveHandle:=CreateFile(Pchar(drive),GENERIC_ALL,FILE_SHARE_READ or FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
end;

读磁盘数据:

procedure  ReadDisk(Outdat:pointer;SectorCount:dword;SectorStart:int64);
var
   p:  PChar;
   offe:int64;
begin
   if  (hDriveHandle  <>  INVALID_HANDLE_VALUE)  then
   begin
     p:=allocmem(SectorCount * ByteSPerSector);
     offe:=SectorStart  *  BytesPerSector;
     FSeek64(hDriveHandle, offe,  0);  //起始扇区
     if  FileRead(hDriveHandle,p[0],integer(SectorCount * BytesperSector))<> integer(SectorCount * BytesperSector) then   //读扇区
         raise  Exception.Create('Read Disk Error!(Dk0001)');
     copymemory(Outdat,@p[0],SectorCount * ByteSPerSector);
     FreeMem(p, SectorCount * BytesperSector);
   end;
end;

读DBR 的BPB 获取 MFT参数:

procedure GetDBR_BPB(SectorStart:int64);
var ctr:pchar;
    temp:array of byte;
begin
  ctr := allocmem(512);     //BPB就1个扇区就够了
  setlength(temp,$54);
  ReadDisk(ctr,1,SectorStart);
  copymemory(@temp[0],@ctr[0],$54);
  FreeMem(ctr,512);
  copymemory(@dbrs.FatID[1],@temp[3],$20);     //只拷贝我关心的数据
  copymemory(@dbrs.PTotSec,@temp[$28],$20);
end;

获取文件列表:

procedure TForm1.GetWJList;
var  Temp,pAss: pBYTE;         //
     pIndexAss: pBYTE;
     MftLen:integer;
     last,j,k,i:integer;
     IndexName,FileName:array[0..256*2-1] of char;   //索引名
     IndexNameLen,FileNameLen:UINT;                  //索引名长度
     Len:LARGE_INTEGER;                              //文件指针
     filena:string;
     dwLen:DWORD;
     pIndex:PBYTE;
begin
   if ListView1.Selected = nil then exit;
   listview2.Clear;
   Temp :=allocmem(1024);           //每个文件2个扇区 = 512*2 =1024
   pIndexAss := allocmem(512);
   pIndex := allocmem(4096);        //一个索引记录一般是4096
   OpenDiskEx(ComboBox1.Text);
   ReadDisk(Temp,2,MFTSectors+2*5);  //$root 在第6个FILE 5#
   CloseDisk;
   //去掉MFT头 和 尾尺寸 的净尺寸
   MftLen := pNTFS_MFT_FILE_HEAD(Temp).mftsize - pNTFS_MFT_FILE_HEAD(Temp).Firstattribute - 8;
   if (pdword(Temp)^ = $454C4946) then   //必须是“FILE”才可以
   begin
     dword(temp):=pNTFS_MFT_FILE_HEAD(Temp).Firstattribute + dword(temp);
     while( MftLen>0 ) do
     begin
       case pNTFS_ATTRIBUTE_HEAD(temp).Attribute of
        $30:    //用30属性来判断是不是$root
         begin
           dword(pAss) := dword(temp) + pNTFS_FILENAME_HEAD(temp).AttributeContentOffset;
           FileNameLen :=pNTFS_FILENAME_ATTRIBUTE(pAss).FileNameLen ;
           dword(pAss):=dword(@pNTFS_FILENAME_ATTRIBUTE(pAss).FileName);
           //转换文件名
           k := WideCharToMultiByte( CP_ACP,WC_COMPOSITECHECK,LPCWSTR(pAss),FileNameLen,pchar(@FileName[0]),254*2,NiL,NiL);
           pbyte(@(FileName[k]))^:=0;
           filena:= FileName;
           if filena <> '.' then  exit;  //不是$root文件退出
         end;
        //还是没有完善..要能够适应1簇=2扇区,4扇区,8扇区.....等等多种情况才可以...现在还是固定读8扇区.
        $A0:   // 索引分配 这个主要是找Runlist ,并读取索引缓冲,得到文件列表
         begin
           pAss := temp;
           dword(pAss) := dword(pAss) + $48;    // RunList 位置
           if not RunAnalyze(pAss) then  exit;  //list不正确、没有退出
           for j :=1 to FMapOut[0].NumsOfLcn do //每次一个RUN
           begin
             Len.QuadPart := FMapOut[j].StartLcn;
             Len.QuadPart := Len.QuadPart * dbrs.SecPerClu+DBRAdres;            //偏移是以DBR的地址为基准的
             for i :=1 to (FMapOut[j].NumsOfLcn div (8 div dbrs.SecPerClu)) do  //每次一个索引记录,通常为4k
             begin
               OpenDiskEx(ComboBox1.Text);
               ReadDisk(pIndex,8,Len.QuadPart);         //读取8个扇区的索引
               CloseDisk;
               if (pDWORD(pIndex)^ <> $58444E49) then   //不是INDX标示就终止
                 break;
               pAss:= pIndex;
               last:=0;
               dword(pAss) := dword(pAss) + $18;           //在索引偏移18H的地方是第一个索引项开始偏移量
               dword(pAss) := dword(pAss) + pDWORD(pAss)^; //这个偏移量是相对偏移18H这个地址开始计算的
               while(Last < 2 ) do                         //INDEX_ITEM在偏移0Ch的地方标示了最后索引项
               begin
                 IndexNameLen := PNTFS_INDEX_ITEM(pAss).FileNameLen;
                 fillmemory(pIndexAss ,512 ,0);
                 copymemory(pIndexAss,@(PNTFS_INDEX_ITEM(pAss).FileName),IndexNameLen*2) ;
                 k := WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,
                       LPCWSTR(pIndexAss),IndexNameLen,@IndexName[0],254*2,NiL,NiL);
                 pbyte(@(IndexName[k]))^:=0;
                 filena:= IndexName;
                 if filena <> ''  then
                 with listview2.items.add do
                   Caption:=filena;
                 dword(pAss) := dword(pAss) + PNTFS_INDEX_ITEM(pAss).IndexLen;
                 Last := PNTFS_INDEX_ITEM(pAss).IndexSign;
               end;
             end;
           end;
         end;
       end;
       MftLen := MftLen - pNTFS_ATTRIBUTE_HEAD(Temp).AllLen;
       dword(Temp) := dword(Temp) + pNTFS_ATTRIBUTE_HEAD(Temp).AllLen;
     end;
   end;
end;

runlist分析的代码参考网上C代码,因为人家比我写的好,精炼!!

function RunAnalyze(RunListData:PBYTE):boolean;
var
   Temp:PBYTE;         //用于操作runlist的临时指针
   StaClusBits:byte;   //runlist开始簇占用的list位数
   ClusSizBits:byte;   //runlist簇的大小占用的list位数
   RunCount:integer;   //runlist里面run的个数
   StartCluster:DWORD; //开始簇
   ClusterSize:WORD;   //簇的大小 
begin
   //初始化参数为0
   RunCount := 0;     //当前运行的个数
   StartCluster := 0;
   ClusterSize := 0;
   Temp := RunListData;  //取得run指针
   while temp^ > 0 do    //当第一个字节为0 就结束了
   begin
     StaClusBits :=  Temp^ shr 4 ;     //开始的簇  取高位 得到 开始簇占用的list位数
     ClusSizBits :=  Temp^ and $F;     //长度      取低位 得到 簇的大小占用的list位数
     //一个run项目的大小 = 1+ (ClusSizBits + StaClusBits) 如:32 那么这个listsize = 1+(2+3)=6
     //排列方式:type  size  Cluster
     if(StaClusBits = 0)then begin
      //遇到 01的情况,开始蔟号还必须加上 上次的cluster 的大小
       StartCluster := StartCluster + ClusterSize ;
   end; {}
     inc(temp);                              //temp指针移动一位到 簇的大小 位置
     ClusterSize:=pWORD(temp)^ and ( $FFFF shr (16- 8*ClusSizBits));
     inc(temp,ClusSizBits);                  //temp指针移动*位到 开始簇 位置
     //开始簇是和前面叠加的
     StartCluster:=StartCluster + (pDWORD(temp)^ and ($FFFFFF shr (24-8*StaClusBits)));
     inc(temp,StaClusBits);                  //temp指针移动*位到 下一个runlist 位置
     RunCount:=RunCount+1;                   //有就+1 最后就是所有run的数目了;
     if(RunCount>4095) then  break;
     //填充输出FMapOut结构
   FMapOut[0].NumsOfLcn := RunCount;
   FMapOut[RunCount].StartLcn := StartCluster and $FFFFFF;
   FMapOut[RunCount].NumsOfLcn := ClusterSize;
   end;
   result:=true;
end;

列出的第一分区文件,还没有区分文件与文件夹(这个比较简单)

就这样吧........遍历就遍历吧.....吐舌头

猜你喜欢

转载自blog.csdn.net/xtbpl/article/details/10230073