PDFid быстро проверяет PDF-файл без его открытия в просмотрщике: показывает заголовок документа, считает объекты, потоки, страницы и маркеры конца файла, отмечает JavaScript, автоматические действия, вложения, шифрование и другие конструкции, а режимы дополнительной статистики, пользовательских индикаторов и пакетной обработки помогают решить, какой образец нужно разбирать глубже.
Работа строится вокруг короткой команды и компактного отчёта в терминале. Пользователь передаёт один файл, архив, набор имён или каталог, после чего сопоставляет обычные структурные счётчики с потенциально опасными именами. Такой результат удобен для первичной сортировки вложений из почты, проверки документов из общего хранилища и подготовки очереди для детального анализа.
PDFid не выносит окончательный вердикт о безопасности. Он ищет последовательности байтов и распознаёт PDF-имена, включая записи с шестнадцатеричной обфускацией, но не строит полное дерево объектов и не раскрывает содержимое сжатых потоков объектов. Поэтому сильный сигнал в отчёте нужно подтверждать парсером, а нулевые значения нельзя считать доказательством отсутствия угрозы.
Скачать PDFid
- Редактирование PDF
- Русский интерфейс
- Просто новичкам
- Нет графического интерфейса
- Не читает ObjStm изнутри
- Требуется Python
Как устроен рабочий процесс PDFid
Основной сценарий состоит из четырёх этапов. Сначала образец помещают в отдельную рабочую папку и не открывают двойным щелчком. Затем запускают PDFid и сохраняют вывод вместе с хешем исходного файла. После этого оценивают сочетания признаков, а не отдельную строку. Наконец, документы с JavaScript, автозапуском, вложениями, необычной структурой или обфускацией передают инструменту, который умеет разбирать объекты и декодировать потоки.
Терминальный отчёт начинается с имени обработанного файла и найденного заголовка PDF. Ниже идут пары индикатор — количество. В верхней части расположены счётчики каркаса документа: объекты, потоки, таблицы перекрёстных ссылок, страницы. В нижней — имена, которые часто встречаются в интерактивных формах, мультимедийных документах и вредоносных образцах. Такое расположение позволяет за несколько секунд увидеть, похож ли файл на простой скан, форму, документ со ссылками или контейнер со сложной логикой.
Полезно заранее определить правило эскалации. Например, один только /AcroForm в анкете не требует того же уровня внимания, что сочетание /JavaScript, /OpenAction и обфусцированного имени. Один /EmbeddedFile в отчёте с приложенным XML может быть ожидаемым, но вложение вместе с /Launch уже требует извлечения объекта и проверки его типа. Формализованное правило уменьшает субъективность и помогает одинаково обрабатывать большие партии документов.

Подготовка к первому запуску
Для запуска нужен совместимый интерпретатор Python. Архив с программой распаковывают в каталог без ограничений на запись, потому что режим журнала и функция disarm создают рядом новые файлы. В рабочей папке удобно держать сам скрипт, дополнительные плагины, файл pdfid.ini и отдельный подкаталог с исследуемыми документами. Образцы не следует смешивать с обычными рабочими PDF, чтобы случайно не открыть подозрительное вложение.
Проверить доступность интерпретатора можно командой python --version или, в системах, где команда разделена по поколениям, python3 --version. Если оболочка сообщает, что команда не найдена, нужно добавить Python в PATH либо вызвать его по полному пути. Ошибка, в которой оболочка не находит pdfid.py, решается переходом в каталог скрипта или указанием полного пути к нему; переименование исследуемого файла здесь не поможет.
Первый тест лучше выполнять на заведомо обычном PDF, созданном из текстового редактора. Это даёт эталон: видны парные значения obj/endobj и stream/endstream, одна или несколько страниц, а большинство риск-индикаторов равны нулю. После такого теста легче отличить ошибку запуска от корректного, но необычного отчёта. Для проверки синтаксиса доступна встроенная справка.
python pdfid.py -h
python pdfid.py sample.pdf

На Windows имена с пробелами заключают в кавычки; то же правило полезно и в других оболочках. Рабочий каталог желательно сделать коротким и понятным, например с подпапками input, output и logs. Исходник оставляют неизменным, а любые производные файлы создают в output. Такой порядок особенно важен при расследовании: можно доказать, какой именно байтовый объект был получен, а какой появился после анализа.
Первая проверка одного PDF
Базовый вызов содержит только имя скрипта и путь к документу. PDFid читает байты, ищет заголовок %PDF в начале доступной области и выводит стандартный набор счётчиков. Он не визуализирует страницы и не запускает встроенные действия документа, поэтому первичная проверка не требует открытия образца в PDF-просмотрщике. Тем не менее сам файл следует считать недоверенным на протяжении всего процесса.
python pdfid.py "samples/invoice.pdf"
Сначала проверьте строку PDF Header. Значение вроде %PDF-1.4 или %PDF-1.7 подтверждает, что найден узнаваемый заголовок, но не доказывает корректность всего файла. Отсутствие заголовка может означать неверный формат, повреждение, преднамеренно изменённое начало или упаковку данных в другой контейнер. Переходить к --force стоит только после проверки расширения, сигнатуры и пути.
Далее сравните obj с endobj и stream с endstream. Равные числа часто встречаются в корректно сформированном документе, но расхождение не является самостоятельной сигнатурой вредоносности: PDF допускает обновления, повреждённые участки и нестандартные генераторы. Значительная разница служит поводом открыть структуру парсером и определить, где начинаются незакрытые или ложные токены.
Число /Page показывает найденные имена страниц, а не гарантированное количество видимых листов во всех случаях. На него могут влиять структура дерева страниц, старые ревизии и байтовые совпадения. Используйте значение как ориентир сложности: одностраничный счёт с сотнями объектов и большим количеством потоков заслуживает дополнительного внимания, но сложный отчёт с графикой тоже законно содержит множество объектов.
Как читать структурные счётчики
obj и endobj
Пара obj/endobj отражает границы косвенных объектов. В обычном файле значения часто совпадают. Если obj заметно больше, возможны обрезанный конец документа, повреждение или последовательности, похожие на токен, внутри данных. Если endobj больше, возможны старые обновления, мусор после окончания файла или совпадения в потоках. PDFid не связывает начало и конец по номерам, поэтому для точной локализации нужен объектный парсер.
Количество объектов само по себе не определяет безопасность. Маленький файл может содержать один объект с действием открытия и скриптом, а большой каталог — тысячи безопасных объектов шрифтов и изображений. Практическую ценность число получает в сравнении с назначением документа, размером файла и другими строками отчёта.
stream и endstream
Потоки хранят сжатый контент страниц, изображения, шрифты, вложенные данные и код. Парность stream/endstream помогает заметить грубые нарушения структуры, но сканер не декодирует каждое содержимое. Высокая энтропия внутри потоков часто естественна для JPEG, сжатого текста или встроенного шрифта. Поэтому необычное число потоков — повод определить их типы, а не основание немедленно помечать документ вредоносным.
xref, trailer и startxref
xref и trailer характерны для классической таблицы перекрёстных ссылок, а startxref указывает на её расположение. В PDF с потоками перекрёстных ссылок обычные xref и trailer могут не встречаться как отдельные токены. Нулевое значение в таком документе не обязательно означает повреждение. Если средство проверки структуры подтверждает xref stream, отсутствие классических строк объяснимо форматом.
Несколько startxref и несколько маркеров %%EOF нередко появляются после инкрементальных сохранений: редактор добавляет новую ревизию, не переписывая прежнюю. Это важно при анализе, потому что старые объекты продолжают физически присутствовать в файле и попадают в строковый подсчёт, даже если текущая ревизия на них не ссылается. Для ответа, какой объект активен, нужно учитывать цепочку обновлений.
/Page
Счётчик /Page удобен как быстрый ориентир, но он не заменяет чтение дерева /Pages. Встреча имени в старой ревизии, шаблоне формы или потоке может изменить итог. Если бизнес-процесс ожидает одну страницу, а счётчик неожиданно велик, сравните видимое дерево страниц, ссылки на дочерние узлы и наличие инкрементальных обновлений.
Индикаторы действий и активного содержимого
Нижняя часть стандартного отчёта собрана не как антивирусная база, а как набор имён, полезных для триажа. Каждое из них имеет законные применения. Риск возникает из контекста: какое действие запускается, на какой объект оно ссылается, что находится в потоке, существует ли обфускация, совпадает ли поведение с назначением документа.

