tags:

views:

1245

answers:

3

How do I open a directory with CreateFile in C# to examine entries of deleted files? Or is it now impossible? I remember way back when being able to open a directory on an NTFS partition using CreateFile or possibly CreateFileEx, but that was using C++ under an older OS.

So far I've got the Windows API calls (to kernel32.dll) working enough to read an existing file but it won't open a directory:

using System;
using System.Collections.Generic;
using System.Text;

using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Runtime.ConstrainedExecution;
using System.Security;

namespace Kernel_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Kernel_Tools cKT = new Kernel_Tools();

            cKT.DoTest("C:\\Temp");
            cKT.DoTest("C:\\Temp\\test.txt");
        }
    }

    [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
    [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
    class Kernel_Tools
    {
        public void DoTest(string cTarget)
        {
            IntPtr cFile = NativeMethods.CreateFile(
                cTarget,
                NativeMethods.GENERIC_READ /* 0 or NativeMethods.GENERIC_READ */ ,
                FileShare.Read,
                IntPtr.Zero /* failed try: NativeMethods.OPEN_ALWAYS */,
                (FileMode) NativeMethods.OPEN_EXISTING,
                NativeMethods.FILE_FLAG_BACKUP_SEMANTICS /* 0 */ ,
                IntPtr.Zero);

            Console.WriteLine(cTarget);
            Console.WriteLine(cFile);

            if ((int)cFile != -1)
            {
                int length = 20;

                byte[] bytes = new byte[length];
                int numRead = 0;

                int ErrorCheck = NativeMethods.ReadFile(cFile, bytes, length, out numRead, IntPtr.Zero);
                // This sample code will not work for all files.
                //int r = NativeMethods.ReadFile(_handle, bytes, length, out numRead, IntPtr.Zero);
                // Since we removed MyFileReader's finalizer, we no longer need to
                // call GC.KeepAlive here.  Platform invoke will keep the SafeHandle
                // instance alive for the duration of the call.
                if (ErrorCheck == 0)
                {
                    Console.WriteLine("Read failed.");
                    NativeMethods.CloseHandle(cFile);
                    return;
                    //throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                if (numRead < length)
                {
                    byte[] newBytes = new byte[numRead];
                    Array.Copy(bytes, newBytes, numRead);
                    bytes = newBytes;
                }

                for (int i = 0; i < bytes.Length; i++)
                    Console.Write((char)bytes[i]);

                Console.Write("\n\r");

                //    Console.WriteLine();
                NativeMethods.CloseHandle(cFile);
            }
        }
    }

    [SuppressUnmanagedCodeSecurity()]
    internal static class NativeMethods
    {
        // Win32 constants for accessing files.
        internal const int GENERIC_READ = unchecked((int)0x80000000);

        internal const int FILE_FLAG_BACKUP_SEMANTICS = unchecked((int)0x02000000);

        internal const int OPEN_EXISTING = unchecked((int)3);

        // Allocate a file object in the kernel, then return a handle to it.
        [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
        internal extern static IntPtr CreateFile(
           String fileName,
           int dwDesiredAccess,
           System.IO.FileShare dwShareMode,
           IntPtr securityAttrs_MustBeZero,
           System.IO.FileMode dwCreationDisposition,
           int dwFlagsAndAttributes,
           IntPtr hTemplateFile_MustBeZero);

        // Use the file handle.
        [DllImport("kernel32", SetLastError = true)]
        internal extern static int ReadFile(
           IntPtr handle,
           byte[] bytes,
           int numBytesToRead,
           out int numBytesRead,
           IntPtr overlapped_MustBeZero);

        // Free the kernel's file object (close the file).
        [DllImport("kernel32", SetLastError = true)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        internal extern static bool CloseHandle(IntPtr handle);
    }
}

Edit 1: Modified it to use OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, and GENERIC_READ.

This will open and display the start of a the specified text file as did the original when run as a Vista administrative user, but it still fails to open the directory. I'm guessing I need the SE_BACKUP_NAME and SE_RESTORE_NAME privileges but am unsure how to specify those other than to write this as a service that runs as Local Machine (something I have only the foggiest idea of how to do).

+1  A: 

I'm not sure what you mean by examining deleted directories, but you should be able to get a handle to a directory by passing the FILE_FLAG_BACKUP_SEMANTICS flag into CreateFile and by making sure to specify OPEN_EXISTING for the creation disposition. From the MSDN article on CreateFile:

To open a directory using CreateFile, specify the FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFlagsAndAttributes. Appropriate security checks still apply when this flag is used without SE_BACKUP_NAME and SE_RESTORE_NAME privileges.

It looks like you've previously tried some of this but commented it out? If this doesn't work for you, you might want to make sure that they the user you're running as has permission to acess the directory in question.

Reuben
I made about 7 or 8 different tests and commented out parts when I wasn't using them so I wouldn't have to start over if I later learned I was partially correct earlier on.
Fred
+7  A: 

AFAIK, it's a fairly involved process. You can't just use CreateFile and enumerate the "deleted files". You have to load up the master file table of the drive, and enumerate that for files marked deleted, and then try to load the data from the disk position listed in the MFT. This would require a lot of Platform Invoked code, and probably a few redefinitions of native data structures in C#.

The short answer to your question is this:

CreateFile("\\\\.\\PhysicalDrive0",
            GENERIC_READ,
            FILE_SHARE_READ|FILE_SHARE_WRITE,
            0,
            OPEN_EXISTING,
            0,
            NULL)

You use create file to open the disk itself.

Here is a really good article about the whole process on Code Project. But, it's all in c++. The code is there, and it seems you know how to p\invoke, so porting it over shouldn't be a problem.

Edit:

The fact that drive is external shouldn't make it any harder, you can still open the disk the same way I showed (maybe use a WMI tool to look up the path once the drive is connected). Then, you can use the information on the Wikipedia page for FAT32 to define data structures that you can read the MFT and other pieces of the file system into. Once you get there, you just iterate through the 32 byte file definitions in the directory table looking at the first byte for:

0xE5    Entry has been previously erased and is available. File undelete utilities must replace this character with a regular character as part of the undeletion process.
scottm
I'm actually more familiar with C++ than C# so I'll probably just use Visual C++. I'm working with C# to learn the language.
Fred
The hard part is that the lost files are on a FAT32 external hard drive.
Fred
Unless this is an educational project, if you're just trying to "undelete" some files from a FAT file system you're probably better off looking for an existing tool that can take care of it for you...
Reuben
A: 

Just to throw another approach at you, in case it's relevant, you could always just watch the directory with a FileSystemWatcher and catch the Deleted event. Of course, you'll need to be watching it at the time of deletion, but it may be a far easier solution then trying to recover it (if it's an option).

Noon Silk
The old backup was actually deleted several years ago. Thanks for the idea though.
Fred