Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2455
  • Last Modified:

Delphi code for undelete file?

Is there a code in Delphi like the undelete DOS command? I want to know the list of the files that is possible to recover?
In the
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_21529939.html?sfQueryTermInfo=1+delphi+undelet

there is a solution but it is no longer available.
0
andreamatt
Asked:
andreamatt
  • 9
  • 5
  • 3
  • +1
2 Solutions
 
CodedKCommented:
I have a source code by Christian Grau.
Once upon a time ha gave it for free now i think he sells it so i cannot give it to you.
Maybe if you search for this source in google you will find it.
0
 
andreamattAuthor Commented:
Hi CodedK,

I don't find it on Google. Some keywords to find?

Andrea
0
 
andreamattAuthor Commented:
Hi,
I find it. Thanks.

Andrea
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
andreamattAuthor Commented:
Hi CodedK,
I have see the software but there is no code for Delphi!
Do you have this code? I only want to know the list of the file that is it possible to recover. No the code to recover the files.

Andrea
0
 
CodedKCommented:
Andrea i cant give something that belongs to someone else.
He decided to sell this, i cannot give his source, sorry.

The source is for Delphi and its very good ! Full application with source (view, undelete, restore deleted partitions) everything is inside.

I don't know what to say... Look harder.
If i find anything else i will post something but i doubt it because everyone sells this code.

0
 
andreamattAuthor Commented:
Hi,
I understand the situation.

Thanks.

Someone have a free code?

Andrea

0
 
CodedKCommented:
The problem Andrea is that this kind of source requires the build of a driver for NT file system...
And then some source to handle this.
So i sincerely i doubt that anyone will answer to your question.

"Windows NT File System Internals : A Developer's Guide"
http://www.amazon.com/Windows-File-System-Internals-Developers/dp/1565922492

I'll upload the sources here :
http://www.geocities.com/kalatz_gr_fisher/Undelete.zip

Tell me when you will download it, so i can remove it.
0
 
andreamattAuthor Commented:
Hi,
just download. You can remove it. I see it now.
Thanks,
Andrea
0
 
andreamattAuthor Commented:
Hi,

good code! Thanks.
But is very difficult for me to find or create a procedure-function to have the list of file from a drive. Can you help me?

Andrea


0
 
CodedKCommented:
Hi Andrea.
Look, you asked for a code that will provide a list of the files that is possible to recover.

Short answer : This is not possible.
Because even the pro applications out there that provide undelete functions can sometime give you a corrupted file back or part of a file.

Moreover the code i gave you is for FAT 32 allocation table. It cannot scan NTFS drives (it scans physical as logical though). Like i told you its unbelievably hard to do something like this. If i had this code i would sold it ! And if i started a project like this it would be a fulltime job. In Torry site there is NO Delphi code for this and there is a reason for that ;)

  • First of all you need to know the specifications for your file system. In case of NTFS this is not publicly available !!!  The link to the book i post earlier is a very good source though.
  • Then you can write a program that reads the partition sector by sector and it will be your program that has to analyze the structure according to the specifications, search for the lost data and try to rebuild the file from the raw sectors. There is a component to read file sectors "Raw Disk Access v.1.1" inside Torry site :  http://www.torry.net/quicksearchd.php?String=Raw+Disk+Access&Title=Yes   , (demo app with source included...free), but this is the 1st step only....  :/
Do not except some 100 line code. You need to build a driver and some serious low level programming. Your question sounds like "can you give me the code of WindowsXP".
Its just not possible sorry.

If you feel like it, there is the code i gave you so you can study it and all the other info.
Sorry for letting you down. :/
If you think that i might be wrong feel free to wait for other experts to help you :)
0
 
andreamattAuthor Commented:
Hi CodedK,
Unfortunately I was not clear in my response or request. I apologize. I'm very sorry for this.
I am only interested in knowing the list of files that can be recovered completely or partially.
I do not care even know if a file is or not in good or poor condition. I am not interested to have the code to retrieve-undelete the file.

The link to the book you have post is very good but for me is difficult to find (in this book-code) or to create a procedure-function to ONLY have the list of file from a specific drive. Can you help me with this new information?
0
 
andreamattAuthor Commented:
Hi,

Now 500 points!

Please, help me!

Andrea
0
 
Russell LibbySoftware Engineer, Advisory Commented:
Andrea,

I'm posting a code conversion done from source on CodeProject. Its for undeleting files on an NTFS drive. It seems "fairly" stable, and I have tested it and it does undelete files by using the MFT of the drive. As CodedK already stated, some of the sectors for the file may already be used for something else, so don't expect miracles. Hopefully this will get you started in the right direction.

Russell


----
unit Ntfs;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  Ntfs
//   Source      :  http://www.codeproject.com/KB/files/NTFSUndelete.aspx
//   Conversion  :  rllibby
//   Date        :  12.08.2008
//   Description :  NTFS drive and file handling
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes;

////////////////////////////////////////////////////////////////////////////////
//   Constants
////////////////////////////////////////////////////////////////////////////////
const
  PHYSICAL_DRIVE                =  '\\.\PhysicalDrive0';
  PART_TABLE                    =  0;
  BOOT_RECORD                   =  1;
  EXTENDED_PART                 =  2;
  PART_UNKNOWN                  =  $00;         // Unknown.
  PART_DOS2_FAT                 =  $01;            // 12-bit FAT.
  PART_DOS3_FAT                 =  $04;            // 16-bit FAT. Partition smaller than 32MB.
  PART_EXTENDED                 =  $05;            // Extended MS-DOS Partition.
  PART_DOS4_FAT                 =  $06;            // 16-bit FAT. Partition larger than or equal to 32MB.
  PART_DOS32                    =  $0B;         // 32-bit FAT. Partition up to 2047GB.
  PART_DOS32X                   =  $0C;            // Same as PART_DOS32(0Bh), but uses Logical Block Address Int 13h extensions.
  PART_DOSX13                   =  $0E;            // Same as PART_DOS4_FAT(06h), but uses Logical Block Address Int 13h extensions.
  PART_DOSX13X                  =  $0F;            // Same as PART_EXTENDED(05h), but uses Logical Block Address Int 13h extensions.