/JS и /JavaScript
/JS и /JavaScript указывают на механизм JavaScript в PDF. Скрипты используют для вычислений в формах, проверки полей, кнопок и автоматизации, но тот же механизм применяют для запутывания ссылок, подготовки эксплуатации уязвимости или запуска цепочки действий. Ненулевое значение требует найти объект со скриптом, декодировать поток при необходимости и прочитать код, не выполняя его в обычном просмотрщике.
Особенно внимательно оценивают сочетание JavaScript с /OpenAction или /AA. В таком случае код может запускаться при открытии документа, входе на страницу, перемещении указателя или другом событии. Сам PDFid показывает только наличие имён, поэтому не сообщает, какое именно событие связано со скриптом и достижим ли объект в активной ревизии.
/AA и /OpenAction
/OpenAction описывает действие, связанное с открытием документа. Это может быть переход на страницу, настройка масштаба или запуск скрипта. /AA обозначает дополнительные действия, которые привязывают к событиям документа, страницы, аннотации или формы. В отчёте важно не просто отметить количество, а найти словарь действия и проследить ссылки до целевого объекта.
Если /OpenAction есть, а /JS и /JavaScript равны нулю, действие всё равно требует проверки: оно может вести к другому типу операции или ссылаться на имя, скрытое в потоке объектов. Аналогично нулевой /OpenAction не исключает запуск через /AA, действие аннотации или цепочку внутри формы.
/Launch
/Launch связан с запуском внешнего приложения или открытием файла средствами операционной системы. Для обычного статического документа это сильный повод к ручному разбору. Нужно установить, какой словарь File Specification используется, на какое имя файла он указывает и не скрыто ли вложение в /EmbeddedFile. Не следует проверять поведение запуском образца на рабочем компьютере.
/RichMedia
/RichMedia относится к мультимедийному содержимому и связанным действиям. Он может встречаться в презентационных материалах, но в счетах, резюме и сканах выглядит нетипично. Проверьте аннотацию RichMedia, конфигурации активации и вложенные ресурсы. Простое удаление видимой аннотации редактором не гарантирует удаления связанных объектов из старых ревизий.
/JBIG2Decode и /Colors > 2^24
/JBIG2Decode обозначает фильтр сжатия двухцветных изображений. Он законен для сканов, однако исторически был связан с уязвимостями, поэтому присутствует в наборе индикаторов. Строка /Colors > 2^24 — специальная проверка аномально большого значения палитры после имени /Colors. Современное решение должно оценивать эти строки вместе с типом изображения, корректностью фильтра и поведением целевого просмотрщика.
Формы, вложения и шифрование
/AcroForm и /XFA
/AcroForm указывает на интерактивную форму: поля, кнопки, вычисления и подписи. /XFA относится к XML Forms Architecture и может сопровождать динамические формы. Оба имени широко используются в легитимных анкетах, поэтому их наличие не является тревогой без дополнительных факторов. Важны скрипты вычисления, события полей, внешние подключения, скрытые значения и несоответствие формы заявленному назначению.
Для формы полезно сопоставить /AcroForm с /JS, /JavaScript, /AA и /OpenAction. Если скрипты отсутствуют, остаются риски ссылок, вложений и уязвимостей в декодерах, но сценарий активной формы становится менее вероятным. Если JavaScript есть, изучают не только видимые поля, но и общие сценарии документа и действия на уровне каталога.
/EmbeddedFile
/EmbeddedFile показывает, что в PDF встречается тип вложенного файла. Это может быть исходный документ, таблица, XML для электронного счёта, аудиофайл или произвольный двоичный объект. PDFid не извлекает вложение и не определяет его настоящий формат. Следующий шаг — найти File Specification, извлечь поток в карантин, вычислить хеш и проверить сигнатуру независимо от имени и расширения.

Счётчик /EmbeddedFile может быть нулевым, хотя данные спрятаны в обычном потоке или нестандартной структуре. И наоборот, одно вложение в архивном PDF может быть ожидаемым. Контекст дают имя файла, MIME-параметры, размер, связи с аннотациями и действиями, а также возможность извлечь объект без исполнения.
/Encrypt
/Encrypt сообщает о словаре шифрования. Защита паролем — обычная функция, а не признак вредоносности. Ограничение анализа в том, что часть содержимого может быть недоступна без пароля. Сначала фиксируют, открывается ли документ с пустым паролем, какой алгоритм и права указаны, затем повторяют структурный анализ после корректной расшифровки копии. Не следует подменять отсутствие видимых индикаторов в зашифрованной области выводом опасности нет.
Object Streams: что означает /ObjStm
/ObjStm обозначает поток объектов — контейнер, в котором несколько PDF-объектов хранятся в сжатом виде. Это штатная возможность формата и часто встречается в современных генераторах. Для триажа она важна потому, что опасные имена и действия могут находиться внутри сжатого содержимого, куда простой строковый проход не заглядывает после декодирования.

