Geçen hafta Calibro'dan bir e-kitap satın aldım. Cynthia Stokes Brown'ın Büyük Tarih: Büyük Patlamadan Bugüne adlı kitabını. İndirdim, açmaya çalıştım — ve işte o zaman eğlence başladı.
Kitap açılmıyordu. Daha doğrusu, sadece Calibro'nun kendi uygulamasında açılıyordu. Telefona kurmayı denedim, güncel cihazlarda uygulama çalışmıyor diye hata verdi. Bilgisayarda okumayı denedim, olmadı. Satın aldığım bir şeyi okuyamıyordum.
Calibro'ya mail attım. Tweet attım. Yanıt vermediler.
O gece bir karar verdim: Bu kitabı okuyacaktım.
DRM Nedir?
DRM (Digital Rights Management), dijital içerikleri korumak için kullanılan şifreleme sistemidir. Teoride mantıklıdır: İçerik üreticilerinin haklarını korur. Pratikte ise satın aldığın bir şeyi kullanamamana neden olur.
Calibro, PocketBook'un PBDRM sistemini kullanıyor. Kitap .lndrm uzantılı bir dosya olarak geliyor. İçini hex editörle açınca şunu gördüm:
50 42 44 52 4D ... → "PBDRM" magic bytesAdobe ADEPT altyapısı üzerine kurulu, RSA + AES-128-CBC şifreleme kullanıyor. Yani:
Kitabın içeriği AES-128-CBC ile şifrelenmiş
AES anahtarı RSA ile şifrelenmiş
RSA şifrelemek için Calibro'nun sunucu tarafındaki public key kullanılmış
Çözmek için Calibro'nun elindeki private key gerekiyor
İlk Hamle: Adobe Digital Editions
İlk denediğim şey Adobe Digital Editions (ADE) ve DeDRM plugin'iydi. Bu ikili çoğu e-kitap DRM'ini kaldırabiliyor. Ama ADE denemeye başlamadan önce şöyle bir hata verdi:
E_ADEPT_NO_TOKENBu hata, ADE'nin Adobe'nin lisans sunucusundan (adeactivate.adobe.com) token alamaması anlamına geliyor. Çözüm denemeleri:
ADE'yi deauthorize edip yeniden authorize etmek
VPN'i kapatmak
Hosts dosyasında Adobe sunucularının bloklanıp bloklanmadığını kontrol etmek (
/etc/hostsveyaC:\Windows\System32\drivers\etc\hosts)ADE'yi güncellemek
Hiçbiri işe yaramadı. Üstelik DeDRM plugin, PBDRM formatını zaten tanımıyordu — bu Adobe'nin standart ADEPT formatı değil, PocketBook'un özel bir implementasyonu.
Memu Emülatörü
Calibro Reader yalnızca Android'de çalışıyor ve güncel telefonlarda çalışmıyor. Çözüm: Memu — Windows'ta Android uygulamaları çalıştıran bir emülatör.
Memu → Android x86_64 emülatör
Calibro Reader APK → ARM binary
Uygulama kuruldu, kitap açıldı, okuyabildim. Ama bu çözüm değildi — bağımlılık yaratıyor, başka cihazda çalışmıyor.
PBDRM Header Analizi
Kitap dosyasını Python ile incelemeye başladım:
data = open('kitap.lndrm', 'rb').read()
print(data[:32].hex())
# 5042 4452 4d00 0100 a896 3191 69d0 5969...
# P B D R MHeader yapısı:
| Offset | Uzunluk | İçerik |
|---|---|---|
| 0x00 | 5 byte | PBDRM magic |
| 0x05 | 2 byte | 0x0001 type |
| 0x07 | 2 byte | 0x00a8 = 168 (şifreli anahtar uzunluğu) |
| 0x09 | 168 byte | RSA ile şifreli AES anahtarı |
| 0x120 | 16 byte | IV (initialization vector) |
| 0x130 | ~ | AES ile şifreli içerik |
168 byte ilginçti — RSA-1024 için 128 byte olması gerekir. 40 byte fazladan ne? Bunun üzerinde saatlerce çalıştım: IV mi, salt mı, başka bir şey mi?
activation.xml
Uygulamanın data dizininde kritik dosyalar vardı:
adb shell "ls /data/data/com.calibro.reader/app_resources/"
# activation.xml
# device.xmlactivation.xml Adobe ADEPT formatındaydı. İçinde iki önemli şey:
privateLicenseKey: RSA-1024 private key (base64 encoded)
pkcs12: Şifreli PKCS12 sertifikası
import base64, xml.etree.ElementTree as ET
from Crypto.PublicKey import RSA
tree = ET.parse('activation.xml')
pk_b64 = tree.find('.//{http://ns.adobe.com/adept}privateLicenseKey').text
pk_der = base64.b64decode(pk_b64)
rsa_key = RSA.import_key(pk_der)
print(f"Key boyutu: {rsa_key.size_in_bytes()} bytes") # 128privateLicenseKey ile .lndrm dosyasındaki 168 byte'lık bloğu çözmeye çalıştım. 168 byte içinde 128 byte'lık RSA bloğunun hangi offset'te olduğunu bulmak için brute force:
from Crypto.Cipher import PKCS1_v1_5
full_block = data[0x09:0x09+168]
for offset in range(0, 41):
rsa_block = full_block[offset:offset+128]
cipher = PKCS1_v1_5.new(rsa_key)
result = cipher.decrypt(rsa_block, b'\xff' * 32)
if result != b'\xff' * 32:
print(f"[+] offset={offset}: {result.hex()}")Hiçbir offset'te başarılı decrypt elde edemedim.
Neden? Çünkü privateLicenseKey kullanıcı kimlik doğrulama anahtarıydı — kitap şifreleme anahtarı değildi. Gerçek şifre çözme anahtarı Calibro'nun sunucusundaydı.
PKCS12 sertifikasının şifresini kırmayı denedim — cihazın fingerprint'i, device ID, salt gibi değerleri şifre olarak denedim. Başarısız.
Frida ile Hook Denemesi
Eğer anahtarı statik analiz ile bulamıyorsam, uygulama çalışırken dinamik olarak yakalamayı deneyebilirdim. Frida bunun için ideal araç — çalışan bir process'e JavaScript kodu enjekte etmeyi sağlıyor.
Plan: libpb_reader.so veya libadobe_rmsdk.so içindeki AES/RSA fonksiyonlarını hook'layıp şifre çözme anında anahtarı yakalamak.
Java.perform(function() {
var EVP_DecryptInit_ex = Module.findExportByName(
"libpb_reader.so",
"EVP_DecryptInit_ex"
);
Interceptor.attach(EVP_DecryptInit_ex, {
onEnter: function(args) {
// args[3] = key, args[4] = iv
var key = args[3].readByteArray(16);
console.log("[+] AES KEY: " + bytesToHex(key));
}
});
});Ama Frida modülleri listelerken libpb_reader.so ve libadobe_rmsdk.so hiç görünmüyordu:
Process.enumerateModules().forEach(function(m) {
console.log(m.name, m.path);
});
// libpb_reader.so YOK
// libadobe_rmsdk.so YOKNeden? Houdini.
Houdini Nedir?
Memu x86_64 tabanlı bir emülatör. Calibro Reader ARM binary olarak derlenmemiş. Intel'in Houdini katmanı ARM kodunu gerçek zamanlı olarak x86'ya çevirerek çalıştırıyor. Bu işlem Frida'nın göremediği ayrı bir memory space'de gerçekleşiyor.
Frida → x86_64 process görüyor
Houdini → ARM kütüphanelerini kendi içinde çalıştırıyor
libpb_reader.so (ARM) → Frida'ya görünmüyor
Memu'yu ARM moduna geçirdim (System Settings → Preferences → ABI → ARM). Fark etmedi:
adb shell getprop ro.product.cpu.abi
# x86_64 ← hala x86_64Android sistemi x86_64 kalmaya devam etti. Frida hala ARM kütüphanelerine ulaşamadı.
/proc/pid/mem — Memory Dump Denemesi
Eğer şifre çözme anında anahtarı yakalayamazsam, belki şifresi çözülmüş veriyi doğrudan RAM'den okuyabilirdim.
EPUB dosyaları ZIP formatındadır. ZIP magic bytes: PK\x03\x04. Uygulama kitabı render ettiğinde bu veri bir an için RAM'de olmalı.
# PID bul
frida-ps -U | grep calibro
# 5254 com.calibro.reader:viewer
# Memory map'i al
adb shell "su 0 cat /proc/5254/maps" > maps.txtmaps.txt incelenince libpb_reader.so ve libadobe_rmsdk.so ARM binary olarak yüklenmiş görünüyordu:
0b9cc000-0c07e000 r--p /data/app/com.calibro.reader-.../lib/arm/libpb_reader.so
031cc000-039f7000 r--p /data/app/com.calibro.reader-.../lib/arm/libadobe_rmsdk.so
Heap bölgelerini dump etmeye çalıştım:
# Python ile dd komutu
cmd = f'adb shell "su 0 dd if=/proc/5254/mem bs=4096 skip={start//4096} count={size//4096}"'
result = subprocess.run(cmd, capture_output=True)
# Sonuç: boş/proc/pid/mem okuma bu Memu kurulumunda root ile bile çalışmıyordu.
Sonuç: DRM Kırılamaz
Uzun denemeler sonunda şu neticeye vardım:
Şifre çözme anahtarı Calibro'nun sunucusunda. Cihaza hiçbir zaman tam olarak gönderilmiyor. Native ARM kod (Houdini altında) onu alıp kullanıyor, dışarıya sızdırmıyor. Frida ARM kütüphanelerine erişemiyor.
Bu noktada stratejimi değiştirdim.
Plan B: Ekran Görüntüsü Otomasyonu
Uygulama kitabı ekranda gösteriyordu. Ekranda olan her şey yakalanabilir — bu bir bilgisayar bilimi gerçeği.
İlk deneme:
adb shell screencap -p /sdcard/test.png
adb pull /sdcard/test.pngSonuç: Tamamen siyah, boş PNG.
Neden? FLAG_SECURE. Bu Android flag'i ekran görüntüsü alınmasını engeller. DRM uygulamalarının kullandığı standart bir koruma.
Ama Memu'nun kendi screenshot mekanizması bu flag'i atlıyordu. Memu'nun sağ kenar çubuğundaki kamera ikonuna tıkladığımda içerikli ekran görüntüsü alınıyordu.
Peki bunu ADB ile tetikleyebilir miydim?
adb shell "input keyevent 120"
# 120 = KEYCODE_SYSRQ = screenshot keycodeEvet! Bu komut Memu'nun screenshot mekanizmasını tetikliyordu ve FLAG_SECURE'u atlıyordu. Dosya /sdcard/Pictures/Screenshots/ klasörüne kaydediliyordu.
Sayfa çevirmek için de klavye ok tuşları çalışıyordu — ekrana tap etmek yerine:
adb shell "input keyevent 22"
# 22 = KEYCODE_DPAD_RIGHT = sağ ok tuşuEkrana tap, sayfa içindeki dipnot linklerine yanlışlıkla tıklıyordu. Klavye tuşu bu sorunu çözdü.
Otomasyon Scripti
import subprocess, time, os
output_dir = r'C:\Users\hicbi\Downloads\kitap_pages'
os.makedirs(output_dir, exist_ok=True)
popup_wait = 10.0 # Screenshot popup'ı kaybolsun
page_turn_delay = 1.5 # Sayfa animasyonu tamamlansın
def adb(cmd):
subprocess.run(f'adb shell {cmd}', shell=True, capture_output=True)
def get_latest_screenshot():
result = subprocess.run(
'adb shell "ls -t /sdcard/Pictures/Screenshots/"',
shell=True, capture_output=True, text=True
)
files = [f.strip() for f in result.stdout.strip().split('\n') if f.strip()]
return files[0] if files else None
def capture_page(page_num):
local = os.path.join(output_dir, f'page_{page_num:04d}.png')
adb('"input keyevent 120"') # Screenshot al
time.sleep(popup_wait) # Popup kaybolsun
latest = get_latest_screenshot()
subprocess.run(
f'adb pull "/sdcard/Pictures/Screenshots/{latest}" "{local}"',
shell=True, capture_output=True
)
def next_page():
adb('"input keyevent 22"') # Sağ ok tuşu
time.sleep(page_turn_delay)
for i in range(1, 283):
capture_page(i)
if i < 282:
next_page()Karşılaşılan Sorunlar ve Çözümleri
1. Screenshot popup'ı
Her screenshot sonrası "Screenshot saved" popup'ı çıkıyor ve kapanmıyordu. ADB ile kapatmayı denedim (input tap, keyevent 4 vs.) — olmadı. Çözüm: Popup zaten 7-10 saniye sonra otomatik kapanıyor, popup_wait = 10 yapınca sorun çözüldü.
2. Dipnot linkleri
Sayfa çevirmek için input tap 1200 450 koordinatı kullanıyordum, ama bazı sayfalarda bu koordinat dipnot linkine denk geliyordu. Uygulama dipnotlar sayfasına atlıyordu. Çözüm: input keyevent 22 (sağ ok tuşu).
3. Screenshots klasörünün dolması
282 sayfa boyunca klasörde biriken PNG'ler ls -t komutunu yavaşlattı, timing bozuldu, sayfalar karıştı. Çözüm: Her 50 sayfada bir klasörü temizle:
adb shell "rm /sdcard/Pictures/Screenshots/*"4. Windows uyku modu Uzun süre klavye/mouse aktivitesi olmayınca Windows uyku moduna girdi, Memu dondu. Çözüm: Güç ayarlarından uyku modunu "Hiçbir zaman" yap. Android tarafı için:
adb shell "settings put system screen_off_timeout 3600000"
# 3600000 ms = 60 dakika5. EPUB sayfa numarası tutarsızlığı 282 sayfalık kitap, 1600x900 ekranda 413 ekran görüntüsüne çıktı. EPUB reader'larda sayfa numarası font boyutuna ve ekran boyutuna göre değişiyor — "sayfa" kavramı görecelidir.
6. Performans şişmesi Uzun süreli çalışmada Memu RAM'i doluyordu. Çözüm: 50'şer sayfa batch'ler halinde çalıştır, her batch sonrası Memu'yu dinlendir.
OCR: Görüntüden Metne
413 ekran görüntüsü elimdeydi. Şimdi metni çıkarmak gerekiyordu.
Tesseract OCR kurulumu:
adresinden Windows installer
Kurulum sırasında "Additional language data" → Turkish seçilmeli
pip install pytesseract pillowimport pytesseract
from PIL import Image
import os
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
folder = r'C:\Users\hicbi\Downloads\kitap_pages'
files = sorted([f for f in os.listdir(folder) if f.endswith('.png')])
with open('buyuk_tarih.txt', 'w', encoding='utf-8') as out:
for i, filename in enumerate(files):
img = Image.open(os.path.join(folder, filename))
text = pytesseract.image_to_string(img, lang='tur')
out.write(text)
out.write(f'\n\n--- Sayfa {i+1} ---\n\n')
print(f'Sayfa {i+1}/{len(files)}', end='\r')Sonuç oldukça başarılıydı. Türkçe karakter tanıma (ş, ğ, ü, ö, ı, ç) neredeyse hatasız çalıştı. Tesseract, yüksek kaliteli ekran görüntülerinde gerçekten iyi iş çıkarıyor.
Sonuç Olarak
Bir kitap için saatler harcadım. Sırasıyla şunları denedim:
✗ Adobe Digital Editions + DeDRM plugin → PBDRM formatını tanımıyor
✗ PBDRM header analizi + RSA brute force → Yanlış anahtar
✗ PKCS12 şifre kırma → Şifre bulunamadı
✗ Frida hook → Houdini ARM emülasyonu engel
✗ /proc/pid/mem dump → İzin yok
✓ ADB screenshot otomasyonu → Çalıştı
DRM sistemi beni değil, içerik üreticisini korudu. Ben yasal olarak satın aldığım bir içeriği okumak için bu kadar çaba harcadım. Korsanlık yapan biri bu adımların hiçbirini atmak zorunda kalmaz — DRM kırılmış versiyonunu internetten bulur.
Belki de DRM'in asıl kurbanları dürüst kullanıcılardır.
Kullanılan Araçlar
| Araç | Amaç |
|---|---|
| Memu | Android emülatör |
| ADB (Android Debug Bridge) | Cihaz kontrolü |
| Frida | Dynamic instrumentation |
| Python + pycryptodome | RSA/AES analizi |
| Tesseract OCR | Görüntüden metin |
| Hex editör | PBDRM format analizi |