////////////////////////////////////////////////////////////////////////////////
//   Data type declarations
////////////////////////////////////////////////////////////////////////////////
type

  DRIVEINFO                     =  packed record
     wCylinder:                 Word;
     wHead:                     Word;
     wSector:                   Word;
         dwNumSectors:              DWORD;
     wType:                     Word;
     dwRelativeSector:          DWORD;
            dwNTRelativeSector:        DWORD;
            dwBytesPerSector:          DWORD;
  end;
  LPDRIVEINFO                   =  ^DRIVEINFO;
  DRIVEINFO_ARRAY               =  Array [0..MaxWord] of DRIVEINFO;
  LPDRIVEINFO_ARRAY             =  ^DRIVEINFO_ARRAY;

  PARTITION                     =  packed record
     chBootInd:                 Byte;
     chHead:                    Byte;
     chSector:                  Byte;
     chCylinder:                Byte;
     chType:                    Byte;
     chLastHead:                Byte;
     chLastSector:              Byte;
     chLastCylinder:            Byte;
     dwRelativeSector:          DWORD;
     dwNumberSectors:           DWORD;
  end;
  LPPARTITION                   =  ^PARTITION;

  ST_FILEINFO                   =  packed record
     dwIndex:                   DWORD;
     szFileName:                Array [0..Pred(MAX_PATH)] of Char;
     n64Create:                 LONGLONG;
     n64Modify:                 LONGLONG;
     n64Modfil:                 LONGLONG;
     n64Access:                 LONGLONG;
     dwAttributes:              DWORD;
     n64Size:                   LONGLONG;
     bDeleted:                  BOOL;
  end;

  NTFS_BPB                      =  packed record
     wBytesPerSec:              Word;
     uchSecPerClust:            Byte;
     wReservedSec:              Word;
     uchReserved:               Array [0..2] of Byte;
            wUnused1:                  Word;
     uchMediaDescriptor:        Byte;
            wUnused2:                  Word;
     wSecPerTrack:              Word;
     wNumberOfHeads:            Word;
            dwHiddenSec:               DWORD;
            dwUnused3:                 DWORD;
            dwUnused4:                 DWORD;
            n64TotalSec:               LONGLONG;
            n64MFTLogicalClustNum:     LONGLONG;
            n64MFTMirrLogicalClustNum: LONGLONG;
            nClustPerMFTRecord:        Integer;
            nClustPerIndexRecord:      Integer;
            n64VolumeSerialNum:        LONGLONG;
            dwChecksum:                DWORD;
  end;

  NTFS_PART_BOOT_SEC            =  packed record
     chJumpInstruction:         Array [0..2] of Char;
     chOemID:                   Array [0..3] of Char;
     chDummy:                   Array [0..3] of Char;
     bpb:                       NTFS_BPB;
         chBootstrapCode:           Array [0..425] of Char;
         wSecMark:                  Word;
  end;

  NTFS_MFT_FILE                 =  packed record
         szSignature:               Array [0..3] of Char;
     wFixupOffset:              Word;
         wFixupSize:                Word;
     n64LogSeqNumber:           LONGLONG;
     wSequence:                 Word;
     wHardLinks:                Word;
     wAttribOffset:             Word;
     wFlags:                    Word;
     dwRecLength:               DWORD;
         dwAllLength:               DWORD;
     n64BaseMftRec:             LONGLONG;
     wNextAttrID:               Word;
     wFixupPattern:             Word;
     dwMFTRecNumber:            DWORD;
  end;

  ATTR_RESIDENT                 =  packed record
         dwLength:                  DWORD;
     wAttrOffset:               Word;
     uchIndexedTag:             Byte;
     uchPadding:                Byte;
  end;

  ATTR_NONRESIDENT              =  packed record
     n64StartVCN:               LONGLONG;
     n64EndVCN:                 LONGLONG;
     wDatarunOffset:            Word;
     wCompressionSize:          Word;
     uchPadding:                Array [0..3] of Byte;
     n64AllocSize:              LONGLONG;
     n64RealSize:               LONGLONG;
     n64StreamSize:             LONGLONG;
  end;

  ATTR_UNION                    =  packed record
     case Integer of
        0  :  (Resident:     ATTR_RESIDENT);
        1  :  (NonResident:  ATTR_NONRESIDENT);
  end;

  NTFS_ATTRIBUTE                =  packed record
         dwType:                    DWORD;
            dwFullLength:              DWORD;
     uchNonResFlag:             Byte;
     uchNameLength:             Byte;
     wNameOffset:               Word;
     wFlags:                    Word;
     wID:                       Word;
     Attr:                      ATTR_UNION;
  end;

  ATTR_STANDARD                 =  packed record
         n64Create:                 LONGLONG;
         n64Modify:                 LONGLONG;
         n64Modfil:                 LONGLONG;
     n64Access:                 LONGLONG;
         dwFATAttributes:           DWORD;
     dwReserved1:               DWORD;
  end;

  ATTR_FILENAME                 =  packed record
     dwMftParentDir:            LONGLONG;
         n64Create:                 LONGLONG;
         n64Modify:                 LONGLONG;
         n64Modfil:                 LONGLONG;
         n64Access:                 LONGLONG;
         n64Allocated:              LONGLONG;
         n64RealSize:               LONGLONG;
     dwFlags:                   DWORD;
     dwEAsReparsTag:            DWORD;
     chFileNameLength:          BYTE;
     chFileNameType:            BYTE;
     wFilename:                 Array [0..511] of WChar;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Class declarations
