Compare commits

...

6 Commits

Author SHA1 Message Date
NoDRM
7eb8f07a33 Bugfix for Nook PDFs? 2022-03-19 16:02:33 +01:00
NoDRM
e4fe032e47 Some untested Python2 Kindle bugfixes 2022-03-19 15:23:07 +01:00
NoDRM
bb170688ba (Hopefully) fix WineGetKeys for Kindle 2022-03-19 15:08:36 +01:00
NoDRM
b283777c0a Add back unpad to fix Python2 support 2022-03-19 10:14:45 +01:00
NoDRM
cf095a4171 Update plugin readme 2022-03-19 09:26:39 +01:00
NoDRM
263cc1d2cf Improve error message 2022-03-19 09:17:29 +01:00
13 changed files with 159 additions and 62 deletions

View File

@@ -96,8 +96,14 @@ import traceback
try:
import __version
except ModuleNotFoundError:
except:
print("#############################")
print("Failed to load the DeDRM plugin")
print("Did you bundle this from source code yourself? If so, you'll need to run make_release.py instead to generate a valid plugin file.")
print("If you have no idea what the above means, please redownload the most recent version of the plugin from the Github Releases page.")
print("If you still receive this error with the released version, please open a bug report and attach the following information:")
print("#############################")
print("Debug information:")
print("__version not found, path is:")
print(sys.path)
print("I'm at:")
@@ -158,7 +164,7 @@ PLUGIN_VERSION_TUPLE = __version.PLUGIN_VERSION_TUPLE
class DeDRM(FileTypePlugin):
name = PLUGIN_NAME
description = "Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Readium LCP, Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
description = "Removes DRM from Adobe Adept (including Kobo), Barnes & Noble, Amazon Kindle, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
supported_platforms = ['linux', 'osx', 'windows']
author = "Apprentice Alf, Apprentice Harper, NoDRM, The Dark Reverser and i♥cabbages"
version = PLUGIN_VERSION_TUPLE
@@ -376,7 +382,7 @@ class DeDRM(FileTypePlugin):
from wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,"ignoblekeyNookStudy.py")
defaultkeys_study = WineGetKeys(scriptpath, ".b64",dedrmprefs['adobewineprefix'])
defaultkeys_study, defaultnames_study = WineGetKeys(scriptpath, ".b64",dedrmprefs['adobewineprefix'])
except:
print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
@@ -942,21 +948,30 @@ class DeDRM(FileTypePlugin):
from kindlekey import kindlekeys
defaultkeys = kindlekeys()
defaultnames = []
else: # linux
from wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,"kindlekey.py")
defaultkeys = WineGetKeys(scriptpath, ".k4i",dedrmprefs['kindlewineprefix'])
defaultkeys, defaultnames = WineGetKeys(scriptpath, ".k4i",dedrmprefs['kindlewineprefix'])
except:
print("{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
pass
newkeys = {}
newkeys = []
newnames = []
for i,keyvalue in enumerate(defaultkeys):
keyname = "default_key_{0:d}".format(i+1)
try:
keyname = "default_key_" + defaultnames[i]
except:
keyname = "default_key_{0:d}".format(i+1)
if keyvalue not in dedrmprefs['kindlekeys'].values():
newkeys[keyname] = keyvalue
newkeys.append(keyvalue)
newnames.append(keyname)
if len(newkeys) > 0:
print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
try:
@@ -964,8 +979,8 @@ class DeDRM(FileTypePlugin):
decoded = True
# store the new successful keys in the defaults
print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
for keyvalue in newkeys.values():
dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
for i,keyvalue in enumerate(newkeys):
dedrmprefs.addnamedvaluetoprefs('kindlekeys',newnames[i],keyvalue)
dedrmprefs.writeprefs()
except Exception as e:
pass

View File

@@ -39,7 +39,7 @@ Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
__version__ = '7.3'
__version__ = '7.4'
import sys, os, struct, getopt
from base64 import b64decode
@@ -128,10 +128,16 @@ if iswindows:
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation'
@@ -381,7 +387,7 @@ if iswindows:
pass
if ktype == 'privateLicenseKey':
userkey = winreg.QueryValueEx(plkkey, 'value')[0]
userkey = unpad(AES.new(keykey, AES.MODE_CBC, b'\x00'*16).decrypt(b64decode(userkey)), 16)[26:]
userkey = unpad(AES.new(keykey, AES.MODE_CBC, b'\x00'*16).decrypt(b64decode(userkey)))[26:]
# print ("found " + uuid_name + " key: " + str(userkey))
keys.append(userkey)

View File

@@ -23,10 +23,17 @@ import sys, os, time
import base64, hashlib
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
PASS_HASH_SECRET = "9ca588496a1bc4394553d9e018d70b9e"
@@ -48,7 +55,7 @@ def decrypt_passhash(passhash, fp):
hash_key = hashlib.sha1(bytearray.fromhex(serial_number + PASS_HASH_SECRET)).digest()[:16]
encrypted_cc_hash = base64.b64decode(passhash)
cc_hash = unpad(AES.new(hash_key, AES.MODE_CBC, encrypted_cc_hash[:16]).decrypt(encrypted_cc_hash[16:]), 16)
cc_hash = unpad(AES.new(hash_key, AES.MODE_CBC, encrypted_cc_hash[:16]).decrypt(encrypted_cc_hash[16:]))
return base64.b64encode(cc_hash).decode("ascii")

View File

@@ -36,10 +36,8 @@ from binascii import a2b_hex, b2a_hex
try:
from Cryptodome.Cipher import AES, DES
from Cryptodome.Util.Padding import pad, unpad
except ImportError:
from Crypto.Cipher import AES, DES
from Crypto.Util.Padding import pad, unpad
# Routines common to Mac and PC
@@ -116,6 +114,20 @@ STORAGE = "backup.ab"
STORAGE1 = "AmazonSecureStorage.xml"
STORAGE2 = "map_data_storage.db"
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
def pad(data, padding_len=16):
padding_data_len = padding_len - (len(data) % padding_len)
plaintext = data + chr(padding_data_len) * padding_data_len
return plaintext
class AndroidObfuscation(object):
'''AndroidObfuscation
For the key, it's written in java, and run in android dalvikvm

View File

@@ -10,13 +10,19 @@ import os
import base64
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlib
from lxml import etree
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
PASS_HASH_SECRET = "9ca588496a1bc4394553d9e018d70b9e"
@@ -46,10 +52,13 @@ def dump_keys(path_to_adobe_folder):
hashes = []
for pass_hash in activation_xml.findall(".//{http://ns.adobe.com/adept}passHash"):
encrypted_cc_hash = base64.b64decode(pass_hash.text)
cc_hash = unpad(AES.new(hash_key, AES.MODE_CBC, encrypted_cc_hash[:16]).decrypt(encrypted_cc_hash[16:]), 16)
hashes.append(base64.b64encode(cc_hash).decode("ascii"))
#print("Nook ccHash is %s" % (base64.b64encode(cc_hash).decode("ascii")))
try:
encrypted_cc_hash = base64.b64decode(pass_hash.text)
cc_hash = unpad(AES.new(hash_key, AES.MODE_CBC, encrypted_cc_hash[:16]).decrypt(encrypted_cc_hash[16:]))
hashes.append(base64.b64encode(cc_hash).decode("ascii"))
#print("Nook ccHash is %s" % (base64.b64encode(cc_hash).decode("ascii")))
except:
pass
return hashes

View File

@@ -16,13 +16,19 @@ import base64
import traceback
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
except:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlib
from lxml import etree
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
NOOK_DATA_FOLDER = "%LOCALAPPDATA%\\Packages\\BarnesNoble.Nook_ahnzqzva31enc\\LocalState"
PASS_HASH_SECRET = "9ca588496a1bc4394553d9e018d70b9e"

View File

@@ -56,11 +56,18 @@ import hashlib
try:
from Cryptodome.Cipher import AES, PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import unpad
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get

View File

@@ -75,11 +75,19 @@ from uuid import UUID
try:
from Cryptodome.Cipher import AES, ARC4, PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES, ARC4, PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import unpad
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
@@ -1593,7 +1601,12 @@ class PDFDocument(object):
def initialize_ebx_ignoble(self, keyb64, docid, param):
self.is_printable = self.is_modifiable = self.is_extractable = True
key = keyb64.decode('base64')[:16]
try:
key = keyb64.decode('base64')[:16]
# This will probably always error, but I'm not 100% sure, so lets leave the old code in.
except AttributeError:
key = codecs.decode(keyb64.encode("ascii"), 'base64')[:16]
length = int_value(param.get('Length', 0)) / 8
rights = str_value(param.get('ADEPT_LICENSE')).decode('base64')
rights = zlib.decompress(rights, -15)

View File

@@ -33,10 +33,8 @@ from io import BytesIO
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.py3compat import bchr
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Util.py3compat import bchr
try:
@@ -750,6 +748,26 @@ def addprottable(ion):
ion.addtocatalog("ProtectedData", 1, SYM_NAMES)
def pkcs7pad(msg, blocklen):
paddinglen = blocklen - len(msg) % blocklen
padding = bchr(paddinglen) * paddinglen
return msg + padding
def pkcs7unpad(msg, blocklen):
_assert(len(msg) % blocklen == 0)
paddinglen = msg[-1]
_assert(paddinglen > 0 and paddinglen <= blocklen, "Incorrect padding - Wrong key")
_assert(msg[-paddinglen:] == bchr(paddinglen) * paddinglen, "Incorrect padding - Wrong key")
return msg[:-paddinglen]
# every VoucherEnvelope version has a corresponding "word" and magic number, used in obfuscating the shared secret
OBFUSCATION_TABLE = {
"V1": (0x00, None),
@@ -865,7 +883,7 @@ class DrmIonVoucher(object):
key = hmac.new(sharedsecret, b"PIDv3", digestmod=hashlib.sha256).digest()
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
b = aes.decrypt(self.ciphertext)
b = unpad(b, 16)
b = pkcs7unpad(b, 16)
self.drmkey = BinaryIonParser(BytesIO(b))
addprottable(self.drmkey)
@@ -1071,7 +1089,7 @@ class DrmIon(object):
def processpage(self, ct, civ, outpages, decompress, decrypt):
if decrypt:
aes = AES.new(self.key[:16], AES.MODE_CBC, civ[:16])
msg = unpad(aes.decrypt(ct), 16)
msg = pkcs7unpad(aes.decrypt(ct), 16)
else:
msg = ct

View File

@@ -69,7 +69,11 @@ import getopt
import re
import traceback
import time
import html.entities
try:
import html.entities as htmlentitydefs
except:
import htmlentitydefs
import json
#@@CALIBRE_COMPAT_CODE@@
@@ -188,7 +192,7 @@ def unescape(text):
else:
# named entity
try:
text = chr(html.entities.name2codepoint[text[1:-1]])
text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
except KeyError:
pass
return text # leave as is
@@ -215,8 +219,11 @@ def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, starttime
else:
mb = topazextract.TopazBook(infile)
bookname = unescape(mb.getBookTitle())
print("Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
try:
bookname = unescape(mb.getBookTitle())
print("Decrypting {1} ebook: {0}".format(bookname, mb.getBookType()))
except:
print("Decrypting {0} ebook.".format(mb.getBookType()))
# copy list of pids
totalpids = list(pids)

View File

@@ -77,7 +77,7 @@ def WineGetKeys(scriptpath, extension, wineprefix=""):
pyexec = WinePythonCLI(wineprefix)
except NoWinePython3Exception:
print('{0} v{1}: Unable to find python3 executable in WINEPREFIX="{2}"'.format(PLUGIN_NAME, PLUGIN_VERSION, wineprefix))
return []
return [], []
basepath, script = os.path.split(scriptpath)
print("{0} v{1}: Running {2} under Wine".format(PLUGIN_NAME, PLUGIN_VERSION, script))

View File

@@ -166,7 +166,7 @@
from __future__ import print_function
__version__ = '10.0.1'
__about__ = "Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__)
__about__ = "Obok v{0}\nCopyright © 2012-2022 Physisticated et al.".format(__version__)
import sys
import os
@@ -185,10 +185,17 @@ import tempfile
try:
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
except ImportError:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def unpad(data, padding=16):
if sys.version_info[0] == 2:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
return data[:-pad_len]
can_parse_xml = True
try:

View File

@@ -1,7 +1,7 @@
Welcome to the tools!
=====================
This file is to give users a quick overview of what is available and how to get started. This document is part of the DeDRM Tools archive from Apprentice Harper's github repository: https://github.com/apprenticeharper/DeDRM_tools/
This file is to give users a quick overview of what is available and how to get started. This document is part of the DeDRM Tools archive from noDRM's github repository: https://github.com/noDRM/DeDRM_tools/
This archive includes calibre plugins to remove DRM from:
@@ -12,21 +12,22 @@ This archive includes calibre plugins to remove DRM from:
These tools do NOT work with Apple's iBooks FairPlay DRM. Use iBook Copy from TunesKit.
These tools no longer work well with books from Barnes & Noble.
Due to a DMCA request, these tools no longer work with LCP-encrypted books - see https://github.com/noDRM/DeDRM_tools/issues/18 for details.
For limitations and work-arounds, see the FAQ at https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md
For limitations and work-arounds, see the FAQ at https://github.com/noDRM/DeDRM_tools/blob/master/FAQs.md
About the tools
---------------
These tools are updated and maintained by Apprentice Harper and many others. You can find the latest updates at Apprentice Harper's github repository https://github.com/apprenticeharper/DeDRM_tools/ and get support by creating an issue at the repository (github account required) or by posting a comment at Apprentice Alf's blog: http://www.apprenticealf.wordpress.com/
These tools are updated and maintained by noDRM and many others. They are based on Apprentice Harper's Calibre plugin. You can find the latest updates at noDRM's github repository https://github.com/noDRM/DeDRM_tools/ and get support by creating an issue at the repository (github account required).
If you re-post these tools, a link to the repository and/or the blog would be appreciated.
If you re-post these tools, a link to the repository would be appreciated.
The tools are provided in the form of plugins for calibre. Calibre is an open source freeware ebook library manager. It is the best tool around for keeping track of your ebooks.
DeDRM plugin for calibre (Mac OS X, Windows)
DeDRM plugin for calibre (Linux, Mac OS X and Windows)
-------------------------------------------------------
calibe 5.x and later are now written in Python 3, and plugins must also use Python 3. If you have calibre 5, you must use version 7.x or later of the plugins. For calibre 4.x and earlier, use version 6.8.x of the plugins.
calibe 5.x and later are now written in Python 3, and plugins must also use Python 3.
The DeDRM plugin for calibre removes DRM from your Kindle and Adobe DRM ebooks when they are imported to calibre. Just install the DeDRM plugin (DeDRM_plugin.zip), following the instructions and configuration directions provided in the ReadMe file and the help links in the plugin's configuration dialogs.
@@ -40,18 +41,6 @@ To import ebooks from the Kobo Desktop app or from a Kobo ebook reader, install
For instructions, see the obok_plugin_ReadMe.txt file.
DeDRM application for Mac OS X users: (Mac OS X 10.6 and above)
---------------------------------------------------------------
DeDRM application for Windows users: (Windows XP through Windows 10)
------------------------------------------------------------------
As of Version 6.7 of the tools, these are no longer provided or supported.
Linux support
-------------
It may be possible to use the plugins on a Linux system, but no support is given at this time.
Credits
-------
The original inept and ignoble scripts were by i♥cabbages
@@ -61,7 +50,8 @@ The original topaz DRM removal script was by CMBDTC
The original topaz format conversion scripts were by some_updates, clarknova and Bart Simpson
The original KFX format decryption was by lulzkabulz, converted to python by Apprentice Naomi and integrated into the tools by tomthumb1997
The alfcrypto library is by some_updates
The DeDRM plugin was based on plugins by DiapDealer and is maintained by Apprentice Alf and Apprentice Harper
The DeDRM plugin is based on plugins by DiapDealer and is currently maintained by noDRM
The DeDRM plugin has been maintained by Apprentice Alf and Apprentice Harper until 2021.
The original obok script was by Physisticated
The plugin conversion was done anonymously.