peepdf помогает разобрать подозрительный PDF до уровня объектов, потоков, ссылок и встроенного JavaScript: пользователь получает сводку структуры, находит действия открытия и вложения, сравнивает ревизии документа, смотрит сырые и декодированные данные, извлекает нужный фрагмент и сохраняет результаты анализа без запуска содержимого в программе чтения PDF.
Работа строится вокруг командной строки и интерактивного приглашения PPDF>. Сначала файл разбирается автоматически, после чего номера объектов из сводки проверяются командами tree, info, object, stream, references и search. Такой порядок не заставляет просматривать сотни элементов подряд: исследователь идёт от отмеченного признака к его словарю, потоку и месту в логической структуре.
Наиболее полезен peepdf там, где обычный просмотр страниц ничего не объясняет: документ может содержать действие /OpenAction, сценарий в /JavaScript, зашифрованный или сжатый поток, внедрённый файл, несколько последовательных обновлений либо намеренно повреждённую таблицу ссылок. Инструмент показывает эти конструкции текстом и позволяет отделить видимое содержимое от механики, срабатывающей при открытии.
Скачать peepdf
- Редактирование PDF
- Русский интерфейс
- Просто новичкам
- Нет графического интерфейса
- Нужен Python 2
- PyV8 ставится отдельно
Как читать первый отчёт peepdf
При запуске с именем документа peepdf сначала выводит паспорт разобранного файла. В верхней части находятся имя, контрольные суммы, размер, объявленная версия PDF, признаки бинарных байтов, линеаризации и шифрования. Эти строки нужны не для формальной справки. Хеш фиксирует именно тот образец, который исследуется; размер помогает заметить подмену файла; флаг Encrypted предупреждает, что часть объектов станет доступна только после расшифровки; значение Updates показывает число добавочных ревизий, которые могли изменить поведение без переписывания исходного тела.
Ниже идёт статистика объектов, потоков, комментариев и ошибок. Внутри каждой ревизии перечисляются Catalog и Info, затем номера всех объектов, потоков и закодированных потоков. Отдельные строки отмечают объекты с JavaScript и подозрительные имена: /OpenAction, /AA, /Launch, /EmbeddedFile, /JS, /JavaScript, /AcroForm, /XFA и известные уязвимые конструкции. Номер в квадратных скобках — рабочая точка входа. Его следует передать команде object или stream, а не воспринимать сам факт совпадения как окончательный вердикт.
Сводка не заменяет ручную проверку. Например, /JavaScript может принадлежать легитимной форме, а /EmbeddedFile — обычному вложению. Напротив, небольшой документ без явного /JavaScript способен скрывать действие через цепочку ссылок или повреждённый объект. Поэтому полезно связать три уровня: автоматический индикатор, словарь объекта и положение объекта в дереве. Совпадение на всех трёх уровнях даёт значительно более надёжное объяснение поведения.
Что проверять в сводке в первую минуту
- Сопоставить хеш файла с записью в журнале расследования, чтобы дальнейшие выводы относились к неизменённому образцу.
- Посмотреть количество Updates: ненулевое значение означает, что команда changelog и сохранение отдельных ревизий будут полезнее простого просмотра последнего состояния.
- Записать номера объектов из строк Objects with JS code, Suspicious elements и Errors.
- Сравнить число Streams и Encoded. Большая разница указывает, что часть содержимого видна напрямую, а часть проходит через фильтры.
- Отметить Encrypted и Binary до извлечения строк: отсутствие читаемого текста при этих признаках не доказывает пустоту документа.
Безопасная подготовка образца
peepdf выполняет статический разбор, однако исследуемый файл всё равно следует считать недоверенным. Рабочую копию помещают в отдельный каталог с коротким именем без пробелов, запрещают автоматический предпросмотр в файловом менеджере и не открывают документ двойным щелчком. Если образец пришёл из почты, сначала сохраняют его как вложение, вычисляют хеш и только затем передают анализатору. Исходник оставляют только для чтения, а результаты перенаправляют в другой каталог.
На изолированной машине полезно отключить сетевой доступ или пропускать его через контролируемый шлюз. Это особенно важно при использовании js_eval, js_analyse и sctest: команды предназначены для исследования кода, но ошибочная зависимость, нестандартная реализация функции PDF JavaScript либо неизвестный дефект анализатора не должны давать образцу путь к рабочей сети. Проверка хеша в сторонней службе репутации также раскрывает идентификатор файла внешней стороне, поэтому параметр проверки применяют только в соответствии с политикой организации.
Имя образца лучше не менять после фиксации хеша. В журнале достаточно указать рабочий путь, SHA-256, время получения и канал поступления. Если требуется передать коллегам извлечённый JavaScript или поток, сохраняют его отдельно и добавляют собственную контрольную сумму. Тогда можно доказать, из какого объекта и какой ревизии получен фрагмент, а повторный анализ не зависит от содержимого буфера терминала.
python peepdf.py suspect.pdf
python peepdf.py -i suspect.pdf
python peepdf.py -i -m suspect.pdf
Первый вариант печатает сводку и завершает работу. Второй открывает интерактивную консоль. Третий включает ручной режим и не запускает автоматическую обработку JavaScript; он предпочтителен для документов с длинными циклами, heap spray и большим объёмом обфусцированного кода. Ручной режим не скрывает объекты: он лишь оставляет исследователю контроль над тем, когда применять JavaScript-команды.
Интерактивная консоль и справка
Приглашение PPDF> означает, что документ уже разобран и команды обращаются к внутренней модели. Команда help без аргумента выводит доступные операции, а help с именем команды показывает синтаксис конкретного действия. Это полезнее запоминания длинного списка параметров: набор аргументов у object, stream, references, decode и js_analyse различается, а подсказка сразу показывает допустимый тип входа — объект, поток, файл, строку или переменную.
Команды open и reset помогают менять контекст. open загружает другой PDF в той же сессии, а reset возвращает состояние анализируемого документа к исходному после модификаций. Перед переключением файла следует сохранить перенаправленные результаты и завершить незаконченные записи в журнале. Команда log позволяет вести протокол консольных действий; для повторяемого исследования это надёжнее ручного копирования отдельных строк.
В интерактивной работе важны короткие итерации. Сначала выполняют info или tree, затем одну команду просмотра, после чего фиксируют вывод. Не стоит сразу запускать несколько преобразований над одним объектом и полагаться на историю терминала: после decode, replace или modify содержимое переменной и документа может отличаться от исходного состояния. Для экспериментов создают отдельную рабочую копию и периодически используют show для проверки переменных.
Группы команд, которые стоит освоить первыми
| Задача | Команды | Результат |
|---|---|---|
| Навигация | info, tree, references | Общая структура и связи между объектами |
| Просмотр данных | object, rawobject, stream, rawstream | Декодированное и физическое представление |
| Поиск | search, offsets, bytes, hash | Совпадения, позиции и контрольные суммы |
| JavaScript | js_code, js_beautify, js_unescape, js_analyse | Извлечение и приведение сценария к читаемому виду |
| Ревизии | changelog, save_version | Изменения между обновлениями и отдельные состояния файла |
Принудительный и мягкий разбор повреждённых PDF
Вредоносные и просто некачественно созданные документы часто нарушают формальные правила PDF: у потока отсутствует /Length, объект завершается без endobj, таблица xref указывает на неверное смещение, после %%EOF остаются дополнительные байты. В обычном режиме peepdf сообщает ошибку и может остановиться до построения полной модели. Параметр -f включает принудительный разбор и позволяет игнорировать часть ошибок, чтобы получить хотя бы доступные объекты и список подозрительных элементов.
Принудительный режим не исправляет файл и не гарантирует, что каждая найденная граница объекта верна. Его вывод следует читать вместе с командой errors. Если подозрительный объект попал в список ошибок, сравнивают object и rawobject, а также проверяют физические смещения. Ошибка декодирования одного потока не должна заставлять доверять остальным потокам: злоумышленник может намеренно разместить ложную длину рядом с полезной нагрузкой.
Параметр -l включает loose mode. Он полезен, когда объект существует в байтах, но не соответствует ожидаемому оформлению или не имеет корректного endobj. В таком режиме анализатор старается поймать деформированные конструкции и включить их в модель. Практический порядок таков: сначала обычный запуск, затем -f при конкретной ошибке, после этого -l для поиска выпавших объектов. Одновременное включение послаблений без сравнения результатов затрудняет понимание, какое именно нарушение изменило картину.
Как интерпретировать расхождения режимов
- Объект виден только в loose mode: проверить его начало и конец через rawobject и offsets, затем найти входящие ссылки.
- Число потоков меняется после -f: возможно, неверная длина заставляла парсер захватить соседние байты или преждевременно остановиться.
- Подозрительный элемент исчезает в одном режиме: проверить сырые имена с шестнадцатеричным экранированием, а не делать вывод по цветной сводке.
- Ошибки относятся к xref: опираться на фактические смещения объектов и логическое дерево, а не только на объявленную таблицу ссылок.
Команда info и выбор нужной ревизии
info повторно выводит структурную сводку уже из интерактивной консоли. Команда удобна после open, decrypt или переключения между версиями, когда верхний отчёт ушёл из видимой области терминала. В ней перечислены Catalog, Info, объекты, потоки, закодированные потоки, объекты с JavaScript, ошибки и подозрительные элементы. Если документ имеет несколько обновлений, сведения группируются по версиям, и один номер объекта может иметь разное содержимое в разных состояниях.
При расследовании важно указывать версию явно, когда синтаксис команды это допускает. Последнее состояние отражает поведение файла при обычном открытии, но ранняя ревизия может содержать удалённое вложение или первоначальный сценарий. Сначала изучают актуальное логическое дерево, затем changelog, после чего обращаются к объектам в той ревизии, где они появились или изменились. Такой подход объясняет не только наличие кода, но и момент его добавления.
Номер Catalog — ключ к корню логической структуры. Номер Info ведёт к словарю метаданных, но отсутствие Info не является ошибкой само по себе. Список Errors показывает объекты, которым требуется ручное чтение. Строка Objects with JS code формируется после обнаружения сценария в объекте или потоке; если включён ручной режим, часть автоматической обработки не выполняется, поэтому поиск имён /JS и /JavaScript остаётся обязательным.
Логическое дерево документа
tree показывает путь от каталога к страницам, действиям, именованным деревьям, формам, аннотациям и потокам. Это логическое представление, поэтому оно отвечает на вопрос зачем объект нужен документу, а не где лежат его байты. Вредоносный сценарий особенно значим, когда цепочка ведёт от /Catalog к /OpenAction или от страницы к /AA, а затем к действию /JavaScript. Поток, не достижимый из каталога, может быть остатком прежней ревизии, приманкой для сканера или скрытым вложением; его проверяют отдельно.
Дерево читают сверху вниз, записывая номера на каждом переходе. Если ветка заканчивается stream, нужно открыть словарь объекта и затем декодированный поток. Если встречается array или dictionary без понятного типа, переходят к object и смотрят ключи /Type, /Subtype, /S, /Names, /Kids и /Resources. Повторяющийся номер объекта в нескольких ветках означает совместное использование, а не дубликат.
Для больших файлов дерево может быть длиннее экрана. Вывод перенаправляют в файл или переменную, после чего ищут интересующие имена. Полезные маркеры: /OpenAction для действия при открытии, /AA для дополнительных действий, /Names и /EmbeddedFiles для вложений, /AcroForm и /XFA для форм, /RichMedia и /Launch для активного содержимого. Наличие маркера требует контекста: /AA на поле формы может запускать проверку ввода, а /Launch — открывать приложение или вложенный файл.
Дерево и таблица xref решают разные задачи
tree строится по ссылкам между объектами, тогда как offsets показывает физическое размещение. Объект может присутствовать в байтах и xref, но не быть достижимым из Catalog. И наоборот, повреждённая xref способна скрыть корректную цепочку, которую loose mode восстановит по сигнатурам объектов. Сопоставление двух представлений помогает отличить логически активную конструкцию от мусора, добавленного для запутывания.
object и rawobject: декодированное и исходное представление
object выводит объект после нормализации имён и строк. Шестнадцатерично экранированное имя вроде /#4a#53 становится /JS, а восьмеричные последовательности в строке превращаются в читаемые символы. Такой вывод удобен для понимания семантики: видны /Type, /Subtype, /S, ссылки вида 5 0 R и другие ключи словаря. Анализатор снимает слой синтаксической маскировки, из-за которой простая строковая проверка могла пропустить опасное имя.
rawobject показывает байты объекта в форме, близкой к файлу: заголовок obj, экранированные имена, исходные строки, stream и endobj. Он нужен, когда нормализованный вывод выглядит подозрительно гладко или когда требуется доказать конкретный приём обфускации. Если object показывает /JavaScript, а rawobject содержит набор #xx, в отчёте следует зафиксировать оба факта: назначение объекта и способ сокрытия.
Для объектов-аннотаций важны ключи /Subtype, /Rect, /A, /AA, /Open, /Contents и /Name. Текстовая аннотация сама по себе не исполняет код, но ссылка /A или /AA может вести к действию. Для объектов действий смотрят /S: значения /JavaScript, /Launch, /URI, /GoTo, /SubmitForm и /ImportData имеют разное поведение. peepdf показывает структуру, но не определяет бизнес-контекст; окончательное решение требует проверить цель действия и путь к нему.
Когда использовать rawobject обязательно
- В списке подозрительных элементов есть имя, но поиск обычной строкой не находит его в файле.
- Парсер сообщает ошибку длины, отсутствующий endobj или несовместимое смещение.
- Строка после декодирования содержит управляющие символы либо выглядит усечённой.
- Нужно подтвердить, что объект изменён в добавочной ревизии, а не только иначе отображён.
- Результат object расходится с другим анализатором и требуется проверить исходный синтаксис.
stream и rawstream: работа с потоками
Потоки хранят содержимое страниц, изображения, шрифты, объектные контейнеры, JavaScript, вложения и произвольные бинарные данные. rawstream показывает закодированные байты, а stream применяет фильтры из словаря и выводит результат декодирования. Если поток использует /FlateDecode, rawstream обычно начинается с данных zlib, тогда как stream может раскрыть текст сценария или команды рисования страницы.
Перед декодированием открывают object для того же номера и записывают цепочку /Filter и параметры /DecodeParms. Порядок фильтров имеет значение: данные проходят через них последовательно. На практике встречаются /FlateDecode, /ASCIIHexDecode, /ASCII85Decode, /LZWDecode, /RunLengthDecode и другие варианты. Некоторые изображения используют /DCTDecode, /JPXDecode или /JBIG2Decode; их содержимое не превращается в читаемый текст и может требовать специализированного декодера.
Если stream возвращает ошибку, нельзя сразу считать поток пустым. Сравнивают /Length с фактическими границами, используют rawstream, проверяют режим -f и сохраняют байты для внешнего анализа. В намеренно повреждённом документе заявленная длина может быть меньше полезной нагрузки или захватывать начало следующего объекта. Команда offsets помогает понять, где расположен следующий заголовок obj и насколько правдоподобно значение /Length.
Декодированный поток сохраняют в файл перенаправлением, а не копированием из терминала. Терминал может заменить непечатаемые байты, интерпретировать управляющие последовательности или обрезать длинную строку. После сохранения вычисляют хеш извлечённого файла и определяют его сигнатуру. Расширение назначают по содержимому, а не по имени объекта: вложенный исполняемый файл может не иметь расширения, а поток изображения может быть дополнительно упакован.
Ссылки между объектами
references отвечает на два разных вопроса. Вариант references to N показывает объекты, которые ссылаются на объект N. Вариант references in N перечисляет ссылки, содержащиеся внутри объекта N. Первая форма помогает понять достижимость и роль подозрительного фрагмента, вторая — продолжить цепочку к потоку, действию, ресурсу или вложению.
PPDF> references to 12
PPDF> references in 12
PPDF> object 12
Если объект с JavaScript не имеет входящих ссылок в текущей ревизии, это ещё не доказательство безвредности. Он мог быть активен в раннем обновлении, использоваться через именованное дерево или находиться внутри объектного потока. Проверяют changelog, дерево имён и ссылки из Catalog. Объект без входящих ссылок также может быть намеренным мусором, рассчитанным на сигнатурный сканер; в таком случае отсутствие пути исполнения является важной частью вывода.
Цепочка от страницы к действию обычно выглядит как Page → Annots → Annot → A или AA → Action. Цепочка автоматического запуска начинается у Catalog через OpenAction либо дополнительные действия. Именованный JavaScript может проходить через Names → JavaScript → Names → Action. Записывая цепочку номеров, исследователь получает воспроизводимое объяснение, которое легко проверить другой программой.
Физические смещения и сырые байты
offsets выводит карту файла: заголовок PDF, начала объектов, секции xref, trailer и %%EOF. Числа позволяют перейти от логической модели к конкретным байтам. Это особенно ценно при повреждённой xref, нескольких %%EOF, добавленном хвосте или несоответствии /Length. Если объект заявлен по одному адресу, а фактически найден по другому, разница может объяснить поведение разных средств просмотра.
Команда bytes показывает участок исходного документа и подходит для проверки сигнатур рядом с границей объекта. В сочетании с hash она позволяет вычислить контрольную сумму выбранных данных, потока, файла или переменной. Для отчёта полезно хранить не только хеш всего PDF, но и хеш извлечённого JavaScript, вложения и каждой сохранённой ревизии. Это связывает выводы с конкретными артефактами.
Физическая карта помогает обнаружить данные после последнего %%EOF. Такой хвост может быть безобидным мусором, ошибкой генератора, вложенным файлом или второй структурой. peepdf не должен быть единственным основанием для определения формата хвоста: байты сохраняют отдельно и проверяют сигнатуру. Если после EOF начинается ZIP, PE или другой контейнер, это фиксируют как отдельный артефакт и ищут ссылки на него из объектов.
Проверка сомнительной длины потока
- Открыть словарь объекта и записать /Length, включая случай, когда длина хранится в косвенном объекте.
- Посмотреть начало потока через rawstream и найти следующий объект в offsets.
- Сравнить заявленное число байтов с расстоянием до endstream и следующего obj.
- Повторить разбор в обычном, принудительном и мягком режимах.
- Сохранить сырые данные без преобразований и вычислить хеш.
Метаданные и их практическая ценность
metadata выводит словарь Info для каждой версии документа. Обычно в нём встречаются /Title, /Author, /Subject, /Keywords, /Creator, /Producer, /CreationDate и /ModDate. Поля помогают построить гипотезу о происхождении файла, но легко изменяются и не являются доказательством авторства. Наиболее полезны несогласованности: дата изменения раньше создания, разные генераторы в соседних ревизиях, пустые поля рядом с аномальной бинарной строкой.
Строки метаданных могут использовать PDFDocEncoding, UTF-16 с маркером порядка байтов или произвольный набор байтов. Если терминал показывает нечитаемые символы, открывают Info через rawobject и сохраняют строку для отдельного декодирования. Нельзя заменять ошибочные символы вручную и затем ссылаться на полученный текст как на исходный: в отчёте указывают сырое значение и предположенную кодировку.
Метаданные сравнивают с внутренней структурой. Producer может указывать обычный редактор, а документ при этом содержит добавочную ревизию с JavaScript, созданную другим инструментом. В таком случае поле описывает только один этап. Changelog и объекты обновления дадут более точную картину. Аналогично отсутствие метаданных не уменьшает риск: многие специально собранные образцы оставляют Info пустым.
Поиск строк, имён и шестнадцатеричных шаблонов
search проходит по документу и помогает находить ASCII-строки и последовательности байтов. Начальный набор запросов обычно включает JavaScript, OpenAction, Launch, EmbeddedFile, XFA, URI, SubmitForm и характерные функции сценариев. Поиск выполняют и по нормализованному представлению, и по сырым данным, потому что имя может быть записано через #xx, а строка — восьмеричными кодами или в сжатом потоке.
Поиск по одному слову даёт много ложных срабатываний. Например, URI может находиться в обычной гиперссылке, а JavaScript — в комментарии или неиспользуемом объекте. После каждого совпадения записывают номер объекта, открывают его словарь и проверяют входящие ссылки. Для бинарных сигнатур используют шестнадцатеричный шаблон, а найденные байты сохраняют, если они похожи на заголовок файла.
Команда xor_search полезна, когда предполагается простое XOR-кодирование, но применять её следует к конкретному потоку или переменной, а не ко всему документу без причины. Перебор создаёт множество случайных совпадений. Результат считается значимым, если после подходящего ключа появляется связная структура: сигнатура, читаемая строка, заголовки формата или последовательность команд, подтверждённая соседними байтами.
Практическая последовательность поиска
- Сначала искать имена PDF-конструкций, которые отмечены в автоматической сводке.
- Затем искать строки внутри декодированных потоков, связанных с этими объектами.
- После этого проверять имена функций JavaScript, признаки вложений и внешних действий.
- В конце искать бинарные сигнатуры в неидентифицированных потоках и данных после EOF.
Перенаправление вывода и переменные
Длинный объект, поток или сценарий не следует анализировать только на экране. peepdf поддерживает перенаправление вывода в файл и в переменную. Обычный знак > создаёт или перезаписывает файл, >> добавляет данные, а варианты с долларом направляют вывод в переменную консоли. Точный синтаксис удобно уточнять через help для используемой команды, поскольку входом может быть объект, поток, файл или строка.
PPDF> stream 13 > object13.bin
PPDF> object 4 > object4.txt
PPDF> set sample suspicious_text
PPDF> show sample
Перед перезаписью проверяют имя файла и рабочий каталог. При нескольких ревизиях в имя включают номер версии и объекта, например v1-object13.js. Это предотвращает смешение результатов. Для переменных выбирают короткие, но однозначные имена: raw13, decoded13, js13. show позволяет проверить содержимое перед decode, xor или replace.
Переменная удобна для цепочки преобразований, но не заменяет сохранённый артефакт. После каждого важного этапа результат записывают в файл и считают хеш. Так можно восстановить путь: сырой поток → декодированный поток → объединённый JavaScript → деобфусцированный текст. Если преобразование оказалось ошибочным, исследователь возвращается к предыдущему сохранённому состоянию, а не пытается отменить изменения вручную.
Декодирование и кодирование данных
decode применяет выбранное преобразование к строке, файлу, потоку или переменной. Команда полезна не только для стандартных PDF-фильтров, но и для данных, которые сценарий хранит в Base64, hex или другой форме. Сначала определяют формат по синтаксису и сигнатуре, затем выполняют одно преобразование и проверяют результат. Автоматическая цепочка из нескольких догадок легко создаёт правдоподобный, но неверный текст.
После декодирования бинарного блока проверяют первые байты и длину. Для PE ожидается сигнатура MZ, для ZIP — PK, для PDF — %PDF, для изображений — соответствующий заголовок. Совпадение сигнатуры лишь определяет контейнер; оно не подтверждает безопасность. Вложение сохраняют без запуска и передают профильному анализатору.
encode, filters и encode_strings предназначены для изменения и тестирования PDF. В защитной лаборатории они позволяют собрать контрольный образец, проверить, распознаёт ли средство мониторинга экранированное имя или цепочку фильтров, и затем вернуть документ к читаемому виду. Эти операции выполняют только над искусственными безвредными данными. Не следует модифицировать оригинал расследуемого файла: сохранённая копия перестанет соответствовать исходному хешу.
Если decode выдаёт мусор, проверяют четыре причины: неверно выбран формат, данные усечены, преобразование должно применяться после другого фильтра или строка содержит разделители и экранирование. В случае PDF-потока цепочку берут из /Filter, а параметры — из /DecodeParms. Для содержимого JavaScript сначала объединяют строковые фрагменты и снимают экранирование, а затем декодируют вложенную строку.
Извлечение и чтение JavaScript
JavaScript в PDF может проверять поля формы, управлять печатью и навигацией, но также использоваться для автоматического запуска, распыления памяти и подготовки полезной нагрузки. peepdf собирает объекты с кодом в сводке и предоставляет набор команд для постепенного разбора. Начинают с object и stream, чтобы увидеть, где хранится сценарий и какое действие его вызывает. Только после этого применяют js_code или js_beautify.
js_code извлекает найденный код, js_beautify расставляет отступы и переносы, js_join соединяет разорванные строковые фрагменты, js_unescape преобразует escape-последовательности, js_jjdecode разбирает код, обфусцированный jjencode, а js_vars показывает переменные и их значения. js_analyse объединяет несколько этапов и ищет интересные данные, но его результат надо сверять с исходным потоком.
Форматирование не делает сценарий безопасным и не доказывает его назначение. После beautify отмечают точки входа, обращения к app, this, util, Collab, getAnnots, submitForm и другим функциям, затем ищут длинные строки, unescape, eval, fromCharCode и циклы наращивания буфера. В отчёте отделяют наблюдение от вывода: объект содержит вызов eval после сборки строки точнее, чем объект заражён без анализа собранного значения.
Безопасный порядок анализа сценария
- Найти объект действия и путь к нему через tree и references.
- Сохранить rawobject и rawstream, если поток закодирован.
- Получить декодированный stream и вычислить его хеш.
- Применить js_join и js_unescape к копии или переменной.
- Использовать js_beautify для чтения структуры, не запуская документ в PDF-просмотрщике.
- Запускать js_eval или расширенный анализ только в изолированной среде и при понятной цели.
PyV8 и ограничения выполнения JavaScript
Расширенные команды JavaScript зависят от PyV8. Без этого компонента базовый разбор PDF, просмотр объектов, потоков, дерева и строк остаётся доступным, но выполнение и часть автоматической интерпретации сценария не работают. Ошибка импорта PyV8 не означает, что в документе нет JavaScript; она означает только отсутствие движка в окружении.
Даже при установленном движке среда выполнения не полностью повторяет Adobe Acrobat или другой просмотрщик. PDF JavaScript обращается к объектной модели приложения: методам документа, аннотаций, форм и событий. Если функция не реализована в обёртке, код может завершиться ошибкой, пойти по другой ветке или не раскрыть следующую ступень. Поэтому основной метод остаётся статическим: восстановить строки и условия, а результат выполнения использовать как дополнительное наблюдение.
Параметр -m отключает автоматический анализ JavaScript при загрузке. Он нужен для сценариев с бесконечным циклом, большим heap spray или дорогими преобразованиями. После открытия в ручном режиме код извлекают по объектам, удаляют повторяющиеся заполнители только в копии и запускают отдельные фрагменты. Нельзя считать тайм-аут признаком безвредности: вредоносный код часто специально расходует время или ожидает свойства конкретной программы.
Типовые ошибки PyV8
- ImportError или module not found. Проверить, что модуль собран для той же разрядности и той же версии Python, которой запускается peepdf.
- Неизвестный метод документа. Зафиксировать вызов и аргументы, затем продолжить статическое восстановление без подмены метода случайным значением.
- Зависание анализа. Перезапустить с -m, извлечь код в файл и разбирать функции по отдельности.
- Различный результат повторных запусков. Проверить использование даты, случайных чисел, окружения и глобальных переменных.
Shellcode и команда sctest
sctest является обёрткой над libemu через pylibemu и предназначен для эмуляции фрагмента машинного кода, найденного в JavaScript или потоке. Команда может показать вызовы API, сетевые строки и поведение распакованного буфера без прямого запуска программы. Зависимость устанавливается отдельно; при её отсутствии peepdf по-прежнему может извлечь байты для внешнего эмулятора.
Перед эмуляцией необходимо доказать, что данные действительно похожи на код. Длинная последовательность после unescape может быть изображением, сжатым блоком или случайными байтами. Проверяют выравнивание, повторяющиеся заполнители, характерные прологи и контекст JavaScript, который передаёт буфер уязвимой функции. Эмуляцию выполняют над сохранённой копией и фиксируют хеш входа.
Результат libemu не является полноценным динамическим отчётом. Эмулятор может не распознать нестандартный декодер, 64-разрядный код, антиэмуляционные проверки или зависимость от памяти процесса. Отсутствие вызовов API не означает отсутствие вредоносного поведения. В таком случае байты передают дизассемблеру или песочнице, сохраняя связь с номером объекта и этапом декодирования.
Шифрованные документы
Флаг Encrypted в сводке означает наличие словаря шифрования. peepdf может разбирать структуру и выполнять decrypt, если пароль известен или документ использует пустой пароль пользователя. После расшифровки проверяют, изменилось ли число доступных объектов и потоков, а также повторяют поиск подозрительных имён. До расшифровки часть строк и потоков может выглядеть как случайные байты.
Пароль владельца и пароль пользователя выполняют разные роли. Успешное открытие документа без запроса не доказывает отсутствие шифрования: пользовательский пароль может быть пустым, а ограничения печати или копирования задаются отдельно. В отчёте указывают факт шифрования и способ получения доступа, но не публикуют секретный пароль. Расшифрованную копию хешируют как новый производный артефакт.
Если decrypt сообщает неверный пароль, сначала проверяют раскладку, кодировку и пробелы. Затем открывают rawobject словаря Encrypt и фиксируют параметры, не пытаясь бесконечно перебирать значения внутри peepdf. Для восстановления пароля применяют специализированное средство в рамках разрешённого расследования. Повреждённый словарь шифрования может требовать принудительного разбора, но результат необходимо сверять другим парсером.
Ревизии, changelog и save_version
PDF допускает добавочные обновления: новые объекты, xref и trailer дописываются в конец, а прежние байты сохраняются. В сводке peepdf число Updates показывает, сколько таких изменений обнаружено. changelog сравнивает версии и перечисляет добавленные, изменённые или удалённые объекты. Это помогает ответить, был ли JavaScript частью исходного документа или появился позже.
save_version сохраняет выбранное состояние в отдельный PDF. Для анализа создают файлы с явными именами v0, v1 и так далее, затем вычисляют хеш каждого. Открывать сохранённые ревизии в обычном просмотрщике по-прежнему нельзя: ранняя версия может быть опаснее финальной. Их снова прогоняют через peepdf и сравнивают дерево, подозрительные элементы и метаданные.
Удалённый в новой версии объект физически может оставаться в исходнике. Поэтому поиск по всему файлу находит строки, которые уже не участвуют в текущей структуре. Changelog и references объясняют такое расхождение. В отчёте полезно писать: объект 17 содержит JavaScript в версии 0, но в версии 1 не имеет пути от Catalog, а не просто перечислять совпадение.
Признаки подозрительного добавочного обновления
- В новой ревизии изменён Catalog или OpenAction без изменения видимых страниц.
- Добавлен небольшой объект действия, который ссылается на крупный поток из ранней версии.
- Producer и ModDate меняются, хотя содержимое страниц остаётся прежним.
- Новая xref содержит необычные смещения или перекрывает существующий номер объекта.
- После сохранения ранней версии исчезает вложение либо внешний переход.
Вложения и внедрённые файлы
Вложения обычно связаны через дерево имён /EmbeddedFiles и объект FileSpec. В FileSpec встречаются /F, /UF, /Desc и /EF; ключ /EF ведёт к потоку EmbeddedFile. tree помогает пройти цепочку от Catalog, а references подтверждает, какие объекты используют поток. Имя файла рассматривают как подсказку: оно может быть подменено, содержать двунаправленные символы или не соответствовать сигнатуре.
Для извлечения сначала открывают словарь потока, записывают /Subtype и параметры, затем сохраняют stream в файл. После декодирования определяют формат по первым байтам и вычисляют хеш. Не следует присваивать расширение .exe только потому, что в /F написано program.exe; и наоборот, файл без расширения может иметь MZ. Если поток использует несколько фильтров, сохраняют также rawstream для воспроизводимости.
Вложение может не запускаться автоматически. Риск повышается, если FileSpec связан с /Launch, /GoToE, /RichMedia или JavaScript, который извлекает данные. В отчёте разделяют наличие вложения и путь активации. Если входящих ссылок нет, проверяют ранние ревизии и объектные потоки. Невостребованный поток всё равно может быть полезным индикатором кампании, но это другая категория вывода.
Формы, XFA и дополнительные действия
/AcroForm указывает на интерактивные поля, а /XFA — на XML Forms Architecture. Формы могут содержать легитимные вычисления и проверки, поэтому сам маркер не равен вредоносности. Исследователь смотрит /Fields, /Kids, /AA, значения /V и /DV, а также сценарии событий K, V, C, F, Fo и Bl. Дополнительные действия могут срабатывать при вводе, фокусе, открытии страницы или печати.
XFA часто хранится в массиве имён и потоков. Поток декодируют и сохраняют как XML, после чего ищут script, event, submit и внешние ссылки. peepdf показывает объектную оболочку и извлекает поток, но сложную XML-структуру удобнее проверять специализированным парсером. Нельзя открывать извлечённую форму в веб-обозревателе или редакторе с активными скриптами.
При большом количестве полей полезно начать с references to для объекта сценария: так видно, какие поля его используют. Один общий скрипт может обслуживать десятки полей, а отдельный подозрительный обработчик — только скрытое поле. Сравнение /Rect, /F и /FT помогает понять, видимо ли поле и какого оно типа.
Пакетный режим и сценарии команд
Параметр -s загружает команды из файла и выполняет их последовательно. Это удобно для повторяемого извлечения: получить info, tree, metadata, конкретные объекты и потоки одинаковым способом для серии образцов. Сценарий должен быть детерминированным и не менять документ, если задача — первичный анализ. Команды modify, replace, encode и save выносят в отдельный лабораторный сценарий.
Перед массовым запуском сценарий проверяют на одном безопасном PDF и одном тестовом повреждённом файле. Номера объектов различаются, поэтому жёстко заданная команда object 5 подходит только для конкретного образца. Для серии документов автоматизируют сводку и поиск, а переход к найденным номерам выполняют отдельным этапом. Выходные каталоги создают заранее, чтобы один файл не перезаписал результаты другого.
Форматы XML и JSON позволяют передавать сводку в другие средства через соответствующие параметры запуска. XML требует lxml. Машинный вывод следует валидировать: ошибка разбора или непечатаемый байт может нарушить документ. В журнале сохраняют код возврата, stderr и хеш входа, а не только успешный JSON.
Минимальный безопасный шаблон автоматизации
- Создать отдельный каталог результатов по SHA-256 входного PDF.
- Запустить сводку без интерактивного выполнения JavaScript.
- Сохранить stdout, stderr и код завершения.
- Извлечь список подозрительных объектов из структурированного вывода.
- Передать номера на ручную проверку, не открывая исходный документ.
Создание и изменение PDF в защитной лаборатории
Команды create, modify, replace, embed, filters, encode_strings, encrypt, malformed_output и save позволяют собирать тестовые PDF и проверять средства защиты. Практический безопасный сценарий — создать документ с безвредной строкой JavaScript, изменить способ записи имени /JavaScript, применить фильтр к потоку и убедиться, что корпоративный сканер всё равно обнаруживает конструкцию. Тестовый код не должен загружать файлы, запускать команды или использовать уязвимости.
modify меняет значение выбранного объекта, replace заменяет данные, embed добавляет вложение, filters управляет фильтрами потока, encrypt задаёт шифрование, save записывает результат. После каждого изменения выполняют info и tree, чтобы убедиться, что ссылки остались корректными. Сохранённый тестовый файл получает новое имя и хеш; исходный образец расследования не используют как шаблон.
malformed_output предназначен для генерации отклонений от спецификации. Он полезен при тестировании устойчивости парсеров, но способен создавать файлы, которые разные программы трактуют неодинаково. Такие образцы маркируют как лабораторные, хранят отдельно и не отправляют пользователям. Цель — проверить обработку конкретного дефекта, например отсутствующего маркера, а не максимизировать неоднозначность.
Ошибки запуска и совместимость
Код peepdf рассчитан на Python 2, поэтому запуск интерпретатором Python 3 обычно приводит к SyntaxError ещё до разбора файла: отличаются формы print, обработка байтов и некоторые библиотеки. Для воспроизводимой работы создают изолированное окружение Python 2.7 и не смешивают его пакеты с системным Python. Результаты после автоматической конвертации кода нельзя считать идентичными без повторной проверки объектов, потоков и ошибок парсинга.
Базовые зависимости включают библиотеки для цветов, XML, криптографии и форматирования JavaScript; часть возможностей включается только при наличии Pillow, PyV8 и pylibemu. Ошибка импорта конкретного модуля решается установкой совместимой версии в то же окружение, из которого запускается peepdf. Команда python -c с импортом модуля помогает проверить путь, но номер и разрядность интерпретатора должны совпадать.
На Windows часто возникает путаница между несколькими python.exe. Перед запуском проверяют путь интерпретатора и вызывают peepdf через него явно. В Linux права на исполнение файла peepdf.py не нужны, если используется python peepdf.py. Цветные управляющие последовательности в журнале отключают параметром -g; это делает вывод удобнее для перенаправления и сравнения.
Частые сообщения и действия
| Сообщение или симптом | Вероятная причина | Что сделать |
|---|---|---|
| SyntaxError при старте | Запуск исходного кода через Python 3 | Использовать изолированный Python 2.7 |
| No module named lxml | Нет библиотеки для XML | Установить её в активное окружение или не запрашивать XML |
| PyV8 not found | Нет движка JavaScript | Продолжить статический разбор либо поставить совместимый модуль |
| Missing /Length | Повреждённый или намеренно деформированный поток | Сравнить обычный, -f и -l, затем проверить offsets |
| Цветовые коды в файле | Перенаправлен цветной вывод | Повторить запуск с -g |
Разбор конкретных ошибок PDF
Сообщение Missing /Length in stream object означает, что словарь потока не содержит обязательной длины либо ссылка на длину не разрешилась. В принудительном режиме парсер пытается найти endstream эвристически. Исследователь открывает rawobject, ищет ближайший endstream и следующий obj, затем сравнивает полученную длину. Если внутри данных встречается ложная строка endstream, автоматическая граница может быть неверной.
Ошибка xref указывает на несоответствие таблицы перекрёстных ссылок фактическим объектам. offsets позволяет увидеть найденные позиции, а loose mode — обнаружить объекты по синтаксису. При нескольких секциях xref проверяют Prev в trailer и changelog. Документ может открываться в просмотрщике, который восстанавливает xref, но анализатор обязан явно зафиксировать расхождение.
Missing endobj или malformed object обычно означает усечённый объект, вложенный мусор или намеренную неоднозначность. Сравнивают rawobject с соседними смещениями и не доверяют декодированному object без проверки. Если два парсера дают разные границы, сохраняют соответствующий диапазон байтов и описывают оба результата.
Ошибки декодирования фильтра возникают из-за повреждённых данных, неверных параметров, неподдерживаемого фильтра или ложного /Filter. Сохраняют rawstream, проверяют сигнатуру и пробуют декодировать только заявленную цепочку. Случайный перебор алгоритмов допустим лишь как гипотеза и должен быть отмечен в отчёте, чтобы не смешивать исходное содержимое с интерпретацией.
Практический сценарий: подозрительное почтовое вложение
Первый этап — зафиксировать SHA-256, размер и имя вложения, затем запустить peepdf без интерактивного выполнения JavaScript. В сводке записывают Updates, Encrypted, Errors, Objects with JS code и Suspicious elements. Если отмечен /OpenAction, открывают указанный объект и через references строят цепочку от Catalog к действию.
Второй этап — tree и metadata. Дерево показывает, связан ли сценарий с открытием, страницей, формой или именованным деревом. Метаданные помогают заметить несогласованное происхождение, но не используются как единственное основание. Если есть обновления, запускают changelog и выясняют, когда добавлено действие.
Третий этап — сохранить rawobject, rawstream и stream подозрительных номеров. JavaScript приводят к читаемому виду через js_join, js_unescape и js_beautify, сохраняя промежуточные файлы. Длинные бинарные строки не исполняют; их декодируют в отдельный артефакт, определяют сигнатуру и хешируют.
Финальный вывод отвечает на четыре вопроса: какой объект запускается, каким событием, какие данные он обрабатывает и куда ведёт цепочка. Если автоматического пути нет, формулировка должна это отражать. Отчёт с номерами объектов и хешами позволяет перепроверить вывод без открытия PDF.
Практический сценарий: скрытое вложение
Начинают с поиска /EmbeddedFile, /Filespec, /EF и дерева /Names. Найденный FileSpec открывают командой object и записывают ссылку из /EF. Затем проверяют references to для потока: входящая ссылка только из FileSpec подтверждает вложение, а дополнительная ссылка из действия может указывать на запуск или переход.
Поток извлекают через stream в файл. Если содержимое не распознаётся, сохраняют rawstream и проверяют /Filter. После декодирования определяют формат по magic-байтам, а имя из /F и /UF рассматривают отдельно. Несовпадение, например имя изображения при сигнатуре ZIP, является сильным индикатором маскировки.
Далее ищут событие активации: /Launch, JavaScript, /RichMedia или ссылку из аннотации. Если пути нет, проверяют предыдущие ревизии. В выводе различают файл вложен и файл запускается. Это предотвращает завышение риска для обычного PDF-портфеля и одновременно не теряет артефакт, полезный для корреляции.
Практический сценарий: документ с несколькими обновлениями
При Updates больше нуля сначала сохраняют общую сводку, затем выполняют changelog. Особое внимание уделяют объектам Catalog, OpenAction, Names, AcroForm и страницам, которые изменены без заметного изменения размера документа. Для каждой версии сохраняют отдельный PDF и повторяют info.
Если JavaScript обнаруживается во всём файле, но отсутствует в дереве финальной версии, проверяют ранние состояния. Возможна обратная ситуация: поток существовал раньше как неиспользуемый, а поздняя ревизия добавила короткое действие, которое на него ссылается. Тогда вредоносное изменение занимает мало байтов и легко теряется в общей статистике.
Метаданные версий сопоставляют с физическими смещениями. Поздняя ModDate рядом с новой xref подтверждает добавочный этап, но дата может быть поддельной. Надёжнее сочетание: объект появился в changelog, его байты находятся после прежнего %%EOF, а новая ссылка делает его достижимым из Catalog.
Практический сценарий: повреждённый образец
Если обычный запуск останавливается, текст ошибки сохраняют дословно. Затем повторяют с -f и -l по отдельности, сравнивают число объектов и потоков. Объекты, которые появились только в одном режиме, открывают через rawobject и проверяют offsets. Цель — не получить максимально длинную сводку, а понять, какие эвристики восстановили структуру.
При несогласованной xref ищут заголовки obj по физической карте и проверяют trailer. При неверной длине потока сохраняют диапазон до предполагаемого endstream и до следующего объекта. При усечённом конце фиксируют отсутствие %%EOF и не дописывают его в оригинал. Для эксперимента можно создать копию и применить модификацию, но выводы по исправленной копии отделяют от исходных данных.
После восстановления минимальной модели строят tree и references. Если подозрительный объект найден только эвристически и не связан с Catalog, это указывают явно. Другой PDF-парсер используют для независимой проверки границ. Расхождение инструментов является результатом анализа, а не помехой, особенно для файлов, рассчитанных на неодинаковую трактовку.
Как оформлять результаты анализа
Хороший отчёт начинается с идентификаторов: хеш исходного PDF, размер, имя рабочей копии и условия запуска. Далее идут структурные факты — число ревизий, ошибки разбора, номера подозрительных объектов, путь от Catalog или страницы, фильтры и тип извлечённого содержимого. Скриншот терминала полезен как иллюстрация, но текстовый вывод и сохранённые артефакты важнее.
Для каждого вывода указывают команду и объект. Например: object 4 содержит действие /JavaScript; references to 4 показывает ссылку из объекта 1; tree связывает объект 1 с Catalog. Такая формулировка воспроизводима. Слова вредоносный и безопасный используют только после установления поведения или уверенной совокупности индикаторов.
Извлечённые файлы называют по версии, номеру объекта и этапу: v0-o13-raw.bin, v0-o13-decoded.js, v0-o13-beautified.js. Рядом хранят SHA-256 и краткое описание преобразования. Если применялся ручной decode или XOR, это обязательно отмечают; иначе коллега может принять производный файл за прямое содержимое потока.
Сравнение peepdf с аналогами
Инструменты анализа PDF различаются глубиной парсинга и рабочим процессом. Одни быстро считают подозрительные ключевые слова, другие дают точечный доступ к объектам, третьи нормализуют и переписывают структуру. peepdf удобен как единая интерактивная среда, но в сложном расследовании его вывод полезно подтверждать независимым парсером.
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| peepdf | Интерактивного перехода от сводки к объектам, потокам, ссылкам, ревизиям и JavaScript | Консоль и зависимости Python 2 усложняют развёртывание |
| PDFiD | Очень быстрого первичного подсчёта подозрительных имён в больших наборах файлов | Ищет ключевые слова и не разбирает полную логическую структуру |
| pdf-parser.py | Точного выбора объектов, фильтрации потоков и автоматизации через командные параметры | Для цепочек ссылок приходится вручную сочетать несколько вызовов |
| qpdf | Проверки синтаксиса, раскрытия объектных потоков, расшифровки и получения структурированного JSON | Не специализируется на эвристике вредоносного JavaScript и индикаторах атак |
| Origami | Программного разбора, изменения и генерации PDF через Ruby, включая глубокую работу с объектами | Требует написания сценариев и знания API для многих расследовательских задач |
Для сортировки большой почтовой очереди разумно начать с PDFiD, а файлы с интересными индикаторами передать peepdf. pdf-parser.py удобен, когда нужен один конкретный объект или стабильный конвейер командной строки. qpdf полезен для независимой проверки целостности, отключения object streams и экспорта JSON. Origami выбирают, когда анализ должен перейти в программное изменение структуры или собственный фильтр. peepdf лучше всего подходит исследователю, которому нужен последовательный диалог с одним документом и сохранение промежуточных артефактов.
Ограничения, которые влияют на выводы
peepdf не отображает страницы так, как их видит пользователь. Он разбирает структуру, поэтому визуальную подмену, невидимый текст, особенности шрифтов и результат сложного рендеринга приходится проверять отдельным безопасным рендерером. Отсутствие подозрительных объектов не исключает фишингового содержимого, которое полностью состоит из обычного текста и ссылок.
Поддержка фильтров и PDF JavaScript не охватывает все варианты спецификации. Неподдерживаемый JPX, JBIG2, нестандартный предиктор или функция Acrobat может оставить поток неразобранным. В таком случае важно сохранить сырой объект и не делать отрицательный вывод. Другой инструмент или библиотека может декодировать данные иначе.
Эвристика подозрительных элементов основана на именах, действиях, известных уязвимых функциях и аномалиях. Легитимная форма способна набрать несколько индикаторов, а хорошо скрытый образец — мало. Оценка и цвет являются приоритизацией, а не классификатором с гарантией. Решение строится на достижимости, содержимом и поведении.
Интерактивная консоль помогает исследовать один файл, но не заменяет масштабируемую систему обработки тысяч документов. Для потока образцов нужны оболочка, ограничения времени и памяти, журналирование и отдельное хранилище результатов. peepdf можно включить в такой конвейер, однако пакетный слой должен обрабатывать аварийное завершение и повреждённый вывод.
Когда peepdf не является подходящим выбором
Для редактирования текста, перестановки страниц, заполнения форм или создания обычного документа нужен PDF-редактор, а не объектный анализатор. Для восстановления визуально повреждённой страницы полезнее рендерер и инструменты работы с содержимым страницы. Для массовой сигнатурной сортировки быстрее сначала применить лёгкий сканер ключевых слов.
Если задача требует полноценного исполнения документа в точной версии конкретного PDF-просмотрщика, статический разбор и PyV8 недостаточны. Нужна изолированная динамическая среда с мониторингом процессов, файловой системы и сети. peepdf в таком сценарии подготавливает гипотезы и извлекает артефакты, но не заменяет песочницу.
Для юридически значимого исследования одного инструмента мало. Результаты подтверждают независимыми средствами, сохраняют исходный носитель и ведут цепочку хранения. Модифицирующие команды не применяют к доказательственной копии. peepdf остаётся удобным средством интерпретации структуры, но методика расследования определяет допустимость и форму доказательств.
Объектные потоки и сжатые таблицы ссылок
В современных PDF несколько обычных объектов могут быть упакованы внутрь /ObjStm. В сводке отдельные номера при этом существуют логически, хотя их текст не лежит в файле как самостоятельный блок N 0 obj. peepdf разбирает объектные потоки и позволяет обращаться к вложенным объектам по номеру. Если ручный поиск заголовка obj ничего не находит, сначала нужно проверить, не указан ли объект в /ObjStm, а не объявлять его вымышленным.
Словарь объектного потока содержит /N и /First: первое значение сообщает число упакованных объектов, второе отделяет индекс от их тел. Сам поток обычно проходит через /FlateDecode. Команда object показывает распакованные элементы, rawstream сохраняет физический контейнер, а stream — декодированные данные. Для доказуемого вывода полезно хранить оба варианта и номер контейнера, потому что смещение вложенного объекта относится к декодированному потоку, а не к началу PDF.
Таблица перекрёстных ссылок также может храниться как /XRef-поток. Повреждение его фильтра или параметров /W влияет на обнаружение многих объектов сразу. В таком случае сравнивают автоматическую модель с offsets и независимым qpdf. Принудительный разбор способен восстановить часть объектов по сигнатурам, но не всегда правильно определяет поколения и состояние свободных записей. В отчёте указывают, какие элементы получены из xref, а какие найдены эвристически.
Почему простой grep не заменяет разбор
Имя /JavaScript может быть записано в объекте, который находится внутри сжатого контейнера; строки и имена могут быть экранированы; поток — зашифрован. Поэтому отсутствие обычной текстовой строки в исходном файле не доказывает отсутствие конструкции. peepdf последовательно раскрывает объектный поток, нормализует имена и строит ссылки, что даёт контекст, недоступный простому поиску байтов.
Цвета, оценка и приоритизация индикаторов
Цветной отчёт помогает быстро отделить обычную статистику от ошибок и подозрительных элементов. Однако цвет зависит от терминала и может потеряться при перенаправлении. Для журналов и машинного сравнения запускают grinch mode с параметром -g: смысл строк сохраняется, а управляющие последовательности исчезают. Это особенно важно, если вывод затем разбирается регулярными выражениями или помещается в систему регистрации инцидентов.
Числовая оценка подозрительности показывает вклад отдельных признаков. Она учитывает структурные аномалии, активные действия, JavaScript, известные уязвимые функции, обфускацию, размер потоков и неиспользуемые объекты. Оценка удобна для очереди, но не является вероятностью заражения. Высокое значение объясняют конкретными объектами, а низкое не отменяет ручной проверки файла с ошибками парсинга.
Приоритет лучше назначать по сочетанию признаков. /OpenAction, ведущий к /JavaScript с декодируемым бинарным блоком, важнее одиночного /JS в неиспользуемом объекте. Ненулевая оценка из-за большого потока изображения может быть нормальной, если дерево связывает его с ресурсами страницы и фильтр соответствует типу изображения. peepdf даёт номера объектов, поэтому каждую строку рейтинга можно превратить в проверяемую гипотезу.
Проверка репутации по хешу
Параметр -c и команда vtcheck проверяют хеш файла в VirusTotal, не подменяя структурный анализ. Совпадение с известным образцом ускоряет атрибуцию, а отсутствие срабатываний означает лишь, что служба не знает данный хеш или ещё не классифицировала его. Перепакованный PDF с тем же поведением получает другой хеш, поэтому отрицательный ответ не уменьшает значимость найденного автоматического действия.
Перед сетевой проверкой учитывают режим конфиденциальности. Даже запрос по хешу сообщает внешней службе, какой идентификатор интересует организацию; автоматическая отправка файла раскрывает само вложение. peepdf используют только в соответствии с правилами обмена образцами. В закрытом расследовании строку хеша можно сохранить для последующей проверки на отдельной машине.
В отчёте разделяют сведения репутации и собственные наблюдения. Формулировка хеш обнаруживается несколькими движками не объясняет структуру документа, а объект 4 запускает поток 13 через OpenAction не определяет семейство вредоносного ПО. Совместно эти факты дают и техническое объяснение, и внешний контекст.
Разбор содержимого страниц
Потоки страниц содержат операторы PDF-графики: BT и ET ограничивают текстовый объект, Tj и TJ выводят строки, Do вызывает XObject, cm меняет матрицу преобразования. peepdf может показать декодированный content stream, но не выполняет полноценный рендеринг и не восстанавливает текст с учётом шрифтов. Для проверки скрытой надписи исследуют словарь ресурсов, ссылки на Font и XObject, затем передают поток профильному средству.
Невидимый объект может использовать нулевой размер, белый цвет, отсечение, прозрачность или размещение за границей страницы. Эти свойства находятся в операторах содержимого и графическом состоянии, а не в списке подозрительных имён. Если расследуется фишинговая страница, структурный анализ дополняют безопасным растровым рендерингом. Сравнение изображения страницы с извлечёнными ссылками помогает выявить подмену адреса или текста.
Вызов Do ведёт к Form XObject или изображению. Через references можно установить, какие страницы используют объект, а stream — сохранить его данные. Один XObject может многократно повторяться на страницах, поэтому большое число ссылок не означает множество копий полезной нагрузки. Важны словарь /Subtype, фильтры и путь от страницы.
Проверка результата другим парсером
Независимая проверка обязательна для файлов с ошибками, несколькими xref и неоднозначными границами потоков. Сначала фиксируют вывод peepdf, затем запускают другой парсер над тем же хешем. Сравнивают число объектов, номера Catalog и Info, наличие шифрования, список потоков и трактовку спорного объекта. Совпадение укрепляет вывод; различие показывает область, которую нужно исследовать по байтам.
qpdf хорошо сообщает синтаксические проблемы и может представить структуру в JSON, pdf-parser.py удобно выбирает объект и раскрывает фильтр, а PDFiD независимо считает подозрительные имена. Origami полезен для программного обхода дерева. Нельзя голосовать большинством без понимания: несколько средств могут использовать одну библиотеку или одинаковую эвристику. Решающий уровень — фактический диапазон байтов и правила PDF.
Если peepdf видит объект, а другой парсер нет, проверяют object stream, поколение объекта, xref и режим loose. Если другой парсер извлекает больше данных из потока, сравнивают /Filter и /DecodeParms. Все команды выполняют над неизменённой копией; нормализация qpdf создаёт новый файл, который следует хешировать отдельно и не путать с исходником.
Контрольный список перед завершением работы
- Хеш исходного PDF записан до любых преобразований.
- Сводка проверена в обычном режиме, а -f или -l применялись только при понятной ошибке.
- Для каждого подозрительного объекта установлены входящие и исходящие ссылки.
- Сырые и декодированные потоки сохранены раздельно.
- Цепочка фильтров и все ручные преобразования задокументированы.
- JavaScript приведён к читаемому виду без открытия PDF в пользовательском просмотрщике.
- Вложения определены по сигнатуре, а не по имени из FileSpec.
- При наличии Updates проверены changelog и отдельные ревизии.
- Ошибки парсинга не были скрыты из финального вывода.
- Критические наблюдения подтверждены другим инструментом или прямыми байтами.
Завершённый анализ peepdf должен оставлять не коллекцию цветных предупреждений, а проверяемую карту документа: корневой объект, путь запуска, номера действий и потоков, состояние каждой ревизии, сохранённые данные и объяснение ограничений. Такая карта позволяет решить, что делать дальше — закрыть ложное срабатывание, передать вложение на отдельный анализ, построить индикаторы или исследовать поведение в песочнице — не подвергая рабочую систему ненужному риску.