////////////////////////////////////////////////////////////////////////////////
type
  TNTFSDrive           =  class(TObject)
  private
     // Private declarations
     FDrive:           THandle;
     FInitialized:     Boolean;
     FStartSector:     DWORD;
     FBytesPerCluster: DWORD;
     FBytesPerSector:  DWORD;
         FMFT:             PByteArray;
     FMFTRecord:       PByteArray;
     FMFTLen:          DWORD;
     FMFTRecordSz:     DWORD;
     function          LoadMFT(nStartCluster: LONGLONG): Integer;
  protected
     // Protected declarations
     procedure         Cleanup;
  public
     // Public declarations
     constructor       Create;
     destructor        Destroy; override;
     function          Initialize(lpDriveInfo: DRIVEINFO): Integer;
         function          GetFileDetail(nFileSeq: DWORD; out stFileInfo: ST_FILEINFO): Integer;
         function          ReadFile(nFileSeq: DWORD; Stream: TStream): Integer;
     // Public properties
     property          BytesPerCluster: DWORD read FBytesPerCluster;
     property          BytesPerSector: DWORD read FBytesPerSector;
     property          Drive: THandle read FDrive;
     property          IsInitialized: Boolean read FInitialized;
     property          StartSector: DWORD read FStartSector;
  end;

  TMFTRecord           =  class(TObject)
  private
     // Private declarations
     FDrive:           THandle;
     FMFTRecord:       PByteArray;
     FMaxMFTRecSize:   DWORD;
     FCurPos:          DWORD;
     FBytesPerCluster: DWORD;
     F64StartPos:      LONGLONG;
     FInUse:           BOOL;
  protected
     // Protected declarations
     FAttrFilename:    ATTR_FILENAME;
     FAttrStandard:    ATTR_STANDARD;
     FFileDataSz:      DWORD;
     FFileData:        PByteArray;
     procedure         Cleanup;
         function          ReadRaw(n64LCN: LONGLONG; lpData: PByteArray; var n64Len: LONGLONG): Integer;
         function          ExtractData(ntfsAttr: NTFS_ATTRIBUTE; var lpData: PByteArray; out dwDataLen: DWORD): Integer;
  public
     // Public declarations
     constructor       Create(hDrive: THandle; n64StartPos: LONGLONG; dwRecSize: DWORD; dwBytesPerCluster: DWORD);
     destructor        Destroy; override;
     function          ExtractFile(lpMFTBuffer: PByteArray; dwLen: DWORD; bExcludeData: Boolean = False): Integer;
  end;

////////////////////////////////////////////////////////////////////////////////
//
//   Function    :  ScanLogicalDrives
//
//   Parameters  :  lpDriveInfo -  [in] Allocated buffer for an array of DRIVEINFO.
//                  Count       -  [in/out] On input, specifies the number of entries
//                                 that lpDriveInfo can hold. On output, it is set
//                                 with the total number of enties located. If the
//                                 buffer is too small to hold all the enties an
//                                 error code of ERROR_INSUFFICIENT_BUFFER will
//                                 be returned.
//
//   Returns     :  On success a code of ERROR_SUCCESS will be returned. If there
//                  are more entries than the buffer can hold, the buffer is filled
//                  and a return of ERROR_INSUFFICIENT_BUFFER is passed back. Any
//                  other code indicates a failure.
//
////////////////////////////////////////////////////////////////////////////////
function   ScanLogicalDrives(lpDriveInfo: LPDRIVEINFO_ARRAY; var Count: Integer): Integer;

implementation

//// TMFTRecord ////////////////////////////////////////////////////////////////
function TMFTRecord.ReadRaw(n64LCN: LONGLONG; lpData: PByteArray; var n64Len: LONGLONG): Integer;
var  n64Pos:        LARGE_INTEGER;
     lpTmp:         PByteArray;
         dwBytesRead:   DWORD;
         dwBytes:       DWORD;
         dwTotRead:     DWORD;
begin

  // Set position
      n64Pos.QuadPart:=(n64LCN) * FBytesPerCluster;

  // Update by actual start position
      Inc(n64Pos.QuadPart, F64StartPos);

      // Data is available in the relative sector from the begining od the drive
      if (SetFilePointer(FDrive, n64Pos.LowPart, @n64Pos.HighPart, FILE_BEGIN) = $FFFFFFFF) then
     // Failed
     result:=GetLastError
  else
  begin
     // Set default result
     result:=ERROR_SUCCESS;
     // Set temp pointer
     lpTmp:=lpData;
     // Clear counters
         dwBytes:=0;
         dwTotRead:=0;
     // While total read is less than length
         while (dwTotRead < n64Len) do
     begin
               // Reading a cluster at a time
               dwBytesRead:=FBytesPerCluster;
               // This can not read partial sectors
               if not(ReadFile(FDrive, lpTmp^, dwBytesRead, dwBytes, nil)) then
        begin
                     // Failure
           result:=GetLastError;
           // Done processing
           break;
        end;
        // Udpate total
               Inc(dwTotRead, dwBytes);
        // Update buffer pointer
        Inc(PChar(lpTmp), dwBytes);
     end;
     // Update total read
     n64Len:=dwTotRead;
  end;

end;

function TMFTRecord.ExtractData(ntfsAttr: NTFS_ATTRIBUTE; var lpData: PByteArray; out dwDataLen: DWORD): Integer;
var  n64Len:        LONGLONG;
     n64Offset:     LONGLONG;
            n64LCN:        LONGLONG;
     lpTmpBuff:     PByteArray;
     lpTemp:        PByteArray;
     dwCurPos:      DWORD;
     dwIndex:       Integer;
            chLenOffSz:    Byte;
            chLenSz:       Byte;
            chOffsetSz:    Byte;