Если /ObjStm больше нуля, нулевые /JS, /OpenAction или /EmbeddedFile нельзя трактовать как окончательные. Передайте файл парсеру с поддержкой извлечения объектов из Object Streams и повторите поиск по декодированному содержимому. В связке с pdf-parser.py для этого применяют режим обработки object streams; затем ищут интересующие имена и просматривают конкретные объекты.
Наличие /ObjStm не означает обфускацию с вредоносной целью. Многие инструменты оптимизируют PDF именно так. Приоритет повышают другие признаки: неожиданно малая видимая страница при большой массе сжатых объектов, несовпадение заявленного назначения с вложенными действиями, шестнадцатерично закодированные имена, повреждённая структура и данные после последнего %%EOF.
Полезный порядок действий: зафиксировать исходный отчёт, проверить структуру независимым средством, извлечь объекты из каждого /ObjStm, повторить поиск имён уже по распакованным объектам, а затем проследить ссылки от каталога и страниц. Так сохраняется различие между фактом физического присутствия токена и его достижимостью в текущем дереве документа.
Обфускация имён и числа в скобках
PDF допускает запись символов имени через знак решётки и две шестнадцатеричные цифры. Например, часть имени JavaScript можно представить кодами символов. PDFid декодирует такую форму при подсчёте и отдельно показывает, сколько совпадений было обфусцировано. В строке вроде /JavaScript 1(1) первое число означает общее количество, а число в скобках — количество имён с шестнадцатеричным кодированием.
Обфускация не всегда злонамеренна: генератор мог экранировать символы автоматически. Однако для короткого делового PDF скрытая запись /JS, /OpenAction или /Launch заметно повышает приоритет. Аналитик должен извлечь объект в исходном и декодированном виде, проверить соседние имена и определить, является ли кодирование частью более широкой попытки затруднить анализ.
Скобки особенно полезны при пакетной сортировке. Два файла могут иметь одинаковое общее число /JavaScript, но один содержит обычное имя, а другой — закодированное. При ограниченном времени второй разумно разбирать раньше, не объявляя его вредоносным автоматически. Порог лучше фиксировать в процедуре обработки, чтобы одинаковые признаки давали одинаковое решение.
При проверке отчёта помните, что PDFid считает имена независимо от их активности. Обфусцированное имя может находиться в неиспользуемой старой ревизии или в данных изображения, а активное действие может быть спрятано в сжатом объекте и не попасть в базовый вывод. Поэтому число в скобках — индикатор направления поиска, а не итоговый класс файла.
Режим --extra: даты, EOF и энтропия
Параметр -e или --extra добавляет даты PDF, число маркеров %%EOF, количество байтов после последнего маркера, общую энтропию и отдельные оценки для областей внутри и вне потоков. Этот режим полезен, когда стандартные имена не дали явного сигнала, но размер, происхождение или поведение документа вызывают вопросы.
python pdfid.py -e "samples/report.pdf"

Маркеры %%EOF
Один %%EOF и ноль байтов после него соответствуют простому файлу без дописанного хвоста, но не гарантируют безопасность. Несколько %%EOF часто объясняются инкрементальными обновлениями. Значительное число байтов после последнего маркера может указывать на добавленные данные, мусор, второй формат или повреждение. Нужно посмотреть хвост в шестнадцатеричном представлении и определить сигнатуру, не запуская найденное содержимое.
Иногда корректный генератор оставляет пробелы или перевод строки после %%EOF. Небольшое значение не следует считать атакой. Важны масштаб, структура байтов и сочетание с другими признаками. Если после конца обнаруживается ZIP, исполняемый заголовок или сценарий, такой хвост извлекают как отдельный артефакт и проверяют независимо.
Даты
PDFid выводит найденные строки даты вместе с именем, после которого они встретились. Сравнивают CreationDate и ModDate, часовые пояса, хронологию и согласованность с контекстом. Невозможная дата, изменение раньше создания или множество противоречивых меток могут отражать плохой генератор, ручную подмену или старые ревизии. Даты легко изменить, поэтому они служат подсказкой, а не надёжным доказательством происхождения.
Энтропия
Энтропия оценивает распределение байтов: сжатые и зашифрованные области обычно дают высокие значения, простой текст — более низкие. PDFid показывает общую оценку, а также области внутри и вне потоков. Высокая энтропия внутри потоков ожидаема для изображений и сжатого контента. Высокая энтропия вне потоков выглядит необычнее и может направить поиск к данным, размещённым вне стандартных контейнеров.
Сравнивать значения лучше между документами одного типа и генератора. Сканированный договор и текстовый счёт имеют разный нормальный профиль. Энтропия не определяет алгоритм сжатия и не различает шифр, изображение и случайные данные. Она помогает решить, куда смотреть дальше, но не заменяет извлечение и идентификацию конкретного потока.
Как убрать нулевые строки параметром --nozero
Параметр -n или --nozero скрывает строки, где количество равно нулю. Для ручного просмотра это сокращает отчёт до реально найденных имён и облегчает сравнение нескольких файлов в терминале. Особенно удобно при фишинговых PDF, где нужно быстро увидеть /URI, /EmbeddedFile или /ObjStm без длинного блока нулей.
python pdfid.py -n "samples/message.pdf"
Сокращённый вывод не меняет сканирование и не исключает индикаторы из проверки; он только скрывает нулевые результаты при печати. Для отчётности и обучения полная форма иногда лучше, потому что явно показывает, какие признаки проверялись. В автоматизированном процессе следует заранее выбрать единый формат, иначе сравнение журналов будет неудобным.
Не путайте отсутствие строки при -n с ошибкой программы. Если отчёт содержит заголовок и несколько структурных счётчиков, скрытые имена просто равны нулю. Если же нет даже заголовка результата, проверьте путь, шаблон имени, права доступа и сообщения перед отчётом.
Просмотр всех PDF-имён
Параметр -a или --all заставляет PDFid показывать все найденные имена, начинающиеся с косой черты, а не только стандартный набор. Это полезно при исследовании нестандартного действия, неизвестного генератора, редкого мультимедийного объекта или попытки спрятать значимый словарь под необычным именем.
python pdfid.py -a "samples/unknown.pdf"
Результат может быть длинным: шрифты, ресурсы, цветовые пространства и внутренние ключи дают десятки или сотни строк. Сначала выделите имена действий, файловых спецификаций, фильтров и сетевых переходов, затем сопоставьте их с объектами через парсер. Сам режим all не сообщает номера объектов, поэтому не заменяет поиск по структуре.
Этот режим также помогает создать профиль доверенного генератора. Если несколько документов от одного источника дают одинаковый набор имён, а новый образец добавляет /Launch, /RichMedia или необычный фильтр, различие становится заметным. Сравнение должно учитывать обновление шаблонов и форм, иначе нормальное изменение бизнес-документа даст ложную тревогу.
Пользовательские индикаторы в pdfid.ini
Файл pdfid.ini размещают рядом со скриптом. В секции [keywords] перечисляют дополнительные имена, которые нужно считать вместе со стандартными. Регистр сохраняется, поэтому запись должна соответствовать PDF-имени. Значение после ключа не требуется: достаточно отдельной строки с нужным именем.
[keywords]
/URI
/GoToR
/SubmitForm
/URI полезен для документов со ссылками, /GoToR — для переходов к внешнему PDF или назначению, /SubmitForm — для отправки данных формы. Конкретный набор следует выбирать по задаче и проверять на тестовом файле. Слишком широкие или короткие ключи создают шум, поскольку PDFid ищет байтовые имена и не оценивает семантику объекта.
После изменения ini запустите контрольный документ и убедитесь, что новые строки появились в отчёте. Если их нет, проверьте имя файла, секцию, каталог запуска и регистр. Скрипт ищет конфигурацию в своём каталоге, а не обязательно в текущей рабочей папке. При нескольких копиях PDFid легко отредактировать один ini и запустить другой скрипт; полный путь устраняет неоднозначность.
Пользовательские индикаторы удобны для повторяемой процедуры, но их нужно документировать. Запишите, зачем добавлено имя, в каких документах оно нормально и какое действие требуется при ненулевом результате. Без контекста новый сотрудник может воспринимать любой /URI как вредоносность, хотя ссылка в отчёте или инструкции часто ожидаема.

