Вопрос

Я уже некоторое время исследую способ предотвратить случайный вход моего пользователя в каталог данных моего приложения.

Мое приложение использует папку для хранения структурированного проекта.Внутренняя структура папки является критичной, и ее не следует портить.Я бы хотел, чтобы мой пользователь видел эту папку целиком и не мог ее открыть (например, пакет Mac).

Есть ли способ сделать это в Windows?

Редактировать из текущих ответов

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

Спасибо всем за ответы по .Net, но, к сожалению, это в основном проект на C++ без какой-либо зависимости от платформы .Net.

Данные, которые я упоминаю, не легкие, это изображения, полученные с электронного микроскопа.Эти данные могут быть огромными (от ~100 МБ до ~1 ГБ), поэтому загружать все в память невозможно.Это огромные изображения, поэтому хранилище должно обеспечивать возможность поэтапного чтения данных путем доступа к одному файлу за раз без загрузки всего архива в память.

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

Расширение Shell выглядит интересно, я буду исследовать решение дальше.

ЛарриФ, можете ли вы рассказать подробнее о драйвере фильтра или DefineDOSDevice?Я не знаком с этими понятиями.

Это было полезно?

Решение 9

Похоже на некоторые Порты FUSE для Windows начинают появляться.Я думаю, что это было бы лучшим решением, поскольку оно позволило бы мне сохранить нетронутым устаревший код (который довольно большой).

Другие советы

Есть пара вещей, которые вы могли бы сделать:

Во-первых, вы можете создать расширение оболочки Windows FolderView, которое создаст собственное представление для вашей важной папки.Создав собственный FolderView, вы можете сделать папку просто пустой и белой с одной строкой текста «Здесь нечего видеть», или вы можете сделать что-то более сложное, например средство просмотра GAC, которое использует тот же метод.Этот метод будет довольно сложным, но эту сложность можно уменьшить, используя что-то вроде этот кодпроект библиотека статьи в качестве основы.

Другим решением было бы создать виртуальную файловую систему ZIP. Это потребует от вас замены любого кода, который напрямую использует System.IO, на использование чего-то другого.ASP.NET 2.0 сделал это именно по этой причине, и вы можете довольно легко построить на его основе, взгляните на это Статья MSDN при реализации VirtualPathProvider.

Структурированное хранилище был разработан для сценария, который вы описываете:

Структурированное хранилище обеспечивает сохранение файлов и данных в COM, обрабатывая один файл как структурированную коллекцию объектов, известных как хранилища и потоки.

«Хранилище» аналогично папке, а «поток» — файлу.По сути, у вас есть один файл, который при доступе с помощью API структурированного хранилища ведет себя и выглядит как полноценная автономная файловая система.

Однако обратите внимание, что:

Глубокое понимание технологий COM является предпосылкой для разработки использования структурированного хранилища.

Внутри или за пределами вашей программы?

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

Если вы воспользуетесь подходом к ZIP-файлу (который я рассматривал для вас, но не упомянул об этом), я бы предложил использовать алгоритм выкачивания, но использовать свою собственную файловую систему...Посмотрите на что-то вроде формата TAR.Затем просто напишите свой код, который будет передавать ВСЕ операции ввода-вывода мимо алгоритмов Inflate/Deflate по мере их записи на диск.Я бы не стал использовать ZIP-файл «ФОРМАТ», так как слишком легко просмотреть файл, найти PK в первых двух байтах и ​​разархивировать файл....

Мне больше всего нравятся предложения Джошперри.

Конечно, вы также можете написать драйвер устройства, который будет хранить все ваши данные в одном файле, но опять же, мы рассматриваем драйвер.(Я не уверен, что вы могли бы реализовать это вне драйвера..Вы, ВЕРОЯТНО, можете внутри своей программы вызвать DefineDOSDevice, дав ему имя, к которому имеет доступ только ваш код, и оно будет рассматриваться как обычная файловая система.).Я поиграю с некоторыми идеями, и если они сработают, я сниму вам образец.Теперь вы меня заинтересовали.