begin

  // Default result
  result:=ERROR_SUCCESS;

  // Set return buffer
  lpData:=nil;

  // Resource protection
  try
     // Get current position
         dwCurPos:=FCurPos;
     // Check resident flag
         if (ntfsAttr.uchNonResFlag = 0) then
     begin
            // Residence attribute, this always resides in the MFT table itself
        dwDataLen:=ntfsAttr.Attr.Resident.dwLength;
        // Allocate memory for data
        lpData:=AllocMem(dwDataLen);
        // Copy data
        Move(FMFTRecord[dwCurPos + ntfsAttr.Attr.Resident.wAttrOffset], lpData^, dwDataLen);
     end
         else
     begin
            // Non-residence attribute, this resides in the other part of the physical drive
               if (ntfsAttr.Attr.NonResident.n64AllocSize = 0) then
        begin
           ntfsAttr.Attr.NonResident.n64AllocSize:=(ntfsAttr.Attr.NonResident.n64EndVCN - ntfsAttr.Attr.NonResident.n64StartVCN) + 1;
        end;
               // ATTR_STANDARD size may not be correct
               dwDataLen:=ntfsAttr.Attr.NonResident.n64RealSize;
               // Allocate for reading data
               lpData:=AllocMem(ntfsAttr.Attr.NonResident.n64AllocSize);
        // Set pointer
               n64LCN:=0;
        // Set pointer to temp data
               lpTmpBuff:=lpData;
        // Update current position
               Inc(dwCurPos, ntfsAttr.Attr.NonResident.wDatarunOffset);
        // While forever
               while True do
        begin
                     // Read the length of LCN / VCN and length
           chLenOffSz:=FMFTRecord[dwCurPos];
           // Update
           Inc(dwCurPos);
           // Check for zero
                     if (chLenOffSz = 0) then break;
           // Get len and offset size
                     chLenSz:=chLenOffSz and $0F;
                     chOffsetSz:=(chLenOffSz and $F0) shr 4;
                     // Read the data length
                     n64Len:=0;
           // Copy memory
                     Move(FMFTRecord[dwCurPos], n64Len, chLenSz);
           // Increment position
           Inc(dwCurPos, chLenSz);
                     // Read the LCN / VCN offset
                     n64Offset:=0;
           // Copy memory
                     Move(FMFTRecord[dwCurPos], n64Offset, chOffsetSz);
           // Increment position
                     Inc(dwCurPos, chOffsetSz);
           // Cast as byte array
           lpTemp:=PByteArray(@n64Offset);
                     // If the last bit of n64Offset is 1 then its -ve so youve got to make it -ve
                     if ((lpTemp[chOffsetSz - 1] and $80) = $80) then
           begin
              for dwIndex:=Pred(SizeOf(LONGLONG)) downto chOffsetSz do lpTemp[dwIndex]:=$FF;
           end;
           // Increment lcn
                     Inc(n64LCN, n64Offset);
           // Multiply and increment
                     n64Len:=n64Len * FBytesPerCluster;
           // Read the actual data; since the data is available out side the MFT table, physical drive should be accessed
           result:=ReadRaw(n64LCN, lpTmpBuff, n64Len);
           // Bail on error
           if (result <> ERROR_SUCCESS) then break;
           // Update the buffer
           Inc(PChar(lpTmpBuff), n64Len);
        end;
     end;
  finally
     // Free memory if failed
     if ((result <> ERROR_SUCCESS) and Assigned(lpData)) then
     begin
        // Free
        FreeMem(lpData);
        // Clear
        lpData:=nil;
        // Set data length
        dwDataLen:=0;
     end;
  end;

end;

function TMFTRecord.ExtractFile(lpMFTBuffer: PByteArray; dwLen: DWORD; bExcludeData: Boolean = False): Integer;
var  ntfsMFT:       NTFS_MFT_FILE;
         ntfsAttr:      NTFS_ATTRIBUTE;
     lpTempData:    PByteArray;
     dwTempData:    DWORD;