Проверка ссылок и фишинговых PDF
Фишинговый PDF часто содержит мало объектов, одну страницу и одну или несколько ссылок. В базовом наборе /URI может отсутствовать, поэтому его добавляют через pdfid.ini или используют подготовленную конфигурацию. Ненулевой счётчик сообщает о наличии имени, но не показывает адрес, подпись ссылки и область страницы.
Следующий шаг — найти объект /URI парсером и извлечь строку без перехода по ней. Сравните отображаемый текст, фактический адрес, схему, домен, кодировку, перенаправления и контекст письма. Даже легитимный домен может быть использован в открытом редиректе, а строка может собираться из нескольких частей. PDFid выполняет роль фильтра: быстро выделяет документы, где вообще есть соответствующая конструкция.
Отсутствие /URI не исключает фишинг. Ссылка может быть оформлена через другой тип действия, запуск вложения, форму, QR-код на изображении или текст, который пользователь должен набрать вручную. Для скана с QR-кодом строковый анализ структуры не увидит адрес; потребуется анализ изображения. Поэтому проверка ссылок должна учитывать видимое содержимое и тип аннотаций.
При пакетной обработке полезно выделить отдельную категорию ссылка без активного кода. Такие файлы не требуют декодирования JavaScript, но требуют анализа назначения перехода и репутации инфраструктуры. Это экономит время: сложные документы с действиями идут объектному аналитику, а простые ссылочные приманки — специалисту по фишингу.
Пакетная обработка нескольких файлов
PDFid принимает несколько путей в одном вызове. Результаты идут последовательно, каждый блок начинается с имени файла. Для небольшой папки можно перечислить документы явно или использовать шаблон оболочки. В журнале обязательно сохраняйте полные имена, иначе одинаковые названия из разных каталогов трудно различить.
python pdfid.py sample1.pdf sample2.pdf sample3.pdf
python pdfid.py "incoming/*.pdf"
Поддержка шаблонов реализована внутри обработки аргументов. Символы *, ? и пары квадратных скобок могут восприниматься как wildcard. Это удобно для серий, но создаёт известную проблему с реальными именами вроде report[1].pdf. Если файл не обрабатывается и появляется предупреждение о шаблоне, включите literalfilenames.
python pdfid.py --literalfilenames "incoming/report[1].pdf"
Параметр -l отключает разворачивание шаблонов для переданных имён. Он нужен не только для квадратных скобок, но и для файлов, содержащих знаки вопроса или звёздочки в системах, где такие символы допустимы. При этом шаблонный массовый выбор перестаёт работать, поэтому literalfilenames применяют к конкретным путям, а не к каталогу целиком.
Для большого списка используйте аргумент вида @files.txt. В текстовом файле каждое имя располагают на отдельной строке. Это уменьшает длину команды и делает состав партии воспроизводимым. Список следует сохранять вместе с результатом, чтобы позже можно было повторить проверку той же выборки.
python pdfid.py @files.txt
При построении списка избегайте пустых, случайно обрезанных и относительных путей, зависящих от текущего каталога. Лучше сформировать абсолютные пути или запускать процедуру из закреплённой рабочей папки. Если один файл не существует, сообщение о пропуске нужно сохранить: молчаливое отсутствие образца может исказить статистику партии.
Сканирование каталогов и рекурсия
Параметр -s или --scan обходит переданный каталог и автоматически записывает результаты в PDFiD.log. Реализация заходит во вложенные каталоги, поэтому перед запуском оцените объём дерева и исключите области, где находятся системные файлы или большие архивы, не относящиеся к задаче.
python pdfid.py --scan incoming
При scan обрабатываются встреченные элементы, а не только файлы с расширением .pdf. Это помогает выявлять PDF под чужим расширением, но увеличивает число строк Not a PDF document и ошибок доступа. Для точной выборки удобнее --recursedir с маской, например для файлов, оканчивающихся на .pdf. Выбор зависит от цели: поиск скрытых PDF требует широкого обхода, а инвентаризация — фильтра.
python pdfid.py --recursedir "incoming/*.pdf"
Рекурсивный режим поддерживает шаблоны и списки @file. Перед массовым запуском испытайте маску на небольшой копии дерева. Ошибка в каталоге может привести к обработке тысяч лишних файлов, большому журналу и длительной работе. PDFid читает содержимое последовательно, поэтому время примерно растёт с суммарным объёмом данных.
Для повторяемых проверок указывайте собственный журнал параметром -o. В scan без него используется PDFiD.log, который открывается на добавление. Старые и новые результаты могут оказаться в одном файле. Имя с датой партии и отдельная папка logs упрощают аудит и предотвращают смешение запусков.
python pdfid.py --recursedir "incoming/*.pdf" -o "logs/batch-01.txt"
ZIP-архивы и пароль infected
В качестве входа можно передать ZIP. PDFid открывает первый элемент архива и пробует пароль infected, который часто используют для безопасной пересылки исследовательских образцов. Это удобно в лаборатории, потому что подозрительный PDF не приходится вручную извлекать в каталог, где его можно случайно открыть.
Есть важное ограничение: анализируется первый элемент списка архива. Если ZIP содержит несколько документов, порядок может не совпасть с ожиданием. Перед проверкой просмотрите список содержимого безопасным архиватором или создайте отдельный контейнер для каждого образца. Не воспринимайте отчёт одного элемента как результат для всего архива.
Для AES-защищённых ZIP поддержка зависит от библиотеки pyzipper. При её отсутствии скрипт использует стандартный модуль zipfile, который подходит не для всех методов шифрования. Ошибка открытия архива может означать неверный пароль, неподдерживаемое шифрование, повреждение или отсутствие элементов. Установите pyzipper в тот же Python, которым запускается PDFid, и повторите тест.
Архив не обезвреживает содержимое сам по себе. Храните образцы в каталоге с ограниченными правами, не включайте предварительный просмотр и не распаковывайте их в общую папку. Пароль infected является соглашением, а не средством защиты от целевого доступа; его задача — предотвратить случайное автоматическое открытие и сканирование на пути передачи.
python pdfid.py "samples/suspicious.zip"
Получение файла по сетевому адресу
PDFid умеет принять сетевой адрес вместо пути к файлу и загрузить данные для сканирования. Встроенный запрос использует короткий тайм-аут, после чего отчёт строится так же, как для файла. Возможность полезна при проверке известного образца, но создаёт сетевой контакт с удалённым сервером и может раскрыть адрес аналитической системы.
Для расследования предпочтительнее сначала получить файл контролируемым способом, сохранить исходные заголовки ответа, вычислить хеш и анализировать копию. Так сохраняются доказательства и исключается ситуация, когда содержимое по адресу меняется между запусками. Не передавайте в команду ссылку из фишингового письма с рабочей станции: сам запрос может подтвердить активность получателя или вызвать перенаправление.
Если сетевой запрос завершается ошибкой, PDFid выводит сообщение доступа. Возможные причины — тайм-аут, TLS-политика среды, перенаправление, авторизация, блокировка прокси или удаление файла. Это не ошибка PDF-анализа. Загрузите объект отдельным инструментом в изолированной сети и передайте сохранённый файл, чтобы отделить транспортную проблему от структуры документа.
Режим --force для нестандартных входных данных
По умолчанию PDFid ищет узнаваемый %PDF в начальной области и сообщает, что вход не является PDF, если заголовок не найден. Параметр -f или --force заставляет продолжить поиск ключевых слов даже без корректного заголовка. Он пригоден для фрагмента файла, извлечённого потока, повреждённого документа или данных с преднамеренно изменённым началом.
python pdfid.py --force "fragments/part.bin"
Force повышает число ложных совпадений: произвольный двоичный файл может содержать последовательности obj, stream или /JS без отношения к PDF-синтаксису. Результат нужно описывать как скан байтов по индикаторам, а не как подтверждение PDF. Сначала проверьте сигнатуру, размер, происхождение и видимые строки, затем используйте парсер в режиме восстановления, если структура действительно напоминает PDF.
Force также полезен в конвейере через стандартный ввод. Если аргументы отсутствуют, скрипт читает stdin. Режим disarm и scan при таком вводе отключаются, потому что им нужны имена и файлы назначения. В конвейере важно не потерять границы объектов: объединение нескольких файлов в один поток делает счётчики непригодными для раздельного решения.
Нестандартный заголовок иногда расположен после служебного префикса. PDFid ищет %PDF в первых 1024 байтах, поэтому небольшой префикс может быть принят. Если заголовок дальше, базовый режим его не увидит. Не перемещайте байты в исходнике ради удобства; создайте копию или используйте средство карвинга, сохранив смещение и хеш исходного объекта.
Disarm: что делает параметр -d
Параметр -d создаёт рядом с исходником файл с вставкой .disarmed перед расширением. Во время копирования он меняет регистр букв в именах /JS, /JavaScript, /AA, /OpenAction, /JBIG2Decode, /RichMedia и /Launch. PDF-имена чувствительны к регистру, поэтому просмотрщик перестаёт распознавать эти конкретные конструкции в изменённой копии.
python pdfid.py --disarm "samples/suspicious.pdf"
Функция не удаляет потоки, вложения и код. Она не проверяет все возможные действия и не переписывает дерево объектов. В список замены не входит /EmbeddedFile, а вредоносность может находиться в изображении, шрифте, уязвимом фильтре, сжатом объекте или нестандартной цепочке. Получившийся файл нельзя считать безопасным и открывать на рабочем компьютере как очищенный документ.
Disarm полезен как исследовательская копия для сравнения поведения и как способ нарушить известные автоматические триггеры. Сохраните исходный хеш и хеш производного файла, отметьте точную команду и не заменяйте оригинал. При юридически значимом анализе любые изменения должны быть отделены от исходного доказательства.
Если имя было записано шестнадцатеричными кодами, PDFid сохраняет форму записи, но меняет регистр букв декодированного имени при создании копии. В консоли отображаются пары преобразования. Этот вывод стоит включить в журнал: он показывает, какие конструкции действительно были затронуты.
Disarm не поддерживается для стандартного ввода, поскольку скрипту нужно сформировать имя выходного файла. Если команда читает pipe и одновременно содержит -d, функция отключается с сообщением. Сначала сохраните поток в карантинный файл, вычислите хеш, затем применяйте disarm к копии.
Плагины, оценки и инструкции
Параметр -p загружает один или несколько Python-плагинов. Плагин получает объект со счётчиками PDFid, вычисляет оценку и при необходимости возвращает текстовую инструкцию. Такая схема позволяет формализовать правила триажа: например, повысить балл за автозапуск и обфускацию, снизить приоритет простого документа без активных конструкций или выделить ссылочные приманки.
python pdfid.py -p plugin_triage "samples/message.pdf"

