Compare commits

..

14 Commits

Author SHA1 Message Date
NoDRM
077e8f5c2a Prepare release v10.0.3 2022-07-13 17:31:57 +02:00
NoDRM
fed8bb716b Add some Python2 compat code I forgot to add earlier 2022-07-13 17:31:57 +02:00
NoDRM
c12d214b59 Fix Obok plugin on Calibre 6 (#98) 2022-07-13 15:34:47 +02:00
Yuki Liu
012ff533ab fix the regular expression 2022-04-21 12:54:17 +00:00
NoDRM
dcbb377566 Fix Nook study key retrieval 2022-03-22 15:49:44 +01:00
NoDRM
76ce6d9c5c Fix Kindle for real 2022-03-20 14:32:22 +01:00
NoDRM
726d72217e Hopefully fix Kindle books 2022-03-20 08:09:00 +01:00
NoDRM
2d51005cf1 Fix print-replica Amazon books 2022-03-19 16:41:59 +01:00
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
24 changed files with 201 additions and 88 deletions

View File

@@ -35,7 +35,7 @@ List of changes since the fork of Apprentice Harper's repository:
- Fix small issue with elibri watermark removal.
- Adobe key name will now contain account email.
## Fixes on master (not yet released):
## Fixes in v10.0.3 (2022-07-13):
- Fix issue where importing a key from Adobe Digital Editions would fail in Python2 (Calibre < 5) if there were non-ASCII characters in the username.
- Add code to support importing multiple decryption keys from ADE.
@@ -56,9 +56,17 @@ List of changes since the fork of Apprentice Harper's repository:
- Drop support for importing key data from the ancient, pre "DeDRM" Calibre plugins ("Ignoble Epub DeDRM", "eReader PDB 2 PML" and "K4MobiDeDRM"). These are from 2011, I doubt anyone still has these installed, I can't even find a working link for these to test them. If you still have encryption keys in one of these plugins, you will need to update to DeDRM v10.0.2 or older (to convert the keys) before updating to DeDRM v10.0.3 or newer.
- Some Python3 bugfixes for Amazon books (merged #10 by ableeker).
- Fix a bug where extracting an Adobe key from ADE on Linux through Wine did fail when using the OpenSSL backend (instead of PyCrypto). See #13 and #14 for details, thanks acaloiaro for the bugfix.
- Make the plugin work on Calibre 6 (Qt 6). If you're running the Calibre 6 beta and you notice any issues, please open a bug report.
- Fix IndexError when DeDRMing some Amazon eBooks.
- Add support for books with the new ADE3.0+ DRM by merging #48 by a980e066a01. Thanks a lot!
- Add support for books with the new ADE3.0+ DRM by merging #48 by a980e066a01. Thanks a lot! (Also fixes #96 on MacOS)
- Remove OpenSSL support, now the plugin will always use the Python crypto libraries.
- Obok: Fix issues with invalid UTF-8 characters by merging #26 by baby-bell.
- Try to fix PDF files with V3 key obfuscation algorithm.
- ineptpdf: Fix broken V=3 key obfuscation algorithm.
- ineptpdf: (Hopefully) fix issues with some B&N PDF files.
- Fix broken Amazon K4PC key retrieval (fixes #38)
- Fix bug that corrupts output file for Print-Replica Amazon books (fixes #30).
- Fix Nook Study key retrieval code (partially fixes #50).
- Make the plugin work on Calibre 6 (Qt 6). (fixes #54 and #98) If you're running Calibre 6 and you notice any issues, please open a bug report.
## Fixes on master (not yet released):
- (None)

View File

@@ -17,7 +17,7 @@ p {margin-top: 0}
<body>
<h1>DeDRM Plugin <span class="version">(v10.0.2)</span></h1>
<h1>DeDRM Plugin <span class="version">(v10.0.3)</span></h1>
<p>This plugin removes DRM from ebooks when they are imported into calibre. If you already have DRMed ebooks in your calibre library, you will need to remove them and import them again.</p>
@@ -39,14 +39,14 @@ p {margin-top: 0}
<h3>Troubleshooting:</h3>
<p >If you find that its not working for you , you can save a lot of time by trying to add the ebook to Calibre in debug mode. This will print out a lot of helpful info that can be copied into any online help requests.</p>
<p>If you find that its not working for you , you can save a lot of time by trying to add the ebook to Calibre in debug mode. This will print out a lot of helpful info that can be copied into any online help requests.</p>
<p>Open a command prompt (terminal window) and type "calibre-debug -g" (without the quotes). Calibre will launch, and you can can add the problem ebook the usual way. The debug info will be output to the original command prompt (terminal window). Copy the resulting output and paste it into the comment you make at my blog.</p>
<p><span class="bold">Note:</span> The Mac version of Calibre doesnt install the command line tools by default. If you go to the Preferences page and click on the miscellaneous button, youll find the option to install the command line tools.</p>
<h3>Credits:</h3>
<ul>
<li>NoDRM for a bunch of updates and maintenance since November 2021, and the Readium LCP support</li>
<li>NoDRM for a bunch of updates and maintenance since November 2021<s>, and the Readium LCP support</s></li>
<li>The Dark Reverser for the Mobipocket and eReader scripts</li>
<li>i♥cabbages for the Adobe Digital Editions scripts</li>
<li>Skindle aka Bart Simpson for the Amazon Kindle for PC script</li>

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,32 +948,39 @@ 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 = {}
newnames = []
for i,keyvalue in enumerate(defaultkeys):
keyname = "default_key_{0:d}".format(i+1)
if keyvalue not in dedrmprefs['kindlekeys'].values():
newkeys[keyname] = keyvalue
newkeys["key_{0:d}".format(i)] = keyvalue
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:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,list(newkeys.items()),[],[],[],self.starttime)
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,newkeys.items(),[],[],[],self.starttime)
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"))
i = 1
for keyvalue in newkeys.values():
dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
while "kindle_key_{0:d}_{1:d}".format(int(time.time()), i) in dedrmprefs['kindlekeys']:
i = i + 1
dedrmprefs.addnamedvaluetoprefs('kindlekeys',"kindle_key_{0:d}_{1:d}".format(int(time.time()), i),keyvalue)
dedrmprefs.writeprefs()
except Exception as e:
traceback.print_exc()
pass
if not decoded:
#if you reached here then no luck raise and exception

View File

@@ -4,7 +4,7 @@
#@@CALIBRE_COMPAT_CODE@@
PLUGIN_NAME = "DeDRM"
__version__ = '10.0.2'
__version__ = '10.0.3'
PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")])
PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE])

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

@@ -6,7 +6,7 @@ __license__ = 'GPL v3'
# Python 3, September 2020
# Standard Python modules.
import sys, os, traceback, json, codecs, base64
import sys, os, traceback, json, codecs, base64, time
from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
QGroupBox, QPushButton, QListWidget, QListWidgetItem, QCheckBox,
@@ -1237,7 +1237,7 @@ class AddKindleDialog(QDialog):
from wineutils import WineGetKeys
scriptpath = os.path.join(parent.parent.alfdir,"kindlekey.py")
defaultkeys = WineGetKeys(scriptpath, ".k4i",parent.getwineprefix())
defaultkeys, defaultnames = WineGetKeys(scriptpath, ".k4i",parent.getwineprefix())
self.default_key = defaultkeys[0]
except:
@@ -1255,7 +1255,7 @@ class AddKindleDialog(QDialog):
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("default_key", self)
self.key_ledit = QLineEdit("default_key_" + str(int(time.time())), self)
self.key_ledit.setToolTip("<p>Enter an identifying name for the current default Kindle for Mac/PC key.")
key_group.addWidget(self.key_ledit)

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

@@ -157,7 +157,7 @@ def getNookLogFiles():
logpath = path +'\\Barnes & Noble\\NOOKstudy\\logs\\BNClientLog.txt'
if os.path.isfile(logpath):
found = True
print('Found nookStudy log file: ' + logpath.encode('ascii','ignore'), file=sys.stderr)
print('Found nookStudy log file: ' + logpath, file=sys.stderr)
logFiles.append(logpath)
else:
home = os.getenv('HOME')

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

@@ -50,13 +50,14 @@
# 9.1.0 - Support for decrypting with owner password, support for V=5, R=5 and R=6 PDF files, support for AES256-encrypted PDFs.
# 9.1.1 - Only support PyCryptodome; clean up the code
# 10.0.0 - Add support for "hardened" Adobe DRM (RMSDK >= 10)
# 10.0.2 - Fix some Python2 stuff
"""
Decrypts Adobe ADEPT-encrypted PDF files.
"""
__license__ = 'GPL v3'
__version__ = "10.0.0"
__version__ = "10.0.2"
import codecs
import hashlib
@@ -65,6 +66,8 @@ import os
import re
import zlib
import struct
import binascii
import base64
from io import BytesIO
from decimal import Decimal
import itertools
@@ -75,11 +78,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
@@ -1361,7 +1372,7 @@ class PDFDocument(object):
return file_key
def process_with_aes(self, key: bytes, encrypt: bool, data: bytes, repetitions: int = 1, iv: bytes = None):
def process_with_aes(self, key, encrypt, data, repetitions = 1, iv = None):
if iv is None:
keylen = len(key)
iv = bytes([0x00]*keylen)
@@ -1594,13 +1605,16 @@ 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]
length = int_value(param.get('Length', 0)) / 8
rights = str_value(param.get('ADEPT_LICENSE')).decode('base64')
rights = codecs.decode(str_value(param.get('ADEPT_LICENSE')), "base64")
rights = zlib.decompress(rights, -15)
rights = etree.fromstring(rights)
expr = './/{http://ns.adobe.com/adept}encryptedKey'
bookkey = ''.join(rights.findtext(expr)).decode('base64')
bookkey = unpad(AES.new(key, AES.MODE_CBC, b'\x00'*16).decrypt(bookkey), 16) # PKCS#7
bookkey = ''.join(rights.findtext(expr))
bookkey = base64.b64decode(bookkey)
bookkey = AES.new(key, AES.MODE_CBC, b'\x00'*16).decrypt(bookkey)
bookkey = unpad(bookkey, 16) # PKCS#7
if len(bookkey) > 16:
bookkey = bookkey[-16:]
ebx_V = int_value(param.get('V', 4))

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)
@@ -268,7 +275,7 @@ def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, pids):
orig_fn_root = os.path.splitext(os.path.basename(infile))[0]
if (
re.match('^B[A-Z0-9]{9}(_EBOK|_EBSP|_sample)?$', orig_fn_root) or
re.match('^{0-9A-F-}{36}$', orig_fn_root)
re.match('^[0-9A-F-]{36}$', orig_fn_root)
): # Kindle for PC / Mac / Android / Fire / iOS
clean_title = cleanup_name(book.getBookTitle())
outfilename = "{}_{}".format(orig_fn_root, clean_title)