begin

  // Check params
      if (FMaxMFTRecSize > dwLen) or not(Assigned(lpMFTBuffer)) then
     // Invalid params
     result:=ERROR_INVALID_PARAMETER
  else
  begin
     // Set pointer to buffer
     FMFTRecord:=lpMFTBuffer;
     // Set current position
     FCurPos:=0;
     // Release file data
     Cleanup;
         // Copy the record header in MFT table
     Move(FMFTRecord[FCurPos], ntfsMFT, SizeOf(NTFS_MFT_FILE));
     // Check signature
     if (StrLComp(ntfsMFT.szSignature, 'FILE', 4) <> 0) then
        // Not the correct signature
        result:=ERROR_INVALID_PARAMETER
     else
     begin
        // Default result
        result:=ERROR_SUCCESS;
        // Determine if in use
        FInUse:=((ntfsMFT.wFlags and $01) = $01);
        // Set current pos
        FCurPos:=ntfsMFT.wAttribOffset;
        // Extract the attribute headers
            repeat
           // Break if current pos is > dwLen
           if (FCurPos >= dwLen) then break;
           // Copy the header
                  Move(FMFTRecord[FCurPos], ntfsAttr, SizeOf(NTFS_ATTRIBUTE));
           // Handle tha attribute type
           case ntfsAttr.dwType of
              // Unused
              $00      :  ;
              // Standard information
                     $10      :
              begin
                 // Extract the data
                           result:=ExtractData(ntfsAttr, lpTempData, dwTempData);
                 // Check result
                 if (result = ERROR_SUCCESS) then
                 begin
                    // Set standard attribute
                    Move(lpTempData^, FAttrStandard, SizeOf(ATTR_STANDARD));
                    // Free mem
                    FreeMem(lpTempData);
                    // Clear size
                    dwTempData:=0;
                 end
                 else
                    // Failure
                    break;
              end;
              // File name
                     $30      :
              begin
                 // Extract the data
                 result:=ExtractData(ntfsAttr, lpTempData, dwTempData);
                 // Check result
                 if (result = ERROR_SUCCESS) then
                 begin
                    // Set filename attribute
                    Move(lpTempData^, FAttrFilename, SizeOf(ATTR_FILENAME));
                    // Free mem
                    FreeMem(lpTempData);
                    // Clear size
                    dwTempData:=0;
                 end
                 else
                    // Failure
                    break;
              end;
              // Object ID
                     $40      :  ;
              // Security descriptor
                     $50      :  ;
              // Volume name
                     $60      :  ;
              // Volume informatio n
              $70      :  ;
              // Data
              $80      :
              begin
                 // Check to see if data is to be excluded
                 if not(bExcludeData) then
                 begin
                    // Extract the file data
                                 result:=ExtractData(ntfsAttr, lpTempData, dwTempData);
                    // Check result
                    if (result = ERROR_SUCCESS) then
                    begin
                       // Start of data, or data to append
                       if Assigned(FFileData) then
                       begin
                          // Realloc buffer
                          ReallocMem(FFileData, FFileDataSz + dwTempData);
                          // Copy new data
                          Move(lpTempData^, FFileData^[FFileDataSz], dwTempData);
                          // Update total file size
                          Inc(FFileDataSz, dwTempData);
                          // Free mem
                          FreeMem(lpTempData);
                          // Clear size
                          dwTempData:=0;
                       end
                       else
                       begin
                          // Set data buffer
                          FFileData:=lpTempData;
                          // Set data buffer size
                          FFileDataSz:=dwTempData;
                          // Clear pointer
                          lpTempData:=nil;
                       end;
                    end
                    else
                       // Failure
                       break;
                 end;
              end;
              // Index root
              $90      :  ;
              // Index allocation
              $A0      :  break;
              // Bitmap
                     $B0      :  ;
              // Reparse point
                     $C0      :  ;
              // EA Information
              $D0      :  ;
              // EA
              $E0      :  ;
              // Property set
                     $F0      :  ;
              // Logged utility stream
                     $100     :  ;
              // First user defined attribute
              $1000    :  ;
              // End
                     $FFFFFFFF:  break;
           else
              // Not handled
              break;
           end;
           // Update the current posisition
           Inc(FCurPos, ntfsAttr.dwFullLength);
        // Repeat while length is not zero
        until (ntfsAttr.dwFullLength = 0);
     end;
  end;

end;

procedure TMFTRecord.Cleanup;
begin

  // Resource protection
  try
     // Clear size
     FFileDataSz:=0;
     // Release memory
     if Assigned(FFileData) then FreeMem(FFileData);
  finally
     // Clear pointer
     FFileData:=nil;
  end;

end;

constructor TMFTRecord.Create(hDrive: THandle; n64StartPos: LONGLONG; dwRecSize: DWORD; dwBytesPerCluster: DWORD);
begin

  // Perform inherited
  inherited Create;

  // Set defaults
  FDrive:=hDrive;
  FMaxMFTRecSize:=dwRecSize;
  FBytesPerCluster:=dwBytesPerCluster;
  F64StartPos:=n64StartPos;
  FillChar(FAttrStandard, SizeOf(ATTR_STANDARD), 0);
  FillChar(FAttrFilename, SizeOf(ATTR_FILENAME), 0);
  FFileData:=nil;
  FMFTRecord:=nil;
  FFileDataSz:=0;
  FCurPos:=0;
  FInUse:=False;

  // Sanity check on the params
  if (hDrive = INVALID_HANDLE_VALUE) then
     // Raise
     raise Exception.Create(SysErrorMessage(ERROR_INVALID_PARAMETER))
  // Params must be non zero, and divisible by 2
  else if (dwRecSize = 0) or ((dwRecSize mod 2) <> 0) then
     // Raise
     raise Exception.Create(SysErrorMessage(ERROR_INVALID_PARAMETER))
  // Params must be non zero, and divisible by 2
  else if (dwBytesPerCluster = 0) or ((dwBytesPerCluster mod 2) <> 0) then
     // Raise
     raise Exception.Create(SysErrorMessage(ERROR_INVALID_PARAMETER));

end;

destructor TMFTRecord.Destroy;
begin

  // Resource protection
  try
     // Cleanup
     Cleanup;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

//// TNTFSDrive ////////////////////////////////////////////////////////////////
function TNTFSDrive.GetFileDetail(nFileSeq: DWORD; out stFileInfo: ST_FILEINFO): Integer;
var  mftFile:       TMFTRecord;
begin

  // Check initialized state
      if not(FInitialized) then
     // Return error
            result:=ERROR_INVALID_ACCESS
  // Check for end
  else if ((nFileSeq * FMFTRecordSz + FMFTRecordSz) >= FMFTLen) then
            result:=ERROR_NO_MORE_FILES
  else
  begin
         // Copy the record of the file in the MFT table
     Move(FMFT[nFileSeq * FMFTRecordSz], FMFTRecord^, FMFTRecordSz);
     // Create record reader
     mftFile:=TMFTRecord.Create(FDrive, FStartSector * FBytesPerSector, FMFTRecordSz, FBytesPerCluster);
     // Resource protection
     try
        // Extract the file
            result:=mftFile.ExtractFile(FMFTRecord, FMFTRecordSz, True);
        // Check result
        if (result = ERROR_SUCCESS) then
        begin
           // Clear outbound struct
           FillChar(stFileInfo, SizeOf(ST_FILEINFO), 0);
           // Set index
           stFileInfo.dwIndex:=nFileSeq;
           // Convert filename from widechar to ansi string
           StrPCopy(@stFileInfo.szFileName, WideCharLenToString(@mftFile.FAttrFilename.wFilename, mftFile.FAttrFilename.chFileNameLength));
           // Set attributes
               stFileInfo.dwAttributes:=mftFile.FAttrFilename.dwFlags;
               stFileInfo.n64Create:=mftFile.FAttrStandard.n64Create;
               stFileInfo.n64Modify:=mftFile.FAttrStandard.n64Modify;
               stFileInfo.n64Access:=mftFile.FAttrStandard.n64Access;
           stFileInfo.n64Modfil:=mftFile.FAttrStandard.n64Modfil;
               stFileInfo.n64Size:=mftFile.FAttrFilename.n64Allocated;
               stFileInfo.n64Size:=stFileInfo.n64Size div FBytesPerCluster;
           if (stFileInfo.n64Size = 0) then stFileInfo.n64Size:=1;
               stFileInfo.bDeleted:=not(mftFile.FInUse);
        end;
     finally
        // Free the object
        mftFile.Free;
     end;
  end;

