Guest User

Obok 2.01: remove Kobo DRM aka kdrm from kepub files

a guest
Sep 5th, 2013
5,369
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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.  
  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.     i += 1
  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)
Add Comment
Please, Sign In to add comment