Плагин исполняется как Python-код с правами пользователя, запустившего команду. Загружайте только проверенные файлы из доверенного источника, просматривайте код и храните контрольную сумму. Подмена плагина опаснее неправильной оценки: вредоносный Python-файл может выполнить произвольные действия ещё до анализа PDF.
Несколько плагинов перечисляют через запятую; также поддерживается @file со списком. Если имя не оканчивается на .py, расширение добавляется автоматически. Скрипт сначала ищет указанный путь, затем каталог рядом с собой. При ошибке загрузки включите -v, чтобы получить исключение и увидеть точное место проблемы.
Минимальная оценка
Параметр -m задаёт минимальный score, при котором результат плагина выводится. Он помогает скрыть низкоприоритетные образцы в большой партии. Порог имеет смысл только в контексте конкретного плагина: шкала и формула определяются его кодом. Нельзя переносить число 0,5 или 1,0 между разными плагинами без изучения их логики.
python pdfid.py -p plugin_triage -m 0.75 "incoming/*.pdf"
CSV
Параметр -c формирует CSV при работе с плагинами или выборкой. Для плагина строки содержат имя файла, имя плагина и score, разделённые точкой с запятой. Такой формат удобно импортировать в таблицу или систему обработки событий. Проверьте кодировку имён и настройку разделителя при открытии, иначе столбцы могут слиться.

Опции плагина
--pluginoptions передаёт строку настроек экземпляру плагина. Значение и синтаксис зависят от конкретного файла. Заключайте строку в кавычки, если она содержит пробелы или символы оболочки. Не придумывайте параметры по аналогии: неверная строка может быть проигнорирована или вызвать исключение.
Выборка выражением --select
Параметр -S принимает выражение Python, которое вычисляется для объекта pdf со счётчиками. Если выражение истинно, отчёт выводится. Это позволяет, например, оставить файлы с JavaScript, объектными потоками или несогласованными структурными числами без отдельного плагина.
python pdfid.py -S "pdf.javascript.count > 0 or pdf.js.count > 0" "incoming/*.pdf"
Доступные поля соответствуют стандартным индикаторам: obj, endobj, stream, endstream, xref, trailer, startxref, page, encrypt, objstm, js, javascript, aa, openaction, acroform, jbig2decode, richmedia, launch, embeddedfile и xfa. Для большинства используется свойство count, а для обфускации — hexcode. Перед сложным правилом проверьте выражение на нескольких известных образцах.
Выражение исполняется через eval, поэтому строка должна рассматриваться как код. Не принимайте её из недоверенного источника и не подставляйте пользовательский ввод в автоматизации. Ошибка синтаксиса приводит к сообщению об оценке выражения; с -v исключение будет поднято, что удобно для отладки, но может остановить пакетный сценарий.
Выборка хорошо подходит для предварительной очереди. Например, отдельное правило выделяет обфусцированные имена, другое — JavaScript вместе с автодействием, третье — несоответствие obj/endobj. Однако сложную политику легче сопровождать в плагине, где можно добавить комментарии, тесты, score и понятную инструкцию аналитику.
Сохранение журнала
Параметр -o добавляет весь печатаемый результат в указанный файл. Если журнал уже существует, новые блоки дописываются в конец. Это важно учитывать при повторном запуске: одинаковый образец может появиться несколько раз, а без временной метки неясно, к какой партии относится блок.
python pdfid.py -e -o "logs/report.txt" "samples/report.pdf"
Хороший журнал сопровождают отдельной таблицей с хешем, размером, временем получения, источником внутри организации и командой запуска. Сам вывод PDFid не вычисляет криптографический хеш и не фиксирует время. Эти данные добавляют внешним скриптом или процедурой. Имя исходного файла не является устойчивым идентификатором: его легко изменить, а разные файлы могут называться одинаково.
Для пакетной автоматизации записывайте stderr и код завершения отдельно от обычного отчёта. Некоторые ошибки выводятся в консоль и могут затеряться, если сохранять только стандартный поток. После обработки сравните число входных путей, успешных блоков и пропусков. Это обнаруживает ситуацию, когда wildcard не совпал ни с одним файлом или каталог оказался недоступен.
Как распознавать ложные срабатывания
PDFid является строковым сканером, поэтому последовательность /JS может случайно встретиться внутри сжатого изображения или другого двоичного потока. В отчёте она будет посчитана, хотя не является PDF-именем активного объекта. Такое ложное срабатывание особенно вероятно для коротких токенов, прежде всего /JS и /AA.