end;

function TNTFSDrive.ReadFile(nFileSeq: DWORD; Stream: TStream): Integer;
var  mftFile:       TMFTRecord;
begin

  // Check initialized state
      if not(FInitialized) then
     // Return error
            result:=ERROR_INVALID_ACCESS
  // Check for end
  else if ((nFileSeq * FMFTRecordSz + FMFTRecordSz) >= FMFTLen) then
            result:=ERROR_NO_MORE_FILES
  // Check stream
  else if (Stream = nil) then
     // Invalid param
     result:=ERROR_INVALID_PARAMETER
  else
  begin
         // Copy the record of the file in the MFT table
     Move(FMFT[nFileSeq * FMFTRecordSz], FMFTRecord^, FMFTRecordSz);
     // Create record reader
     mftFile:=TMFTRecord.Create(FDrive, FStartSector * FBytesPerSector, FMFTRecordSz, FBytesPerCluster);
     // Resource protection
     try
        // Extract the file
            result:=mftFile.ExtractFile(FMFTRecord, FMFTRecordSz, False);
        // Check result
        if (result = ERROR_SUCCESS) then
        begin
           // Write file data to stream
           Stream.Write(mftFile.FFileData^, mftFile.FFileDataSz);
        end;
     finally
        // Free the object
        mftFile.Free;
     end;
  end;

end;

function TNTFSDrive.LoadMFT(nStartCluster: LONGLONG): Integer;
var  wszMFTName:    Array [0..4] of WideChar;
     mftRec:        TMFTRecord;
     n64Pos:        LARGE_INTEGER;
     dwBytes:       DWORD;
begin

  // Convert $MFT to wide char
  StringToWideChar('$MFT', @wszMFTName, SizeOf(wszMFTName));

  // NTFS starting point
  n64Pos.QuadPart:=FBytesPerSector * FStartSector;

  // MFT starting point
  n64Pos.QuadPart:=n64Pos.QuadPart + nStartCluster * FBytesPerCluster;

      // Set the pointer to the MFT start
  if (SetFilePointer(FDrive, n64Pos.LowPart, @n64Pos.HighPart, FILE_BEGIN) = $FFFFFFFF) then
     // Failure
     result:=GetLastError
  else
  begin
     // Reading the first record in the NTFS table; first record in the NTFS is always MFT record
     if not(Windows.ReadFile(FDrive, FMFTRecord^, FMFTRecordSz, dwBytes, nil)) then
        // Failed to read
        result:=GetLastError
     else
     begin
        // Create mft record
        mftRec:=TMFTRecord.Create(FDrive, LONGLONG(FStartSector * FBytesPerSector), FMFTRecordSz, FBytesPerCluster);
        // Resource protection
        try
           // Now extract the MFT record just like the other MFT table records
           result:=mftRec.ExtractFile(FMFTRecord, dwBytes);
           // Check result
           if (result = ERROR_SUCCESS) then
           begin
              // Check mft name
              if not(CompareMem(@mftRec.FAttrFilename.wFilename, @wszMFTName, 8)) then
                 // Set result
                 result:=ERROR_BAD_DEVICE
              else
              begin
                 // Delete MFT pointer
                 if Assigned(FMFT) then
                 begin
                    FreeMem(FMFT);
                    FMFT:=nil;
                    FMFTLen:=0;
                 end;
                 // This data (m_puchFileData) is special since it is the data of entire MFT file
                 FMFT:=AllocMem(mftRec.FFileDataSz);
                 // Set size
                 FMFTLen:=mftRec.FFileDataSz;
                 // Store this file to read other files
                 Move(mftRec.FFileData^, FMFT^,FMFTLen);
              end;
           end;
        finally
           // Free the record info
           mftRec.Free;
        end;
     end;
  end;

end;

function TNTFSDrive.Initialize(lpDriveInfo: DRIVEINFO): Integer;
var  lpBootSec:     NTFS_PART_BOOT_SEC;
     n64StartPos:   LARGE_INTEGER;
     dwBytes:       DWORD;
