Можно ли изменить размер файла в режиме ядра?
От: Suares  
Дата: 20.10.14 13:44
Оценка:
Сначала опишу, что я хочу сделать. Допустим у нас на компьютере есть папка E:\Data\Private, где будут лежать секретные файлы, содержащие схемы по отмыванию денег Так как они являются очень секретными, то и просматривать их хочу только я. Т.е. при сохранении файла в эту папку он будет шифроваться, а при чтении расшифровываться.

Решение придумал такое, что есть некое приложение, через которое можно будет открыть эту папку. И в тот момент когда происходит открытие папки, запускался драйвер, который будет перехватывать запросы по чтению/записи к E:\Data\Private.

За основу взял драйвер с WDK: SwapBuffer File System Minifilter Driver. Добавил только возможность пересылать массив символов с драйвера в мое приложение, где собственно будет шифрование/расшифрование данных. Алгоритм шифрования использую AES.

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

Повторюсь, у нас уже лежит ранее зашифрованный файл. При его чтении происходит следующее, выделяется буфер размером таким же как этот зашифрованный файл и заполняется этими данными, зашифрованными данными. Он называется swappedBuffer. Создается еще один буфер уже в PostRead, называется origBuf, для расшифрованного содержимого. И у origBuf размер ТАКОЙ ЖЕ как и у swappedBuffer. При этом мы знаем, что размер зашифрованных данных будет вегда больше чем реальных. Это связанно с блоками в AES алгоритме, в моем случае блоки кратны 16 байт. Во время копирования из swappedBuffer в origBuf происходит расшифровка. В итоге в origBuf будет лежать реальный контент файла. НО этот буфер велик для реальных данных и по этому при открытии файла я вижу в конце лишние байты(символы).

Создается origBuf буфер всего одной строчкой кода:

origBuf = MmGetSystemAddressForMdlSafe(iopb->Parameters.Read.MdlAddress, NormalPagePriority);

И как так получается, что этот буфер равняется размеру swappedBuffer для меня загадка.
Я искал переменную, которая показывала бы размер файла, который считывается, например length = 64 байта, а после расшифрования присвоить ей реальный размер, там length = 55. Как бы принудительно сказать системе, что там 55 байт, а не 64. Но не находил такого. И еще в добавок все переменные структур, которые я просматривал, связанные с размером файла по 512 байт.

И того сейчас так
strlen(origBuf) = strlen(swappedBuffer) = 64 байт
в реале, для нашего примера, должно быть так
strlen(origBuf) = 55 байт, а strlen(swappedBuffer) = 64 байт
нужно как-то такое обойти, но не могу найти или придумать что-либо как усечь размер origBuf до нужно нам размера.
Вообще можно смухлевать в драйвере с размерами файлов?
Re: Можно ли изменить размер файла в режиме ядра?
От: ononim  
Дата: 20.10.14 18:04
Оценка: +1
S>И того сейчас так
S>strlen(origBuf) = strlen(swappedBuffer) = 64 байт
S>в реале, для нашего примера, должно быть так
S>strlen(origBuf) = 55 байт, а strlen(swappedBuffer) = 64 байт
S>нужно как-то такое обойти, но не могу найти или придумать что-либо как усечь размер origBuf до нужно нам размера.
S>Вообще можно смухлевать в драйвере с размерами файлов?

FLT_CALLBACK_DATA::IoStatus::Information, не?
+ еще надо будет подмухлевывать результаты IRP_MJ_QUERY_INFORMATION дабы EndOfFile возвращать корректный в FILE_STANDARD_INFORMATION. А еще — IRP_MJ_DIRECTORY_CONTROL, потому что в списке файлов в директории тоже размер возвращается. Изменение размера файла делается при помощи IRP_MJ_SET_INFORMATION/FileEndOfFileInformation — уверен вы не хотите чтобы приложения падали, увидев в только что отращенном хвосте файла некий мусор вместо ожидаемых нуликов.
Для затравки хватит, а потом придет x64 и все толком расскажет, если сами к тому времени не нагуглите примеры, которых, я уверен, в инете валом.
Как много веселых ребят, и все делают велосипед...
Отредактировано 20.10.2014 18:10 ononim . Предыдущая версия .
Re: Можно ли изменить размер файла в режиме ядра?
От: x64 Россия http://x64blog.name
Дата: 21.10.14 00:08
Оценка: 16 (2)
S>Решение придумал такое, что есть некое приложение, через которое можно будет открыть эту папку.