View File

@@ -453,7 +453,7 @@ class MobiBook:
if crypto_type == 0:
print("This book is not encrypted.")
# we must still check for Print Replica
self.print_replica = (self.loadSection(1)[0:4] == '%MOP')
self.print_replica = (self.loadSection(1)[0:4] == b'%MOP')
self.mobi_data = self.data_file
return
if crypto_type != 2 and crypto_type != 1:
@@ -524,7 +524,7 @@ class MobiBook:
# print "record %d, extra_size %d" %(i,extra_size)
decoded_data = PC1(found_key, data[0:len(data) - extra_size])
if i==1:
self.print_replica = (decoded_data[0:4] == '%MOP')
self.print_replica = (decoded_data[0:4] == b'%MOP')
mobidataList.append(decoded_data)
if extra_size > 0:
mobidataList.append(data[-extra_size:])

View File

@@ -4,6 +4,7 @@
#@@CALIBRE_COMPAT_CODE@@
from ignoblekeyGenPassHash import generate_key
import sys
__license__ = 'GPL v3'
@@ -21,8 +22,13 @@ DETAILED_MESSAGE = \
def uStrCmp (s1, s2, caseless=False):
import unicodedata as ud
str1 = s1 if isinstance(s1, str) else str(s1)
str2 = s2 if isinstance(s2, str) else str(s2)
if sys.version_info[0] == 2:
str1 = s1 if isinstance(s1, unicode) else unicode(s1)
str2 = s2 if isinstance(s2, unicode) else unicode(s2)
else:
str1 = s1 if isinstance(s1, str) else str(s1)
str2 = s2 if isinstance(s2, str) else str(s2)
if caseless:
return ud.normalize('NFC', str1.lower()) == ud.normalize('NFC', str2.lower())
else:

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

