View difference between Paste ID: 1jUzW8qx and ZVwQjmAh
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/env python
2
#
3
# Updated September 2013 by Anon
4
# Version 2.01
5
# Incorporated minor fixes posted at Apprentice Alf's.
6
#
7
# Updates July 2012 by Michael Newton
8
# PWSD ID is no longer a MAC address, but should always
9
# be stored in the registry. Script now works with OS X
10
# and checks plist for values instead of registry. Must
11
# have biplist installed for OS X support.
12
#
13
##########################################################
14
#                    KOBO DRM CRACK BY                   #
15
#                      PHYSISTICATED                     #
16
##########################################################
17
# This app was made for Python 2.7 on Windows 32-bit
18
#
19
# This app needs pycrypto - get from here:
20
# http://www.voidspace.org.uk/python/modules.shtml
21
#
22
# Usage: obok.py
23
# Choose the book you want to decrypt
24
#
25
# Shouts to my krew - you know who you are - and one in
26
# particular who gave me a lot of help with this - thank
27
# you so much!
28
#
29
# Kopimi /K\
30
# Keep sharing, keep copying, but remember that nothing is
31
# for free - make sure you compensate your favorite
32
# authors - and cut out the middle man whenever possible
33
# ;) ;) ;)
34
#
35
# DRM AUTOPSY
36
# The Kobo DRM was incredibly easy to crack, but it took
37
# me months to get around to making this. Here's the
38
# basics of how it works:
39
# 1: Get MAC address of first NIC in ipconfig (sometimes 
40
# stored in registry as pwsdid)
41
# 2: Get user ID (stored in tons of places, this gets it
42
# from HKEY_CURRENT_USER\Software\Kobo\Kobo Desktop 
43
# Edition\Browser\cookies)
44
# 3: Concatenate and SHA256, take the second half - this
45
# is your master key
46
# 4: Open %LOCALAPPDATA%\Kobo Desktop Editions\Kobo.sqlite
47
# and dump content_keys
48
# 5: Unbase64 the keys, then decode these with the master
49
# key - these are your page keys
50
# 6: Unzip EPUB of your choice, decrypt each page with its
51
# page key, then zip back up again
52
#
53
# WHY USE THIS WHEN INEPT WORKS FINE? (adobe DRM stripper)
54
# Inept works very well, but authors on Kobo can choose
55
# what DRM they want to use - and some have chosen not to
56
# let people download them with Adobe Digital Editions -
57
# they would rather lock you into a single platform.
58
#
59
# With Obok, you can sync Kobo Desktop, decrypt all your
60
# ebooks, and then use them on whatever device you want
61
# - you bought them, you own them, you can do what you
62
# like with them.
63
#
64
# Obok is Kobo backwards, but it is also means "next to"
65
# in Polish.
66
# When you buy a real book, it is right next to you. You
67
# can read it at home, at work, on a train, you can lend
68
# it to a friend, you can scribble on it, and add your own
69
# explanations/translations.
70
#
71
# Obok gives you this power over your ebooks - no longer
72
# are you restricted to one device. This allows you to
73
# embed foreign fonts into your books, as older Kobo's
74
# can't display them properly. You can read your books
75
# on your phones, in different PC readers, and different
76
# ereader devices. You can share them with your friends
77
# too, if you like - you can do that with a real book
78
# after all.
79
# 
80
"""
81
Decrypt Kobo encrypted EPUB books.
82
"""
83
84
import os
85
import sys
86
if sys.platform.startswith('win'):
87
    import _winreg
88
elif sys.platform.startswith('darwin'):
89
    from biplist import readPlist
90
import re
91
import string
92
import hashlib
93
import sqlite3
94
import base64
95
import binascii
96
import zipfile
97
from Crypto.Cipher import AES
98
99
def SHA256(raw):
100
    return hashlib.sha256(raw).hexdigest()
101
102
def RemoveAESPadding(contents):
103
    lastchar = binascii.b2a_hex(contents[-1:])
104
    strlen = int(lastchar, 16)
105
    padding = strlen
106
    if(strlen == 1):
107
        return contents[:-1]
108
    if(strlen < 16):
109
        for i in range(strlen):
110
            testchar = binascii.b2a_hex(contents[-strlen:-(strlen-1)])
111
            if(testchar != lastchar):
112
                padding = 0
113
    if(padding > 0):
114
        contents = contents[:-padding]
115
    return contents
116
117
def GetVolumeKeys(dbase, enc):
118
    volumekeys = {}
119
    for row in dbase.execute("SELECT * from content_keys"):
120
        if(row[0] not in volumekeys):
121
            volumekeys[row[0]] = {}
122
        volumekeys[row[0]][row[1]] = {}
123
        volumekeys[row[0]][row[1]]["encryptedkey"] = base64.b64decode(row[2])
124
        volumekeys[row[0]][row[1]]["decryptedkey"] = enc.decrypt(volumekeys[row[0]][row[1]]["encryptedkey"])
125
    # get book name
