Запись на логический диск WriteFile() == ERROR_ACCESS_DENIED
От: zaza1  
Дата: 01.10.07 16:12
Оценка:
Доброго дня!

Прошу помощи или подсказки вот в каком вопросе.. возникла необходимость реализовать запись на диск напрямую, т.е. не в файл. все происходит под вистой, с правами все ок, т.е. запуск программы идет под админом.

пишу элементарный код:

HANDLE h=CreateFileW(L"\\\\.\\D:",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_WRITE|FILE_SHARE_READ,0,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,0);
SetFilePointer(h,0x80000000-0x400,0,FILE_BEGIN);
GetLastError();
VOID *a=VirtualAlloc(0,512,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
DWORD dwBytes;
WriteFile(h,a,512,&dwBytes,0);
GetLastError();

CreateFile проходит успешно, SetFilePointer тоже.. а вот WriteFile возвращает ERROR_ACCESS_DENIED. При этом, если установить SetFilePointer ближе к началу диска запись может пройти успешно, хотя по моим наблюдениям тоже не всегда.. если открыть диск в WinHex ситуация аналогичная, что и не удивительно ибо механизм такой же. на форумах msdn нашел топики с аналогичной проблемой
https://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=891426&SiteID=1
https://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1581337&SiteID=1&pageid=0

Решения однако нет. собственно мои изыскания приводят вот к чему.. удалось выяснить что если убить на диске файловую систему, то проблема решается и все работает отлично — запись проходит. Анализировав работу NtCreateFile сделал вывод что такую ошибку (STATUS_ACCESS_DENIED) судя по всему дает вызов IoCallDriver, т.е. низлежащий драйвер.. разобраться дальше не хватает опыта/мозгов.. под вистой ядро отлаживать очень паршиво — пользую Syser — постоянные бсоды и подвисания отладчика (какие то косяки с выводом на экран помоему..). ощущение такое, что какая то служба или драйвер блокирует запись на диск "напрямую так сказать". зачем — для меня загадка, видимо какая то особенность политики безопасности.. вобщем я в замешательстве, кто что знает подскажите плиз?
Re: Запись на логический диск WriteFile() == ERROR_ACCESS_DE
От: zaza1  
Дата: 01.10.07 22:40
Оценка:
на ixbt подсказали локнуть диск DeviceIoControl(..FSCTL_LOCK_VOLUME..), все бы хорошо, но только не хочется диск локать полносью ибо возможно это только если ничего с диска не открыто. LockFile не работает..
Re: Запись на логический диск WriteFile() == ERROR_ACCESS_DE
От: Ivan Россия www.rsdn.ru
Дата: 02.10.07 08:48
Оценка:
Здравствуйте, zaza1, Вы писали:

> под вистой ядро отлаживать очень паршиво

MSDN http://msdn2.microsoft.com/en-us/library/aa365748.aspx

If you write directly to a volume that is mounted by a file system, you must first obtain exclusive access to the volume. Otherwise, you risk causing data corruption or system instability, because your application's writes may conflict with other changes coming from the file system and leave the contents of the volume in an inconsistent state. To prevent these problems, the following changes have been made in Windows Vista and later:


A write on a volume handle will succeed if the volume is not mounted by a file system, or if one of the following conditions is true:

— The sectors to be written to are boot sectors.
— The sectors to be written to reside outside of file system space.
— You have explicitly locked or dismounted the volume by using FSCTL_LOCK_VOLUME or FSCTL_DISMOUNT_VOLUME.
— The volume has no file system. (In other words, it has been mounted as a RAW volume.)

A write on a disk handle will succeed if one of the following conditions is true:

— The sectors to be written to do not fall within a volume's extents.
— The sectors to be written to fall within a mounted volume, but you have explicitly locked or dismounted the volume by using FSCTL_LOCK_VOLUME or FSCTL_DISMOUNT_VOLUME.
— The sectors to be written to fall within a volume that is not mounted or has no file system.


здесь есть информация по поводу возможных причин введения таких ограничений
http://theinvisiblethings.blogspot.com/2006/10/vista-rc2-vs-pagefile-attack-and-some.html

> пользую Syser — постоянные бсоды и подвисания отладчика (какие то косяки с выводом на экран помоему..).

лучше для отладки ядра использовать WinDbg

В режиме ядра эти ограничения уже мешать не будут — поэтому один из вариантов решения проблемы — разработка драйвера для низкоуровневой работы с диском. Этот драйвер необходимо будет подписать — иначе он не будет загружаться в Vista x64, как подписывать — см. в форуме "Низкоуровневое программирование"
Re[2]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: zaza1  
Дата: 02.10.07 16:40
Оценка:
Здравствуйте, Ivan, Вы писали:

Благодарю за то что подкинули информации)