Проверка начинается с поиска каждого совпадения парсером, который умеет показать окружающий объект и позиции. Если токен находится в байтах JPEG, шрифта или сжатого потока и не участвует в словаре, риск снижается. Если имя расположено в словаре действия и на него есть ссылка из каталога, страницы, аннотации или формы, сигнал подтверждается.
Не используйте правило один /JS — вредоносный файл. Законная форма может содержать десятки сценариев, а случайные байты дают единичное совпадение. Сопоставляйте тип документа, число страниц, автодействия, обфускацию, вложения, URI, структуру и результаты декодирования. Чем больше независимых признаков указывают на одну цепочку, тем увереннее вывод.
Ложным может быть не только риск-индикатор, но и структурный токен. Слова stream и endstream иногда встречаются внутри данных. Инкрементальные обновления оставляют старые объекты, которые уже не используются. Поэтому равенство счётчиков — полезная эвристика, а не строгая валидация грамматики PDF.
Нулевая строка тоже может вводить в заблуждение. Имя может быть разделено, скрыто внутри Object Stream, зашифровано, представлено другим механизмом или отсутствовать при наличии уязвимого бинарного объекта. Хорошая процедура использует PDFid для выбора следующего шага, а не для финального разрешения открыть документ.
Разбор типовых сочетаний индикаторов
JavaScript и OpenAction
Сочетание /JavaScript или /JS с /OpenAction означает, что документ содержит и сценарный механизм, и действие открытия. Найдите объект каталога, значение OpenAction и цепочку ссылок. Если действие ведёт к JavaScript, извлеките код и проанализируйте строки, функции, декодирование и обращения к объектам документа. Не выполняйте сценарий в обычном просмотрщике.
JavaScript без действий
JavaScript без /OpenAction и /AA может быть привязан к кнопке формы или не использоваться вовсе. Проверьте /AcroForm, аннотации и именованные сценарии. Вредоносный документ может ожидать щелчка пользователя, поэтому отсутствие автозапуска снижает, но не устраняет риск. Для бизнес-формы сопоставьте код с расчётами полей и проверкой ввода.
ObjStm при нулевых риск-строках
Наличие /ObjStm и нулевые JavaScript, Launch, EmbeddedFile требуют декодировать object streams. Если после распаковки опасных имён нет, оцените обычные потоки, фильтры, изображения и шрифты. Если имена появляются только после распаковки, исходный отчёт следует дополнить пометкой о слепой зоне строкового сканирования.
EmbeddedFile и Launch
Сочетание вложения с запуском относится к высокому приоритету. Найдите Filespec, имя, описательную строку, поток EmbeddedFile и действие Launch. Извлеките вложение, определите формат по сигнатуре, вычислите хеш и проверьте статически. Даже если имя заканчивается на .txt или .pdf, фактический формат может быть другим.
Encrypt и минимум видимых данных
Шифрование вместе с почти пустым отчётом означает ограниченную наблюдаемость. Попробуйте разрешённые пароли, проверьте, открывается ли документ с пустой строкой, и создайте расшифрованную исследовательскую копию. Сравните хеши и повторите PDFid. Не считайте нулевые строки подтверждением чистоты, пока интересующая область остаётся зашифрованной.
URI без JavaScript
Одна или несколько /URI при отсутствии скриптов характерны для ссылочного фишинга и обычных документов со ссылками. Извлеките адреса, сравните их с видимым текстом и назначением документа. Приоритет зависит от домена, перенаправлений, контекста письма и оформления страницы, а не от самого факта URI.
Много EOF и данные после конца
Несколько %%EOF без данных после последнего часто объясняются обновлениями. Большой хвост после последнего %%EOF требует карвинга и идентификации. Если хвост содержит второй файл, сохраните его отдельно и проверьте. Если это только пробелы или служебная подпись, документируйте объяснение, чтобы признак не повторялся как необоснованная тревога.
Практический сценарий: вложение из электронной почты
Получив PDF из письма, сначала сохраните его без предварительного просмотра, вычислите SHA-256 и зафиксируйте заголовки сообщения. Запустите базовый PDFid, затем -e. Если документ заявлен как счёт на одну страницу, сравните /Page, количество объектов, потоки, вложения, URI и активные действия с ожидаемым профилем.
Если видны /URI, извлеките адреса и проверьте их отдельно. Если есть JavaScript или действия, перейдите к объектному парсеру. При /EmbeddedFile извлеките вложение в карантин. При /ObjStm декодируйте объектные потоки до интерпретации нулевых индикаторов. Каждый шаг должен создавать новый артефакт, а не изменять исходный PDF.
Если отчёт выглядит обычным, не открывайте файл автоматически только на этом основании. Проверьте почтовый контекст: отправителя, цепочку Reply-To, тему, ожидаемость счёта, цифровую подпись и репутацию вложения. PDFid покрывает структуру документа, но не проверяет социальную инженерию, изображения с QR-кодами и неизвестные уязвимости просмотрщика.
Решение можно оформить тремя уровнями: разрешить после независимых проверок, отправить на углублённый анализ или изолировать как подтверждённо опасный. PDFid чаще всего переводит файл между первым и вторым уровнем. Подтверждение третьего требует содержимого объекта, поведения, сигнатуры вложения или другого доказательства.
Практический сценарий: массовый триаж каталога
Для сотен файлов сначала сформируйте неизменяемый список путей и хешей. Запустите рекурсивную проверку с журналом, затем отдельный проход с плагином или select. Не смешивайте результаты разных правил в одну безымянную таблицу: колонка должна показывать, какое выражение или плагин сформировал score.
Полезно разбить очередь на группы: активные действия; вложения; ссылки; шифрование; объектные потоки; структурные аномалии; отсутствие сигналов. Один файл может попасть в несколько групп. Приоритет задают сочетания, происхождение и бизнес-контекст. Например, /AcroForm ожидаем в анкетах, но неожиданен в сканированном пропуске.
Для контроля качества вручную проверьте случайную выборку из низкоприоритетной группы. Это обнаруживает слепые зоны правила, ошибку конфигурации pdfid.ini и файлы, которые не были обработаны. Отдельно пересмотрите все сообщения Not a PDF document: среди них могут быть PDF с изменённым расширением, контейнеры или повреждённые образцы.
По окончании сохраните версию команды, конфигурацию, плагины, хеши и время запуска. Одна и та же партия с другим pdfid.ini даст иной набор строк, поэтому конфигурация является частью метода. Статистика без неё не воспроизводима.
Практический сценарий: проверка простого резюме или скана
Небольшой документ без форм часто содержит несколько объектов, пару потоков, одну страницу и нули в активных именах. Параметр -e помогает проверить, есть ли байты после %%EOF и насколько данные сосредоточены в потоках. Такой профиль согласуется с изображением страницы, но не доказывает, что изображение и его декодер безопасны.

