Создание файлов с некорректными именами в FAT32 и NTFS
Статья написана для журнала "Хакер" в 2004 году. Она вышла в номере 02/04 (62).
Во времена операционной системы MS-DOS и файловой системы FAT16 существовали серьезные ограничения, касающиеся имен файлов. Так, максимальная длина имени файла составляла 8 символов, а расширения – 3 символа. С появлением Windows 95, максимальная длина имени файла увеличилась до 255 символов, и теперь нам чаще всего не приходится гадать, что скрывается в файле с названием MIAF9D~1.ZIP. В новых файловых системах FAT32 и NTFS с тех времен остались другие, менее заметные ограничения, которые можно обходить и использовать в своих целях.
Запрещенные символы
Правила, касающиеся имен файлов, содержатся в так называемых «Соглашениях об именах файлов» (Filename Conventions). В этом документе описано, какие символы допустимо использовать в названиях файлов, какие символы являются разделителями пути, максимальная длина пути, и т.п. Здесь же оговариваются и ограничения. К примеру, символы “\”, “/”, “?”, “|”, “*”, “” и “:” имеют специальное значение в Windows при операциях с файлами, в частности, из командной строки, и поэтому не могут быть использованы в имени отдельного файла. Это ограничение, по-видимому, обойти невозможно, так как при обращении к системным функциям для работы с файлами Windows стопроцентно выделяет их среди других символов и по-своему интерпретирует. Здесь нужно обратить внимание на специфическое использование символов точки «.», двоеточия «:», и пробела. Символ пробела может встречаться в имени файла или каталога, точка используется как разделитель имени файла от расширения, а двоеточие – как разделитель между буквой диска и остальной частью пути. Использование двоеточия не допускается нигде, кроме как после буквы диска, с исключением для файловой системы NTFS, где двоеточие используется еще и в качестве разделителя между нормальным именем файла и прикрепленными к нему файловыми потоками. Точка и пробел могут стоять в любом месте в имени файла, но не могут быть завершающими символами. Это странное, на первый взгляд, ограничение существует, как объясняет Microsoft, ради совместимости новых файловых систем со старыми, такими как HPFS, используемой в OS/2 и FAT16. Я думаю, что это ограничение частично связано с двумя существующими во всех используемых в Windows файловых системах виртуальными файловыми объектами (так называемые «точки»). При работе с файловыми менеджерами типа TotalCMD, для перехода в предыдущую папку надо щелкать по каталогу с названием «..». В файловых системах так обозначается родительский каталог относительно текущего пути, а текущая папка обозначается как «.». Строго говоря, эти объекты не являются настоящими файлами или каталогами. Это просто абстрактные объекты, используемые по традиции для навигации между папками. В Windows Explorer они вообще не показываются. Так как пользователь может создавать файлы, имена которых начинаются с точки (но не в Windows Explorer), то Microsoft заблокировала возможность ставить точки в конце названия, чтобы было невозможно создать файл «..». Чем мелкомягких не устраивают пробелы в конце названия, мне не понятно.
Имена DOS-устройств
В каждой Windows системе существует эмуляция MS-DOS, и этот факт тоже накладывает свои ограничения. При работе в командной строке используются псевдонимы для устройств, работа с которыми ничем не отличается от работы с обычными файлами. Под устройства зарезервированы следующие имена файлов: AUX, CON, NUL, PRN, COM1-COM9 и LPT1-LPT9. Простейший пример работы с этими объектами: если в командной строке ввести «dir > prn | sort», то отсортированный список файлов и каталогов текущей папки начнет распечатываться из принтера. Здесь «prn» означает принтер. Понятно, что если бы существовала возможность называть файлы зарезервированными именами устройств, то возникла бы путаница, поэтому эта возможность заблокирована.
Способы обхода ограничений
Мне известно три способа обхода описанных ограничений. Общий принцип их действия таков: определенным образом составляется название файла, после чего оно передается какой-либо системной функции для работы с файлами. В результате этого алгоритм проверки параметра на корректность не срабатывает, и мы получаем нужный результат - файл или каталог с некорректным с точки зрения системы названием. Какие это открывает возможности, я опишу позже. А пока поговорим о самих способах. Некоторые способы можно использовать не только программно, но и на пользовательском уровне. Способ первый: Использование UNC-путей. Это, на мой взгляд, самый простой и удобный способ. Разберем его на примере создания файла с точкой в конце названия. При его создании мы будем использовать стандартные функции для работы с файлами, но при этом мы будем указывать полный путь до объекта, и добавлять в начале пути четыре символа "\\?\" или "\\.\". Получится примерно следующее: "\\?\f:\test\prn". Дальше работаем с файлом как обычно, то есть мы можем писать в него, читать из него, копировать, удалять и делать все остальное, используя обычные функции. Надо только не забывать, что везде, где требуется имя файла, необходимо указывать полный путь с UNC-префиксом.
Тестирование показало, что использование префикса "\\?\" более надежно, чем "\\.\". При использовании второго префикса, к примеру, можно потерпеть неудачу при попытке удаления файла. Этот способ еще хорош тем, что работает и в командной строке. Действительно, возможны манипуляции с файлами прямо из командной строки, не прибегая к каким либо языкам программирования. Набранная в командной строке команда "type \\?\f:\test\prn" отобразит содержимое созданного файла. Пример создания файла
Этот способ тоже можно использовать в командной строке, но только с каталогами. Если набрать в ней строку
А что мне с этого?
Создай файл или каталог с некорректным именем, используя любой из вышеописанных способов. Теперь попробуй сделать с ним то, что каждый день делаешь с другими файлами и каталогами. Попробуй скопировать файл, переместить его, переименовать, открыть его любой программой, удалить, наконец.
Ну что, получилось что-нибудь? Вряд ли, потому что при попытке доступа к файлу система использует те же самые функции, которые используем мы, но, в отличие от нас, система не знает наших хитрых способов, и поэтому получается так, что система фактически блокирует сама себя. Ну как, может сам догадаешься, какие возможности это предлагает? Первое что приходит в голову - это блокировка доступа к секретной информации. Конечно, это не так надежно, как шифрование, но что мешает тебе зашифровать какой либо файл, и, для верности, еще и закрыть к нему доступ, задав некорректное имя. Кроме того, мало кто сможет скопировать такой файл, что бы подобрать позже пароль в спокойной обстановке. Есть возможность вообще спрятать файл так, что его не будет видно. Способ работает только с файловыми системами FAT\FAT32. Если переименовать существующий файл, содержащий информацию, задав ему имя <..>, то файл перестает быть видимым в Проводнике. Соответственно найти невидимый файл будет довольно сложно. Файл можно увидеть только из командной строки, или в файловых менеджерах. Однако если файл создать в корневом каталоге диска, то файловые менеджеры его тоже не видят. Еще один возможный трюк: создается папка с таким же именем, и тоже невидимая. Суть в том, что в этой папке, используя способ №1 можно создавать файлы! В эту папку можно сохранять какие либо файлы, а чтобы получить к ним доступ, нужно как минимум знать их имена. Средствами Windows получить список файлов из этой папки невозможно, и получается защита не хуже архива с паролем!
Другое возможное использование - в западлостроении. Ничто не мешает нам создать на компе ламера некоторое количество файлов, размером по 4Гб каждый (если у ламера FAT32), или один большой файл любого размера (если NTFS), и переименовать их, ну, например, в имена DOS-устройств. Или можно сделать их невидимыми. В любом случае, эти файлы удаляются неочевидным способом, и поэтому большинству людей, скорее всего, придется форматировать винт и переустанавливать систему (если винт содержит только один раздел). Кстати, scandisk и другие подобные утилиты, почему-то не видят ничего странного в файлах с такими названиями.
Наверное, каждый программист хоть раз в жизни пробовал писать вирус или троян. Естественно, чтобы вирус был настоящим вирусом, нужно намертво закрепить его в системе, а так же принять меры к тому, чтобы его как можно дольше не обнаружил антивирус. Я попытался использовать некорректные имена файлов в этих целях. Я создал на винте новую папку и поместил в него файл, содержащий тестовый вирус EICAR. Это вирус не приносит никакого вреда, и создан специально для тестирования антивирусов. Затем я просканировал папку с помощью Norton Antivirus. NAV правильно определил вирус и предложил отправить его в карантин. Я отказался, а вместо этого сделал вот что: я переименовал файл
Файловые потоки NTFS
Файловая система NTFS поддерживает так называемые "альтернативные файловые потоки" (Alternate Data Streams) во всех версиях NTFS. Эта технология позволяет прикреплять к файлу, расположенному на томе с NTFS другие файлы (называемые потоками), содержащие любые данные. Прикрепленный к файлу поток не виден ни из проводника, ни из командной строки. Путь к потоку, относительно файла, к которому он прикреплен, выглядит так: "file.ext:stream". Допускается также такой синтаксис для доступа к потоку: "file.ext:stream:$DATA". Более того, главный файл, к которому прикреплены потоки, сам может рассматриваться в качестве потока. В этом случае путь будет выглядеть так: <:>. Потоки широко используются системой для хранения какой либо служебной информации о файле, к примеру сводки документа. Атрибуты файла или каталога тоже хранятся в потоке с названием $Attribute_List. Вообще, символ "$" в названии потока говорит о том, что он каким то образом используется системой.
Universal Naming Convention
Universal Naming Convention - сокращенно UNC, можно дословно перевести как "Универсальное Соглашение об Именах", это формат для записи пути к файлу расположенному на удаленном компьютере. Он имеет вид "\\server\share\path". Server это, как ни странно сервер, share - это расшаренный ресурс на нем, а дальше следует путь к файлу в обычном формате. Такой способ доступа к файлам можно использовать и для локальной машины, только в этом случае вместо "server" нужно подставлять "?" или ".", а путь к файлу указывать вместе с буквой диска. Например так: "\\?\C:\folder\file.txt".
Листинг 1.
#include#include void CreateStrangeFile(char *filename) { char *curdir; //текущая папка char *uncpath; //полный путь до файла в формате UNC GetCurrentDirectory(MAX_PATH,curdir); //получаем текущий каталог wsprintf(uncpath,"\\\\?\\%s\\%s",curdir,filename); //формируем UNC-путь //создаем новый файл HANDLE hFile = CreateFile( uncpath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, NULL, NULL); DWORD ret; //записываем секретную инфу WriteFile(hFile,"This is a super secret info",28,&ret, NULL); CloseHandle(hFile); } int main() { CreateStrangeFile("prn"); //создаем файл }
Листинг 2.
#includeint main() { //переменные для нового и старого имени файла char *old = "C:\\TMP\\somefile.txt"; char *_new = "C:\\TMP\\twodots.."; char newname[1024]; //добавляем символы <.> в новое имя файла wsprintf(newname,"%s.\\",_new); //переименовываем файл MoveFile(old,newname); }
Листинг 3.
#includevoid main() { HANDLE hStream = CreateFile( "space :stream",GENERIC_WRITE,FILE_SHARE_WRITE, NULL,CREATE_ALWAYS,NULL,NULL ); DWORD ret; WriteFile( hStream, "This is space :stream", 21, &ret, NULL ); CloseHandle(hStream); }
Информация к размышлению
Microsoft Knowledge Base: support.microsoft.com Q320081, Q315226, Q115827, Q303074, Q120716.
По теме NTFS также есть следующее:
- Специальные файлы NTFS. О файлах типа $Volume, $MFT и подобных.
- Использование NtFsControlFile для получение информации о файле на NTFS
- Предварительный взгляд на возможности файловой системы Protogon.
Автор: амдф
Дата: 2004 г.
Избранное
Остальное
По вопросам сотрудничества и другим вопросам по работе сайта пишите на cleogroup[собака]yandex.ru