>> пользую Syser — постоянные бсоды и подвисания отладчика (какие то косяки с выводом на экран помоему..).

I>лучше для отладки ядра использовать WinDbg

воможно но для этого нужно две тачки — не всегда удобно

I>В режиме ядра эти ограничения уже мешать не будут — поэтому один из вариантов решения проблемы — разработка драйвера для низкоуровневой работы с диском. Этот драйвер необходимо будет подписать — иначе он не будет загружаться в Vista x64, как подписывать — см. в форуме "Низкоуровневое программирование"


этот вариант вполне подходит) вообще софтина исключительно для личного пользования поэтому готов даже виндовый драйвер пропатчить, знать бы где) вот только вот какая проблема.. я почему и стал изучать этот вопрос — драйвер режима ядра не мог писать на диск.. т.е. партиция открывалась драйвером (ZwCreateFile) в нулевом кольце и запись туда проводилась с использованием ZwWriteFile. и все это дело сваливалось со STATUS_ACCESS_DENIED. меня эт до глубины души возмутило — все ж происходит в ядре, какие уж тут ограничения.. в юзер моде ситуация абсолютно аналогичная. т.е. как я понимаю над драйвером непосредственно пишущим на диск висит драйвер-фильтр, который рулит ситуацией, какой irp пакет принять и толкнуть дальше вниз по стеку, а какой обломать, причем это не должен быть драйвер файловой системы, ведь она здесь никоем разом не завязана.. самое простое решение которое мне в голову приходит — просто отрубить этот фильтр. по моим соображениям функционал не пострадает, а защита эта отвалится.. но вот только кто он?
Re[3]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: Unmanaged Россия ICQ 476611995
Дата: 02.10.07 17:09
Оценка:
I>>лучше для отладки ядра использовать WinDbg
Z>воможно но для этого нужно две тачки — не всегда удобно

Виртуальные машины придумали в том числе и по этой причине.
STATUS_INVALID_DEVICE_REQUEST
Re[4]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: zaza1  
Дата: 02.10.07 17:54
Оценка:
Здравствуйте, Unmanaged, Вы писали:

I>>>лучше для отладки ядра использовать WinDbg

Z>>воможно но для этого нужно две тачки — не всегда удобно

U>Виртуальные машины придумали в том числе и по этой причине.


вощем это вопрос философский — не суть темы)) у меня например виста на буке стоит.. диска нет естественно. искать ее ща, на вмварь ставить немного лениво, да и тормоза мне кажется будут если пустить 2 висты на одной тачке — хотя хз) вобщем это все не так уж принципиально)
Re[3]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: Константин Л. Франция  
Дата: 03.10.07 09:37
Оценка:
Здравствуйте, zaza1, Вы писали:

[]

UAC, virtualization, luafv.sys?

здесь
Re[4]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: zaza1  
Дата: 03.10.07 20:33
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, zaza1, Вы писали:


КЛ>[]


КЛ>UAC, virtualization, luafv.sys?


КЛ>здесь


UAC отключен.. luafv.sys попробывал отрубить — ситуация таже..
копаюсь в ядре. похоже что картинка такая — при попытке записи идет вызов рутины записи из fastfat (партиция под фатом). и обламывается все там. странно немножко получается.. ну каким раком сюда приходится драйвер файловой системы? открывается то ведь диск, что там за файловая система совершенно не важно в этом случае. разве что такие заподлянки устраивать) как жешь тут блин сделать все малой кровью..)
Re[5]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: zaza1  
Дата: 03.10.07 21:07
Оценка:
Решение найдено!))

поистине красивое — я прям горд за себя!.. просто пихнуть туда fastfat с xp)) just replace так сказать))
Re[3]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: Ivan Россия www.rsdn.ru
Дата: 04.10.07 11:51
Оценка:
Здравствуйте, zaza1, Вы писали:

Z>воможно но для этого нужно две тачки — не всегда удобно