Если потоков мало и почти весь объём находится внутри них, определите типы фильтров. JPEG, FlateDecode и шрифтовые потоки ожидаемы, но их содержимое всё равно может эксплуатировать ошибку декодера. Для документа из недоверенного письма безопаснее преобразовать страницу в изображение в изолированной среде или использовать защищённый процесс просмотра после проверки.
Даты и метаданные сравнивают с письмом, но не считают доказательством авторства. Резюме может быть создано давно и отправлено сегодня; сканер может установить неверный часовой пояс. Ищите явные противоречия и сочетайте их с другими фактами.
Ошибки запуска и способы устранения
Python не найден
Сообщение оболочки о неизвестной команде означает, что интерпретатор не доступен по указанному имени. Проверьте установку, PATH и команду python3. В виртуальной среде активируйте её перед запуском. Если установлено несколько Python, зависимости pyzipper и simplejson должны находиться именно в том окружении, которое выполняет скрипт.
Не найден pdfid.py
Проверьте текущий каталог и полный путь. В Windows обратите внимание на скрытое расширение: файл мог сохраниться как pdfid.py.txt. В системах с чувствительным регистром PDFid.py и pdfid.py — разные имена. Не запускайте случайную одноимённую копию из каталога загрузок, если рабочая процедура использует проверенный файл.
Не найден исследуемый PDF
Кавычки нужны для пробелов. Относительный путь вычисляется от текущего каталога оболочки, а не от каталога скрипта. Выведите текущую папку и существование файла. В пакетном режиме сообщение о несуществующих путях не должно игнорироваться: сравните список входов с числом отчётов.
Имя с квадратными скобками не обрабатывается
Квадратные скобки воспринимаются как шаблон. Используйте --literalfilenames и передайте точное имя в кавычках. Если нужно одновременно обработать буквальные имена и маску, выполните отдельные команды либо сформируйте список @file и убедитесь, как режим применяется к его строкам.
Not a PDF document
Сначала проверьте, что выбран правильный файл и что он не является HTML-страницей ошибки, ZIP или изображением. Затем найдите сигнатуру %PDF в первых 1024 байтах. Для повреждённого фрагмента используйте --force, но маркируйте результат как эвристический. Если файл зашифрован внешним контейнером, сначала распакуйте контейнер в изоляции.
Ошибка открытия ZIP
Проверьте целостность архива, наличие первого элемента, пароль infected и метод шифрования. Установите pyzipper для AES. Если пароль другой, встроенный путь его не запрашивает; извлеките файл доверенным архиватором в карантин или создайте копию ZIP с согласованным паролем. Не перебирайте пароль в рабочей среде без разрешения.
Ошибка плагина
Включите -v, проверьте путь, расширение, совместимость Python и наличие зависимостей. Убедитесь, что плагин вызывает регистрацию класса и предоставляет ожидаемые методы. Перед повторным массовым запуском проверьте один файл. Ошибка одного плагина не должна оставлять впечатление, что весь каталог обработан успешно.
Пустой результат с wildcard
Проверьте маску и кавычки. В некоторых оболочках шаблон раскрывается до передачи программе, в других его обрабатывает PDFid. Предупреждение о wildcard без совпадений означает, что список файлов пуст. Не принимайте отсутствие отчётов за отсутствие подозрительных PDF.
Нет прав на журнал или disarmed-файл
Выберите каталог, где разрешена запись, и не запускайте анализ с избыточными привилегиями только ради обхода запрета. Проверьте, не открыт ли журнал другой программой и не существует ли каталог с именем ожидаемого файла. Для read-only доказательств создайте рабочую копию и явно направьте выход в отдельную папку.
Слишком большой журнал
Уточните маску, разделите каталог на партии и используйте -n, select или минимальный score. Сохраняйте полный исходный отчёт хотя бы для выбранных образцов, иначе скрытые нулевые поля и правила фильтрации затруднят аудит. Не удаляйте старый журнал до проверки числа обработанных файлов.
Ограничения метода, которые нельзя игнорировать
PDFid не является грамматическим валидатором PDF. Он не строит таблицу объектов, не разрешает ссылки, не определяет активную ревизию и не декодирует все фильтры. Счётчик сообщает о найденной последовательности, но не о месте, назначении и достижимости. Это фундаментальная причина ложных положительных и ложно отрицательных результатов.
Содержимое Object Streams остаётся слепой зоной базового прохода. Шифрование ограничивает видимость. Вредоносные данные могут находиться в изображениях, шрифтах, мультимедийных потоках и вложениях без стандартного имени. Эксплуатация ошибки просмотрщика может не требовать JavaScript. Поэтому нулевые риск-индикаторы не являются сертификатом безопасности.
Программа не проверяет репутацию адресов, хешей и подписей, не эмулирует JavaScript, не запускает песочницу и не анализирует сетевое поведение. Эти функции выполняют другие уровни процесса. Сильная сторона PDFid — быстрый, прозрачный и воспроизводимый набор структурных признаков, который легко читать и автоматизировать.
Режим disarm меняет лишь ограниченный список имён и не выполняет санацию. Плагинная оценка отражает формулу плагина, а не универсальную вероятность вредоносности. Энтропия не различает сжатие и шифрование. Даты редактируемы. Любой из этих показателей приобретает смысл только в сочетании с разбором объектов и контекстом получения.
Сравнение PDFid с аналогами
Инструменты статического анализа PDF различаются глубиной. PDFid оптимален для быстрого счёта индикаторов; аналоги чаще строят структуру, декодируют потоки или предлагают интерактивную навигацию. Выбор зависит от того, нужно ли отсортировать тысячу файлов за короткое время или объяснить содержимое одного подозрительного объекта.
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| PDFid | Быстрого триажа по структуре, активным именам, обфускации, EOF и энтропии | Не раскрывает содержимое Object Streams и не связывает токены с объектами |
| pdf-parser.py | Поиска конкретных объектов, декодирования потоков и проверки ссылок после сигнала PDFid | Требует ручной интерпретации подробного вывода и знания структуры PDF |
| peepdf | Интерактивного исследования объектов, ревизий, JavaScript, потоков и зашифрованных файлов | Нужно освоить собственные консольные команды и режимы анализа |
| Origami | Скриптового разбора, изменения и генерации PDF с доступом к объектной модели | Требует Ruby и заметно сложнее для короткой первичной проверки |
| PDF Stream Dumper | Визуального анализа потоков, JavaScript и низкоуровневых объектов в графической среде | Ориентирован на Windows и требует осторожности со специализированными функциями |
| qpdf | Проверки целостности структуры, расшифровки, преобразования и получения JSON-представления | Не оценивает вредоносность и не даёт готового списка риск-индикаторов |
Для входящего потока документов разумно начинать с PDFid, затем передавать отмеченные файлы pdf-parser.py или peepdf. Origami выбирают, когда анализ нужно встроить в Ruby-сценарий или требуется управляемая модификация объектной модели. PDF Stream Dumper удобен специалисту, которому нужна визуальная навигация в Windows. qpdf полезен как независимая проверка структуры и подготовка расшифрованной или нормализованной копии, но не заменяет триаж угроз.
Контрольный чек-лист интерпретации отчёта
- Сверьте имя, размер и SHA-256 входного файла с зафиксированным образцом; не полагайтесь только на расширение.
- Проверьте PDF Header и объясните отсутствие заголовка до использования --force.
- Сопоставьте obj/endobj и stream/endstream, но не трактуйте расхождение без объектного разбора.
- Учтите xref streams и инкрементальные обновления при нулевых xref/trailer или нескольких startxref.
- Оцените /Page относительно заявленного типа документа, размера и количества объектов.
- Для /JS и /JavaScript найдите объект, декодируйте содержимое и установите событие запуска.
- Для /OpenAction и /AA проследите ссылки от каталога, страницы, аннотации или поля формы.
- Для /EmbeddedFile извлеките поток в карантин и определите формат по сигнатуре.
- При /ObjStm распакуйте объектные потоки до вывода об отсутствии активных имён.
- При /Encrypt повторите анализ доступной расшифрованной копии и опишите ограничения видимости.
- Проверьте числа в скобках: обфусцированные имена повышают приоритет ручного разбора.
- Запустите -e и объясните EOF, данные после конца, даты и энтропию в контексте типа файла.
- Подтвердите короткие токены вроде /JS парсером, чтобы исключить совпадение внутри изображения.
- Сохраните команду, конфигурацию, плагины, журнал и хеши всех производных файлов.
- Сформулируйте следующий шаг: разрешение, углублённый статический анализ, изоляция или динамическое исследование.
Чек-лист специально отделяет наблюдение от вывода. /JS = 1 — наблюдение; скрипт расположен в активном OpenAction и декодирует вложенный поток — подтверждённая структура; файл вредоносный — вывод, для которого нужны содержание, намерение или поведение. Такое разделение делает отчёт проверяемым другим специалистом.
Точечные ответы о результатах PDFid
Нулевые /JS и /JavaScript означают, что файл безопасен?
Нет. Скрипт может быть скрыт в Object Stream, зашифрован или представлен иначе; атака может использовать вложение, ссылку, изображение, шрифт либо уязвимость декодера без JavaScript. Нулевые значения лишь уменьшают один класс наблюдаемых сигналов.
Один /OpenAction всегда опасен?
Нет. Действие открытия может задавать страницу или масштаб. Нужно посмотреть тип действия и целевой объект. OpenAction вместе с JavaScript, Launch или обфускацией повышает приоритет, но окончательное решение принимают после разбора словаря.
Почему xref и trailer равны нулю в корректном файле?
Документ может использовать поток перекрёстных ссылок вместо классической таблицы и отдельного trailer. Подтвердите это структурным анализатором. Также токены могут быть скрыты или файл повреждён, поэтому ноль требует объяснения, а не автоматической тревоги.
Можно ли доверять числу страниц?
Используйте его как ориентир. PDFid считает имя /Page, а не рендерит дерево страниц. Старые ревизии, необычная структура и совпадения в данных могут менять число. Точное количество получают из активного дерева /Pages или безопасного рендеринга.
Высокая энтропия означает шифрование?
Не обязательно. Сжатые изображения, шрифты и контент тоже дают высокую энтропию. Сравните области внутри и вне потоков, фильтры и словарь /Encrypt. Энтропия показывает распределение байтов, а не алгоритм и не намерение.
Файл .disarmed.pdf можно открыть без риска?
Нет. Изменяются только определённые имена. Вложения, изображения, шрифты, неизвестные действия и уязвимые потоки остаются. Используйте такую копию лишь в контролируемом исследовании и не подменяйте ею процедуру санации.
PDFid извлекает ссылки и вложения?
Он считает соответствующие имена, если они включены в набор индикаторов, но не извлекает содержимое. Для адреса нужен объектный парсер, для вложения — извлечение потока и проверка сигнатуры. Счётчик отвечает сколько найдено, а не что именно внутри.
Можно ли проверить не-PDF файл?
Force выполнит байтовый поиск, но результат не станет анализом другого формата. Для DOCX, исполняемого файла, изображения или архива нужны профильные инструменты. ZIP поддерживается только как контейнер, из которого читается первый элемент для проверки как PDF.
Почему один и тот же файл даёт разные отчёты на двух компьютерах?
Сравните хеш файла, pdfid.ini, параметры -a/-e/-n/-f, плагины, списки @file и фактически запущенную копию скрипта. Конфигурация дополнительных ключей и формат вывода меняют видимые строки. Для воспроизводимости храните эти компоненты вместе с журналом.
Как встроить PDFid в безопасный процесс
В почтовом шлюзе или SOC PDFid удобно использовать после вычисления хеша и определения типа файла, но до открытия человеком. Результат преобразуют в структурированные поля: число JavaScript, автодействий, вложений, Object Streams, обфусцированных имён, EOF и байтов после конца. Правило маршрутизации отправляет сильные сочетания на детальный разбор.
Не запускайте внешние плагины от привилегированной учётной записи и не позволяйте пользователю передавать произвольное select-выражение. Аргументы нужно экранировать, пути ограничивать рабочим каталогом, журналы защищать от подмены. Сам PDF анализируется как данные, но оболочка и Python-код плагина остаются поверхностью автоматизации.
Для конфиденциальных документов достоинство такого процесса в том, что содержимое не требуется отправлять стороннему сервису. Однако сетевой режим программы изменяет эту модель: запрос уходит к указанному серверу. В регулируемой среде отключите использование сетевых адресов политикой сценария и принимайте только заранее сохранённые файлы.
Порог решения проверяют на собственном наборе обычных и вредоносных документов. Универсальный score без калибровки создаёт либо шум, либо пропуски. Отдельно оценивают формы, сканы, электронные счета с вложенным XML, презентации и фишинговые одностраничники, потому что их нормальные профили различаются.
Регулярно пересматривайте пользовательские индикаторы и правила эскалации. Новые способы упаковки могут не затрагивать стандартные имена, а бизнес-процесс может законно начать использовать XFA или вложения. Изменение правила документируют и тестируют на сохранённой контрольной выборке, чтобы видеть влияние на ложные тревоги и пропуски.
Итоговый порядок действий
Начните с базового отчёта и не пытайтесь сразу интерпретировать каждое число как вердикт. Подтвердите формат, оцените структурные пары, отметьте активные имена и обфускацию. Затем добавьте -e, чтобы увидеть EOF, хвост, даты и энтропию. Для большой партии используйте -n, журнал, @file, рекурсию, select или проверенный плагин.
Любой /ObjStm переводит нулевые риск-строки в категорию нужно раскрыть потоки. Любой /EmbeddedFile требует идентификации вложения. JavaScript и действия требуют прослеживания ссылок. /URI требует извлечения адреса и проверки контекста. Структурное расхождение требует валидатора и парсера, а не догадки по одному числу.
Сохраняйте исходник неизменным. Производные копии, включая disarmed, расшифрованные и извлечённые объекты, получают собственные хеши и понятные имена. Журнал должен позволять другому специалисту повторить команду и получить тот же результат при той же конфигурации.
PDFid приносит максимальную пользу в роли первого фильтра: он быстро превращает непрозрачный PDF в компактный набор наблюдаемых признаков и показывает, где требуется более глубокий инструмент. Безопасное решение возникает не из одного счётчика, а из последовательности подтверждений — от байтового индикатора к объекту, от объекта к действию и от действия к фактическому риску.