Quantcast
Channel: How to invoke NtSetInformationFile (w/ FILE_LINK_INFORMATION) in c# - Stack Overflow
Viewing all articles
Browse latest Browse all 2

How to invoke NtSetInformationFile (w/ FILE_LINK_INFORMATION) in c#

$
0
0

The following is an attempt to reproduce the CreateHardLink functionality as described here.

The reason I even need to do this is because this is the only way that I know I'll have the necessary permissions (this code is running in .Net, in WinPE and has asserted the necessary privileges for restore). In particular, I'm using the BackupSemantics flag and the SE_RESTORE_NAME privilege. The normal pInvoke mechanism to CreateHardLink has no provisions for a restore program to use BackupSemantics...and there are scads of files my account doesn't have "normal" access to - hence, this mess.

 unsafe bool CreateLink( string linkName, string existingFileName )
 {
   var access = 
     NativeMethods.EFileAccess.AccessSystemSecurity |
     NativeMethods.EFileAccess.WriteAttributes |
     NativeMethods.EFileAccess.Synchronize;

   var disp = NativeMethods.ECreationDisposition.OpenExisting;

   var flags = 
     NativeMethods.EFileAttributes.BackupSemantics |
     NativeMethods.EFileAttributes.OpenReparsePoint;

   var share = 
     FileShare.ReadWrite | 
     FileShare.Delete;

   var handle = NativeMethods.CreateFile
   ( 
     existingFileName, 
     access, 
     ( uint ) share, 
     IntPtr.Zero, 
     ( uint ) disp, 
     ( uint ) flags, 
     IntPtr.Zero 
   );

   if ( !handle.IsInvalid )
   {
     var mem = Marshal.AllocHGlobal( 1024 );
     try
     {
       var linkInfo = new NativeMethods.FILE_LINK_INFORMATION( );
       var ioStatus = new NativeMethods.IO_STATUS_BLOCK( );
       linkInfo.replaceIfExisting = false;
       linkInfo.directoryHandle = IntPtr.Zero;
       linkInfo.fileName = linkName;
       linkInfo.fileNameLength = ( uint )
         Encoding
         .Unicode
         .GetByteCount( linkInfo.fileName );

       Marshal.StructureToPtr( linkInfo, mem, true );
       var result = NativeMethods.NtSetInformationFile
       ( 
         handle.DangerousGetHandle( ), 
         ref ioStatus, 
         mem.ToPointer( ), 
         1024,
         NativeMethods.FILE_INFORMATION_CLASS.FileLinkInformation 
       );

       return result == 0;
     }
     finally
     {
       Marshal.FreeHGlobal( mem );
     }
   }
   return false;
 }

I keep getting a result from NtSetInformationFile that says I have specified an invalid parameter to a system function. (Result=0xC000000D). I'm unsure about how I've declared the structures - as one of 'em has a file name's length is followed by the "first character" of the name. It's documented here.

Here's how I've declared the structures - and the import. This is just best-guess stuff, as I've not found anyone who's declared this in c# (pinvoke.net and other places) I've messed with a number of permutations...all with the exact same error:

[StructLayout( LayoutKind.Sequential, Pack = 4 )]
internal struct FILE_LINK_INFORMATION
{
  [MarshalAs( UnmanagedType.Bool )]
  public bool replaceIfExisting;
  public IntPtr directoryHandle;
  public uint fileNameLength;
  [MarshalAs( UnmanagedType.ByValTStr, SizeConst = MAX_PATH )]
  public string fileName;
}

internal struct IO_STATUS_BLOCK
{
  uint status;
  ulong information;
}

[DllImport( "ntdll.dll", CharSet = CharSet.Unicode )]
unsafe internal static extern uint NtSetInformationFile
( 
  IntPtr fileHandle, 
  ref IO_STATUS_BLOCK IoStatusBlock, 
  void* infoBlock, 
  uint length, 
  FILE_INFORMATION_CLASS fileInformation 
);

Any light you can shed on the dumb thing I've done would be most appreciated.

EDIT:

At the risk of drawing more downvotes, I'll explain the context, without which, might have had some believing I was looking for a hack. It's a selective backup/restore program that lives in the midst of state-management software - mostly kiosks and POS terminals and library computers. Backup and restore operations happen in a pre-boot environment (WinPE).

What ended up working, with respect to using the function was the need to change the structure FILE_LINK_INFORMATION and a twist in the file naming. First, the working FILE_LINK_INFORMATION needs to go like this:

[StructLayout( LayoutKind.Sequential, CharSet = CharSet.Unicode )]
internal struct FILE_LINK_INFORMATION
{
  [MarshalAs( UnmanagedType.U1 )]
  public bool ReplaceIfExists;
  public IntPtr RootDirectory;
  public uint FileNameLength;
  [MarshalAs( UnmanagedType.ByValTStr, SizeConst = MAX_PATH )]
  public string FileName;
}

As Harry Johnston mentioned, the Pack=4 was wrong - and the marshalling of the bool needed to be a little different. The MAX_PATH is 260.

Then, when calling NtSetInformationFile in the context of a file that is opened with Read,Write, and Delete access and sharing:

unsafe bool CreateLink( DirectoryEntry linkEntry, DirectoryEntry existingEntry, SafeFileHandle existingFileHandle )
{
  var statusBlock = new NativeMethods.IO_STATUS_BLOCK( );
  var linkInfo = new NativeMethods.FILE_LINK_INFORMATION( );
  linkInfo.ReplaceIfExists = true;
  linkInfo.FileName = @"\??\" + storage.VolumeQualifiedName( streamCatalog.FullName( linkEntry ) );
  linkInfo.FileNameLength = ( uint ) linkInfo.FileName.Length * 2;
  var size = Marshal.SizeOf( linkInfo );
  var buffer = Marshal.AllocHGlobal( size );
  try
  {
    Marshal.StructureToPtr( linkInfo, buffer, false );
    var result = NativeMethods.NtSetInformationFile
    (
      existingFileHandle.DangerousGetHandle( ),
      statusBlock,
      buffer,
      ( uint ) size,
      NativeMethods.FILE_INFORMATION_CLASS.FileLinkInformation
    );
    if ( result != 0 )
    {
      Session.Emit( "{0:x8}: {1}\n{2}", result, linkInfo.FileName, streamCatalog.FullName( existingEntry ) );
    }
    return ( result == 0 );
  }
  finally
  {
    Marshal.FreeHGlobal( buffer );
  }
}

Note, in particular, the namespace prefix - didn't work until I added that.

By the way, the DirectoryEntry describes a file that was supposed to be on disk as of the last backup.

With respect to not using CreateHardLink, as the original article describes, there was a vulnerability illustrated using NtSetInformationFile where there caller didn't need any particular permissions to add the link. Bummer! I suspect that when Microsoft closed the hole, they also introduced an issue with CreateHardLink. I will revisit this posting when I know more.


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images