Вы можете поместить каталог вашего проекта в файл .zip и хранить там свои данные, так же, как используется .jar (я знаю, что .jar в значительной степени предназначен только для чтения, это для примера).Сделать нестандартное расширение, чтобы двойной щелчок не имел немедленного эффекта, готово.;-)

Конечно, это означает, что вам придется обернуть весь ввод-вывод файла, чтобы вместо этого использовать .zip, в зависимости от того, как построена ваша программа, это может быть утомительно.Это уже сделано для Java: TrueZip.Может быть, вы можете использовать это как вдохновение?

Если у вас возник соблазн - я бы не советовал возиться с правами доступа к папкам, по понятным причинам это не поможет.

Вы можете использовать изолированное хранилище.

http://www.ondotnet.com/pub/a/dotnet/2003/04/21/isolatedstorage.html

Это не решает всех проблем, но надежно защищает данные приложений.

Иметь ввиду:если вы сохраните его в файловой системе, пользователь ВСЕГДА сможет его увидеть.Вмешайтесь в проводник, и вместо этого я использую cmd.exe.Или Тотал Коммандер.Или что-нибудь еще.

Если вы не хотите, чтобы люди портили ваши файлы, я бы порекомендовал

  • шифрование их, чтобы предотвратить подделку файлов
  • помещение их в архив (т.е.ZIP), возможно, защитив его паролем, а затем сжимая/распаковывая во время выполнения (я бы поискал алгоритмы, которые быстро изменяют архивы)

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

Конечно, вы никогда не сможете полностью контролировать файлы на компьютере пользователя, не контролируя сам компьютер.

Я видел программное обеспечение (Agilian от Visual Paradigm), которое использовало предложение Томалака о zip-архиве в качестве «файла проекта».Zip-файлы хорошо понятны, а использование нестандартного расширения файла не позволяет обычному пользователю возиться с «файлом».Большим преимуществом этого является то, что в случае повреждения для устранения проблемы можно использовать стандартные инструменты, и вам не придется беспокоиться о создании специальных инструментов для поддержки вашего основного приложения.

Я рад слышать, что вы делаете это на C++.Кажется, никто больше не считает C++ «необходимым».Это все C# и ASP.NET то...Даже я работаю в доме, где весь C#, хотя я поклялся, что буду никогда переключитесь, поскольку C++ делает все, что мне когда-либо понадобится, и даже больше.Я достаточно взрослый, чтобы очистить свою память!хех..В любом случае, вернемся к обсуждаемому вопросу...

А DefineDOSDevice() — это метод, который вы используете для назначения букв дисков, имен портов (LPT1, COM1 и т. д.).Вы передаете ему имя, некоторые флаги и «путь», который обрабатывает это устройство.Но не позволяйте этому обмануть вас.Это не путь к файловой системе, это путь к объекту NT.Я уверен, что вы видели их как «\Device\HardDisk0» и т. д.Вы можете использовать WinObj.exe из sysinternals, чтобы понять, что я имею в виду.В любом случае, вы можете создать драйвер устройства, а затем указать на него символическую ссылку MSDOS, и все готово.Но, конечно, это кажется большой работой над исходной проблемой.

Сколько из этих файлов от мегабайт до гигабайт находится в обычном каталоге?Возможно, вам лучше всего просто поместить все файлы в один гигантский файл и сохранить рядом с ним индексный файл (или заголовок каждого файла), который указывает на следующий «Файл» внутри вашего файла «виртуальной файловой системы».

Хорошим примером может быть формат архива Microsoft MSN.Я изменил этот формат архива, когда работал в AV-компании, и это на самом деле довольно креативно, но ОЧЕНЬ просто.Все это можно сделать в одном файле, а если вы хотите пофантазировать, вы МОГ храните данные в трех файлах в конфигурации типа RAID 5, поэтому, если какой-либо из трех файлов будет поврежден, вы МОЖЕТЕ восстановить остальные.Кроме того, пользователи увидят только 3 ОЧЕНЬ большие файлы в каталоге и не смогут получить доступ к отдельным (внутренним) файлам.

