-
Kindle 앱의 심각한 불안정성 문제로 인해, Amazon에서 구매한 전자책을 다른 리더로 읽으려고 시도함
-
Amazon 웹 DRM 시스템은 여러 보안 레이어와 난수화된 문자 매핑으로 이중 삼중 보호를 적용함
- 각 요청마다 완전히 다른 폰트 매핑을 사용하는 방식으로 일반적인 해킹 시도를 어렵게 만듦
- 이미지 기반 해싱과 SSIM 기반 폰트 매칭을 통해 모든 난수화된 글리프를 실제 문자로 복원하는 방법을 개발함
- 결과적으로, 구매한 책 전체를 Calibre 등 개인 전자책 라이브러리에서 완전하게 복원 및 보관하는 솔루션을 완성함
TL;DR
- 첫 Amazon 전자책을 구매 후 Kindle Android 앱의 불안정으로 큰 불만을 가짐
- 단순히 오프라인에서 자유롭게 읽고 싶었으나, 다운로드 및 백업 불가로 인해 불편함을 느낌
-
아마존 웹 리더의 DRM 시스템을 분석하며, 복잡한 난수화 및 알파벳 매핑 구조에 직면함
-
이미지 해시와 폰트 매칭 방식으로 난수를 해결하고, 소유 책을 성공적으로 복원함
- 본 코드 사용 시, 구매한 책의 소유권과 백업 권리의 중요성에 주목하는 입장임
Amazon Made This Personal
The One Time I Tried To Do Things The Right Way
- 기존에는 주로 비공식 경로로 전자책을 얻었으나, 저자를 지원하기 위해 Amazon에서 처음으로 정식 구매를 시도함
- Kindle 앱에서 책을 열자 계속 크래시 발생으로 제대로 읽기 불가능함
I Just Wanted To Read My Book
- 앱의 불안정함 때문에 웹 리더를 사용하려 했으나, 오프라인 다운로드, 외부 리더 연동이 모두 막혀있음을 발견함
- Calibre 등 개인 라이브러리와 연동 불가, 백업 불가, 심지어 아마존이 언제든 삭제 가능한 형태임
- 실질적으로 소유권이 없는 임대 형태임을 깨달음
It Becomes Personal
-
환불이나 비공식 경로를 선택할 수도 있었지만, 구매한 책의 소유권을 증명하기 위해 직접 웹 클라이언트 리버스 엔지니어링 결심
-
Kindle Cloud Reader(웹)에서 네트워크 요청 분석 중 /renderer/render 엔드포인트를 확인함
-
다운로드에 필요한 조건
-
세션 쿠키 (Amazon 로그인)
-
렌더링 토큰 (startReading API로 획득)
-
ADP 세션 토큰 (추가 인증)
-
브라우저와 동일한 헤더 및 쿠키로 요청 시 TAR 파일 제공
-
TAR 파일 내 구성 예시
-
page_data_0_4.json : (텍스트 정보, 실제로는 텍스트가 아님)
-
glyphs.json : 모든 글자의 SVG 정의
-
toc.json : 목차 데이터
-
metadata.json : 메타정보
-
location_map.json : 위치 매핑 정보
Amazon's Obfuscation Layers of Ebook Hell
-
텍스트 추출 시 나오는 데이터 예시
{
"type": "TextRun",
"glyphs": [24, 25, 74, 123, 91, 18, 19, 30, 4, ...],
"style": "paragraph"
}
- 실제 글자가 아닌 글리프 ID 배열로 구성됨
- 각 glyph ID는 특정 문자에 고정되지 않고 SVG 기반의 이미지로 제공됨
-
치환 암호(서브스티튜션 사이퍼) 구조로, 문자와 글리프 ID 매핑이 일정하지 않음
The Alphabet Changes Every. Five. Pages.
-
5페이지마다 글리프 매핑이 무작위로 재설정됨
- 각 요청마다 다른 mapping table이 적용되어, 전자책 전체를 한 번에 매핑하거나 해석 불가함
- 예시로, 920페이지 책의 경우
- 184개의 요청 필요
- 184번의 랜덤 알파벳 해독 필요
- 총 361개의 유니크 글리프 파악
- 1,051,745개의 글리프 해독 필요
Fake Font Hints (They're Getting Sneaky)
- SVG path 내에 의미 없는 미세한 무브 명령(m3,1 m1,6 m-4,-7)이 포함됨
- 브라우저에서 정상 렌더링되지만, Python 등 SVG 라이브러리에서는 잘못된 연결선이 생김
- 단순 path 단위 분석을 통한 파싱 실패 유도
- 실제 파싱 시 전체 path를 한 번에 충실하게 렌더링해야 문제 해결 가능
Multiple Font Variants
- 폰트도 하나가 아닌 4종류(bookerly_normal, italic, bold, bolditalic) 가 별도 사용됨
-
합자, 특수 문자(ffi, fl 등) 역시 별도의 글리프로 존재하여 매핑 난이도 상승
OCR Is Mid (My Failed Attempt)
- 렌더된 글리프 이미지를 기존 광학 문자 인식(OCR) 툴로 해석 시도
- 약 51%의 인식률, 나머지는 실패
- 맥락 없는 단일 글자는 OCR에서 구분 불가(1, l, I, 구두점 등 혼동)
The Solution That Actually Worked
- 각 요청마다 glyphs.json에 SVG path 정보가 포함됨
- 글리프 ID는 매번 달라져도, 동일 문자라면 SVG shape(이미지)는 동일함
Why Direct SVG Comparison Failed
- SVG path 좌표 직접 비교 시, 미세한 차이와 path 명령의 다름으로 실패함
Pixel-Perfect Matching
-
SVG를 이미지로 렌더링한 후, 픽셀 단위 비교로 동일 글리프 식별
- 모든 SVG를 512x512 해상도의 이미지로 변환(cairosvg 사용)
- 각각의 이미지를 지각 해시(Perceptual hash) 로 처리 — 동일 형태라면 해시값 일치
- 184개의 랜덤 알파벳 매핑을 해시값 기반으로 통합
- 실제 문자와 매칭 위해 Bookerly TTF 폰트의 각 문자를 SSIM(Structural Similarity Index) 방식으로 비교
Why SSIM Is Perfect For This
- SSIM은 이미지 구조를 비교하여 렌더링의 미세한 차이, 안티앨리어싱, 크기 불일치를 허용함
- 미지의 글리프마다 최고 SSIM 점수의 TTF 문자를 매칭함
Handling The Edge Cases
-
합자(ff, fi, fl, ffi, ffl)는 여러 문자를 한 글리프로 처리하므로, TTF에 직접 추가 및 해시 처리
-
특수문자, 다양한 폰트 스타일(굵게, 이탤릭, 굵은 이탤릭) 모두 별도 라이브러리로 관리
The Moment It All Worked
Final Statistics
=== 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 Reconstruction With Perfect Formatting
- JSON 데이터 내 각 텍스트 블록의 좌표, 폰트 스타일, 내부 링크 정보를 활용하여
-
문단 구분, 정렬, 볼드·이탤릭, 폰트 크기, 내부 링크까지 원본에 가깝게 유지하는 EPUB 생성
The Real Conclusion
- Amazon은 웹 DRM에 상당한 노력을 들였음을 확인
- 실사용을 위해선 과한 노력이나, 지식 습득 및 기술 데모로서는 가치가 있음
- 이 내용은 구매자 본인의 책 백업 및 소유권 보호 목적임을 강조함
참고 자료 (오픈소스 코드)