begin

  // Cleanup first
  Cleanup;

  // Default result
  result:=ERROR_SUCCESS;

  // Resource protection
  try
     // Attempt to open the physical disk
     FDrive:=CreateFile(PHYSICAL_DRIVE, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
     // Check handle
     if (FDrive = INVALID_HANDLE_VALUE) then
        // Failure
        result:=GetLastError
     else
     begin
        // Set starting sector
        FStartSector:=lpDriveInfo.dwNTRelativeSector;
        // Set bytes per sector
        FBytesPerSector:=512;
        // Set start pos
        n64StartPos.QuadPart:=FBytesPerSector * FStartSector;
            // Set pointer to the starting NTFS volume sector in the physical drive
            SetFilePointer(FDrive, n64StartPos.LowPart, @n64StartPos.HighPart, FILE_BEGIN);
            // Read the boot sector for the MFT infomation
            if not(Windows.ReadFile(FDrive, lpBootSec, SizeOf(lpBootSec), dwBytes, nil)) then
           // Failure
           result:=GetLastError
        else
        begin
           // Check the buffer for NTFS
           if (StrLComp(@lpBootSec.chOemID, 'NTFS', 4) <> 0) then
              // Invalid drive type
              result:=ERROR_INVALID_DRIVE
           else
           begin
                  // Cluster is the logical entity which is made up of several sectors (a physical entity)
                  FBytesPerCluster:=lpBootSec.bpb.uchSecPerClust * lpBootSec.bpb.wBytesPerSec;
              // Set record size
              FMFTRecordSz:=$01 shl ((-1) * (Byte(lpBootSec.bpb.nClustPerMFTRecord)));
              // Allocate memory for mft record
              FMFTRecord:=AllocMem(FMFTRecordSz);
              // Load the mft table
              result:=LoadMFT(lpBootSec.bpb.n64MFTLogicalClustNum);
           end;
        end;
     end;
  finally
     // Set initialized state
     FInitialized:=(result = ERROR_SUCCESS);
  end;

end;

procedure TNTFSDrive.Cleanup;
begin

  // Resource protection
  try
     // Clear state
     FInitialized:=False;
     FStartSector:=0;
     FBytesPerCluster:=0;
     FBytesPerSector:=0;
     FMFTLen:=0;
     FMFTRecordSz:=0;
     // Close handle if open
     if (FDrive <> INVALID_HANDLE_VALUE) then CloseHandle(FDrive);
     // Deallocate memory
     if Assigned(FMFT) then FreeMem(FMFT);
     if Assigned(FMFTRecord) then FreeMem(FMFTRecord);
  finally
     // Clear state
     FMFT:=nil;
     FMFTRecord:=nil;
     FDrive:=INVALID_HANDLE_VALUE;
  end;

end;

constructor TNTFSDrive.Create;
begin

  // Perform inherited
  inherited Create;

  // Set defaults
  FDrive:=INVALID_HANDLE_VALUE;
  FInitialized:=False;
  FStartSector:=0;
  FBytesPerCluster:=0;
  FBytesPerSector:=0;
  FMFT:=nil;
  FMFTRecord:=nil;
  FMFTLen:=0;
  FMFTRecordSz:=0;

end;

destructor TNTFSDrive.Destroy;
begin

  // Resource protection
  try
     // Perform claenup
     Cleanup;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

//// ScanLogicalDrives /////////////////////////////////////////////////////////
function ScanLogicalDrives(lpDriveInfo: LPDRIVEINFO_ARRAY; var Count: Integer): Integer;
var  lpSector:      Array [0..511] of Char;
     lpPartTbl:     LPPARTITION;
     lpDrive:       DRIVEINFO;
     n64Pos:        LARGE_INTEGER;
     hDrive:        THandle;
         dwPrevRel:     DWORD;
         dwMainPrevRel: DWORD;
     dwBytes:       DWORD;
     dwExtended:    Integer;
     dwCount:       Integer;
     dwIndex:       Integer;
begin

  // Set initial defaults
  FillChar(lpDrive, SizeOf(lpDrive), 0);
  dwCount:=(-1);
  dwIndex:=0;
  dwExtended:=0;
  dwPrevRel:=0;
  dwMainPrevRel:=0;

  // Open the drive
      hDrive:=CreateFile(PHYSICAL_DRIVE, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

  // Check handle
  if (hDrive = INVALID_HANDLE_VALUE) then
     // Failure
     result:=GetLastError
  else
  begin
     // Resource protection
     try
        // Read the first sector
        if not(ReadFile(hDrive, lpSector, 512, dwBytes, nil)) then
           // Failed to read first sector
           result:=GetLastError
        else
        begin
           // Set success state
           result:=ERROR_SUCCESS;
           // Get pointer to partition table
           lpPartTbl:=LPPARTITION(@lpSector[$1BE]);
           // Scan logical partitions
           while (dwIndex < 4) do
           begin
              // Fill in drive info
              lpDrive.wCylinder:=lpPartTbl^.chCylinder;
                     lpDrive.wHead:=lpPartTbl^.chHead;
                     lpDrive.wSector:=lpPartTbl^.chSector;
                     lpDrive.dwNumSectors:=lpPartTbl^.dwNumberSectors;
              // Check for extended partition or boot record
              if ((lpPartTbl^.chType = PART_EXTENDED) or (lpPartTbl^.chType = PART_DOSX13X)) then
              begin
                 lpDrive.wType:=EXTENDED_PART;
                 dwMainPrevRel:=lpPartTbl^.dwRelativeSector;
                           lpDrive.dwNTRelativeSector:=dwMainPrevRel;
              end
              else
              begin
                 lpDrive.wType:=BOOT_RECORD;
                           lpDrive.dwNTRelativeSector:=dwMainPrevRel + lpPartTbl^.dwRelativeSector;
              end;
              // Break if extended part or unknown type
                     if (lpDrive.wType = EXTENDED_PART) or (lpPartTbl^.chType = 0) then break;
              // Update count
              Inc(dwCount);
              // Check for buffer space
              if Assigned(lpDriveInfo) and (dwCount < Count) then
                 // Fill in array index
                 lpDriveInfo^[dwCount]:=lpDrive
              else
                 // Set error result for buffer size
                 result:=ERROR_INSUFFICIENT_BUFFER;
              // Update partition table
              Inc(lpPartTbl);
              // Update indexer
              Inc(dwIndex);
           end;
           // Break if index = 4
           if (dwIndex < 4) then
           begin
              // Scan partition tables
              while (dwExtended < 50) do
              begin
                 // Check drive info
                 if (lpDrive.wType = EXTENDED_PART) then
                 begin
                    // Set relative sector location
                    n64Pos.QuadPart:=lpDrive.dwNTRelativeSector * 512;
                    // Set file pointer
                              if (SetFilePointer(hDrive, n64Pos.LowPart, @n64Pos.HighPart, FILE_BEGIN) = $FFFFFFFF) then
                    begin
                       // Failure
                       result:=GetLastError;
                       // Done processing
                       break;
                    end;
                    // Read from drive
                              if not(ReadFile(hDrive, lpSector, 512, dwBytes, nil)) then
                    begin
                       // Failure
                       result:=GetLastError;
                       // Done processing
                       break;
                    end
                    // Must have read 512 bytes
                    else if (dwBytes <> 512) then
                    begin
                       // Failure
                       result:=ERROR_READ_FAULT;
                       // Done processing
                       break;
                    end;
                    // Access the partition table
                    lpPartTbl:=LPPARTITION(@lpSector[$1BE]);
                    // Set indexer
                    dwIndex:=0;
                    // Scan drive info
                    while (dwIndex < 4) do
                    begin
                       // Fill in drive info
                       lpDrive.wCylinder:=lpPartTbl^.chCylinder;
                              lpDrive.wHead:=lpPartTbl^.chHead;
                              lpDrive.wSector:=lpPartTbl^.chSector;
                              lpDrive.dwNumSectors:=lpPartTbl^.dwNumberSectors;
                       lpDrive.dwRelativeSector:=0;
                       // Check for extended partition or boot record
                       if ((lpPartTbl^.chType = PART_EXTENDED) or (lpPartTbl^.chType = PART_DOSX13X)) then
                       begin
                          lpDrive.wType:=EXTENDED_PART;
                          dwPrevRel:=lpPartTbl^.dwRelativeSector;
                                          lpDrive.dwNTRelativeSector:=dwPrevRel + dwMainPrevRel;
                       end
                       else
                       begin
                          lpDrive.wType:=BOOT_RECORD;
                          lpDrive.dwNTRelativeSector:=dwMainPrevRel + dwPrevRel + lpPartTbl^.dwRelativeSector;
                       end;
                       // Break if extended part or unknown type
                              if (lpDrive.wType = EXTENDED_PART) or (lpPartTbl^.chType = 0) then break;
                       // Update count
                       Inc(dwCount);
                       // Check for buffer space
                       if Assigned(lpDriveInfo) and (dwCount < Count) then
                          // Fill in array index
                          lpDriveInfo^[dwCount]:=lpDrive
                       else
                          // Set error result for buffer size
                          result:=ERROR_INSUFFICIENT_BUFFER;
                       // Update partition table
                       Inc(lpPartTbl);
                       // Update indexer
                       Inc(dwIndex);
                    end;
                    // Break if index = 4
                    if (dwIndex = 4) then break;
                 end;
                 // Update extended counter
                 Inc(dwExtended);
              end;
           end;
        end;
     finally
        // Close the drive handle
        CloseHandle(hDrive);
     end;
     // Set number of entries processed
     Count:=Succ(dwCount);
  end;

end;

end.

-- example usage --
var  lpDrives:      Array [0..63] of DRIVEINFO;
     stFileInfo:    ST_FILEINFO;
     strmFile:      TFileStream;
     dwIndex:       Integer;
     dwUndelete:    Integer;
begin

  // Max size of array index
  dwIndex:=64;
  // Only want the first drive
  if (ScanLogicalDrives(@lpDrives, dwIndex) = ERROR_SUCCESS) then
  begin
     // Create ntfs drive handler
     with TNTFSDrive.Create do
     begin
        // Initialize; this takes a few seconds because it loads the full MFT
        Initialize(lpDrives[0]);
        // Set starting index to skip the ($filenames)
        dwIndex:=30;
        // Create directory
        CreateDirectory('c:\undelete files', nil);
        // Set undelete count
        dwUndelete:=0;
        // While true
        while True do
        begin
           // Get file details
           if not(GetFileDetail(dwIndex, stFileInfo) = ERROR_SUCCESS) then break;
           // Check deleted
           if stFileInfo.bDeleted then
           begin
              try
                 // Create file stream
                 strmFile:=TFileStream.Create('c:\undelete files\' + stFileInfo.szFileName, fmCreate);
                 // Resource protection
                 try
                    // Read file into stream
                    ReadFile(dwIndex, strmFile);
                 finally
                    // Free file stream
                    strmFile.Free;
                 end;
                 // Increment count
                 Inc(dwUndelete);
                 // Break after undeleting up to 20 files
                 if (dwUndelete > 20) then break;
              except
                 // Ignore exception
              end;
           end;
           // Check for deleted
           Inc(dwIndex);
        end;
     end;
  end;

end;

0
 
CodedKCommented:
Hi Russell. I'm glad to see you :)
0
 
Russell LibbySoftware Engineer, Advisory Commented:
@CodedK

Thanks, its good to be seen. Lots of new people in this TA, most don't even know me...

Russell
0
 
andreamattAuthor Commented:
Hi,
Thanks. I look this code in this days. But for the FAT or FAT32?
My request is:
"I am only interested in knowing the list of files that can be recovered completely or partially in a drive"
REMEMBER: the list of file!


Andrea
0
 
Russell LibbySoftware Engineer, Advisory Commented:
My reading skills are fine, but you have completely failed to mention if you are trying to recover files on a FAT or NTFS based file system. I gave you code for NTFS, which is obciously not what you want. So good luck with your question.

Russell
0
 
prasidduttaCommented:
CodedK: I need the source code also. If poosible send an email at dev[at]thebat.net
I would really glad.
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 9
  • 5
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now