TL;DR
-
- Я купил свою первую электронную книгу на Amazon
- Приложение Amazon Kindle для Android было очень глючным и постоянно вылетало
- Пытался скачать книгу, чтобы использовать её в работающей читалке
- Понял, что Amazon больше не позволяет этого делать
- Решил назло провести реверс-инжиниринг их системы обфускации
- Обнаружил несколько уровней защиты, включая случайный алфавит
- Обошел их все с помощью волшебства сопоставления шрифтов
Часть 1: Amazon сделал это личным делом
Единственный раз, когда я попытался сделать всё правильно
Я годами «получал» электронные книги. Но в этот раз я подумал: «Давайте поддержим автора».
Скачал приложение Kindle на Android. Открыл книгу.
Сбой.
Я просто хотел прочитать книгу
Приложение вылетает. Ладно, воспользуюсь веб-читалкой. Ой, погодите, не могу скачать для чтения офлайн. А если я в самолёте? Погоди, я даже не могу экспортировать её в Calibre? Где я храню ВСЕ остальные книги?
Итак, давайте разберёмся:
- Я заплатил за эту книгу
- Я могу читать её только в сломанном приложении Amazon
- Я не могу её скачать
- Я не могу создать резервную копию
- Она мне не принадлежит
- Amazon может удалить её, когда захочет.
Это аренда, а не покупка.
Здесь не написано «Аренда»
Это становится личным
Я мог бы вернуть деньги и «получить» её за 30 секунд. Было бы проще.
Но дело не в этом.
Дело в том, что Я ЗАПЛАТИЛ ЗА ЭТУ КНИГУ. Она моя. И я буду читать её в Calibre вместе с остальными книгами из моей библиотеки, даже если для этого мне придётся взломать их веб-клиент.
Часть 2: Время реверс-инжиниринга
Kindle Cloud Reader (веб-версия) действительно работает. Просматривая сетевые запросы, я заметил следующее:
https://read.amazon.com/renderer/render
Для загрузки вам понадобятся:
- Сеансовые cookie-файлы — стандартный вход в Amazon
- Токен рендеринга — из вызова startReading API
- Токен сеанса ADP — дополнительный уровень аутентификации
Отправляю те же заголовки и cookie-файлы, которые браузер отправляет, и получаю обратно TAR-файл.
Что внутри TAR?
page_data_0_4.json # The "text" (spoiler: it's not text) glyphs.json # SVG definitions for every character toc.json # Table of contents metadata.json # Book info location_map.json # Position mappings
Часть 3: Слои обфускации Amazon превращают электронные книги в ад
Скачал первые несколько страниц, ожидал увидеть текст. Вместо этого получил это:
{
"type": "TextRun",
"glyphs": [24, 25, 74, 123, 91, 18, 19, 30, 4, ...],
"style": "paragraph"
}
Это не буквы. Это идентификаторы глифов. Символ «T» — это не Unicode 84, а глиф 24.
А глиф 24 — это просто последовательность цифр, определяющих траекторию кисти, это просто изображение буквы.
Это подстановочный шифр! Каждый символ соответствует непоследовательному идентификатору глифа.
Алфавит меняется каждые. Пять. Страниц.
Загрузил следующую партию страниц. Та же буква «T» теперь имеет номер 87.
Следующая партия? Глиф 142.
Они рандомизируют весь алфавит при КАЖДОМ запросе.
Это означает:
- Вы можете получить только 5 страниц одновременно (жёсткий лимит API)
- Каждый запрос получает совершенно новые соответствия глифов
- Идентификаторы глифов не переходят из запроса в запрос
- Невозможно создать единую таблицу сопоставлений для всей книги
Позвольте мне показать вам, насколько это плохо
Для моей 920-страничной книги:
- Требуется 184 отдельных запроса к API
- 184 различных случайных алфавитов для взлома
- Обнаружено 361 уникальный глиф (a-z, A-Z, знаки препинания, лигатуры)
- Всего 1 051 745 глифов для декодирования
Поддельные подсказки шрифтов (хитрость)
Некоторые пути SVG содержали такой мусор:
M695.068,0 L697.51,-27.954 m3,1 m1,6 m-4,-7 L699.951,-55.908 ...
Глядя на него, мы видим эти крошечные команды m3,1 m1,6 m-4,-7, это микрооперации MoveTo.
Почему это зло:
- Браузеры отлично с ними справляются (встроенный Path2D)
- Библиотеки Python SVG создают ложные соединительные линии
- Глифы выглядят искажёнными при прямой отрисовке
- Ломается path-sampling подходы
Это намеренная защита от парсинга. Глифы отлично отображаются в браузере, но из-за этого мы не можем просто сравнивать пути в нашем парсере.
Взгляните:
Веселье!
В конце концов я понял, что закрашивание всего пути смягчает эту проблему.
Несколько вариантов шрифта
Не один шрифт. ЧЕТЫРЕ варианта:
- bookerly_normal (99% глифов)
- bookerly_italic (выделение)
- bookerly_bold (заголовки)
- bookerly_bolditalic (выделение заголовков)
Плюс специальные лигатуры: ff, fi, fl, ffi, ffl.
Больше вариаций = больше уникальных глифов для взлома = больше проблем.
OCR (моя неудачная попытка)
Пытался запустить OCR на отрисованных глифах. Результаты:
- Распознано 178 из 348 глифов (51%)
- 170 глифов полностью провалились
OCR просто ужасно распознаёт отдельные символы без контекста. Перепутал «l» с «I» с «1». Не смог разобраться со знаками препинания. Полностью отказался от лигатур.
Для хорошей работы OCR, вероятно, нужны слова и предложения.
Часть 4: Решение, которое действительно сработало
Каждый запрос включает файл glyphs.json с определениями путей SVG:
{
"24": {
"path": "M 450 1480 L 820 1480 L 820 0 L 1050 0 L 1050 1480 ...",
"fontFamily": "bookerly_normal"
},
"87": {
"path": "M 450 1480 L 820 1480 L 820 0 L 1050 0 L 1050 1480 ...",
"fontFamily": "bookerly_normal"
}
}
Идентификаторы глифов меняются, а фигуры SVG — нет.
Почему прямое сравнение SVG не удалось
Первая попытка: нормализовать и сравнить координаты путей в SVG.
Неудача, потому что:
- Координаты немного различаются
- Команды путей представлены по-разному
Попиксельное совпадение
К черту сравнение координат. Давайте просто отрисуем всё и сравним пиксели.
1. Отрисовываю каждый SVG-файл как изображение
- Использую cairosvg (позволяет корректно обрабатывать эти фальшивые линии в шрифтах)
- Отрисовываю с разрешением 512 x 512 пикселей для точности
2. Генерирую перцептивные хеш-коды
- Хеширую каждое отрисованное изображение
- Хеш-код становится уникальным идентификатором
- Одинаковая форма = одинаковый хеш-код, независимо от идентификатора глифа
3. Создаю нормализованное пространство глифов
- Сопоставляю все 184 случайных алфавита с идентификаторами на основе хешей
- Теперь глиф «a1b2c3d4…» всегда означает букву «T»
4. Сопоставляю с реальными символами
- Скачал шрифты Bookerly TTF
- Отрисовываю каждый символ (A-Z, a-z, 0-9, знаки препинания)
- Использую SSIM (индекс структурного сходства) для сопоставления
Почему SSIM идеально подходит для этого
SSIM сравнивает структуру изображения, а не пиксели напрямую. Он обрабатывает:
- Небольшие различия в рендеринге
- Вариации сглаживания
- Небольшие проблемы масштабирования
Для каждого неизвестного глифа нахожу TTF символ с наивысшим рейтингом SSIM. Это и есть наша буква.
Обработка пограничных случаев
Лигатуры: ff, fi, fl, ffi, ffl
- Это отдельные глифы для нескольких символов
- Пришлось добавить их в библиотеку TTF вручную
Специальные символы: длинное тире, кавычки, маркеры
- Расширенный набор символов, выходящий за рамки базового ASCII
- Сопоставление со всем диапазоном Unicode в Bookerly
Варианты шрифтов: жирный, курсив, жирный-курсив
- Созданы отдельные библиотеки для каждого варианта
- Сопоставление со всеми библиотеками, выбор лучшего рейтинга
Часть 5: Когда всё заработало
Итоговая статистика:
=== NORMALIZATION PHASE === Total batches processed: 184 Unique glyphs found: 361 Total glyphs in book: 1,051,745 === MATCHING PHASE === Successfully matched 361/361 unique glyphs (100.00%) Failed to match: 0 glyphs Average SSIM score: 0.9527 === DECODED OUTPUT === Total characters: 5,623,847 Pages: 920
Идеально. Каждый символ декодирован правильно.
Реконструкция EPUB с идеальным форматированием
JSON включает позиционирование для каждого фрагмента текста:
{
"glyphs": [24, 25, 74],
"rect": {"left": 100, "top": 200, "right": 850, "bottom": 220},
"fontStyle": "italic",
"fontWeight": 700,
"fontSize": 12.5,
"link": {"positionId": 7539}
}
Я использовал это, чтобы сохранить:
- Разрывы абзацев (изменение координаты Y)
- Выравнивание текста (шаблоны координаты X)
- Выделение жирным/курсивным шрифтом
- Размеры шрифтов
- Внутренние ссылки
Финальный EPUB практически неотличим от оригинала!
Реальный вывод
Amazon приложил немало усилий для обфускации своего веб-читалки.
Стоило ли это того? Прочитать одну книгу? Нет.
Доказать свою точку зрения? Безусловно.
Узнать о рендеринге SVG, перцепционном хешировании и метриках шрифтов? Вероятно, да.
Используйте эти знания ответственно.
Это для резервного копирования книг, которые ВЫ КУПИЛИ.
Не подавай на меня в суд, спасибо.