Я предоставил вам код, который распаковывает один из этих форматов архива MSN.У меня нет кода, который СОЗДАЕТ его, но из исходного кода вы сможете без проблем создать/написать его.Если файлы часто удаляются и/или переименовываются, это может вызвать проблемы с используемым пространством в файле, которое придется время от времени сокращать.

Этот формат даже поддерживает поля CRC, поэтому вы можете проверить, все ли в порядке с файлом.Мне так и не удалось полностью отменить алгоритм, который Microsoft использовала для CRC данных, но у меня есть довольно хорошая идея.

Вы не сможете сохранить текущие процедуры ввода-вывода, а это означает, что CreateFile() не сможет просто открыть любой файл в архиве, однако, благодаря сверхкрутости C++, вы можете переопределить вызов CreateFile для реализации формат вашего архива.

Если вам нужна помощь с его проблемой, а это достаточно большая проблема, возможно, мы могли бы поговорить в автономном режиме и найти для вас решение.

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

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

Прежде чем приступить к изучению драйвера устройства, загрузите WinDDK.Там повсюду образцы драйверов.

Если вам интересно, почему меня это так волнует, то это потому, что в течение многих лет у меня было в планах написать подобный драйвер, который должен был быть для Windows. И Совместимость с OSX, что позволит пользователям защищать тома дисков (USB-ключи, съемные тома) БЕЗ установки каких-либо драйверов или сложного (и громоздкого, иногда раздражающего) программного обеспечения.В последние годы многие производители оборудования сделали подобные вещи, но я не думаю, что безопасность настолько надежна.Я рассматриваю возможность использования RSA и AES, точно так же, как работают GPG и PGP.Первоначально со мной связались по поводу того, что (я верю, но не имею доказательств) будет использоваться для защиты файлов MP3.Поскольку они будут храниться в зашифрованном формате, они просто не будут работать без правильной парольной фразы.Но я видел и другое применение этому средству.(Это было тогда, когда USB-ключ на 16 МБ (да, МЭГ) стоил более 100 долларов или около того).

Этот проект также сопровождался моей системой безопасности ПК для нефтегазовой отрасли, в которой использовалось что-то похожее на смарт-карты, только гораздо проще в использовании, повторное использование/перевыпуск невозможно (читай:ОЧЕНЬ сложно и маловероятно) взломать, и я мог бы использовать это на своих детях дома!(Поскольку всегда идет борьба за то, кто получит больше времени за компьютером, а кто больше, и так далее, и дальше, и дальше, и...)

Уф..Мне кажется, я здесь отклонился от темы.В любом случае, вот пример формата архива Microsoft MSN.Посмотрите, сможете ли вы использовать что-то подобное, зная, что вы всегда можете «перейти» прямо к файлу, следуя смещениям в файле при анализе/поиске запрошенного файла в главном файле;или в предварительно проанализированных данных, хранящихся в памяти.А поскольку вы не будете загружать необработанные данные двоичного файла в память, вашим единственным ограничением, вероятно, будет ограничение размера файла в 4 ГБ на 32-битных машинах.

Формат MARC (Microsoft MSN Archive) выглядит (примерно) следующим образом:

  • 12-байтовый заголовок (только один)
    • Файловая магия
    • MARC-версия
    • Количество файлов (в следующей таблице)
  • 68-байтовые заголовки таблиц файлов (от 1 до Header.NumFiles из них)
    • Имя файла
    • Размер файла
    • Контрольная сумма
    • смещение необработанных данных файла

Теперь в 12-байтовых записях таблицы файлов 32 бита используются для длины файла и смещений.Для ваших ОЧЕНЬ больших файлов вам, возможно, придется увеличить длину целых чисел до 48 или 64 бит.

Вот код, который я написал для их обработки.

#define MARC_FILE_MAGIC         0x4352414D // In Little Endian
#define MARC_FILENAME_LEN       56 //(You'll notice this is rather small)
#define MARC_HEADER_SIZE        12
#define MARC_FILE_ENT_SIZE      68

#define MARC_DATA_SIZE          1024 * 128 // 128k Read Buffer should be enough.