Напомню, что злоумышленник может внедриться в процесс этого приложения и свободно читать/писать шифрованные файлы. Это можно решить, например, защищёнными службами, когда содержимое шифрованного файла читается/пишется исключительно из этой службы. Однако, если под "приложением" понималось всего лишь окошко для ввода пароля, то забей.

S>Повторюсь, у нас уже лежит ранее зашифрованный файл. При его чтении происходит следующее, выделяется буфер размером таким же как этот зашифрованный файл и заполняется этими данными, зашифрованными данными. Он называется swappedBuffer. Создается еще один буфер уже в PostRead, называется origBuf, для расшифрованного содержимого. И у origBuf размер ТАКОЙ ЖЕ как и у swappedBuffer. При этом мы знаем, что размер зашифрованных данных будет вегда больше чем реальных. Это связанно с блоками в AES алгоритме, в моем случае блоки кратны 16 байт. Во время копирования из swappedBuffer в origBuf происходит расшифровка. В итоге в origBuf будет лежать реальный контент файла. НО этот буфер велик для реальных данных и по этому при открытии файла я вижу в конце лишние байты(символы).


Есть такое понятие, как виртуализация файловой системы, которое само по себе состоит из нескольких аспектов, ну и как бы виртуализация размера файла — один из них. И реализовать это чисто технически особо и не проблема, знай себе перехватывай соответствующие запросы, где размер фигурирует (а именно query information, query directory и т.п.), и ставь там что необходимо, плюс реагируй на запросы изменения размера типа set information. Но в твоём случае надо ещё знать, какой размер оригинального файла, иначе просто непонятно, какое значение подставлять. Тут есть несколько вариантов решения проблемы. Например, можно к каждому шифруемому файлу добавлять заголовок в начало, добавлять, разумеется, прозрачно для приложений, ну а в заголовке хранить разную метаинформацию, например, размер оригинального файла, тип шифра, хеши всякие для валидации пароля и пр. Как вариант, можно также сообщать об изменениях в шифрованном файле из драйвера в некое управляющее приложение (службу), которое будет вести базу с метаинформацией о шифрованных файлах, хотя мне это не очень нравится. Этот вопрос уже обсуждался, воспользуйся поиском по форуму.

S>Я искал переменную, которая показывала бы размер файла, который считывается, например length = 64 байта, а после расшифрования присвоить ей реальный размер, там length = 55.


Лолшто? Нет никакой "переменной", которая "показывает" размер файла. Всё несколько сложнее, чем ты думаешь. Смотри, как всё происходит: первое, что необходимо сделать, это выровнять смещение (offset) и размер (length) в PreRead по длине блока твоего блочного шифра, т.е. если это AES, то блок 16 байт, значит и прочитать надо столько байт, сколько кратно 16, покажем на примере — оригинальный файл всего = 31 байт, шифрованный файл всего = 32 байта (выровнен же), приложением запрошено было = 17 байт по смещению = 4 байта, в коде PreRead ты ставишь смещение = 0 байт и длину = 32 байта, далее создаёшь буфер размером 32 байта и сохраняешь его адрес в completion-контексте, отправляешь запрос ниже и ловишь его же в PostRead, здесь у тебя есть всё, что необходимо, ты знаешь размер оригинальных данных и оригинальное смещение (из параметров запроса), у тебя есть буфер с шифрованными данными (swappedBuffer, из контекста), ты смотришь в IoStatus.Information, это кол-во байт, которое вернула тебе ФС, это значение может быть меньше запрошенных тобой 32 байт, но оно точно должно быть кратно 16, иначе можно предположить, что файл повреждён и тогда запрос следует завершить с ошибочным статусом (ну, скажем, STATUS_DATA_ERROR), если же всё в порядке, тогда из расшифрованного буфера ты берёшь 17 байт, начиная с 4-го байта, и копируешь их в буфер приложения (origBuf), в IoStatus.Information ставишь 17 и статус STATUS_SUCCESS. Это самый простой случай из возможных, но допустим приложение хочет прочитать не 17 байт, а 32 байта по смещению 0 (это — нормальная ситуация, приложение не обязано знать размер файла, оно может запросить сколько угодно, а ФС обязана вернуть столько, сколько возможно), но ведь размер файла 31, ты не можешь вернуть больше, а значит в PostRead ты должен знать размер оригинального файла, чтобы вычислить максимальное кол-во байт, которое допустимо вернуть из расшифрованного буфера.