как рядом советовали — отлаживаться удобно на виртуальной машине, а WinDbg запускать на основной, подключаясь через виртуальный com1 (через pipe).

Z>этот вариант вполне подходит) вообще софтина исключительно для личного пользования поэтому готов даже виндовый драйвер пропатчить, знать бы где) вот только вот какая проблема..

Мне попался уже готовый продукт, который позволяет писать напрямую на диск — но он, по-моему, коммерческий:
http://www.eldos.com/rawdisk/


>я почему и стал изучать этот вопрос — драйвер режима ядра не мог писать на диск.. т.е. партиция открывалась драйвером >(ZwCreateFile) в нулевом кольце и запись туда проводилась с использованием ZwWriteFile. и все это дело сваливалось со >STATUS_ACCESS_DENIED. меня эт до глубины души возмутило — все ж происходит в ядре, какие уж тут ограничения..

>в юзер моде ситуация абсолютно аналогичная. т.е. как я понимаю над драйвером непосредственно пишущим на диск висит >драйвер-фильтр, который рулит ситуацией, какой irp пакет принять и толкнуть дальше вниз по стеку, а какой обломать, >причем это не должен быть драйвер файловой системы, ведь она здесь никоем разом не завязана..

Я посмотрел отладчиком — запросы на запись обламывают в нескольких разных драйверах. Запросы на запись к волюму (если открывать как \\.\C отклоняются непосредственно в драйверах файловых систем — Ntfs (NtfsCommonWrite) и Fastfat (FatCommonWrite). Запросы к диску (если открыть файл как \\.\PhysicalDrive0) отклоняет драйвер volmgr, к которому обращается для проверки возможности записи в конкретное место диска partmgr.

ZwWriteFile в режиме ядра ломается, так как запрос к волюму проходит через драйвер файловой системы.
>самое простое решение которое мне в голову приходит — просто отрубить этот фильтр. по моим соображениям функционал не >пострадает, а защита эта отвалится.. но вот только кто он?

Проверок несколько и в разных драйверах. Замена fastfat версией от XP может сработать, а вот насчет Ntfs — маловероятно, так как Ntfs изменился со времен XP — поддержка транзакций и т.п.

Один из возможных "универсальных" вариантов — сделать свой драйвер, в котором использовать не ZwWriteFile, а создавать IRP вручную и отправлять его не файловой системе, а непосредственно волюму, например, так:
NTSTATUS TestDirectWrite()
{
    NTSTATUS status = 0;
    HANDLE hDrive = 0; 
    OBJECT_ATTRIBUTES attrs = {0}; 
    IO_STATUS_BLOCK iosb = {0};

    char wrt_buf[512] = {0};
    PFILE_OBJECT pFileObj = 0;
    PIRP pIrp = 0;
    LARGE_INTEGER liOffset = {0x1000000, 0};
    PDEVICE_OBJECT pRelated = 0;

    KEVENT event;
    UNICODE_STRING  usPath = {0};

    RtlInitUnicodeString(&usPath, L"\\??\\E:");
    KeInitializeEvent( &event, NotificationEvent, FALSE);

    InitializeObjectAttributes(&attrs, &usPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
    
    status = ZwOpenFile(&hDrive, GENERIC_WRITE, &attrs, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);

    if(NT_SUCCESS(status))
    {
        status = ObReferenceObjectByHandle(hDrive, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &pFileObj, 0);
        ZwClose(hDrive);

        if(NT_SUCCESS(status))
        {
            pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, pFileObj->DeviceObject, wrt_buf, 
                sizeof(wrt_buf), &liOffset, &event, &iosb);

            if(!pIrp)
            {
                status = STATUS_INSUFFICIENT_RESOURCES;
            }
            else
            {
                status = IoCallDriver( pFileObj->DeviceObject, pIrp );
                if(status == STATUS_PENDING)
                {
                    status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
                    status = iosb.Status;
                }
            }

            ObDereferenceObject( pFileObj );
        }
    }
    return status;
}
Re[4]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: zaza1  
Дата: 04.10.07 21:25
Оценка:
Здравствуйте, Ivan, Вы писали:

I>Здравствуйте, zaza1, Вы писали:


Замена фата сработала, а нтфс мне и нужна, так что вобщем то решена проблема.
За код благодарю, я такое решение рассматривал как последний вариант.. оно вроде как очевидно, но драйверами серьезно не занимался никогда, поэтому у меня тут паранойя небольшая)