@@ -3,7 +3,7 @@ from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__version__ = '10.0.0'
__version__ = '10.0.3'
__docformat__ = 'restructuredtext en'
#####################################################################
@@ -20,7 +20,7 @@ except NameError:
PLUGIN_NAME = 'Obok DeDRM'
PLUGIN_SAFE_NAME = PLUGIN_NAME.strip().lower().replace(' ', '_')
PLUGIN_DESCRIPTION = _('Removes DRM from Kobo kepubs and adds them to the library.')
PLUGIN_VERSION_TUPLE = (10, 0, 0)
PLUGIN_VERSION_TUPLE = (10, 0, 3)
PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE])
HELPFILE_NAME = PLUGIN_SAFE_NAME + '_Help.htm'
PLUGIN_AUTHORS = 'Anon'

View File

@@ -62,7 +62,7 @@ class ConfigWidget(QWidget):
def edit_kobo_directory(self):
tmpkobodirectory = QFileDialog.getExistingDirectory(self, "Select Kobo directory", self.kobodirectory or "/home", QFileDialog.ShowDirsOnly)
tmpkobodirectory = QFileDialog.getExistingDirectory(self, "Select Kobo directory", self.kobodirectory or "/home", QFileDialog.Option.ShowDirsOnly)
if tmpkobodirectory != u"" and tmpkobodirectory is not None:
self.kobodirectory = tmpkobodirectory

