Site icon AppTractor

Как я реверснул обфускацию Amazon Kindle Web, потому что их приложение было отстойным

TL;DR

Часть 1: Amazon сделал это личным делом

Единственный раз, когда я попытался сделать всё правильно

Я годами «получал» электронные книги. Но в этот раз я подумал: «Давайте поддержим автора».

Скачал приложение Kindle на Android. Открыл книгу.

Сбой.

Я просто хотел прочитать книгу

Приложение вылетает. Ладно, воспользуюсь веб-читалкой. Ой, погодите, не могу скачать для чтения офлайн. А если я в самолёте? Погоди, я даже не могу экспортировать её в Calibre? Где я храню ВСЕ остальные книги?

Итак, давайте разберёмся:

Это аренда, а не покупка.

Здесь не написано «Аренда»

Это становится личным

Я мог бы вернуть деньги и «получить» её за 30 секунд. Было бы проще.

Но дело не в этом.

Дело в том, что Я ЗАПЛАТИЛ ЗА ЭТУ КНИГУ. Она моя. И я буду читать её в Calibre вместе с остальными книгами из моей библиотеки, даже если для этого мне придётся взломать их веб-клиент.

Часть 2: Время реверс-инжиниринга

Kindle Cloud Reader (веб-версия) действительно работает. Просматривая сетевые запросы, я заметил следующее:

https://read.amazon.com/renderer/render

Для загрузки вам понадобятся:

  1. Сеансовые cookie-файлы — стандартный вход в Amazon
  2. Токен рендеринга — из вызова startReading API
  3.  Токен сеанса 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.

Они рандомизируют весь алфавит при КАЖДОМ запросе.

Это означает:

Позвольте мне показать вам, насколько это плохо

Для моей 920-страничной книги:

Поддельные подсказки шрифтов (хитрость)

Некоторые пути 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.

Почему это зло:

Это намеренная защита от парсинга. Глифы отлично отображаются в браузере, но из-за этого мы не можем просто сравнивать пути в нашем парсере.

Взгляните:

Веселье!

В конце концов я понял, что закрашивание всего пути смягчает эту проблему.

Несколько вариантов шрифта

Не один шрифт. ЧЕТЫРЕ варианта:

Плюс специальные лигатуры: ff, fi, fl, ffi, ffl.

Больше вариаций = больше уникальных глифов для взлома = больше проблем.

OCR (моя неудачная попытка)

Пытался запустить OCR на отрисованных глифах. Результаты:

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-файл как изображение

2. Генерирую перцептивные хеш-коды

3. Создаю нормализованное пространство глифов

4. Сопоставляю с реальными символами

Почему SSIM идеально подходит для этого

SSIM сравнивает структуру изображения, а не пиксели напрямую. Он обрабатывает:

Для каждого неизвестного глифа нахожу TTF символ с наивысшим рейтингом SSIM. Это и есть наша буква.

Обработка пограничных случаев

Лигатуры: ff, fi, fl, ffi, ffl

Специальные символы: длинное тире, кавычки, маркеры

Варианты шрифтов: жирный, курсив, жирный-курсив

Часть 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}
}

Я использовал это, чтобы сохранить:

Финальный EPUB практически неотличим от оригинала!

Реальный вывод

Amazon приложил немало усилий для обфускации своего веб-читалки.

Стоило ли это того? Прочитать одну книгу? Нет.

Доказать свою точку зрения? Безусловно.

Узнать о рендеринге SVG, перцепционном хешировании и метриках шрифтов? Вероятно, да.

Используйте эти знания ответственно.

Это для резервного копирования книг, которые ВЫ КУПИЛИ.

Не подавай на меня в суд, спасибо.

Источник

 

Exit mobile version