Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I open a directory with CreateFile in C# to examine deleted entries?

Tags:

c#

winapi

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).

like image 785
Fred Avatar asked Aug 27 '09 22:08

Fred


People also ask

How do you open directory?

Type cd folderName to open a folder in your directory. For example, in your User folder you can type cd documents and press ↵ Enter to open your Documents folder.

How can I read a directory in AC program?

To access a directory, use the opendir() function. It's prototyped in the dirent. h header file as: DIR *opendir(const char *filename);

How do you create a directory in C?

This task can be accomplished by using the mkdir() function. Directories are created with this function. (There is also a shell command mkdir which does the same thing). The mkdir() function creates a new, empty directory with name filename.

What is CreateFile?

The CreateFile function can create a new file or open an existing file. You must specify the file name, creation instructions, and other attributes. When an application creates a new file, the operating system adds it to the specified directory.


1 Answers

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.
like image 159
scottm Avatar answered Sep 19 '22 11:09

scottm