S>И еще в добавок все переменные структур, которые я просматривал, связанные с размером файла по 512 байт.


Забудь про 512, и вообще забудь про размер сектора, оно тебе только на руку, в случае non-cached запроса тебе и размер и смещение придут уже в выровненном виде (по 512 или по 1024 или по 4096 это не важно, всё равно по 16 же), а в худшем случае, когда ничего не выровнено, что делать ты уже знаешь, теперь знаешь.
JID: x64j@jabber.ru
Re[2]: Можно ли изменить размер файла в режиме ядра?
От: x64 Россия http://x64blog.name
Дата: 21.10.14 00:11
Оценка: +1
O>...нагуглите примеры, которых, я уверен, в инете валом.

Вот тут я сомневаюсь, покажите мне примеры полноценных прозрачно шифрующих файловых фильтров, да так, чтобы с подробными комментариями, с учётом всех возможных ситуаций и пр.
JID: x64j@jabber.ru
Re[3]: Можно ли изменить размер файла в режиме ядра?
От: mike_rs Россия  
Дата: 21.10.14 06:59
Оценка:
Здравствуйте, x64, Вы писали:

O>>...нагуглите примеры, которых, я уверен, в инете валом.


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


osr dmk например, но ессно небесплатно
Re[2]: Можно ли изменить размер файла в режиме ядра?
От: Suares  
Дата: 24.10.14 14:59
Оценка:
Здравствуйте, x64, Вы писали:

x64>... знай себе перехватывай соответствующие запросы, где размер фигурирует (а именно query information, query directory и т.п.), и ставь там что необходимо, плюс реагируй на запросы изменения размера типа set information.


Спасибо за ответ! Некоторые моменты я понял.
Я просмотрел почти все кейсы в IRP_MJ_QUERY_INFORMATION пакете, т.е. структуры в которых есть поле связанное с размеров файла. И оказалось, что только один кейс проходит — FileStandardInformation, перед открытием файла, в котором поля AllocationSize и EndOfFile структуры FILE_STANDARD_INFORMATION отображают размер файла.
В post-callback делаю так:

    switch (Data->Iopb->Parameters.QueryFileInformation.FileInformationClass)
    {
        case FileStandardInformation:
            standardInfo = (PFILE_STANDARD_INFORMATION)Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
            // print info.
            break;
    }


Но Вы еще писали, что в IRP_MJ_DIRECTORY_CONTROL пакете тоже где-то указывается размер файла, я все еще не могу найти его. Пробывал по аналогии как в IRP_MJ_QUERY_INFORMATION сделать, ну естественно уже в IRP_MJ_DIRECTORY_CONTROL пакете, что-то типо такого:

    switch (Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass)
    {
        case FileBothDirectoryInformation:
            bothDirInfo = (PFILE_BOTH_DIR_INFORMATION)Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
            // print info.
            break;
        case FileDirectoryInformation:
            // ...
        case FileFullDirectoryInformation:
            // ...
        
    }


но это успехом не увенчалось. При выводе информации с bothDirInfo выводятся какие-то огромные цифры. Как же тут достучаться до цифры, показывающей размер файла?

И еще один вопрос. Я правильно понимаю, что мне нужно подставить свою цифру(цифру показывающую реальный размер файла после расшифрования) во все поля связанные с размером файла, который хочу открыть, иначе ничего не получится? Так как пробывал EndOfFile из FILE_STANDARD_INFORMATION переписывать, в итоге при открытии файла выводится ошибка: "Not enough storage is available to process this command".
Re[3]: Можно ли изменить размер файла в режиме ядра?
От: x64 Россия http://x64blog.name
Дата: 24.10.14 22:17
Оценка:
S>И оказалось, что только один кейс проходит — FileStandardInformation...

