Конференция "WinAPI" » Как удалить файл без восстановления [D7]
 
  • guav © (24.03.08 11:42) [40]
    > [39] guav ©   (24.03.08 11:36)
    > (простой способ воспроизвести [14] - пробовать на сжатом файле).

    Сейчас что-то не получается просто сжать и изменять, чтобы файл "переехал". Но явление тоже наблюдал.
  • Riply © (24.03.08 15:09) [41]
    > [40] guav ©   (24.03.08 11:42)
    > Сейчас что-то не получается просто сжать и изменять, чтобы файл "переехал".
    > Но явление тоже наблюдал.

    Я уже написала процедуру, рекурсивно создающую дамп Run - ов всех объектов,
    содержащихся в директории вместе с их именами и LCN - ми.
    Осталось реализовать сравнение дампов,
    и можно будет "во всеоружии" приступить к поимке переезжающих файлов :)
  • Игорь Шевченко © (25.03.08 09:52) [42]
    Riply ©   (24.03.08 15:09) [41]

    "Я нашел,  как применить здесь нестирающиеся шины  из  полиструктурного  волокна  с  вырожденными аминными  связями и неполными кислородными группами.  Но я не знаю пока, как использовать регенерирующий реактор на субтепловых нейтронах.  Миша, Мишок!  Как быть с реактором?"
    (с) А и Б Стругацкие
  • Riply © (25.03.08 09:52) [43]
    Мне не удалось воспроизвести "переезд" кластеров файла,
    как прямое следствие записи в него.
    Это означает только недостаточность моих знаний, но никак
    не то, что данный "переезд" невозможен.
    Уж не говоря о том, что не поймала сегодня - поймаю завтра :)
    Повторюсь: эффект, я наблюдала и не раз.
    Тогда он мне был совсем не нужен.
    Я рассматривала возможности "безопасной прямой записи" данных в файл.
    И то, что мне удалось зафиксировать "переезд"
    ставило крест на моем способе "блокировки класеров".
    Помню, расстроилась тогда :)

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

    Я приведу только схему кода, т.к. в нем используется очень много
    "самолапных" функций и классов (очень лень выдергивать все это. Sorry),
    но попробую прокоментировать происходящее.
    Приступим:
    function ReadWippedFile(const pSourName: PUNICODE_STRING): NTSTATUS;
    const
    StuffExt: WideString = '.stuff';
    sHelloDolly: WideString = 'Hello Dolly !!!' + sLineBreak + 'Wipped file: ';
    var
    SourLcn, StuffLcn: _LCN;
    StuffName: UNICODE_STRING;
    MFTDisk: TMFTScan;
    hFileObj: THandle;
    pBuffer: Pointer;
    BufferSize, SourceClustersCount, StuffClustersCount: DWord;
    begin
    StuffLcn := -1;
    SourLcn := -1;
    SourceClustersCount := 4;
    StuffClustersCount := SourceClustersCount * 2;
    RtlInitUnicodeString(@StuffName, PWideChar(pSourName.Buffer + StuffExt));
    // Создаем тестовый файл и временный файл, в который будем перемещать класеры.
    // Задаем им размеры.
    Result := Nt_FileCreateSized(pSourName, SourceClustersCount * BytesPerCluster, SET_SIZE_FLAG_INSTANCE, nil);
    if NT_SUCCESS(Result) then Result := Nt_FileCreateSized(@StuffName, StuffClustersCount * BytesPerCluster, SET_SIZE_FLAG_INSTANCE, nil);
    if NT_SUCCESS(Result) then
     begin
      MFTDisk := TMFTScan.Create;
      try // TMFTScan - класс для работы с диском
       Result := MFTDisk._Set_CallBackFunctions(pSourName, nil); // настраиваем
       if Result = STATUS_SUCCESS then Result := MFTDisk.NtGetObjectRetrievalPointers(@StuffName, 0);
       if Result = STATUS_SUCCESS then
        begin // получили RetrievalPointers. Проверяем их пригодность как буфера для перемещения
         with PRETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData)^ do
          if ExtentCount > 0 then
           with Extents[0] do
            if (Lcn.QuadPart > 0) and (NextVcn.QuadPart >= SourceClustersCount)
             then StuffLcn := Extents[0].Lcn.QuadPart;
         if StuffLcn <= 0 then
          begin
           ShowMessage('Stuff file is fragmented. Try again' + sLineBreak
                       + Pars_RETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData));
           Exit;
          end
         else Nt_DeleteFileU(@StuffName, 0);
         Result := MFTDisk.NtGetObjectRetrievalPointers(pSourName, 0);
         if Result = STATUS_SUCCESS then
          begin // Запоминаем исходные RetrievalPointers нашего файла и записываем их в лог
           with PRETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData)^ do
           if ExtentCount > 0 then SourLcn := Extents[0].Lcn.QuadPart;
           Log_WriteStatusNT('Try to move to: ' + IntToStr(StuffLcn) + sLineBreak
                             + Pars_RETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData), Result);
          end;
         // Подготовка закончена, приступаем к работе
         if Result = STATUS_SUCCESS then
          begin // Открываем наш файл
           Result := Nt_OpenFile(pSourName, FILE_WRITE_DATA or SYNCHRONIZE, FILE_SHARE_ALX_NONE,
                                 FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, nil, nil, hFileObj);
           if Result = STATUS_SUCCESS then
            try
             BufferSize := SourceClustersCount * BytesPerCluster;
             pBuffer := GetMemory(BufferSize);
             if pBuffer <> nil then
              try
               Записываем в буфер приветствие для Dolly и имя файла.
               FillBuffer(pBuffer, BufferSize, sHelloDolly + sLineBreakW + pSourName.Buffer);
               Result := Nt_WriteFile(hFileObj, pBuffer, BufferSize, nil);
               FlushFileBuffers(hFileObj);
               // Записали данные в файл, приступаем к их уничтожению
               NT_SUCCESS_SET_WORST(Result, Nt_SetFilePointer(hFileObj, nil, nil));
               if NT_SUCCESS(Result) then
                begin
                 FillChar(pBuffer^, BufferSize, 0); // Очистили буфер и записали первую половину файла
                 Result := Nt_WriteFile(hFileObj, pBuffer, BufferSize div 2, nil);
                 if Result = STATUS_SUCCESS then
                  begin
                   // Перемещаем кластеры нашего файла, записываем в лог новое расположение
                   Result := MFTDisk.MoveObjectClusters(pSourName, StuffLcn);
                   if NT_SUCCESS_SET_WORST(Result, MFTDisk.NtGetObjectRetrievalPointers(pSourName, 0))
                    then Log_WriteMessage(Pars_RETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData));
                   // и затираем вторую половину файла
                   NT_SUCCESS_SET_WORST(Result, Nt_WriteFile(hFileObj, IncPtr(pBuffer, BufferSize div 2), BufferSize div 2, nil));
                  end;
                 FillChar(pBuffer^, BufferSize, 0);
                 // читаем старые сектора нашего файла (прямое чтение)
                 Result := MFTDisk.ReadClustersToBuffer(pBuffer, SourLcn, SourceClustersCount);
                 if Result = STATUS_SUCCESS // и сохраняем их в др. файл
                  then Result := Nt_CreateFileData(@StuffName, pBuffer, BufferSize, nil, nil);
                end;
              finally
               FreeMem(pBuffer);
              end
             else Result := STATUS_INSUFFICIENT_RESOURCES;
            finally
             NT_SUCCESS_SET_WORST(Result, NtClose(hFileObj));
            end;
          end;
        end;
      finally
       MFTDisk.Free;
      end;
     end;
    end;


    Читаем лог:
    Try to move to: 623215
    ExtentCount 1
    Index 0 Lcn 1778061 Count 4
    The operation completed successfully
    ExtentCount 1
    Index 0 Lcn 623215 Count 4
    The operation completed successfully

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

    Смотрим данные, полученные прямым чтением старых секторов:
    H e l l o   D o l l y   ! ! !
    W i p p e d   f i l e :  
    \ ? ? \ I : \ Z _ S c a n T e s t D i r \ S o u r c e \ C l u s t e r e d F i l e . t x t
    (Привожу только кусок, на самом деле считано все полностью). В новых кластерах, разумеется нули :)

    P.S.
    Операции затирания, прямого чтения и перемещения можно "тасовать" как угодно
    (разумеется не нарушая логики). Результат остается тем же.

    P.P.S.
    Пример собирался "на коленке" так что строго не судите :)
    Если возникнет необходимость - напишу нормальный (пригодный для компиляции).
    Но очень лень этим заниматься. :)
  • Riply © (25.03.08 10:03) [44]
    > [43] Riply ©   (25.03.08 09:52)

    Несколько добавлений:
    Как видно из примера для перемещения кластеров не использовался Handle файла.
    MoveObjectClusters(pSourName, StuffLcn);
    Это было сделано для того, чтобы показать, что "монопольное" открытие, тоже не спасает.
    Перемещались кластеры "законными" методами с помощью FSCTL_MOVE_FILE
  • WipeInfo (25.03.08 10:09) [45]
    Eraser
    WipeInfo
  • Игорь Шевченко © (25.03.08 10:26) [46]

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


    Вот же. А ошибку в примере ты уже нашла ?
  • Riply © (25.03.08 10:32) [47]
    >  [46] Игорь Шевченко ©   (25.03.08 10:26)
    > Вот же. А ошибку в примере ты уже нашла ?

    Ну вот. Как всегда :(
    Пол ночи писала это дело, уже голова плохо соображает.
    И где она (ошибка) ?
  • Riply © (25.03.08 10:52) [48]
    > [46] Игорь Шевченко ©   (25.03.08 10:26)

    Ну нельзя же так издеваться над софорумчанкой :)
    В чем эта чертова ошибка ?
  • guav © (25.03.08 11:57) [49]
    > [45] WipeInfo   (25.03.08 10:09)
    > Eraser

    Вот кстати эту тулзу смотрел (код открыт).
    На NTFS она логфайл не чистит, а на FAT имеются ошибки, которые моут привести к проче данных.
  • Игорь Шевченко © (25.03.08 11:59) [50]
    Riply ©   (25.03.08 10:52) [48]

    Ошибка в том, что при обычной записи в файл (несжатый) его кластеры автомагически не перемещаются. Ибо незачем.
  • Riply © (25.03.08 12:14) [51]
    > [50] Игорь Шевченко ©   (25.03.08 11:59)
    > Riply ©   (25.03.08 10:52) [48]
    > Ошибка в том, что при обычной записи в файл (несжатый)
    > его кластеры автомагически не перемещаются. Ибо незачем.

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

    Насчет "незачем", я бы не была столь категорична, ибо пути системы неисповедимы. :)
    Это относиться и к сторонним программам, установленным на компьютер.
    И если кто угодно из них имеет возможность переместить наш файл, когда мы в него пишем,
    то какая здесь может быть гарантия ?
    В примере я специально использовала стандартный файл, даже не фрагментированный,
    что бы показать, что и в этом случае запись может происходить "не туда".
  • Игорь Шевченко © (25.03.08 13:03) [52]
    Riply ©   (25.03.08 12:14) [51]


    > Ведь не случайно эффект я наблюдала на фрагментированных
    > файлах.


    Воспроизводи. До тех пор, пока не воспроизвела, считается за глюк инструмента.


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


    В примере ты его параллельно дварковала, причем влэндишным способом.
  • Riply © (25.03.08 13:14) [53]
    >  [52] Игорь Шевченко ©   (25.03.08 13:03)
    > Воспроизводи. До тех пор, пока не воспроизвела, считается за глюк инструмента.

    "Ох не легкая это работа, из болота тащить бегемота." (с)
    Что ж делать, буду пытаться. Но требуется время (кроме удовольствия, еще, к сожалению, и работа существует :)

    > В примере ты его параллельно дварковала, причем влэндишным способом.
    :)
    Дык дваркование из примера можно убрать и поместить
    в другой порцесс. Просто так было легче совместить его с записью в файл :)
  • Игорь Шевченко © (25.03.08 13:17) [54]
    Давай сделаем проще:
    Ты создашь файл, стандартным образом, скопируешь файл с исходным текстов какого-нибудь твоего примера, килобайт на 10 в файл с именем a.txt.

    После этого ты его аккуратно откроешь на запись, и пропишешь от начала до конца нулями или произвольным символом.

    var
     AFile: HFILE;
     FileSize, Worker: Cardinal;
     I: Integer;
     Buffer: array[0..1023] of char;
    begin
     FillChar(Buffer, SizeOf(Buffer), 65);
     AFile := CreateFile('a.txt', GENERIC_READ or GENERIC_WRITE,
       FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
     Win32Check(AFile <> INVALID_HANDLE_VALUE);
     try
       FileSize := GetFileSize(AFile, @Worker);
       if Worker <> 0 then
         raise Exception.Create('File too large to test');
       for I := 0 to (FileSize div SizeOf(Buffer)) - 1 do
         if not WriteFile(AFile, Buffer, SizeOf(Buffer), Worker, nil)
            or (Worker <> SizeOf(Buffer)) then
         raise Exception.Create('Write failed');
       if FileSize mod SizeOf(Buffer) <> 0 then
         if not WriteFile(AFile, Buffer, FileSize mod SizeOf(Buffer), Worker,
            nil) or (Worker <> FileSize mod SizeOf(Buffer)) then
         raise Exception.Create('Write failed');
     finally
       CloseHandle(AFile);
     end;
    end;



    После этого ты любым тебе доступным инструментом восстановишь содержимое этого файла :)

    Потом то же самое с сжатым файлом и с разреженным.

    Договорились ?
  • Игорь Шевченко © (25.03.08 13:20) [55]
    В дополнение к [54]

    В параллель с файлом ты можешь делать все, что угодно. Но принцип такой, что после окончания всех процессов с файлом, ты, имея диск с NTFS, пытаешь восстановить. Разумеется, способ, когда ты заранее скопируешь кластеры, занимаемые файлом до его затирания, а потом перепишешь файл в другое место каким-либо способом, хотя бы и влэндишным, считается неспортивным.
    Да, разумеется ход процесса восстановления надо в студию, чтобы пример был воспроизводимым.
  • Riply © (25.03.08 13:28) [56]
    > [54] Игорь Шевченко ©   (25.03.08 13:17)
    > Договорились ?

    Т.е. мне надо найти условия, при которых данное затирание
    будет работать не сто процентно.
    Причем такие, что сбой в затирании был воспроизводим
    при разумном числе попыток ?

    Если я правильно поняла и мне дается время, то согласна :)
  • Игорь Шевченко © (25.03.08 13:32) [57]
    Riply ©   (25.03.08 13:28) [56]

    Тебе надо сделать

    > После этого ты любым тебе доступным инструментом восстановишь
    > содержимое этого файла


    Время разумеется не ограничено :)
  • Riply © (25.03.08 13:36) [58]
    > [57] Игорь Шевченко ©   (25.03.08 13:32)
    > После этого ты любым тебе доступным инструментом восстановишь
    > содержимое этого файла

    Может не обязательно полное содержимое, а хотя бы один сектор (ну или кластер) ?

    > Время разумеется не ограничено :)

    Ну а после этого уточнения, я полностью согласная :)
  • WipeInfo (25.03.08 14:38) [59]
    Удалено модератором
    Примечание: Offtopic
 
Конференция "WinAPI" » Как удалить файл без восстановления [D7]
Есть новые Нет новых   [134433   +21][b:0][p:0.004]