1

以下は、ここで説明されている CreateHardLink 機能を再現する試みです。

これを行う必要がある理由は、これが必要なアクセス許可を持っていることがわかっている唯一の方法だからです (このコードは .Net と WinPE で実行されており、復元に必要な特権をアサートしています)。特に、BackupSemantics フラグと SE_RESTORE_NAME 権限を使用しています。CreateHardLink への通常の pInvoke メカニズムには、復元プログラムが BackupSemantics を使用するための規定がありません...そして、私のアカウントが「通常の」アクセス権を持っていないファイルの群れがあります - したがって、この混乱.

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

NtSetInformationFile から、システム関数に無効なパラメータを指定したという結果が返ってきます。(結果 = 0xC000000D)。構造体をどのように宣言したかわかりません.1つにはファイル名の長さがあり、その後に名前の「最初の文字」が続きます。ここに文書化されています。

構造体とインポートを宣言した方法は次のとおりです。これは、c#(pinvoke.netおよびその他の場所)でこれを宣言した人を見つけられなかったため、推測に過ぎません。多くの順列を台無しにしました...すべてまったく同じエラーが発生しました:

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

私が行ったばかげたことにあなたが当てることができる光は、最も高く評価されます.

編集:

より多くの反対票を集める危険を冒して、コンテキストを説明します。これがなければ、ハックを探していると信じていたかもしれません. これは、状態管理ソフトウェア (主にキオスクや POS 端末、図書館のコンピューター) の中にある、選択的なバックアップ/復元プログラムです。バックアップおよび復元操作は、起動前環境 (WinPE) で行われます。

関数の使用に関して、最終的に機能したのは、構造を変更する必要がありFILE_LINK_INFORMATION、ファイルの命名にひねりがあったことです。まず、作業は次のFILE_LINK_INFORMATIONようにする必要があります。

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

ハリー・ジョンストンが述べたように、Pack=4 は間違っていました - そして bool のマーシャリングは少し違う必要がありました。はMAX_PATH260です。

NtSetInformationFile次に、読み取り、書き込み、および削除アクセスと共有で開かれたファイルのコンテキストで呼び出す場合:

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

特に、名前空間プレフィックスに注意してください-追加するまで機能しませんでした。

ちなみに、DirectoryEntryは最後のバックアップの時点でディスク上にあるはずだったファイルを表しています。

を使用しないことに関してはCreateHardLink、元の記事で説明されているように、NtSetInformationFile呼び出し元がリンクを追加するために特定のアクセス許可を必要としない場所を使用して示されている脆弱性がありました。残念!Microsoft が穴を塞いだときに、CreateHardLink. 詳細がわかり次第、この投稿を再訪します。

4

1 に答える 1

1

最後の手段以外でカーネル API を使用することはお勧めしませんが、差し迫った問題は、FILE_LINK_INFO構造を正しくパックしていないことだと思います。

4 バイトのパッキングを指定しました。ドキュメントによるとdirectoryHandle、オフセットは 4 になります。ただし、おそらく 64 ビット システムで実行しているため、正しいオフセットは 8 です。

これを修正する方法は定かではありませんが、デフォルトのパッキング ルールを使用する必要があると考えられます。つまり、何も指定Packしないでください。(8 バイトのパッキングを指定した場合FileName、オフセット 20 にあるはずのときに、おそらくオフセット 24 に配置されることに注意してください。)

于 2016-11-01T00:16:46.820 に答える