Нужно обрабатывать все.
Про FileAllInformation не забыл?

S>case FileStandardInformation:

S>standardInfo = (PFILE_STANDARD_INFORMATION)Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
S>// print info.
S>break;

А где обработка?

S>...я все еще не могу найти его.


Обрати внимание на "QueryFileInformation", не тупи.

S>Пробывал...


Пожалуйста, не надо пробывать.

S>...по аналогии как в IRP_MJ_QUERY_INFORMATION сделать, ну естественно уже в IRP_MJ_DIRECTORY_CONTROL пакете, что-то типо такого:


Да где обработка-то?

S>При выводе информации с bothDirInfo выводятся какие-то огромные цифры.


При условии, что адрес буфера правильный:

DbgPrint (
    "Original size = %I64u",
    bothDirInfo -> EndOfFile.QuadPart);


Это в PostQueryDirectory, разумеется. Ну и результат операции-то проверить не забудем, т.е. если в IoStatus.Status не STATUS_SUCCESS и не STATUS_BUFFER_OVERFLOW, то данных просто нет, подменять нечего. Кроме того, в выходном буфере может находиться несколько записей, тебе нужно найти свою (по имени файла, например, или по ID или ещё как), нельзя подменять первую же попавшуюся, которая по адресу в поле .DirectoryBuffer.

S>...иначе ничего не получится?


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

S>Так как пробывал...


Пожалуйста...

S>...EndOfFile из FILE_STANDARD_INFORMATION переписывать, в итоге при открытии файла выводится ошибка: "Not enough storage is available to process this command".


Для начала, исправь грубейшую ошибку в коде, на которую я указал выше. А вообще, конечно, не видя полного исходника и не зная ничего конкретного о ситуации, сказать тут что-либо осмысленное проблематично. Я могу посоветовать только взять Process Monitor и нацелить его на конкретное приложение, в котором возникает данная проблема. Настроив правильно фильтры (и символы, чтобы стеки разглядывать), ты увидишь операцию, на которой произошёл сбой, это даст тебе дополнительную пищу для размышлений. Судя по возникшей ошибке, я могу лишь предположить, что произошло примерно следующее: ты некорректно подменил значение поля EndOfFile, в результате чего оно оказалось настолько большим, что приложение не смогло выделить столько памяти и/или создать секцию такого размера.
JID: x64j@jabber.ru
Re[4]: Можно ли изменить размер файла в режиме ядра?
От: Suares  
Дата: 03.11.14 17:11
Оценка:
Здравствуйте, x64, Вы писали:

x64>Нужно обрабатывать все.

x64>Про FileAllInformation не забыл?

Да, забыл. Уже исправил, спасибо.
Обработка IRP_MJ_QUERY_INFORMATION сейчас у меня выглядит вот так:

