2014-12-05 5 views
0

Я пытаюсь закодировать простую функцию чтения NTFS для расширенных атрибутов (а не альтернативных потоков данных!) В C#. Он будет использоваться в некоторых сценариях powershell позже, поэтому мне нужно стричь с C#.Pinvoke NtOpenFile и NtQueryEaFile, чтобы читать расширенные атрибуты NTFS в C#

До сих пор я не собрал некоторые Infos о NtOpenFile:

[StructLayout(LayoutKind.Sequential, Pack = 0)] 
     public struct OBJECT_ATTRIBUTES 
     { 
      public Int32 Length; 
      public IntPtr RootDirectory; 
      public IntPtr ObjectName; 
      public uint Attributes; 
      public IntPtr SecurityDescriptor; 
      public IntPtr SecurityQualityOfService; 

     } 

     [StructLayout(LayoutKind.Sequential, Pack = 0)] 
     public struct IO_STATUS_BLOCK 
     { 
      public uint status; 
      public IntPtr information; 
     } 

     [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)] 
     public static extern int NtOpenFile(
      out IntPtr handle, 
      System.IO.FileAccess access, 
      ref OBJECT_ATTRIBUTES objectAttributes, 
      out IO_STATUS_BLOCK ioStatus, 
      System.IO.FileShare share, 
      uint openOptions 
      ); 

, но до сих пор нет Infos на NtQueryEaFile и нет демо-кода, чтобы вызвать его и Маршалл своих результаты, спасибо за вашу помощь!

