Операция переименования файла в фильте файловых систем

Тема в разделе "WASM.NT.KERNEL", создана пользователем Sheph, 26 янв 2008.

  1. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    Всем доброго времени суток, пишу драйвер фильтра файловых систем, возникла необходимость отлавливать переименование файлов, ловлю всё как написано в DDK: IRP_MJ_SET_INFORMATION и получаю структуру FILE_RENAME_INFORMATION, а в ней FileName всегда имеет вид:
    \??\C:\...
    Ну, или любая другая буква вместо C.
    Вопрос, что такое "\??\" ? Это тоже самое что и "\DosDevices" ?
    И ещё, почему-то из всех типов переименований в винде (их всего 3) у меня всегда вылазит Fully Qualified Rename даже тогда когда я делаю Simple Rename, вопрос, как заставить винду переименовывать по типу Simple Rename или Relative Rename ? Хочется протестировать весь код даже и тот который выполняется крайне редко...
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Это в NT-формате получается через RtlDosPathNameToNtPathName_U()
     
  3. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    Clerk, а как превратить этот путь опять в DosName ? Функции RtlNtPathNameToDosPathName_U() нету. Руками ?
    Если да, то какие ещё префиксы возможны кроме \??\ и \DosDevices\ ?
     
  4. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Да. В Windows XP есть каталог Global?? с глобальными объектами. для совместимости оставлена ссылка \?? (он так раньше назывался) и \DosDevices.
     
  5. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    Great, а, так это "\Global??", теперь ясно. Тогда такой вопрос, выходит что в качестве префикса имени может выступать вообще любой каталог ? Как же мне тогда действовать, у меня такая задача получить нормальное человеческое имя файла из поля FileName структуры FILE_RENAME_INFORMATION, что мне делать ? Пока я понимаю так:
    1. Проверить начало строки на соотвествие с "\??\", "\Global??\" и "\DosDevice\", если одна из этих строк есть, то далее следует полное имя файла
    2. Если нет, то проверяем начало строки на соотвествие шаблону "[Drive Letter]:", если соответсвует, то это полный путь
    3. В противном случае в FileName какая-то ерунда (Рассматривается пока-что только Fully Qualified Rename) и что делать не понятно
     
  6. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    что значит "человеческое имя"? С точки зрения ядра, путь \??\C:\boot.ini, к примеру, вполне нормальный.
    Нет, может быть много символьных ссылок. Слышал про subst и DefineDosDevice?
    Так вот, путь к файлу может выглядеть в виде \??\mysuperdrive\directory\file.txt, при учете, что создана ссылка mysuperdrive на C:
    Единственный уникальный путь, который файл может иметь в OS Windows NT-based, это \Device\HarddiskVolume2\windows\explorer.exe - то есть путь к устройству диска относительно корня каталога объектов (\Device\HarddiskVolume2 это DEVICE_OBJECT партиции на диске), а дальше - относительный путь (относительно корня диска).
    Поэтому все ссылки нужно приводить к этому пути, могу даже дать функцию, которая это делает. Сам писал, аж полчаса убил)) Она разрешает все символьные ссылки в пути к файлу:

    Код (Text):
    1. BOOLEAN
    2. QuerySymbolicLink(
    3.     IN PUNICODE_STRING LinkName,
    4.     OUT PUNICODE_STRING Destination,
    5.     OUT PULONG Returned
    6.     )
    7. {
    8.     HANDLE hLink;
    9.     NTSTATUS Status;
    10.     OBJECT_ATTRIBUTES Oa;
    11.     ULONG RetLength;
    12.  
    13.     InitializeObjectAttributes( &Oa, LinkName, OBJ_KERNEL_HANDLE, 0, 0 );
    14.  
    15.     Status = ZwOpenSymbolicLinkObject( &hLink,
    16.         GENERIC_READ,
    17.         &Oa );
    18.  
    19.     if( !NT_SUCCESS(Status) )
    20.     {
    21.         return FALSE;
    22.     }
    23.  
    24.     Status = ZwQuerySymbolicLinkObject( hLink,
    25.         Destination,
    26.         &RetLength );
    27.  
    28.     if( !NT_SUCCESS(Status) )
    29.     {
    30.         ZwClose( hLink );
    31.         return FALSE;
    32.     }
    33.  
    34.     if( Returned )
    35.         *Returned = RetLength;
    36.  
    37.     ZwClose( hLink );
    38.     return TRUE;
    39. }
    40.  
    41.  
    42. VOID
    43. ResolveSymLinks(
    44.     IN PUNICODE_STRING InputPath,
    45.     OUT PUNICODE_STRING pOutputPath
    46.     )
    47. {
    48.     UNICODE_STRING OutputPath = {0};
    49.     UNICODE_STRING Queried = {0};
    50.     UNICODE_STRING temp = {0};
    51.  
    52.     for( PWSTR ip = InputPath->Buffer; ip < (InputPath->Buffer + InputPath->Length/2); ip++ )
    53.     {
    54.         if( *ip == L'\\' )
    55.         {
    56.             //
    57.             // Separator. Query symlink for the left part.
    58.             //
    59.  
    60.             ULONG Pos = ((ULONG_PTR)ip-(ULONG_PTR)InputPath->Buffer);
    61.  
    62.             temp = *InputPath;
    63.             temp.Length = (USHORT) Pos;
    64.            
    65.             if( !Queried.Buffer )
    66.             {
    67.                 Queried.MaximumLength = 512;
    68.                 Queried.Buffer = (PWSTR) ExAllocatePool(PagedPool, Queried.MaximumLength);
    69.             }
    70.  
    71.             if( QuerySymbolicLink( &temp, &Queried, NULL ) )
    72.             {
    73.                 //
    74.                 // Symlink okay. Replace.
    75.                 //
    76.  
    77.                 PWSTR Temp = OutputPath.Buffer;
    78.  
    79.                 OutputPath.MaximumLength = (USHORT)(Queried.Length+(InputPath->Length-Pos)+2);
    80.                 OutputPath.Buffer = (PWSTR) ExAllocatePool(PagedPool, OutputPath.MaximumLength);
    81.                 OutputPath.Length = 0;
    82.  
    83.                 RtlAppendUnicodeStringToString( &OutputPath, &Queried );
    84.                 RtlAppendUnicodeToString( &OutputPath, ip );
    85.  
    86.                 if( Temp )
    87.                     ExFreePool( Temp );
    88.  
    89.                 InputPath = &OutputPath;
    90.                 ip = InputPath->Buffer;
    91.                 continue;
    92.             }
    93.         }
    94.     }
    95.  
    96.     *pOutputPath = *InputPath;
    97.  
    98.     if( Queried.Buffer )
    99.         ExFreePool( Queried.Buffer );
    100. }
    ResolveSymLinks возвращает UNICODE_STRING с резолвнутыми символьными ссылками (место выделено в пуле), либо исходную строку (НЕ копию), поэтому перед ExFreePool нужно проверить, что Resolved.Buffer!=Input.Buffer
    Пример разрешения \SystemRoot ниже. Дело в том, SystemRoot -ссылка, которая указывает на другую ссылку, которая уже ссылается на реальный диск.
    (\SystemRoot => \Device\Harddisk0\Partition2 => \Device\HarddiskVolume2)
    Код (Text):
    1.     UNICODE_STRING SystemRootPath, SystemRoot;
    2.     RtlInitUnicodeString( &SystemRootPath, L"\\SystemRoot\\" );
    3.  
    4.     ResolveSymLinks( &SystemRootPath, &SystemRoot );
    5.  
    6.     KdPrint(("SystemRoot is: %S\n", SystemRoot.Buffer));
    7.  
    8.     if( SystemRoot.Buffer != SystemRootPath.Buffer )
    9.         ExFreePool( SystemRoot.Buffer );
    Если указывается только каталог, то заключительный слеш обязятелен, иначе если имя файла - нет.

    Именно это тебе и нужно - имена обоих файлов (входного и шаблонного) нужно преобразовать к "нормальному" пути без символьных ссылок и тогда уже сравнивать.
     
  7. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    Great, спасибо, всё понятно.
    А ещё ведь может быть длинное имя типа:
    \\?\....
    т.е надо ещё и проверять на наличие строки "\\?\", правда я не знаю передаются ли такие строки в ядро или винда сама "\\?\" убирает, я вот тестил, а "\\?\" не встречал ни разу, он вообще может появиться ?
     
  8. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    \\?\ такого быть не может. скорее всего, ты имеешь в виду передаваемое \\.\ в CreateFileA/W, а это означает пространство имен локального компьютера, то есть другими словами преобразовывается сразу в \??\
    Так что в ядре пути, которые ты будешь встречать, отсчитываются от корня каталога объектов. важно понимать, что каталог объектов содержит только объекты устройств, драйверов и т.п., в нем нет файлов. То есть когда передается имя файла \Device\HarddiskVolume2\Windows\explorer.exe, то NtCreateFile начинает парсить имя. Она доходит до HarddiskVolume2, видит, что это объект "устройство" и вызывает драйвер для этого устройства (драйвер файловой системы, точнее), передавая ему дальнейший "хвост" пути (\Windows\explorer.exe). Драйвер файловой системы по запросу IRP_MJ_CREATE уже производит поиск файла, а так же решает, что же с ним делать - удовлетворять запрос не открытие, или же завершать в ошибкой, если у пользователя не хватает привилегий.
     
  9. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    в предыдущем посте я, в принципе, немного слукавил насчет преобразования \\.\ => \??
    есть локальные каталоги для публичных объектов, которые лежат в \Sessions\x\DosDevices\y, и обычно используются они. Там, например, хранятся сетевые диски (например Z: => \Device\LanmanRedirector\;Z;000.....\192.168.0.1\MyDrive), "просабстенные" командой subst и другие локальный для текущего пользователя диски (например, диск, создаваемый TrueCrypt: W: => \Device\TrueCryptVolumeW)
     
  10. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    Great, под "\\?\" я имел ввиду длинные имена, т.е имена > MAX_PATH символов, если я хочу создать файл с длинным именем я не могу просто сделать CreateFile("C:\....",...), я должен делать CreateFile("\\?\C:\...",...).
    Я понял почему "\\?\" быть не может, потому все имена файлов преобразуются в имена объектов. Спасибо, всё понятно. Тема в общем-то закрыта, хотя
    Остаётся для меня загадкой до сих пор...
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Аа, понял. Ну это тот же глобальный каталог объектов по сути. Либо из сессионс либо из \??
    Напомни, что такое simple rename/fully qualified rename/relative rename.. чтото не помню. тогда мб отвечу
     
  12. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    Simpe rename - файл просто переименовывается не меняя местоположения на диске, допустим я переименоваываю C:\Windows\test.txt в C:\Windows\test2.txt
    Relative rename - файл переносится, но имя его не меняется, допустим C:\Windows\test.txt в C:\test.txt
    Fully Qualified Rename - переименование и перенос. Пример - C:\Windows\test.txt в C:\test2.txt
    Соответсвенно ОСь по разному обрабатывает эти ситуации, из DDK:
    Так вот, анализируя RootDirectory и FileName у меня всегда получается Fully Qualified Rename, даже когда я переименовываю C:\Windows\test.txt в C:\Windows\test2.txt
    Вот я и хочу понять, DDK врёт или я что-то не так делаю...
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Не помню точно, разве FILE_RENAME_INFORMATION содержит путь в формате NT ?
     
  14. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    Sheph
    Нет не врет, просто симпле редко встречается, оболочки типа фара-експлорера работают с полными именами
     
  15. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    z0mailbox, я и в винде переименовывал (в эксплорере) и прогу писал, которая MoveFile вызывает по разному, всё равно одно и тоже.
    Clerk, да
     
  16. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    Sheph
    Я столкнулся с симпле при ренеймах файлов в шареном каталоге с ремоутной машины
    То есть внутри SRV.SYS
    Больше нигде вжывую не видел
     
  17. Sheph

    Sheph New Member

    Публикаций:
    0
    Регистрация:
    24 янв 2008
    Сообщения:
    89
    z0mailbox, у меня и при ренаме на сетевых шарах full используется (
     
  18. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    Вроде нет обратной функции.
    Надо перебирать локальные диски, вызывать QueryDosDevice, сравнивать со строкой.

    В Notify функциях встречал вообще без префиксов - \WINDOWS\...., \Program Files\.....