View File

@@ -409,7 +409,7 @@ class ReadOnlyTableWidgetItem(QTableWidgetItem):
def __init__(self, text):
if text is None:
text = ''
QTableWidgetItem.__init__(self, text, QTableWidgetItem.UserType)
QTableWidgetItem.__init__(self, text, QTableWidgetItem.ItemType.UserType)
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
class AuthorTableWidgetItem(ReadOnlyTableWidgetItem):
@@ -448,7 +448,7 @@ class IconWidgetItem(ReadOnlyTableWidgetItem):
class NumericTableWidgetItem(QTableWidgetItem):
def __init__(self, number, is_read_only=False):
QTableWidgetItem.__init__(self, '', QTableWidgetItem.UserType)
QTableWidgetItem.__init__(self, '', QTableWidgetItem.ItemType.UserType)
self.setData(Qt.DisplayRole, number)
if is_read_only:
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)

View File

@@ -1,6 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Version 10.0.3 July 2022
# Fix Calibre 6
#
# Version 10.0.1 February 2022
# Remove OpenSSL support to only support PyCryptodome; clean up the code.
#
@@ -166,7 +169,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 +188,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

@@ -8,7 +8,7 @@
<body>
<h1>Obok DeDRM Plugin</h1>
<h3>(version 10.0.0)</h3>
<h3>(version 10.0.2)</h3>
<h3>Installation:</h3>

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.