EDIT1 Получение немного дальше с этим новым кодом, но все еще застряли в какой-то момент с «Access Denied» после вызова NtQueryEaFile. Есть идеи ?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 
using System.Reflection; 
using System.IO; 
using System.ComponentModel; 
using HANDLE = System.IntPtr; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     [StructLayout(LayoutKind.Sequential, Pack = 0)] 
     public struct OBJECT_ATTRIBUTES 
     { 
      public Int32 Length; 
      public IntPtr RootDirectory; 
      public IntPtr ObjectName; 
      public uint Attributes; 
      public IntPtr SecurityDescriptor; 
      public IntPtr SecurityQualityOfService; 

     } 

     [StructLayout(LayoutKind.Sequential, Pack = 0)] 
     public struct IO_STATUS_BLOCK 
     { 
      public uint status; 
      public IntPtr information; 
     } 

     [StructLayout(LayoutKind.Sequential, Pack = 0)] 
     public struct UNICODE_STRING 
     { 
      public ushort Length; 
      public ushort MaximumLength; 
      public IntPtr Buffer; 

     } 


     [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)] 
     public static extern uint NtOpenFile(
      out IntPtr handle, 
      System.IO.FileAccess access, 
      ref OBJECT_ATTRIBUTES objectAttributes, 
      out IO_STATUS_BLOCK ioStatus, 
      System.IO.FileShare share, 
      uint openOptions 
      ); 


     [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)] 
     public static extern uint NtQueryEaFile(
      IntPtr handle, 
      out IO_STATUS_BLOCK ioStatus, 
      out IntPtr buffer, 
      uint length, 
      bool retSingleEntry, 
      IntPtr eaList, 
      uint eaListLength, 
      IntPtr eaIndex, 
      bool restartScan 
      ); 

     [DllImport("ntdll.dll")] 
     public static extern void RtlInitUnicodeString(
      out UNICODE_STRING DestinationString, 
      [MarshalAs(UnmanagedType.LPWStr)] string SourceString); 

     [DllImport("ntdll.dll")] 
     public static extern uint RtlNtStatusToDosError(uint Status); 

     [DllImport("kernel32.dll")] 
     public static extern uint FormatMessage(int dwFlags, IntPtr lpSource, uint dwMessageId, 
      int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr Arguments); 

     static void Main(string[] args) 
     { 
      UInt32 FILE_OPEN = 0x1; 
      UInt32 OBJ_CASE_INSENSITIVE = 0x40; 
      UInt32 FILE_READ_EA = 8; 
      UInt32 FILE_RANDOM_ACCESS = 0x00000800; 
      UInt32 FILE_DIRECTORY_FILE = 0x00000002; 
      UInt32 FILE_NON_DIRECTORY_FILE = 0x00000040; 
      UInt32 FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000; 
      uint NT_SUCCESS = 0x0; 
      bool restartScan = false; 
      bool returnSingleEntry = false; 

      IntPtr _RootHandle; //This will need to be initialized with the root handle, can use CreateFile from kernel32.dll 
      _RootHandle = IntPtr.Zero; 

      UNICODE_STRING unicodeString; 
      RtlInitUnicodeString(out unicodeString, @"\??\C:\temp"); 
      IntPtr unicodeIntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(unicodeString)); 
      Marshal.StructureToPtr(unicodeString, unicodeIntPtr, false); 

      OBJECT_ATTRIBUTES objAttributes = new OBJECT_ATTRIBUTES(); 
      IO_STATUS_BLOCK ioStatusBlock = new IO_STATUS_BLOCK(); 
      //Microsoft.Win32.SafeHandles.SafeFileHandle hFile; 
      HANDLE hFile; 


      objAttributes.Length = System.Convert.ToInt32(Marshal.SizeOf(objAttributes)); 
      objAttributes.ObjectName = unicodeIntPtr; 
      objAttributes.RootDirectory = _RootHandle; 
      objAttributes.Attributes = OBJ_CASE_INSENSITIVE; 
      objAttributes.SecurityDescriptor = IntPtr.Zero; 
      objAttributes.SecurityQualityOfService = IntPtr.Zero; 

      uint status = NtOpenFile(out hFile, FileAccess.Read, ref objAttributes, out ioStatusBlock, FileShare.Read, FILE_DIRECTORY_FILE | FILE_READ_EA | FILE_OPEN_FOR_BACKUP_INTENT); 
      if (status != NT_SUCCESS) 
       ExitWithError(status);   

      IntPtr buffer = Marshal.AllocHGlobal(65535); 
      status = NtQueryEaFile(hFile, out ioStatusBlock, out buffer, System.Convert.ToUInt32(Marshal.SizeOf(buffer)), returnSingleEntry, IntPtr.Zero, 0, IntPtr.Zero, restartScan); 
      if (status != NT_SUCCESS) 
       ExitWithError(status); 

     } 

     public static void ExitWithError(uint errorCode) 
     { 
      Console.WriteLine(GetSystemMessage(RtlNtStatusToDosError(errorCode))); 
      Environment.Exit(1); 
     } 

     public static string GetSystemMessage(uint errorCode) 
     { 
      int capacity = 512; 
      int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 
      StringBuilder sb = new StringBuilder(capacity); 
      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, errorCode, 0, 
       sb, sb.Capacity, IntPtr.Zero); 
      int i = sb.Length; 
      if (i > 0 && sb[i - 1] == 10) i--; 
      if (i > 0 && sb[i - 1] == 13) i--; 
      sb.Length = i; 
      return sb.ToString(); 
     } 

    } 
} 

EDIT2 Рассмотрев этот код: http://jbutera.net/mirror/git/alexpux/Cygwin/winsup/cygwin/ntea.cc я модифицирована мину со следующим:

NtOpenFile Сейчас:

[DllImport("ntdll.dll", ExactSpelling = true)] 
     public static extern uint NtOpenFile(
      out SafeFileHandle handle, 
      UInt32 access, 
      ref OBJECT_ATTRIBUTES objectAttributes, 
      out IO_STATUS_BLOCK ioStatus, 
      System.IO.FileShare share, 
      uint openOptions 
      ); 

NtQueryEaFile Сейчас:

[DllImport("ntdll.dll", ExactSpelling = true)] 
public static extern uint NtQueryEaFile(
    SafeFileHandle handle, 
    out IO_STATUS_BLOCK ioStatus, 
    out IntPtr buffer, 
    uint length, 
    bool retSingleEntry, 
    IntPtr eaList, 
    uint eaListLength, 
    IntPtr eaIndex, 
    bool restartScan 
    ); 