#define MARC_ERR_OK              0      // No error
#define MARC_ERR_OOD             314    // Out of data error
#define MARC_ERR_OS              315    // Error returned by the OS
#define MARC_ERR_CRC             316    // CRC error

struct marc_file_hdr
{
    ULONG            h_magic;
    ULONG            h_version;
    ULONG            h_files;
    int              h_fd;
    struct marc_dir *h_dir;
};

struct marc_file
{
    char            f_filename[MARC_FILENAME_LEN];
    long            f_filesize;
    unsigned long   f_checksum;
    long            f_offset;
};

struct marc_dir
{
    struct marc_file       *dir_file;
    ULONG                   dir_filenum;
    struct marc_dir        *dir_next;
};

Это дает вам представление о заголовках, которые я для них написал, а вот функция open.Да, здесь отсутствуют все звонки в службу поддержки, процедуры устранения ошибок и т. д., но суть вы поняли.Пожалуйста, извините за смесь стилей кода C и C++.Наш сканер представлял собой кластер из множества различных проблем, подобных этой...Я использовал устаревшие вызовы, такие как open(), fopen(), чтобы соответствовать стандартам остальной части кода.

struct marc_file_hdr *marc_open(char *filename)
{
    struct marc_file_hdr *fhdr  = (struct marc_file_hdr*)malloc(sizeof(marc_file_hdr));
    fhdr->h_dir = NULL;

#if defined(_sopen_s)
    int errno = _sopen_s(fhdr->h_fd, filename, _O_BINARY | _O_RDONLY, _SH_DENYWR, _S_IREAD | _S_IWRITE);
#else
    fhdr->h_fd = open(filename, _O_BINARY | _O_RDONLY);
#endif
    if(fhdr->h_fd < 0)
    {
        marc_close(fhdr);
        return NULL;
    }

    //Once we have the file open, read all the file headers, and populate our main headers linked list.
    if(read(fhdr->h_fd, fhdr, MARC_HEADER_SIZE) != MARC_HEADER_SIZE)
    {
        errmsg("MARC: Could not read MARC header from file %s.\n", filename);
        marc_close(fhdr);
        return NULL;
    }

    // Verify the file magic
    if(fhdr->h_magic != MARC_FILE_MAGIC)
    {
        errmsg("MARC: Incorrect file magic %x found in MARC file.", fhdr->h_magic);
        marc_close(fhdr);
        return NULL;
    }

    if(fhdr->h_files <= 0)
    {
        errmsg("MARC: No files found in archive.\n");
        marc_close(fhdr);
        return NULL;
    }

    // Get all the file headers from this archive, and link them to the main header.
    struct marc_dir *lastdir = NULL, *curdir = NULL;
    curdir = (struct marc_dir*)malloc(sizeof(marc_dir));
    fhdr->h_dir = curdir;

    for(int x = 0;x < fhdr->h_files;x++)
    {
        if(lastdir)
        {
            lastdir->dir_next = (struct marc_dir*)malloc(sizeof(marc_dir));
            lastdir->dir_next->dir_next = NULL;
            curdir = lastdir->dir_next;
        }

        curdir->dir_file = (struct marc_file*)malloc(sizeof(marc_file));
        curdir->dir_filenum = x + 1;

        if(read(fhdr->h_fd, curdir->dir_file, MARC_FILE_ENT_SIZE) != MARC_FILE_ENT_SIZE)
        {
            errmsg("MARC: Could not read file header for file %d\n", x);
            marc_close(fhdr);
            return NULL;
        }
        // LEF: Just a little extra insurance...
        curdir->dir_file->f_filename[MARC_FILENAME_LEN] = NULL;

        lastdir = curdir;
    }
    lastdir->dir_next = NULL;

    return fhdr;
}

Затем у вас есть простой метод извлечения.Имейте в виду, что это было сделано исключительно для сканирования на вирусы, поэтому здесь нет никаких процедур поиска и т. д.Это было сделано для того, чтобы просто выгрузить файл, отсканировать его и двигаться дальше.Ниже приведена процедура кода CRC, которую, Я ВЕРЮ, использовала Microsoft, но я не уверен. ЧТО именно они CRC'ed.Он может включать данные заголовка + данные файла и т. д.Я просто не настолько заинтересован, чтобы вернуться и попытаться повернуть это вспять.В любом случае, как видите, в этом формате архива нет сжатия, но оно есть. ОЧЕНЬ легко добавить.При желании могу предоставить полный исходник.(Я думаю, что осталась только процедура close() и код, который вызывает и извлекает каждый файл и т. д.!!)

