miken32

obok2.py

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