Как открыть каталог с CreateFile в C #, чтобы проверить удаленные записи? - PullRequest
5 голосов
/ 28 августа 2009

Как открыть каталог с CreateFile в C #, чтобы проверить записи удаленных файлов? Или это сейчас невозможно? Я помню, как когда-то назад была возможность открыть каталог в разделе NTFS с помощью CreateFile или, возможно, CreateFileEx, но это было с использованием C ++ под более старой ОС.

Пока у меня достаточно вызовов Windows API (к kernel32.dll), чтобы прочитать существующий файл, но он не откроет каталог:

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);
    }
}

Редактировать 1: изменил его, чтобы использовать OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS и GENERIC_READ.

Это откроет и отобразит начало указанного текстового файла, как и оригинал при запуске от имени пользователя с правами администратора Vista, но по-прежнему не удается открыть каталог. Я предполагаю, что мне нужны привилегии SE_BACKUP_NAME и SE_RESTORE_NAME, но я не уверен, как указать их, кроме как записать это как службу, которая работает как Локальная машина (что-то, у меня есть только самое туманное представление о том, как это сделать).

Ответы [ 3 ]

7 голосов
/ 31 августа 2009

AFAIK, это довольно сложный процесс. Вы не можете просто использовать CreateFile и перечислить «удаленные файлы». Вы должны загрузить таблицу основных файлов привода и перечислить ее для файлов, помеченных как удаленные, а затем попытаться загрузить данные с позиции диска, указанной в MFT. Это потребует большого количества кода, вызванного платформой, и, возможно, нескольких переопределений собственных структур данных в C #.

Краткий ответ на ваш вопрос:

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

Вы используете файл create, чтобы открыть сам диск.

Вот действительно хорошая статья обо всем процессе в Code Project. Но это все на c ++. Код есть, и кажется, что вы знаете, как выполнить p \ invoke, поэтому перенос его не должен быть проблемой.

Изменить:

Тот факт, что диск является внешним, не должен усложнять его, вы все равно можете открыть диск так, как я показал (возможно, используйте WMI tool , чтобы найти путь после подключения диска ). Затем вы можете использовать информацию на странице Википедии для FAT32 , чтобы определить структуры данных, в которые вы можете читать MFT и другие части файловой системы. Как только вы туда доберетесь, вы просто перебираете 32-байтовые определения файлов в таблице каталогов, ища первый байт для:

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.
2 голосов
/ 28 августа 2009

Я не уверен, что вы имеете в виду, изучая удаленные каталоги, но вы сможете получить указатель на каталог, передав флаг FILE_FLAG_BACKUP_SEMANTICS в CreateFile и , убедившись, что укажите OPEN_EXISTING для создания распоряжения. Из статьи MSDN о CreateFile :

Чтобы открыть каталог с помощью CreateFile, укажите FILE_FLAG_BACKUP_SEMANTICS помечать как часть dwFlagsAndAttributes. Соответствующие проверки безопасности еще применять, когда этот флаг используется без SE_BACKUP_NAME и SE_RESTORE_NAME привилегии.

Похоже, вы уже пробовали это, но прокомментировали? Если это не работает для вас, возможно, вы захотите убедиться, что у пользователя, с которым вы работаете, есть разрешение на доступ к рассматриваемому каталогу.

0 голосов
/ 06 сентября 2009

Просто чтобы предложить вам другой подход, в случае его актуальности, вы всегда можете просто посмотреть каталог с помощью FileSystemWatcher и перехватить событие Deleted. Конечно, вам нужно будет наблюдать за ним во время удаления, но это может оказаться гораздо более простым решением, чем попытаться восстановить его (если это возможно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...