bool marc_extract(struct marc_file_hdr *marc, struct marc_file *marcfile, char *file, int &err)
{
    // Create the file from marcfile, in *file's location, return any errors.
    int ofd = 0;
#if defined(_sopen_s)
     err = _sopen_s(ofd, filename, _O_CREAT | _O_SHORT_LIVED | _O_BINARY | _O_RDWR, _SH_DENYNO, _S_IREAD | _S_IWRITE);
#else
    ofd = open(file, _O_CREAT | _O_SHORT_LIVED | _O_BINARY | _O_RDWR);
#endif

    // Seek to the offset of the file to extract
    if(lseek(marc->h_fd, marcfile->f_offset, SEEK_SET) != marcfile->f_offset)
    {
        errmsg("MARC: Could not seek to offset 0x%04x for file %s.\n", marcfile->f_offset, marcfile->f_filename);
        close(ofd);
        err = MARC_ERR_OS; // Get the last error from the OS.
        return false;
    }

    unsigned char *buffer = (unsigned char*)malloc(MARC_DATA_SIZE);

    long bytesleft = marcfile->f_filesize;
    long readsize = MARC_DATA_SIZE >= marcfile->f_filesize ? marcfile->f_filesize : MARC_DATA_SIZE;
    unsigned long crc = 0;

    while(bytesleft)
    {
        if(read(marc->h_fd, buffer, readsize) != readsize)
        {
            errmsg("MARC: Failed to extract data from MARC archive.\n");
            free(buffer);
            close(ofd);
            err = MARC_ERR_OOD;
            return false;
        }

        crc = marc_checksum(buffer, readsize, crc);

        if(write(ofd, buffer, readsize) != readsize)
        {
            errmsg("MARC: Failed to write data to file.\n");
            free(buffer);
            close(ofd);
            err = MARC_ERR_OS; // Get the last error from the OS.
            return false;
        }
        bytesleft -= readsize;
        readsize = MARC_DATA_SIZE >= bytesleft ? bytesleft : MARC_DATA_SIZE;
    }

    // LEF:  I can't quite figure out how the checksum is computed, but I think it has to do with the file header, PLUS the data in the file, or it's checked on the data in the file
    //       minus any BOM's at the start...  So, we'll just rem out this code for now, but I wrote it anyways.
    //if(crc != marcfile->f_checksum)
    //{
    //    warningmsg("MARC: File CRC does not match.  File could be corrupt, or altered.  CRC=0x%08X, expected 0x%08X\n", crc, marcfile->f_checksum);
    //    err = MARC_ERR_CRC;
    //}

    free(buffer);
    close(ofd);

    return true;
}

Вот МОЯ предполагаемая процедура CRC (возможно, я украл ее у Стюарта Кея и libmspack, не могу вспомнить):

static unsigned long marc_checksum(void *pv, UINT cb, unsigned long seed)
{
    int count = cb / 4;
    unsigned long csum = seed;
    BYTE *p = (BYTE*)pv;
    unsigned long ul;

    while(count-- > 0)
    {
        ul = *p++;
        ul |= (((unsigned long)(*p++)) <<  8);
        ul |= (((unsigned long)(*p++)) << 16);
        ul |= (((unsigned long)(*p++)) << 24);
        csum ^= ul;
    }

    ul = 0;
    switch(cb % 4)
    {
        case 3: ul |= (((unsigned long)(*p++)) << 16);
        case 2: ul |= (((unsigned long)(*p++)) <<  8);
        case 1: ul |= *p++;
        default: break;
    }
    csum ^= ul;

    return csum;
}                                                                                     

Ну, думаю, этот пост уже достаточно длинный...Свяжитесь со мной, если вам нужна помощь или есть вопросы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top