кстати вот еще какой вопрос.. заметил такую вещь. если дергать IoGetDeviceObjectPointer (применительно к партиции) с FILE_READ_DATA сразу после запуска системы, функция обламывается со STATUS_SHARING_VIOLATION (помоему так). минут через 10 начинает работать нормально, какой то драйвер отпускает видимо и все ок станивится.. если вызывать функцию с ACCESS_MASK=0 тогда все ок.. вопрос вообще в чем разница? и еще.. копнул в эту функцию IoGetDeviceObjectPointer там идет вызов ZwOpenFile с ShareAccess=0, т.е. с эксклюзивным доступом, изза чего и получается SHARING_VIOLATION.. зачем? почему бы не вызывать с FILE_SHARE_READ|FILE_SHARE_WRITE? наверное не зря сделали как сделали, но чем черевато все же?
Re[5]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: Ivan Россия www.rsdn.ru
Дата: 05.10.07 08:34
Оценка:
Здравствуйте, zaza1, Вы писали:

Z>кстати вот еще какой вопрос.. заметил такую вещь. если дергать IoGetDeviceObjectPointer (применительно к >партиции) с FILE_READ_DATA сразу после запуска системы, функция обламывается со STATUS_SHARING_VIOLATION

>(помоему так). минут через 10 начинает работать нормально, какой то драйвер отпускает видимо и все ок >станивится.. если вызывать функцию с ACCESS_MASK=0 тогда все ок.. вопрос вообще в чем разница?

разница в том, что при определенных значениях accessmask можно открыть напрямую volume, в остальных случаях вернут верхний device_object из стека файловой системы. Подробнее:

Relative Open Requests for Direct Device Open Handles
The I/O Manager performs a direct device open in response to create or open
requests that meet all of the following criteria:

· The volume name has no trailing characters. For example, G: is
valid, but G:\ and G:\a\b are not.

· The create request is not relative to another file handle.

· The requested access includes one or more of the following, and no
other access types: SYNCHRONIZE, FILE_READ_ATTRIBUTES, READ_CONTROL,
ACCESS_SYSTEM_SECURITY, WRITE_OWNER, WRITE_DAC

For a normal create or open request on a storage volume, the I/O Manager
typically attempts to mount a file system, if none is already mounted.
However, when performing a direct device open, the I/O Manager does not
mount or send requests through a file system. Instead, it sends the
IRP_MJ_CREATE request directly to the storage stack, bypassing any file
system that has been mounted for the volume. Requests for further operations
(such as read, write, or DeviceIoControl) on the file handle are sent to the
topmost device object in the storage stack for the volume.

The I/O Manager performs a direct device open only when the caller requests
limited access to the device, such as the access required to read device
attributes. This type of open operation occurs rarely, but is useful when an
application wants to query certain attributes of a storage volume without
forcing a file system to be mounted.




>и еще.. копнул в эту функцию IoGetDeviceObjectPointer там идет вызов ZwOpenFile с ShareAccess=0, т.е. с >эксклюзивным доступом, изза чего и получается SHARING_VIOLATION.. зачем? почему бы не вызывать с >FILE_SHARE_READ|FILE_SHARE_WRITE? наверное не зря сделали как сделали, но чем черевато все же?


Почему функция IoGetDeviceObjectPointer не имеет параметра sharing мне неизвестно, но обойти это можно, вручную открывая ZwOpenFile с нужными параметрами sharing'а и затем ObOpenObjectByHandle.
Re[6]: Запись на логический диск WriteFile() == ERROR_ACCESS
От: zaza1  
Дата: 05.10.07 18:13
Оценка:
Здравствуйте, Ivan, Вы писали:

I>разница в том, что при определенных значениях accessmask можно открыть напрямую volume, в остальных случаях вернут верхний device_object из стека файловой системы. Подробнее:


ага.. благодарю за исчерпывающий ответ)


I>Почему функция IoGetDeviceObjectPointer не имеет параметра sharing мне неизвестно, но обойти это можно, вручную открывая ZwOpenFile с нужными параметрами sharing'а и затем ObOpenObjectByHandle.


ну да, функция в общем-то бесхитростная и сводится к выполнению этих действий и ничего более интересного там и нет.

Большое спасибо за помощь, а то неделю уже бился.. теперь все разрешилось и многое понято и осознано) Еще раз спасибо!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.