Simple Solve
Пока что сырой и не окончательно обдуманный фреймворк для
автоматического анализа чего бы то ни было. Изначально
задумывался для автоматического решения CTF-задач.
Схож с концепцией Dataflow Programming, но, в отличие от нее,
граф потока данных создается "на лету".
Принцип
Для начала, базовые определения:
- Узел - черный ящик, принимающий на вход одни данные и порождающий на выходе другие
- Токен - единица данных, передаваемая между узлами. Помимо самих данных содержит
так же информацию об их типе и показатель достоверности.
И перейдем сразу к примеру как оно работает.
Допустим, нам даны:
- Узел, принимающий на вход произвольный файл,
и возвращающий zip-архив, если этот файл является zip-архивом.
- Узел, принимающий на вход zip-архив и возврашающий его содержимое, по токену на каждый файл
- Токен типа "файл", представляющий zip-архив с произвольными данными
Архив что мы дали на вход будет рекурсивно распакован.
Мы можем написать множество независимых модулей, каждый из которых будет так или иначе
"доставать" из одних входных данных другие. Будь то, скажем, exif-данные изображений или
адреса электронной почты. Очевидно применение такого подхода в анализе чего-либо.
Скажем, анализ малвари, решение CTF-задач в категориях forensics, stegano и recon.
В принципе, даже анализ социального графа весьма красиво укладывается в эту парадигму.
Сложности
Релевантность
В некоторых случаях можно говорить о релевантности и достоверности данных.
Если нас она не интересует, будем всегда считать ее равной единице.
- Порождая токен, узел сам задает степень его релевантности
- Данные с меньшей релевантностью обрабатываются с меньшим приоритетом
- Данные с релевантностью ниже пороговой не обрабатываются. Откуда же им взяться?
-- Из комбинации двух и более токенов с низкой релевантностью (см. ниже)
Множественные входы
Базовый случай, когда все узлы имеют ровно по одному количеству входов, реализуется
очевидным способом, но функциональность такого подхода была бы сильно ограничена.
-
Узел может принимать фиксированное число токенов на вход. В таком случае
в дело вступает комбинаторика, а вместе с ней заметное проседание производительности,
во всяком случае, если решать задачу "в лоб" - при появлении нового токена, для каждого
узла проверяем, является ли он одним из необходимых, и подбираем далее все возможные сочетания
других, уже существующих токенов.
-
Узел может принимать бесконечное число токенов. Пока не ясно что делать и как быть:
- Решать задачу через внутреннее состояние узла. Узел может хранить ранее поступившие токены,
что по идее и значит их бесконечное объединение. Однако он никогда не узнает какой из них последний
- Создавать специальные узлы, получающие на вход все существующие токены заданного типа при появлении
каждого очередного. То есть выполняться они будут много раз. В таком случае хорошо бы реализовать
возможность де-актуализации веток обработки, в которых фигурировали предыдущие результаты работы узла.
Как бы стремно это не было, такой подход наиболее очевиден и надежен.
- Аналогично предыдущему варианту, но активировать узел только когда больше нечего делать.
- Аналогично предыдущему, но активировать подобно обычному узлу, при поступлении токена определенного типа,
не равного типу объединения. В принципе, такой подход более универсален чем способ выше.
- Объединение по запросу (см. ниже)
Ленивые данные
Эквивалентно активации узлов по запросу. Достаточно обернуть Token.data
в LazyPromise
.
Система типов токенов
В базовом случае тип токена - строка. Но что если структура такова, что данные
могут представлять собой сразу несколько типов? Аналогично множественному наследованию в
программировании.
Решения этого вопроса, не требующие добавления отдельной функциональности в ядро:
- Мы можем просто дублировать одни и те же данные в виде различных токенов с разными типами.
Но в этой мешанине будет легко запутаться, особенно если структура такова, что многие узлы
порождают расширенную версию того же токена.
- При расширении токена новым типом, можно порождать только само надслоение как отдельный токен.
В таком случае мы излишне нагружаем систему множественных входов
Скорее всего имеет смысл решить это как-то.
Персистентность
Если узлы не имеют внутреннего состояния, то общее состояние в каждый момент времени
характеризуется набором токенов и необработанных ребер.
Было бы неплохо добавить возможность сохранения и загрузки состояния для прерывания
и продолжения выполнения. Просто потому что это, в целом, возможно и ничего не мешает.
Параллельность
Если
- Узлы не имеют внутреннего состояния
- Токены сериализуемы
- Токены не изменяются
То ничего не мешает распараллелить исполнение на несколько потоков или даже машин.