Вызов NtOpenFile в настоящее время:

UInt32 FILE_OPEN = 0x1; 
      UInt32 OBJ_CASE_INSENSITIVE = 0x40; 
      UInt32 FILE_READ_EA = 8; 
      UInt32 FILE_RANDOM_ACCESS = 0x00000800; 
      UInt32 FILE_DIRECTORY_FILE = 0x00000002; 
      UInt32 FILE_NON_DIRECTORY_FILE = 0x00000040; 
      UInt32 FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000; 
      UInt32 READ_CONTROL = 0x00020000; 
      const UInt32 STATUS_NO_EAS_ON_FILE = 0xC0000052; 

      uint status = NtOpenFile(out hFile, READ_CONTROL | FILE_READ_EA, ref objAttributes, out ioStatusBlock, FileShare.ReadWrite | FileShare.Delete, FILE_OPEN_FOR_BACKUP_INTENT); 
      if (status != NT_SUCCESS) 
       ExitWithError(status);   

      IntPtr buffer = Marshal.AllocHGlobal(65535); 

      // status = NtQueryEaFile(fileHandle, &ioStatus, qbuf, sizeof(FILE_FULL_EA_INFORMATION), TRUE, NULL, 0, &QueryEAIndex, FALSE); 
      status = NtQueryEaFile(hFile, out ioStatusBlock,out buffer, System.Convert.ToUInt32(Marshal.SizeOf(buffer)), returnSingleEntry, IntPtr.Zero, 0, IntPtr.Zero, restartScan); 
      switch (status) 
      { 
       case STATUS_NO_EAS_ON_FILE: 
        Console.WriteLine("No EAs found"); 
        break; 

       case NT_SUCCESS: 
        Console.WriteLine("EAs found !"); 
        break; 

       default: 
        ExitWithError(status); 
        break; 
      }      

И угадайте, что? Работает ! Ну .. Почти ... Чтение каталога без EAs правильно выбрасывает STATUS_NO_EAS_ON_FILE. Но чтение каталога с ЭА бросает STATUS_BUFFER_TOO_SMALL (согласно http://msdn.microsoft.com/fr-fr/library/cc704588.aspx), так что я на самом деле есть некоторые PBR с моим буферном определение/распределение: -/

+0

Мой совет получить работающий C++ код и начать оттуда. Тогда у вас есть что-то известное, чтобы работать, и все, что остается, это вмешательство. Зачем использовать Pack = 0? Что заставило вас использовать SetLastError = true? –

+0

Я могу сказать вам, что второй буфер param не так. Это не параметр «out». Это буфер, который вы выделяете. Поэтому '[Out] byte []' будет хорошим вариантом. В любом случае, поскольку это недокументированная функция, нам трудно понять, что правильно или неправильно. Вы уверены, что вам нужно использовать недокументированную apis? –

+0

Для использования Pack = 0 (значение по умолчанию равно 0 в любом случае afaik) и SetLastError, оно исходит из этого примера https://easyhook.codeplex.com/discussions/468770 – Usul

ответ

0

Документация для NtOpenFile доступна на MSDN. Для NtQueryEaFile, я думаю, вы могли бы просто обратиться к процедуре ZwQueryEaFile в наборе драйверов для Windows (see on MSDN). Большинство подпрограмм Zw... реплицируют собственные вызовы API NT (Nt...), поэтому подпись должна быть одинаковой.

P/Invoke для NtQueryEaFile (от ZwQueryEaFile):

[DllImport("ntdll.dll", CharSet = CharSet.Unicode)] 
public static extern UInt32 NtQueryEaFile(
    [In] SafeFileHandle FileHandle, 
    [Out] out IO_STATUS_BLOCK IoStatusBlock, 
    [Out] out IntPtr Buffer, 
    [In] UInt32 Length, 
    [In] Boolean ReturnSingleEntry, 
    [In, Optional] IntPtr EaList, 
    [In] UInt32 EaListLength, 
    [In, Optional] UInt32 EaIndex, 
    [In] Boolean RestartScan 
);