126
    for key in volumekeys.keys():
127
        volumekeys[key]["title"] = dbase.execute("SELECT Title from content where ContentID = '%s'" % (key)).fetchone()[0]
128
    return volumekeys
129
130
def ByteArrayToString(bytearr):
131
    wincheck = re.match("@ByteArray\\((.+)\\)", bytearr)
132
    if wincheck:
133
        return wincheck.group(1)
134
    return bytearr
135
136
def GetUserHexKey(prefs = ""):
137
    "find wsuid and pwsdid"
138
    wsuid = ""
139
    pwsdid = ""
140
    if sys.platform.startswith('win'):
141
        regkey_browser = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Kobo\\Kobo Desktop Edition\\Browser")
142
        cookies = _winreg.QueryValueEx(regkey_browser, "cookies")
143
        bytearrays = cookies[0]
144
    elif sys.platform.startswith('darwin'):
145
        cookies = readPlist(prefs)
146
        bytearrays = cookies["Browser.cookies"]
147
    for bytearr in bytearrays:
148
        cookie = ByteArrayToString(bytearr)
149
        print cookie
150
        wsuidcheck = re.match("^wsuid=([0-9a-f-]+)", cookie)
151
        if(wsuidcheck):
152
            wsuid = wsuidcheck.group(1)
153
        pwsdidcheck = re.match("^pwsdid=([0-9a-f-]+)", cookie)
154
        if (pwsdidcheck):
155
            pwsdid = pwsdidcheck.group(1)
156
157
    if(wsuid == "" or pwsdid == ""):
158
        print "wsuid or pwsdid key not found :/"
159
        exit()
160
    preuserkey = string.join((pwsdid, wsuid), "")
161
    print SHA256(pwsdid)
162
    userkey = SHA256(preuserkey)
163
    return userkey[32:]
164-
    kobodir = string.join((os.environ['LOCALAPPDATA'], "Kobo\\Kobo Desktop Edition"), delim)
164+
165
# get dirs
166
if sys.platform.startswith('win'):
167
    delim = "\\"
168
    if (sys.getwindowsversion().major > 5):
169
        kobodir = string.join((os.environ['LOCALAPPDATA'], "Kobo\\Kobo Desktop Edition"), delim)
170
    else:
171
        kobodir = string.join((os.environ['USERPROFILE'], "Local Settings\\Application Data\\Kobo\\Kobo Desktop Edition"), delim)
172
    prefs = ""
173
elif sys.platform.startswith('darwin'):
174
    delim = "/"
175
    kobodir = string.join((os.environ['HOME'], "Library/Application Support/Kobo/Kobo Desktop Edition"), delim)
176
    prefs = string.join((os.environ['HOME'], "Library/Preferences/com.kobo.Kobo Desktop Edition.plist"), delim)
177
sqlitefile = string.join((kobodir, "Kobo.sqlite"), delim)
178
bookdir = string.join((kobodir, "kepub"), delim)
179
180
# get key
181
userkeyhex = GetUserHexKey(prefs)
182
# load into AES
183
userkey = binascii.a2b_hex(userkeyhex)
184
enc = AES.new(userkey, AES.MODE_ECB)
185
186
# open sqlite
187
conn = sqlite3.connect(sqlitefile)
188
dbcursor = conn.cursor()
189
# get volume keys
190
volumekeys = GetVolumeKeys(dbcursor, enc)
191
192
# choose a volumeID
193
194
volumeid = ""
195
print "Choose a book to decrypt:"
196
i = 1
197
for key in volumekeys.keys():
198
    print "%d: %s" % (i, volumekeys[key]["title"])
199
    i += 1
200
201
num = input("...")
202
203
i = 1
204
for key in volumekeys.keys():
205
    if(i == num):
206
        volumeid = key
207-
# make filename out of a-zA-Z0-9 from title
207+
208-
outname = "%s.epub" % (re.sub("[^a-zA-Z0-9]", "", volumekeys[volumeid]["title"]))
208+
209
if(volumeid == ""):
210
    exit()
211
212
zippath = string.join((bookdir, volumeid), delim)
213
214
z = zipfile.ZipFile(zippath, "r")
215
# make filename out of Unicode alphanumeric and whitespace equivalents from title
216
outname = "%s.epub" % (re.sub("[^\s\w]", "", volumekeys[volumeid]["title"], 0, re.UNICODE))
217
zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED)
218
for filename in z.namelist():
219
    #print filename
220
    # read in and decrypt
221
    if(filename in volumekeys[volumeid]):
222
        # do decrypted version
223
        pagekey = volumekeys[volumeid][filename]["decryptedkey"]
224
        penc = AES.new(pagekey, AES.MODE_ECB)
225
        contents = RemoveAESPadding(penc.decrypt(z.read(filename)))
226
        # need to fix padding
227
        zout.writestr(filename, contents)
228
    else:
229
        zout.writestr(filename, z.read(filename))
230
231
print "Book saved as %s%s%s" % (os.getcwd(), delim, outname)