- PVSM.RU - https://www.pvsm.ru -

Подробнее об этом хаке и особенностях его работы можно узнать из доклада на !!con 2020 «Playing Breakout… inside a PDF!!» [1]
Если вы его не смотрели, то попробуйте открыть файл breakout.pdf [2] в Chrome.
Как и многие из вас, я всегда считал PDF довольно безопасным форматом: автор создаёт текст и графику, после чего он открывается в программе просмотра PDF, больше ничего не делая. Несколько лет назад я мимоходом слышал об уязвимостях Adobe Reader, но особо не задумывался о том, как они могут возникать.
Изначально Adobe сделала PDF именно для этого, но мы уже выяснили, что сегодня это совсем не так. В 1310-страничной спецификации PDF [3] (на самом деле довольно понятном и интересном чтиве) описывается безумное количество возможностей, в том числе:
но самое интересное для нас…
Разумеется, большинство программ для чтения PDF (кроме Adobe Reader) не реализует основную часть этих возможностей. Однако Chrome реализует JavaScript! Если вы откроете подобный файл PDF в Chrome, то он запустит скрипт. Я выяснил, повторив действия из этого поста о создании PDF с JS [16].
Однако здесь есть хитрость. Chrome реализует только крошечное подмножество огромной поверхности Acrobat JavaScript API. Реализация API в PDFium браузера Chrome в основном состоит из подобных заглушек [17]:
FX_BOOL Document::addAnnot(IJS_Context* cc,
const CJS_Parameters& params,
CJS_Value& vRet,
CFX_WideString& sError) {
// Not supported.
return TRUE;
}
FX_BOOL Document::addField(IJS_Context* cc,
const CJS_Parameters& params,
CJS_Value& vRet,
CFX_WideString& sError) {
// Not supported.
return TRUE;
}
FX_BOOL Document::exportAsText(IJS_Context* cc,
const CJS_Parameters& params,
CJS_Value& vRet,
CFX_WideString& sError) {
// Unsafe, not supported.
return TRUE;
}
И я понимаю опасения разработчиков — этот Adobe JavaScript API имеет совершенно огромную площадь поверхности [18]. Предположительно, скрипты могут выполнять такие действия, как соединение с произвольными базами данных [19], распознавание подключенных мониторов [20], импорт внешних ресурсов [21] и манипулирование 3D-объектами [22].
Поэтому в Chrome получилась такая странная ситуация: мы можем выполнять произвольные вычисления, однако имеем эту странную, ограниченную поверхность API, при которой ввод-вывод и передачу данных между программой и пользователем реализовывать очень неудобно.
Вероятно, можно встроить в PDF компилятор C, скомпилировав его в JS, например, с помощью Emscripten [23], но тогда компилятор C должен будет получать ввод из формы простого текста (plain text), а вывод выполнять снова в поле формы.
На самом деле, я заинтересовался PDF пару недель назад из-за PostScript [24]; я читал посты Дона Хопкинса о NeWS [25] — напоминающей AJAX системе, но реализованной в 80-х на PostScript.
Забавно, что PDF стал реакцией на PostScript, который был слишком выразительным (являясь полнофункциональным языком программирования), слишком сложным в анализе и восприятии. Наверно, PDF по-прежнему остаётся в этом плане шагом вперёд, но всё равно смешно, что он разросся всеми этими возможностями.
Ещё один любопытный момент: как любой долгоживущий цифровой формат (лично я испытываю нежные чувства к файловой системе FAT), PDF сам по себе является своего рода историческим документом. Мы можем отследить, как поколения инженеров добавляли нужные им в своё время функции, пытаясь при этом не поломать уже существующие.
Я не совсем понимаю, зачем разработчики Chrome вообще заморачивались поддержкой JS. Они взяли код программы чтения PDF из Foxit [26]; возможно, у Foxit был какой-то клиент, использовавший валидацию форм через JavaScript?
Кроме того, Chrome использует тот же рантайм, что и в браузере, хоть и не раскрывает браузерных API. Насколько я понимаю, это значит, что можно использовать такие возможности ES6, как стрелочные функции и прокси.
Так что же мы можем сделать с предоставляемой нам Chrome поверхностью API?
Кстати, должен извиниться за неидеальное распознавание коллизий и непостоянную скорость игры. БОльшую часть игры я содрал с туториала [27].
Первые доступные пользователю точки ввода-вывода, которые я смог найти в реализации PDF API браузера Chrome, находились в Field.cpp [28].
Мы не можем менять цвет заливки [29] текстового поля во время выполнения, зато можем менять прямоугольник его границ [30] и задавать стиль границ [31]. Мы не можем считывать точное положение мыши [32], однако можем при создании PDF привязать к полям скрипты mouse-enter и mouse-leave. Также во время выполнения нельзя добавлять поля: придётся ограничиться тем, что мы поместили в PDF в момент создания. Любопытно, почему разработчики выбрали именно эти методы? Это похоже на какой-то стереотип о программировании на олдскульном FORTRAN: необходимо объявлять все переменные заранее, чтобы компилятор мог статически выделить под них память.
Итак, файл PDF генерируется скриптом [33], заранее создающим набор текстовых полей, в том числе и игровых элементов:
Но для правильной работы игры мы также внесём некоторые хаки.
Во-первых, мы создаём тонкую длинную «полосу» текстового поля для каждого столбца нижней половины экрана. Полосы получают событие mouse-enter, когда игрок перемещает мышь по оси X, поэтому ракетка может перемещаться при движении мыши.
Во-вторых, мы создаём поле под названием «whole», закрывающее всю верхнюю половину экрана. Chrome не ожидает, что отображение PDF будет меняться, поэтому если перемещать поля в JS, то получатся довольно сильные артефакты. Это поле «whole» решает данную проблему — мы включаем/отключаем его во время рендеринга кадра. Этот трюк заставляет Chrome подчищать артефакты.
Кроме того, при перемещении поля, похоже, сбрасывается его поток внешнего отображения [34]. Выбранный вами красивый внешний вид из произвольной PDF-графики «слетает» и заменяется простым залитым прямоугольником с границей. Поэтому в моей игре используется упрощённый словарь характеристик внешнего вида [35]. В самом крайнем случае указанный там цвет заливки при перемещении виджета остаётся неизменным.
Закажите сервер и сразу начинайте работать! Создание VDS любой конфигурации [40] в течение минуты, в том числе серверов для хранения большого объёма данных до 4000 ГБ. Эпичненько :)
Автор: Mikhail
Источник [41]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/nenormal-noe-programmirovanie/360412
Ссылки в тексте:
[1] «Playing Breakout… inside a PDF!!»: https://www.youtube.com/watch?v=6rbJu10Telc
[2] файл breakout.pdf: https://cdn.jsdelivr.net/gh/osnr/horrifying-pdf-experiments@master/breakout.pdf
[3] 1310-страничной спецификации PDF: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf
[4] Встроенный Flash: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=1123
[5] звука: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=783
[6] видео: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=784
[7] 3D-объектов: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=789
[8] Web capture: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=946
[9] Произвольные математические функции: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=166
[10] Тьюринг-неполное подмножество PostScript: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=176
[11] подмножество XHTML и CSS: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=680
[12] файлов: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=638
[13] коллекций файлов: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=588
[14] скрипты JavaScript: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=709
[15] стандартной библиотеки, совершенно отличающейся от библиотеки браузера: https://wwwimages2.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_api_reference.pdf
[16] этого поста о создании PDF с JS: https://mariomalwareanalysis.blogspot.com/2012/02/how-to-embed-javascript-into-pdf.html
[17] подобных заглушек: https://pdfium.googlesource.com/pdfium/+/chromium/2557/fpdfsdk/src/javascript/Document.cpp#258
[18] совершенно огромную площадь поверхности: https://wwwimages2.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=3
[19] соединение с произвольными базами данных: https://wwwimages2.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=36
[20] распознавание подключенных мониторов: https://wwwimages2.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=537
[21] импорт внешних ресурсов: https://wwwimages2.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_api_reference.pdf#page=317
[22] манипулирование 3D-объектами: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/js_3d_api_reference.pdf
[23] Emscripten: https://kripken.github.io/emscripten-site/
[24] PostScript: https://en.wikipedia.org/wiki/PostScript
[25] NeWS: https://en.wikipedia.org/wiki/NeWS
[26] код программы чтения PDF из Foxit: https://plus.google.com/+FrancoisBeaufort/posts/9wwSiWDDKKP
[27] туториала: https://developer.mozilla.org/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript
[28] Field.cpp: https://pdfium.googlesource.com/pdfium/+/chromium/2524/fpdfsdk/src/javascript/Field.cpp
[29] менять цвет заливки: https://pdfium.googlesource.com/pdfium/+/chromium/2524/fpdfsdk/src/javascript/Field.cpp#1631
[30] менять прямоугольник его границ: https://pdfium.googlesource.com/pdfium/+/chromium/2524/fpdfsdk/src/javascript/Field.cpp#2356
[31] задавать стиль границ: https://pdfium.googlesource.com/pdfium/+/chromium/2524/fpdfsdk/src/javascript/Field.cpp#479
[32] считывать точное положение мыши: https://pdfium.googlesource.com/pdfium/+/chromium/2524/fpdfsdk/src/javascript/Document.cpp#1107
[33] скриптом: https://github.com/osnr/horrifying-pdf-experiments/blob/master/generate_breakout.py
[34] поток внешнего отображения: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=612
[35] упрощённый словарь характеристик внешнего вида: https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=642
[36] Minimal PDF: https://brendanzagaeski.appspot.com/0004.html
[37] Hand-coded PDF: https://brendanzagaeski.appspot.com/0005.html
[38] PDF Inside and Out: https://blogs.adobe.com/pdfdevjunkie/files/pdfdevjunkie/PDF_Inside_and_Out.pdf
[39] pdfrw: https://github.com/pmaupin/pdfrw
[40] VDS любой конфигурации: https://vdsina.ru/cloud-servers?partner=habr223
[41] Источник: https://habr.com/ru/post/536200/?utm_source=habrahabr&utm_medium=rss&utm_campaign=536200
Нажмите здесь для печати.