FLT_PREOP_CALLBACK_STATUS
SwapPreQueryInformation(
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
{
    FLT_POSTOP_CALLBACK_STATUS status = FLT_PREOP_SYNCHRONIZE;

    UNREFERENCED_PARAMETER(CompletionContext);

    PAGED_CODE();

    if (!IsFileNeedProccessing(&FltObjects->FileObject->FileName))
    {
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    switch (Data->Iopb->Parameters.QueryFileInformation.FileInformationClass)
    {
    case FileAllInformation:
    case FileStandardInformation:

        break;

    default:

        status = FLT_PREOP_SUCCESS_NO_CALLBACK;

        break;
    }

    return status;
}


FLT_POSTOP_CALLBACK_STATUS
SwapPostQueryInformation(
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_opt_ PVOID CompletionContext,
    _In_ FLT_POST_OPERATION_FLAGS Flags
    )
{
    FLT_POSTOP_CALLBACK_STATUS          status = FLT_POSTOP_FINISHED_PROCESSING;
    PFILE_ALL_INFORMATION               allInfo;
    PFILE_STANDARD_INFORMATION          standardInfo;

    UNREFERENCED_PARAMETER(CompletionContext);

    PAGED_CODE();

    switch (Data->Iopb->Parameters.QueryFileInformation.FileInformationClass)
    {
    case FileAllInformation:

        allInfo = (PFILE_ALL_INFORMATION)Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
        allInfo->StandardInformation.EndOfFile.QuadPart = GetDecryptedFileSize(&FltObjects->FileObject->FileName);

        break;

    case FileStandardInformation:

        standardInfo = (PFILE_STANDARD_INFORMATION)Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
        standardInfo->EndOfFile.QuadPart = GetDecryptedFileSize(&FltObjects->FileObject->FileName);

        break;

    default:

        status = FLT_POSTOP_FINISHED_PROCESSING;

        break;
    }

    return status;
}


GetDecryptedFileSize — возвращает оригинальный размер файла, который ранее был записан в хедер.
Это работает, для текстовых файлов вполне все ок. Т.е. открывается то количество байт, которое ранее было присвоено EndOfFile. Но Вы еще писали о том, что нужно в IRP_MJ_DIRECTORY_CONTROL тоже изменить значение в нужной записи:

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


Тут у меня и возникла проблема. В Post процедуре делаю так:

    switch (iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass)
    {
        case FileBothDirectoryInformation:
    
            DbgPrint(
                "SB!SwapPostDirCtrlBuffers:   BothDirInfo: FileName = %wZ.",
                iopb->Parameters.DirectoryControl.QueryDirectory.FileName);
    
            bothDirInfo = (PFILE_BOTH_DIR_INFORMATION)iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
    
            DbgPrint(
                "SB!SwapPostDirCtrlBuffers:   BothDirInfo: AllocSize = %I64u, EndOfFile = %I64u.",
                bothDirInfo->AllocationSize.QuadPart,
                bothDirInfo->EndOfFile.QuadPart);
    
            break;
    }


Я специально оставил только один файл в нужной мне папке. FileName показывает название нужного файла, а вот AllocSize и EndOfFile показывает либо 0, либо огромные числа.
Вот тут нужна подсказка, как правильно обработать кейс в таком случае?
Re[5]: Можно ли изменить размер файла в режиме ядра?
От: x64 Россия http://x64blog.name
Дата: 04.11.14 03:19
Оценка:
S>Я специально оставил только один файл в нужной мне папке. FileName показывает название нужного файла, а вот AllocSize и EndOfFile показывает либо 0, либо огромные числа.

Ты поди буфер-то подменил в PreQueryDirectory, да? А в поле DirectoryBuffer буфер приходит всегда оригинальный, в минифильтрах вообще все параметры в completion routine приходят оригинальные (читать внимательно комментарии в коде), и если моё предположение верно, то в оригинальный буфер тупо ничего не записалось, а записалось всё в твой буфер, там и ищи данные. Решение простое: не надо подменять буфер в PreQueryDirectory, потому что в сэмпле это делается в демонстрационных целях исключительно, никакой реальной необходимости у тебя в этом нет.

S>Вот тут нужна подсказка...


Оценочки не забываем!
JID: x64j@jabber.ru
Re: Можно ли изменить размер файла в режиме ядра?
От: BlackEric http://black-eric.lj.ru
Дата: 04.11.14 09:23
Оценка:
Здравствуйте, Suares, Вы писали:

S>Сначала опишу, что я хочу сделать. Допустим у нас на компьютере есть папка E:\Data\Private, где будут лежать секретные файлы, содержащие схемы по отмыванию денег Так как они являются очень секретными, то и просматривать их хочу только я. Т.е. при сохранении файла в эту папку он будет шифроваться, а при чтении расшифровываться.


S>Решение придумал такое, что есть некое приложение, через которое можно будет открыть эту папку. И в тот момент когда происходит открытие папки, запускался драйвер, который будет перехватывать запросы по чтению/записи к E:\Data\Private.


S>За основу взял драйвер с WDK: SwapBuffer File System Minifilter Driver. Добавил только возможность пересылать массив символов с драйвера в мое приложение, где собственно будет шифрование/расшифрование данных. Алгоритм шифрования использую AES.


А почему нельзя взять старый добрый TrueCrypt или что там принято вместо него сейчас использовать? Мне кажется вы изобретаете велосипед. Хотя поковыряться, если есть время в такие вещах очень интересно
https://github.com/BlackEric001
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.